Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / rendering / RenderMultiColumnSet.cpp
1 /*
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 INTERRUPTION) 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/RenderMultiColumnSet.h"
28
29 #include "core/rendering/PaintInfo.h"
30 #include "core/rendering/RenderLayer.h"
31 #include "core/rendering/RenderMultiColumnBlock.h"
32 #include "core/rendering/RenderMultiColumnFlowThread.h"
33
34 using namespace std;
35
36 namespace WebCore {
37
38 RenderMultiColumnSet::RenderMultiColumnSet(RenderFlowThread* flowThread)
39     : RenderRegionSet(0, flowThread)
40     , m_computedColumnCount(1)
41     , m_computedColumnWidth(0)
42     , m_computedColumnHeight(0)
43     , m_maxColumnHeight(RenderFlowThread::maxLogicalHeight())
44     , m_minSpaceShortage(RenderFlowThread::maxLogicalHeight())
45     , m_minimumColumnHeight(0)
46 {
47 }
48
49 RenderMultiColumnSet* RenderMultiColumnSet::createAnonymous(RenderFlowThread* flowThread)
50 {
51     Document& document = flowThread->document();
52     RenderMultiColumnSet* renderer = new RenderMultiColumnSet(flowThread);
53     renderer->setDocumentForAnonymous(&document);
54     return renderer;
55 }
56
57 LayoutUnit RenderMultiColumnSet::heightAdjustedForSetOffset(LayoutUnit height) const
58 {
59     RenderMultiColumnBlock* multicolBlock = toRenderMultiColumnBlock(parent());
60     LayoutUnit contentLogicalTop = logicalTop() - multicolBlock->borderBefore() - multicolBlock->paddingBefore();
61
62     height -= contentLogicalTop;
63     return max(height, LayoutUnit(1)); // Let's avoid zero height, as that would probably cause an infinite amount of columns to be created.
64 }
65
66 LayoutUnit RenderMultiColumnSet::pageLogicalTopForOffset(LayoutUnit offset) const
67 {
68     LayoutUnit portionLogicalTop = (isHorizontalWritingMode() ? flowThreadPortionRect().y() : flowThreadPortionRect().x());
69     unsigned columnIndex = columnIndexAtOffset(offset, AssumeNewColumns);
70     return portionLogicalTop + columnIndex * computedColumnHeight();
71 }
72
73 void RenderMultiColumnSet::setAndConstrainColumnHeight(LayoutUnit newHeight)
74 {
75     m_computedColumnHeight = newHeight;
76     if (m_computedColumnHeight > m_maxColumnHeight)
77         m_computedColumnHeight = m_maxColumnHeight;
78     // FIXME: the height may also be affected by the enclosing pagination context, if any.
79 }
80
81 unsigned RenderMultiColumnSet::findRunWithTallestColumns() const
82 {
83     unsigned indexWithLargestHeight = 0;
84     LayoutUnit largestHeight;
85     LayoutUnit previousOffset;
86     size_t runCount = m_contentRuns.size();
87     ASSERT(runCount);
88     for (size_t i = 0; i < runCount; i++) {
89         const ContentRun& run = m_contentRuns[i];
90         LayoutUnit height = run.columnLogicalHeight(previousOffset);
91         if (largestHeight < height) {
92             largestHeight = height;
93             indexWithLargestHeight = i;
94         }
95         previousOffset = run.breakOffset();
96     }
97     return indexWithLargestHeight;
98 }
99
100 void RenderMultiColumnSet::distributeImplicitBreaks()
101 {
102     unsigned breakCount = forcedBreaksCount();
103
104 #ifndef NDEBUG
105     // There should be no implicit breaks assumed at this point.
106     for (unsigned i = 0; i < breakCount; i++)
107         ASSERT(!m_contentRuns[i].assumedImplicitBreaks());
108 #endif // NDEBUG
109
110     // There will always be at least one break, since the flow thread reports a "forced break" at
111     // end of content.
112     ASSERT(breakCount >= 1);
113
114     // If there is room for more breaks (to reach the used value of column-count), imagine that we
115     // insert implicit breaks at suitable locations. At any given time, the content run with the
116     // currently tallest columns will get another implicit break "inserted", which will increase its
117     // column count by one and shrink its columns' height. Repeat until we have the desired total
118     // number of breaks. The largest column height among the runs will then be the initial column
119     // height for the balancer to use.
120     while (breakCount < m_computedColumnCount) {
121         unsigned index = findRunWithTallestColumns();
122         m_contentRuns[index].assumeAnotherImplicitBreak();
123         breakCount++;
124     }
125 }
126
127 LayoutUnit RenderMultiColumnSet::calculateBalancedHeight(bool initial) const
128 {
129     if (initial) {
130         // Start with the lowest imaginable column height.
131         unsigned index = findRunWithTallestColumns();
132         LayoutUnit startOffset = index > 0 ? m_contentRuns[index - 1].breakOffset() : LayoutUnit();
133         return std::max<LayoutUnit>(m_contentRuns[index].columnLogicalHeight(startOffset), m_minimumColumnHeight);
134     }
135
136     if (columnCount() <= computedColumnCount()) {
137         // With the current column height, the content fits without creating overflowing columns. We're done.
138         return m_computedColumnHeight;
139     }
140
141     if (forcedBreaksCount() > 1 && forcedBreaksCount() >= computedColumnCount()) {
142         // Too many forced breaks to allow any implicit breaks. Initial balancing should already
143         // have set a good height. There's nothing more we should do.
144         return m_computedColumnHeight;
145     }
146
147     // If the initial guessed column height wasn't enough, stretch it now. Stretch by the lowest
148     // amount of space shortage found during layout.
149
150     ASSERT(m_minSpaceShortage > 0); // We should never _shrink_ the height!
151     ASSERT(m_minSpaceShortage != RenderFlowThread::maxLogicalHeight()); // If this happens, we probably have a bug.
152     if (m_minSpaceShortage == RenderFlowThread::maxLogicalHeight())
153         return m_computedColumnHeight; // So bail out rather than looping infinitely.
154
155     return m_computedColumnHeight + m_minSpaceShortage;
156 }
157
158 void RenderMultiColumnSet::clearForcedBreaks()
159 {
160     m_contentRuns.clear();
161 }
162
163 void RenderMultiColumnSet::addForcedBreak(LayoutUnit offsetFromFirstPage)
164 {
165     if (!toRenderMultiColumnBlock(parent())->requiresBalancing())
166         return;
167     if (!m_contentRuns.isEmpty() && offsetFromFirstPage <= m_contentRuns.last().breakOffset())
168         return;
169     // Append another item as long as we haven't exceeded used column count. What ends up in the
170     // overflow area shouldn't affect column balancing.
171     if (m_contentRuns.size() < m_computedColumnCount)
172         m_contentRuns.append(ContentRun(offsetFromFirstPage));
173 }
174
175 bool RenderMultiColumnSet::recalculateBalancedHeight(bool initial)
176 {
177     ASSERT(toRenderMultiColumnBlock(parent())->requiresBalancing());
178
179     LayoutUnit oldColumnHeight = m_computedColumnHeight;
180     if (initial)
181         distributeImplicitBreaks();
182     LayoutUnit newColumnHeight = calculateBalancedHeight(initial);
183     setAndConstrainColumnHeight(newColumnHeight);
184
185     // After having calculated an initial column height, the multicol container typically needs at
186     // least one more layout pass with a new column height, but if a height was specified, we only
187     // need to do this if we think that we need less space than specified. Conversely, if we
188     // determined that the columns need to be as tall as the specified height of the container, we
189     // have already laid it out correctly, and there's no need for another pass.
190
191     if (m_computedColumnHeight == oldColumnHeight)
192         return false; // No change. We're done.
193
194     m_minSpaceShortage = RenderFlowThread::maxLogicalHeight();
195     clearForcedBreaks();
196     return true; // Need another pass.
197 }
198
199 void RenderMultiColumnSet::recordSpaceShortage(LayoutUnit spaceShortage)
200 {
201     if (spaceShortage >= m_minSpaceShortage)
202         return;
203
204     // The space shortage is what we use as our stretch amount. We need a positive number here in
205     // order to get anywhere.
206     ASSERT(spaceShortage > 0);
207
208     m_minSpaceShortage = spaceShortage;
209 }
210
211 void RenderMultiColumnSet::updateLogicalWidth()
212 {
213     RenderMultiColumnBlock* parentBlock = toRenderMultiColumnBlock(parent());
214     setComputedColumnWidthAndCount(parentBlock->columnWidth(), parentBlock->columnCount()); // FIXME: This will eventually vary if we are contained inside regions.
215
216     // FIXME: When we add regions support, we'll start it off at the width of the multi-column
217     // block in that particular region.
218     setLogicalWidth(parentBox()->contentLogicalWidth());
219
220     // If we overflow, increase our logical width.
221     unsigned colCount = columnCount();
222     LayoutUnit colGap = columnGap();
223     LayoutUnit minimumContentLogicalWidth = colCount * computedColumnWidth() + (colCount - 1) * colGap;
224     LayoutUnit currentContentLogicalWidth = contentLogicalWidth();
225     LayoutUnit delta = max(LayoutUnit(), minimumContentLogicalWidth - currentContentLogicalWidth);
226     if (!delta)
227         return;
228
229     // Increase our logical width by the delta.
230     setLogicalWidth(logicalWidth() + delta);
231 }
232
233 void RenderMultiColumnSet::prepareForLayout()
234 {
235     RenderMultiColumnBlock* multicolBlock = toRenderMultiColumnBlock(parent());
236     RenderStyle* multicolStyle = multicolBlock->style();
237
238     // Set box logical top.
239     ASSERT(!previousSiblingBox() || !previousSiblingBox()->isRenderMultiColumnSet()); // FIXME: multiple set not implemented; need to examine previous set to calculate the correct logical top.
240     setLogicalTop(multicolBlock->borderBefore() + multicolBlock->paddingBefore());
241
242     // Set box width.
243     updateLogicalWidth();
244
245     if (multicolBlock->requiresBalancing()) {
246         // Set maximum column height. We will not stretch beyond this.
247         m_maxColumnHeight = RenderFlowThread::maxLogicalHeight();
248         if (!multicolStyle->logicalHeight().isAuto()) {
249             m_maxColumnHeight = multicolBlock->computeContentLogicalHeight(multicolStyle->logicalHeight(), -1);
250             if (m_maxColumnHeight == -1)
251                 m_maxColumnHeight = RenderFlowThread::maxLogicalHeight();
252         }
253         if (!multicolStyle->logicalMaxHeight().isUndefined()) {
254             LayoutUnit logicalMaxHeight = multicolBlock->computeContentLogicalHeight(multicolStyle->logicalMaxHeight(), -1);
255             if (logicalMaxHeight != -1 && m_maxColumnHeight > logicalMaxHeight)
256                 m_maxColumnHeight = logicalMaxHeight;
257         }
258         m_maxColumnHeight = heightAdjustedForSetOffset(m_maxColumnHeight);
259         m_computedColumnHeight = 0; // Restart balancing.
260     } else {
261         setAndConstrainColumnHeight(heightAdjustedForSetOffset(multicolBlock->columnHeightAvailable()));
262     }
263
264     clearForcedBreaks();
265
266     // Nuke previously stored minimum column height. Contents may have changed for all we know.
267     m_minimumColumnHeight = 0;
268 }
269
270 void RenderMultiColumnSet::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
271 {
272     computedValues.m_extent = m_computedColumnHeight;
273     computedValues.m_position = logicalTop;
274 }
275
276 LayoutUnit RenderMultiColumnSet::columnGap() const
277 {
278     // FIXME: Eventually we will cache the column gap when the widths of columns start varying, but for now we just
279     // go to the parent block to get the gap.
280     RenderMultiColumnBlock* parentBlock = toRenderMultiColumnBlock(parent());
281     if (parentBlock->style()->hasNormalColumnGap())
282         return parentBlock->style()->fontDescription().computedPixelSize(); // "1em" is recommended as the normal gap setting. Matches <p> margins.
283     return parentBlock->style()->columnGap();
284 }
285
286 unsigned RenderMultiColumnSet::columnCount() const
287 {
288     // We must always return a value of 1 or greater. Column count = 0 is a meaningless situation,
289     // and will confuse and cause problems in other parts of the code.
290     if (!computedColumnHeight())
291         return 1;
292
293     // Our portion rect determines our column count. We have as many columns as needed to fit all the content.
294     LayoutUnit logicalHeightInColumns = flowThread()->isHorizontalWritingMode() ? flowThreadPortionRect().height() : flowThreadPortionRect().width();
295     unsigned count = ceil(static_cast<float>(logicalHeightInColumns) / computedColumnHeight());
296     ASSERT(count >= 1);
297     return count;
298 }
299
300 LayoutRect RenderMultiColumnSet::columnRectAt(unsigned index) const
301 {
302     LayoutUnit colLogicalWidth = computedColumnWidth();
303     LayoutUnit colLogicalHeight = computedColumnHeight();
304     LayoutUnit colLogicalTop = borderBefore() + paddingBefore();
305     LayoutUnit colLogicalLeft = borderAndPaddingLogicalLeft();
306     LayoutUnit colGap = columnGap();
307     if (style()->isLeftToRightDirection())
308         colLogicalLeft += index * (colLogicalWidth + colGap);
309     else
310         colLogicalLeft += contentLogicalWidth() - colLogicalWidth - index * (colLogicalWidth + colGap);
311
312     if (isHorizontalWritingMode())
313         return LayoutRect(colLogicalLeft, colLogicalTop, colLogicalWidth, colLogicalHeight);
314     return LayoutRect(colLogicalTop, colLogicalLeft, colLogicalHeight, colLogicalWidth);
315 }
316
317 unsigned RenderMultiColumnSet::columnIndexAtOffset(LayoutUnit offset, ColumnIndexCalculationMode mode) const
318 {
319     LayoutRect portionRect(flowThreadPortionRect());
320
321     // Handle the offset being out of range.
322     LayoutUnit flowThreadLogicalTop = isHorizontalWritingMode() ? portionRect.y() : portionRect.x();
323     if (offset < flowThreadLogicalTop)
324         return 0;
325     // If we're laying out right now, we cannot constrain against some logical bottom, since it
326     // isn't known yet. Otherwise, just return the last column if we're past the logical bottom.
327     if (mode == ClampToExistingColumns) {
328         LayoutUnit flowThreadLogicalBottom = isHorizontalWritingMode() ? portionRect.maxY() : portionRect.maxX();
329         if (offset >= flowThreadLogicalBottom)
330             return columnCount() - 1;
331     }
332
333     // Just divide by the column height to determine the correct column.
334     return static_cast<float>(offset - flowThreadLogicalTop) / computedColumnHeight();
335 }
336
337 LayoutRect RenderMultiColumnSet::flowThreadPortionRectAt(unsigned index) const
338 {
339     LayoutRect portionRect = flowThreadPortionRect();
340     if (isHorizontalWritingMode())
341         portionRect = LayoutRect(portionRect.x(), portionRect.y() + index * computedColumnHeight(), portionRect.width(), computedColumnHeight());
342     else
343         portionRect = LayoutRect(portionRect.x() + index * computedColumnHeight(), portionRect.y(), computedColumnHeight(), portionRect.height());
344     return portionRect;
345 }
346
347 LayoutRect RenderMultiColumnSet::flowThreadPortionOverflowRect(const LayoutRect& portionRect, unsigned index, unsigned colCount, LayoutUnit colGap) const
348 {
349     // This function determines the portion of the flow thread that paints for the column. Along the inline axis, columns are
350     // unclipped at outside edges (i.e., the first and last column in the set), and they clip to half the column
351     // gap along interior edges.
352     //
353     // In the block direction, we will not clip overflow out of the top of the first column, or out of the bottom of
354     // the last column. This applies only to the true first column and last column across all column sets.
355     //
356     // FIXME: Eventually we will know overflow on a per-column basis, but we can't do this until we have a painting
357     // mode that understands not to paint contents from a previous column in the overflow area of a following column.
358     // This problem applies to regions and pages as well and is not unique to columns.
359     bool isFirstColumn = !index;
360     bool isLastColumn = index == colCount - 1;
361     bool isLeftmostColumn = style()->isLeftToRightDirection() ? isFirstColumn : isLastColumn;
362     bool isRightmostColumn = style()->isLeftToRightDirection() ? isLastColumn : isFirstColumn;
363
364     // Calculate the overflow rectangle, based on the flow thread's, clipped at column logical
365     // top/bottom unless it's the first/last column.
366     LayoutRect overflowRect = overflowRectForFlowThreadPortion(portionRect, isFirstColumn && isFirstRegion(), isLastColumn && isLastRegion());
367
368     // Avoid overflowing into neighboring columns, by clipping in the middle of adjacent column
369     // gaps. Also make sure that we avoid rounding errors.
370     if (isHorizontalWritingMode()) {
371         if (!isLeftmostColumn)
372             overflowRect.shiftXEdgeTo(portionRect.x() - colGap / 2);
373         if (!isRightmostColumn)
374             overflowRect.shiftMaxXEdgeTo(portionRect.maxX() + colGap - colGap / 2);
375     } else {
376         if (!isLeftmostColumn)
377             overflowRect.shiftYEdgeTo(portionRect.y() - colGap / 2);
378         if (!isRightmostColumn)
379             overflowRect.shiftMaxYEdgeTo(portionRect.maxY() + colGap - colGap / 2);
380     }
381     return overflowRect;
382 }
383
384 void RenderMultiColumnSet::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
385 {
386     if (style()->visibility() != VISIBLE)
387         return;
388
389     RenderBlockFlow::paintObject(paintInfo, paintOffset);
390
391     // FIXME: Right now we're only painting in the foreground phase.
392     // Columns should technically respect phases and allow for background/float/foreground overlap etc., just like
393     // RenderBlocks do. Note this is a pretty minor issue, since the old column implementation clipped columns
394     // anyway, thus making it impossible for them to overlap one another. It's also really unlikely that the columns
395     // would overlap another block.
396     if (!m_flowThread || !isValid() || (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection))
397         return;
398
399     paintColumnRules(paintInfo, paintOffset);
400 }
401
402 void RenderMultiColumnSet::paintColumnRules(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
403 {
404     if (paintInfo.context->paintingDisabled())
405         return;
406
407     RenderStyle* blockStyle = toRenderMultiColumnBlock(parent())->style();
408     const Color& ruleColor = resolveColor(blockStyle, CSSPropertyWebkitColumnRuleColor);
409     bool ruleTransparent = blockStyle->columnRuleIsTransparent();
410     EBorderStyle ruleStyle = blockStyle->columnRuleStyle();
411     LayoutUnit ruleThickness = blockStyle->columnRuleWidth();
412     LayoutUnit colGap = columnGap();
413     bool renderRule = ruleStyle > BHIDDEN && !ruleTransparent;
414     if (!renderRule)
415         return;
416
417     unsigned colCount = columnCount();
418     if (colCount <= 1)
419         return;
420
421     bool antialias = shouldAntialiasLines(paintInfo.context);
422
423     bool leftToRight = style()->isLeftToRightDirection();
424     LayoutUnit currLogicalLeftOffset = leftToRight ? LayoutUnit() : contentLogicalWidth();
425     LayoutUnit ruleAdd = borderAndPaddingLogicalLeft();
426     LayoutUnit ruleLogicalLeft = leftToRight ? LayoutUnit() : contentLogicalWidth();
427     LayoutUnit inlineDirectionSize = computedColumnWidth();
428     BoxSide boxSide = isHorizontalWritingMode()
429         ? leftToRight ? BSLeft : BSRight
430         : leftToRight ? BSTop : BSBottom;
431
432     for (unsigned i = 0; i < colCount; i++) {
433         // Move to the next position.
434         if (leftToRight) {
435             ruleLogicalLeft += inlineDirectionSize + colGap / 2;
436             currLogicalLeftOffset += inlineDirectionSize + colGap;
437         } else {
438             ruleLogicalLeft -= (inlineDirectionSize + colGap / 2);
439             currLogicalLeftOffset -= (inlineDirectionSize + colGap);
440         }
441
442         // Now paint the column rule.
443         if (i < colCount - 1) {
444             LayoutUnit ruleLeft = isHorizontalWritingMode() ? paintOffset.x() + ruleLogicalLeft - ruleThickness / 2 + ruleAdd : paintOffset.x() + borderLeft() + paddingLeft();
445             LayoutUnit ruleRight = isHorizontalWritingMode() ? ruleLeft + ruleThickness : ruleLeft + contentWidth();
446             LayoutUnit ruleTop = isHorizontalWritingMode() ? paintOffset.y() + borderTop() + paddingTop() : paintOffset.y() + ruleLogicalLeft - ruleThickness / 2 + ruleAdd;
447             LayoutUnit ruleBottom = isHorizontalWritingMode() ? ruleTop + contentHeight() : ruleTop + ruleThickness;
448             IntRect pixelSnappedRuleRect = pixelSnappedIntRectFromEdges(ruleLeft, ruleTop, ruleRight, ruleBottom);
449             drawLineForBoxSide(paintInfo.context, pixelSnappedRuleRect.x(), pixelSnappedRuleRect.y(), pixelSnappedRuleRect.maxX(), pixelSnappedRuleRect.maxY(), boxSide, ruleColor, ruleStyle, 0, 0, antialias);
450         }
451
452         ruleLogicalLeft = currLogicalLeftOffset;
453     }
454 }
455
456 void RenderMultiColumnSet::repaintFlowThreadContent(const LayoutRect& repaintRect) const
457 {
458     // Figure out the start and end columns and only check within that range so that we don't walk the
459     // entire column set. Put the repaint rect into flow thread coordinates by flipping it first.
460     LayoutRect flowThreadRepaintRect(repaintRect);
461     flowThread()->flipForWritingMode(flowThreadRepaintRect);
462
463     // Now we can compare this rect with the flow thread portions owned by each column. First let's
464     // just see if the repaint rect intersects our flow thread portion at all.
465     LayoutRect clippedRect(flowThreadRepaintRect);
466     clippedRect.intersect(RenderRegion::flowThreadPortionOverflowRect());
467     if (clippedRect.isEmpty())
468         return;
469
470     // Now we know we intersect at least one column. Let's figure out the logical top and logical
471     // bottom of the area we're repainting.
472     LayoutUnit repaintLogicalTop = isHorizontalWritingMode() ? flowThreadRepaintRect.y() : flowThreadRepaintRect.x();
473     LayoutUnit repaintLogicalBottom = (isHorizontalWritingMode() ? flowThreadRepaintRect.maxY() : flowThreadRepaintRect.maxX()) - 1;
474
475     unsigned startColumn = columnIndexAtOffset(repaintLogicalTop);
476     unsigned endColumn = columnIndexAtOffset(repaintLogicalBottom);
477
478     LayoutUnit colGap = columnGap();
479     unsigned colCount = columnCount();
480     for (unsigned i = startColumn; i <= endColumn; i++) {
481         LayoutRect colRect = columnRectAt(i);
482
483         // Get the portion of the flow thread that corresponds to this column.
484         LayoutRect flowThreadPortion = flowThreadPortionRectAt(i);
485
486         // Now get the overflow rect that corresponds to the column.
487         LayoutRect flowThreadOverflowPortion = flowThreadPortionOverflowRect(flowThreadPortion, i, colCount, colGap);
488
489         // Do a repaint for this specific column.
490         repaintFlowThreadContentRectangle(repaintRect, flowThreadPortion, flowThreadOverflowPortion, colRect.location());
491     }
492 }
493
494 void RenderMultiColumnSet::collectLayerFragments(LayerFragments& fragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect)
495 {
496     // The two rectangles passed to this method are physical, except that we pretend that there's
497     // only one long column (that's how a flow thread works).
498     //
499     // Then there's the output from this method - the stuff we put into the list of fragments. The
500     // fragment.paginationOffset point is the actual physical translation required to get from a
501     // location in the flow thread to a location in a given column. The fragment.paginationClip
502     // rectangle, on the other hand, is in the same coordinate system as the two rectangles passed
503     // to this method (flow thread coordinates).
504     //
505     // All other rectangles in this method are sized physically, and the inline direction coordinate
506     // is physical too, but the block direction coordinate is "logical top". This is the same as
507     // e.g. RenderBox::frameRect(). These rectangles also pretend that there's only one long column,
508     // i.e. they are for the flow thread.
509
510     // Put the layer bounds into flow thread-local coordinates by flipping it first. Since we're in
511     // a renderer, most rectangles are represented this way.
512     LayoutRect layerBoundsInFlowThread(layerBoundingBox);
513     flowThread()->flipForWritingMode(layerBoundsInFlowThread);
514
515     // Now we can compare with the flow thread portions owned by each column. First let's
516     // see if the rect intersects our flow thread portion at all.
517     LayoutRect clippedRect(layerBoundsInFlowThread);
518     clippedRect.intersect(RenderRegion::flowThreadPortionOverflowRect());
519     if (clippedRect.isEmpty())
520         return;
521
522     // Now we know we intersect at least one column. Let's figure out the logical top and logical
523     // bottom of the area we're checking.
524     LayoutUnit layerLogicalTop = isHorizontalWritingMode() ? layerBoundsInFlowThread.y() : layerBoundsInFlowThread.x();
525     LayoutUnit layerLogicalBottom = (isHorizontalWritingMode() ? layerBoundsInFlowThread.maxY() : layerBoundsInFlowThread.maxX()) - 1;
526
527     // Figure out the start and end columns and only check within that range so that we don't walk the
528     // entire column set.
529     unsigned startColumn = columnIndexAtOffset(layerLogicalTop);
530     unsigned endColumn = columnIndexAtOffset(layerLogicalBottom);
531
532     LayoutUnit colLogicalWidth = computedColumnWidth();
533     LayoutUnit colGap = columnGap();
534     unsigned colCount = columnCount();
535
536     for (unsigned i = startColumn; i <= endColumn; i++) {
537         // Get the portion of the flow thread that corresponds to this column.
538         LayoutRect flowThreadPortion = flowThreadPortionRectAt(i);
539
540         // Now get the overflow rect that corresponds to the column.
541         LayoutRect flowThreadOverflowPortion = flowThreadPortionOverflowRect(flowThreadPortion, i, colCount, colGap);
542
543         // In order to create a fragment we must intersect the portion painted by this column.
544         LayoutRect clippedRect(layerBoundsInFlowThread);
545         clippedRect.intersect(flowThreadOverflowPortion);
546         if (clippedRect.isEmpty())
547             continue;
548
549         // We also need to intersect the dirty rect. We have to apply a translation and shift based off
550         // our column index.
551         LayoutPoint translationOffset;
552         LayoutUnit inlineOffset = i * (colLogicalWidth + colGap);
553         if (!style()->isLeftToRightDirection())
554             inlineOffset = -inlineOffset;
555         translationOffset.setX(inlineOffset);
556         LayoutUnit blockOffset = isHorizontalWritingMode() ? -flowThreadPortion.y() : -flowThreadPortion.x();
557         if (isFlippedBlocksWritingMode(style()->writingMode()))
558             blockOffset = -blockOffset;
559         translationOffset.setY(blockOffset);
560         if (!isHorizontalWritingMode())
561             translationOffset = translationOffset.transposedPoint();
562         // FIXME: The translation needs to include the multicolumn set's content offset within the
563         // multicolumn block as well. This won't be an issue until we start creating multiple multicolumn sets.
564
565         // Shift the dirty rect to be in flow thread coordinates with this translation applied.
566         LayoutRect translatedDirtyRect(dirtyRect);
567         translatedDirtyRect.moveBy(-translationOffset);
568
569         // See if we intersect the dirty rect.
570         clippedRect = layerBoundingBox;
571         clippedRect.intersect(translatedDirtyRect);
572         if (clippedRect.isEmpty())
573             continue;
574
575         // Something does need to paint in this column. Make a fragment now and supply the physical translation
576         // offset and the clip rect for the column with that offset applied.
577         LayerFragment fragment;
578         fragment.paginationOffset = translationOffset;
579
580         LayoutRect flippedFlowThreadOverflowPortion(flowThreadOverflowPortion);
581         // Flip it into more a physical (RenderLayer-style) rectangle.
582         flowThread()->flipForWritingMode(flippedFlowThreadOverflowPortion);
583         fragment.paginationClip = flippedFlowThreadOverflowPortion;
584         fragments.append(fragment);
585     }
586 }
587
588 const char* RenderMultiColumnSet::renderName() const
589 {
590     return "RenderMultiColumnSet";
591 }
592
593 }