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