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/layout/ng/ng_box.h" | 7 #include "core/layout/ng/ng_box.h" |
8 #include "core/layout/ng/ng_constraint_space.h" | 8 #include "core/layout/ng/ng_constraint_space.h" |
9 #include "core/layout/ng/ng_physical_fragment.h" | 9 #include "core/layout/ng/ng_physical_fragment.h" |
10 #include "core/layout/ng/ng_length_utils.h" | 10 #include "core/layout/ng/ng_length_utils.h" |
| 11 #include "core/layout/ng/ng_units.h" |
11 #include "core/style/ComputedStyle.h" | 12 #include "core/style/ComputedStyle.h" |
12 #include "testing/gtest/include/gtest/gtest.h" | 13 #include "testing/gtest/include/gtest/gtest.h" |
13 | 14 |
14 namespace blink { | 15 namespace blink { |
15 namespace { | 16 namespace { |
16 | 17 |
17 class NGBlockLayoutAlgorithmTest : public ::testing::Test { | 18 class NGBlockLayoutAlgorithmTest : public ::testing::Test { |
18 protected: | 19 protected: |
19 void SetUp() override { style_ = ComputedStyle::create(); } | 20 void SetUp() override { style_ = ComputedStyle::create(); } |
20 | 21 |
| 22 NGPhysicalFragment* RunBlockLayoutAlgorithm(const NGConstraintSpace* space, |
| 23 NGBox* first_child) { |
| 24 NGBlockLayoutAlgorithm algorithm(style_, first_child); |
| 25 NGPhysicalFragment* frag; |
| 26 while (!algorithm.Layout(space, &frag)) |
| 27 continue; |
| 28 return frag; |
| 29 } |
| 30 |
21 RefPtr<ComputedStyle> style_; | 31 RefPtr<ComputedStyle> style_; |
22 }; | 32 }; |
23 | 33 |
24 TEST_F(NGBlockLayoutAlgorithmTest, FixedSize) { | 34 TEST_F(NGBlockLayoutAlgorithmTest, FixedSize) { |
25 style_->setWidth(Length(30, Fixed)); | 35 style_->setWidth(Length(30, Fixed)); |
26 style_->setHeight(Length(40, Fixed)); | 36 style_->setHeight(Length(40, Fixed)); |
27 | 37 |
28 NGConstraintSpace* space = new NGConstraintSpace( | 38 auto* space = new NGConstraintSpace( |
29 HorizontalTopBottom, NGLogicalSize(LayoutUnit(100), NGSizeIndefinite)); | 39 HorizontalTopBottom, NGLogicalSize(LayoutUnit(100), NGSizeIndefinite)); |
| 40 NGPhysicalFragment* frag = RunBlockLayoutAlgorithm(space, nullptr); |
30 | 41 |
31 NGBlockLayoutAlgorithm algorithm(style_, nullptr); | |
32 NGPhysicalFragment* frag; | |
33 while (!algorithm.Layout(space, &frag)) | |
34 ; | |
35 EXPECT_EQ(frag->Width(), LayoutUnit(30)); | 42 EXPECT_EQ(frag->Width(), LayoutUnit(30)); |
36 EXPECT_EQ(frag->Height(), LayoutUnit(40)); | 43 EXPECT_EQ(frag->Height(), LayoutUnit(40)); |
37 } | 44 } |
38 | 45 |
39 // Verifies that two children are laid out with the correct size and position. | 46 // Verifies that two children are laid out with the correct size and position. |
40 TEST_F(NGBlockLayoutAlgorithmTest, LayoutBlockChildren) { | 47 TEST_F(NGBlockLayoutAlgorithmTest, LayoutBlockChildren) { |
41 const int kWidth = 30; | 48 const int kWidth = 30; |
42 const int kHeight1 = 20; | 49 const int kHeight1 = 20; |
43 const int kHeight2 = 30; | 50 const int kHeight2 = 30; |
44 const int kMarginTop = 5; | 51 const int kMarginTop = 5; |
45 const int kMarginBottom = 20; | 52 const int kMarginBottom = 20; |
46 style_->setWidth(Length(kWidth, Fixed)); | 53 style_->setWidth(Length(kWidth, Fixed)); |
47 | 54 |
48 NGConstraintSpace* space = new NGConstraintSpace( | |
49 HorizontalTopBottom, NGLogicalSize(LayoutUnit(100), NGSizeIndefinite)); | |
50 | |
51 RefPtr<ComputedStyle> first_style = ComputedStyle::create(); | 55 RefPtr<ComputedStyle> first_style = ComputedStyle::create(); |
52 first_style->setHeight(Length(kHeight1, Fixed)); | 56 first_style->setHeight(Length(kHeight1, Fixed)); |
53 NGBox* first_child = new NGBox(first_style.get()); | 57 NGBox* first_child = new NGBox(first_style.get()); |
54 | 58 |
55 RefPtr<ComputedStyle> second_style = ComputedStyle::create(); | 59 RefPtr<ComputedStyle> second_style = ComputedStyle::create(); |
56 second_style->setHeight(Length(kHeight2, Fixed)); | 60 second_style->setHeight(Length(kHeight2, Fixed)); |
57 second_style->setMarginTop(Length(kMarginTop, Fixed)); | 61 second_style->setMarginTop(Length(kMarginTop, Fixed)); |
58 second_style->setMarginBottom(Length(kMarginBottom, Fixed)); | 62 second_style->setMarginBottom(Length(kMarginBottom, Fixed)); |
59 NGBox* second_child = new NGBox(second_style.get()); | 63 NGBox* second_child = new NGBox(second_style.get()); |
60 | 64 |
61 first_child->SetNextSibling(second_child); | 65 first_child->SetNextSibling(second_child); |
62 | 66 |
63 NGBlockLayoutAlgorithm algorithm(style_, first_child); | 67 auto* space = new NGConstraintSpace( |
64 NGPhysicalFragment* frag; | 68 HorizontalTopBottom, NGLogicalSize(LayoutUnit(100), NGSizeIndefinite)); |
65 while (!algorithm.Layout(space, &frag)) | 69 NGPhysicalFragment* frag = RunBlockLayoutAlgorithm(space, first_child); |
66 ; | 70 |
67 EXPECT_EQ(frag->Width(), LayoutUnit(kWidth)); | 71 EXPECT_EQ(frag->Width(), LayoutUnit(kWidth)); |
68 EXPECT_EQ(frag->Height(), LayoutUnit(kHeight1 + kHeight2 + kMarginTop)); | 72 EXPECT_EQ(frag->Height(), LayoutUnit(kHeight1 + kHeight2 + kMarginTop)); |
69 EXPECT_EQ(frag->Type(), NGPhysicalFragmentBase::FragmentBox); | 73 EXPECT_EQ(frag->Type(), NGPhysicalFragmentBase::FragmentBox); |
70 ASSERT_EQ(frag->Children().size(), 2UL); | 74 ASSERT_EQ(frag->Children().size(), 2UL); |
71 | 75 |
72 const NGPhysicalFragmentBase* child = frag->Children()[0]; | 76 const NGPhysicalFragmentBase* child = frag->Children()[0]; |
73 EXPECT_EQ(child->Height(), kHeight1); | 77 EXPECT_EQ(child->Height(), kHeight1); |
74 EXPECT_EQ(child->TopOffset(), 0); | 78 EXPECT_EQ(child->TopOffset(), 0); |
75 | 79 |
76 child = frag->Children()[1]; | 80 child = frag->Children()[1]; |
(...skipping 22 matching lines...) Expand all Loading... |
99 div1_style->setMarginTop(Length(kDiv1MarginTop, Fixed)); | 103 div1_style->setMarginTop(Length(kDiv1MarginTop, Fixed)); |
100 NGBox* div1 = new NGBox(div1_style.get()); | 104 NGBox* div1 = new NGBox(div1_style.get()); |
101 | 105 |
102 // DIV2 | 106 // DIV2 |
103 RefPtr<ComputedStyle> div2_style = ComputedStyle::create(); | 107 RefPtr<ComputedStyle> div2_style = ComputedStyle::create(); |
104 div2_style->setMarginTop(Length(kDiv2MarginTop, Fixed)); | 108 div2_style->setMarginTop(Length(kDiv2MarginTop, Fixed)); |
105 NGBox* div2 = new NGBox(div2_style.get()); | 109 NGBox* div2 = new NGBox(div2_style.get()); |
106 | 110 |
107 div1->SetFirstChild(div2); | 111 div1->SetFirstChild(div2); |
108 | 112 |
109 NGConstraintSpace* space = new NGConstraintSpace( | 113 auto* space = new NGConstraintSpace( |
110 HorizontalTopBottom, NGLogicalSize(LayoutUnit(100), NGSizeIndefinite)); | 114 HorizontalTopBottom, NGLogicalSize(LayoutUnit(100), NGSizeIndefinite)); |
111 NGBlockLayoutAlgorithm algorithm(style_, div1); | 115 NGPhysicalFragment* frag = RunBlockLayoutAlgorithm(space, div1); |
112 NGPhysicalFragment* frag; | |
113 while (!algorithm.Layout(space, &frag)) | |
114 ; | |
115 | 116 |
116 EXPECT_EQ(frag->MarginStrut().margin_block_start, kDiv1MarginTop); | 117 EXPECT_EQ(frag->MarginStrut(), NGMarginStrut({LayoutUnit(kDiv1MarginTop)})); |
117 ASSERT_EQ(frag->Children().size(), 1UL); | 118 ASSERT_EQ(frag->Children().size(), 1UL); |
118 const NGPhysicalFragmentBase* div2_fragment = frag->Children()[0]; | 119 const NGPhysicalFragmentBase* div2_fragment = frag->Children()[0]; |
119 EXPECT_EQ(div2_fragment->MarginStrut().margin_block_start, kDiv2MarginTop); | 120 EXPECT_EQ(div2_fragment->MarginStrut(), |
| 121 NGMarginStrut({LayoutUnit(kDiv2MarginTop)})); |
120 } | 122 } |
121 | 123 |
122 // Verifies the collapsing margins case for the next pair: | 124 // Verifies the collapsing margins case for the next pair: |
123 // - bottom margin of box and top margin of its next in-flow following sibling. | 125 // - bottom margin of box and top margin of its next in-flow following sibling. |
124 // | 126 // |
125 // Test case's HTML representation: | 127 // Test case's HTML representation: |
126 // <div style="margin-bottom: 20px; height: 50px;"> <!-- DIV1 --> | 128 // <div style="margin-bottom: 20px; height: 50px;"> <!-- DIV1 --> |
127 // <div style="margin-bottom: -15px"></div> <!-- DIV2 --> | 129 // <div style="margin-bottom: -15px"></div> <!-- DIV2 --> |
128 // </div> | 130 // </div> |
129 // <div style="margin-top: 10px; height: 50px;"> <!-- DIV3 --> | 131 // <div style="margin-top: 10px; height: 50px;"> <!-- DIV3 --> |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
161 | 163 |
162 // DIV4 | 164 // DIV4 |
163 RefPtr<ComputedStyle> div4_style = ComputedStyle::create(); | 165 RefPtr<ComputedStyle> div4_style = ComputedStyle::create(); |
164 div4_style->setMarginTop(Length(kDiv4MarginTop, Fixed)); | 166 div4_style->setMarginTop(Length(kDiv4MarginTop, Fixed)); |
165 NGBox* div4 = new NGBox(div4_style.get()); | 167 NGBox* div4 = new NGBox(div4_style.get()); |
166 | 168 |
167 div1->SetFirstChild(div2); | 169 div1->SetFirstChild(div2); |
168 div3->SetFirstChild(div4); | 170 div3->SetFirstChild(div4); |
169 div1->SetNextSibling(div3); | 171 div1->SetNextSibling(div3); |
170 | 172 |
171 NGConstraintSpace* space = new NGConstraintSpace( | 173 auto* space = new NGConstraintSpace( |
172 HorizontalTopBottom, NGLogicalSize(LayoutUnit(100), NGSizeIndefinite)); | 174 HorizontalTopBottom, NGLogicalSize(LayoutUnit(100), NGSizeIndefinite)); |
173 NGBlockLayoutAlgorithm algorithm(style_, div1); | 175 NGPhysicalFragment* frag = RunBlockLayoutAlgorithm(space, div1); |
174 NGPhysicalFragment* frag; | |
175 while (!algorithm.Layout(space, &frag)) | |
176 ; | |
177 | 176 |
178 ASSERT_EQ(frag->Children().size(), 2UL); | 177 ASSERT_EQ(frag->Children().size(), 2UL); |
179 | 178 |
180 const NGPhysicalFragmentBase* child = frag->Children()[0]; | 179 const NGPhysicalFragmentBase* child = frag->Children()[0]; |
181 EXPECT_EQ(child->Height(), kHeight); | 180 EXPECT_EQ(child->Height(), kHeight); |
182 EXPECT_EQ(child->TopOffset(), 0); | 181 EXPECT_EQ(child->TopOffset(), 0); |
183 | 182 |
184 child = frag->Children()[1]; | 183 child = frag->Children()[1]; |
185 EXPECT_EQ(child->Height(), kHeight); | 184 EXPECT_EQ(child->Height(), kHeight); |
186 EXPECT_EQ(child->TopOffset(), kHeight + kExpectedCollapsedMargin); | 185 EXPECT_EQ(child->TopOffset(), kHeight + kExpectedCollapsedMargin); |
187 } | 186 } |
188 | 187 |
| 188 // Verifies the collapsing margins case for the next pair: |
| 189 // - bottom margin of a last in-flow child and bottom margin of its parent if |
| 190 // the parent has 'auto' computed height |
| 191 // |
| 192 // Test case's HTML representation: |
| 193 // <div style="margin-bottom: 20px; height: 50px;"> <!-- DIV1 --> |
| 194 // <div style="margin-bottom: 200px; height: 50px;"/> <!-- DIV2 --> |
| 195 // </div> |
| 196 // |
| 197 // Expected: |
| 198 // 1) Margins are collapsed with the result = std::max(20, 200) |
| 199 // if DIV1.height == auto |
| 200 // 2) Margins are NOT collapsed if DIV1.height != auto |
| 201 TEST_F(NGBlockLayoutAlgorithmTest, CollapsingMarginsCase3) { |
| 202 const int kHeight = 50; |
| 203 const int kDiv1MarginBottom = 20; |
| 204 const int kDiv2MarginBottom = 200; |
| 205 |
| 206 // DIV1 |
| 207 RefPtr<ComputedStyle> div1_style = ComputedStyle::create(); |
| 208 div1_style->setMarginBottom(Length(kDiv1MarginBottom, Fixed)); |
| 209 NGBox* div1 = new NGBox(div1_style.get()); |
| 210 |
| 211 // DIV2 |
| 212 RefPtr<ComputedStyle> div2_style = ComputedStyle::create(); |
| 213 div2_style->setHeight(Length(kHeight, Fixed)); |
| 214 div2_style->setMarginBottom(Length(kDiv2MarginBottom, Fixed)); |
| 215 NGBox* div2 = new NGBox(div2_style.get()); |
| 216 |
| 217 div1->SetFirstChild(div2); |
| 218 |
| 219 auto* space = new NGConstraintSpace( |
| 220 HorizontalTopBottom, NGLogicalSize(LayoutUnit(100), NGSizeIndefinite)); |
| 221 NGPhysicalFragment* frag = RunBlockLayoutAlgorithm(space, div1); |
| 222 |
| 223 // Verify that margins are collapsed. |
| 224 EXPECT_EQ(frag->MarginStrut(), |
| 225 NGMarginStrut({LayoutUnit(0), LayoutUnit(kDiv2MarginBottom)})); |
| 226 |
| 227 // Verify that margins are NOT collapsed. |
| 228 div1_style->setHeight(Length(kHeight, Fixed)); |
| 229 frag = RunBlockLayoutAlgorithm(space, div1); |
| 230 EXPECT_EQ(frag->MarginStrut(), |
| 231 NGMarginStrut({LayoutUnit(0), LayoutUnit(kDiv1MarginBottom)})); |
| 232 } |
| 233 |
189 // Verifies that a box's size includes its borders and padding, and that | 234 // Verifies that a box's size includes its borders and padding, and that |
190 // children are positioned inside the content box. | 235 // children are positioned inside the content box. |
191 // | 236 // |
192 // Test case's HTML representation: | 237 // Test case's HTML representation: |
193 // <style> | 238 // <style> |
194 // #div1 { width:100px; height:100px; } | 239 // #div1 { width:100px; height:100px; } |
195 // #div1 { border-style:solid; border-width:1px 2px 3px 4px; } | 240 // #div1 { border-style:solid; border-width:1px 2px 3px 4px; } |
196 // #div1 { padding:5px 6px 7px 8px; } | 241 // #div1 { padding:5px 6px 7px 8px; } |
197 // </style> | 242 // </style> |
198 // <div id="div1"> | 243 // <div id="div1"> |
(...skipping 28 matching lines...) Expand all Loading... |
227 div1_style->setPaddingRight(Length(kPaddingRight, Fixed)); | 272 div1_style->setPaddingRight(Length(kPaddingRight, Fixed)); |
228 div1_style->setPaddingBottom(Length(kPaddingBottom, Fixed)); | 273 div1_style->setPaddingBottom(Length(kPaddingBottom, Fixed)); |
229 div1_style->setPaddingLeft(Length(kPaddingLeft, Fixed)); | 274 div1_style->setPaddingLeft(Length(kPaddingLeft, Fixed)); |
230 NGBox* div1 = new NGBox(div1_style.get()); | 275 NGBox* div1 = new NGBox(div1_style.get()); |
231 | 276 |
232 RefPtr<ComputedStyle> div2_style = ComputedStyle::create(); | 277 RefPtr<ComputedStyle> div2_style = ComputedStyle::create(); |
233 NGBox* div2 = new NGBox(div2_style.get()); | 278 NGBox* div2 = new NGBox(div2_style.get()); |
234 | 279 |
235 div1->SetFirstChild(div2); | 280 div1->SetFirstChild(div2); |
236 | 281 |
237 NGConstraintSpace* space = new NGConstraintSpace( | 282 auto* space = new NGConstraintSpace( |
238 HorizontalTopBottom, NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite)); | 283 HorizontalTopBottom, NGLogicalSize(LayoutUnit(1000), NGSizeIndefinite)); |
239 NGBlockLayoutAlgorithm algorithm(style_, div1); | 284 NGPhysicalFragment* frag = RunBlockLayoutAlgorithm(space, div1); |
240 NGPhysicalFragment* frag; | |
241 while (!algorithm.Layout(space, &frag)) | |
242 ; | |
243 | 285 |
244 ASSERT_EQ(frag->Children().size(), 1UL); | 286 ASSERT_EQ(frag->Children().size(), 1UL); |
245 | 287 |
246 // div1 | 288 // div1 |
247 const NGPhysicalFragmentBase* child = frag->Children()[0]; | 289 const NGPhysicalFragmentBase* child = frag->Children()[0]; |
248 EXPECT_EQ(kBorderLeft + kPaddingLeft + kWidth + kPaddingRight + kBorderRight, | 290 EXPECT_EQ(kBorderLeft + kPaddingLeft + kWidth + kPaddingRight + kBorderRight, |
249 child->Width()); | 291 child->Width()); |
250 EXPECT_EQ(kBorderTop + kPaddingTop + kHeight + kPaddingBottom + kBorderBottom, | 292 EXPECT_EQ(kBorderTop + kPaddingTop + kHeight + kPaddingBottom + kBorderBottom, |
251 child->Height()); | 293 child->Height()); |
252 | 294 |
253 ASSERT_TRUE(child->Type() == NGPhysicalFragmentBase::FragmentBox); | 295 ASSERT_TRUE(child->Type() == NGPhysicalFragmentBase::FragmentBox); |
254 ASSERT_EQ(static_cast<const NGPhysicalFragment*>(child)->Children().size(), | 296 ASSERT_EQ(static_cast<const NGPhysicalFragment*>(child)->Children().size(), |
255 1UL); | 297 1UL); |
256 | 298 |
257 // div2 | 299 // div2 |
258 child = static_cast<const NGPhysicalFragment*>(child)->Children()[0]; | 300 child = static_cast<const NGPhysicalFragment*>(child)->Children()[0]; |
259 EXPECT_EQ(kBorderTop + kPaddingTop, child->TopOffset()); | 301 EXPECT_EQ(kBorderTop + kPaddingTop, child->TopOffset()); |
260 EXPECT_EQ(kBorderLeft + kPaddingLeft, child->LeftOffset()); | 302 EXPECT_EQ(kBorderLeft + kPaddingLeft, child->LeftOffset()); |
261 } | 303 } |
262 } // namespace | 304 } // namespace |
263 } // namespace blink | 305 } // namespace blink |
OLD | NEW |