OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "core/layout/ng/ng_block_layout_algorithm.h" | 5 #include "core/layout/ng/ng_block_layout_algorithm.h" |
6 | 6 |
7 #include "core/dom/NodeComputedStyle.h" | 7 #include "core/dom/NodeComputedStyle.h" |
8 #include "core/dom/TagCollection.h" | 8 #include "core/dom/TagCollection.h" |
| 9 #include "core/layout/LayoutTestHelper.h" |
9 #include "core/layout/ng/layout_ng_block_flow.h" | 10 #include "core/layout/ng/layout_ng_block_flow.h" |
| 11 #include "core/layout/ng/ng_block_break_token.h" |
10 #include "core/layout/ng/ng_block_node.h" | 12 #include "core/layout/ng/ng_block_node.h" |
11 #include "core/layout/ng/ng_constraint_space.h" | 13 #include "core/layout/ng/ng_constraint_space.h" |
12 #include "core/layout/ng/ng_constraint_space_builder.h" | 14 #include "core/layout/ng/ng_constraint_space_builder.h" |
13 #include "core/layout/ng/ng_floating_object.h" | 15 #include "core/layout/ng/ng_floating_object.h" |
14 #include "core/layout/ng/ng_length_utils.h" | 16 #include "core/layout/ng/ng_length_utils.h" |
15 #include "core/layout/LayoutTestHelper.h" | |
16 #include "core/layout/ng/ng_physical_box_fragment.h" | 17 #include "core/layout/ng/ng_physical_box_fragment.h" |
17 #include "core/layout/ng/ng_physical_fragment.h" | 18 #include "core/layout/ng/ng_physical_fragment.h" |
18 #include "core/layout/ng/ng_units.h" | 19 #include "core/layout/ng/ng_units.h" |
19 #include "core/style/ComputedStyle.h" | 20 #include "core/style/ComputedStyle.h" |
20 #include "testing/gmock/include/gmock/gmock.h" | 21 #include "testing/gmock/include/gmock/gmock.h" |
21 #include "testing/gtest/include/gtest/gtest.h" | 22 #include "testing/gtest/include/gtest/gtest.h" |
22 | 23 |
23 namespace blink { | 24 namespace blink { |
24 namespace { | 25 namespace { |
25 | 26 |
26 using testing::ElementsAre; | 27 using testing::ElementsAre; |
27 using testing::Pointee; | 28 using testing::Pointee; |
28 | 29 |
29 NGConstraintSpace* ConstructConstraintSpace(NGWritingMode writing_mode, | 30 NGConstraintSpace* ConstructConstraintSpace( |
30 TextDirection direction, | 31 NGWritingMode writing_mode, |
31 NGLogicalSize size, | 32 TextDirection direction, |
32 bool shrink_to_fit = false) { | 33 NGLogicalSize size, |
| 34 bool shrink_to_fit = false, |
| 35 LayoutUnit fragmentainer_space_available = LayoutUnit()) { |
| 36 NGFragmentationType block_fragmentation = |
| 37 fragmentainer_space_available != LayoutUnit() |
| 38 ? NGFragmentationType::kFragmentColumn |
| 39 : NGFragmentationType::kFragmentNone; |
| 40 |
33 return NGConstraintSpaceBuilder(writing_mode) | 41 return NGConstraintSpaceBuilder(writing_mode) |
34 .SetAvailableSize(size) | 42 .SetAvailableSize(size) |
35 .SetPercentageResolutionSize(size) | 43 .SetPercentageResolutionSize(size) |
36 .SetTextDirection(direction) | 44 .SetTextDirection(direction) |
37 .SetIsShrinkToFit(shrink_to_fit) | 45 .SetIsShrinkToFit(shrink_to_fit) |
| 46 .SetFragmentainerSpaceAvailable(fragmentainer_space_available) |
| 47 .SetFragmentationType(block_fragmentation) |
38 .ToConstraintSpace(writing_mode); | 48 .ToConstraintSpace(writing_mode); |
39 } | 49 } |
40 | 50 |
41 typedef bool TestParamLayoutNG; | 51 typedef bool TestParamLayoutNG; |
42 class NGBlockLayoutAlgorithmTest | 52 class NGBlockLayoutAlgorithmTest |
43 : public ::testing::WithParamInterface<TestParamLayoutNG>, | 53 : public ::testing::WithParamInterface<TestParamLayoutNG>, |
44 public RenderingTest { | 54 public RenderingTest { |
45 public: | 55 public: |
46 NGBlockLayoutAlgorithmTest() { | 56 NGBlockLayoutAlgorithmTest() { |
47 RuntimeEnabledFeatures::setLayoutNGEnabled(true); | 57 RuntimeEnabledFeatures::setLayoutNGEnabled(true); |
(...skipping 2152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2200 // Verify #clears-right block | 2210 // Verify #clears-right block |
2201 ASSERT_EQ(2UL, container_clear_fragment->Children().size()); | 2211 ASSERT_EQ(2UL, container_clear_fragment->Children().size()); |
2202 auto* clears_right_fragment = | 2212 auto* clears_right_fragment = |
2203 toNGPhysicalBoxFragment(container_clear_fragment->Children()[1].get()); | 2213 toNGPhysicalBoxFragment(container_clear_fragment->Children()[1].get()); |
2204 // 20 = right-float's block end offset (130 + 80) - | 2214 // 20 = right-float's block end offset (130 + 80) - |
2205 // container_clear->offsetTop() 190 | 2215 // container_clear->offsetTop() 190 |
2206 EXPECT_THAT(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(20)), | 2216 EXPECT_THAT(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(20)), |
2207 clears_right_fragment->Offset()); | 2217 clears_right_fragment->Offset()); |
2208 } | 2218 } |
2209 | 2219 |
| 2220 // Tests that a block won't fragment if it doesn't reach the fragmentation line. |
| 2221 TEST_F(NGBlockLayoutAlgorithmTest, NoFragmentation) { |
| 2222 setBodyInnerHTML(R"HTML( |
| 2223 <!DOCTYPE html> |
| 2224 <style> |
| 2225 #container { |
| 2226 width: 150px; |
| 2227 height: 200px; |
| 2228 } |
| 2229 </style> |
| 2230 <div id='container'></div> |
| 2231 )HTML"); |
| 2232 |
| 2233 LayoutUnit kFragmentainerSpaceAvailable(200); |
| 2234 |
| 2235 NGBlockNode* node = new NGBlockNode( |
| 2236 toLayoutBlockFlow(getLayoutObjectByElementId("container"))); |
| 2237 auto* space = ConstructConstraintSpace( |
| 2238 kHorizontalTopBottom, TextDirection::kLtr, |
| 2239 NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite), false, |
| 2240 kFragmentainerSpaceAvailable); |
| 2241 |
| 2242 // We should only have one 150x200 fragment with no fragmentation. |
| 2243 RefPtr<const NGPhysicalFragment> fragment = |
| 2244 NGBlockLayoutAlgorithm(node, space).Layout()->PhysicalFragment(); |
| 2245 EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(200)), fragment->Size()); |
| 2246 ASSERT_TRUE(fragment->BreakToken()->IsFinished()); |
| 2247 } |
| 2248 |
| 2249 // Tests that a block will fragment if it reaches the fragmentation line. |
| 2250 TEST_F(NGBlockLayoutAlgorithmTest, SimpleFragmentation) { |
| 2251 setBodyInnerHTML(R"HTML( |
| 2252 <!DOCTYPE html> |
| 2253 <style> |
| 2254 #container { |
| 2255 width: 150px; |
| 2256 height: 300px; |
| 2257 } |
| 2258 </style> |
| 2259 <div id='container'></div> |
| 2260 )HTML"); |
| 2261 |
| 2262 LayoutUnit kFragmentainerSpaceAvailable(200); |
| 2263 |
| 2264 NGBlockNode* node = new NGBlockNode( |
| 2265 toLayoutBlockFlow(getLayoutObjectByElementId("container"))); |
| 2266 auto* space = ConstructConstraintSpace( |
| 2267 kHorizontalTopBottom, TextDirection::kLtr, |
| 2268 NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite), false, |
| 2269 kFragmentainerSpaceAvailable); |
| 2270 |
| 2271 RefPtr<const NGPhysicalFragment> fragment = |
| 2272 NGBlockLayoutAlgorithm(node, space).Layout()->PhysicalFragment(); |
| 2273 EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(200)), fragment->Size()); |
| 2274 ASSERT_FALSE(fragment->BreakToken()->IsFinished()); |
| 2275 |
| 2276 fragment = NGBlockLayoutAlgorithm(node, space, |
| 2277 toNGBlockBreakToken(fragment->BreakToken())) |
| 2278 .Layout() |
| 2279 ->PhysicalFragment(); |
| 2280 EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(100)), fragment->Size()); |
| 2281 ASSERT_TRUE(fragment->BreakToken()->IsFinished()); |
| 2282 } |
| 2283 |
| 2284 // Tests that children inside the same block formatting context fragment when |
| 2285 // reaching a fragmentation line. |
| 2286 TEST_F(NGBlockLayoutAlgorithmTest, InnerChildrenFragmentation) { |
| 2287 setBodyInnerHTML(R"HTML( |
| 2288 <!DOCTYPE html> |
| 2289 <style> |
| 2290 #container { |
| 2291 width: 150px; |
| 2292 padding-top: 20px; |
| 2293 } |
| 2294 #child1 { |
| 2295 height: 200px; |
| 2296 margin-bottom: 20px; |
| 2297 } |
| 2298 #child2 { |
| 2299 height: 100px; |
| 2300 margin-top: 20px; |
| 2301 } |
| 2302 </style> |
| 2303 <div id='container'> |
| 2304 <div id='child1'></div> |
| 2305 <div id='child2'></div> |
| 2306 </div> |
| 2307 )HTML"); |
| 2308 |
| 2309 LayoutUnit kFragmentainerSpaceAvailable(200); |
| 2310 |
| 2311 NGBlockNode* node = new NGBlockNode( |
| 2312 toLayoutBlockFlow(getLayoutObjectByElementId("container"))); |
| 2313 auto* space = ConstructConstraintSpace( |
| 2314 kHorizontalTopBottom, TextDirection::kLtr, |
| 2315 NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite), false, |
| 2316 kFragmentainerSpaceAvailable); |
| 2317 |
| 2318 RefPtr<const NGPhysicalFragment> fragment = |
| 2319 NGBlockLayoutAlgorithm(node, space).Layout()->PhysicalFragment(); |
| 2320 EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(200)), fragment->Size()); |
| 2321 ASSERT_FALSE(fragment->BreakToken()->IsFinished()); |
| 2322 |
| 2323 FragmentChildIterator iterator(toNGPhysicalBoxFragment(fragment.get())); |
| 2324 const NGPhysicalBoxFragment* child = iterator.NextChild(); |
| 2325 EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(180)), child->Size()); |
| 2326 EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(20)), child->Offset()); |
| 2327 |
| 2328 EXPECT_FALSE(iterator.NextChild()); |
| 2329 |
| 2330 fragment = NGBlockLayoutAlgorithm(node, space, |
| 2331 toNGBlockBreakToken(fragment->BreakToken())) |
| 2332 .Layout() |
| 2333 ->PhysicalFragment(); |
| 2334 EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(140)), fragment->Size()); |
| 2335 ASSERT_TRUE(fragment->BreakToken()->IsFinished()); |
| 2336 |
| 2337 iterator.SetParent(toNGPhysicalBoxFragment(fragment.get())); |
| 2338 child = iterator.NextChild(); |
| 2339 EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(20)), child->Size()); |
| 2340 EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(0)), child->Offset()); |
| 2341 |
| 2342 child = iterator.NextChild(); |
| 2343 EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(100)), child->Size()); |
| 2344 EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(40)), child->Offset()); |
| 2345 |
| 2346 EXPECT_FALSE(iterator.NextChild()); |
| 2347 } |
| 2348 |
| 2349 // Tests that children which establish new formatting contexts fragment |
| 2350 // correctly. |
| 2351 TEST_F(NGBlockLayoutAlgorithmTest, |
| 2352 InnerFormattingContextChildrenFragmentation) { |
| 2353 setBodyInnerHTML(R"HTML( |
| 2354 <!DOCTYPE html> |
| 2355 <style> |
| 2356 #container { |
| 2357 width: 150px; |
| 2358 padding-top: 20px; |
| 2359 } |
| 2360 #child1 { |
| 2361 height: 200px; |
| 2362 margin-bottom: 20px; |
| 2363 contain: paint; |
| 2364 } |
| 2365 #child2 { |
| 2366 height: 100px; |
| 2367 margin-top: 20px; |
| 2368 contain: paint; |
| 2369 } |
| 2370 </style> |
| 2371 <div id='container'> |
| 2372 <div id='child1'></div> |
| 2373 <div id='child2'></div> |
| 2374 </div> |
| 2375 )HTML"); |
| 2376 |
| 2377 LayoutUnit kFragmentainerSpaceAvailable(200); |
| 2378 |
| 2379 NGBlockNode* node = new NGBlockNode( |
| 2380 toLayoutBlockFlow(getLayoutObjectByElementId("container"))); |
| 2381 auto* space = ConstructConstraintSpace( |
| 2382 kHorizontalTopBottom, TextDirection::kLtr, |
| 2383 NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite), false, |
| 2384 kFragmentainerSpaceAvailable); |
| 2385 |
| 2386 RefPtr<const NGPhysicalFragment> fragment = |
| 2387 NGBlockLayoutAlgorithm(node, space).Layout()->PhysicalFragment(); |
| 2388 EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(200)), fragment->Size()); |
| 2389 ASSERT_FALSE(fragment->BreakToken()->IsFinished()); |
| 2390 |
| 2391 FragmentChildIterator iterator(toNGPhysicalBoxFragment(fragment.get())); |
| 2392 const NGPhysicalBoxFragment* child = iterator.NextChild(); |
| 2393 EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(180)), child->Size()); |
| 2394 EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(20)), child->Offset()); |
| 2395 |
| 2396 EXPECT_FALSE(iterator.NextChild()); |
| 2397 |
| 2398 fragment = NGBlockLayoutAlgorithm(node, space, |
| 2399 toNGBlockBreakToken(fragment->BreakToken())) |
| 2400 .Layout() |
| 2401 ->PhysicalFragment(); |
| 2402 EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(140)), fragment->Size()); |
| 2403 ASSERT_TRUE(fragment->BreakToken()->IsFinished()); |
| 2404 |
| 2405 iterator.SetParent(toNGPhysicalBoxFragment(fragment.get())); |
| 2406 child = iterator.NextChild(); |
| 2407 EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(20)), child->Size()); |
| 2408 EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(0)), child->Offset()); |
| 2409 |
| 2410 child = iterator.NextChild(); |
| 2411 EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(100)), child->Size()); |
| 2412 EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(40)), child->Offset()); |
| 2413 |
| 2414 EXPECT_FALSE(iterator.NextChild()); |
| 2415 } |
| 2416 |
| 2417 // Tests that children inside a block container will fragment if the container |
| 2418 // doesn't reach the fragmentation line. |
| 2419 TEST_F(NGBlockLayoutAlgorithmTest, InnerChildrenFragmentationSmallHeight) { |
| 2420 setBodyInnerHTML(R"HTML( |
| 2421 <!DOCTYPE html> |
| 2422 <style> |
| 2423 #container { |
| 2424 width: 150px; |
| 2425 padding-top: 20px; |
| 2426 height: 50px; |
| 2427 } |
| 2428 #child1 { |
| 2429 height: 200px; |
| 2430 margin-bottom: 20px; |
| 2431 } |
| 2432 #child2 { |
| 2433 height: 100px; |
| 2434 margin-top: 20px; |
| 2435 } |
| 2436 </style> |
| 2437 <div id='container'> |
| 2438 <div id='child1'></div> |
| 2439 <div id='child2'></div> |
| 2440 </div> |
| 2441 )HTML"); |
| 2442 |
| 2443 LayoutUnit kFragmentainerSpaceAvailable(200); |
| 2444 |
| 2445 NGBlockNode* node = new NGBlockNode( |
| 2446 toLayoutBlockFlow(getLayoutObjectByElementId("container"))); |
| 2447 auto* space = ConstructConstraintSpace( |
| 2448 kHorizontalTopBottom, TextDirection::kLtr, |
| 2449 NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite), false, |
| 2450 kFragmentainerSpaceAvailable); |
| 2451 |
| 2452 RefPtr<const NGPhysicalFragment> fragment = |
| 2453 NGBlockLayoutAlgorithm(node, space).Layout()->PhysicalFragment(); |
| 2454 EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(70)), fragment->Size()); |
| 2455 ASSERT_FALSE(fragment->BreakToken()->IsFinished()); |
| 2456 |
| 2457 FragmentChildIterator iterator(toNGPhysicalBoxFragment(fragment.get())); |
| 2458 const NGPhysicalBoxFragment* child = iterator.NextChild(); |
| 2459 EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(180)), child->Size()); |
| 2460 EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(20)), child->Offset()); |
| 2461 |
| 2462 EXPECT_FALSE(iterator.NextChild()); |
| 2463 |
| 2464 fragment = NGBlockLayoutAlgorithm(node, space, |
| 2465 toNGBlockBreakToken(fragment->BreakToken())) |
| 2466 .Layout() |
| 2467 ->PhysicalFragment(); |
| 2468 EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(0)), fragment->Size()); |
| 2469 ASSERT_TRUE(fragment->BreakToken()->IsFinished()); |
| 2470 |
| 2471 iterator.SetParent(toNGPhysicalBoxFragment(fragment.get())); |
| 2472 child = iterator.NextChild(); |
| 2473 EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(20)), child->Size()); |
| 2474 EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(0)), child->Offset()); |
| 2475 |
| 2476 child = iterator.NextChild(); |
| 2477 EXPECT_EQ(NGPhysicalSize(LayoutUnit(150), LayoutUnit(100)), child->Size()); |
| 2478 EXPECT_EQ(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(40)), child->Offset()); |
| 2479 |
| 2480 EXPECT_FALSE(iterator.NextChild()); |
| 2481 } |
| 2482 |
2210 } // namespace | 2483 } // namespace |
2211 } // namespace blink | 2484 } // namespace blink |
OLD | NEW |