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