Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(609)

Side by Side Diff: Source/core/rendering/RenderMultiColumnFlowThread.cpp

Issue 296413007: [New Multicolumn] Add support for column-span:all (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@359976
Patch Set: code review Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 blink { 32 namespace blink {
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_columnSetHeightsAreUnknown(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
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 RenderMultiColumnSet* multicolSet = firstMultiColumnSet();
114 if (!multicolSet)
115 return 0;
116
117 if (!multicolSet->nextSiblingMultiColumnSet()) {
118 // There is only one set. This is easy, then: if it's in the flow thread , it's part of the set.
119 return renderer->isDescendantOf(this) ? multicolSet : 0;
120 }
121
122 // This is SLOW! But luckily very uncommon. You need to dynamically insert a spanner into the
123 // middle of the tree to trigger this.
124 for (; multicolSet; multicolSet = multicolSet->nextSiblingMultiColumnSet()) {
125 RenderObject* startRenderer;
126 if (RenderBox* sibling = previousColumnSetOrSpannerSiblingOf(multicolSet )) {
127 // Adjacent sets should not occur. Currently we would have no way of figuring out what each
128 // of them contains then.
129 ASSERT(!sibling->isRenderMultiColumnSet());
130 startRenderer = findColumnSpannerPlaceholder(sibling)->nextInPreOrde rAfterChildren(this);
131 } else {
132 startRenderer = firstChild();
133 }
134 ASSERT(startRenderer);
135
136 RenderObject* stopRenderer;
137 if (RenderBox* sibling = nextColumnSetOrSpannerSiblingOf(multicolSet)) {
138 // Adjacent sets should not occur. Currently we would have no way of figuring out what each
139 // of them contains then.
140 ASSERT(!sibling->isRenderMultiColumnSet());
141 stopRenderer = findColumnSpannerPlaceholder(sibling);
142 } else {
143 stopRenderer = 0;
144 }
145
146 for (RenderObject* walker = startRenderer; walker != stopRenderer; walke r = walker->nextInPreOrder(this)) {
147 if (walker == renderer)
148 return multicolSet;
149 }
150 }
151
152 return 0;
88 } 153 }
89 154
90 void RenderMultiColumnFlowThread::populate() 155 void RenderMultiColumnFlowThread::populate()
91 { 156 {
92 RenderBlockFlow* multicolContainer = multiColumnBlockFlow(); 157 RenderBlockFlow* multicolContainer = multiColumnBlockFlow();
93 ASSERT(!nextSibling()); 158 ASSERT(!nextSibling());
94 // Reparent children preceding the flow thread into the flow thread. It's mu lticol content 159 // 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 160 // 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. 161 // sets and spanners) will be inserted there as we insert elements into the flow thread.
97 multicolContainer->moveChildrenTo(this, multicolContainer->firstChild(), thi s, true); 162 multicolContainer->moveChildrenTo(this, multicolContainer->firstChild(), thi s, true);
98 } 163 }
99 164
100 void RenderMultiColumnFlowThread::evacuateAndDestroy() 165 void RenderMultiColumnFlowThread::evacuateAndDestroy()
101 { 166 {
102 RenderBlockFlow* multicolContainer = multiColumnBlockFlow(); 167 RenderBlockFlow* multicolContainer = multiColumnBlockFlow();
168 m_beingEvacuated = true;
169
170 // First promote all children of the flow thread. Before we move them to the flow thread's
171 // container, we need to unregister the flow thread, so that they aren't jus t re-added again to
172 // the flow thread that we're trying to empty.
173 multicolContainer->resetMultiColumnFlowThread();
174 moveAllChildrenTo(multicolContainer, true);
175
176 // Move spanners back to their original DOM position in the tree, and destro y the placeholders.
177 SpannerMap::iterator it;
178 while ((it = m_spannerMap.begin()) != m_spannerMap.end()) {
179 RenderBox* spanner = it->key;
180 RenderMultiColumnSpannerPlaceholder* placeholder = it->value;
181 RenderBlockFlow* originalContainer = toRenderBlockFlow(placeholder->pare nt());
182 multicolContainer->removeChild(spanner);
183 originalContainer->addChild(spanner, placeholder);
184 placeholder->destroy();
185 m_spannerMap.remove(it);
186 }
103 187
104 // Remove all sets. 188 // Remove all sets.
105 while (RenderMultiColumnSet* columnSet = firstMultiColumnSet()) 189 while (RenderMultiColumnSet* columnSet = firstMultiColumnSet())
106 columnSet->destroy(); 190 columnSet->destroy();
107 191
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, 192 // 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 193 // 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 194 // in other parts of the code that deal with reparenting renderers, let's do the cleanup on our
120 // own here as well. 195 // own here as well.
121 deleteLineBoxTree(); 196 deleteLineBoxTree();
122 197
123 destroy(); 198 destroy();
124 } 199 }
125 200
126 LayoutSize RenderMultiColumnFlowThread::columnOffset(const LayoutPoint& point) c onst 201 LayoutSize RenderMultiColumnFlowThread::columnOffset(const LayoutPoint& point) c onst
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
160 m_needsColumnHeightsRecalculation = false; 235 m_needsColumnHeightsRecalculation = false;
161 return; 236 return;
162 } 237 }
163 238
164 for (RenderMultiColumnSet* columnSet = firstMultiColumnSet(); columnSet; col umnSet = columnSet->nextSiblingMultiColumnSet()) { 239 for (RenderMultiColumnSet* columnSet = firstMultiColumnSet(); columnSet; col umnSet = columnSet->nextSiblingMultiColumnSet()) {
165 if (!m_inBalancingPass) { 240 if (!m_inBalancingPass) {
166 // This is the initial layout pass. We need to reset the column heig ht, because contents 241 // This is the initial layout pass. We need to reset the column heig ht, because contents
167 // typically have changed. 242 // typically have changed.
168 columnSet->resetColumnHeight(); 243 columnSet->resetColumnHeight();
169 } 244 }
245 columnSet->resetFlow();
170 } 246 }
171 247
172 invalidateRegions(); 248 invalidateRegions();
173 m_needsColumnHeightsRecalculation = heightIsAuto(); 249 m_needsColumnHeightsRecalculation = true;
174 layout(); 250 layout();
175 } 251 }
176 252
177 bool RenderMultiColumnFlowThread::recalculateColumnHeights() 253 bool RenderMultiColumnFlowThread::recalculateColumnHeights()
178 { 254 {
179 // All column sets that needed layout have now been laid out, so we can fina lly validate them. 255 // All column sets that needed layout have now been laid out, so we can fina lly validate them.
180 validateRegions(); 256 validateRegions();
181 257
182 if (!m_needsColumnHeightsRecalculation) 258 if (!m_needsColumnHeightsRecalculation)
183 return false; 259 return false;
(...skipping 15 matching lines...) Expand all
199 } 275 }
200 } 276 }
201 277
202 if (needsRelayout) 278 if (needsRelayout)
203 setChildNeedsLayout(MarkOnlyThis); 279 setChildNeedsLayout(MarkOnlyThis);
204 280
205 m_inBalancingPass = needsRelayout; 281 m_inBalancingPass = needsRelayout;
206 return needsRelayout; 282 return needsRelayout;
207 } 283 }
208 284
285 void RenderMultiColumnFlowThread::advanceToNextColumnSet(RenderMultiColumnSpanne rPlaceholder* placeholder)
286 {
287 LayoutUnit logicalBottomInFlowThread = placeholder->offsetFromLogicalTopOfFi rstPage();
288 for (RenderBox* prev = previousColumnSetOrSpannerSiblingOf(placeholder->span ner()); prev; prev = previousColumnSetOrSpannerSiblingOf(prev)) {
289 if (!prev->isRenderMultiColumnSet())
290 continue;
291 toRenderMultiColumnSet(prev)->endFlow(logicalBottomInFlowThread);
292 break;
293 }
294
295 for (RenderBox* next = nextColumnSetOrSpannerSiblingOf(placeholder->spanner( )); next; next = nextColumnSetOrSpannerSiblingOf(next)) {
296 if (!next->isRenderMultiColumnSet())
297 continue;
298 m_lastSetWorkedOn = toRenderMultiColumnSet(next);
299 m_lastSetWorkedOn->beginFlow(logicalBottomInFlowThread);
300 break;
301 }
302 }
303
209 void RenderMultiColumnFlowThread::calculateColumnCountAndWidth(LayoutUnit& width , unsigned& count) const 304 void RenderMultiColumnFlowThread::calculateColumnCountAndWidth(LayoutUnit& width , unsigned& count) const
210 { 305 {
211 RenderBlock* columnBlock = multiColumnBlockFlow(); 306 RenderBlock* columnBlock = multiColumnBlockFlow();
212 const RenderStyle* columnStyle = columnBlock->style(); 307 const RenderStyle* columnStyle = columnBlock->style();
213 LayoutUnit availableWidth = columnBlock->contentLogicalWidth(); 308 LayoutUnit availableWidth = columnBlock->contentLogicalWidth();
214 LayoutUnit columnGap = columnBlock->columnGap(); 309 LayoutUnit columnGap = columnBlock->columnGap();
215 LayoutUnit computedColumnWidth = max<LayoutUnit>(1, LayoutUnit(columnStyle-> columnWidth())); 310 LayoutUnit computedColumnWidth = max<LayoutUnit>(1, LayoutUnit(columnStyle-> columnWidth()));
216 unsigned computedColumnCount = max<int>(1, columnStyle->columnCount()); 311 unsigned computedColumnCount = max<int>(1, columnStyle->columnCount());
217 312
218 ASSERT(!columnStyle->hasAutoColumnCount() || !columnStyle->hasAutoColumnWidt h()); 313 ASSERT(!columnStyle->hasAutoColumnCount() || !columnStyle->hasAutoColumnWidt h());
219 if (columnStyle->hasAutoColumnWidth() && !columnStyle->hasAutoColumnCount()) { 314 if (columnStyle->hasAutoColumnWidth() && !columnStyle->hasAutoColumnCount()) {
220 count = computedColumnCount; 315 count = computedColumnCount;
221 width = std::max<LayoutUnit>(0, (availableWidth - ((count - 1) * columnG ap)) / count); 316 width = std::max<LayoutUnit>(0, (availableWidth - ((count - 1) * columnG ap)) / count);
222 } else if (!columnStyle->hasAutoColumnWidth() && columnStyle->hasAutoColumnC ount()) { 317 } else if (!columnStyle->hasAutoColumnWidth() && columnStyle->hasAutoColumnC ount()) {
223 count = std::max<LayoutUnit>(1, (availableWidth + columnGap) / (computed ColumnWidth + columnGap)); 318 count = std::max<LayoutUnit>(1, (availableWidth + columnGap) / (computed ColumnWidth + columnGap));
224 width = ((availableWidth + columnGap) / count) - columnGap; 319 width = ((availableWidth + columnGap) / count) - columnGap;
225 } else { 320 } else {
226 count = std::max<LayoutUnit>(std::min<LayoutUnit>(computedColumnCount, ( availableWidth + columnGap) / (computedColumnWidth + columnGap)), 1); 321 count = std::max<LayoutUnit>(std::min<LayoutUnit>(computedColumnCount, ( availableWidth + columnGap) / (computedColumnWidth + columnGap)), 1);
227 width = ((availableWidth + columnGap) / count) - columnGap; 322 width = ((availableWidth + columnGap) / count) - columnGap;
228 } 323 }
229 } 324 }
230 325
326 bool RenderMultiColumnFlowThread::isDescendantValidColumnSpanner(RenderObject* d escendant) const
327 {
328 // We assume that we're inside the flow thread. This function is not to be c alled otherwise.
329 ASSERT(descendant->isDescendantOf(this));
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 == this;
349 }
350 ASSERT(ancestor->style()->columnSpan() != ColumnSpanAll || !isDescendant ValidColumnSpanner(ancestor));
351 if (ancestor->isUnsplittableForPagination())
352 return false;
353 }
354 ASSERT_NOT_REACHED();
355 return false;
356 }
357
231 const char* RenderMultiColumnFlowThread::renderName() const 358 const char* RenderMultiColumnFlowThread::renderName() const
232 { 359 {
233 return "RenderMultiColumnFlowThread"; 360 return "RenderMultiColumnFlowThread";
234 } 361 }
235 362
236 void RenderMultiColumnFlowThread::addRegionToThread(RenderMultiColumnSet* column Set) 363 void RenderMultiColumnFlowThread::addRegionToThread(RenderMultiColumnSet* column Set)
237 { 364 {
238 if (RenderMultiColumnSet* nextSet = columnSet->nextSiblingMultiColumnSet()) { 365 if (RenderMultiColumnSet* nextSet = columnSet->nextSiblingMultiColumnSet()) {
239 RenderMultiColumnSetList::iterator it = m_multiColumnSetList.find(nextSe t); 366 RenderMultiColumnSetList::iterator it = m_multiColumnSetList.find(nextSe t);
240 ASSERT(it != m_multiColumnSetList.end()); 367 ASSERT(it != m_multiColumnSetList.end());
241 m_multiColumnSetList.insertBefore(it, columnSet); 368 m_multiColumnSetList.insertBefore(it, columnSet);
242 } else { 369 } else {
243 m_multiColumnSetList.add(columnSet); 370 m_multiColumnSetList.add(columnSet);
244 } 371 }
245 columnSet->setIsValid(true); 372 columnSet->setIsValid(true);
246 } 373 }
247 374
248 void RenderMultiColumnFlowThread::willBeRemovedFromTree() 375 void RenderMultiColumnFlowThread::willBeRemovedFromTree()
249 { 376 {
250 // Detach all column sets from the flow thread. Cannot destroy them at this point, since they 377 // Detach all column sets from the flow thread. Cannot destroy them at this point, since they
251 // are siblings of this object, and there may be pointers to this object's s ibling somewhere 378 // are siblings of this object, and there may be pointers to this object's s ibling somewhere
252 // further up on the call stack. 379 // further up on the call stack.
253 for (RenderMultiColumnSet* columnSet = firstMultiColumnSet(); columnSet; col umnSet = columnSet->nextSiblingMultiColumnSet()) 380 for (RenderMultiColumnSet* columnSet = firstMultiColumnSet(); columnSet; col umnSet = columnSet->nextSiblingMultiColumnSet())
254 columnSet->detachRegion(); 381 columnSet->detachRegion();
255 multiColumnBlockFlow()->resetMultiColumnFlowThread(); 382 multiColumnBlockFlow()->resetMultiColumnFlowThread();
256 RenderFlowThread::willBeRemovedFromTree(); 383 RenderFlowThread::willBeRemovedFromTree();
257 } 384 }
258 385
386 RenderObject* RenderMultiColumnFlowThread::resolveMovedChild(RenderObject* child ) const
387 {
388 if (child->style()->columnSpan() != ColumnSpanAll || !child->isBox()) {
389 // We only need to resolve for column spanners.
390 return child;
391 }
392 // The renderer for the actual DOM node that establishes a spanner is moved from its original
393 // location in the render tree to becoming a sibling of the column sets. In other words, it's
394 // moved out from the flow thread (and becomes a sibling of it). When we for instance want to
395 // create and insert a renderer for the sibling node immediately preceding t he spanner, we need
396 // to map that spanner renderer to the spanner's placeholder, which is where the new inserted
397 // renderer belongs.
398 if (RenderMultiColumnSpannerPlaceholder* placeholder = findColumnSpannerPlac eholder(toRenderBox(child)))
399 return placeholder;
400
401 // This is an invalid spanner, or its placeholder hasn't been created yet. T his happens when
402 // moving an entire subtree into the flow thread, when we are processing the insertion of this
403 // spanner's preceding sibling, and we obviously haven't got as far as proce ssing this spanner
404 // yet.
405 return child;
406 }
407
408 void RenderMultiColumnFlowThread::flowThreadDescendantInserted(RenderObject* des cendant)
409 {
410 if (m_beingEvacuated)
411 return;
412 RenderObject* subtreeRoot = descendant;
413 for (; descendant; descendant = descendant->nextInPreOrder(subtreeRoot)) {
414 if (descendant->isRenderMultiColumnSpannerPlaceholder()) {
415 // A spanner's placeholder has been inserted. The actual spanner ren derer is moved from
416 // where it would otherwise occur (if it weren't a spanner) to becom ing a sibling of the
417 // column sets.
418 RenderMultiColumnSpannerPlaceholder* placeholder = toRenderMultiColu mnSpannerPlaceholder(descendant);
419 ASSERT(!m_spannerMap.get(placeholder->spanner()));
420 m_spannerMap.add(placeholder->spanner(), placeholder);
421 ASSERT(!placeholder->slowFirstChild()); // There should be no childr en here, but if there are, we ought to skip them.
422 continue;
423 }
424 RenderBlockFlow* multicolContainer = multiColumnBlockFlow();
425 RenderObject* nextRendererInFlowThread = descendant->nextInPreOrderAfter Children(this);
426 RenderObject* insertBeforeMulticolChild = 0;
427 if (isDescendantValidColumnSpanner(descendant)) {
428 // This is a spanner (column-span:all). Such renderers are moved fro m where they would
429 // otherwise occur in the render tree to becoming a direct child of the multicol container,
430 // so that they live among the column sets. This simplifies the layo ut implementation, and
431 // basically just relies on regular block layout done by the RenderB lockFlow that
432 // establishes the multicol container.
433 RenderBlockFlow* container = toRenderBlockFlow(descendant->parent()) ;
434 RenderMultiColumnSet* setToSplit = 0;
435 if (nextRendererInFlowThread) {
436 setToSplit = findSetRendering(descendant);
437 if (setToSplit) {
438 setToSplit->setNeedsLayoutAndFullPaintInvalidation();
439 insertBeforeMulticolChild = setToSplit->nextSibling();
440 }
441 }
442 // Moving a spanner's renderer so that it becomes a sibling of the c olumn sets requires us
443 // to insert an anonymous placeholder in the tree where the spanner' s renderer otherwise
444 // would have been. This is needed for two reasons: We need a way of separating inline
445 // content before and after the spanner, so that it becomes separate line boxes. Secondly,
446 // this placeholder serves as a break point for column sets, so that , when encountered, we
447 // end flowing one column set and move on to the next one.
448 RenderMultiColumnSpannerPlaceholder* placeholder = RenderMultiColumn SpannerPlaceholder::createAnonymous(this, toRenderBox(descendant), container->st yle());
449 container->addChild(placeholder, descendant->nextSibling());
450 container->removeChild(descendant);
451 multicolContainer->RenderBlock::addChild(descendant, insertBeforeMul ticolChild);
452
453 // The spanner has now been moved out from the flow thread, but we d on't want to
454 // examine its children anyway. They are all part of the spanner and shouldn't trigger
455 // creation of column sets or anything like that. Continue at its or iginal position in
456 // the tree, i.e. where the placeholder was just put.
457 if (subtreeRoot == descendant)
458 subtreeRoot = placeholder;
459 descendant = placeholder;
460 } else {
461 // This is regular multicol content, i.e. not part of a spanner.
462 if (nextRendererInFlowThread && nextRendererInFlowThread->isRenderMu ltiColumnSpannerPlaceholder()) {
463 // Inserted right before a spanner. Is there a set for us there?
464 RenderMultiColumnSpannerPlaceholder* placeholder = toRenderMulti ColumnSpannerPlaceholder(nextRendererInFlowThread);
465 if (RenderObject* previous = placeholder->spanner()->previousSib ling()) {
466 if (previous->isRenderMultiColumnSet())
467 continue; // There's already a set there. Nothing to do.
468 }
469 insertBeforeMulticolChild = placeholder->spanner();
470 } else if (RenderMultiColumnSet* lastSet = lastMultiColumnSet()) {
471 // This child is not an immediate predecessor of a spanner, whic h means that if this
472 // child precedes a spanner at all, there has to be a column set created for us there
473 // already. If it doesn't precede any spanner at all, on the oth er hand, we need a
474 // column set at the end of the multicol container. We don't rea lly check here if the
475 // child inserted precedes any spanner or not (as that's an expe nsive operation). Just
476 // make sure we have a column set at the end. It's no big deal i f it remains unused.
477 if (!lastSet->nextSibling())
478 continue;
479 }
480 }
481 // Need to create a new column set when there's no set already created. We also always insert
482 // another column set after a spanner. Even if it turns out that there a re no renderers
483 // following the spanner, there may be bottom margins there, which take up space.
484 RenderMultiColumnSet* newSet = RenderMultiColumnSet::createAnonymous(thi s, multicolContainer->style());
485 multicolContainer->RenderBlock::addChild(newSet, insertBeforeMulticolChi ld);
486 invalidateRegions();
487
488 // We cannot handle immediate column set siblings at the moment (and the re's no need for
489 // it, either). There has to be at least one spanner separating them.
490 ASSERT(!previousColumnSetOrSpannerSiblingOf(newSet) || !previousColumnSe tOrSpannerSiblingOf(newSet)->isRenderMultiColumnSet());
491 ASSERT(!nextColumnSetOrSpannerSiblingOf(newSet) || !nextColumnSetOrSpann erSiblingOf(newSet)->isRenderMultiColumnSet());
492 }
493 }
494
495 void RenderMultiColumnFlowThread::flowThreadDescendantOrSiblingWillBeRemoved(Ren derObject* relative)
496 {
497 if (m_beingEvacuated)
498 return;
499 invalidateRegions();
500 if (relative->isRenderMultiColumnSpannerPlaceholder()) {
501 // Remove the map entry for this spanner, but leave the actual spanner r enderer alone. Also
502 // keep the reference to the spanner, since the placeholder may be about to be re-inserted
503 // into the tree.
504 ASSERT(relative->isDescendantOf(this));
505 m_spannerMap.remove(toRenderMultiColumnSpannerPlaceholder(relative)->spa nner());
506 return;
507 }
508 if (relative->style()->columnSpan() == ColumnSpanAll) {
509 if (relative->parent() != parent())
510 return; // not a valid spanner.
511
512 // The placeholder may already have been removed, but if it hasn't, do s o now.
513 if (RenderMultiColumnSpannerPlaceholder* placeholder = m_spannerMap.get( toRenderBox(relative))) {
514 placeholder->containingBlock()->RenderBlock::removeChild(placeholder );
515 m_spannerMap.remove(toRenderBox(relative));
516 }
517
518 if (RenderObject* next = relative->nextSibling()) {
519 if (RenderObject* previous = relative->previousSibling()) {
520 if (previous->isRenderMultiColumnSet() && next->isRenderMultiCol umnSet()) {
521 // Merge two sets that no longer will be separated by a span ner.
522 next->destroy();
523 previous->setNeedsLayoutAndFullPaintInvalidation();
524 }
525 }
526 }
527 }
528 // Note that we might end up with empty column sets if all column content is removed. That's no
529 // big deal though (and locating them would be expensive), and they will be found and re-used if
530 // content is added again later.
531 }
532
259 void RenderMultiColumnFlowThread::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const 533 void RenderMultiColumnFlowThread::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
260 { 534 {
261 // We simply remain at our intrinsic height. 535 // We simply remain at our intrinsic height.
262 computedValues.m_extent = logicalHeight; 536 computedValues.m_extent = logicalHeight;
263 computedValues.m_position = logicalTop; 537 computedValues.m_position = logicalTop;
264 } 538 }
265 539
266 void RenderMultiColumnFlowThread::updateLogicalWidth() 540 void RenderMultiColumnFlowThread::updateLogicalWidth()
267 { 541 {
268 LayoutUnit columnWidth; 542 LayoutUnit columnWidth;
269 calculateColumnCountAndWidth(columnWidth, m_columnCount); 543 calculateColumnCountAndWidth(columnWidth, m_columnCount);
270 setLogicalWidth(columnWidth); 544 setLogicalWidth(columnWidth);
271 } 545 }
272 546
273 void RenderMultiColumnFlowThread::layout() 547 void RenderMultiColumnFlowThread::layout()
274 { 548 {
549 ASSERT(!m_columnSetHeightsAreUnknown);
550 m_columnSetHeightsAreUnknown = true;
551 m_lastSetWorkedOn = 0;
552 if (RenderBox* first = firstColumnSetOrSpanner()) {
553 if (first->isRenderMultiColumnSet()) {
554 m_lastSetWorkedOn = toRenderMultiColumnSet(first);
555 m_lastSetWorkedOn->beginFlow(LayoutUnit());
556 }
557 }
275 RenderFlowThread::layout(); 558 RenderFlowThread::layout();
276 if (RenderMultiColumnSet* lastSet = lastMultiColumnSet()) 559 if (RenderMultiColumnSet* lastSet = lastMultiColumnSet()) {
560 if (!nextColumnSetOrSpannerSiblingOf(lastSet))
561 lastSet->endFlow(logicalHeight());
277 lastSet->expandToEncompassFlowThreadContentsIfNeeded(); 562 lastSet->expandToEncompassFlowThreadContentsIfNeeded();
563 }
564 m_columnSetHeightsAreUnknown = false;
565 m_lastSetWorkedOn = 0;
278 } 566 }
279 567
280 void RenderMultiColumnFlowThread::setPageBreak(LayoutUnit offset, LayoutUnit spa ceShortage) 568 void RenderMultiColumnFlowThread::setPageBreak(LayoutUnit offset, LayoutUnit spa ceShortage)
281 { 569 {
282 // Only positive values are interesting (and allowed) here. Zero space short age may be reported 570 // Only positive values are interesting (and allowed) here. Zero space short age may be reported
283 // when we're at the top of a column and the element has zero height. Ignore this, and also 571 // when we're at the top of a column and the element has zero height. Ignore this, and also
284 // ignore any negative values, which may occur when we set an early break in order to honor 572 // ignore any negative values, which may occur when we set an early break in order to honor
285 // widows in the next column. 573 // widows in the next column.
286 if (spaceShortage <= 0) 574 if (spaceShortage <= 0)
287 return; 575 return;
288 576
289 if (RenderMultiColumnSet* multicolSet = columnSetAtBlockOffset(offset)) 577 if (RenderMultiColumnSet* multicolSet = columnSetAtBlockOffset(offset))
290 multicolSet->recordSpaceShortage(spaceShortage); 578 multicolSet->recordSpaceShortage(spaceShortage);
291 } 579 }
292 580
293 void RenderMultiColumnFlowThread::updateMinimumPageHeight(LayoutUnit offset, Lay outUnit minHeight) 581 void RenderMultiColumnFlowThread::updateMinimumPageHeight(LayoutUnit offset, Lay outUnit minHeight)
294 { 582 {
295 if (RenderMultiColumnSet* multicolSet = columnSetAtBlockOffset(offset)) 583 if (RenderMultiColumnSet* multicolSet = columnSetAtBlockOffset(offset))
296 multicolSet->updateMinimumColumnHeight(minHeight); 584 multicolSet->updateMinimumColumnHeight(minHeight);
297 } 585 }
298 586
299 RenderMultiColumnSet* RenderMultiColumnFlowThread::columnSetAtBlockOffset(Layout Unit /*offset*/) const 587 RenderMultiColumnSet* RenderMultiColumnFlowThread::columnSetAtBlockOffset(Layout Unit offset) const
300 { 588 {
301 // For now there's only one column set, so this is easy: 589 if (m_columnSetHeightsAreUnknown) {
302 return firstMultiColumnSet(); 590 // Layout in progress. We are calculating the set heights as we speak, s o the column set range
591 // information is not up-to-date.
592
593 RenderMultiColumnSet* columnSet = m_lastSetWorkedOn ? m_lastSetWorkedOn : firstMultiColumnSet();
594 if (!columnSet) {
595 // If there's no set, bail. This multicol is empty or only consists of spanners. There
596 // are no regions.
597 return 0;
598 }
599 // The last set worked on is a good guess. But if we're not within the b ounds, search for the
600 // right one.
601 if (offset < columnSet->logicalTopInFlowThread()) {
602 // In some cases we need to search backwards for a column set we've advanced past. This
603 // happens when a block crosses a column set boundary, and someone w ants to examine or
604 // adjust its top after or during layout. FIXME: If its top acually gets adjusted
605 // (e.g. because of an incorrect initial top position estimate), thi s may be problematic
606 // for column balancing, but returning the correct set here is at le ast better than
607 // nothing.
608 do {
609 if (RenderMultiColumnSet* prev = columnSet->previousSiblingMulti ColumnSet())
610 columnSet = prev;
611 else
612 break;
613 } while (offset < columnSet->logicalTopInFlowThread());
614 } else {
615 // We currently don't support searching forwards for a set, and ther e should be no need
616 // for it, either.
617 ASSERT(offset < columnSet->logicalBottomInFlowThread());
618 }
619 return columnSet;
620 }
621
622 ASSERT(!m_regionsInvalidated);
623 if (offset <= 0)
624 return m_multiColumnSetList.isEmpty() ? 0 : m_multiColumnSetList.first() ;
625
626 MultiColumnSetSearchAdapter adapter(offset);
627 m_multiColumnSetIntervalTree.allOverlapsWithAdapter<MultiColumnSetSearchAdap ter>(adapter);
628
629 // If no set was found, the offset is in the flow thread overflow.
630 if (!adapter.result() && !m_multiColumnSetList.isEmpty())
631 return m_multiColumnSetList.last();
632 return adapter.result();
303 } 633 }
304 634
305 bool RenderMultiColumnFlowThread::addForcedRegionBreak(LayoutUnit offset, Render Object* /*breakChild*/, bool /*isBefore*/, LayoutUnit* offsetBreakAdjustment) 635 bool RenderMultiColumnFlowThread::addForcedRegionBreak(LayoutUnit offset, Render Object* breakChild, bool /*isBefore*/, LayoutUnit* offsetBreakAdjustment)
306 { 636 {
307 if (RenderMultiColumnSet* multicolSet = columnSetAtBlockOffset(offset)) { 637 if (RenderMultiColumnSet* multicolSet = columnSetAtBlockOffset(offset)) {
308 multicolSet->addContentRun(offset); 638 // Spanner placeholders force a break (to make sure that the unused part of the last column
639 // is empty), but this break should not affect column balancing.
640 if (!breakChild->isRenderMultiColumnSpannerPlaceholder())
641 multicolSet->addContentRun(offset);
309 if (offsetBreakAdjustment) 642 if (offsetBreakAdjustment)
310 *offsetBreakAdjustment = pageLogicalHeightForOffset(offset) ? pageRe mainingLogicalHeightForOffset(offset, IncludePageBoundary) : LayoutUnit(); 643 *offsetBreakAdjustment = pageLogicalHeightForOffset(offset) ? pageRe mainingLogicalHeightForOffset(offset, IncludePageBoundary) : LayoutUnit();
311 return true; 644 return true;
312 } 645 }
313 return false; 646 return false;
314 } 647 }
315 648
316 bool RenderMultiColumnFlowThread::isPageLogicalHeightKnown() const 649 bool RenderMultiColumnFlowThread::isPageLogicalHeightKnown() const
317 { 650 {
318 if (RenderMultiColumnSet* columnSet = lastMultiColumnSet()) 651 if (RenderMultiColumnSet* columnSet = lastMultiColumnSet())
319 return columnSet->pageLogicalHeight(); 652 return columnSet->pageLogicalHeight();
320 return false; 653 return false;
321 } 654 }
322 655
323 } 656 }
OLDNEW
« no previous file with comments | « Source/core/rendering/RenderMultiColumnFlowThread.h ('k') | Source/core/rendering/RenderMultiColumnSet.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698