Chromium Code Reviews| 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/RenderMultiColumnSpannerSet.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) |
| 36 , m_inBalancingPass(false) | 38 , m_inBalancingPass(false) |
| 37 , m_needsColumnHeightsRecalculation(false) | 39 , m_needsColumnHeightsRecalculation(false) |
| 38 , m_progressionIsInline(true) | 40 , m_progressionIsInline(true) |
| 41 , m_beingEvacuated(false) | |
| 39 { | 42 { |
| 40 setFlowThreadState(InsideInFlowThread); | 43 setFlowThreadState(InsideInFlowThread); |
| 41 } | 44 } |
| 42 | 45 |
| 43 RenderMultiColumnFlowThread::~RenderMultiColumnFlowThread() | 46 RenderMultiColumnFlowThread::~RenderMultiColumnFlowThread() |
| 44 { | 47 { |
| 45 } | 48 } |
| 46 | 49 |
| 47 RenderMultiColumnFlowThread* RenderMultiColumnFlowThread::createAnonymous(Docume nt& document, RenderStyle* parentStyle) | 50 RenderMultiColumnFlowThread* RenderMultiColumnFlowThread::createAnonymous(Docume nt& document, RenderStyle* parentStyle) |
| 48 { | 51 { |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 63 | 66 |
| 64 RenderMultiColumnSet* RenderMultiColumnFlowThread::lastMultiColumnSet() const | 67 RenderMultiColumnSet* RenderMultiColumnFlowThread::lastMultiColumnSet() const |
| 65 { | 68 { |
| 66 for (RenderObject* sibling = multiColumnBlockFlow()->lastChild(); sibling; s ibling = sibling->previousSibling()) { | 69 for (RenderObject* sibling = multiColumnBlockFlow()->lastChild(); sibling; s ibling = sibling->previousSibling()) { |
| 67 if (sibling->isRenderMultiColumnSet()) | 70 if (sibling->isRenderMultiColumnSet()) |
| 68 return toRenderMultiColumnSet(sibling); | 71 return toRenderMultiColumnSet(sibling); |
| 69 } | 72 } |
| 70 return 0; | 73 return 0; |
| 71 } | 74 } |
| 72 | 75 |
| 73 void RenderMultiColumnFlowThread::addChild(RenderObject* newChild, RenderObject* beforeChild) | 76 RenderMultiColumnSet* RenderMultiColumnFlowThread::findSetRendering(RenderObject * renderer) const |
| 74 { | 77 { |
| 75 RenderBlockFlow::addChild(newChild, beforeChild); | 78 ASSERT(!containingColumnSpanner(renderer)); // should not be used for spanne rs or content inside them. |
| 76 if (firstMultiColumnSet()) | 79 RenderMultiColumnSet* multicolSet = firstMultiColumnSet(); |
| 77 return; | 80 if (!multicolSet) |
| 81 return 0; | |
| 78 | 82 |
| 79 // For now we only create one column set. It's created as soon as the multic ol container gets | 83 if (!multicolSet->nextSiblingMultiColumnSet()) { |
| 80 // any content at all. | 84 // There is only one set. This is easy, then: if it's in the flow thread , it's part of the set. |
| 81 RenderMultiColumnSet* newSet = RenderMultiColumnSet::createAnonymous(this, m ultiColumnBlockFlow()->style()); | 85 return renderer->isDescendantOf(this) ? multicolSet : 0; |
| 86 } | |
| 82 | 87 |
| 83 // Need to skip RenderBlockFlow's implementation of addChild(), or we'd get redirected right | 88 // This is SLOW! But luckily very uncommon. You need to dynamically insert a spanner into the |
| 84 // back here. | 89 // middle of the tree to trigger this. |
| 85 multiColumnBlockFlow()->RenderBlock::addChild(newSet); | 90 for (; multicolSet; multicolSet = multicolSet->nextSiblingMultiColumnSet()) { |
| 91 if (multicolSet->isRenderMultiColumnSpannerSet()) | |
| 92 continue; | |
| 93 RenderObject* startRenderer; | |
| 94 if (RenderMultiColumnSet* sibling = multicolSet->previousSiblingMultiCol umnSet()) { | |
| 95 // Adjacent sets should not occur. Currently we would have no way of figuring out what each | |
| 96 // of them contains then. | |
| 97 ASSERT(sibling->isRenderMultiColumnSpannerSet()); | |
| 98 startRenderer = toRenderMultiColumnSpannerSet(sibling)->rendererInFl owThread()->nextInPreOrderAfterChildren(this); | |
| 99 } else { | |
| 100 startRenderer = firstChild(); | |
| 101 } | |
| 102 ASSERT(startRenderer); | |
| 86 | 103 |
| 87 invalidateRegions(); | 104 RenderObject* stopRenderer; |
| 105 if (RenderMultiColumnSet* sibling = multicolSet->nextSiblingMultiColumnS et()) { | |
| 106 // Adjacent sets should not occur. Currently we would have no way of figuring out what each | |
| 107 // of them contains then. | |
| 108 ASSERT(sibling->isRenderMultiColumnSpannerSet()); | |
| 109 stopRenderer = toRenderMultiColumnSpannerSet(sibling)->rendererInFlo wThread(); | |
| 110 } else { | |
| 111 stopRenderer = 0; | |
| 112 } | |
| 113 | |
| 114 for (RenderObject* walker = startRenderer; walker != stopRenderer; walke r = walker->nextInPreOrder(this)) { | |
| 115 if (walker == renderer) | |
| 116 return multicolSet; | |
| 117 } | |
| 118 } | |
| 119 | |
| 120 return 0; | |
| 121 } | |
| 122 | |
| 123 RenderMultiColumnSpannerSet* RenderMultiColumnFlowThread::containingColumnSpanne r(const RenderObject* descendant) const | |
| 124 { | |
| 125 ASSERT(descendant->isDescendantOf(this)); | |
| 126 | |
| 127 // Before we spend time on searching the ancestry, see if there's a quick wa y to determine | |
| 128 // whether there might be any spanners at all. | |
| 129 RenderMultiColumnSet* firstSet = firstMultiColumnSet(); | |
| 130 if (!firstSet || (firstSet == lastMultiColumnSet() && !firstSet->isRenderMul tiColumnSpannerSet())) | |
| 131 return 0; | |
| 132 | |
| 133 // We have spanners. See if the renderer in question is one or inside of one then. | |
| 134 for (const RenderObject* ancestor = descendant; ancestor && ancestor != this ; ancestor = ancestor->parent()) { | |
| 135 if (RenderMultiColumnSpannerSet* spanner = m_spannerMap.get(ancestor)) | |
| 136 return spanner; | |
| 137 } | |
| 138 return 0; | |
| 88 } | 139 } |
| 89 | 140 |
| 90 void RenderMultiColumnFlowThread::populate() | 141 void RenderMultiColumnFlowThread::populate() |
| 91 { | 142 { |
| 92 RenderBlockFlow* multicolContainer = multiColumnBlockFlow(); | 143 RenderBlockFlow* multicolContainer = multiColumnBlockFlow(); |
| 93 ASSERT(!nextSibling()); | 144 ASSERT(!nextSibling()); |
| 94 // Reparent children preceding the flow thread into the flow thread. It's mu lticol content | 145 // 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 | 146 // 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. | 147 // sets and spanners) will be inserted there as we insert elements into the flow thread. |
| 97 multicolContainer->moveChildrenTo(this, multicolContainer->firstChild(), thi s, true); | 148 multicolContainer->moveChildrenTo(this, multicolContainer->firstChild(), thi s, true); |
| 98 } | 149 } |
| 99 | 150 |
| 100 void RenderMultiColumnFlowThread::evacuateAndDestroy() | 151 void RenderMultiColumnFlowThread::evacuateAndDestroy() |
| 101 { | 152 { |
| 102 RenderBlockFlow* multicolContainer = multiColumnBlockFlow(); | 153 RenderBlockFlow* multicolContainer = multiColumnBlockFlow(); |
| 154 m_beingEvacuated = true; | |
| 103 | 155 |
| 104 // Remove all sets. | 156 // Remove all sets. |
| 105 while (RenderMultiColumnSet* columnSet = firstMultiColumnSet()) | 157 while (RenderMultiColumnSet* columnSet = firstMultiColumnSet()) |
| 106 columnSet->destroy(); | 158 columnSet->destroy(); |
| 107 | 159 |
| 108 ASSERT(!previousSibling()); | 160 ASSERT(!previousSibling()); |
| 109 ASSERT(!nextSibling()); | 161 ASSERT(!nextSibling()); |
| 110 | 162 |
| 111 // Finally we can promote all flow thread's children. Before we move them to the flow thread's | 163 // 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 | 164 // container, we need to unregister the flow thread, so that they aren't jus t re-added again to |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 160 m_needsColumnHeightsRecalculation = false; | 212 m_needsColumnHeightsRecalculation = false; |
| 161 return; | 213 return; |
| 162 } | 214 } |
| 163 | 215 |
| 164 for (RenderMultiColumnSet* columnSet = firstMultiColumnSet(); columnSet; col umnSet = columnSet->nextSiblingMultiColumnSet()) { | 216 for (RenderMultiColumnSet* columnSet = firstMultiColumnSet(); columnSet; col umnSet = columnSet->nextSiblingMultiColumnSet()) { |
| 165 if (!m_inBalancingPass) { | 217 if (!m_inBalancingPass) { |
| 166 // This is the initial layout pass. We need to reset the column heig ht, because contents | 218 // This is the initial layout pass. We need to reset the column heig ht, because contents |
| 167 // typically have changed. | 219 // typically have changed. |
| 168 columnSet->resetColumnHeight(); | 220 columnSet->resetColumnHeight(); |
| 169 } | 221 } |
| 222 columnSet->resetFlow(); | |
| 170 } | 223 } |
| 171 | 224 |
| 172 invalidateRegions(); | 225 invalidateRegions(); |
| 173 m_needsColumnHeightsRecalculation = heightIsAuto(); | 226 m_needsColumnHeightsRecalculation = true; |
| 174 layout(); | 227 layout(); |
| 175 } | 228 } |
| 176 | 229 |
| 177 bool RenderMultiColumnFlowThread::recalculateColumnHeights() | 230 bool RenderMultiColumnFlowThread::recalculateColumnHeights() |
| 178 { | 231 { |
| 179 // All column sets that needed layout have now been laid out, so we can fina lly validate them. | 232 // All column sets that needed layout have now been laid out, so we can fina lly validate them. |
| 180 validateRegions(); | 233 validateRegions(); |
| 181 | 234 |
| 182 if (!m_needsColumnHeightsRecalculation) | 235 if (!m_needsColumnHeightsRecalculation) |
| 183 return false; | 236 return false; |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 221 width = std::max<LayoutUnit>(0, (availableWidth - ((count - 1) * columnG ap)) / count); | 274 width = std::max<LayoutUnit>(0, (availableWidth - ((count - 1) * columnG ap)) / count); |
| 222 } else if (!columnStyle->hasAutoColumnWidth() && columnStyle->hasAutoColumnC ount()) { | 275 } else if (!columnStyle->hasAutoColumnWidth() && columnStyle->hasAutoColumnC ount()) { |
| 223 count = std::max<LayoutUnit>(1, (availableWidth + columnGap) / (computed ColumnWidth + columnGap)); | 276 count = std::max<LayoutUnit>(1, (availableWidth + columnGap) / (computed ColumnWidth + columnGap)); |
| 224 width = ((availableWidth + columnGap) / count) - columnGap; | 277 width = ((availableWidth + columnGap) / count) - columnGap; |
| 225 } else { | 278 } else { |
| 226 count = std::max<LayoutUnit>(std::min<LayoutUnit>(computedColumnCount, ( availableWidth + columnGap) / (computedColumnWidth + columnGap)), 1); | 279 count = std::max<LayoutUnit>(std::min<LayoutUnit>(computedColumnCount, ( availableWidth + columnGap) / (computedColumnWidth + columnGap)), 1); |
| 227 width = ((availableWidth + columnGap) / count) - columnGap; | 280 width = ((availableWidth + columnGap) / count) - columnGap; |
| 228 } | 281 } |
| 229 } | 282 } |
| 230 | 283 |
| 284 bool RenderMultiColumnFlowThread::isDescendantValidColumnSpanner(RenderObject* d escendant) const | |
| 285 { | |
| 286 // We assume that we're inside the flow thread. This function is not to be c alled otherwise. | |
| 287 ASSERT(descendant->isDescendantOf(this)); | |
| 288 | |
| 289 // First make sure that the renderer itself has the right properties for bec oming a spanner. | |
| 290 if (descendant->style()->columnSpan() != ColumnSpanAll || !descendant->isBox () || descendant->isFloatingOrOutOfFlowPositioned()) | |
| 291 return false; | |
| 292 | |
| 293 RenderBlock* container = descendant->containingBlock(); | |
| 294 if (!container->isRenderBlockFlow() || container->childrenInline()) { | |
| 295 // Needs to be block-level. | |
| 296 return false; | |
| 297 } | |
| 298 | |
| 299 // This looks like a spanner, but if we're inside something unbreakable, it' s not to be treated as one. | |
| 300 for (RenderBox* ancestor = toRenderBox(descendant)->parentBox(); ancestor; a ncestor = ancestor->parentBox()) { | |
| 301 if (ancestor->isRenderFlowThread()) { | |
| 302 // Don't allow any intervening non-multicol fragmentation contexts. The spec doesn't say | |
| 303 // anything about disallowing this, but it's just going to be too co mplicated to | |
| 304 // implement (not to mention specify behavior). | |
| 305 return ancestor == this; | |
| 306 } | |
| 307 if (m_spannerMap.get(ancestor)) | |
| 308 return false; // Nested spanners not allowed. | |
| 309 if (ancestor->isUnsplittableForPagination()) | |
| 310 return false; | |
| 311 } | |
| 312 ASSERT_NOT_REACHED(); | |
| 313 return false; | |
| 314 } | |
| 315 | |
| 231 const char* RenderMultiColumnFlowThread::renderName() const | 316 const char* RenderMultiColumnFlowThread::renderName() const |
| 232 { | 317 { |
| 233 return "RenderMultiColumnFlowThread"; | 318 return "RenderMultiColumnFlowThread"; |
| 234 } | 319 } |
| 235 | 320 |
| 236 void RenderMultiColumnFlowThread::addRegionToThread(RenderMultiColumnSet* column Set) | 321 void RenderMultiColumnFlowThread::addRegionToThread(RenderMultiColumnSet* column Set) |
| 237 { | 322 { |
| 238 if (RenderMultiColumnSet* nextSet = columnSet->nextSiblingMultiColumnSet()) { | 323 if (RenderMultiColumnSet* nextSet = columnSet->nextSiblingMultiColumnSet()) { |
| 239 RenderMultiColumnSetList::iterator it = m_multiColumnSetList.find(nextSe t); | 324 RenderMultiColumnSetList::iterator it = m_multiColumnSetList.find(nextSe t); |
| 240 ASSERT(it != m_multiColumnSetList.end()); | 325 ASSERT(it != m_multiColumnSetList.end()); |
| 241 m_multiColumnSetList.insertBefore(it, columnSet); | 326 m_multiColumnSetList.insertBefore(it, columnSet); |
| 242 } else { | 327 } else { |
| 243 m_multiColumnSetList.add(columnSet); | 328 m_multiColumnSetList.add(columnSet); |
| 244 } | 329 } |
| 245 columnSet->setIsValid(true); | 330 columnSet->setIsValid(true); |
| 246 } | 331 } |
| 247 | 332 |
| 248 void RenderMultiColumnFlowThread::willBeRemovedFromTree() | 333 void RenderMultiColumnFlowThread::willBeRemovedFromTree() |
| 249 { | 334 { |
| 335 m_spannerMap.clear(); | |
| 250 // Detach all column sets from the flow thread. Cannot destroy them at this point, since they | 336 // 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 | 337 // are siblings of this object, and there may be pointers to this object's s ibling somewhere |
| 252 // further up on the call stack. | 338 // further up on the call stack. |
| 253 for (RenderMultiColumnSet* columnSet = firstMultiColumnSet(); columnSet; col umnSet = columnSet->nextSiblingMultiColumnSet()) | 339 for (RenderMultiColumnSet* columnSet = firstMultiColumnSet(); columnSet; col umnSet = columnSet->nextSiblingMultiColumnSet()) |
| 254 columnSet->detachRegion(); | 340 columnSet->detachRegion(); |
| 255 multiColumnBlockFlow()->resetMultiColumnFlowThread(); | 341 multiColumnBlockFlow()->resetMultiColumnFlowThread(); |
| 256 RenderFlowThread::willBeRemovedFromTree(); | 342 RenderFlowThread::willBeRemovedFromTree(); |
| 257 } | 343 } |
| 258 | 344 |
| 345 bool RenderMultiColumnFlowThread::isColumnSpanner(const RenderObject* descendant ) const | |
| 346 { | |
| 347 ASSERT(descendant->isDescendantOf(this)); | |
| 348 return m_spannerMap.get(descendant); | |
| 349 } | |
| 350 | |
| 351 bool RenderMultiColumnFlowThread::isInsideColumnSpanner(const RenderObject* desc endant) const | |
| 352 { | |
| 353 ASSERT(descendant->isDescendantOf(this)); | |
| 354 return containingColumnSpanner(descendant); | |
| 355 } | |
| 356 | |
| 357 LayoutUnit RenderMultiColumnFlowThread::enterColumnSpanner(RenderBox* renderer, LayoutUnit logicalTop) | |
| 358 { | |
| 359 ASSERT(renderer->isDescendantOf(this)); | |
| 360 RenderMultiColumnSpannerSet* spannerSet = m_spannerMap.get(renderer); | |
| 361 ASSERT(spannerSet); | |
| 362 spannerSet->setChildNeedsLayout(); | |
|
Julien - ping for review
2014/09/30 00:39:32
2 mistakes here:
- by default setChildNeedsLayout
mstensho (USE GERRIT)
2014/09/30 20:20:28
Done.
Uploaded this change separately, as it modi
| |
| 363 RenderMultiColumnSet* previousSet = spannerSet->previousSiblingMultiColumnSe t(); | |
| 364 if (!previousSet) { | |
| 365 // The first set is entered at the beginning of flow thread layout. If t he first set happens | |
| 366 // to be a spanner, we have nothing more to do here. | |
| 367 return LayoutUnit(); | |
| 368 } | |
| 369 | |
| 370 RenderBlock* cb = renderer->containingBlock(); | |
| 371 LayoutUnit logicalTopInFlowThread = cb->offsetFromLogicalTopOfFirstPage() + logicalTop; | |
| 372 LayoutUnit adjustment; | |
| 373 if (!previousSet->isRenderMultiColumnSpannerSet() && previousSet->pageLogica lHeight()) { | |
| 374 // Pad flow thread offset to a column boundary, so that contents that's supposed to come | |
| 375 // after the spanner (or the spanner itself) don't bleed into the column preceding the | |
| 376 // spanner. | |
| 377 LayoutUnit columnLogicalTopInFlowThread = previousSet->pageLogicalTopFor Offset(logicalTopInFlowThread); | |
| 378 if (columnLogicalTopInFlowThread != logicalTopInFlowThread) { | |
| 379 adjustment = columnLogicalTopInFlowThread + previousSet->pageLogical Height() - logicalTopInFlowThread; | |
| 380 logicalTopInFlowThread += adjustment; | |
| 381 } | |
| 382 } | |
| 383 | |
| 384 if (!previousSet->isRenderMultiColumnSpannerSet()) | |
| 385 previousSet->endFlow(logicalTopInFlowThread); | |
| 386 spannerSet->beginFlow(logicalTopInFlowThread); | |
| 387 | |
| 388 m_lastSetWorkedOn = spannerSet; | |
| 389 return adjustment; | |
| 390 } | |
| 391 | |
| 392 void RenderMultiColumnFlowThread::leaveColumnSpanner(RenderBox* renderer, Layout Unit logicalBottom) | |
| 393 { | |
| 394 ASSERT(m_lastSetWorkedOn == m_spannerMap.get(renderer)); | |
| 395 | |
| 396 RenderBlock* cb = renderer->containingBlock(); | |
| 397 LayoutUnit logicalBottomInFlowThread = cb->offsetFromLogicalTopOfFirstPage() + logicalBottom; | |
| 398 m_lastSetWorkedOn->endFlow(logicalBottomInFlowThread); | |
| 399 RenderMultiColumnSet* nextSet = m_lastSetWorkedOn->nextSiblingMultiColumnSet (); | |
| 400 if (nextSet) { | |
| 401 m_lastSetWorkedOn = nextSet; | |
| 402 if (!m_lastSetWorkedOn->isRenderMultiColumnSpannerSet()) | |
| 403 m_lastSetWorkedOn->beginFlow(logicalBottomInFlowThread); | |
| 404 } | |
| 405 } | |
| 406 | |
| 407 void RenderMultiColumnFlowThread::flowThreadDescendantInserted(RenderObject* des cendant) | |
| 408 { | |
| 409 ASSERT(!m_beingEvacuated); | |
| 410 RenderObject* subtreeRoot = descendant; | |
| 411 RenderBlockFlow* multicolContainer = multiColumnBlockFlow(); | |
| 412 for (; descendant; descendant = descendant->nextInPreOrder(subtreeRoot)) { | |
|
Julien - ping for review
2014/09/30 00:39:32
Modifying the passed-in parameter is a bad smell,
mstensho (USE GERRIT)
2014/09/30 20:20:28
Done.
| |
| 413 RenderObject* nextRenderer = descendant->nextInPreOrderAfterChildren(thi s); | |
| 414 RenderMultiColumnSet* insertBeforeSet = 0; | |
| 415 if (isDescendantValidColumnSpanner(descendant)) { | |
| 416 ASSERT(!containingColumnSpanner(descendant)); | |
| 417 RenderMultiColumnSet* setToSplit = 0; | |
| 418 if (nextRenderer) { | |
| 419 if (nextRenderer->isColumnSpanAll()) { | |
| 420 insertBeforeSet = m_spannerMap.get(nextRenderer); | |
| 421 } else { | |
| 422 RenderObject* previousRenderer = descendant->previousInPreOr der(this); | |
| 423 if (previousRenderer) { | |
| 424 if (RenderMultiColumnSpannerSet* previousSpanner = conta iningColumnSpanner(previousRenderer)) { | |
| 425 insertBeforeSet = previousSpanner->nextSiblingMultiC olumnSet(); | |
| 426 } else { | |
| 427 // This is in the middle of an existing column set. Need to split it and put the | |
| 428 // spanner between them. | |
| 429 setToSplit = findSetRendering(previousRenderer); | |
| 430 setToSplit->setNeedsLayoutAndFullPaintInvalidation() ; | |
| 431 insertBeforeSet = setToSplit->nextSiblingMultiColumn Set(); | |
| 432 } | |
| 433 } else { | |
| 434 insertBeforeSet = firstMultiColumnSet(); | |
| 435 } | |
| 436 } | |
| 437 } | |
| 438 // Insert the spanner. | |
| 439 RenderMultiColumnSpannerSet* newSpanner = RenderMultiColumnSpannerSe t::createAnonymous(this, multicolContainer->style(), toRenderBox(descendant)); | |
| 440 multicolContainer->RenderBlock::addChild(newSpanner, insertBeforeSet ); | |
| 441 m_spannerMap.add(descendant, newSpanner); | |
| 442 invalidateRegions(); | |
| 443 if (!setToSplit) | |
| 444 continue; | |
| 445 } else { | |
| 446 if (containingColumnSpanner(descendant)) | |
| 447 continue; | |
| 448 if (nextRenderer) { | |
| 449 if (RenderMultiColumnSpannerSet* spanner = containingColumnSpann er(nextRenderer)) { | |
| 450 // Inserted right before a spanner. Is there a set for us th ere? | |
| 451 RenderMultiColumnSet* previous = spanner->previousSiblingMul tiColumnSet(); | |
| 452 if (previous && !previous->isRenderMultiColumnSpannerSet()) | |
| 453 continue; | |
| 454 insertBeforeSet = spanner; | |
| 455 } else if (lastMultiColumnSet()) { | |
| 456 continue; | |
| 457 } | |
| 458 } else if (RenderMultiColumnSet* lastSet = lastMultiColumnSet()) { | |
| 459 if (!lastSet->isRenderMultiColumnSpannerSet()) | |
| 460 continue; | |
| 461 } | |
| 462 } | |
| 463 RenderMultiColumnSet* newSet = RenderMultiColumnSet::createAnonymous(thi s, multicolContainer->style()); | |
| 464 multicolContainer->RenderBlock::addChild(newSet, insertBeforeSet); | |
| 465 invalidateRegions(); | |
| 466 | |
| 467 // We cannot handle immediate column set siblings at the moment (and the re's no need for | |
| 468 // it, either). There has to be at least one spanner separating them. | |
| 469 ASSERT(!newSet->previousSiblingMultiColumnSet() || newSet->previousSibli ngMultiColumnSet()->isRenderMultiColumnSpannerSet()); | |
| 470 ASSERT(!newSet->nextSiblingMultiColumnSet() || newSet->nextSiblingMultiC olumnSet()->isRenderMultiColumnSpannerSet()); | |
| 471 } | |
| 472 } | |
| 473 | |
| 474 void RenderMultiColumnFlowThread::flowThreadDescendantWillBeRemoved(RenderObject * descendant) | |
| 475 { | |
| 476 if (m_beingEvacuated) | |
| 477 return; | |
| 478 RenderMultiColumnSpannerSet* containingSpanner = containingColumnSpanner(des cendant); | |
| 479 if (containingSpanner && containingSpanner->rendererInFlowThread() != descen dant) | |
| 480 return; // Only removing stuff inside a spanner, and not the spanner its elf. Nothing to see here. | |
| 481 RenderObject* next; | |
| 482 for (RenderObject* renderer = descendant; renderer; renderer = next) { | |
| 483 RenderMultiColumnSpannerSet* spanner = m_spannerMap.get(renderer); | |
| 484 if (!spanner) { | |
| 485 next = renderer->nextInPreOrder(descendant); | |
| 486 continue; | |
| 487 } | |
| 488 next = renderer->nextInPreOrderAfterChildren(descendant); // It's a span ner. Its children are of no interest to us. | |
| 489 if (RenderMultiColumnSet* nextSet = spanner->nextSiblingMultiColumnSet() ) { | |
| 490 RenderMultiColumnSet* previousSet = spanner->previousSiblingMultiCol umnSet(); | |
| 491 if (nextSet && !nextSet->isRenderMultiColumnSpannerSet() | |
| 492 && previousSet && !previousSet->isRenderMultiColumnSpannerSet()) { | |
| 493 // Need to merge two column sets. | |
| 494 nextSet->destroy(); | |
| 495 previousSet->setNeedsLayout(); | |
| 496 invalidateRegions(); | |
| 497 } | |
| 498 } | |
| 499 m_spannerMap.remove(renderer); | |
| 500 spanner->destroy(); | |
| 501 } | |
| 502 if (containingSpanner) | |
| 503 return; // Column content has not been removed. | |
| 504 | |
| 505 // Get rid of column sets we no longer need. | |
| 506 RenderMultiColumnSpannerSet* adjacentPreviousSpanner = 0; | |
| 507 RenderObject* previousRenderer = descendant->previousInPreOrder(this); | |
| 508 if (previousRenderer) { | |
| 509 adjacentPreviousSpanner = containingColumnSpanner(previousRenderer); | |
| 510 if (!adjacentPreviousSpanner) | |
| 511 return; // Preceded by column content. Set still needed. | |
| 512 } | |
| 513 RenderMultiColumnSpannerSet* adjacentNextSpanner = 0; | |
| 514 RenderObject* nextRenderer = descendant->nextInPreOrderAfterChildren(this); | |
| 515 if (nextRenderer) { | |
| 516 adjacentNextSpanner = containingColumnSpanner(nextRenderer); | |
| 517 if (!adjacentNextSpanner) | |
| 518 return; // Followed by column content. Set still needed. | |
| 519 } | |
| 520 RenderMultiColumnSet* columnSetToRemove; | |
| 521 if (adjacentNextSpanner) { | |
| 522 columnSetToRemove = adjacentNextSpanner->previousSiblingMultiColumnSet() ; | |
| 523 } else if (adjacentPreviousSpanner) { | |
| 524 columnSetToRemove = adjacentPreviousSpanner->nextSiblingMultiColumnSet() ; | |
| 525 } else { | |
| 526 columnSetToRemove = firstMultiColumnSet(); | |
| 527 ASSERT(columnSetToRemove); | |
| 528 ASSERT(!columnSetToRemove->previousSiblingMultiColumnSet()); | |
| 529 ASSERT(!columnSetToRemove->nextSiblingMultiColumnSet()); | |
| 530 } | |
| 531 ASSERT(columnSetToRemove); | |
| 532 columnSetToRemove->destroy(); | |
| 533 } | |
| 534 | |
| 535 void RenderMultiColumnFlowThread::flowThreadDescendantStyleDidChange(RenderObjec t* descendant) | |
| 536 { | |
| 537 ASSERT(descendant->isDescendantOf(this)); | |
| 538 if (RenderMultiColumnSpannerSet* spanner = m_spannerMap.get(descendant)) | |
| 539 spanner->updateMarginProperties(); | |
| 540 } | |
| 541 | |
| 259 void RenderMultiColumnFlowThread::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const | 542 void RenderMultiColumnFlowThread::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const |
| 260 { | 543 { |
| 261 // We simply remain at our intrinsic height. | 544 // We simply remain at our intrinsic height. |
| 262 computedValues.m_extent = logicalHeight; | 545 computedValues.m_extent = logicalHeight; |
| 263 computedValues.m_position = logicalTop; | 546 computedValues.m_position = logicalTop; |
| 264 } | 547 } |
| 265 | 548 |
| 266 void RenderMultiColumnFlowThread::updateLogicalWidth() | 549 void RenderMultiColumnFlowThread::updateLogicalWidth() |
| 267 { | 550 { |
| 268 LayoutUnit columnWidth; | 551 LayoutUnit columnWidth; |
| 269 calculateColumnCountAndWidth(columnWidth, m_columnCount); | 552 calculateColumnCountAndWidth(columnWidth, m_columnCount); |
| 270 setLogicalWidth(columnWidth); | 553 setLogicalWidth(columnWidth); |
| 271 } | 554 } |
| 272 | 555 |
| 273 void RenderMultiColumnFlowThread::layout() | 556 void RenderMultiColumnFlowThread::layout() |
| 274 { | 557 { |
| 558 ASSERT(!m_lastSetWorkedOn); | |
| 559 m_lastSetWorkedOn = firstMultiColumnSet(); | |
| 560 if (m_lastSetWorkedOn) | |
| 561 m_lastSetWorkedOn->beginFlow(LayoutUnit()); | |
| 275 RenderFlowThread::layout(); | 562 RenderFlowThread::layout(); |
| 276 if (RenderMultiColumnSet* lastSet = lastMultiColumnSet()) | 563 if (RenderMultiColumnSet* lastSet = lastMultiColumnSet()) { |
| 564 lastSet->endFlow(logicalHeight()); | |
| 277 lastSet->expandToEncompassFlowThreadContentsIfNeeded(); | 565 lastSet->expandToEncompassFlowThreadContentsIfNeeded(); |
| 566 } | |
| 567 m_lastSetWorkedOn = 0; | |
| 278 } | 568 } |
| 279 | 569 |
| 280 void RenderMultiColumnFlowThread::setPageBreak(LayoutUnit offset, LayoutUnit spa ceShortage) | 570 void RenderMultiColumnFlowThread::setPageBreak(LayoutUnit offset, LayoutUnit spa ceShortage) |
| 281 { | 571 { |
| 282 // Only positive values are interesting (and allowed) here. Zero space short age may be reported | 572 // 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 | 573 // 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 | 574 // ignore any negative values, which may occur when we set an early break in order to honor |
| 285 // widows in the next column. | 575 // widows in the next column. |
| 286 if (spaceShortage <= 0) | 576 if (spaceShortage <= 0) |
| 287 return; | 577 return; |
| 288 | 578 |
| 289 if (RenderMultiColumnSet* multicolSet = columnSetAtBlockOffset(offset)) | 579 if (RenderMultiColumnSet* multicolSet = columnSetAtBlockOffset(offset)) |
| 290 multicolSet->recordSpaceShortage(spaceShortage); | 580 multicolSet->recordSpaceShortage(spaceShortage); |
| 291 } | 581 } |
| 292 | 582 |
| 293 void RenderMultiColumnFlowThread::updateMinimumPageHeight(LayoutUnit offset, Lay outUnit minHeight) | 583 void RenderMultiColumnFlowThread::updateMinimumPageHeight(LayoutUnit offset, Lay outUnit minHeight) |
| 294 { | 584 { |
| 295 if (RenderMultiColumnSet* multicolSet = columnSetAtBlockOffset(offset)) | 585 if (RenderMultiColumnSet* multicolSet = columnSetAtBlockOffset(offset)) |
| 296 multicolSet->updateMinimumColumnHeight(minHeight); | 586 multicolSet->updateMinimumColumnHeight(minHeight); |
| 297 } | 587 } |
| 298 | 588 |
| 299 RenderMultiColumnSet* RenderMultiColumnFlowThread::columnSetAtBlockOffset(Layout Unit /*offset*/) const | 589 RenderMultiColumnSet* RenderMultiColumnFlowThread::columnSetAtBlockOffset(Layout Unit offset) const |
| 300 { | 590 { |
| 301 // For now there's only one column set, so this is easy: | 591 if (m_lastSetWorkedOn) { |
| 302 return firstMultiColumnSet(); | 592 // Layout in progress. We are calculating the set heights as we speak, s o the column set range |
| 593 // information is not up-to-date. | |
| 594 RenderMultiColumnSet* columnSet = m_lastSetWorkedOn; | |
| 595 if (offset < columnSet->logicalTopInFlowThread()) { | |
| 596 // In some cases we need to search backwards for a column set we've advanced past. This | |
| 597 // happens when a block crosses a column set boundary, and someone w ants to examine or | |
| 598 // adjust its top after or during layout. FIXME: If its top acually gets adjusted | |
| 599 // (e.g. because of an incorrect initial top position estimate), thi s may be problematic | |
| 600 // for column balancing, but returning the correct set here is at le ast better than | |
| 601 // nothing. | |
| 602 do { | |
| 603 if (RenderMultiColumnSet* prev = columnSet->previousSiblingMulti ColumnSet()) | |
| 604 columnSet = prev; | |
| 605 else | |
| 606 break; | |
| 607 } while (offset < columnSet->logicalTopInFlowThread()); | |
| 608 } | |
| 609 return columnSet; | |
| 610 } | |
| 611 | |
| 612 ASSERT(!m_regionsInvalidated); | |
| 613 if (offset <= 0) | |
| 614 return m_multiColumnSetList.isEmpty() ? 0 : m_multiColumnSetList.first() ; | |
| 615 | |
| 616 MultiColumnSetSearchAdapter adapter(offset); | |
| 617 m_multiColumnSetIntervalTree.allOverlapsWithAdapter<MultiColumnSetSearchAdap ter>(adapter); | |
| 618 | |
| 619 // If no set was found, the offset is in the flow thread overflow. | |
| 620 if (!adapter.result() && !m_multiColumnSetList.isEmpty()) | |
| 621 return m_multiColumnSetList.last(); | |
| 622 return adapter.result(); | |
| 303 } | 623 } |
| 304 | 624 |
| 305 bool RenderMultiColumnFlowThread::addForcedRegionBreak(LayoutUnit offset, Render Object* /*breakChild*/, bool /*isBefore*/, LayoutUnit* offsetBreakAdjustment) | 625 bool RenderMultiColumnFlowThread::addForcedRegionBreak(LayoutUnit offset, Render Object* /*breakChild*/, bool /*isBefore*/, LayoutUnit* offsetBreakAdjustment) |
| 306 { | 626 { |
| 307 if (RenderMultiColumnSet* multicolSet = columnSetAtBlockOffset(offset)) { | 627 if (RenderMultiColumnSet* multicolSet = columnSetAtBlockOffset(offset)) { |
| 308 multicolSet->addContentRun(offset); | 628 multicolSet->addContentRun(offset); |
| 309 if (offsetBreakAdjustment) | 629 if (offsetBreakAdjustment) |
| 310 *offsetBreakAdjustment = pageLogicalHeightForOffset(offset) ? pageRe mainingLogicalHeightForOffset(offset, IncludePageBoundary) : LayoutUnit(); | 630 *offsetBreakAdjustment = pageLogicalHeightForOffset(offset) ? pageRe mainingLogicalHeightForOffset(offset, IncludePageBoundary) : LayoutUnit(); |
| 311 return true; | 631 return true; |
| 312 } | 632 } |
| 313 return false; | 633 return false; |
| 314 } | 634 } |
| 315 | 635 |
| 316 bool RenderMultiColumnFlowThread::isPageLogicalHeightKnown() const | 636 bool RenderMultiColumnFlowThread::isPageLogicalHeightKnown() const |
| 317 { | 637 { |
| 318 if (RenderMultiColumnSet* columnSet = lastMultiColumnSet()) | 638 if (RenderMultiColumnSet* columnSet = lastMultiColumnSet()) |
| 319 return columnSet->pageLogicalHeight(); | 639 return columnSet->pageLogicalHeight(); |
| 320 return false; | 640 return false; |
| 321 } | 641 } |
| 322 | 642 |
| 323 } | 643 } |
| OLD | NEW |