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 |
| 83 // Need to skip RenderBlockFlow's implementation of addChild(), or we'd get
redirected right |
| 84 // back here. |
| 85 multiColumnBlockFlow()->RenderBlock::addChild(newSet); |
| 86 |
| 87 invalidateRegions(); |
| 88 } |
| 89 |
55 void RenderMultiColumnFlowThread::populate() | 90 void RenderMultiColumnFlowThread::populate() |
56 { | 91 { |
57 RenderBlockFlow* multicolContainer = multiColumnBlockFlow(); | 92 RenderBlockFlow* multicolContainer = multiColumnBlockFlow(); |
58 ASSERT(!nextSibling()); | 93 ASSERT(!nextSibling()); |
59 // Reparent children preceding the flow thread into the flow thread. It's mu
lticol content | 94 // 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 | 95 // 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. | 96 // sets and spanners) will be inserted there as we insert elements into the
flow thread. |
62 multicolContainer->moveChildrenTo(this, multicolContainer->firstChild(), thi
s, true); | 97 multicolContainer->moveChildrenTo(this, multicolContainer->firstChild(), thi
s, true); |
63 } | 98 } |
64 | 99 |
65 void RenderMultiColumnFlowThread::evacuateAndDestroy() | 100 void RenderMultiColumnFlowThread::evacuateAndDestroy() |
66 { | 101 { |
67 RenderBlockFlow* multicolContainer = multiColumnBlockFlow(); | 102 RenderBlockFlow* multicolContainer = multiColumnBlockFlow(); |
68 | 103 |
69 // Remove all sets. | 104 // Remove all sets. |
70 for (RenderBox* sibling = nextSiblingBox(); sibling;) { | 105 while (RenderMultiColumnSet* columnSet = firstMultiColumnSet()) |
71 RenderBox* nextSibling = sibling->nextSiblingBox(); | 106 columnSet->destroy(); |
72 if (sibling->isRenderMultiColumnSet()) | |
73 sibling->destroy(); | |
74 sibling = nextSibling; | |
75 } | |
76 | 107 |
77 ASSERT(!previousSibling()); | 108 ASSERT(!previousSibling()); |
78 ASSERT(!nextSibling()); | 109 ASSERT(!nextSibling()); |
79 | 110 |
80 // Finally we can promote all flow thread's children. Before we move them to
the flow thread's | 111 // 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 | 112 // 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. | 113 // the flow thread that we're trying to empty. |
83 multicolContainer->resetMultiColumnFlowThread(); | 114 multicolContainer->resetMultiColumnFlowThread(); |
84 moveAllChildrenTo(multicolContainer, true); | 115 moveAllChildrenTo(multicolContainer, true); |
85 | 116 |
86 // FIXME: it's scary that neither destroy() nor the move*Children* methods t
ake care of this, | 117 // FIXME: it's scary that neither destroy() nor the move*Children* methods t
ake care of this, |
87 // and instead leave you with dangling root line box pointers. But since thi
s is how it is done | 118 // and instead leave you with dangling root line box pointers. But since thi
s is how it is done |
88 // in other parts of the code that deal with reparenting renderers, let's do
the cleanup on our | 119 // in other parts of the code that deal with reparenting renderers, let's do
the cleanup on our |
89 // own here as well. | 120 // own here as well. |
90 deleteLineBoxTree(); | 121 deleteLineBoxTree(); |
91 | 122 |
92 destroy(); | 123 destroy(); |
93 } | 124 } |
94 | 125 |
95 void RenderMultiColumnFlowThread::layoutColumns(bool relayoutChildren, SubtreeLa
youtScope& layoutScope) | 126 void RenderMultiColumnFlowThread::layoutColumns(bool relayoutChildren, SubtreeLa
youtScope& layoutScope) |
96 { | 127 { |
97 // Update the dimensions of our regions before we lay out the flow thread. | 128 // Update the dimensions of our regions before we lay out the flow thread. |
98 // FIXME: Eventually this is going to get way more complicated, and we will
be destroying regions | 129 // FIXME: Eventually this is going to get way more complicated, and we will
be destroying regions |
99 // instead of trying to keep them around. | 130 // instead of trying to keep them around. |
100 RenderBlockFlow* container = multiColumnBlockFlow(); | |
101 bool shouldInvalidateRegions = false; | 131 bool shouldInvalidateRegions = false; |
102 for (RenderBox* childBox = container->firstChildBox(); childBox; childBox =
childBox->nextSiblingBox()) { | 132 for (RenderMultiColumnSet* columnSet = firstMultiColumnSet(); columnSet; col
umnSet = columnSet->nextSiblingMultiColumnSet()) { |
103 if (childBox == this) | 133 if (relayoutChildren || columnSet->needsLayout()) { |
104 continue; | 134 if (!m_inBalancingPass) |
105 | 135 columnSet->prepareForLayout(); |
106 if (relayoutChildren || childBox->needsLayout()) { | |
107 if (!m_inBalancingPass && childBox->isRenderMultiColumnSet()) | |
108 toRenderMultiColumnSet(childBox)->prepareForLayout(); | |
109 shouldInvalidateRegions = true; | 136 shouldInvalidateRegions = true; |
110 } | 137 } |
111 } | 138 } |
112 | 139 |
113 if (shouldInvalidateRegions) | 140 if (shouldInvalidateRegions) |
114 invalidateRegions(); | 141 invalidateRegions(); |
115 | 142 |
116 if (relayoutChildren) | 143 if (relayoutChildren) |
117 layoutScope.setChildNeedsLayout(this); | 144 layoutScope.setChildNeedsLayout(this); |
118 | 145 |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
166 { | 193 { |
167 if (!m_needsRebalancing) | 194 if (!m_needsRebalancing) |
168 return false; | 195 return false; |
169 | 196 |
170 // Column heights may change here because of balancing. We may have to do mu
ltiple layout | 197 // Column heights may change here because of balancing. We may have to do mu
ltiple layout |
171 // passes, depending on how the contents is fitted to the changed column hei
ghts. In most | 198 // passes, depending on how the contents is fitted to the changed column hei
ghts. In most |
172 // cases, laying out again twice or even just once will suffice. Sometimes w
e need more | 199 // cases, laying out again twice or even just once will suffice. Sometimes w
e need more |
173 // passes than that, though, but the number of retries should not exceed the
number of | 200 // passes than that, though, but the number of retries should not exceed the
number of |
174 // columns, unless we have a bug. | 201 // columns, unless we have a bug. |
175 bool needsRelayout = false; | 202 bool needsRelayout = false; |
176 for (RenderBox* childBox = multiColumnBlockFlow()->firstChildBox(); childBox
; childBox = childBox->nextSiblingBox()) { | 203 for (RenderMultiColumnSet* multicolSet = firstMultiColumnSet(); multicolSet;
multicolSet = multicolSet->nextSiblingMultiColumnSet()) { |
177 if (childBox != this && childBox->isRenderMultiColumnSet()) { | 204 if (multicolSet->recalculateBalancedHeight(!m_inBalancingPass)) { |
178 RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(childBox)
; | 205 multicolSet->setChildNeedsLayout(MarkOnlyThis); |
179 if (multicolSet->recalculateBalancedHeight(!m_inBalancingPass)) { | 206 needsRelayout = true; |
180 multicolSet->setChildNeedsLayout(MarkOnlyThis); | |
181 needsRelayout = true; | |
182 } | |
183 } | 207 } |
184 } | 208 } |
185 | 209 |
186 if (needsRelayout) | 210 if (needsRelayout) |
187 setChildNeedsLayout(MarkOnlyThis); | 211 setChildNeedsLayout(MarkOnlyThis); |
188 | 212 |
189 m_inBalancingPass = needsRelayout; | 213 m_inBalancingPass = needsRelayout; |
190 return needsRelayout; | 214 return needsRelayout; |
191 } | 215 } |
192 | 216 |
193 const char* RenderMultiColumnFlowThread::renderName() const | 217 const char* RenderMultiColumnFlowThread::renderName() const |
194 { | 218 { |
195 return "RenderMultiColumnFlowThread"; | 219 return "RenderMultiColumnFlowThread"; |
196 } | 220 } |
197 | 221 |
| 222 void RenderMultiColumnFlowThread::addRegionToThread(RenderRegion* renderRegion) |
| 223 { |
| 224 RenderMultiColumnSet* columnSet = toRenderMultiColumnSet(renderRegion); |
| 225 if (RenderMultiColumnSet* nextSet = columnSet->nextSiblingMultiColumnSet())
{ |
| 226 RenderRegionList::iterator it = m_regionList.find(nextSet); |
| 227 ASSERT(it != m_regionList.end()); |
| 228 m_regionList.insertBefore(it, columnSet); |
| 229 } else { |
| 230 m_regionList.add(columnSet); |
| 231 } |
| 232 renderRegion->setIsValid(true); |
| 233 } |
| 234 |
| 235 void RenderMultiColumnFlowThread::willBeRemovedFromTree() |
| 236 { |
| 237 // Detach all column sets from the flow thread. Cannot destroy them at this
point, since they |
| 238 // are siblings of this object, and there may be pointers to this object's s
ibling somewhere |
| 239 // further up on the call stack. |
| 240 for (RenderMultiColumnSet* columnSet = firstMultiColumnSet(); columnSet; col
umnSet = columnSet->nextSiblingMultiColumnSet()) |
| 241 columnSet->detachRegion(); |
| 242 multiColumnBlockFlow()->resetMultiColumnFlowThread(); |
| 243 RenderFlowThread::willBeRemovedFromTree(); |
| 244 } |
| 245 |
198 void RenderMultiColumnFlowThread::computeLogicalHeight(LayoutUnit logicalHeight,
LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const | 246 void RenderMultiColumnFlowThread::computeLogicalHeight(LayoutUnit logicalHeight,
LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const |
199 { | 247 { |
200 // We simply remain at our intrinsic height. | 248 // We simply remain at our intrinsic height. |
201 computedValues.m_extent = logicalHeight; | 249 computedValues.m_extent = logicalHeight; |
202 computedValues.m_position = logicalTop; | 250 computedValues.m_position = logicalTop; |
203 } | 251 } |
204 | 252 |
205 LayoutUnit RenderMultiColumnFlowThread::initialLogicalWidth() const | 253 LayoutUnit RenderMultiColumnFlowThread::initialLogicalWidth() const |
206 { | 254 { |
207 return columnWidth(); | 255 return columnWidth(); |
208 } | 256 } |
209 | 257 |
210 void RenderMultiColumnFlowThread::autoGenerateRegionsToBlockOffset(LayoutUnit /*
offset*/) | |
211 { | |
212 // This function ensures we have the correct column set information at all t
imes. | |
213 // For a simple multi-column layout in continuous media, only one column set
child is required. | |
214 // Once a column is nested inside an enclosing pagination context, the numbe
r of column sets | |
215 // required becomes 2n-1, where n is the total number of nested pagination c
ontexts. For example: | |
216 // | |
217 // Column layout with no enclosing pagination model = 2 * 1 - 1 = 1 column s
et. | |
218 // Columns inside pages = 2 * 2 - 1 = 3 column sets (bottom of first page, a
ll the subsequent pages, then the last page). | |
219 // Columns inside columns inside pages = 2 * 3 - 1 = 5 column sets. | |
220 // | |
221 // In addition, column spans will force a column set to "split" into before/
after sets around the spanning element. | |
222 // | |
223 // Finally, we will need to deal with columns inside regions. If regions hav
e variable widths, then there will need | |
224 // to be unique column sets created inside any region whose width is differe
nt from its surrounding regions. This is | |
225 // actually pretty similar to the spanning case, in that we break up the col
umn sets whenever the width varies. | |
226 // | |
227 // FIXME: For now just make one column set. This matches the old multi-colum
n code. | |
228 // Right now our goal is just feature parity with the old multi-column code
so that we can switch over to the | |
229 // new code as soon as possible. | |
230 RenderMultiColumnSet* firstSet = toRenderMultiColumnSet(firstRegion()); | |
231 if (firstSet) | |
232 return; | |
233 | |
234 invalidateRegions(); | |
235 | |
236 RenderBlockFlow* parentBlock = multiColumnBlockFlow(); | |
237 firstSet = RenderMultiColumnSet::createAnonymous(this); | |
238 firstSet->setStyle(RenderStyle::createAnonymousStyleWithDisplay(parentBlock-
>style(), BLOCK)); | |
239 parentBlock->RenderBlock::addChild(firstSet); | |
240 | |
241 // Even though we aren't placed yet, we can go ahead and set up our size. At
this point we're | |
242 // typically in the middle of laying out the thread, attempting to paginate,
and we need to do | |
243 // some rudimentary "layout" of the set now, so that pagination will work. | |
244 firstSet->prepareForLayout(); | |
245 | |
246 validateRegions(); | |
247 } | |
248 | |
249 void RenderMultiColumnFlowThread::setPageBreak(LayoutUnit offset, LayoutUnit spa
ceShortage) | 258 void RenderMultiColumnFlowThread::setPageBreak(LayoutUnit offset, LayoutUnit spa
ceShortage) |
250 { | 259 { |
251 if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlock
Offset(offset))) | 260 if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlock
Offset(offset))) |
252 multicolSet->recordSpaceShortage(spaceShortage); | 261 multicolSet->recordSpaceShortage(spaceShortage); |
253 } | 262 } |
254 | 263 |
255 void RenderMultiColumnFlowThread::updateMinimumPageHeight(LayoutUnit offset, Lay
outUnit minHeight) | 264 void RenderMultiColumnFlowThread::updateMinimumPageHeight(LayoutUnit offset, Lay
outUnit minHeight) |
256 { | 265 { |
257 if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlock
Offset(offset))) | 266 if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlock
Offset(offset))) |
258 multicolSet->updateMinimumColumnHeight(minHeight); | 267 multicolSet->updateMinimumColumnHeight(minHeight); |
259 } | 268 } |
260 | 269 |
| 270 RenderRegion* RenderMultiColumnFlowThread::regionAtBlockOffset(LayoutUnit /*offs
et*/) const |
| 271 { |
| 272 // For now there's only one column set, so this is easy: |
| 273 return firstMultiColumnSet(); |
| 274 } |
| 275 |
261 bool RenderMultiColumnFlowThread::addForcedRegionBreak(LayoutUnit offset, Render
Object* /*breakChild*/, bool /*isBefore*/, LayoutUnit* offsetBreakAdjustment) | 276 bool RenderMultiColumnFlowThread::addForcedRegionBreak(LayoutUnit offset, Render
Object* /*breakChild*/, bool /*isBefore*/, LayoutUnit* offsetBreakAdjustment) |
262 { | 277 { |
263 if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlock
Offset(offset))) { | 278 if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlock
Offset(offset))) { |
264 multicolSet->addForcedBreak(offset); | 279 multicolSet->addForcedBreak(offset); |
265 if (offsetBreakAdjustment) | 280 if (offsetBreakAdjustment) |
266 *offsetBreakAdjustment = pageLogicalHeightForOffset(offset) ? pageRe
mainingLogicalHeightForOffset(offset, IncludePageBoundary) : LayoutUnit(); | 281 *offsetBreakAdjustment = pageLogicalHeightForOffset(offset) ? pageRe
mainingLogicalHeightForOffset(offset, IncludePageBoundary) : LayoutUnit(); |
267 return true; | 282 return true; |
268 } | 283 } |
269 return false; | 284 return false; |
270 } | 285 } |
271 | 286 |
272 bool RenderMultiColumnFlowThread::isPageLogicalHeightKnown() const | 287 bool RenderMultiColumnFlowThread::isPageLogicalHeightKnown() const |
273 { | 288 { |
274 for (RenderBox* renderer = parentBox()->lastChildBox(); renderer; renderer =
renderer->previousSiblingBox()) { | 289 if (RenderMultiColumnSet* columnSet = lastMultiColumnSet()) |
275 if (renderer->isRenderMultiColumnSet()) | 290 return columnSet->computedColumnHeight(); |
276 return toRenderMultiColumnSet(renderer)->computedColumnHeight(); | 291 return false; |
277 } | |
278 // A column set hasn't been created yet. Height may already be known if colu
mn-fill is 'auto', though. | |
279 return !requiresBalancing(); | |
280 } | 292 } |
281 | 293 |
282 } | 294 } |
OLD | NEW |