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" | |
8 #include "core/dom/TagCollection.h" | |
9 #include "core/layout/ng/layout_ng_block_flow.h" | |
7 #include "core/layout/ng/ng_block_node.h" | 10 #include "core/layout/ng/ng_block_node.h" |
8 #include "core/layout/ng/ng_constraint_space.h" | 11 #include "core/layout/ng/ng_constraint_space.h" |
9 #include "core/layout/ng/ng_constraint_space_builder.h" | 12 #include "core/layout/ng/ng_constraint_space_builder.h" |
13 #include "core/layout/ng/ng_floating_object.h" | |
10 #include "core/layout/ng/ng_length_utils.h" | 14 #include "core/layout/ng/ng_length_utils.h" |
15 #include "core/layout/LayoutTestHelper.h" | |
11 #include "core/layout/ng/ng_physical_box_fragment.h" | 16 #include "core/layout/ng/ng_physical_box_fragment.h" |
12 #include "core/layout/ng/ng_physical_fragment.h" | 17 #include "core/layout/ng/ng_physical_fragment.h" |
13 #include "core/layout/ng/ng_units.h" | 18 #include "core/layout/ng/ng_units.h" |
14 #include "core/layout/LayoutTestHelper.h" | 19 #include "testing/gmock/include/gmock/gmock.h" |
15 #include "core/style/ComputedStyle.h" | 20 #include "core/style/ComputedStyle.h" |
16 #include "testing/gtest/include/gtest/gtest.h" | 21 #include "testing/gtest/include/gtest/gtest.h" |
17 | 22 |
18 namespace blink { | 23 namespace blink { |
19 namespace { | 24 namespace { |
20 | 25 |
26 using testing::ElementsAre; | |
27 using testing::Pointee; | |
28 | |
21 NGConstraintSpace* ConstructConstraintSpace(NGWritingMode writing_mode, | 29 NGConstraintSpace* ConstructConstraintSpace(NGWritingMode writing_mode, |
22 TextDirection direction, | 30 TextDirection direction, |
23 NGLogicalSize size, | 31 NGLogicalSize size, |
24 bool shrink_to_fit = false) { | 32 bool shrink_to_fit = false) { |
25 return NGConstraintSpaceBuilder(writing_mode) | 33 return NGConstraintSpaceBuilder(writing_mode) |
26 .SetAvailableSize(size) | 34 .SetAvailableSize(size) |
27 .SetPercentageResolutionSize(size) | 35 .SetPercentageResolutionSize(size) |
28 .SetTextDirection(direction) | 36 .SetTextDirection(direction) |
29 .SetWritingMode(writing_mode) | 37 .SetWritingMode(writing_mode) |
30 .SetIsShrinkToFit(shrink_to_fit) | 38 .SetIsShrinkToFit(shrink_to_fit) |
(...skipping 18 matching lines...) Expand all Loading... | |
49 NGBlockNode* first_child) { | 57 NGBlockNode* first_child) { |
50 NGBlockNode parent(style_.get()); | 58 NGBlockNode parent(style_.get()); |
51 parent.SetFirstChild(first_child); | 59 parent.SetFirstChild(first_child); |
52 | 60 |
53 NGBlockLayoutAlgorithm algorithm(style_.get(), first_child, space); | 61 NGBlockLayoutAlgorithm algorithm(style_.get(), first_child, space); |
54 | 62 |
55 NGPhysicalFragment* fragment = algorithm.Layout(); | 63 NGPhysicalFragment* fragment = algorithm.Layout(); |
56 return toNGPhysicalBoxFragment(fragment); | 64 return toNGPhysicalBoxFragment(fragment); |
57 } | 65 } |
58 | 66 |
67 std::pair<NGPhysicalBoxFragment*, NGConstraintSpace*> | |
68 RunBlockLayoutAlgorithmForElement(Element* element) { | |
69 LayoutNGBlockFlow* block_flow = | |
70 toLayoutNGBlockFlow(element->layoutObject()); | |
71 NGConstraintSpace* space = | |
72 NGConstraintSpace::CreateFromLayoutObject(*block_flow); | |
73 NGPhysicalBoxFragment* fragment = RunBlockLayoutAlgorithm( | |
74 space, new NGBlockNode(element->layoutObject()->slowFirstChild())); | |
75 return std::make_pair(fragment, space); | |
76 } | |
77 | |
59 MinAndMaxContentSizes RunComputeMinAndMax(NGBlockNode* first_child) { | 78 MinAndMaxContentSizes RunComputeMinAndMax(NGBlockNode* first_child) { |
60 // The constraint space is not used for min/max computation, but we need | 79 // The constraint space is not used for min/max computation, but we need |
61 // it to create the algorithm. | 80 // it to create the algorithm. |
62 NGConstraintSpace* space = | 81 NGConstraintSpace* space = |
63 ConstructConstraintSpace(kHorizontalTopBottom, TextDirection::kLtr, | 82 ConstructConstraintSpace(kHorizontalTopBottom, TextDirection::kLtr, |
64 NGLogicalSize(LayoutUnit(), LayoutUnit())); | 83 NGLogicalSize(LayoutUnit(), LayoutUnit())); |
65 NGBlockLayoutAlgorithm algorithm(style_.get(), first_child, space); | 84 NGBlockLayoutAlgorithm algorithm(style_.get(), first_child, space); |
66 MinAndMaxContentSizes sizes; | 85 MinAndMaxContentSizes sizes; |
67 EXPECT_TRUE(algorithm.ComputeMinAndMaxContentSizes(&sizes)); | 86 EXPECT_TRUE(algorithm.ComputeMinAndMaxContentSizes(&sizes)); |
68 return sizes; | 87 return sizes; |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
160 // DIV2 | 179 // DIV2 |
161 child = static_cast<const NGPhysicalBoxFragment*>(child)->Children()[0]; | 180 child = static_cast<const NGPhysicalBoxFragment*>(child)->Children()[0]; |
162 | 181 |
163 EXPECT_EQ(kHeight, child->Height()); | 182 EXPECT_EQ(kHeight, child->Height()); |
164 EXPECT_EQ(0, child->TopOffset()); | 183 EXPECT_EQ(0, child->TopOffset()); |
165 EXPECT_EQ(kMarginLeft, child->LeftOffset()); | 184 EXPECT_EQ(kMarginLeft, child->LeftOffset()); |
166 } | 185 } |
167 | 186 |
168 // Verifies the collapsing margins case for the next pair: | 187 // Verifies the collapsing margins case for the next pair: |
169 // - top margin of a box and top margin of its first in-flow child. | 188 // - top margin of a box and top margin of its first in-flow child. |
170 // | 189 // Verifies that floats are positioned at the top of the first child that can |
171 // Test case's HTML representation: | 190 // determine its position after margins collapsed. |
172 // <div style="margin-top: 20px; height: 50px;"> <!-- DIV1 --> | 191 // TODO(glebl): Enable with new the float/margins collapsing algorithm. |
173 // <div style="margin-top: 10px"></div> <!-- DIV2 --> | 192 TEST_F(NGBlockLayoutAlgorithmTest, DISABLED_CollapsingMarginsCase1WithFloats) { |
174 // </div> | 193 setBodyInnerHTML( |
175 // | 194 "<style>" |
176 // Expected: | 195 " #container {" |
177 // - Empty margin strut of the fragment that establishes new formatting context | 196 " height: 200px;" |
178 // - Margins are collapsed resulting a single margin 20px = max(20px, 10px) | 197 " width: 200px;" |
179 // - The top offset of DIV2 == 20px | 198 " margin-top: 10px;" |
180 TEST_F(NGBlockLayoutAlgorithmTest, CollapsingMarginsCase1) { | 199 " padding: 0 7px;" |
181 const int kHeight = 50; | 200 " background-color: red;" |
182 const int kDiv1MarginTop = 20; | 201 " }" |
183 const int kDiv2MarginTop = 10; | 202 " #first-child {" |
184 | 203 " margin-top: 20px;" |
185 // DIV1 | 204 " height: 10px;" |
186 RefPtr<ComputedStyle> div1_style = ComputedStyle::create(); | 205 " background-color: blue;" |
187 div1_style->setHeight(Length(kHeight, Fixed)); | 206 " }" |
188 div1_style->setMarginTop(Length(kDiv1MarginTop, Fixed)); | 207 " #float-child-left {" |
189 NGBlockNode* div1 = new NGBlockNode(div1_style.get()); | 208 " float: left;" |
190 | 209 " height: 10px;" |
191 // DIV2 | 210 " width: 10px;" |
192 RefPtr<ComputedStyle> div2_style = ComputedStyle::create(); | 211 " padding: 10px;" |
193 div2_style->setMarginTop(Length(kDiv2MarginTop, Fixed)); | 212 " margin: 10px;" |
194 NGBlockNode* div2 = new NGBlockNode(div2_style.get()); | 213 " background-color: green;" |
195 | 214 " }" |
196 div1->SetFirstChild(div2); | 215 " #float-child-right {" |
197 | 216 " float: right;" |
198 auto* space = | 217 " height: 30px;" |
199 NGConstraintSpaceBuilder(kHorizontalTopBottom) | 218 " width: 30px;" |
200 .SetAvailableSize(NGLogicalSize(LayoutUnit(100), NGSizeIndefinite)) | 219 " background-color: pink;" |
201 .SetPercentageResolutionSize( | 220 " }" |
202 NGLogicalSize(LayoutUnit(100), NGSizeIndefinite)) | 221 "</style>" |
203 .SetTextDirection(TextDirection::kLtr) | 222 "<div id='container'>" |
204 .SetIsNewFormattingContext(true) | 223 " <div id='float-child-left'></div>" |
205 .ToConstraintSpace(); | 224 " <div id='float-child-right'></div>" |
206 NGPhysicalBoxFragment* frag = RunBlockLayoutAlgorithm(space, div1); | 225 " <div id='first-child'></div>" |
207 | 226 "</div>"); |
208 EXPECT_TRUE(frag->MarginStrut().IsEmpty()); | 227 |
209 ASSERT_EQ(frag->Children().size(), 1UL); | 228 // ** Run LayoutNG algorithm ** |
210 const NGPhysicalBoxFragment* div2_fragment = | 229 NGConstraintSpace* space; |
211 static_cast<const NGPhysicalBoxFragment*>(frag->Children()[0].get()); | 230 NGPhysicalBoxFragment* fragment; |
212 EXPECT_EQ(NGDeprecatedMarginStrut({LayoutUnit(kDiv2MarginTop)}), | 231 std::tie(fragment, space) = RunBlockLayoutAlgorithmForElement( |
213 div2_fragment->MarginStrut()); | 232 document().getElementsByTagName("html")->item(0)); |
214 EXPECT_EQ(kDiv1MarginTop, div2_fragment->TopOffset()); | 233 ASSERT_EQ(fragment->Children().size(), 1UL); |
234 auto* body_fragment = toNGPhysicalBoxFragment(fragment->Children()[0]); | |
235 // 20 = max(first child's margin top, containers's margin top) | |
236 int body_top_offset = 20; | |
237 EXPECT_THAT(LayoutUnit(body_top_offset), body_fragment->TopOffset()); | |
238 // 8 = body's margin | |
239 int body_left_offset = 8; | |
240 EXPECT_THAT(LayoutUnit(body_left_offset), body_fragment->LeftOffset()); | |
241 ASSERT_EQ(1UL, body_fragment->Children().size()); | |
242 auto* container_fragment = | |
243 toNGPhysicalBoxFragment(body_fragment->Children()[0]); | |
244 // 0 = collapsed with body's margin | |
245 EXPECT_THAT(LayoutUnit(0), container_fragment->TopOffset()); | |
246 ASSERT_EQ(1UL, container_fragment->Children().size()); | |
247 auto* first_child_fragment = | |
248 toNGPhysicalBoxFragment(container_fragment->Children()[0]); | |
249 // 0 = collapsed with container's margin | |
250 EXPECT_THAT(LayoutUnit(0), first_child_fragment->TopOffset()); | |
251 | |
252 // ** Verify layout tree ** | |
253 Element* first_child = document().getElementById("first-child"); | |
254 int first_child_block_offset = body_top_offset; | |
255 EXPECT_EQ(first_child_block_offset, first_child->offsetTop()); | |
256 | |
257 // The left float to be positioned at first-child's top. | |
ikilpatrick
2017/01/24 22:05:37
So I was more pushing back on the wording here, it
Gleb Lanbin
2017/01/24 22:30:43
Done.
| |
258 Element* float_child_left = document().getElementById("float-child-left"); | |
259 // first_child_block_offset + 10(float-child-left's margin) | |
260 int float_child_left_block_offset = first_child_block_offset + 10; | |
261 EXPECT_EQ(float_child_left_block_offset, float_child_left->offsetTop()); | |
262 | |
263 // The right float to be positioned at first-child's top. | |
ikilpatrick
2017/01/24 22:05:37
and here.
Gleb Lanbin
2017/01/24 22:30:43
Done.
| |
264 Element* float_child_right = document().getElementById("float-child-right"); | |
265 // Should be equal to first_child_block_offset | |
266 int float_child_right_block_offset = first_child_block_offset; | |
267 EXPECT_EQ(float_child_right_block_offset, float_child_right->offsetTop()); | |
268 | |
269 // ** Verify exclusions ** | |
270 // float-child-left's height(10) + padding(2x10) + margin(2x10) = 50px | |
271 NGLogicalSize exclusion1_size = {LayoutUnit(50), LayoutUnit(50)}; | |
272 // float-child-left's inline offset | |
273 // 15 = body's margin(8) + container's inline padding(7) | |
274 NGLogicalOffset exclusion1_offset = {LayoutUnit(15), | |
275 LayoutUnit(first_child_block_offset)}; | |
276 NGLogicalRect exclusion1_rect = {exclusion1_offset, exclusion1_size}; | |
277 NGExclusion expected_exclusion1 = {exclusion1_rect, NGExclusion::kFloatLeft}; | |
278 | |
279 NGLogicalSize exclusion2_size = {LayoutUnit(30), LayoutUnit(30)}; | |
280 // float-child-right's inline offset | |
281 // right_float_offset = 200 container's width - right float width 30 = 170 | |
282 // 185 = body's margin(8) + right_float_offset(170) + container's padding(7) | |
283 NGLogicalOffset exclusion2_offset = {LayoutUnit(185), | |
284 LayoutUnit(first_child_block_offset)}; | |
285 NGLogicalRect exclusion2_rect = {exclusion2_offset, exclusion2_size}; | |
286 NGExclusion expected_exclusion2 = {exclusion2_rect, NGExclusion::kFloatRight}; | |
287 | |
288 EXPECT_THAT(space->Exclusions()->storage, | |
289 (ElementsAre(Pointee(expected_exclusion1), | |
290 Pointee(expected_exclusion2)))); | |
215 } | 291 } |
216 | 292 |
217 // Verifies the collapsing margins case for the next pair: | 293 // Verifies the collapsing margins case for the next pair: |
218 // - bottom margin of box and top margin of its next in-flow following sibling. | 294 // - bottom margin of box and top margin of its next in-flow following sibling. |
219 // | 295 // - top and bottom margins of a box that does not establish a new block |
220 // Test case's HTML representation: | 296 // formatting context and that has zero computed 'min-height', zero or 'auto' |
221 // <div style="margin-bottom: 20px; height: 50px;"> <!-- DIV1 --> | 297 // computed 'height', and no in-flow children |
222 // <div style="margin-bottom: -15px"></div> <!-- DIV2 --> | 298 // TODO(glebl): Enable with new the float/margins collapsing algorithm. |
223 // <div></div> <!-- DIV3 --> | 299 TEST_F(NGBlockLayoutAlgorithmTest, DISABLED_CollapsingMarginsCase2WithFloats) { |
224 // </div> | 300 setBodyInnerHTML( |
225 // <div></div> <!-- DIV4 --> | 301 "<style>" |
226 // <div style="margin-top: 10px; height: 50px;"> <!-- DIV5 --> | 302 "#first-child {" |
227 // <div></div> <!-- DIV6 --> | 303 " background-color: red;" |
228 // <div style="margin-top: -30px"></div> <!-- DIV7 --> | 304 " height: 50px;" |
229 // </div> | 305 " margin-bottom: 20px;" |
230 // | 306 "}" |
231 // Expected: | 307 "#float-between-empties {" |
232 // Margins are collapsed resulting an overlap | 308 " background-color: green;" |
233 // -10px = max(20px, 10px) - max(abs(-15px), abs(-30px)) | 309 " float: left;" |
234 // between DIV2 and DIV3. Zero-height blocks are ignored. | 310 " height: 30px;" |
235 TEST_F(NGBlockLayoutAlgorithmTest, CollapsingMarginsCase2) { | 311 " width: 30px;" |
236 const int kHeight = 50; | 312 "}" |
237 const int kDiv1MarginBottom = 20; | 313 "#float-between-nonempties {" |
238 const int kDiv2MarginBottom = -15; | 314 " background-color: lightgreen;" |
239 const int kDiv5MarginTop = 10; | 315 " float: left;" |
240 const int kDiv7MarginTop = -30; | 316 " height: 40px;" |
241 const int kExpectedCollapsedMargin = -10; | 317 " width: 40px;" |
242 | 318 "}" |
243 // DIV1 | 319 "#float-top-align {" |
244 RefPtr<ComputedStyle> div1_style = ComputedStyle::create(); | 320 " background-color: seagreen;" |
245 div1_style->setHeight(Length(kHeight, Fixed)); | 321 " float: left;" |
246 div1_style->setMarginBottom(Length(kDiv1MarginBottom, Fixed)); | 322 " height: 50px;" |
247 NGBlockNode* div1 = new NGBlockNode(div1_style.get()); | 323 " width: 50px;" |
248 | 324 "}" |
249 // DIV2 | 325 "#second-child {" |
250 RefPtr<ComputedStyle> div2_style = ComputedStyle::create(); | 326 " background-color: blue;" |
251 div2_style->setMarginBottom(Length(kDiv2MarginBottom, Fixed)); | 327 " height: 50px;" |
252 NGBlockNode* div2 = new NGBlockNode(div2_style.get()); | 328 " margin-top: 10px;" |
253 | 329 "}" |
254 // Empty DIVs: DIV3, DIV4, DIV6 | 330 "</style>" |
255 NGBlockNode* div3 = new NGBlockNode(ComputedStyle::create().get()); | 331 "<div id='first-child'>" |
256 NGBlockNode* div4 = new NGBlockNode(ComputedStyle::create().get()); | 332 " <div id='empty1' style='margin-bottom: -15px'></div>" |
257 NGBlockNode* div6 = new NGBlockNode(ComputedStyle::create().get()); | 333 " <div id='float-between-empties'></div>" |
258 | 334 " <div id='empty2'></div>" |
259 // DIV5 | 335 "</div>" |
260 RefPtr<ComputedStyle> div5_style = ComputedStyle::create(); | 336 "<div id='float-between-nonempties'></div>" |
261 div5_style->setHeight(Length(kHeight, Fixed)); | 337 "<div id='second-child'>" |
262 div5_style->setMarginTop(Length(kDiv5MarginTop, Fixed)); | 338 " <div id='float-top-align'></div>" |
263 NGBlockNode* div5 = new NGBlockNode(div5_style.get()); | 339 " <div id='empty3'></div>" |
264 | 340 " <div id='empty4' style='margin-top: -30px'></div>" |
265 // DIV7 | 341 "</div>" |
266 RefPtr<ComputedStyle> div7_style = ComputedStyle::create(); | 342 "<div id='empty5'></div>"); |
267 div7_style->setMarginTop(Length(kDiv7MarginTop, Fixed)); | 343 |
268 NGBlockNode* div7 = new NGBlockNode(div7_style.get()); | 344 // ** Run LayoutNG algorithm ** |
269 | 345 NGConstraintSpace* space; |
270 div1->SetFirstChild(div2); | 346 NGPhysicalBoxFragment* fragment; |
271 div2->SetNextSibling(div3); | 347 std::tie(fragment, space) = RunBlockLayoutAlgorithmForElement( |
272 div1->SetNextSibling(div4); | 348 document().getElementsByTagName("html")->item(0)); |
273 div4->SetNextSibling(div5); | 349 |
274 div5->SetFirstChild(div6); | 350 auto* body_fragment = toNGPhysicalBoxFragment(fragment->Children()[0]); |
275 div6->SetNextSibling(div7); | 351 // -7 = empty1's margin(-15) + body's margin(8) |
276 | 352 int body_top_offset = -7; |
277 auto* space = ConstructConstraintSpace( | 353 EXPECT_THAT(LayoutUnit(body_top_offset), body_fragment->TopOffset()); |
278 kHorizontalTopBottom, TextDirection::kLtr, | 354 int body_left_offset = 8; |
279 NGLogicalSize(LayoutUnit(100), NGSizeIndefinite)); | 355 EXPECT_THAT(LayoutUnit(body_top_offset), body_fragment->TopOffset()); |
280 NGPhysicalBoxFragment* frag = RunBlockLayoutAlgorithm(space, div1); | 356 ASSERT_EQ(3UL, body_fragment->Children().size()); |
281 | 357 |
282 ASSERT_EQ(frag->Children().size(), 3UL); | 358 auto* first_child_fragment = |
283 | 359 toNGPhysicalBoxFragment(body_fragment->Children()[0]); |
284 // DIV1 | 360 EXPECT_THAT(LayoutUnit(), first_child_fragment->TopOffset()); |
285 const NGPhysicalFragment* child = frag->Children()[0]; | 361 |
286 EXPECT_EQ(kHeight, child->Height()); | 362 auto* second_child_fragment = |
287 EXPECT_EQ(0, child->TopOffset()); | 363 toNGPhysicalBoxFragment(body_fragment->Children()[1]); |
288 | 364 // 40 = first_child's height(50) - margin's collapsing result(10) |
289 // DIV5 | 365 int second_child_block_offset = 40; |
290 child = frag->Children()[2]; | 366 EXPECT_THAT(LayoutUnit(second_child_block_offset), |
291 EXPECT_EQ(kHeight, child->Height()); | 367 second_child_fragment->TopOffset()); |
292 EXPECT_EQ(kHeight + kExpectedCollapsedMargin, child->TopOffset()); | 368 |
369 auto* empty5_fragment = toNGPhysicalBoxFragment(body_fragment->Children()[2]); | |
370 // 90 = first_child's height(50) + collapsed margins(-10) + | |
371 // second child's height(50) | |
372 int empty5_fragment_block_offset = 90; | |
373 EXPECT_THAT(LayoutUnit(empty5_fragment_block_offset), | |
374 empty5_fragment->TopOffset()); | |
375 | |
376 ASSERT_EQ(3UL, body_fragment->PositionedFloats().size()); | |
377 auto float_nonempties_fragment = | |
378 body_fragment->PositionedFloats().at(1)->fragment; | |
379 // 70 = first_child's height(50) + first child's margin-bottom(20) | |
380 EXPECT_THAT(LayoutUnit(70), float_nonempties_fragment->TopOffset()); | |
381 EXPECT_THAT(LayoutUnit(0), float_nonempties_fragment->LeftOffset()); | |
382 | |
383 // ** Verify layout tree ** | |
384 Element* first_child = document().getElementById("first-child"); | |
385 // -7 = body_top_offset | |
386 EXPECT_EQ(body_top_offset, first_child->offsetTop()); | |
387 | |
388 NGLogicalSize float_empties_exclusion_size = {LayoutUnit(30), LayoutUnit(30)}; | |
389 NGLogicalOffset float_empties_exclusion_offset = { | |
390 LayoutUnit(body_left_offset), LayoutUnit(body_top_offset)}; | |
391 NGLogicalRect float_empties_exclusion_rect = {float_empties_exclusion_offset, | |
392 float_empties_exclusion_size}; | |
393 NGExclusion float_empties_exclusion = {float_empties_exclusion_rect, | |
394 NGExclusion::kFloatLeft}; | |
395 | |
396 NGLogicalSize float_nonempties_exclusion_size = {LayoutUnit(40), | |
397 LayoutUnit(40)}; | |
398 // 63 = first_child_margin_strut(20) + first-child's height(50) + | |
399 // body_top_offset(-7) | |
400 NGLogicalOffset float_nonempties_exclusion_offset = { | |
401 LayoutUnit(body_left_offset), LayoutUnit(63)}; | |
402 NGLogicalRect float_nonempties_exclusion_rect = { | |
403 float_nonempties_exclusion_offset, float_nonempties_exclusion_size}; | |
404 NGExclusion float_nonempties_exclusion = {float_nonempties_exclusion_rect, | |
405 NGExclusion::kFloatLeft}; | |
406 | |
407 NGLogicalSize float_top_align_exclusion_size = {LayoutUnit(50), | |
408 LayoutUnit(50)}; | |
409 // 63 = float_nonempties_exclusion_offset because of the top edge alignment | |
410 // rule. | |
411 // 48 = body's margin + float_nonempties_exclusion_size | |
412 NGLogicalOffset float_top_align_exclusion_offset = {LayoutUnit(48), | |
413 LayoutUnit(63)}; | |
414 NGLogicalRect float_top_align_exclusion_rect = { | |
415 float_top_align_exclusion_offset, float_top_align_exclusion_size}; | |
416 NGExclusion float_top_align_exclusion = {float_top_align_exclusion_rect, | |
417 NGExclusion::kFloatLeft}; | |
418 | |
419 EXPECT_THAT(space->Exclusions()->storage, | |
420 (ElementsAre(Pointee(float_empties_exclusion), | |
421 Pointee(float_nonempties_exclusion), | |
422 Pointee(float_top_align_exclusion)))); | |
293 } | 423 } |
294 | 424 |
295 // Verifies the collapsing margins case for the next pair: | 425 // Verifies the collapsing margins case for the next pair: |
296 // - bottom margin of a last in-flow child and bottom margin of its parent if | 426 // - bottom margin of a last in-flow child and bottom margin of its parent if |
297 // the parent has 'auto' computed height | 427 // the parent has 'auto' computed height |
298 // | 428 // TODO(glebl): Enable with new the float/margins collapsing algorithm. |
299 // Test case's HTML representation: | 429 TEST_F(NGBlockLayoutAlgorithmTest, DISABLED_CollapsingMarginsCase3) { |
300 // <div style="margin-bottom: 20px; height: 50px;"> <!-- DIV1 --> | 430 setBodyInnerHTML( |
301 // <div style="margin-bottom: 200px; height: 50px;"/> <!-- DIV2 --> | 431 "<style>" |
302 // </div> | 432 " #container {" |
303 // | 433 " margin-bottom: 20px;" |
304 // Expected: | 434 " }" |
305 // 1) Margins are collapsed with the result = std::max(20, 200) | 435 " #child {" |
306 // if DIV1.height == auto | 436 " margin-bottom: 200px;" |
307 // 2) Margins are NOT collapsed if DIV1.height != auto | 437 " height: 50px;" |
308 TEST_F(NGBlockLayoutAlgorithmTest, CollapsingMarginsCase3) { | 438 " }" |
309 const int kHeight = 50; | 439 "</style>" |
310 const int kDiv1MarginBottom = 20; | 440 "<div id='container'>" |
311 const int kDiv2MarginBottom = 200; | 441 " <div id='child'></div>" |
312 | 442 "</div>"); |
313 // DIV1 | 443 |
314 RefPtr<ComputedStyle> div1_style = ComputedStyle::create(); | 444 const NGPhysicalBoxFragment* body_fragment; |
315 div1_style->setMarginBottom(Length(kDiv1MarginBottom, Fixed)); | 445 const NGPhysicalBoxFragment* container_fragment; |
316 NGBlockNode* div1 = new NGBlockNode(div1_style.get()); | 446 const NGPhysicalBoxFragment* child_fragment; |
317 | 447 const NGPhysicalBoxFragment* fragment; |
318 // DIV2 | 448 auto run_test = [&](const Length& container_height) { |
319 RefPtr<ComputedStyle> div2_style = ComputedStyle::create(); | 449 Element* container = document().getElementById("container"); |
320 div2_style->setHeight(Length(kHeight, Fixed)); | 450 container->mutableComputedStyle()->setHeight(container_height); |
321 div2_style->setMarginBottom(Length(kDiv2MarginBottom, Fixed)); | 451 std::tie(fragment, std::ignore) = RunBlockLayoutAlgorithmForElement( |
322 NGBlockNode* div2 = new NGBlockNode(div2_style.get()); | 452 document().getElementsByTagName("html")->item(0)); |
323 | 453 ASSERT_EQ(1UL, fragment->Children().size()); |
324 div1->SetFirstChild(div2); | 454 body_fragment = toNGPhysicalBoxFragment(fragment->Children()[0]); |
325 | 455 container_fragment = toNGPhysicalBoxFragment(body_fragment->Children()[0]); |
326 auto* space = ConstructConstraintSpace( | 456 ASSERT_EQ(1UL, container_fragment->Children().size()); |
327 kHorizontalTopBottom, TextDirection::kLtr, | 457 child_fragment = toNGPhysicalBoxFragment(container_fragment->Children()[0]); |
328 NGLogicalSize(LayoutUnit(100), NGSizeIndefinite)); | 458 }; |
329 NGPhysicalBoxFragment* frag = RunBlockLayoutAlgorithm(space, div1); | 459 |
330 | 460 // height == auto |
331 // Verify that margins are collapsed. | 461 run_test(Length(Auto)); |
332 EXPECT_EQ( | 462 // Margins are collapsed with the result 200 = std::max(20, 200) |
333 NGDeprecatedMarginStrut({LayoutUnit(0), LayoutUnit(kDiv2MarginBottom)}), | 463 // The fragment size 258 == body's margin 8 + child's height 50 + 200 |
334 frag->MarginStrut()); | 464 EXPECT_EQ(NGPhysicalSize(LayoutUnit(800), LayoutUnit(258)), fragment->Size()); |
335 | 465 // EXPECT_EQ(NGMarginStrut({LayoutUnit(200)}), |
336 // Verify that margins are NOT collapsed. | 466 // container_fragment->EndMarginStrut()); |
337 div1_style->setHeight(Length(kHeight, Fixed)); | 467 |
338 frag = RunBlockLayoutAlgorithm(space, div1); | 468 // height == fixed |
339 EXPECT_EQ( | 469 run_test(Length(50, Fixed)); |
340 NGDeprecatedMarginStrut({LayoutUnit(0), LayoutUnit(kDiv1MarginBottom)}), | 470 // Margins are not collapsed, so fragment still has margins == 20. |
341 frag->MarginStrut()); | 471 // The fragment size 78 == body's margin 8 + child's height 50 + 20 |
472 // EXPECT_EQ(NGPhysicalSize(LayoutUnit(800), LayoutUnit(78)), | |
473 // fragment->Size()); | |
474 // EXPECT_EQ(NGMarginStrut(), container_fragment->EndMarginStrut()); | |
342 } | 475 } |
343 | 476 |
344 // Verifies that 2 adjoining margins are not collapsed if there is padding or | 477 // Verifies that 2 adjoining margins are not collapsed if there is padding or |
345 // border that separates them. | 478 // border that separates them. |
346 // | 479 // TODO(glebl): Enable with new the float/margins collapsing algorithm. |
347 // Test case's HTML representation: | 480 TEST_F(NGBlockLayoutAlgorithmTest, DISABLED_CollapsingMarginsCase4) { |
348 // <div style="margin: 30px 0px; padding: 20px 0px;"> <!-- DIV1 --> | 481 setBodyInnerHTML( |
349 // <div style="margin: 200px 0px; height: 50px;"/> <!-- DIV2 --> | 482 "<style>" |
350 // </div> | 483 " #container {" |
351 // | 484 " margin: 30px 0px;" |
352 // Expected: | 485 " width: 200px;" |
353 // Margins do NOT collapse if there is an interfering padding or border. | 486 " }" |
354 TEST_F(NGBlockLayoutAlgorithmTest, CollapsingMarginsCase4) { | 487 " #child {" |
355 const int kHeight = 50; | 488 " margin: 200px 0px;" |
356 const int kDiv1Margin = 30; | 489 " height: 50px;" |
357 const int kDiv1Padding = 20; | 490 " background-color: blue;" |
358 const int kDiv2Margin = 200; | 491 " }" |
359 | 492 "</style>" |
360 // DIV1 | 493 "<div id='container'>" |
361 RefPtr<ComputedStyle> div1_style = ComputedStyle::create(); | 494 " <div id='child'></div>" |
362 div1_style->setMarginTop(Length(kDiv1Margin, Fixed)); | 495 "</div>"); |
363 div1_style->setMarginBottom(Length(kDiv1Margin, Fixed)); | 496 |
364 div1_style->setPaddingTop(Length(kDiv1Padding, Fixed)); | 497 const NGPhysicalBoxFragment* body_fragment; |
365 div1_style->setPaddingBottom(Length(kDiv1Padding, Fixed)); | 498 const NGPhysicalBoxFragment* container_fragment; |
366 NGBlockNode* div1 = new NGBlockNode(div1_style.get()); | 499 const NGPhysicalBoxFragment* child_fragment; |
367 | 500 const NGPhysicalBoxFragment* fragment; |
368 // DIV2 | 501 auto run_test = [&](const Length& container_padding_top) { |
369 RefPtr<ComputedStyle> div2_style = ComputedStyle::create(); | 502 Element* container = document().getElementById("container"); |
370 div2_style->setHeight(Length(kHeight, Fixed)); | 503 container->mutableComputedStyle()->setPaddingTop(container_padding_top); |
371 div2_style->setMarginTop(Length(kDiv2Margin, Fixed)); | 504 std::tie(fragment, std::ignore) = RunBlockLayoutAlgorithmForElement( |
372 div2_style->setMarginBottom(Length(kDiv2Margin, Fixed)); | 505 document().getElementsByTagName("html")->item(0)); |
373 NGBlockNode* div2 = new NGBlockNode(div2_style.get()); | 506 ASSERT_EQ(1UL, fragment->Children().size()); |
374 | 507 body_fragment = toNGPhysicalBoxFragment(fragment->Children()[0]); |
375 div1->SetFirstChild(div2); | 508 container_fragment = toNGPhysicalBoxFragment(body_fragment->Children()[0]); |
376 | 509 ASSERT_EQ(1UL, container_fragment->Children().size()); |
377 auto* space = ConstructConstraintSpace( | 510 child_fragment = toNGPhysicalBoxFragment(container_fragment->Children()[0]); |
378 kHorizontalTopBottom, TextDirection::kLtr, | 511 }; |
379 NGLogicalSize(LayoutUnit(100), NGSizeIndefinite)); | 512 |
380 NGPhysicalBoxFragment* frag = RunBlockLayoutAlgorithm(space, div1); | 513 // with padding |
381 | 514 run_test(Length(20, Fixed)); |
382 // Verify that margins do NOT collapse. | 515 // 500 = child's height 50 + 2xmargin 400 + paddint-top 20 + |
383 frag = RunBlockLayoutAlgorithm(space, div1); | 516 // container's margin 30 |
384 EXPECT_EQ(NGDeprecatedMarginStrut( | 517 EXPECT_EQ(NGPhysicalSize(LayoutUnit(800), LayoutUnit(500)), fragment->Size()); |
385 {LayoutUnit(kDiv1Margin), LayoutUnit(kDiv1Margin)}), | 518 // 30 = max(body's margin 8, container margin 30) |
386 frag->MarginStrut()); | 519 EXPECT_EQ(LayoutUnit(30), body_fragment->TopOffset()); |
387 ASSERT_EQ(frag->Children().size(), 1UL); | 520 // 220 = container's padding top 20 + child's margin |
388 | 521 EXPECT_EQ(LayoutUnit(220), child_fragment->TopOffset()); |
389 EXPECT_EQ(NGDeprecatedMarginStrut( | 522 |
390 {LayoutUnit(kDiv2Margin), LayoutUnit(kDiv2Margin)}), | 523 // without padding |
391 static_cast<const NGPhysicalBoxFragment*>(frag->Children()[0].get()) | 524 run_test(Length(0, Fixed)); |
392 ->MarginStrut()); | 525 // 450 = 2xmax(body's margin 8, container's margin 30, child's margin 200) + |
393 | 526 // child's height 50 |
394 // Reset padding and verify that margins DO collapse. | 527 EXPECT_EQ(NGPhysicalSize(LayoutUnit(800), LayoutUnit(450)), fragment->Size()); |
395 div1_style->setPaddingTop(Length(0, Fixed)); | 528 // 200 = (body's margin 8, container's margin 30, child's margin 200) |
396 div1_style->setPaddingBottom(Length(0, Fixed)); | 529 EXPECT_EQ(LayoutUnit(200), body_fragment->TopOffset()); |
397 frag = RunBlockLayoutAlgorithm(space, div1); | 530 // 0 = collapsed margins |
398 EXPECT_EQ(NGDeprecatedMarginStrut( | 531 EXPECT_EQ(LayoutUnit(0), child_fragment->TopOffset()); |
399 {LayoutUnit(kDiv2Margin), LayoutUnit(kDiv2Margin)}), | |
400 frag->MarginStrut()); | |
401 } | 532 } |
402 | 533 |
403 // Verifies that margins of 2 adjoining blocks with different writing modes | 534 // Verifies that margins of 2 adjoining blocks with different writing modes |
404 // get collapsed. | 535 // get collapsed. |
405 // | 536 // |
406 // Test case's HTML representation: | 537 // Test case's HTML representation: |
407 // <div style="writing-mode: vertical-lr;"> | 538 // <div style="writing-mode: vertical-lr;"> |
408 // <div style="margin-right: 60px; width: 60px;">vertical</div> | 539 // <div style="margin-right: 60px; width: 60px;">vertical</div> |
409 // <div style="margin-left: 100px; writing-mode: horizontal-tb;"> | 540 // <div style="margin-left: 100px; writing-mode: horizontal-tb;"> |
410 // horizontal | 541 // horizontal |
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
625 EXPECT_EQ(NGPhysicalFragment::kFragmentBox, frag->Type()); | 756 EXPECT_EQ(NGPhysicalFragment::kFragmentBox, frag->Type()); |
626 EXPECT_EQ(LayoutUnit(kWidth + kPaddingLeft), frag->WidthOverflow()); | 757 EXPECT_EQ(LayoutUnit(kWidth + kPaddingLeft), frag->WidthOverflow()); |
627 ASSERT_EQ(1UL, frag->Children().size()); | 758 ASSERT_EQ(1UL, frag->Children().size()); |
628 | 759 |
629 const NGPhysicalFragment* child = frag->Children()[0]; | 760 const NGPhysicalFragment* child = frag->Children()[0]; |
630 EXPECT_EQ(LayoutUnit(kChildWidth), child->Width()); | 761 EXPECT_EQ(LayoutUnit(kChildWidth), child->Width()); |
631 EXPECT_EQ(LayoutUnit(kPaddingLeft + 10), child->LeftOffset()); | 762 EXPECT_EQ(LayoutUnit(kPaddingLeft + 10), child->LeftOffset()); |
632 EXPECT_EQ(LayoutUnit(0), child->TopOffset()); | 763 EXPECT_EQ(LayoutUnit(0), child->TopOffset()); |
633 } | 764 } |
634 | 765 |
635 // Verifies that 3 Left/Right float fragments and one regular block fragment | 766 // Verifies that floats can be correctly positioned if they are inside of nested |
636 // are correctly positioned by the algorithm. | 767 // empty blocks. |
637 // | 768 // TODO(glebl): Enable with new the float/margins collapsing algorithm. |
638 // Test case's HTML representation: | 769 TEST_F(NGBlockLayoutAlgorithmTest, DISABLED_PositionFloatInsideEmptyBlocks) { |
639 // <div id="parent" style="width: 200px; height: 200px;"> | 770 setBodyInnerHTML( |
640 // <div style="float:left; width: 30px; height: 30px; | 771 "<!DOCTYPE html>" |
641 // margin-top: 10px;"/> <!-- DIV1 --> | 772 "<style>" |
642 // <div style="width: 30px; height: 30px;"/> <!-- DIV2 --> | 773 " #container {" |
643 // <div style="float:right; width: 50px; height: 50px;"/> <!-- DIV3 --> | 774 " height: 200px;" |
644 // <div style="float:left; width: 120px; height: 120px; | 775 " width: 200px;" |
645 // margin-left: 30px;"/> <!-- DIV4 --> | 776 " }" |
646 // </div> | 777 " #empty1 {" |
647 // | 778 " margin: 20px;" |
648 // Expected: | 779 " padding: 0 20px;" |
649 // - Left float(DIV1) is positioned at the left. | 780 " }" |
650 // - Regular block (DIV2) is positioned behind DIV1. | 781 " #empty2 {" |
651 // - Right float(DIV3) is positioned at the right below DIV2 | 782 " margin: 15px;" |
652 // - Left float(DIV4) is positioned at the left below DIV3. | 783 " padding: 0 15px;" |
653 TEST_F(NGBlockLayoutAlgorithmTest, PositionFloatFragments) { | 784 " }" |
654 const int kParentLeftPadding = 10; | 785 " #float {" |
655 const int kDiv1TopMargin = 10; | 786 " float: left;" |
656 const int kParentSize = 200; | 787 " height: 5px;" |
657 const int kDiv1Size = 30; | 788 " width: 5px;" |
658 const int kDiv2Size = 30; | 789 " padding: 10px;" |
659 const int kDiv3Size = 50; | 790 " margin: 10px;" |
660 const int kDiv4Size = kParentSize - kDiv3Size; | 791 " background-color: green;" |
661 const int kDiv4LeftMargin = kDiv1Size; | 792 " }" |
662 | 793 "</style>" |
663 style_->setHeight(Length(kParentSize, Fixed)); | 794 "<div id='container'>" |
664 style_->setWidth(Length(kParentSize, Fixed)); | 795 " <div id='empty1'>" |
665 style_->setPaddingLeft(Length(kParentLeftPadding, Fixed)); | 796 " <div id='empty2'>" |
666 | 797 " <div id='float'></div>" |
667 // DIV1 | 798 " </div>" |
668 RefPtr<ComputedStyle> div1_style = ComputedStyle::create(); | 799 " </div>" |
669 div1_style->setWidth(Length(kDiv1Size, Fixed)); | 800 "</div>"); |
670 div1_style->setHeight(Length(kDiv1Size, Fixed)); | 801 |
671 div1_style->setFloating(EFloat::kLeft); | 802 // ** Run LayoutNG algorithm ** |
672 div1_style->setMarginTop(Length(kDiv1TopMargin, Fixed)); | 803 NGConstraintSpace* space; |
673 NGBlockNode* div1 = new NGBlockNode(div1_style.get()); | 804 NGPhysicalBoxFragment* fragment; |
674 | 805 std::tie(fragment, space) = RunBlockLayoutAlgorithmForElement( |
675 // DIV2 | 806 document().getElementsByTagName("html")->item(0)); |
676 RefPtr<ComputedStyle> div2_style = ComputedStyle::create(); | 807 |
677 div2_style->setWidth(Length(kDiv2Size, Fixed)); | 808 auto* body_fragment = toNGPhysicalBoxFragment(fragment->Children()[0]); |
678 div2_style->setHeight(Length(kDiv2Size, Fixed)); | 809 // 20 = std::max(empty1's margin, empty2's margin, body's margin) |
679 NGBlockNode* div2 = new NGBlockNode(div2_style.get()); | 810 int body_top_offset = 20; |
680 | 811 EXPECT_THAT(LayoutUnit(body_top_offset), body_fragment->TopOffset()); |
681 // DIV3 | 812 ASSERT_EQ(1UL, body_fragment->Children().size()); |
682 RefPtr<ComputedStyle> div3_style = ComputedStyle::create(); | 813 auto* container_fragment = |
683 div3_style->setWidth(Length(kDiv3Size, Fixed)); | 814 toNGPhysicalBoxFragment(body_fragment->Children()[0]); |
684 div3_style->setHeight(Length(kDiv3Size, Fixed)); | 815 ASSERT_EQ(1UL, container_fragment->Children().size()); |
685 div3_style->setFloating(EFloat::kRight); | 816 |
686 NGBlockNode* div3 = new NGBlockNode(div3_style.get()); | 817 auto* empty1_fragment = |
687 | 818 toNGPhysicalBoxFragment(container_fragment->Children()[0]); |
688 // DIV4 | 819 // 0, vertical margins got collapsed |
689 RefPtr<ComputedStyle> div4_style = ComputedStyle::create(); | 820 EXPECT_THAT(LayoutUnit(), empty1_fragment->TopOffset()); |
690 div4_style->setWidth(Length(kDiv4Size, Fixed)); | 821 // 20 empty1's margin |
691 div4_style->setHeight(Length(kDiv4Size, Fixed)); | 822 int empty1_inline_offset = 20; |
692 div4_style->setMarginLeft(Length(kDiv4LeftMargin, Fixed)); | 823 EXPECT_THAT(LayoutUnit(empty1_inline_offset), empty1_fragment->LeftOffset()); |
693 div4_style->setFloating(EFloat::kLeft); | 824 ASSERT_EQ(empty1_fragment->Children().size(), 1UL); |
694 NGBlockNode* div4 = new NGBlockNode(div4_style.get()); | 825 |
695 | 826 auto* empty2_fragment = |
696 div1->SetNextSibling(div2); | 827 toNGPhysicalBoxFragment(empty1_fragment->Children()[0]); |
697 div2->SetNextSibling(div3); | 828 // 0, vertical margins got collapsed |
698 div3->SetNextSibling(div4); | 829 EXPECT_THAT(LayoutUnit(), empty2_fragment->TopOffset()); |
699 | 830 // 35 = empty1's padding(20) + empty2's padding(15) |
700 auto* space = ConstructConstraintSpace( | 831 int empty2_inline_offset = 35; |
701 kHorizontalTopBottom, TextDirection::kLtr, | 832 EXPECT_THAT(LayoutUnit(empty2_inline_offset), empty2_fragment->LeftOffset()); |
702 NGLogicalSize(LayoutUnit(kParentSize), LayoutUnit(kParentSize))); | 833 |
703 NGPhysicalBoxFragment* frag = RunBlockLayoutAlgorithm(space, div1); | 834 ASSERT_EQ(1UL, body_fragment->PositionedFloats().size()); |
704 ASSERT_EQ(frag->Children().size(), 4UL); | 835 auto float_fragment = body_fragment->PositionedFloats().at(0)->fragment; |
705 | 836 // 10 = float's padding |
706 // DIV1 | 837 EXPECT_THAT(LayoutUnit(10), float_fragment->TopOffset()); |
707 const NGPhysicalFragment* child1 = frag->Children()[0]; | 838 // 25 = empty2's padding(15) + float's padding(10) |
708 EXPECT_EQ(kDiv1TopMargin, child1->TopOffset()); | 839 int float_inline_offset = 25; |
709 EXPECT_EQ(kParentLeftPadding, child1->LeftOffset()); | 840 EXPECT_THAT(float_fragment->LeftOffset(), LayoutUnit(float_inline_offset)); |
710 | 841 |
711 // DIV2 | 842 // ** Verify layout tree ** |
712 const NGPhysicalFragment* child2 = frag->Children()[1]; | 843 Element* left_float = document().getElementById("float"); |
713 EXPECT_EQ(0, child2->TopOffset()); | 844 // 88 = body's margin(8) + |
714 EXPECT_EQ(kParentLeftPadding, child2->LeftOffset()); | 845 // empty1's padding and margin + empty2's padding and margins + float's |
715 | 846 // padding |
716 // DIV3 | 847 EXPECT_THAT(left_float->offsetLeft(), 88); |
717 const NGPhysicalFragment* child3 = frag->Children()[2]; | 848 // 30 = body_top_offset(collapsed margins result) + float's padding |
718 EXPECT_EQ(kDiv2Size, child3->TopOffset()); | 849 EXPECT_THAT(body_top_offset + 10, left_float->offsetTop()); |
719 EXPECT_EQ(kParentLeftPadding + kParentSize - kDiv3Size, child3->LeftOffset()); | 850 |
720 | 851 // ** Legacy Floating objects ** |
721 // DIV4 | 852 Element* body = document().getElementsByTagName("body")->item(0); |
722 const NGPhysicalFragment* child4 = frag->Children()[3]; | 853 auto& floating_objects = |
723 EXPECT_EQ(kDiv2Size + kDiv3Size, child4->TopOffset()); | 854 const_cast<FloatingObjects*>( |
724 EXPECT_EQ(kParentLeftPadding + kDiv4LeftMargin, child4->LeftOffset()); | 855 toLayoutBlockFlow(body->layoutObject())->floatingObjects()) |
856 ->mutableSet(); | |
857 ASSERT_EQ(1UL, floating_objects.size()); | |
858 auto floating_object = floating_objects.takeFirst(); | |
859 ASSERT_TRUE(floating_object->isPlaced()); | |
860 // 80 = float_inline_offset(25) + accumulative offset of empty blocks(35 + 20) | |
861 EXPECT_THAT(LayoutUnit(80), floating_object->x()); | |
862 // 10 = float's padding | |
863 EXPECT_THAT(LayoutUnit(10), floating_object->y()); | |
725 } | 864 } |
726 | 865 |
866 // Verifies that left/right floating and regular blocks can be positioned | |
867 // correctly by the algorithm. | |
868 // TODO(glebl): Enable with new the float/margins collapsing algorithm. | |
869 TEST_F(NGBlockLayoutAlgorithmTest, DISABLED_PositionFloatFragments) { | |
870 setBodyInnerHTML( | |
871 "<style>" | |
872 " #container {" | |
873 " height: 200px;" | |
874 " width: 200px;" | |
875 " }" | |
876 " #left-float {" | |
877 " background-color: red;" | |
878 " float:left;" | |
879 " height: 30px;" | |
880 " width: 30px;" | |
881 " }" | |
882 " #left-wide-float {" | |
883 " background-color: greenyellow;" | |
884 " float:left;" | |
885 " height: 30px;" | |
886 " width: 180px;" | |
887 " }" | |
888 " #regular {" | |
889 " width: 40px;" | |
890 " height: 40px;" | |
891 " background-color: green;" | |
892 " }" | |
893 " #right-float {" | |
894 " background-color: cyan;" | |
895 " float:right;" | |
896 " width: 50px;" | |
897 " height: 50px;" | |
898 " }" | |
899 " #left-float-with-margin {" | |
900 " background-color: black;" | |
901 " float:left;" | |
902 " height: 120px;" | |
903 " margin: 10px;" | |
904 " width: 120px;" | |
905 " }" | |
906 "</style>" | |
907 "<div id='container'>" | |
908 " <div id='left-float'></div>" | |
909 " <div id='left-wide-float'></div>" | |
910 " <div id='regular'></div>" | |
911 " <div id='right-float'></div>" | |
912 " <div id='left-float-with-margin'></div>" | |
913 "</div>"); | |
914 | |
915 // ** Run LayoutNG algorithm ** | |
916 NGConstraintSpace* space; | |
917 NGPhysicalBoxFragment* fragment; | |
918 std::tie(fragment, space) = RunBlockLayoutAlgorithmForElement( | |
919 document().getElementsByTagName("html")->item(0)); | |
920 | |
921 // ** Verify LayoutNG fragments and the list of positioned floats ** | |
922 EXPECT_THAT(LayoutUnit(), fragment->TopOffset()); | |
923 ASSERT_EQ(1UL, fragment->Children().size()); | |
924 auto* body_fragment = toNGPhysicalBoxFragment(fragment->Children()[0]); | |
925 EXPECT_THAT(LayoutUnit(8), body_fragment->TopOffset()); | |
926 auto* container_fragment = | |
927 toNGPhysicalBoxFragment(body_fragment->Children()[0]); | |
928 ASSERT_EQ(1UL, container_fragment->Children().size()); | |
929 ASSERT_EQ(4UL, container_fragment->PositionedFloats().size()); | |
930 | |
931 // ** Verify layout tree ** | |
932 Element* left_float = document().getElementById("left-float"); | |
933 // 8 = body's margin-top | |
934 int left_float_block_offset = 8; | |
935 EXPECT_EQ(left_float_block_offset, left_float->offsetTop()); | |
936 auto left_float_fragment = | |
937 container_fragment->PositionedFloats().at(0)->fragment; | |
938 EXPECT_THAT(LayoutUnit(), left_float_fragment->TopOffset()); | |
939 | |
940 Element* left_wide_float = document().getElementById("left-wide-float"); | |
941 // left-wide-float is positioned right below left-float as it's too wide. | |
942 // 38 = left_float_block_offset + | |
943 // left-float's height 30 | |
944 int left_wide_float_block_offset = 38; | |
945 EXPECT_EQ(left_wide_float_block_offset, left_wide_float->offsetTop()); | |
946 auto left_wide_float_fragment = | |
947 container_fragment->PositionedFloats().at(1)->fragment; | |
948 // 30 = left-float's height. | |
949 EXPECT_THAT(LayoutUnit(30), left_wide_float_fragment->TopOffset()); | |
950 | |
951 Element* regular = document().getElementById("regular"); | |
952 // regular_block_offset = body's margin-top 8 | |
953 int regular_block_offset = 8; | |
954 EXPECT_EQ(regular_block_offset, regular->offsetTop()); | |
955 auto* regular_block_fragment = | |
956 toNGPhysicalBoxFragment(container_fragment->Children()[0]); | |
957 EXPECT_THAT(LayoutUnit(), regular_block_fragment->TopOffset()); | |
958 | |
959 Element* right_float = document().getElementById("right-float"); | |
960 // 158 = body's margin-left 8 + container's width 200 - right_float's width 50 | |
961 int right_float_inline_offset = 158; | |
962 // it's positioned right after our left_wide_float | |
963 // 68 = left_wide_float_block_offset 38 + left-wide-float's height 30 | |
964 int right_float_block_offset = left_wide_float_block_offset + 30; | |
965 EXPECT_EQ(right_float_inline_offset, right_float->offsetLeft()); | |
966 EXPECT_EQ(right_float_block_offset, right_float->offsetTop()); | |
967 auto right_float_fragment = | |
968 container_fragment->PositionedFloats().at(2)->fragment; | |
969 // 60 = right_float_block_offset(68) - body's margin(8) | |
970 EXPECT_THAT(LayoutUnit(right_float_block_offset - 8), | |
971 right_float_fragment->TopOffset()); | |
972 // 150 = right_float_inline_offset(158) - body's margin(8) | |
973 EXPECT_THAT(LayoutUnit(right_float_inline_offset - 8), | |
974 right_float_fragment->LeftOffset()); | |
975 | |
976 Element* left_float_with_margin = | |
977 document().getElementById("left-float-with-margin"); | |
978 // 18 = body's margin(8) + left-float-with-margin's margin(10) | |
979 int left_float_with_margin_inline_offset = 18; | |
980 EXPECT_EQ(left_float_with_margin_inline_offset, | |
981 left_float_with_margin->offsetLeft()); | |
982 // 78 = left_wide_float_block_offset 38 + left-wide-float's height 30 + | |
983 // left-float-with-margin's margin(10) | |
984 int left_float_with_margin_block_offset = 78; | |
985 EXPECT_EQ(left_float_with_margin_block_offset, | |
986 left_float_with_margin->offsetTop()); | |
987 auto left_float_with_margin_fragment = | |
988 container_fragment->PositionedFloats().at(3)->fragment; | |
989 // 70 = left_float_with_margin_block_offset(78) - body's margin(8) | |
990 EXPECT_THAT(LayoutUnit(left_float_with_margin_block_offset - 8), | |
991 left_float_with_margin_fragment->TopOffset()); | |
992 // 10 = left_float_with_margin_inline_offset(18) - body's margin(8) | |
993 EXPECT_THAT(LayoutUnit(left_float_with_margin_inline_offset - 8), | |
994 left_float_with_margin_fragment->LeftOffset()); | |
995 | |
996 // ** Verify exclusions ** | |
997 NGLogicalSize left_float_exclusion_size = {LayoutUnit(30), LayoutUnit(30)}; | |
998 // this should be equal to body's margin(8) | |
999 NGLogicalOffset left_float_exclusion_offset = {LayoutUnit(8), LayoutUnit(8)}; | |
1000 NGLogicalRect left_float_exclusion_rect = {left_float_exclusion_offset, | |
1001 left_float_exclusion_size}; | |
1002 NGExclusion left_float_exclusion = {left_float_exclusion_rect, | |
1003 NGExclusion::kFloatLeft}; | |
1004 | |
1005 NGLogicalSize left_wide_exclusion_size = {LayoutUnit(180), LayoutUnit(30)}; | |
1006 NGLogicalOffset left_wide_exclusion_offset = { | |
1007 LayoutUnit(8), LayoutUnit(left_wide_float_block_offset)}; | |
1008 NGLogicalRect left_wide_exclusion_rect = {left_wide_exclusion_offset, | |
1009 left_wide_exclusion_size}; | |
1010 NGExclusion left_wide_exclusion = {left_wide_exclusion_rect, | |
1011 NGExclusion::kFloatLeft}; | |
1012 | |
1013 NGLogicalSize right_float_exclusion_size = {LayoutUnit(50), LayoutUnit(50)}; | |
1014 NGLogicalOffset right_float_exclusion_offset = { | |
1015 LayoutUnit(right_float_inline_offset), | |
1016 LayoutUnit(right_float_block_offset)}; | |
1017 NGLogicalRect right_float_exclusion_rect = {right_float_exclusion_offset, | |
1018 right_float_exclusion_size}; | |
1019 NGExclusion right_float_exclusion = {right_float_exclusion_rect, | |
1020 NGExclusion::kFloatRight}; | |
1021 | |
1022 // left-float-with-margin's size(120) + margin(2x10) | |
1023 NGLogicalSize left_float_with_margin_exclusion_size = {LayoutUnit(140), | |
1024 LayoutUnit(140)}; | |
1025 // Exclusion starts from the right_float_block_offset position. | |
1026 NGLogicalOffset left_float_with_margin_exclusion_offset = { | |
1027 LayoutUnit(8), LayoutUnit(right_float_block_offset)}; | |
1028 NGLogicalRect left_float_with_margin_exclusion_rect = { | |
1029 left_float_with_margin_exclusion_offset, | |
1030 left_float_with_margin_exclusion_size}; | |
1031 NGExclusion left_float_with_margin_exclusion = { | |
1032 left_float_with_margin_exclusion_rect, NGExclusion::kFloatLeft}; | |
1033 | |
1034 EXPECT_THAT( | |
1035 space->Exclusions()->storage, | |
1036 (ElementsAre(Pointee(left_float_exclusion), Pointee(left_wide_exclusion), | |
1037 Pointee(right_float_exclusion), | |
1038 Pointee(left_float_with_margin_exclusion)))); | |
1039 } | |
1040 | |
727 // Verifies that NG block layout algorithm respects "clear" CSS property. | 1041 // Verifies that NG block layout algorithm respects "clear" CSS property. |
728 // | 1042 // TODO(glebl): Enable with new the float/margins collapsing algorithm. |
729 // Test case's HTML representation: | 1043 TEST_F(NGBlockLayoutAlgorithmTest, DISABLED_PositionFragmentsWithClear) { |
730 // <div id="parent" style="width: 200px; height: 200px;"> | 1044 setBodyInnerHTML( |
731 // <div style="float: left; width: 30px; height: 30px;"/> <!-- DIV1 --> | 1045 "<style>" |
732 // <div style="float: right; width: 40px; height: 40px; | 1046 " #container {" |
733 // clear: left;"/> <!-- DIV2 --> | 1047 " height: 200px;" |
734 // <div style="clear: ...; width: 50px; height: 50px;"/> <!-- DIV3 --> | 1048 " width: 200px;" |
735 // </div> | 1049 " }" |
736 // | 1050 " #float-left {" |
737 // Expected: | 1051 " background-color: red;" |
738 // - DIV2 is positioned below DIV1 because it has clear: left; | 1052 " float: left;" |
739 // - DIV3 is positioned below DIV1 if clear: left; | 1053 " height: 30px;" |
740 // - DIV3 is positioned below DIV2 if clear: right; | 1054 " width: 30px;" |
741 // - DIV3 is positioned below DIV2 if clear: both; | 1055 " }" |
742 TEST_F(NGBlockLayoutAlgorithmTest, PositionFragmentsWithClear) { | 1056 " #float-right {" |
743 const int kParentSize = 200; | 1057 " background-color: blue;" |
744 const int kDiv1Size = 30; | 1058 " float: right;" |
745 const int kDiv2Size = 40; | 1059 " height: 170px;" |
746 const int kDiv3Size = 50; | 1060 " width: 40px;" |
747 | 1061 " }" |
748 style_->setHeight(Length(kParentSize, Fixed)); | 1062 " #clearance {" |
749 style_->setWidth(Length(kParentSize, Fixed)); | 1063 " background-color: yellow;" |
750 | 1064 " height: 60px;" |
751 // DIV1 | 1065 " width: 60px;" |
752 RefPtr<ComputedStyle> div1_style = ComputedStyle::create(); | 1066 " margin: 20px;" |
753 div1_style->setWidth(Length(kDiv1Size, Fixed)); | 1067 " }" |
754 div1_style->setHeight(Length(kDiv1Size, Fixed)); | 1068 " #block {" |
755 div1_style->setFloating(EFloat::kLeft); | 1069 " margin: 40px;" |
756 NGBlockNode* div1 = new NGBlockNode(div1_style.get()); | 1070 " background-color: black;" |
757 | 1071 " height: 60px;" |
758 // DIV2 | 1072 " width: 60px;" |
759 RefPtr<ComputedStyle> div2_style = ComputedStyle::create(); | 1073 " }" |
760 div2_style->setWidth(Length(kDiv2Size, Fixed)); | 1074 " #adjoining-clearance {" |
761 div2_style->setHeight(Length(kDiv2Size, Fixed)); | 1075 " background-color: green;" |
762 div2_style->setClear(EClear::ClearLeft); | 1076 " clear: left;" |
763 div2_style->setFloating(EFloat::kRight); | 1077 " height: 20px;" |
764 NGBlockNode* div2 = new NGBlockNode(div2_style.get()); | 1078 " width: 20px;" |
765 | 1079 " margin: 30px;" |
766 // DIV3 | 1080 " }" |
767 RefPtr<ComputedStyle> div3_style = ComputedStyle::create(); | 1081 "</style>" |
768 div3_style->setWidth(Length(kDiv3Size, Fixed)); | 1082 "<div id='container'>" |
769 div3_style->setHeight(Length(kDiv3Size, Fixed)); | 1083 " <div id='float-left'></div>" |
770 NGBlockNode* div3 = new NGBlockNode(div3_style.get()); | 1084 " <div id='float-right'></div>" |
771 | 1085 " <div id='clearance'></div>" |
772 div1->SetNextSibling(div2); | 1086 " <div id='block'></div>" |
773 div2->SetNextSibling(div3); | 1087 " <div id='adjoining-clearance'></div>" |
774 | 1088 "</div>"); |
775 // clear: left; | 1089 |
776 div3_style->setClear(EClear::ClearLeft); | 1090 const NGPhysicalBoxFragment* clerance_fragment; |
777 auto* space = ConstructConstraintSpace( | 1091 const NGPhysicalBoxFragment* body_fragment; |
778 kHorizontalTopBottom, TextDirection::kLtr, | 1092 const NGPhysicalBoxFragment* container_fragment; |
779 NGLogicalSize(LayoutUnit(kParentSize), LayoutUnit(kParentSize))); | 1093 const NGPhysicalBoxFragment* block_fragment; |
780 NGPhysicalBoxFragment* frag = RunBlockLayoutAlgorithm(space, div1); | 1094 const NGPhysicalBoxFragment* adjoining_clearance_fragment; |
781 const NGPhysicalFragment* child3 = frag->Children()[2]; | 1095 auto run_with_clearance = [&](EClear clear_value) { |
782 EXPECT_EQ(kDiv1Size, child3->TopOffset()); | 1096 NGPhysicalBoxFragment* fragment; |
783 | 1097 Element* el_with_clear = document().getElementById("clearance"); |
784 // clear: right; | 1098 el_with_clear->mutableComputedStyle()->setClear(clear_value); |
785 div3_style->setClear(EClear::ClearRight); | 1099 std::tie(fragment, std::ignore) = RunBlockLayoutAlgorithmForElement( |
786 space = ConstructConstraintSpace( | 1100 document().getElementsByTagName("html")->item(0)); |
787 kHorizontalTopBottom, TextDirection::kLtr, | 1101 ASSERT_EQ(1UL, fragment->Children().size()); |
788 NGLogicalSize(LayoutUnit(kParentSize), LayoutUnit(kParentSize))); | 1102 body_fragment = toNGPhysicalBoxFragment(fragment->Children()[0]); |
789 frag = RunBlockLayoutAlgorithm(space, div1); | 1103 container_fragment = toNGPhysicalBoxFragment(body_fragment->Children()[0]); |
790 child3 = frag->Children()[2]; | 1104 ASSERT_EQ(3UL, container_fragment->Children().size()); |
791 EXPECT_EQ(kDiv1Size + kDiv2Size, child3->TopOffset()); | 1105 clerance_fragment = |
792 | 1106 toNGPhysicalBoxFragment(container_fragment->Children()[0]); |
793 // clear: both; | 1107 block_fragment = toNGPhysicalBoxFragment(container_fragment->Children()[1]); |
794 div3_style->setClear(EClear::ClearBoth); | 1108 adjoining_clearance_fragment = |
795 space = ConstructConstraintSpace( | 1109 toNGPhysicalBoxFragment(container_fragment->Children()[2]); |
796 kHorizontalTopBottom, TextDirection::kLtr, | 1110 }; |
797 NGLogicalSize(LayoutUnit(kParentSize), LayoutUnit(kParentSize))); | 1111 |
798 frag = RunBlockLayoutAlgorithm(space, div1); | 1112 // clear: none |
799 space = ConstructConstraintSpace( | 1113 run_with_clearance(EClear::ClearNone); |
800 kHorizontalTopBottom, TextDirection::kLtr, | 1114 // 20 = std::max(body's margin 8, clearance's margins 20) |
801 NGLogicalSize(LayoutUnit(kParentSize), LayoutUnit(kParentSize))); | 1115 EXPECT_EQ(LayoutUnit(20), body_fragment->TopOffset()); |
802 child3 = frag->Children()[2]; | 1116 EXPECT_EQ(LayoutUnit(0), container_fragment->TopOffset()); |
803 EXPECT_EQ(kDiv1Size + kDiv2Size, child3->TopOffset()); | 1117 // 0 = collapsed margins |
1118 EXPECT_EQ(LayoutUnit(0), clerance_fragment->TopOffset()); | |
1119 // 100 = clearance's height 60 + | |
1120 // std::max(clearance's margins 20, block's margins 40) | |
1121 EXPECT_EQ(LayoutUnit(100), block_fragment->TopOffset()); | |
1122 // 200 = 100 + block's height 60 + max(adjoining_clearance's margins 30, | |
1123 // block's margins 40) | |
1124 EXPECT_EQ(LayoutUnit(200), adjoining_clearance_fragment->TopOffset()); | |
1125 | |
1126 // clear: right | |
1127 run_with_clearance(EClear::ClearRight); | |
1128 // 8 = body's margin. This doesn't collapse its margins with 'clearance' block | |
1129 // as it's not an adjoining block to body. | |
1130 EXPECT_EQ(LayoutUnit(8), body_fragment->TopOffset()); | |
1131 EXPECT_EQ(LayoutUnit(0), container_fragment->TopOffset()); | |
1132 // 170 = float-right's height | |
1133 EXPECT_EQ(LayoutUnit(170), clerance_fragment->TopOffset()); | |
1134 // 270 = float-right's height + clearance's height 60 + | |
1135 // max(clearance's margin 20, block margin 40) | |
1136 EXPECT_EQ(LayoutUnit(270), block_fragment->TopOffset()); | |
1137 // 370 = block's offset 270 + block's height 60 + | |
1138 // std::max(block's margin 40, adjoining_clearance's margin 30) | |
1139 EXPECT_EQ(LayoutUnit(370), adjoining_clearance_fragment->TopOffset()); | |
1140 | |
1141 // clear: left | |
1142 run_with_clearance(EClear::ClearLeft); | |
1143 // 8 = body's margin. This doesn't collapse its margins with 'clearance' block | |
1144 // as it's not an adjoining block to body. | |
1145 EXPECT_EQ(LayoutUnit(8), body_fragment->TopOffset()); | |
1146 EXPECT_EQ(LayoutUnit(0), container_fragment->TopOffset()); | |
1147 // 30 = float_left's height | |
1148 EXPECT_EQ(LayoutUnit(30), clerance_fragment->TopOffset()); | |
1149 // 130 = float_left's height + clearance's height 60 + | |
1150 // max(clearance's margin 20, block margin 40) | |
1151 EXPECT_EQ(LayoutUnit(130), block_fragment->TopOffset()); | |
1152 // 230 = block's offset 130 + block's height 60 + | |
1153 // std::max(block's margin 40, adjoining_clearance's margin 30) | |
1154 EXPECT_EQ(LayoutUnit(230), adjoining_clearance_fragment->TopOffset()); | |
1155 | |
1156 // clear: both | |
1157 // same as clear: right | |
1158 run_with_clearance(EClear::ClearBoth); | |
1159 EXPECT_EQ(LayoutUnit(8), body_fragment->TopOffset()); | |
1160 EXPECT_EQ(LayoutUnit(0), container_fragment->TopOffset()); | |
1161 EXPECT_EQ(LayoutUnit(170), clerance_fragment->TopOffset()); | |
1162 EXPECT_EQ(LayoutUnit(270), block_fragment->TopOffset()); | |
1163 EXPECT_EQ(LayoutUnit(370), adjoining_clearance_fragment->TopOffset()); | |
804 } | 1164 } |
805 | 1165 |
806 // Verifies that we compute the right min and max-content size. | 1166 // Verifies that we compute the right min and max-content size. |
807 TEST_F(NGBlockLayoutAlgorithmTest, ComputeMinMaxContent) { | 1167 TEST_F(NGBlockLayoutAlgorithmTest, ComputeMinMaxContent) { |
808 const int kWidth = 50; | 1168 const int kWidth = 50; |
809 const int kWidthChild1 = 20; | 1169 const int kWidthChild1 = 20; |
810 const int kWidthChild2 = 30; | 1170 const int kWidthChild2 = 30; |
811 | 1171 |
812 // This should have no impact on the min/max content size. | 1172 // This should have no impact on the min/max content size. |
813 style_->setWidth(Length(kWidth, Fixed)); | 1173 style_->setWidth(Length(kWidth, Fixed)); |
(...skipping 749 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1563 EXPECT_EQ(LayoutUnit(194), fragment->LeftOffset()); | 1923 EXPECT_EQ(LayoutUnit(194), fragment->LeftOffset()); |
1564 EXPECT_EQ(LayoutUnit(), fragment->TopOffset()); | 1924 EXPECT_EQ(LayoutUnit(), fragment->TopOffset()); |
1565 EXPECT_EQ(LayoutUnit(16), fragment->Width()); | 1925 EXPECT_EQ(LayoutUnit(16), fragment->Width()); |
1566 EXPECT_EQ(LayoutUnit(50), fragment->Height()); | 1926 EXPECT_EQ(LayoutUnit(50), fragment->Height()); |
1567 EXPECT_EQ(0UL, fragment->Children().size()); | 1927 EXPECT_EQ(0UL, fragment->Children().size()); |
1568 EXPECT_FALSE(iterator.NextChild()); | 1928 EXPECT_FALSE(iterator.NextChild()); |
1569 } | 1929 } |
1570 | 1930 |
1571 } // namespace | 1931 } // namespace |
1572 } // namespace blink | 1932 } // namespace blink |
OLD | NEW |