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 |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 45 } | 45 } |
| 46 | 46 |
| 47 RenderMultiColumnFlowThread* RenderMultiColumnFlowThread::createAnonymous(Docume nt& document, RenderStyle* parentStyle) | 47 RenderMultiColumnFlowThread* RenderMultiColumnFlowThread::createAnonymous(Docume nt& document, RenderStyle* parentStyle) |
| 48 { | 48 { |
| 49 RenderMultiColumnFlowThread* renderer = new RenderMultiColumnFlowThread(); | 49 RenderMultiColumnFlowThread* renderer = new RenderMultiColumnFlowThread(); |
| 50 renderer->setDocumentForAnonymous(&document); | 50 renderer->setDocumentForAnonymous(&document); |
| 51 renderer->setStyle(RenderStyle::createAnonymousStyleWithDisplay(parentStyle, BLOCK)); | 51 renderer->setStyle(RenderStyle::createAnonymousStyleWithDisplay(parentStyle, BLOCK)); |
| 52 return renderer; | 52 return renderer; |
| 53 } | 53 } |
| 54 | 54 |
| 55 RenderMultiColumnSet* RenderMultiColumnFlowThread::firstMultiColumnSet() const | |
| 56 { | |
| 57 for (RenderObject* sibling = nextSibling(); sibling; sibling = sibling->next Sibling()) { | |
| 58 if (sibling->isRenderMultiColumnSet()) | |
| 59 return toRenderMultiColumnSet(sibling); | |
| 60 } | |
| 61 return 0; | |
| 62 } | |
| 63 | |
| 64 RenderMultiColumnSet* RenderMultiColumnFlowThread::lastMultiColumnSet() const | |
| 65 { | |
| 66 for (RenderObject* sibling = multiColumnBlockFlow()->lastChild(); sibling; s ibling = sibling->previousSibling()) { | |
| 67 if (sibling->isRenderMultiColumnSet()) | |
| 68 return toRenderMultiColumnSet(sibling); | |
| 69 } | |
| 70 return 0; | |
| 71 } | |
| 72 | |
| 73 void RenderMultiColumnFlowThread::addChild(RenderObject* newChild, RenderObject* beforeChild) | |
| 74 { | |
| 75 RenderBlockFlow::addChild(newChild, beforeChild); | |
| 76 if (firstMultiColumnSet()) | |
| 77 return; | |
| 78 | |
| 79 // For now we only create one column set. It's created as soon as the multic ol container gets | |
| 80 // any content at all. | |
| 81 RenderMultiColumnSet* newSet = RenderMultiColumnSet::createAnonymous(this, m ultiColumnBlockFlow()->style()); | |
| 82 multiColumnBlockFlow()->RenderBlock::addChild(newSet); | |
|
ojan
2014/04/24 21:38:43
Why do we need the RenderBlock version? Can the mu
mstensho (USE GERRIT)
2014/04/24 22:22:22
Yes, multiColumnBlockFlow() is the one that has a
| |
| 83 invalidateRegions(); | |
| 84 } | |
| 85 | |
| 55 void RenderMultiColumnFlowThread::populate() | 86 void RenderMultiColumnFlowThread::populate() |
| 56 { | 87 { |
| 57 RenderBlockFlow* multicolContainer = multiColumnBlockFlow(); | 88 RenderBlockFlow* multicolContainer = multiColumnBlockFlow(); |
| 58 ASSERT(!nextSibling()); | 89 ASSERT(!nextSibling()); |
| 59 // Reparent children preceding the flow thread into the flow thread. It's mu lticol content | 90 // Reparent children preceding the flow thread into the flow thread. It's mu lticol content |
| 60 // now. At this point there's obviously nothing after the flow thread, but r enderers (column | 91 // now. At this point there's obviously nothing after the flow thread, but r enderers (column |
| 61 // sets and spanners) will be inserted there as we insert elements into the flow thread. | 92 // sets and spanners) will be inserted there as we insert elements into the flow thread. |
| 62 multicolContainer->moveChildrenTo(this, multicolContainer->firstChild(), thi s, true); | 93 multicolContainer->moveChildrenTo(this, multicolContainer->firstChild(), thi s, true); |
| 63 } | 94 } |
| 64 | 95 |
| 65 void RenderMultiColumnFlowThread::evacuateAndDestroy() | 96 void RenderMultiColumnFlowThread::evacuateAndDestroy() |
| 66 { | 97 { |
| 67 RenderBlockFlow* multicolContainer = multiColumnBlockFlow(); | 98 RenderBlockFlow* multicolContainer = multiColumnBlockFlow(); |
| 68 | 99 |
| 69 // Remove all sets. | 100 // Remove all sets. |
| 70 for (RenderBox* sibling = nextSiblingBox(); sibling;) { | 101 while (RenderMultiColumnSet* columnSet = firstMultiColumnSet()) |
|
ojan
2014/04/24 21:38:43
How do we have multiple sets? It looks like addChi
mstensho (USE GERRIT)
2014/04/24 22:22:22
For now, only one set. In the (near) future, we ca
ojan
2014/04/24 22:46:59
It's just a bit weird to me to have a while loop t
| |
| 71 RenderBox* nextSibling = sibling->nextSiblingBox(); | 102 columnSet->destroy(); |
| 72 if (sibling->isRenderMultiColumnSet()) | |
| 73 sibling->destroy(); | |
| 74 sibling = nextSibling; | |
| 75 } | |
| 76 | 103 |
| 77 ASSERT(!previousSibling()); | 104 ASSERT(!previousSibling()); |
| 78 ASSERT(!nextSibling()); | 105 ASSERT(!nextSibling()); |
| 79 | 106 |
| 80 // Finally we can promote all flow thread's children. Before we move them to the flow thread's | 107 // Finally we can promote all flow thread's children. Before we move them to the flow thread's |
| 81 // container, we need to unregister the flow thread, so that they aren't jus t re-added again to | 108 // container, we need to unregister the flow thread, so that they aren't jus t re-added again to |
| 82 // the flow thread that we're trying to empty. | 109 // the flow thread that we're trying to empty. |
| 83 multicolContainer->resetMultiColumnFlowThread(); | 110 multicolContainer->resetMultiColumnFlowThread(); |
| 84 moveAllChildrenTo(multicolContainer, true); | 111 moveAllChildrenTo(multicolContainer, true); |
| 85 | 112 |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 160 { | 187 { |
| 161 if (!m_needsRebalancing) | 188 if (!m_needsRebalancing) |
| 162 return false; | 189 return false; |
| 163 | 190 |
| 164 // Column heights may change here because of balancing. We may have to do mu ltiple layout | 191 // Column heights may change here because of balancing. We may have to do mu ltiple layout |
| 165 // passes, depending on how the contents is fitted to the changed column hei ghts. In most | 192 // passes, depending on how the contents is fitted to the changed column hei ghts. In most |
| 166 // cases, laying out again twice or even just once will suffice. Sometimes w e need more | 193 // cases, laying out again twice or even just once will suffice. Sometimes w e need more |
| 167 // passes than that, though, but the number of retries should not exceed the number of | 194 // passes than that, though, but the number of retries should not exceed the number of |
| 168 // columns, unless we have a bug. | 195 // columns, unless we have a bug. |
| 169 bool needsRelayout = false; | 196 bool needsRelayout = false; |
| 170 for (RenderBox* childBox = multiColumnBlockFlow()->firstChildBox(); childBox ; childBox = childBox->nextSiblingBox()) { | 197 for (RenderMultiColumnSet* multicolSet = firstMultiColumnSet(); multicolSet; multicolSet = multicolSet->nextSiblingMultiColumnSet()) { |
| 171 if (childBox != this && childBox->isRenderMultiColumnSet()) { | 198 if (multicolSet->recalculateBalancedHeight(!m_inBalancingPass)) { |
| 172 RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(childBox) ; | 199 multicolSet->setChildNeedsLayout(MarkOnlyThis); |
| 173 if (multicolSet->recalculateBalancedHeight(!m_inBalancingPass)) { | 200 needsRelayout = true; |
| 174 multicolSet->setChildNeedsLayout(MarkOnlyThis); | |
| 175 needsRelayout = true; | |
| 176 } | |
| 177 } | 201 } |
| 178 } | 202 } |
| 179 | 203 |
| 180 if (needsRelayout) | 204 if (needsRelayout) |
| 181 setChildNeedsLayout(MarkOnlyThis); | 205 setChildNeedsLayout(MarkOnlyThis); |
| 182 | 206 |
| 183 m_inBalancingPass = needsRelayout; | 207 m_inBalancingPass = needsRelayout; |
| 184 return needsRelayout; | 208 return needsRelayout; |
| 185 } | 209 } |
| 186 | 210 |
| 187 const char* RenderMultiColumnFlowThread::renderName() const | 211 const char* RenderMultiColumnFlowThread::renderName() const |
| 188 { | 212 { |
| 189 return "RenderMultiColumnFlowThread"; | 213 return "RenderMultiColumnFlowThread"; |
| 190 } | 214 } |
| 191 | 215 |
| 216 void RenderMultiColumnFlowThread::addRegionToThread(RenderRegion* renderRegion) | |
| 217 { | |
| 218 RenderMultiColumnSet* columnSet = toRenderMultiColumnSet(renderRegion); | |
| 219 if (RenderMultiColumnSet* nextSet = columnSet->nextSiblingMultiColumnSet()) { | |
| 220 RenderRegionList::iterator it = m_regionList.find(nextSet); | |
| 221 ASSERT(it != m_regionList.end()); | |
| 222 m_regionList.insertBefore(it, columnSet); | |
| 223 } else { | |
| 224 m_regionList.add(columnSet); | |
| 225 } | |
| 226 renderRegion->setIsValid(true); | |
| 227 } | |
| 228 | |
| 229 void RenderMultiColumnFlowThread::willBeRemovedFromTree() | |
| 230 { | |
| 231 // Detach all column sets from the flow thread. Cannot destroy them at this point, since they | |
| 232 // are siblings of this object, and there may be pointers to this object's s ibling somewhere | |
| 233 // further up on the call stack. | |
| 234 for (RenderMultiColumnSet* columnSet = firstMultiColumnSet(); columnSet; col umnSet = columnSet->nextSiblingMultiColumnSet()) | |
| 235 columnSet->detachRegion(); | |
| 236 multiColumnBlockFlow()->resetMultiColumnFlowThread(); | |
| 237 RenderFlowThread::willBeRemovedFromTree(); | |
| 238 } | |
| 239 | |
| 192 void RenderMultiColumnFlowThread::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const | 240 void RenderMultiColumnFlowThread::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const |
| 193 { | 241 { |
| 194 // We simply remain at our intrinsic height. | 242 // We simply remain at our intrinsic height. |
| 195 computedValues.m_extent = logicalHeight; | 243 computedValues.m_extent = logicalHeight; |
| 196 computedValues.m_position = logicalTop; | 244 computedValues.m_position = logicalTop; |
| 197 } | 245 } |
| 198 | 246 |
| 199 LayoutUnit RenderMultiColumnFlowThread::initialLogicalWidth() const | 247 LayoutUnit RenderMultiColumnFlowThread::initialLogicalWidth() const |
| 200 { | 248 { |
| 201 return columnWidth(); | 249 return columnWidth(); |
| 202 } | 250 } |
| 203 | 251 |
| 204 void RenderMultiColumnFlowThread::autoGenerateRegionsToBlockOffset(LayoutUnit /* offset*/) | |
| 205 { | |
| 206 // This function ensures we have the correct column set information at all t imes. | |
| 207 // For a simple multi-column layout in continuous media, only one column set child is required. | |
| 208 // Once a column is nested inside an enclosing pagination context, the numbe r of column sets | |
| 209 // required becomes 2n-1, where n is the total number of nested pagination c ontexts. For example: | |
| 210 // | |
| 211 // Column layout with no enclosing pagination model = 2 * 1 - 1 = 1 column s et. | |
| 212 // Columns inside pages = 2 * 2 - 1 = 3 column sets (bottom of first page, a ll the subsequent pages, then the last page). | |
| 213 // Columns inside columns inside pages = 2 * 3 - 1 = 5 column sets. | |
| 214 // | |
| 215 // In addition, column spans will force a column set to "split" into before/ after sets around the spanning element. | |
| 216 // | |
| 217 // Finally, we will need to deal with columns inside regions. If regions hav e variable widths, then there will need | |
| 218 // to be unique column sets created inside any region whose width is differe nt from its surrounding regions. This is | |
| 219 // actually pretty similar to the spanning case, in that we break up the col umn sets whenever the width varies. | |
| 220 // | |
| 221 // FIXME: For now just make one column set. This matches the old multi-colum n code. | |
| 222 // Right now our goal is just feature parity with the old multi-column code so that we can switch over to the | |
| 223 // new code as soon as possible. | |
| 224 RenderMultiColumnSet* firstSet = toRenderMultiColumnSet(firstRegion()); | |
| 225 if (firstSet) | |
| 226 return; | |
| 227 | |
| 228 invalidateRegions(); | |
| 229 | |
| 230 RenderBlockFlow* parentBlock = multiColumnBlockFlow(); | |
| 231 firstSet = RenderMultiColumnSet::createAnonymous(this); | |
| 232 firstSet->setStyle(RenderStyle::createAnonymousStyleWithDisplay(parentBlock- >style(), BLOCK)); | |
| 233 parentBlock->RenderBlock::addChild(firstSet); | |
| 234 | |
| 235 // Even though we aren't placed yet, we can go ahead and set up our size. At this point we're | |
| 236 // typically in the middle of laying out the thread, attempting to paginate, and we need to do | |
| 237 // some rudimentary "layout" of the set now, so that pagination will work. | |
| 238 firstSet->prepareForLayout(); | |
| 239 | |
| 240 validateRegions(); | |
| 241 } | |
| 242 | |
| 243 void RenderMultiColumnFlowThread::setPageBreak(LayoutUnit offset, LayoutUnit spa ceShortage) | 252 void RenderMultiColumnFlowThread::setPageBreak(LayoutUnit offset, LayoutUnit spa ceShortage) |
| 244 { | 253 { |
| 245 if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlock Offset(offset))) | 254 if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlock Offset(offset))) |
| 246 multicolSet->recordSpaceShortage(spaceShortage); | 255 multicolSet->recordSpaceShortage(spaceShortage); |
| 247 } | 256 } |
| 248 | 257 |
| 249 void RenderMultiColumnFlowThread::updateMinimumPageHeight(LayoutUnit offset, Lay outUnit minHeight) | 258 void RenderMultiColumnFlowThread::updateMinimumPageHeight(LayoutUnit offset, Lay outUnit minHeight) |
| 250 { | 259 { |
| 251 if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlock Offset(offset))) | 260 if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlock Offset(offset))) |
| 252 multicolSet->updateMinimumColumnHeight(minHeight); | 261 multicolSet->updateMinimumColumnHeight(minHeight); |
| 253 } | 262 } |
| 254 | 263 |
| 264 RenderRegion* RenderMultiColumnFlowThread::regionAtBlockOffset(LayoutUnit /*offs et*/) | |
| 265 { | |
| 266 // For now there's only one column set, so this is easy: | |
| 267 return firstMultiColumnSet(); | |
| 268 } | |
| 269 | |
| 255 bool RenderMultiColumnFlowThread::addForcedRegionBreak(LayoutUnit offset, Render Object* /*breakChild*/, bool /*isBefore*/, LayoutUnit* offsetBreakAdjustment) | 270 bool RenderMultiColumnFlowThread::addForcedRegionBreak(LayoutUnit offset, Render Object* /*breakChild*/, bool /*isBefore*/, LayoutUnit* offsetBreakAdjustment) |
| 256 { | 271 { |
| 257 if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlock Offset(offset))) { | 272 if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlock Offset(offset))) { |
| 258 multicolSet->addForcedBreak(offset); | 273 multicolSet->addForcedBreak(offset); |
| 259 if (offsetBreakAdjustment) | 274 if (offsetBreakAdjustment) |
| 260 *offsetBreakAdjustment = pageLogicalHeightForOffset(offset) ? pageRe mainingLogicalHeightForOffset(offset, IncludePageBoundary) : LayoutUnit(); | 275 *offsetBreakAdjustment = pageLogicalHeightForOffset(offset) ? pageRe mainingLogicalHeightForOffset(offset, IncludePageBoundary) : LayoutUnit(); |
| 261 return true; | 276 return true; |
| 262 } | 277 } |
| 263 return false; | 278 return false; |
| 264 } | 279 } |
| 265 | 280 |
| 266 bool RenderMultiColumnFlowThread::isPageLogicalHeightKnown() const | 281 bool RenderMultiColumnFlowThread::isPageLogicalHeightKnown() const |
| 267 { | 282 { |
| 268 for (RenderBox* renderer = parentBox()->lastChildBox(); renderer; renderer = renderer->previousSiblingBox()) { | 283 if (RenderMultiColumnSet* columnSet = lastMultiColumnSet()) |
| 269 if (renderer->isRenderMultiColumnSet()) | 284 return columnSet->computedColumnHeight(); |
| 270 return toRenderMultiColumnSet(renderer)->computedColumnHeight(); | 285 return false; |
| 271 } | |
| 272 // A column set hasn't been created yet. Height may already be known if colu mn-fill is 'auto', though. | |
| 273 return !requiresBalancing(); | |
| 274 } | 286 } |
| 275 | 287 |
| 276 } | 288 } |
| OLD | NEW |