blob: e93c68aedd5e3c88be5e37d863e34ea204b25732 [file] [log] [blame]
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001/*
2 * Copyright (C) 2012 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS IN..0TERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
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.
24 */
25
26#include "config.h"
27#include "core/rendering/RenderMultiColumnFlowThread.h"
28
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010029#include "core/rendering/RenderMultiColumnSet.h"
30
31namespace WebCore {
32
33RenderMultiColumnFlowThread::RenderMultiColumnFlowThread()
Torne (Richard Coles)d5428f32014-03-18 10:21:16 +000034 : m_columnCount(1)
35 , m_columnWidth(0)
36 , m_columnHeightAvailable(0)
37 , m_inBalancingPass(false)
38 , m_needsRebalancing(false)
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010039{
40 setFlowThreadState(InsideInFlowThread);
41}
42
43RenderMultiColumnFlowThread::~RenderMultiColumnFlowThread()
44{
45}
46
Torne (Richard Coles)d5428f32014-03-18 10:21:16 +000047RenderMultiColumnFlowThread* RenderMultiColumnFlowThread::createAnonymous(Document& document, RenderStyle* parentStyle)
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010048{
Torne (Richard Coles)f5e4ad52013-08-05 13:57:57 +010049 RenderMultiColumnFlowThread* renderer = new RenderMultiColumnFlowThread();
Torne (Richard Coles)d5428f32014-03-18 10:21:16 +000050 renderer->setDocumentForAnonymous(&document);
51 renderer->setStyle(RenderStyle::createAnonymousStyleWithDisplay(parentStyle, BLOCK));
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010052 return renderer;
53}
54
Bo Liuf91f5fa2014-05-01 10:37:55 -070055RenderMultiColumnSet* RenderMultiColumnFlowThread::firstMultiColumnSet() const
56{
57 for (RenderObject* sibling = nextSibling(); sibling; sibling = sibling->nextSibling()) {
58 if (sibling->isRenderMultiColumnSet())
59 return toRenderMultiColumnSet(sibling);
60 }
61 return 0;
62}
63
64RenderMultiColumnSet* RenderMultiColumnFlowThread::lastMultiColumnSet() const
65{
66 for (RenderObject* sibling = multiColumnBlockFlow()->lastChild(); sibling; sibling = sibling->previousSibling()) {
67 if (sibling->isRenderMultiColumnSet())
68 return toRenderMultiColumnSet(sibling);
69 }
70 return 0;
71}
72
73void 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 multicol container gets
80 // any content at all.
81 RenderMultiColumnSet* newSet = RenderMultiColumnSet::createAnonymous(this, multiColumnBlockFlow()->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
Ben Murdoch6f543c72014-04-16 11:17:22 +010090void RenderMultiColumnFlowThread::populate()
91{
92 RenderBlockFlow* multicolContainer = multiColumnBlockFlow();
93 ASSERT(!nextSibling());
94 // Reparent children preceding the flow thread into the flow thread. It's multicol content
95 // now. At this point there's obviously nothing after the flow thread, but renderers (column
96 // sets and spanners) will be inserted there as we insert elements into the flow thread.
97 multicolContainer->moveChildrenTo(this, multicolContainer->firstChild(), this, true);
98}
99
100void RenderMultiColumnFlowThread::evacuateAndDestroy()
101{
102 RenderBlockFlow* multicolContainer = multiColumnBlockFlow();
103
104 // Remove all sets.
Bo Liuf91f5fa2014-05-01 10:37:55 -0700105 while (RenderMultiColumnSet* columnSet = firstMultiColumnSet())
106 columnSet->destroy();
Ben Murdoch6f543c72014-04-16 11:17:22 +0100107
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 just re-added again to
113 // the flow thread that we're trying to empty.
114 multicolContainer->resetMultiColumnFlowThread();
115 moveAllChildrenTo(multicolContainer, true);
116
Ben Murdoch10f88d52014-04-24 10:50:33 +0100117 // FIXME: it's scary that neither destroy() nor the move*Children* methods take care of this,
118 // and instead leave you with dangling root line box pointers. But since this is how it is done
119 // in other parts of the code that deal with reparenting renderers, let's do the cleanup on our
120 // own here as well.
121 deleteLineBoxTree();
122
Ben Murdoch6f543c72014-04-16 11:17:22 +0100123 destroy();
124}
125
Bo Liuf91f5fa2014-05-01 10:37:55 -0700126LayoutSize RenderMultiColumnFlowThread::columnOffset(const LayoutPoint& point) const
127{
128 if (!hasValidRegionInfo())
129 return LayoutSize(0, 0);
130
131 LayoutPoint flowThreadPoint(point);
132 flipForWritingMode(flowThreadPoint);
133 LayoutUnit blockOffset = isHorizontalWritingMode() ? flowThreadPoint.y() : flowThreadPoint.x();
134 RenderRegion* renderRegion = regionAtBlockOffset(blockOffset);
135 if (!renderRegion)
136 return LayoutSize(0, 0);
137 return toRenderMultiColumnSet(renderRegion)->flowThreadTranslationAtOffset(blockOffset);
138}
139
Torne (Richard Coles)d5428f32014-03-18 10:21:16 +0000140void RenderMultiColumnFlowThread::layoutColumns(bool relayoutChildren, SubtreeLayoutScope& layoutScope)
141{
142 // Update the dimensions of our regions before we lay out the flow thread.
143 // FIXME: Eventually this is going to get way more complicated, and we will be destroying regions
144 // instead of trying to keep them around.
Torne (Richard Coles)d5428f32014-03-18 10:21:16 +0000145 bool shouldInvalidateRegions = false;
Bo Liuf91f5fa2014-05-01 10:37:55 -0700146 for (RenderMultiColumnSet* columnSet = firstMultiColumnSet(); columnSet; columnSet = columnSet->nextSiblingMultiColumnSet()) {
147 if (relayoutChildren || columnSet->needsLayout()) {
148 if (!m_inBalancingPass)
149 columnSet->prepareForLayout();
Torne (Richard Coles)d5428f32014-03-18 10:21:16 +0000150 shouldInvalidateRegions = true;
151 }
152 }
153
154 if (shouldInvalidateRegions)
155 invalidateRegions();
156
157 if (relayoutChildren)
158 layoutScope.setChildNeedsLayout(this);
159
160 if (requiresBalancing()) {
161 // At the end of multicol layout, relayoutForPagination() is called unconditionally, but if
162 // no children are to be laid out (e.g. fixed width with layout already being up-to-date),
163 // we want to prevent it from doing any work, so that the column balancing machinery doesn't
164 // kick in and trigger additional unnecessary layout passes. Actually, it's not just a good
165 // idea in general to not waste time on balancing content that hasn't been re-laid out; we
166 // are actually required to guarantee this. The calculation of implicit breaks needs to be
167 // preceded by a proper layout pass, since it's layout that sets up content runs, and the
168 // runs get deleted right after every pass.
169 m_needsRebalancing = shouldInvalidateRegions || needsLayout();
170 }
171
172 layoutIfNeeded();
173}
174
175bool RenderMultiColumnFlowThread::computeColumnCountAndWidth()
176{
177 RenderBlock* columnBlock = multiColumnBlockFlow();
178 LayoutUnit oldColumnWidth = m_columnWidth;
179
180 // Calculate our column width and column count.
181 m_columnCount = 1;
182 m_columnWidth = columnBlock->contentLogicalWidth();
183
184 const RenderStyle* columnStyle = columnBlock->style();
185 ASSERT(!columnStyle->hasAutoColumnCount() || !columnStyle->hasAutoColumnWidth());
186
187 LayoutUnit availWidth = m_columnWidth;
188 LayoutUnit colGap = columnBlock->columnGap();
189 LayoutUnit colWidth = max<LayoutUnit>(1, LayoutUnit(columnStyle->columnWidth()));
190 int colCount = max<int>(1, columnStyle->columnCount());
191
192 if (columnStyle->hasAutoColumnWidth() && !columnStyle->hasAutoColumnCount()) {
193 m_columnCount = colCount;
194 m_columnWidth = std::max<LayoutUnit>(0, (availWidth - ((m_columnCount - 1) * colGap)) / m_columnCount);
195 } else if (!columnStyle->hasAutoColumnWidth() && columnStyle->hasAutoColumnCount()) {
196 m_columnCount = std::max<LayoutUnit>(1, (availWidth + colGap) / (colWidth + colGap));
197 m_columnWidth = ((availWidth + colGap) / m_columnCount) - colGap;
198 } else {
199 m_columnCount = std::max<LayoutUnit>(std::min<LayoutUnit>(colCount, (availWidth + colGap) / (colWidth + colGap)), 1);
200 m_columnWidth = ((availWidth + colGap) / m_columnCount) - colGap;
201 }
202
203 return m_columnWidth != oldColumnWidth;
204}
205
206bool RenderMultiColumnFlowThread::recalculateColumnHeights()
207{
208 if (!m_needsRebalancing)
209 return false;
210
211 // Column heights may change here because of balancing. We may have to do multiple layout
212 // passes, depending on how the contents is fitted to the changed column heights. In most
213 // cases, laying out again twice or even just once will suffice. Sometimes we need more
214 // passes than that, though, but the number of retries should not exceed the number of
215 // columns, unless we have a bug.
216 bool needsRelayout = false;
Bo Liuf91f5fa2014-05-01 10:37:55 -0700217 for (RenderMultiColumnSet* multicolSet = firstMultiColumnSet(); multicolSet; multicolSet = multicolSet->nextSiblingMultiColumnSet()) {
218 if (multicolSet->recalculateBalancedHeight(!m_inBalancingPass)) {
219 multicolSet->setChildNeedsLayout(MarkOnlyThis);
220 needsRelayout = true;
Torne (Richard Coles)d5428f32014-03-18 10:21:16 +0000221 }
222 }
223
224 if (needsRelayout)
225 setChildNeedsLayout(MarkOnlyThis);
226
227 m_inBalancingPass = needsRelayout;
228 return needsRelayout;
229}
230
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100231const char* RenderMultiColumnFlowThread::renderName() const
Ben Murdoch02772c62013-07-26 10:21:05 +0100232{
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100233 return "RenderMultiColumnFlowThread";
234}
235
Bo Liuf91f5fa2014-05-01 10:37:55 -0700236void RenderMultiColumnFlowThread::addRegionToThread(RenderRegion* renderRegion)
237{
238 RenderMultiColumnSet* columnSet = toRenderMultiColumnSet(renderRegion);
239 if (RenderMultiColumnSet* nextSet = columnSet->nextSiblingMultiColumnSet()) {
240 RenderRegionList::iterator it = m_regionList.find(nextSet);
241 ASSERT(it != m_regionList.end());
242 m_regionList.insertBefore(it, columnSet);
243 } else {
244 m_regionList.add(columnSet);
245 }
246 renderRegion->setIsValid(true);
247}
248
249void RenderMultiColumnFlowThread::willBeRemovedFromTree()
250{
251 // Detach all column sets from the flow thread. Cannot destroy them at this point, since they
252 // are siblings of this object, and there may be pointers to this object's sibling somewhere
253 // further up on the call stack.
254 for (RenderMultiColumnSet* columnSet = firstMultiColumnSet(); columnSet; columnSet = columnSet->nextSiblingMultiColumnSet())
255 columnSet->detachRegion();
256 multiColumnBlockFlow()->resetMultiColumnFlowThread();
257 RenderFlowThread::willBeRemovedFromTree();
258}
259
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100260void RenderMultiColumnFlowThread::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
261{
262 // We simply remain at our intrinsic height.
263 computedValues.m_extent = logicalHeight;
264 computedValues.m_position = logicalTop;
265}
266
267LayoutUnit RenderMultiColumnFlowThread::initialLogicalWidth() const
268{
Torne (Richard Coles)d5428f32014-03-18 10:21:16 +0000269 return columnWidth();
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100270}
271
Ben Murdoch591b9582013-07-10 11:41:44 +0100272void RenderMultiColumnFlowThread::setPageBreak(LayoutUnit offset, LayoutUnit spaceShortage)
273{
274 if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlockOffset(offset)))
275 multicolSet->recordSpaceShortage(spaceShortage);
276}
277
278void RenderMultiColumnFlowThread::updateMinimumPageHeight(LayoutUnit offset, LayoutUnit minHeight)
279{
280 if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlockOffset(offset)))
281 multicolSet->updateMinimumColumnHeight(minHeight);
282}
283
Bo Liuf91f5fa2014-05-01 10:37:55 -0700284RenderRegion* RenderMultiColumnFlowThread::regionAtBlockOffset(LayoutUnit /*offset*/) const
285{
286 // For now there's only one column set, so this is easy:
287 return firstMultiColumnSet();
288}
289
Torne (Richard Coles)09380292014-02-21 12:17:33 +0000290bool RenderMultiColumnFlowThread::addForcedRegionBreak(LayoutUnit offset, RenderObject* /*breakChild*/, bool /*isBefore*/, LayoutUnit* offsetBreakAdjustment)
291{
292 if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlockOffset(offset))) {
293 multicolSet->addForcedBreak(offset);
294 if (offsetBreakAdjustment)
295 *offsetBreakAdjustment = pageLogicalHeightForOffset(offset) ? pageRemainingLogicalHeightForOffset(offset, IncludePageBoundary) : LayoutUnit();
296 return true;
297 }
298 return false;
299}
300
Ben Murdoch07a852d2014-03-31 11:51:52 +0100301bool RenderMultiColumnFlowThread::isPageLogicalHeightKnown() const
302{
Bo Liuf91f5fa2014-05-01 10:37:55 -0700303 if (RenderMultiColumnSet* columnSet = lastMultiColumnSet())
304 return columnSet->computedColumnHeight();
305 return false;
Ben Murdoch07a852d2014-03-31 11:51:52 +0100306}
307
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100308}