1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 #include "core/layout/LayoutMultiColumnFlowThread.h"
9 #include "core/layout/LayoutMultiColumnSet.h"
10 #include "core/layout/LayoutMultiColumnSpannerPlaceholder.h"
11 #include "core/layout/LayoutTestHelper.h"
13 #include <gtest/gtest.h>
19 class MultiColumnRenderingTest
: public RenderingTest
{
21 LayoutMultiColumnFlowThread
* findFlowThread(const char* id
) const;
23 // Generate a signature string based on what kind of column boxes the flow thread has
24 // established. 'c' is used for regular column content sets, while 's' is used for spanners.
25 // '?' is used when there's an unknown box type (which should be considered a failure).
26 String
columnSetSignature(LayoutMultiColumnFlowThread
*);
27 String
columnSetSignature(const char* multicolId
);
29 void setMulticolHTML(const String
&);
32 LayoutMultiColumnFlowThread
* MultiColumnRenderingTest::findFlowThread(const char* id
) const
34 Node
* multicol
= document().getElementById(id
);
37 LayoutBlockFlow
* multicolContainer
= toLayoutBlockFlow(multicol
->layoutObject());
38 if (!multicolContainer
)
40 return multicolContainer
->multiColumnFlowThread();
43 String
MultiColumnRenderingTest::columnSetSignature(LayoutMultiColumnFlowThread
* flowThread
)
45 String signature
= "";
46 for (LayoutBox
* columnBox
= flowThread
->firstMultiColumnBox();
48 columnBox
= columnBox
->nextSiblingMultiColumnBox()) {
49 if (columnBox
->isLayoutMultiColumnSpannerPlaceholder())
50 signature
.append('s');
51 else if (columnBox
->isLayoutMultiColumnSet())
52 signature
.append('c');
54 signature
.append('?');
59 String
MultiColumnRenderingTest::columnSetSignature(const char* multicolId
)
61 return columnSetSignature(findFlowThread(multicolId
));
64 void MultiColumnRenderingTest::setMulticolHTML(const String
& html
)
68 " #mc { -webkit-columns:2; }"
69 " .s, #spanner, #spanner1, #spanner2 { -webkit-column-span:all; }"
71 setBodyInnerHTML(style
+ html
);
74 TEST_F(MultiColumnRenderingTest
, OneBlockWithInDepthTreeStructureCheck
)
76 // Examine the layout tree established by a simple multicol container with a block with some text inside.
77 setMulticolHTML("<div id='mc'><div>xxx</div></div>");
78 Node
* multicol
= document().getElementById("mc");
79 ASSERT_TRUE(multicol
);
80 LayoutBlockFlow
* multicolContainer
= toLayoutBlockFlow(multicol
->layoutObject());
81 ASSERT_TRUE(multicolContainer
);
82 LayoutMultiColumnFlowThread
* flowThread
= multicolContainer
->multiColumnFlowThread();
83 ASSERT_TRUE(flowThread
);
84 EXPECT_EQ(columnSetSignature(flowThread
), "c");
85 EXPECT_EQ(flowThread
->parent(), multicolContainer
);
86 EXPECT_FALSE(flowThread
->previousSibling());
87 LayoutMultiColumnSet
* columnSet
= flowThread
->firstMultiColumnSet();
88 ASSERT_TRUE(columnSet
);
89 EXPECT_EQ(columnSet
->previousSibling(), flowThread
);
90 EXPECT_FALSE(columnSet
->nextSibling());
91 LayoutBlockFlow
* block
= toLayoutBlockFlow(flowThread
->firstChild());
93 EXPECT_FALSE(block
->nextSibling());
94 ASSERT_TRUE(block
->firstChild());
95 EXPECT_TRUE(block
->firstChild()->isText());
96 EXPECT_FALSE(block
->firstChild()->nextSibling());
99 TEST_F(MultiColumnRenderingTest
, Empty
)
101 // If there's no column content, there should be no column set.
102 setMulticolHTML("<div id='mc'></div>");
103 EXPECT_EQ(columnSetSignature("mc"), "");
106 TEST_F(MultiColumnRenderingTest
, OneBlock
)
108 // There is some content, so we should create a column set.
109 setMulticolHTML("<div id='mc'><div id='block'></div></div>");
110 LayoutMultiColumnFlowThread
* flowThread
= findFlowThread("mc");
111 ASSERT_EQ(columnSetSignature(flowThread
), "c");
112 LayoutMultiColumnSet
* columnSet
= flowThread
->firstMultiColumnSet();
113 EXPECT_EQ(flowThread
->mapDescendantToColumnSet(document().getElementById("block")->layoutObject()), columnSet
);
116 TEST_F(MultiColumnRenderingTest
, TwoBlocks
)
118 // No matter how much content, we should only create one column set (unless there are spanners).
119 setMulticolHTML("<div id='mc'><div id='block1'></div><div id='block2'></div></div>");
120 LayoutMultiColumnFlowThread
* flowThread
= findFlowThread("mc");
121 ASSERT_EQ(columnSetSignature(flowThread
), "c");
122 LayoutMultiColumnSet
* columnSet
= flowThread
->firstMultiColumnSet();
123 EXPECT_EQ(flowThread
->mapDescendantToColumnSet(document().getElementById("block1")->layoutObject()), columnSet
);
124 EXPECT_EQ(flowThread
->mapDescendantToColumnSet(document().getElementById("block2")->layoutObject()), columnSet
);
127 TEST_F(MultiColumnRenderingTest
, Spanner
)
129 // With one spanner and no column content, we should create a spanner set.
130 setMulticolHTML("<div id='mc'><div id='spanner'></div></div>");
131 LayoutMultiColumnFlowThread
* flowThread
= findFlowThread("mc");
132 ASSERT_EQ(columnSetSignature(flowThread
), "s");
133 LayoutBox
* columnBox
= flowThread
->firstMultiColumnBox();
134 EXPECT_EQ(flowThread
->firstMultiColumnSet(), nullptr);
135 EXPECT_EQ(flowThread
->containingColumnSpannerPlaceholder(document().getElementById("spanner")->layoutObject()), columnBox
);
136 EXPECT_EQ(document().getElementById("spanner")->layoutObject()->spannerPlaceholder(), columnBox
);
139 TEST_F(MultiColumnRenderingTest
, ContentThenSpanner
)
141 // With some column content followed by a spanner, we need a column set followed by a spanner set.
142 setMulticolHTML("<div id='mc'><div id='columnContent'></div><div id='spanner'></div></div>");
143 LayoutMultiColumnFlowThread
* flowThread
= findFlowThread("mc");
144 ASSERT_EQ(columnSetSignature(flowThread
), "cs");
145 LayoutBox
* columnBox
= flowThread
->firstMultiColumnBox();
146 EXPECT_EQ(flowThread
->mapDescendantToColumnSet(document().getElementById("columnContent")->layoutObject()), columnBox
);
147 columnBox
= columnBox
->nextSiblingMultiColumnBox();
148 EXPECT_EQ(flowThread
->containingColumnSpannerPlaceholder(document().getElementById("spanner")->layoutObject()), columnBox
);
149 EXPECT_EQ(flowThread
->containingColumnSpannerPlaceholder(document().getElementById("columnContent")->layoutObject()), nullptr);
152 TEST_F(MultiColumnRenderingTest
, SpannerThenContent
)
154 // With a spanner followed by some column content, we need a spanner set followed by a column set.
155 setMulticolHTML("<div id='mc'><div id='spanner'></div><div id='columnContent'></div></div>");
156 LayoutMultiColumnFlowThread
* flowThread
= findFlowThread("mc");
157 ASSERT_EQ(columnSetSignature(flowThread
), "sc");
158 LayoutBox
* columnBox
= flowThread
->firstMultiColumnBox();
159 EXPECT_EQ(flowThread
->containingColumnSpannerPlaceholder(document().getElementById("spanner")->layoutObject()), columnBox
);
160 columnBox
= columnBox
->nextSiblingMultiColumnBox();
161 EXPECT_EQ(flowThread
->mapDescendantToColumnSet(document().getElementById("columnContent")->layoutObject()), columnBox
);
162 EXPECT_EQ(flowThread
->containingColumnSpannerPlaceholder(document().getElementById("columnContent")->layoutObject()), nullptr);
165 TEST_F(MultiColumnRenderingTest
, ContentThenSpannerThenContent
)
167 // With column content followed by a spanner followed by some column content, we need a column
168 // set followed by a spanner set followed by a column set.
169 setMulticolHTML("<div id='mc'><div id='columnContentBefore'></div><div id='spanner'></div><div id='columnContentAfter'></div></div>");
170 LayoutMultiColumnFlowThread
* flowThread
= findFlowThread("mc");
171 ASSERT_EQ(columnSetSignature(flowThread
), "csc");
172 LayoutBox
* columnBox
= flowThread
->firstMultiColumnSet();
173 EXPECT_EQ(flowThread
->mapDescendantToColumnSet(document().getElementById("columnContentBefore")->layoutObject()), columnBox
);
174 EXPECT_EQ(flowThread
->containingColumnSpannerPlaceholder(document().getElementById("columnContentBefore")->layoutObject()), nullptr);
175 columnBox
= columnBox
->nextSiblingMultiColumnBox();
176 EXPECT_EQ(flowThread
->containingColumnSpannerPlaceholder(document().getElementById("spanner")->layoutObject()), columnBox
);
177 columnBox
= columnBox
->nextSiblingMultiColumnBox();
178 EXPECT_EQ(flowThread
->mapDescendantToColumnSet(document().getElementById("columnContentAfter")->layoutObject()), columnBox
);
179 EXPECT_EQ(flowThread
->containingColumnSpannerPlaceholder(document().getElementById("columnContentAfter")->layoutObject()), nullptr);
182 TEST_F(MultiColumnRenderingTest
, TwoSpanners
)
184 // With two spanners and no column content, we need two spanner sets.
185 setMulticolHTML("<div id='mc'><div id='spanner1'></div><div id='spanner2'></div></div>");
186 LayoutMultiColumnFlowThread
* flowThread
= findFlowThread("mc");
187 ASSERT_EQ(columnSetSignature(flowThread
), "ss");
188 LayoutBox
* columnBox
= flowThread
->firstMultiColumnBox();
189 EXPECT_EQ(flowThread
->firstMultiColumnSet(), nullptr);
190 EXPECT_EQ(flowThread
->containingColumnSpannerPlaceholder(document().getElementById("spanner1")->layoutObject()), columnBox
);
191 EXPECT_EQ(document().getElementById("spanner1")->layoutObject()->spannerPlaceholder(), columnBox
);
192 columnBox
= columnBox
->nextSiblingMultiColumnBox();
193 EXPECT_EQ(flowThread
->containingColumnSpannerPlaceholder(document().getElementById("spanner2")->layoutObject()), columnBox
);
194 EXPECT_EQ(document().getElementById("spanner2")->layoutObject()->spannerPlaceholder(), columnBox
);
197 TEST_F(MultiColumnRenderingTest
, SpannerThenContentThenSpanner
)
199 // With two spanners and some column content in-between, we need a spanner set, a column set and another spanner set.
200 setMulticolHTML("<div id='mc'><div id='spanner1'></div><div id='columnContent'></div><div id='spanner2'></div></div>");
201 LayoutMultiColumnFlowThread
* flowThread
= findFlowThread("mc");
202 ASSERT_EQ(columnSetSignature(flowThread
), "scs");
203 LayoutMultiColumnSet
* columnSet
= flowThread
->firstMultiColumnSet();
204 EXPECT_EQ(columnSet
->nextSiblingMultiColumnSet(), nullptr);
205 LayoutBox
* columnBox
= flowThread
->firstMultiColumnBox();
206 EXPECT_EQ(flowThread
->containingColumnSpannerPlaceholder(document().getElementById("spanner1")->layoutObject()), columnBox
);
207 columnBox
= columnBox
->nextSiblingMultiColumnBox();
208 EXPECT_EQ(columnBox
, columnSet
);
209 EXPECT_EQ(flowThread
->mapDescendantToColumnSet(document().getElementById("columnContent")->layoutObject()), columnSet
);
210 EXPECT_EQ(flowThread
->containingColumnSpannerPlaceholder(document().getElementById("columnContent")->layoutObject()), nullptr);
211 columnBox
= columnBox
->nextSiblingMultiColumnBox();
212 EXPECT_EQ(flowThread
->containingColumnSpannerPlaceholder(document().getElementById("spanner2")->layoutObject()), columnBox
);
215 TEST_F(MultiColumnRenderingTest
, SpannerWithSpanner
)
217 // column-span:all on something inside column-span:all has no effect.
218 setMulticolHTML("<div id='mc'><div id='spanner'><div id='invalidSpanner' class='s'></div></div></div>");
219 LayoutMultiColumnFlowThread
* flowThread
= findFlowThread("mc");
220 ASSERT_EQ(columnSetSignature(flowThread
), "s");
221 LayoutBox
* columnBox
= flowThread
->firstMultiColumnBox();
222 EXPECT_EQ(flowThread
->containingColumnSpannerPlaceholder(document().getElementById("spanner")->layoutObject()), columnBox
);
223 EXPECT_EQ(flowThread
->containingColumnSpannerPlaceholder(document().getElementById("invalidSpanner")->layoutObject()), columnBox
);
224 EXPECT_EQ(toLayoutMultiColumnSpannerPlaceholder(columnBox
)->layoutObjectInFlowThread(), document().getElementById("spanner")->layoutObject());
225 EXPECT_EQ(document().getElementById("spanner")->layoutObject()->spannerPlaceholder(), columnBox
);
226 EXPECT_EQ(document().getElementById("invalidSpanner")->layoutObject()->spannerPlaceholder(), nullptr);
229 TEST_F(MultiColumnRenderingTest
, SubtreeWithSpanner
)
231 setMulticolHTML("<div id='mc'><div id='outer'><div id='block1'></div><div id='spanner'></div><div id='block2'></div></div></div>");
232 LayoutMultiColumnFlowThread
* flowThread
= findFlowThread("mc");
233 EXPECT_EQ(columnSetSignature(flowThread
), "csc");
234 LayoutBox
* columnBox
= flowThread
->firstMultiColumnBox();
235 EXPECT_EQ(flowThread
->mapDescendantToColumnSet(document().getElementById("outer")->layoutObject()), columnBox
);
236 EXPECT_EQ(flowThread
->mapDescendantToColumnSet(document().getElementById("block1")->layoutObject()), columnBox
);
237 columnBox
= columnBox
->nextSiblingMultiColumnBox();
238 EXPECT_EQ(flowThread
->containingColumnSpannerPlaceholder(document().getElementById("spanner")->layoutObject()), columnBox
);
239 EXPECT_EQ(document().getElementById("spanner")->layoutObject()->spannerPlaceholder(), columnBox
);
240 EXPECT_EQ(toLayoutMultiColumnSpannerPlaceholder(columnBox
)->layoutObjectInFlowThread(), document().getElementById("spanner")->layoutObject());
241 EXPECT_EQ(flowThread
->containingColumnSpannerPlaceholder(document().getElementById("outer")->layoutObject()), nullptr);
242 EXPECT_EQ(flowThread
->containingColumnSpannerPlaceholder(document().getElementById("block1")->layoutObject()), nullptr);
243 EXPECT_EQ(flowThread
->containingColumnSpannerPlaceholder(document().getElementById("block2")->layoutObject()), nullptr);
244 columnBox
= columnBox
->nextSiblingMultiColumnBox();
245 EXPECT_EQ(flowThread
->mapDescendantToColumnSet(document().getElementById("block2")->layoutObject()), columnBox
);
248 TEST_F(MultiColumnRenderingTest
, SubtreeWithSpannerAfterSpanner
)
250 setMulticolHTML("<div id='mc'><div id='spanner1'></div><div id='outer'>text<div id='spanner2'></div><div id='after'></div></div></div>");
251 LayoutMultiColumnFlowThread
* flowThread
= findFlowThread("mc");
252 EXPECT_EQ(columnSetSignature(flowThread
), "scsc");
253 LayoutBox
* columnBox
= flowThread
->firstMultiColumnBox();
254 EXPECT_EQ(flowThread
->containingColumnSpannerPlaceholder(document().getElementById("spanner1")->layoutObject()), columnBox
);
255 EXPECT_EQ(toLayoutMultiColumnSpannerPlaceholder(columnBox
)->layoutObjectInFlowThread(), document().getElementById("spanner1")->layoutObject());
256 EXPECT_EQ(document().getElementById("spanner1")->layoutObject()->spannerPlaceholder(), columnBox
);
257 columnBox
= columnBox
->nextSiblingMultiColumnBox();
258 EXPECT_EQ(flowThread
->mapDescendantToColumnSet(document().getElementById("outer")->layoutObject()), columnBox
);
259 columnBox
= columnBox
->nextSiblingMultiColumnBox();
260 EXPECT_EQ(flowThread
->containingColumnSpannerPlaceholder(document().getElementById("spanner2")->layoutObject()), columnBox
);
261 EXPECT_EQ(toLayoutMultiColumnSpannerPlaceholder(columnBox
)->layoutObjectInFlowThread(), document().getElementById("spanner2")->layoutObject());
262 EXPECT_EQ(document().getElementById("spanner2")->layoutObject()->spannerPlaceholder(), columnBox
);
263 EXPECT_EQ(flowThread
->containingColumnSpannerPlaceholder(document().getElementById("outer")->layoutObject()), nullptr);
264 EXPECT_EQ(flowThread
->containingColumnSpannerPlaceholder(document().getElementById("after")->layoutObject()), nullptr);
265 columnBox
= columnBox
->nextSiblingMultiColumnBox();
266 EXPECT_EQ(flowThread
->mapDescendantToColumnSet(document().getElementById("after")->layoutObject()), columnBox
);
269 TEST_F(MultiColumnRenderingTest
, SubtreeWithSpannerBeforeSpanner
)
271 setMulticolHTML("<div id='mc'><div id='outer'>text<div id='spanner1'></div>text</div><div id='spanner2'></div></div>");
272 LayoutMultiColumnFlowThread
* flowThread
= findFlowThread("mc");
273 EXPECT_EQ(columnSetSignature(flowThread
), "cscs");
274 LayoutBox
* columnBox
= flowThread
->firstMultiColumnSet();
275 EXPECT_EQ(flowThread
->mapDescendantToColumnSet(document().getElementById("outer")->layoutObject()), columnBox
);
276 columnBox
= columnBox
->nextSiblingMultiColumnBox();
277 EXPECT_EQ(flowThread
->containingColumnSpannerPlaceholder(document().getElementById("spanner1")->layoutObject()), columnBox
);
278 EXPECT_EQ(document().getElementById("spanner1")->layoutObject()->spannerPlaceholder(), columnBox
);
279 EXPECT_EQ(toLayoutMultiColumnSpannerPlaceholder(columnBox
)->layoutObjectInFlowThread(), document().getElementById("spanner1")->layoutObject());
280 columnBox
= columnBox
->nextSiblingMultiColumnBox()->nextSiblingMultiColumnBox();
281 EXPECT_EQ(flowThread
->containingColumnSpannerPlaceholder(document().getElementById("spanner2")->layoutObject()), columnBox
);
282 EXPECT_EQ(document().getElementById("spanner2")->layoutObject()->spannerPlaceholder(), columnBox
);
283 EXPECT_EQ(toLayoutMultiColumnSpannerPlaceholder(columnBox
)->layoutObjectInFlowThread(), document().getElementById("spanner2")->layoutObject());
284 EXPECT_EQ(flowThread
->containingColumnSpannerPlaceholder(document().getElementById("outer")->layoutObject()), nullptr);
287 TEST_F(MultiColumnRenderingTest
, columnSetAtBlockOffset
)
289 setMulticolHTML("<div id='mc' style='line-height:100px;'>text<br>text<br>text<br>text<br>text<div id='spanner1'>spanner</div>text<br>text<div id='spanner2'>text<br>text</div>text</div>");
290 LayoutMultiColumnFlowThread
* flowThread
= findFlowThread("mc");
291 EXPECT_EQ(columnSetSignature(flowThread
), "cscsc");
292 LayoutMultiColumnSet
* firstRow
= flowThread
->firstMultiColumnSet();
293 EXPECT_EQ(flowThread
->columnSetAtBlockOffset(LayoutUnit(-10000)), firstRow
); // negative overflow
294 EXPECT_EQ(flowThread
->columnSetAtBlockOffset(LayoutUnit()), firstRow
);
295 EXPECT_EQ(flowThread
->columnSetAtBlockOffset(LayoutUnit(499)), firstRow
); // bottom of last line in first row.
296 LayoutMultiColumnSet
* secondRow
= firstRow
->nextSiblingMultiColumnSet();
297 EXPECT_EQ(flowThread
->columnSetAtBlockOffset(LayoutUnit(500)), secondRow
);
298 EXPECT_EQ(flowThread
->columnSetAtBlockOffset(LayoutUnit(699)), secondRow
);
299 LayoutMultiColumnSet
* thirdRow
= secondRow
->nextSiblingMultiColumnSet();
300 EXPECT_EQ(flowThread
->columnSetAtBlockOffset(LayoutUnit(700)), thirdRow
);
301 EXPECT_EQ(flowThread
->columnSetAtBlockOffset(LayoutUnit(799)), thirdRow
); // bottom of last row
302 EXPECT_EQ(flowThread
->columnSetAtBlockOffset(LayoutUnit(10000)), thirdRow
); // overflow
305 TEST_F(MultiColumnRenderingTest
, columnSetAtBlockOffsetVerticalRl
)
307 setMulticolHTML("<div id='mc' style='line-height:100px; -webkit-writing-mode:vertical-rl;'>text<br>text<br>text<br>text<br>text<div id='spanner1'>spanner</div>text<br>text<div id='spanner2'>text<br>text</div>text</div>");
308 LayoutMultiColumnFlowThread
* flowThread
= findFlowThread("mc");
309 EXPECT_EQ(columnSetSignature(flowThread
), "cscsc");
310 LayoutMultiColumnSet
* firstRow
= flowThread
->firstMultiColumnSet();
311 EXPECT_EQ(flowThread
->columnSetAtBlockOffset(LayoutUnit(-10000)), firstRow
); // negative overflow
312 EXPECT_EQ(flowThread
->columnSetAtBlockOffset(LayoutUnit()), firstRow
);
313 EXPECT_EQ(flowThread
->columnSetAtBlockOffset(LayoutUnit(499)), firstRow
); // bottom of last line in first row.
314 LayoutMultiColumnSet
* secondRow
= firstRow
->nextSiblingMultiColumnSet();
315 EXPECT_EQ(flowThread
->columnSetAtBlockOffset(LayoutUnit(500)), secondRow
);
316 EXPECT_EQ(flowThread
->columnSetAtBlockOffset(LayoutUnit(699)), secondRow
);
317 LayoutMultiColumnSet
* thirdRow
= secondRow
->nextSiblingMultiColumnSet();
318 EXPECT_EQ(flowThread
->columnSetAtBlockOffset(LayoutUnit(700)), thirdRow
);
319 EXPECT_EQ(flowThread
->columnSetAtBlockOffset(LayoutUnit(799)), thirdRow
); // bottom of last row
320 EXPECT_EQ(flowThread
->columnSetAtBlockOffset(LayoutUnit(10000)), thirdRow
); // overflow
323 TEST_F(MultiColumnRenderingTest
, columnSetAtBlockOffsetVerticalLr
)
325 setMulticolHTML("<div id='mc' style='line-height:100px; -webkit-writing-mode:vertical-lr;'>text<br>text<br>text<br>text<br>text<div id='spanner1'>spanner</div>text<br>text<div id='spanner2'>text<br>text</div>text</div>");
326 LayoutMultiColumnFlowThread
* flowThread
= findFlowThread("mc");
327 EXPECT_EQ(columnSetSignature(flowThread
), "cscsc");
328 LayoutMultiColumnSet
* firstRow
= flowThread
->firstMultiColumnSet();
329 EXPECT_EQ(flowThread
->columnSetAtBlockOffset(LayoutUnit(-10000)), firstRow
); // negative overflow
330 EXPECT_EQ(flowThread
->columnSetAtBlockOffset(LayoutUnit()), firstRow
);
331 EXPECT_EQ(flowThread
->columnSetAtBlockOffset(LayoutUnit(499)), firstRow
); // bottom of last line in first row.
332 LayoutMultiColumnSet
* secondRow
= firstRow
->nextSiblingMultiColumnSet();
333 EXPECT_EQ(flowThread
->columnSetAtBlockOffset(LayoutUnit(500)), secondRow
);
334 EXPECT_EQ(flowThread
->columnSetAtBlockOffset(LayoutUnit(699)), secondRow
);
335 LayoutMultiColumnSet
* thirdRow
= secondRow
->nextSiblingMultiColumnSet();
336 EXPECT_EQ(flowThread
->columnSetAtBlockOffset(LayoutUnit(700)), thirdRow
);
337 EXPECT_EQ(flowThread
->columnSetAtBlockOffset(LayoutUnit(799)), thirdRow
); // bottom of last row
338 EXPECT_EQ(flowThread
->columnSetAtBlockOffset(LayoutUnit(10000)), thirdRow
); // overflow
341 class MultiColumnTreeModifyingTest
: public MultiColumnRenderingTest
{
343 void setMulticolHTML(const char*);
344 void reparentLayoutObject(const char* newParentId
, const char* childId
, const char* insertBeforeId
= nullptr);
345 void destroyLayoutObject(LayoutObject
* child
);
346 void destroyLayoutObject(const char* childId
);
349 void MultiColumnTreeModifyingTest::setMulticolHTML(const char* html
)
351 MultiColumnRenderingTest::setMulticolHTML(html
);
352 // Allow modifications to the layout tree structure, because that's what we want to test.
353 document().lifecycle().advanceTo(DocumentLifecycle::InStyleRecalc
);
356 void MultiColumnTreeModifyingTest::reparentLayoutObject(const char* newParentId
, const char* childId
, const char* insertBeforeId
)
358 LayoutObject
* newParent
= document().getElementById(newParentId
)->layoutObject();
359 LayoutObject
* child
= document().getElementById(childId
)->layoutObject();
360 LayoutObject
* insertBefore
= insertBeforeId
? document().getElementById(insertBeforeId
)->layoutObject() : nullptr;
362 newParent
->addChild(child
, insertBefore
);
365 void MultiColumnTreeModifyingTest::destroyLayoutObject(LayoutObject
* child
)
367 // Remove and destroy in separate steps, so that we get to test removal of subtrees.
369 child
->node()->detach();
372 void MultiColumnTreeModifyingTest::destroyLayoutObject(const char* childId
)
374 destroyLayoutObject(document().getElementById(childId
)->layoutObject());
377 TEST_F(MultiColumnTreeModifyingTest
, InsertFirstContentAndRemove
)
379 setMulticolHTML("<div id='block'></div><div id='mc'></div>");
380 LayoutMultiColumnFlowThread
* flowThread
= findFlowThread("mc");
381 LayoutBlockFlow
* block
= toLayoutBlockFlow(document().getElementById("block")->layoutObject());
382 LayoutBlockFlow
* multicolContainer
= toLayoutBlockFlow(document().getElementById("mc")->layoutObject());
384 multicolContainer
->addChild(block
);
385 EXPECT_EQ(block
->parent(), flowThread
);
386 // A set should have appeared, now that the multicol container has content.
387 EXPECT_EQ(columnSetSignature(flowThread
), "c");
389 destroyLayoutObject(block
);
390 // The set should be gone again now, since there's nothing inside the multicol container anymore.
391 EXPECT_EQ(columnSetSignature("mc"), "");
394 TEST_F(MultiColumnTreeModifyingTest
, InsertContentBeforeContentAndRemove
)
396 setMulticolHTML("<div id='block'></div><div id='mc'><div id='insertBefore'></div></div>");
397 EXPECT_EQ(columnSetSignature("mc"), "c");
398 reparentLayoutObject("mc", "block", "insertBefore");
399 // There was already some content prior to our insertion, so no new set should be inserted.
400 EXPECT_EQ(columnSetSignature("mc"), "c");
401 destroyLayoutObject("block");
402 // There's still some content after the removal, so the set should remain.
403 EXPECT_EQ(columnSetSignature("mc"), "c");
406 TEST_F(MultiColumnTreeModifyingTest
, InsertContentAfterContentAndRemove
)
408 setMulticolHTML("<div id='block'></div><div id='mc'><div></div></div>");
409 EXPECT_EQ(columnSetSignature("mc"), "c");
410 reparentLayoutObject("mc", "block");
411 // There was already some content prior to our insertion, so no new set should be inserted.
412 EXPECT_EQ(columnSetSignature("mc"), "c");
413 destroyLayoutObject("block");
414 // There's still some content after the removal, so the set should remain.
415 EXPECT_EQ(columnSetSignature("mc"), "c");
418 TEST_F(MultiColumnTreeModifyingTest
, InsertSpannerAndRemove
)
420 setMulticolHTML("<div id='spanner'></div><div id='mc'></div>");
421 LayoutMultiColumnFlowThread
* flowThread
= findFlowThread("mc");
422 LayoutBlockFlow
* spanner
= toLayoutBlockFlow(document().getElementById("spanner")->layoutObject());
423 LayoutBlockFlow
* multicolContainer
= toLayoutBlockFlow(document().getElementById("mc")->layoutObject());
425 multicolContainer
->addChild(spanner
);
426 EXPECT_EQ(spanner
->parent(), flowThread
);
427 // We should now have a spanner placeholder, since we just moved a spanner into the multicol container.
428 EXPECT_EQ(columnSetSignature(flowThread
), "s");
429 destroyLayoutObject(spanner
);
430 EXPECT_EQ(columnSetSignature(flowThread
), "");
433 TEST_F(MultiColumnTreeModifyingTest
, InsertTwoSpannersAndRemove
)
435 setMulticolHTML("<div id='block'>ee<div class='s'></div><div class='s'></div></div><div id='mc'></div>");
436 reparentLayoutObject("mc", "block");
437 EXPECT_EQ(columnSetSignature("mc"), "css");
438 destroyLayoutObject("block");
439 EXPECT_EQ(columnSetSignature("mc"), "");
442 TEST_F(MultiColumnTreeModifyingTest
, InsertSpannerAfterContentAndRemove
)
444 setMulticolHTML("<div id='spanner'></div><div id='mc'><div></div></div>");
445 reparentLayoutObject("mc", "spanner");
446 // We should now have a spanner placeholder, since we just moved a spanner into the multicol container.
447 EXPECT_EQ(columnSetSignature("mc"), "cs");
448 destroyLayoutObject("spanner");
449 EXPECT_EQ(columnSetSignature("mc"), "c");
452 TEST_F(MultiColumnTreeModifyingTest
, InsertSpannerBeforeContentAndRemove
)
454 setMulticolHTML("<div id='spanner'></div><div id='mc'><div id='columnContent'></div></div>");
455 reparentLayoutObject("mc", "spanner", "columnContent");
456 // We should now have a spanner placeholder, since we just moved a spanner into the multicol container.
457 EXPECT_EQ(columnSetSignature("mc"), "sc");
458 destroyLayoutObject("spanner");
459 EXPECT_EQ(columnSetSignature("mc"), "c");
462 TEST_F(MultiColumnTreeModifyingTest
, InsertSpannerBetweenContentAndRemove
)
464 setMulticolHTML("<div id='spanner'></div><div id='mc'><div></div><div id='insertBefore'></div></div>");
465 reparentLayoutObject("mc", "spanner", "insertBefore");
466 // Since the spanner was inserted in the middle of column content, what used to be one column
467 // set had to be split in two, in order to get a spot to insert the spanner placeholder.
468 EXPECT_EQ(columnSetSignature("mc"), "csc");
469 destroyLayoutObject("spanner");
470 // The spanner placeholder should be gone again now, and the two sets be merged into one.
471 EXPECT_EQ(columnSetSignature("mc"), "c");
474 TEST_F(MultiColumnTreeModifyingTest
, InsertSubtreeWithContentAndSpannerAndRemove
)
476 setMulticolHTML("<div id='block'>text<div id='spanner'></div>text</div><div id='mc'></div>");
477 reparentLayoutObject("mc", "block");
478 EXPECT_EQ(columnSetSignature("mc"), "csc");
479 destroyLayoutObject("block");
480 EXPECT_EQ(columnSetSignature("mc"), "");
483 TEST_F(MultiColumnTreeModifyingTest
, InsertInsideSpannerAndRemove
)
485 setMulticolHTML("<div id='block'>text</div><div id='mc'><div id='spanner'></div></div>");
486 reparentLayoutObject("spanner", "block");
487 EXPECT_EQ(columnSetSignature("mc"), "s");
488 destroyLayoutObject("block");
489 EXPECT_EQ(columnSetSignature("mc"), "s");
492 TEST_F(MultiColumnTreeModifyingTest
, InsertSpannerInContentBeforeSpannerAndRemove
)
494 setMulticolHTML("<div id='spanner'></div><div id='mc'><div></div><div id='insertBefore'></div><div class='s'></div></div>");
495 EXPECT_EQ(columnSetSignature("mc"), "cs");
496 reparentLayoutObject("mc", "spanner", "insertBefore");
497 EXPECT_EQ(columnSetSignature("mc"), "cscs");
498 destroyLayoutObject("spanner");
499 EXPECT_EQ(columnSetSignature("mc"), "cs");
502 TEST_F(MultiColumnTreeModifyingTest
, InsertSpannerInContentAfterSpannerAndRemove
)
504 setMulticolHTML("<div id='spanner'></div><div id='mc'><div class='s'></div><div></div><div id='insertBefore'></div></div>");
505 EXPECT_EQ(columnSetSignature("mc"), "sc");
506 reparentLayoutObject("mc", "spanner", "insertBefore");
507 EXPECT_EQ(columnSetSignature("mc"), "scsc");
508 destroyLayoutObject("spanner");
509 EXPECT_EQ(columnSetSignature("mc"), "sc");
512 TEST_F(MultiColumnTreeModifyingTest
, InsertSpannerAfterSpannerAndRemove
)
514 setMulticolHTML("<div id='spanner'></div><div id='mc'><div class='s'></div></div>");
515 reparentLayoutObject("mc", "spanner");
516 EXPECT_EQ(columnSetSignature("mc"), "ss");
517 destroyLayoutObject("spanner");
518 EXPECT_EQ(columnSetSignature("mc"), "s");
521 TEST_F(MultiColumnTreeModifyingTest
, InsertSpannerBeforeSpannerAndRemove
)
523 setMulticolHTML("<div id='spanner'></div><div id='mc'><div id='insertBefore' class='s'></div></div>");
524 reparentLayoutObject("mc", "spanner", "insertBefore");
525 EXPECT_EQ(columnSetSignature("mc"), "ss");
526 destroyLayoutObject("spanner");
527 EXPECT_EQ(columnSetSignature("mc"), "s");
530 TEST_F(MultiColumnTreeModifyingTest
, InsertContentBeforeSpannerAndRemove
)
532 setMulticolHTML("<div id='block'></div><div id='mc'><div id='insertBefore' class='s'></div></div>");
533 reparentLayoutObject("mc", "block", "insertBefore");
534 EXPECT_EQ(columnSetSignature("mc"), "cs");
535 destroyLayoutObject("block");
536 EXPECT_EQ(columnSetSignature("mc"), "s");
539 TEST_F(MultiColumnTreeModifyingTest
, InsertContentAfterContentBeforeSpannerAndRemove
)
541 setMulticolHTML("<div id='block'></div><div id='mc'>text<div id='insertBefore' class='s'></div></div>");
542 EXPECT_EQ(columnSetSignature("mc"), "cs");
543 reparentLayoutObject("mc", "block", "insertBefore");
544 // There was already some content before the spanner prior to our insertion, so no new set
545 // should be inserted.
546 EXPECT_EQ(columnSetSignature("mc"), "cs");
547 destroyLayoutObject("block");
548 EXPECT_EQ(columnSetSignature("mc"), "cs");
551 TEST_F(MultiColumnTreeModifyingTest
, InsertContentAfterContentAndSpannerAndRemove
)
553 setMulticolHTML("<div id='block'></div><div id='mc'>content<div class='s'></div></div>");
554 EXPECT_EQ(columnSetSignature("mc"), "cs");
555 reparentLayoutObject("mc", "block");
556 EXPECT_EQ(columnSetSignature("mc"), "csc");
557 destroyLayoutObject("block");
558 EXPECT_EQ(columnSetSignature("mc"), "cs");
561 TEST_F(MultiColumnTreeModifyingTest
, InsertContentBeforeSpannerAndContentAndRemove
)
563 setMulticolHTML("<div id='block'></div><div id='mc'><div id='insertBefore' class='s'></div>content</div>");
564 EXPECT_EQ(columnSetSignature("mc"), "sc");
565 reparentLayoutObject("mc", "block", "insertBefore");
566 EXPECT_EQ(columnSetSignature("mc"), "csc");
567 destroyLayoutObject("block");
568 EXPECT_EQ(columnSetSignature("mc"), "sc");
571 TEST_F(MultiColumnTreeModifyingTest
, InsertSpannerIntoContentBeforeSpannerAndRemove
)
573 setMulticolHTML("<div id='spanner'></div><div id='mc'><div></div><div id='insertBefore'></div><div class='s'></div><div class='s'></div><div></div></div>");
574 EXPECT_EQ(columnSetSignature("mc"), "cssc");
575 reparentLayoutObject("mc", "spanner", "insertBefore");
576 EXPECT_EQ(columnSetSignature("mc"), "cscssc");
577 destroyLayoutObject("spanner");
578 EXPECT_EQ(columnSetSignature("mc"), "cssc");
581 TEST_F(MultiColumnTreeModifyingTest
, InsertSpannerIntoContentAfterSpannerAndRemove
)
583 setMulticolHTML("<div id='spanner'></div><div id='mc'><div></div><div class='s'></div><div class='s'></div><div></div><div id='insertBefore'></div></div>");
584 EXPECT_EQ(columnSetSignature("mc"), "cssc");
585 reparentLayoutObject("mc", "spanner", "insertBefore");
586 EXPECT_EQ(columnSetSignature("mc"), "csscsc");
587 destroyLayoutObject("spanner");
588 EXPECT_EQ(columnSetSignature("mc"), "cssc");
591 TEST_F(MultiColumnTreeModifyingTest
, InsertInvalidSpannerAndRemove
)
593 setMulticolHTML("<div class='s' id='invalidSpanner'></div><div id='mc'><div id='spanner'></div></div>");
594 EXPECT_EQ(columnSetSignature("mc"), "s");
595 reparentLayoutObject("spanner", "invalidSpanner");
596 // It's not allowed to nest spanners.
597 EXPECT_EQ(columnSetSignature("mc"), "s");
598 destroyLayoutObject("invalidSpanner");
599 EXPECT_EQ(columnSetSignature("mc"), "s");
602 TEST_F(MultiColumnTreeModifyingTest
, InsertSpannerWithInvalidSpannerAndRemove
)
604 setMulticolHTML("<div id='spanner'><div class='s' id='invalidSpanner'></div></div><div id='mc'></div>");
605 reparentLayoutObject("mc", "spanner");
606 // It's not allowed to nest spanners.
607 EXPECT_EQ(columnSetSignature("mc"), "s");
608 destroyLayoutObject("spanner");
609 EXPECT_EQ(columnSetSignature("mc"), "");
612 TEST_F(MultiColumnTreeModifyingTest
, InsertInvalidSpannerInSpannerBetweenContentAndRemove
)
614 setMulticolHTML("<div class='s' id='invalidSpanner'></div><div id='mc'>text<div id='spanner'></div>text</div>");
615 EXPECT_EQ(columnSetSignature("mc"), "csc");
616 reparentLayoutObject("spanner", "invalidSpanner");
617 EXPECT_EQ(columnSetSignature("mc"), "csc");
618 destroyLayoutObject("invalidSpanner");
619 EXPECT_EQ(columnSetSignature("mc"), "csc");
622 TEST_F(MultiColumnTreeModifyingTest
, InsertContentAndSpannerAndRemove
)
624 setMulticolHTML("<div id='block'>text<div id='spanner'></div></div><div id='mc'>text</div>");
625 reparentLayoutObject("mc", "block");
626 EXPECT_EQ(columnSetSignature("mc"), "cs");
627 destroyLayoutObject("block");
628 EXPECT_EQ(columnSetSignature("mc"), "c");
631 TEST_F(MultiColumnTreeModifyingTest
, InsertContentAndSpannerAndContentAndRemove
)
633 setMulticolHTML("<div id='block'><div id='spanner'></div>text</div><div id='mc'></div>");
634 reparentLayoutObject("mc", "block");
635 EXPECT_EQ(columnSetSignature("mc"), "csc");
636 destroyLayoutObject("block");
637 EXPECT_EQ(columnSetSignature("mc"), "");
640 TEST_F(MultiColumnTreeModifyingTest
, InsertSubtreeWithSpannerAndRemove
)
642 setMulticolHTML("<div id='block'>text<div class='s'></div>text</div><div id='mc'></div>");
643 reparentLayoutObject("mc", "block");
644 EXPECT_EQ(columnSetSignature("mc"), "csc");
645 destroyLayoutObject("block");
646 EXPECT_EQ(columnSetSignature("mc"), "");
649 TEST_F(MultiColumnTreeModifyingTest
, InsertSubtreeWithSpannerAfterContentAndRemove
)
651 setMulticolHTML("<div id='block'>text<div class='s'></div>text</div><div id='mc'>column content</div>");
652 reparentLayoutObject("mc", "block");
653 EXPECT_EQ(columnSetSignature("mc"), "csc");
654 destroyLayoutObject("block");
655 EXPECT_EQ(columnSetSignature("mc"), "c");
658 TEST_F(MultiColumnTreeModifyingTest
, InsertSubtreeWithSpannerBeforeContentAndRemove
)
660 setMulticolHTML("<div id='block'>text<div class='s'></div>text</div><div id='mc'><div id='insertBefore'>column content</div></div>");
661 reparentLayoutObject("mc", "block", "insertBefore");
662 EXPECT_EQ(columnSetSignature("mc"), "csc");
663 destroyLayoutObject("block");
664 EXPECT_EQ(columnSetSignature("mc"), "c");
667 TEST_F(MultiColumnTreeModifyingTest
, InsertSubtreeWithSpannerInsideContentAndRemove
)
669 setMulticolHTML("<div id='block'>text<div class='s'></div>text</div><div id='mc'><div>outside<div id='insertBefore'>outside</div></div></div>");
670 EXPECT_EQ(columnSetSignature("mc"), "c");
671 reparentLayoutObject("mc", "block", "insertBefore");
672 EXPECT_EQ(columnSetSignature("mc"), "csc");
673 destroyLayoutObject("block");
674 EXPECT_EQ(columnSetSignature("mc"), "c");
677 TEST_F(MultiColumnTreeModifyingTest
, InsertSubtreeWithSpannerAfterSpannerAndRemove
)
679 setMulticolHTML("<div id='block'>text<div class='s'></div>text</div><div id='mc'><div class='s'></div></div>");
680 EXPECT_EQ(columnSetSignature("mc"), "s");
681 reparentLayoutObject("mc", "block");
682 EXPECT_EQ(columnSetSignature("mc"), "scsc");
683 destroyLayoutObject("block");
684 EXPECT_EQ(columnSetSignature("mc"), "s");
687 TEST_F(MultiColumnTreeModifyingTest
, InsertSubtreeWithSpannerBeforeSpannerAndRemove
)
689 setMulticolHTML("<div id='block'>text<div class='s'></div>text</div><div id='mc'><div id='insertBefore' class='s'></div></div>");
690 EXPECT_EQ(columnSetSignature("mc"), "s");
691 reparentLayoutObject("mc", "block", "insertBefore");
692 EXPECT_EQ(columnSetSignature("mc"), "cscs");
693 destroyLayoutObject("block");
694 EXPECT_EQ(columnSetSignature("mc"), "s");
697 TEST_F(MultiColumnTreeModifyingTest
, RemoveSpannerAndContent
)
699 setMulticolHTML("<div id='mc'><div id='block'>text<div class='s'></div>text</div></div>");
700 EXPECT_EQ(columnSetSignature("mc"), "csc");
701 destroyLayoutObject("block");
702 EXPECT_EQ(columnSetSignature("mc"), "");
705 TEST_F(MultiColumnTreeModifyingTest
, RemoveSpannerAndSomeContentBefore
)
707 setMulticolHTML("<div id='mc'>text<div id='block'>text<div class='s'></div></div></div>");
708 EXPECT_EQ(columnSetSignature("mc"), "cs");
709 destroyLayoutObject("block");
710 EXPECT_EQ(columnSetSignature("mc"), "c");
713 TEST_F(MultiColumnTreeModifyingTest
, RemoveSpannerAndAllContentBefore
)
715 setMulticolHTML("<div id='mc'><div id='block'>text<div class='s'></div></div></div>");
716 EXPECT_EQ(columnSetSignature("mc"), "cs");
717 destroyLayoutObject("block");
718 EXPECT_EQ(columnSetSignature("mc"), "");
721 TEST_F(MultiColumnTreeModifyingTest
, RemoveSpannerAndAllContentBeforeWithContentAfter
)
723 setMulticolHTML("<div id='mc'><div id='block'>text<div class='s'></div></div>text</div>");
724 EXPECT_EQ(columnSetSignature("mc"), "csc");
725 destroyLayoutObject("block");
726 EXPECT_EQ(columnSetSignature("mc"), "c");
729 TEST_F(MultiColumnTreeModifyingTest
, RemoveSpannerAndSomeContentAfter
)
731 setMulticolHTML("<div id='mc'><div id='block'><div class='s'></div>text</div>text</div>");
732 EXPECT_EQ(columnSetSignature("mc"), "csc");
733 destroyLayoutObject("block");
734 EXPECT_EQ(columnSetSignature("mc"), "c");
737 TEST_F(MultiColumnTreeModifyingTest
, RemoveSpannerAndAllContentAfter
)
739 setMulticolHTML("<div id='mc'><div id='block'><div class='s'></div>text</div></div>");
740 EXPECT_EQ(columnSetSignature("mc"), "csc");
741 destroyLayoutObject("block");
742 EXPECT_EQ(columnSetSignature("mc"), "");
745 TEST_F(MultiColumnTreeModifyingTest
, RemoveSpannerAndAllContentAfterWithContentBefore
)
747 setMulticolHTML("<div id='mc'>text<div id='block'><div class='s'></div>text</div></div>");
748 EXPECT_EQ(columnSetSignature("mc"), "csc");
749 destroyLayoutObject("block");
750 EXPECT_EQ(columnSetSignature("mc"), "c");
753 TEST_F(MultiColumnTreeModifyingTest
, RemoveTwoSpannersBeforeContent
)
755 setMulticolHTML("<div id='mc'><div id='block'><div class='s'></div><div class='s'></div></div>text</div>");
756 EXPECT_EQ(columnSetSignature("mc"), "cssc");
757 destroyLayoutObject("block");
758 EXPECT_EQ(columnSetSignature("mc"), "c");
761 TEST_F(MultiColumnTreeModifyingTest
, RemoveSpannerAndContentAndSpanner
)
763 setMulticolHTML("<div id='mc'><div id='block'><div class='s'></div>text<div class='s'></div>text</div></div>");
764 EXPECT_EQ(columnSetSignature("mc"), "cscsc");
765 destroyLayoutObject("block");
766 EXPECT_EQ(columnSetSignature("mc"), "");
769 TEST_F(MultiColumnTreeModifyingTest
, RemoveSpannerAndContentAndSpannerBeforeContent
)
771 setMulticolHTML("<div id='mc'><div id='block'><div class='s'></div>text<div class='s'></div></div>text</div>");
772 EXPECT_EQ(columnSetSignature("mc"), "cscsc");
773 destroyLayoutObject("block");
774 EXPECT_EQ(columnSetSignature("mc"), "c");
777 TEST_F(MultiColumnTreeModifyingTest
, RemoveSpannerAndContentAndSpannerAfterContent
)
779 setMulticolHTML("<div id='mc'>text<div id='block'><div class='s'></div>text<div class='s'></div></div></div>");
780 EXPECT_EQ(columnSetSignature("mc"), "cscs");
781 destroyLayoutObject("block");
782 EXPECT_EQ(columnSetSignature("mc"), "c");
785 TEST_F(MultiColumnTreeModifyingTest
, RemoveInvalidSpannerInSpannerBetweenContent
)
787 setMulticolHTML("<div id='mc'>text<div class='s'><div id='spanner'></div></div>text</div>");
788 EXPECT_EQ(columnSetSignature("mc"), "csc");
789 destroyLayoutObject("spanner");
790 EXPECT_EQ(columnSetSignature("mc"), "csc");
793 TEST_F(MultiColumnTreeModifyingTest
, RemoveSpannerWithInvalidSpannerBetweenContent
)
795 setMulticolHTML("<div id='mc'>text<div id='spanner'><div class='s'></div></div>text</div>");
796 EXPECT_EQ(columnSetSignature("mc"), "csc");
797 destroyLayoutObject("spanner");
798 EXPECT_EQ(columnSetSignature("mc"), "c");
801 } // anonymous namespace