Home | History | Annotate | Download | only in rendering
      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     LayoutRect overflowRect(portionRect);
    286     if (isHorizontalWritingMode()) {
    287         if (isLeftmostColumn) {
    288             // Shift to the logical left overflow of the flow thread to make sure it's all covered.
    289             overflowRect.shiftXEdgeTo(min(flowThread()->visualOverflowRect().x(), portionRect.x()));
    290         } else {
    291             // Expand into half of the logical left column gap.
    292             overflowRect.shiftXEdgeTo(portionRect.x() - colGap / 2);
    293         }
    294         if (isRightmostColumn) {
    295             // Shift to the logical right overflow of the flow thread to ensure content can spill out of the column.
    296             overflowRect.shiftMaxXEdgeTo(max(flowThread()->visualOverflowRect().maxX(), portionRect.maxX()));
    297         } else {
    298             // Expand into half of the logical right column gap.
    299             overflowRect.shiftMaxXEdgeTo(portionRect.maxX() + colGap / 2);
    300         }
    301     } else {
    302         if (isLeftmostColumn) {
    303             // Shift to the logical left overflow of the flow thread to make sure it's all covered.
    304             overflowRect.shiftYEdgeTo(min(flowThread()->visualOverflowRect().y(), portionRect.y()));
    305         } else {
    306             // Expand into half of the logical left column gap.
    307             overflowRect.shiftYEdgeTo(portionRect.y() - colGap / 2);
    308         }
    309         if (isRightmostColumn) {
    310             // Shift to the logical right overflow of the flow thread to ensure content can spill out of the column.
    311             overflowRect.shiftMaxYEdgeTo(max(flowThread()->visualOverflowRect().maxY(), portionRect.maxY()));
    312         } else {
    313             // Expand into half of the logical right column gap.
    314             overflowRect.shiftMaxYEdgeTo(portionRect.maxY() + colGap / 2);
    315         }
    316     }
    317     return overflowRectForFlowThreadPortion(overflowRect, isFirstRegion() && isFirstColumn, isLastRegion() && isLastColumn);
    318 }
    319 
    320 void RenderMultiColumnSet::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
    321 {
    322     if (style()->visibility() != VISIBLE)
    323         return;
    324 
    325     RenderBlock::paintObject(paintInfo, paintOffset);
    326 
    327     // FIXME: Right now we're only painting in the foreground phase.
    328     // Columns should technically respect phases and allow for background/float/foreground overlap etc., just like
    329     // RenderBlocks do. Note this is a pretty minor issue, since the old column implementation clipped columns
    330     // anyway, thus making it impossible for them to overlap one another. It's also really unlikely that the columns
    331     // would overlap another block.
    332     if (!m_flowThread || !isValid() || (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection))
    333         return;
    334 
    335     paintColumnRules(paintInfo, paintOffset);
    336 }
    337 
    338 void RenderMultiColumnSet::paintColumnRules(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
    339 {
    340     if (paintInfo.context->paintingDisabled())
    341         return;
    342 
    343     RenderStyle* blockStyle = toRenderMultiColumnBlock(parent())->style();
    344     const Color& ruleColor = resolveColor(blockStyle, CSSPropertyWebkitColumnRuleColor);
    345     bool ruleTransparent = blockStyle->columnRuleIsTransparent();
    346     EBorderStyle ruleStyle = blockStyle->columnRuleStyle();
    347     LayoutUnit ruleThickness = blockStyle->columnRuleWidth();
    348     LayoutUnit colGap = columnGap();
    349     bool renderRule = ruleStyle > BHIDDEN && !ruleTransparent;
    350     if (!renderRule)
    351         return;
    352 
    353     unsigned colCount = columnCount();
    354     if (colCount <= 1)
    355         return;
    356 
    357     bool antialias = shouldAntialiasLines(paintInfo.context);
    358 
    359     bool leftToRight = style()->isLeftToRightDirection();
    360     LayoutUnit currLogicalLeftOffset = leftToRight ? LayoutUnit() : contentLogicalWidth();
    361     LayoutUnit ruleAdd = borderAndPaddingLogicalLeft();
    362     LayoutUnit ruleLogicalLeft = leftToRight ? LayoutUnit() : contentLogicalWidth();
    363     LayoutUnit inlineDirectionSize = computedColumnWidth();
    364     BoxSide boxSide = isHorizontalWritingMode()
    365         ? leftToRight ? BSLeft : BSRight
    366         : leftToRight ? BSTop : BSBottom;
    367 
    368     for (unsigned i = 0; i < colCount; i++) {
    369         // Move to the next position.
    370         if (leftToRight) {
    371             ruleLogicalLeft += inlineDirectionSize + colGap / 2;
    372             currLogicalLeftOffset += inlineDirectionSize + colGap;
    373         } else {
    374             ruleLogicalLeft -= (inlineDirectionSize + colGap / 2);
    375             currLogicalLeftOffset -= (inlineDirectionSize + colGap);
    376         }
    377 
    378         // Now paint the column rule.
    379         if (i < colCount - 1) {
    380             LayoutUnit ruleLeft = isHorizontalWritingMode() ? paintOffset.x() + ruleLogicalLeft - ruleThickness / 2 + ruleAdd : paintOffset.x() + borderLeft() + paddingLeft();
    381             LayoutUnit ruleRight = isHorizontalWritingMode() ? ruleLeft + ruleThickness : ruleLeft + contentWidth();
    382             LayoutUnit ruleTop = isHorizontalWritingMode() ? paintOffset.y() + borderTop() + paddingTop() : paintOffset.y() + ruleLogicalLeft - ruleThickness / 2 + ruleAdd;
    383             LayoutUnit ruleBottom = isHorizontalWritingMode() ? ruleTop + contentHeight() : ruleTop + ruleThickness;
    384             IntRect pixelSnappedRuleRect = pixelSnappedIntRectFromEdges(ruleLeft, ruleTop, ruleRight, ruleBottom);
    385             drawLineForBoxSide(paintInfo.context, pixelSnappedRuleRect.x(), pixelSnappedRuleRect.y(), pixelSnappedRuleRect.maxX(), pixelSnappedRuleRect.maxY(), boxSide, ruleColor, ruleStyle, 0, 0, antialias);
    386         }
    387 
    388         ruleLogicalLeft = currLogicalLeftOffset;
    389     }
    390 }
    391 
    392 void RenderMultiColumnSet::repaintFlowThreadContent(const LayoutRect& repaintRect) const
    393 {
    394     // Figure out the start and end columns and only check within that range so that we don't walk the
    395     // entire column set. Put the repaint rect into flow thread coordinates by flipping it first.
    396     LayoutRect flowThreadRepaintRect(repaintRect);
    397     flowThread()->flipForWritingMode(flowThreadRepaintRect);
    398 
    399     // Now we can compare this rect with the flow thread portions owned by each column. First let's
    400     // just see if the repaint rect intersects our flow thread portion at all.
    401     LayoutRect clippedRect(flowThreadRepaintRect);
    402     clippedRect.intersect(RenderRegion::flowThreadPortionOverflowRect());
    403     if (clippedRect.isEmpty())
    404         return;
    405 
    406     // Now we know we intersect at least one column. Let's figure out the logical top and logical
    407     // bottom of the area we're repainting.
    408     LayoutUnit repaintLogicalTop = isHorizontalWritingMode() ? flowThreadRepaintRect.y() : flowThreadRepaintRect.x();
    409     LayoutUnit repaintLogicalBottom = (isHorizontalWritingMode() ? flowThreadRepaintRect.maxY() : flowThreadRepaintRect.maxX()) - 1;
    410 
    411     unsigned startColumn = columnIndexAtOffset(repaintLogicalTop);
    412     unsigned endColumn = columnIndexAtOffset(repaintLogicalBottom);
    413 
    414     LayoutUnit colGap = columnGap();
    415     unsigned colCount = columnCount();
    416     for (unsigned i = startColumn; i <= endColumn; i++) {
    417         LayoutRect colRect = columnRectAt(i);
    418 
    419         // Get the portion of the flow thread that corresponds to this column.
    420         LayoutRect flowThreadPortion = flowThreadPortionRectAt(i);
    421 
    422         // Now get the overflow rect that corresponds to the column.
    423         LayoutRect flowThreadOverflowPortion = flowThreadPortionOverflowRect(flowThreadPortion, i, colCount, colGap);
    424 
    425         // Do a repaint for this specific column.
    426         repaintFlowThreadContentRectangle(repaintRect, flowThreadPortion, flowThreadOverflowPortion, colRect.location());
    427     }
    428 }
    429 
    430 void RenderMultiColumnSet::collectLayerFragments(LayerFragments& fragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect)
    431 {
    432     // Put the layer bounds into flow thread-local coordinates by flipping it first.
    433     LayoutRect layerBoundsInFlowThread(layerBoundingBox);
    434     flowThread()->flipForWritingMode(layerBoundsInFlowThread);
    435 
    436     // Do the same for the dirty rect.
    437     LayoutRect dirtyRectInFlowThread(dirtyRect);
    438     flowThread()->flipForWritingMode(dirtyRectInFlowThread);
    439 
    440     // Now we can compare with the flow thread portions owned by each column. First let's
    441     // see if the rect intersects our flow thread portion at all.
    442     LayoutRect clippedRect(layerBoundsInFlowThread);
    443     clippedRect.intersect(RenderRegion::flowThreadPortionOverflowRect());
    444     if (clippedRect.isEmpty())
    445         return;
    446 
    447     // Now we know we intersect at least one column. Let's figure out the logical top and logical
    448     // bottom of the area we're checking.
    449     LayoutUnit layerLogicalTop = isHorizontalWritingMode() ? layerBoundsInFlowThread.y() : layerBoundsInFlowThread.x();
    450     LayoutUnit layerLogicalBottom = (isHorizontalWritingMode() ? layerBoundsInFlowThread.maxY() : layerBoundsInFlowThread.maxX()) - 1;
    451 
    452     // Figure out the start and end columns and only check within that range so that we don't walk the
    453     // entire column set.
    454     unsigned startColumn = columnIndexAtOffset(layerLogicalTop);
    455     unsigned endColumn = columnIndexAtOffset(layerLogicalBottom);
    456 
    457     LayoutUnit colLogicalWidth = computedColumnWidth();
    458     LayoutUnit colGap = columnGap();
    459     unsigned colCount = columnCount();
    460 
    461     for (unsigned i = startColumn; i <= endColumn; i++) {
    462         // Get the portion of the flow thread that corresponds to this column.
    463         LayoutRect flowThreadPortion = flowThreadPortionRectAt(i);
    464 
    465         // Now get the overflow rect that corresponds to the column.
    466         LayoutRect flowThreadOverflowPortion = flowThreadPortionOverflowRect(flowThreadPortion, i, colCount, colGap);
    467 
    468         // In order to create a fragment we must intersect the portion painted by this column.
    469         LayoutRect clippedRect(layerBoundsInFlowThread);
    470         clippedRect.intersect(flowThreadOverflowPortion);
    471         if (clippedRect.isEmpty())
    472             continue;
    473 
    474         // We also need to intersect the dirty rect. We have to apply a translation and shift based off
    475         // our column index.
    476         LayoutPoint translationOffset;
    477         LayoutUnit inlineOffset = i * (colLogicalWidth + colGap);
    478         if (!style()->isLeftToRightDirection())
    479             inlineOffset = -inlineOffset;
    480         translationOffset.setX(inlineOffset);
    481         LayoutUnit blockOffset = isHorizontalWritingMode() ? -flowThreadPortion.y() : -flowThreadPortion.x();
    482         if (isFlippedBlocksWritingMode(style()->writingMode()))
    483             blockOffset = -blockOffset;
    484         translationOffset.setY(blockOffset);
    485         if (!isHorizontalWritingMode())
    486             translationOffset = translationOffset.transposedPoint();
    487         // FIXME: The translation needs to include the multicolumn set's content offset within the
    488         // multicolumn block as well. This won't be an issue until we start creating multiple multicolumn sets.
    489 
    490         // Shift the dirty rect to be in flow thread coordinates with this translation applied.
    491         LayoutRect translatedDirtyRect(dirtyRectInFlowThread);
    492         translatedDirtyRect.moveBy(-translationOffset);
    493 
    494         // See if we intersect the dirty rect.
    495         clippedRect = layerBoundsInFlowThread;
    496         clippedRect.intersect(translatedDirtyRect);
    497         if (clippedRect.isEmpty())
    498             continue;
    499 
    500         // Something does need to paint in this column. Make a fragment now and supply the physical translation
    501         // offset and the clip rect for the column with that offset applied.
    502         LayerFragment fragment;
    503         fragment.paginationOffset = translationOffset;
    504 
    505         LayoutRect flippedFlowThreadOverflowPortion(flowThreadOverflowPortion);
    506         flipForWritingMode(flippedFlowThreadOverflowPortion);
    507         fragment.paginationClip = flippedFlowThreadOverflowPortion;
    508         fragments.append(fragment);
    509     }
    510 }
    511 
    512 const char* RenderMultiColumnSet::renderName() const
    513 {
    514     return "RenderMultiColumnSet";
    515 }
    516 
    517 }
    518