OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (C) 2012 Apple Inc. All rights reserved. | 2 * Copyright (C) 2012 Apple Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
11 * documentation and/or other materials provided with the distribution. | 11 * documentation and/or other materials provided with the distribution. |
12 * | 12 * |
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR | 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR |
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
20 * PROFITS; OR BUSINESS IN..0TERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | 20 * PROFITS; OR BUSINESS IN..0TERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
24 */ | 24 */ |
25 | 25 |
26 #include "config.h" | 26 #include "config.h" |
27 #include "core/rendering/RenderMultiColumnFlowThread.h" | 27 #include "core/rendering/RenderMultiColumnFlowThread.h" |
28 | 28 |
29 #include "core/rendering/RenderMultiColumnSet.h" | 29 #include "core/rendering/RenderMultiColumnSet.h" |
30 #include "core/rendering/RenderMultiColumnSpannerPlaceholder.h" | |
30 | 31 |
31 namespace WebCore { | 32 namespace WebCore { |
32 | 33 |
33 RenderMultiColumnFlowThread::RenderMultiColumnFlowThread() | 34 RenderMultiColumnFlowThread::RenderMultiColumnFlowThread() |
34 : m_columnCount(1) | 35 : m_lastSetWorkedOn(0) |
36 , m_columnCount(1) | |
35 , m_columnHeightAvailable(0) | 37 , m_columnHeightAvailable(0) |
38 , m_inLayout(false) | |
36 , m_inBalancingPass(false) | 39 , m_inBalancingPass(false) |
37 , m_needsColumnHeightsRecalculation(false) | 40 , m_needsColumnHeightsRecalculation(false) |
38 , m_progressionIsInline(true) | 41 , m_progressionIsInline(true) |
42 , m_beingEvacuated(false) | |
39 { | 43 { |
40 setFlowThreadState(InsideInFlowThread); | 44 setFlowThreadState(InsideInFlowThread); |
41 } | 45 } |
42 | 46 |
43 RenderMultiColumnFlowThread::~RenderMultiColumnFlowThread() | 47 RenderMultiColumnFlowThread::~RenderMultiColumnFlowThread() |
44 { | 48 { |
45 } | 49 } |
46 | 50 |
47 RenderMultiColumnFlowThread* RenderMultiColumnFlowThread::createAnonymous(Docume nt& document, RenderStyle* parentStyle) | 51 RenderMultiColumnFlowThread* RenderMultiColumnFlowThread::createAnonymous(Docume nt& document, RenderStyle* parentStyle) |
48 { | 52 { |
(...skipping 14 matching lines...) Expand all Loading... | |
63 | 67 |
64 RenderMultiColumnSet* RenderMultiColumnFlowThread::lastMultiColumnSet() const | 68 RenderMultiColumnSet* RenderMultiColumnFlowThread::lastMultiColumnSet() const |
65 { | 69 { |
66 for (RenderObject* sibling = multiColumnBlockFlow()->lastChild(); sibling; s ibling = sibling->previousSibling()) { | 70 for (RenderObject* sibling = multiColumnBlockFlow()->lastChild(); sibling; s ibling = sibling->previousSibling()) { |
67 if (sibling->isRenderMultiColumnSet()) | 71 if (sibling->isRenderMultiColumnSet()) |
68 return toRenderMultiColumnSet(sibling); | 72 return toRenderMultiColumnSet(sibling); |
69 } | 73 } |
70 return 0; | 74 return 0; |
71 } | 75 } |
72 | 76 |
73 void RenderMultiColumnFlowThread::addChild(RenderObject* newChild, RenderObject* beforeChild) | 77 RenderBox* RenderMultiColumnFlowThread::firstColumnSetOrSpanner() const |
74 { | 78 { |
75 RenderBlockFlow::addChild(newChild, beforeChild); | 79 if (RenderObject* sibling = nextSibling()) { |
76 if (firstMultiColumnSet()) | 80 ASSERT(sibling->isBox()); |
77 return; | 81 ASSERT(sibling->isRenderMultiColumnSet() || findColumnSpannerPlaceholder (toRenderBox(sibling))); |
82 return toRenderBox(sibling); | |
83 } | |
84 return 0; | |
85 } | |
78 | 86 |
79 // For now we only create one column set. It's created as soon as the multic ol container gets | 87 RenderBox* RenderMultiColumnFlowThread::nextColumnSetOrSpannerSiblingOf(const Re nderBox* child) |
80 // any content at all. | 88 { |
81 RenderMultiColumnSet* newSet = RenderMultiColumnSet::createAnonymous(this, m ultiColumnBlockFlow()->style()); | 89 if (!child) |
90 return 0; | |
91 if (RenderObject* sibling = child->nextSibling()) { | |
92 ASSERT(sibling->isBox()); | |
93 return toRenderBox(sibling); | |
94 } | |
95 return 0; | |
96 } | |
82 | 97 |
83 // Need to skip RenderBlockFlow's implementation of addChild(), or we'd get redirected right | 98 RenderBox* RenderMultiColumnFlowThread::previousColumnSetOrSpannerSiblingOf(cons t RenderBox* child) |
84 // back here. | 99 { |
85 multiColumnBlockFlow()->RenderBlock::addChild(newSet); | 100 if (!child) |
101 return 0; | |
102 if (RenderObject* sibling = child->previousSibling()) { | |
103 ASSERT(sibling->isBox()); | |
104 if (sibling->isRenderFlowThread()) | |
105 return 0; | |
106 return toRenderBox(sibling); | |
107 } | |
108 return 0; | |
109 } | |
86 | 110 |
87 invalidateRegions(); | 111 RenderMultiColumnSet* RenderMultiColumnFlowThread::findSetRendering(RenderObject * renderer) const |
112 { | |
113 for (RenderMultiColumnSet* multicolSet = firstMultiColumnSet(); multicolSet; multicolSet = multicolSet->nextSiblingMultiColumnSet()) { | |
114 if (multicolSet->renders(renderer)) | |
115 return multicolSet; | |
116 } | |
117 return 0; | |
88 } | 118 } |
89 | 119 |
90 void RenderMultiColumnFlowThread::populate() | 120 void RenderMultiColumnFlowThread::populate() |
91 { | 121 { |
92 RenderBlockFlow* multicolContainer = multiColumnBlockFlow(); | 122 RenderBlockFlow* multicolContainer = multiColumnBlockFlow(); |
93 ASSERT(!nextSibling()); | 123 ASSERT(!nextSibling()); |
94 // Reparent children preceding the flow thread into the flow thread. It's mu lticol content | 124 // Reparent children preceding the flow thread into the flow thread. It's mu lticol content |
95 // now. At this point there's obviously nothing after the flow thread, but r enderers (column | 125 // now. At this point there's obviously nothing after the flow thread, but r enderers (column |
96 // sets and spanners) will be inserted there as we insert elements into the flow thread. | 126 // sets and spanners) will be inserted there as we insert elements into the flow thread. |
97 multicolContainer->moveChildrenTo(this, multicolContainer->firstChild(), thi s, true); | 127 multicolContainer->moveChildrenTo(this, multicolContainer->firstChild(), thi s, true); |
98 } | 128 } |
99 | 129 |
100 void RenderMultiColumnFlowThread::evacuateAndDestroy() | 130 void RenderMultiColumnFlowThread::evacuateAndDestroy() |
101 { | 131 { |
102 RenderBlockFlow* multicolContainer = multiColumnBlockFlow(); | 132 RenderBlockFlow* multicolContainer = multiColumnBlockFlow(); |
133 m_beingEvacuated = true; | |
134 | |
135 // First promote all children of the flow thread. Before we move them to the flow thread's | |
136 // container, we need to unregister the flow thread, so that they aren't jus t re-added again to | |
137 // the flow thread that we're trying to empty. | |
138 multicolContainer->resetMultiColumnFlowThread(); | |
139 moveAllChildrenTo(multicolContainer, true); | |
140 | |
141 // Move spanners back to their original DOM position in the tree, and destro y the placeholders. | |
142 SpannerMap::iterator it; | |
143 while ((it = m_spannerMap.begin()) != m_spannerMap.end()) { | |
144 RenderBox* spanner = it->key; | |
145 RenderMultiColumnSpannerPlaceholder* placeholder = it->value; | |
146 RenderBlockFlow* originalContainer = toRenderBlockFlow(placeholder->pare nt()); | |
147 multicolContainer->removeChild(spanner); | |
148 originalContainer->addChild(spanner, placeholder); | |
149 placeholder->destroy(); | |
150 m_spannerMap.remove(it); | |
151 } | |
103 | 152 |
104 // Remove all sets. | 153 // Remove all sets. |
105 while (RenderMultiColumnSet* columnSet = firstMultiColumnSet()) | 154 while (RenderMultiColumnSet* columnSet = firstMultiColumnSet()) |
106 columnSet->destroy(); | 155 columnSet->destroy(); |
107 | 156 |
108 ASSERT(!previousSibling()); | |
109 ASSERT(!nextSibling()); | |
110 | |
111 // Finally we can promote all flow thread's children. Before we move them to the flow thread's | |
112 // container, we need to unregister the flow thread, so that they aren't jus t re-added again to | |
113 // the flow thread that we're trying to empty. | |
114 multicolContainer->resetMultiColumnFlowThread(); | |
115 moveAllChildrenTo(multicolContainer, true); | |
116 | |
117 // FIXME: it's scary that neither destroy() nor the move*Children* methods t ake care of this, | 157 // FIXME: it's scary that neither destroy() nor the move*Children* methods t ake care of this, |
118 // and instead leave you with dangling root line box pointers. But since thi s is how it is done | 158 // and instead leave you with dangling root line box pointers. But since thi s is how it is done |
119 // in other parts of the code that deal with reparenting renderers, let's do the cleanup on our | 159 // in other parts of the code that deal with reparenting renderers, let's do the cleanup on our |
120 // own here as well. | 160 // own here as well. |
121 deleteLineBoxTree(); | 161 deleteLineBoxTree(); |
122 | 162 |
123 destroy(); | 163 destroy(); |
124 } | 164 } |
125 | 165 |
126 LayoutSize RenderMultiColumnFlowThread::columnOffset(const LayoutPoint& point) c onst | 166 LayoutSize RenderMultiColumnFlowThread::columnOffset(const LayoutPoint& point) c onst |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
160 m_needsColumnHeightsRecalculation = false; | 200 m_needsColumnHeightsRecalculation = false; |
161 return; | 201 return; |
162 } | 202 } |
163 | 203 |
164 for (RenderMultiColumnSet* columnSet = firstMultiColumnSet(); columnSet; col umnSet = columnSet->nextSiblingMultiColumnSet()) { | 204 for (RenderMultiColumnSet* columnSet = firstMultiColumnSet(); columnSet; col umnSet = columnSet->nextSiblingMultiColumnSet()) { |
165 if (!m_inBalancingPass) { | 205 if (!m_inBalancingPass) { |
166 // This is the initial layout pass. We need to reset the column heig ht, because contents | 206 // This is the initial layout pass. We need to reset the column heig ht, because contents |
167 // typically have changed. | 207 // typically have changed. |
168 columnSet->resetColumnHeight(); | 208 columnSet->resetColumnHeight(); |
169 } | 209 } |
210 columnSet->resetFlow(); | |
170 } | 211 } |
171 | 212 |
172 invalidateRegions(); | 213 invalidateRegions(); |
173 m_needsColumnHeightsRecalculation = heightIsAuto(); | 214 m_needsColumnHeightsRecalculation = true; |
174 layout(); | 215 layout(); |
175 } | 216 } |
176 | 217 |
177 bool RenderMultiColumnFlowThread::recalculateColumnHeights() | 218 bool RenderMultiColumnFlowThread::recalculateColumnHeights() |
178 { | 219 { |
179 // All column sets that needed layout have now been laid out, so we can fina lly validate them. | 220 // All column sets that needed layout have now been laid out, so we can fina lly validate them. |
180 validateRegions(); | 221 validateRegions(); |
181 | 222 |
182 if (!m_needsColumnHeightsRecalculation) | 223 if (!m_needsColumnHeightsRecalculation) |
183 return false; | 224 return false; |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
250 { | 291 { |
251 // Detach all column sets from the flow thread. Cannot destroy them at this point, since they | 292 // Detach all column sets from the flow thread. Cannot destroy them at this point, since they |
252 // are siblings of this object, and there may be pointers to this object's s ibling somewhere | 293 // are siblings of this object, and there may be pointers to this object's s ibling somewhere |
253 // further up on the call stack. | 294 // further up on the call stack. |
254 for (RenderMultiColumnSet* columnSet = firstMultiColumnSet(); columnSet; col umnSet = columnSet->nextSiblingMultiColumnSet()) | 295 for (RenderMultiColumnSet* columnSet = firstMultiColumnSet(); columnSet; col umnSet = columnSet->nextSiblingMultiColumnSet()) |
255 columnSet->detachRegion(); | 296 columnSet->detachRegion(); |
256 multiColumnBlockFlow()->resetMultiColumnFlowThread(); | 297 multiColumnBlockFlow()->resetMultiColumnFlowThread(); |
257 RenderFlowThread::willBeRemovedFromTree(); | 298 RenderFlowThread::willBeRemovedFromTree(); |
258 } | 299 } |
259 | 300 |
301 RenderObject* RenderMultiColumnFlowThread::resolveMovedChild(RenderObject* child ) const | |
302 { | |
303 if (child->style()->columnSpan() != ColumnSpanAll || !child->isBox()) { | |
304 // We only need to resolve for column spanners. | |
305 return child; | |
306 } | |
307 // The renderer for the actual DOM node that establishes a spanner is moved from its original | |
308 // location in the render tree to becoming a sibling of the column sets. In other words, it's | |
309 // moved out from the flow thread (and becomes a sibling of it). When we for instance want to | |
310 // create and insert a renderer for the sibling node immediately preceding t he spanner, we need | |
311 // to map that spanner renderer to the spanner's placeholder, which is where the new inserted | |
312 // renderer belongs. | |
313 if (RenderMultiColumnSpannerPlaceholder* placeholder = findColumnSpannerPlac eholder(toRenderBox(child))) | |
314 return placeholder; | |
315 | |
316 // This is an invalid spanner, or its placeholder hasn't been created yet. T his happens when | |
317 // moving an entire subtree into the flow thread, when we are processing the insertion of this | |
318 // spanner's preceding sibling, and we obviously haven't got as far as proce ssing this spanner | |
319 // yet. | |
320 return child; | |
321 } | |
322 | |
323 static bool isValidColumnSpanner(RenderMultiColumnFlowThread* flowThread, Render Object* descendant) | |
rune
2014/06/19 14:24:32
Why not make this a member method of the RenderMul
mstensho (USE GERRIT)
2014/08/26 09:43:58
Done.
rune
2014/08/27 08:17:38
Acknowledged.
| |
324 { | |
325 // We assume that we're inside the flow thread. This function is not to be c alled otherwise. | |
326 ASSERT(descendant->isDescendantOf(flowThread)); | |
327 | |
328 if (flowThread->isRenderPagedFlowThread()) | |
329 return false; // Spanners are for true multicol only. | |
330 | |
331 // First make sure that the renderer itself has the right properties for bec oming a spanner. | |
332 RenderStyle* style = descendant->style(); | |
333 if (style->columnSpan() != ColumnSpanAll || !descendant->isBox() || descenda nt->isFloatingOrOutOfFlowPositioned()) | |
334 return false; | |
335 | |
336 RenderBlock* container = descendant->containingBlock(); | |
337 if (!container->isRenderBlockFlow() || container->childrenInline()) { | |
338 // Needs to be block-level. | |
339 return false; | |
340 } | |
341 | |
342 // This looks like a spanner, but if we're inside something unbreakable, it' s not to be treated as one. | |
343 for (RenderBox* ancestor = toRenderBox(descendant)->parentBox(); ancestor; a ncestor = ancestor->parentBox()) { | |
344 if (ancestor->isRenderFlowThread()) { | |
345 // Don't allow any intervening non-multicol fragmentation contexts. The spec doesn't say | |
346 // anything about disallowing this, but it's just going to be too co mplicated to | |
347 // implement (not to mention specify behavior). | |
348 return ancestor == flowThread; | |
349 } | |
350 ASSERT(ancestor->style()->columnSpan() != ColumnSpanAll || !isValidColum nSpanner(flowThread, ancestor)); | |
351 if (ancestor->isUnsplittableForPagination()) | |
352 return false; | |
353 } | |
354 ASSERT_NOT_REACHED(); | |
355 return false; | |
356 } | |
357 | |
358 void RenderMultiColumnFlowThread::flowThreadDescendantInserted(RenderObject* des cendant) | |
359 { | |
360 if (m_beingEvacuated) | |
361 return; | |
362 RenderObject* subtreeRoot = descendant; | |
363 for (; descendant; descendant = descendant->nextInPreOrder(subtreeRoot)) { | |
364 if (descendant->isRenderMultiColumnSpannerPlaceholder()) { | |
365 // A spanner's placeholder has been inserted. The actual spanner ren derer is moved from | |
366 // where it wound otherwise occur (if it weren't a spanner) to becom ing a sibling of the | |
rune
2014/06/19 14:24:33
"where it _would_ otherwise ..."
mstensho (USE GERRIT)
2014/08/26 09:43:58
Done.
rune
2014/08/27 08:17:38
Acknowledged.
| |
367 // column sets. | |
368 RenderMultiColumnSpannerPlaceholder* placeholder = toRenderMultiColu mnSpannerPlaceholder(descendant); | |
369 ASSERT(!m_spannerMap.get(placeholder->spanner())); | |
370 m_spannerMap.add(placeholder->spanner(), placeholder); | |
371 ASSERT(!placeholder->slowFirstChild()); // There should be no childr en here, but if there are, we ought to skip them. | |
372 continue; | |
373 } | |
374 RenderBlockFlow* multicolContainer = multiColumnBlockFlow(); | |
375 RenderObject* nextRendererInFlowThread = descendant->nextInPreOrderAfter Children(this); | |
376 RenderObject* insertBeforeMulticolChild = 0; | |
377 if (isValidColumnSpanner(this, descendant)) { | |
378 // This is a spanner (column-span:all). Such renderers are moved fro m where they would | |
379 // otherwise occur in the render tree to becoming a direct child of the multicol container, | |
380 // so that they live among the column sets. This simplifies the layo ut implementation, and | |
381 // basically just relies on regular block layout done by the RenderB lockFlow that | |
382 // establishes the multicol container. | |
383 RenderBlockFlow* container = toRenderBlockFlow(descendant->parent()) ; | |
384 RenderMultiColumnSet* setToSplit = 0; | |
385 if (nextRendererInFlowThread) { | |
386 setToSplit = findSetRendering(descendant); | |
387 if (setToSplit) { | |
388 setToSplit->setNeedsLayoutAndFullRepaint(); | |
389 insertBeforeMulticolChild = setToSplit->nextSibling(); | |
390 } | |
391 } | |
392 // Moving a spanner's renderer so that it becomes a sibling of the c olumn sets requires us | |
393 // to insert an anonymous placeholder in the tree where the spanner' s renderer otherwise | |
394 // would have been. This is needed for a two reasons: We need a way of separating inline | |
rune
2014/06/19 14:24:32
"needed for a two reasons"?
mstensho (USE GERRIT)
2014/08/26 09:43:58
Done.
rune
2014/08/27 08:17:38
Acknowledged.
| |
395 // content before and after the spanner, so that it becomes separate line boxes. Secondly, | |
396 // this placeholder serves as a break point for column sets, so that , when encountered, we | |
397 // end flowing one column set and move to the next one. | |
rune
2014/06/19 14:24:33
"move _on_ to the next one" perhaps?
mstensho (USE GERRIT)
2014/08/26 09:43:58
Done.
rune
2014/08/27 08:17:38
Acknowledged.
| |
398 RenderMultiColumnSpannerPlaceholder* placeholder = RenderMultiColumn SpannerPlaceholder::createAnonymous(this, toRenderBox(descendant), container->st yle()); | |
399 container->addChild(placeholder, descendant->nextSibling()); | |
400 container->removeChild(descendant); | |
401 multicolContainer->RenderBlock::addChild(descendant, insertBeforeMul ticolChild); | |
402 | |
403 // The spanner has now been moved out from the flow thread, but we d on't want to | |
404 // examine its children anyway. They are all part of the spanner and shouldn't trigger | |
405 // creation of column sets or anything like that. Continue at its or iginal position in | |
406 // the tree, i.e. where the placeholder was just put. | |
407 if (subtreeRoot == descendant) | |
408 subtreeRoot = placeholder; | |
409 descendant = placeholder; | |
410 } else { | |
411 // This is regular multicol content, i.e. not part of a spanner. | |
412 if (nextRendererInFlowThread && nextRendererInFlowThread->isRenderMu ltiColumnSpannerPlaceholder()) { | |
413 // Inserted right before a spanner. Is there a set for us there? | |
414 RenderMultiColumnSpannerPlaceholder* placeholder = toRenderMulti ColumnSpannerPlaceholder(nextRendererInFlowThread); | |
415 if (RenderObject* previous = placeholder->spanner()->previousSib ling()) { | |
416 if (previous->isRenderMultiColumnSet()) | |
417 continue; // There's already a set there. Nothing to do. | |
418 } | |
419 insertBeforeMulticolChild = placeholder->spanner(); | |
420 } else if (RenderMultiColumnSet* lastSet = lastMultiColumnSet()) { | |
421 // This child is not an immediate predecessor of a spanner, whic h means that if this | |
422 // child precedes a spanner at all, there has to be a column set created for us there | |
423 // already. If it doesn't precede any spanner at all, on the oth er hand, we need a | |
424 // column set at the end of the multicol container. We don't rea lly check here if the | |
425 // child inserted precedes any spanner or not (as that's an expe nsive operation). Just | |
426 // make sure we have a column set at the end. It's no big deal i f it remains unused. | |
427 if (!lastSet->nextSibling()) | |
428 continue; | |
429 } | |
430 } | |
431 // Need to create a new column set when there's no set already created. We also always insert | |
432 // another column set after a spanner. Even if it turns out that there a re no renderers | |
433 // following the spanner, there may be bottom margins there, which take up space. | |
434 RenderMultiColumnSet* newSet = RenderMultiColumnSet::createAnonymous(thi s, multicolContainer->style()); | |
435 multicolContainer->RenderBlock::addChild(newSet, insertBeforeMulticolChi ld); | |
436 invalidateRegions(); | |
437 | |
438 // We cannot handle immediate column set siblings at the moment (and the re's no need for | |
439 // it, either). There has to be at least one spanner separating them. | |
440 ASSERT(!previousColumnSetOrSpannerSiblingOf(newSet) || !previousColumnSe tOrSpannerSiblingOf(newSet)->isRenderMultiColumnSet()); | |
441 ASSERT(!nextColumnSetOrSpannerSiblingOf(newSet) || !nextColumnSetOrSpann erSiblingOf(newSet)->isRenderMultiColumnSet()); | |
442 } | |
443 } | |
444 | |
445 void RenderMultiColumnFlowThread::flowThreadRelativeWillBeRemoved(RenderObject* relative) | |
446 { | |
447 if (m_beingEvacuated) | |
448 return; | |
449 invalidateRegions(); | |
450 if (relative->isRenderMultiColumnSpannerPlaceholder()) { | |
451 // Remove the map entry for this spanner, but leave the actual spanner r enderer alone. Also | |
452 // keep the reference to the spanner, since the placeholder may be about to be re-inserted | |
453 // in the tree. | |
rune
2014/06/19 14:24:32
"into the tree"
mstensho (USE GERRIT)
2014/08/26 09:43:58
Done.
rune
2014/08/27 08:17:38
Acknowledged.
| |
454 ASSERT(relative->isDescendantOf(this)); | |
455 m_spannerMap.remove(toRenderMultiColumnSpannerPlaceholder(relative)->spa nner()); | |
456 return; | |
457 } | |
458 if (relative->style()->columnSpan() == ColumnSpanAll) { | |
459 if (relative->parent() != parent()) | |
460 return; // not a valid spanner. | |
461 | |
462 // The placeholder may already have been removed, but if it hasn't, do s o now. | |
463 if (RenderMultiColumnSpannerPlaceholder* placeholder = m_spannerMap.get( toRenderBox(relative))) { | |
464 placeholder->containingBlock()->RenderBlock::removeChild(placeholder ); | |
465 m_spannerMap.remove(toRenderBox(relative)); | |
466 } | |
467 | |
468 if (RenderObject* next = relative->nextSibling()) { | |
469 if (RenderObject* previous = relative->previousSibling()) { | |
470 if (previous->isRenderMultiColumnSet() && next->isRenderMultiCol umnSet()) { | |
471 // Merge two sets that no longer will be separated by a span ner. | |
472 next->destroy(); | |
473 previous->setNeedsLayoutAndFullRepaint(); | |
474 } | |
475 } | |
476 } | |
477 } | |
478 // Note that we might end up with empty column sets if all column content is removed. That's no | |
479 // big deal though (and locating them would be expensive), and they will be found and re-used if | |
480 // content is added again later. | |
481 } | |
482 | |
483 void RenderMultiColumnFlowThread::flowThreadDescendantBoxLaidOut(RenderBox* desc endant, LayoutUnit logicalBottomInFlowThread) | |
484 { | |
485 if (!descendant->isRenderMultiColumnSpannerPlaceholder()) | |
486 return; | |
487 RenderMultiColumnSpannerPlaceholder* placeholder = toRenderMultiColumnSpanne rPlaceholder(descendant); | |
488 | |
489 for (RenderBox* prev = previousColumnSetOrSpannerSiblingOf(placeholder->span ner()); prev; prev = previousColumnSetOrSpannerSiblingOf(prev)) { | |
490 if (prev->isRenderMultiColumnSet()) { | |
491 toRenderMultiColumnSet(prev)->endFlow(logicalBottomInFlowThread); | |
492 break; | |
493 } | |
494 } | |
495 | |
496 for (RenderBox* next = nextColumnSetOrSpannerSiblingOf(placeholder->spanner( )); next; next = nextColumnSetOrSpannerSiblingOf(next)) { | |
497 if (next->isRenderMultiColumnSet()) { | |
498 m_lastSetWorkedOn = toRenderMultiColumnSet(next); | |
499 m_lastSetWorkedOn->beginFlow(logicalBottomInFlowThread); | |
500 break; | |
501 } | |
502 } | |
503 } | |
504 | |
260 void RenderMultiColumnFlowThread::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const | 505 void RenderMultiColumnFlowThread::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const |
261 { | 506 { |
262 // We simply remain at our intrinsic height. | 507 // We simply remain at our intrinsic height. |
263 computedValues.m_extent = logicalHeight; | 508 computedValues.m_extent = logicalHeight; |
264 computedValues.m_position = logicalTop; | 509 computedValues.m_position = logicalTop; |
265 } | 510 } |
266 | 511 |
267 void RenderMultiColumnFlowThread::updateLogicalWidth() | 512 void RenderMultiColumnFlowThread::updateLogicalWidth() |
268 { | 513 { |
269 LayoutUnit columnWidth; | 514 LayoutUnit columnWidth; |
270 calculateColumnCountAndWidth(columnWidth, m_columnCount); | 515 calculateColumnCountAndWidth(columnWidth, m_columnCount); |
271 setLogicalWidth(columnWidth); | 516 setLogicalWidth(columnWidth); |
272 } | 517 } |
273 | 518 |
274 void RenderMultiColumnFlowThread::layout() | 519 void RenderMultiColumnFlowThread::layout() |
275 { | 520 { |
521 ASSERT(!m_inLayout); | |
522 m_inLayout = true; | |
523 m_lastSetWorkedOn = 0; | |
524 if (RenderBox* first = firstColumnSetOrSpanner()) { | |
525 if (first->isRenderMultiColumnSet()) { | |
526 m_lastSetWorkedOn = toRenderMultiColumnSet(first); | |
527 m_lastSetWorkedOn->beginFlow(LayoutUnit()); | |
528 } | |
529 } | |
276 RenderFlowThread::layout(); | 530 RenderFlowThread::layout(); |
277 if (RenderMultiColumnSet* lastSet = lastMultiColumnSet()) | 531 if (RenderMultiColumnSet* lastSet = lastMultiColumnSet()) { |
532 if (!nextColumnSetOrSpannerSiblingOf(lastSet)) | |
533 lastSet->endFlow(logicalHeight()); | |
278 lastSet->expandToEncompassFlowThreadContentsIfNeeded(); | 534 lastSet->expandToEncompassFlowThreadContentsIfNeeded(); |
535 } | |
536 m_inLayout = false; | |
537 m_lastSetWorkedOn = 0; | |
279 } | 538 } |
280 | 539 |
281 void RenderMultiColumnFlowThread::setPageBreak(LayoutUnit offset, LayoutUnit spa ceShortage) | 540 void RenderMultiColumnFlowThread::setPageBreak(LayoutUnit offset, LayoutUnit spa ceShortage) |
282 { | 541 { |
283 // Only positive values are interesting (and allowed) here. Zero space short age may be reported | 542 // Only positive values are interesting (and allowed) here. Zero space short age may be reported |
284 // when we're at the top of a column and the element has zero height. Ignore this, and also | 543 // when we're at the top of a column and the element has zero height. Ignore this, and also |
285 // ignore any negative values, which may occur when we set an early break in order to honor | 544 // ignore any negative values, which may occur when we set an early break in order to honor |
286 // widows in the next column. | 545 // widows in the next column. |
287 if (spaceShortage <= 0) | 546 if (spaceShortage <= 0) |
288 return; | 547 return; |
289 | 548 |
290 if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlock Offset(offset))) | 549 if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlock Offset(offset))) |
291 multicolSet->recordSpaceShortage(spaceShortage); | 550 multicolSet->recordSpaceShortage(spaceShortage); |
292 } | 551 } |
293 | 552 |
294 void RenderMultiColumnFlowThread::updateMinimumPageHeight(LayoutUnit offset, Lay outUnit minHeight) | 553 void RenderMultiColumnFlowThread::updateMinimumPageHeight(LayoutUnit offset, Lay outUnit minHeight) |
295 { | 554 { |
296 if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlock Offset(offset))) | 555 if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlock Offset(offset))) |
297 multicolSet->updateMinimumColumnHeight(minHeight); | 556 multicolSet->updateMinimumColumnHeight(minHeight); |
298 } | 557 } |
299 | 558 |
300 RenderRegion* RenderMultiColumnFlowThread::regionAtBlockOffset(LayoutUnit /*offs et*/) const | 559 RenderRegion* RenderMultiColumnFlowThread::regionAtBlockOffset(LayoutUnit offset ) const |
301 { | 560 { |
302 // For now there's only one column set, so this is easy: | 561 if (!m_inLayout) |
303 return firstMultiColumnSet(); | 562 return RenderFlowThread::regionAtBlockOffset(offset); |
rune
2014/06/19 14:24:33
A single set is still the case for RenderPagedFlow
mstensho (USE GERRIT)
2014/08/26 09:43:58
Yes.
rune
2014/08/27 08:17:38
Acknowledged.
| |
563 | |
564 // Layout in progress. We are calculating the set heights as we speak, so th e region range | |
565 // information is not up-to-date. | |
566 | |
567 RenderMultiColumnSet* columnSet = m_lastSetWorkedOn ? m_lastSetWorkedOn : fi rstMultiColumnSet(); | |
568 if (!columnSet) { | |
569 // If there's no set, bail. This multicol is empty or only consists of s panners. There | |
570 // are no regions. | |
571 return 0; | |
572 } | |
573 // The last set worked on is a good guess. But if we're not within the bound s, search for the | |
574 // right one. | |
575 if (offset < columnSet->logicalTopInFlowThread()) { | |
576 do { | |
577 if (RenderMultiColumnSet* prev = columnSet->previousSiblingMultiColu mnSet()) | |
578 columnSet = prev; | |
579 else | |
580 break; | |
581 } while (offset < columnSet->logicalTopInFlowThread()); | |
582 } else { | |
583 while (offset >= columnSet->logicalBottomInFlowThread()) { | |
584 RenderMultiColumnSet* next = columnSet->nextSiblingMultiColumnSet(); | |
585 if (!next || !next->hasBeenFlowed()) | |
586 break; | |
rune
2014/06/19 14:24:33
If I understand this correctly, if we ask for a re
mstensho (USE GERRIT)
2014/08/26 09:43:58
Done.
I'm not aware of any usecase for searching
rune
2014/08/27 08:17:38
Acknowledged.
| |
587 columnSet = next; | |
588 } | |
589 } | |
590 return columnSet; | |
304 } | 591 } |
305 | 592 |
306 bool RenderMultiColumnFlowThread::addForcedRegionBreak(LayoutUnit offset, Render Object* /*breakChild*/, bool /*isBefore*/, LayoutUnit* offsetBreakAdjustment) | 593 bool RenderMultiColumnFlowThread::addForcedRegionBreak(LayoutUnit offset, Render Object* breakChild, bool /*isBefore*/, LayoutUnit* offsetBreakAdjustment) |
307 { | 594 { |
308 if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlock Offset(offset))) { | 595 if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlock Offset(offset))) { |
309 multicolSet->addContentRun(offset); | 596 // Spanner placeholders force a break (to make sure that the unused part of the last column |
597 // is empty), but this break should not affect column balancing. | |
598 if (!breakChild->isRenderMultiColumnSpannerPlaceholder()) | |
599 multicolSet->addContentRun(offset); | |
310 if (offsetBreakAdjustment) | 600 if (offsetBreakAdjustment) |
311 *offsetBreakAdjustment = pageLogicalHeightForOffset(offset) ? pageRe mainingLogicalHeightForOffset(offset, IncludePageBoundary) : LayoutUnit(); | 601 *offsetBreakAdjustment = pageLogicalHeightForOffset(offset) ? pageRe mainingLogicalHeightForOffset(offset, IncludePageBoundary) : LayoutUnit(); |
312 return true; | 602 return true; |
313 } | 603 } |
314 return false; | 604 return false; |
315 } | 605 } |
316 | 606 |
317 bool RenderMultiColumnFlowThread::isPageLogicalHeightKnown() const | 607 bool RenderMultiColumnFlowThread::isPageLogicalHeightKnown() const |
318 { | 608 { |
319 if (RenderMultiColumnSet* columnSet = lastMultiColumnSet()) | 609 if (RenderMultiColumnSet* columnSet = lastMultiColumnSet()) |
320 return columnSet->pageLogicalHeight(); | 610 return columnSet->pageLogicalHeight(); |
321 return false; | 611 return false; |
322 } | 612 } |
323 | 613 |
324 } | 614 } |
OLD | NEW |