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/paint/BoxPainter.h"
     30 #include "core/paint/MultiColumnSetPainter.h"
     31 #include "core/paint/ObjectPainter.h"
     32 #include "core/rendering/PaintInfo.h"
     33 #include "core/rendering/RenderLayer.h"
     34 #include "core/rendering/RenderMultiColumnFlowThread.h"
     35 
     36 namespace blink {
     37 
     38 RenderMultiColumnSet::RenderMultiColumnSet(RenderFlowThread* flowThread)
     39     : RenderRegion(0, flowThread)
     40     , m_columnHeight(0)
     41     , m_maxColumnHeight(RenderFlowThread::maxLogicalHeight())
     42     , m_minSpaceShortage(RenderFlowThread::maxLogicalHeight())
     43     , m_minimumColumnHeight(0)
     44 {
     45 }
     46 
     47 RenderMultiColumnSet* RenderMultiColumnSet::createAnonymous(RenderFlowThread* flowThread, RenderStyle* parentStyle)
     48 {
     49     Document& document = flowThread->document();
     50     RenderMultiColumnSet* renderer = new RenderMultiColumnSet(flowThread);
     51     renderer->setDocumentForAnonymous(&document);
     52     renderer->setStyle(RenderStyle::createAnonymousStyleWithDisplay(parentStyle, BLOCK));
     53     return renderer;
     54 }
     55 
     56 RenderMultiColumnSet* RenderMultiColumnSet::nextSiblingMultiColumnSet() const
     57 {
     58     for (RenderObject* sibling = nextSibling(); sibling; sibling = sibling->nextSibling()) {
     59         if (sibling->isRenderMultiColumnSet())
     60             return toRenderMultiColumnSet(sibling);
     61     }
     62     return 0;
     63 }
     64 
     65 RenderMultiColumnSet* RenderMultiColumnSet::previousSiblingMultiColumnSet() const
     66 {
     67     for (RenderObject* sibling = previousSibling(); sibling; sibling = sibling->previousSibling()) {
     68         if (sibling->isRenderMultiColumnSet())
     69             return toRenderMultiColumnSet(sibling);
     70     }
     71     return 0;
     72 }
     73 
     74 LayoutSize RenderMultiColumnSet::flowThreadTranslationAtOffset(LayoutUnit blockOffset) const
     75 {
     76     unsigned columnIndex = columnIndexAtOffset(blockOffset);
     77     LayoutRect portionRect(flowThreadPortionRectAt(columnIndex));
     78     flipForWritingMode(portionRect);
     79     LayoutRect columnRect(columnRectAt(columnIndex));
     80     flipForWritingMode(columnRect);
     81     return contentBoxRect().location() + columnRect.location() - portionRect.location();
     82 }
     83 
     84 LayoutUnit RenderMultiColumnSet::heightAdjustedForSetOffset(LayoutUnit height) const
     85 {
     86     // Adjust for the top offset within the content box of the multicol container (containing
     87     // block), unless this is the first set. We know that the top offset for the first set will be
     88     // zero, but if the multicol container has non-zero top border or padding, the set's top offset
     89     // (initially being 0 and relative to the border box) will be negative until it has been laid
     90     // out. Had we used this bogus offset, we would calculate the wrong height, and risk performing
     91     // a wasted layout iteration. Of course all other sets (if any) have this problem in the first
     92     // layout pass too, but there's really nothing we can do there until the flow thread has been
     93     // laid out anyway.
     94     if (previousSiblingMultiColumnSet()) {
     95         RenderBlockFlow* multicolBlock = multiColumnBlockFlow();
     96         LayoutUnit contentLogicalTop = logicalTop() - multicolBlock->borderAndPaddingBefore();
     97         height -= contentLogicalTop;
     98     }
     99     return max(height, LayoutUnit(1)); // Let's avoid zero height, as that would probably cause an infinite amount of columns to be created.
    100 }
    101 
    102 LayoutUnit RenderMultiColumnSet::pageLogicalTopForOffset(LayoutUnit offset) const
    103 {
    104     unsigned columnIndex = columnIndexAtOffset(offset, AssumeNewColumns);
    105     return logicalTopInFlowThread() + columnIndex * pageLogicalHeight();
    106 }
    107 
    108 void RenderMultiColumnSet::setAndConstrainColumnHeight(LayoutUnit newHeight)
    109 {
    110     m_columnHeight = newHeight;
    111     if (m_columnHeight > m_maxColumnHeight)
    112         m_columnHeight = m_maxColumnHeight;
    113     // FIXME: the height may also be affected by the enclosing pagination context, if any.
    114 }
    115 
    116 unsigned RenderMultiColumnSet::findRunWithTallestColumns() const
    117 {
    118     unsigned indexWithLargestHeight = 0;
    119     LayoutUnit largestHeight;
    120     LayoutUnit previousOffset = logicalTopInFlowThread();
    121     size_t runCount = m_contentRuns.size();
    122     ASSERT(runCount);
    123     for (size_t i = 0; i < runCount; i++) {
    124         const ContentRun& run = m_contentRuns[i];
    125         LayoutUnit height = run.columnLogicalHeight(previousOffset);
    126         if (largestHeight < height) {
    127             largestHeight = height;
    128             indexWithLargestHeight = i;
    129         }
    130         previousOffset = run.breakOffset();
    131     }
    132     return indexWithLargestHeight;
    133 }
    134 
    135 void RenderMultiColumnSet::distributeImplicitBreaks()
    136 {
    137 #if ENABLE(ASSERT)
    138     // There should be no implicit breaks assumed at this point.
    139     for (unsigned i = 0; i < m_contentRuns.size(); i++)
    140         ASSERT(!m_contentRuns[i].assumedImplicitBreaks());
    141 #endif // ENABLE(ASSERT)
    142 
    143     // Insert a final content run to encompass all content. This will include overflow if this is
    144     // the last set.
    145     addContentRun(logicalBottomInFlowThread());
    146     unsigned columnCount = m_contentRuns.size();
    147 
    148     // If there is room for more breaks (to reach the used value of column-count), imagine that we
    149     // insert implicit breaks at suitable locations. At any given time, the content run with the
    150     // currently tallest columns will get another implicit break "inserted", which will increase its
    151     // column count by one and shrink its columns' height. Repeat until we have the desired total
    152     // number of breaks. The largest column height among the runs will then be the initial column
    153     // height for the balancer to use.
    154     while (columnCount < usedColumnCount()) {
    155         unsigned index = findRunWithTallestColumns();
    156         m_contentRuns[index].assumeAnotherImplicitBreak();
    157         columnCount++;
    158     }
    159 }
    160 
    161 LayoutUnit RenderMultiColumnSet::calculateColumnHeight(BalancedHeightCalculation calculationMode) const
    162 {
    163     if (calculationMode == GuessFromFlowThreadPortion) {
    164         // Initial balancing. Start with the lowest imaginable column height. We use the tallest
    165         // content run (after having "inserted" implicit breaks), and find its start offset (by
    166         // looking at the previous run's end offset, or, if there's no previous run, the set's start
    167         // offset in the flow thread).
    168         unsigned index = findRunWithTallestColumns();
    169         LayoutUnit startOffset = index > 0 ? m_contentRuns[index - 1].breakOffset() : logicalTopInFlowThread();
    170         return std::max<LayoutUnit>(m_contentRuns[index].columnLogicalHeight(startOffset), m_minimumColumnHeight);
    171     }
    172 
    173     if (actualColumnCount() <= usedColumnCount()) {
    174         // With the current column height, the content fits without creating overflowing columns. We're done.
    175         return m_columnHeight;
    176     }
    177 
    178     if (m_contentRuns.size() >= usedColumnCount()) {
    179         // Too many forced breaks to allow any implicit breaks. Initial balancing should already
    180         // have set a good height. There's nothing more we should do.
    181         return m_columnHeight;
    182     }
    183 
    184     // If the initial guessed column height wasn't enough, stretch it now. Stretch by the lowest
    185     // amount of space shortage found during layout.
    186 
    187     ASSERT(m_minSpaceShortage > 0); // We should never _shrink_ the height!
    188     ASSERT(m_minSpaceShortage != RenderFlowThread::maxLogicalHeight()); // If this happens, we probably have a bug.
    189     if (m_minSpaceShortage == RenderFlowThread::maxLogicalHeight())
    190         return m_columnHeight; // So bail out rather than looping infinitely.
    191 
    192     return m_columnHeight + m_minSpaceShortage;
    193 }
    194 
    195 void RenderMultiColumnSet::addContentRun(LayoutUnit endOffsetFromFirstPage)
    196 {
    197     if (!multiColumnFlowThread()->heightIsAuto())
    198         return;
    199     if (!m_contentRuns.isEmpty() && endOffsetFromFirstPage <= m_contentRuns.last().breakOffset())
    200         return;
    201     // Append another item as long as we haven't exceeded used column count. What ends up in the
    202     // overflow area shouldn't affect column balancing.
    203     if (m_contentRuns.size() < usedColumnCount())
    204         m_contentRuns.append(ContentRun(endOffsetFromFirstPage));
    205 }
    206 
    207 bool RenderMultiColumnSet::recalculateColumnHeight(BalancedHeightCalculation calculationMode)
    208 {
    209     ASSERT(multiColumnFlowThread()->heightIsAuto());
    210 
    211     LayoutUnit oldColumnHeight = m_columnHeight;
    212     if (calculationMode == GuessFromFlowThreadPortion) {
    213         // Post-process the content runs and find out where the implicit breaks will occur.
    214         distributeImplicitBreaks();
    215     }
    216     LayoutUnit newColumnHeight = calculateColumnHeight(calculationMode);
    217     setAndConstrainColumnHeight(newColumnHeight);
    218 
    219     // After having calculated an initial column height, the multicol container typically needs at
    220     // least one more layout pass with a new column height, but if a height was specified, we only
    221     // need to do this if we think that we need less space than specified. Conversely, if we
    222     // determined that the columns need to be as tall as the specified height of the container, we
    223     // have already laid it out correctly, and there's no need for another pass.
    224 
    225     // We can get rid of the content runs now, if we haven't already done so. They are only needed
    226     // to calculate the initial balanced column height. In fact, we have to get rid of them before
    227     // the next layout pass, since each pass will rebuild this.
    228     m_contentRuns.clear();
    229 
    230     if (m_columnHeight == oldColumnHeight)
    231         return false; // No change. We're done.
    232 
    233     m_minSpaceShortage = RenderFlowThread::maxLogicalHeight();
    234     return true; // Need another pass.
    235 }
    236 
    237 void RenderMultiColumnSet::recordSpaceShortage(LayoutUnit spaceShortage)
    238 {
    239     if (spaceShortage >= m_minSpaceShortage)
    240         return;
    241 
    242     // The space shortage is what we use as our stretch amount. We need a positive number here in
    243     // order to get anywhere.
    244     ASSERT(spaceShortage > 0);
    245 
    246     m_minSpaceShortage = spaceShortage;
    247 }
    248 
    249 void RenderMultiColumnSet::resetColumnHeight()
    250 {
    251     // Nuke previously stored minimum column height. Contents may have changed for all we know.
    252     m_minimumColumnHeight = 0;
    253 
    254     m_maxColumnHeight = calculateMaxColumnHeight();
    255 
    256     LayoutUnit oldColumnHeight = pageLogicalHeight();
    257 
    258     if (multiColumnFlowThread()->heightIsAuto())
    259         m_columnHeight = 0;
    260     else
    261         setAndConstrainColumnHeight(heightAdjustedForSetOffset(multiColumnFlowThread()->columnHeightAvailable()));
    262 
    263     if (pageLogicalHeight() != oldColumnHeight)
    264         setChildNeedsLayout(MarkOnlyThis);
    265 
    266     // Content runs are only needed in the initial layout pass, in order to find an initial column
    267     // height, and should have been deleted afterwards. We're about to rebuild the content runs, so
    268     // the list needs to be empty.
    269     ASSERT(m_contentRuns.isEmpty());
    270 }
    271 
    272 void RenderMultiColumnSet::expandToEncompassFlowThreadContentsIfNeeded()
    273 {
    274     ASSERT(multiColumnFlowThread()->lastMultiColumnSet() == this);
    275     LayoutRect rect(flowThreadPortionRect());
    276 
    277     // Get the offset within the flow thread in its block progression direction. Then get the
    278     // flow thread's remaining logical height including its overflow and expand our rect
    279     // to encompass that remaining height and overflow. The idea is that we will generate
    280     // additional columns and pages to hold that overflow, since people do write bad
    281     // content like <body style="height:0px"> in multi-column layouts.
    282     bool isHorizontal = flowThread()->isHorizontalWritingMode();
    283     LayoutUnit logicalTopOffset = isHorizontal ? rect.y() : rect.x();
    284     LayoutRect layoutRect = flowThread()->layoutOverflowRect();
    285     LayoutUnit logicalHeightWithOverflow = (isHorizontal ? layoutRect.maxY() : layoutRect.maxX()) - logicalTopOffset;
    286     setFlowThreadPortionRect(LayoutRect(rect.x(), rect.y(), isHorizontal ? rect.width() : logicalHeightWithOverflow, isHorizontal ? logicalHeightWithOverflow : rect.height()));
    287 }
    288 
    289 void RenderMultiColumnSet::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
    290 {
    291     computedValues.m_extent = m_columnHeight;
    292     computedValues.m_position = logicalTop;
    293 }
    294 
    295 LayoutUnit RenderMultiColumnSet::calculateMaxColumnHeight() const
    296 {
    297     RenderBlockFlow* multicolBlock = multiColumnBlockFlow();
    298     RenderStyle* multicolStyle = multicolBlock->style();
    299     LayoutUnit availableHeight = multiColumnFlowThread()->columnHeightAvailable();
    300     LayoutUnit maxColumnHeight = availableHeight ? availableHeight : RenderFlowThread::maxLogicalHeight();
    301     if (!multicolStyle->logicalMaxHeight().isMaxSizeNone()) {
    302         LayoutUnit logicalMaxHeight = multicolBlock->computeContentLogicalHeight(multicolStyle->logicalMaxHeight(), -1);
    303         if (logicalMaxHeight != -1 && maxColumnHeight > logicalMaxHeight)
    304             maxColumnHeight = logicalMaxHeight;
    305     }
    306     return heightAdjustedForSetOffset(maxColumnHeight);
    307 }
    308 
    309 LayoutUnit RenderMultiColumnSet::columnGap() const
    310 {
    311     RenderBlockFlow* parentBlock = multiColumnBlockFlow();
    312     if (parentBlock->style()->hasNormalColumnGap())
    313         return parentBlock->style()->fontDescription().computedPixelSize(); // "1em" is recommended as the normal gap setting. Matches <p> margins.
    314     return parentBlock->style()->columnGap();
    315 }
    316 
    317 unsigned RenderMultiColumnSet::actualColumnCount() const
    318 {
    319     // We must always return a value of 1 or greater. Column count = 0 is a meaningless situation,
    320     // and will confuse and cause problems in other parts of the code.
    321     if (!pageLogicalHeight())
    322         return 1;
    323 
    324     // Our portion rect determines our column count. We have as many columns as needed to fit all the content.
    325     LayoutUnit logicalHeightInColumns = flowThread()->isHorizontalWritingMode() ? flowThreadPortionRect().height() : flowThreadPortionRect().width();
    326     if (!logicalHeightInColumns)
    327         return 1;
    328 
    329     unsigned count = ceil(logicalHeightInColumns.toFloat() / pageLogicalHeight().toFloat());
    330     ASSERT(count >= 1);
    331     return count;
    332 }
    333 
    334 LayoutRect RenderMultiColumnSet::columnRectAt(unsigned index) const
    335 {
    336     LayoutUnit colLogicalWidth = pageLogicalWidth();
    337     LayoutUnit colLogicalHeight = pageLogicalHeight();
    338     LayoutUnit colLogicalTop = borderBefore() + paddingBefore();
    339     LayoutUnit colLogicalLeft = borderAndPaddingLogicalLeft();
    340     LayoutUnit colGap = columnGap();
    341 
    342     if (multiColumnFlowThread()->progressionIsInline()) {
    343         if (style()->isLeftToRightDirection())
    344             colLogicalLeft += index * (colLogicalWidth + colGap);
    345         else
    346             colLogicalLeft += contentLogicalWidth() - colLogicalWidth - index * (colLogicalWidth + colGap);
    347     } else {
    348         colLogicalTop += index * (colLogicalHeight + colGap);
    349     }
    350 
    351     if (isHorizontalWritingMode())
    352         return LayoutRect(colLogicalLeft, colLogicalTop, colLogicalWidth, colLogicalHeight);
    353     return LayoutRect(colLogicalTop, colLogicalLeft, colLogicalHeight, colLogicalWidth);
    354 }
    355 
    356 unsigned RenderMultiColumnSet::columnIndexAtOffset(LayoutUnit offset, ColumnIndexCalculationMode mode) const
    357 {
    358     LayoutRect portionRect(flowThreadPortionRect());
    359 
    360     // Handle the offset being out of range.
    361     LayoutUnit flowThreadLogicalTop = isHorizontalWritingMode() ? portionRect.y() : portionRect.x();
    362     if (offset < flowThreadLogicalTop)
    363         return 0;
    364     // If we're laying out right now, we cannot constrain against some logical bottom, since it
    365     // isn't known yet. Otherwise, just return the last column if we're past the logical bottom.
    366     if (mode == ClampToExistingColumns) {
    367         LayoutUnit flowThreadLogicalBottom = isHorizontalWritingMode() ? portionRect.maxY() : portionRect.maxX();
    368         if (offset >= flowThreadLogicalBottom)
    369             return actualColumnCount() - 1;
    370     }
    371 
    372     // Just divide by the column height to determine the correct column.
    373     return (offset - flowThreadLogicalTop).toFloat() / pageLogicalHeight().toFloat();
    374 }
    375 
    376 LayoutRect RenderMultiColumnSet::flowThreadPortionRectAt(unsigned index) const
    377 {
    378     LayoutRect portionRect = flowThreadPortionRect();
    379     if (isHorizontalWritingMode())
    380         portionRect = LayoutRect(portionRect.x(), portionRect.y() + index * pageLogicalHeight(), portionRect.width(), pageLogicalHeight());
    381     else
    382         portionRect = LayoutRect(portionRect.x() + index * pageLogicalHeight(), portionRect.y(), pageLogicalHeight(), portionRect.height());
    383     return portionRect;
    384 }
    385 
    386 LayoutRect RenderMultiColumnSet::flowThreadPortionOverflowRect(const LayoutRect& portionRect, unsigned index, unsigned colCount, LayoutUnit colGap) const
    387 {
    388     // This function determines the portion of the flow thread that paints for the column. Along the inline axis, columns are
    389     // unclipped at outside edges (i.e., the first and last column in the set), and they clip to half the column
    390     // gap along interior edges.
    391     //
    392     // In the block direction, we will not clip overflow out of the top of the first column, or out of the bottom of
    393     // the last column. This applies only to the true first column and last column across all column sets.
    394     //
    395     // FIXME: Eventually we will know overflow on a per-column basis, but we can't do this until we have a painting
    396     // mode that understands not to paint contents from a previous column in the overflow area of a following column.
    397     // This problem applies to regions and pages as well and is not unique to columns.
    398     bool isFirstColumn = !index;
    399     bool isLastColumn = index == colCount - 1;
    400     bool isLeftmostColumn = style()->isLeftToRightDirection() ? isFirstColumn : isLastColumn;
    401     bool isRightmostColumn = style()->isLeftToRightDirection() ? isLastColumn : isFirstColumn;
    402 
    403     // Calculate the overflow rectangle, based on the flow thread's, clipped at column logical
    404     // top/bottom unless it's the first/last column.
    405     LayoutRect overflowRect = overflowRectForFlowThreadPortion(portionRect, isFirstColumn && isFirstRegion(), isLastColumn && isLastRegion());
    406 
    407     // Avoid overflowing into neighboring columns, by clipping in the middle of adjacent column
    408     // gaps. Also make sure that we avoid rounding errors.
    409     if (isHorizontalWritingMode()) {
    410         if (!isLeftmostColumn)
    411             overflowRect.shiftXEdgeTo(portionRect.x() - colGap / 2);
    412         if (!isRightmostColumn)
    413             overflowRect.shiftMaxXEdgeTo(portionRect.maxX() + colGap - colGap / 2);
    414     } else {
    415         if (!isLeftmostColumn)
    416             overflowRect.shiftYEdgeTo(portionRect.y() - colGap / 2);
    417         if (!isRightmostColumn)
    418             overflowRect.shiftMaxYEdgeTo(portionRect.maxY() + colGap - colGap / 2);
    419     }
    420     return overflowRect;
    421 }
    422 
    423 void RenderMultiColumnSet::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
    424 {
    425     MultiColumnSetPainter(*this).paintObject(paintInfo, paintOffset);
    426 }
    427 
    428 void RenderMultiColumnSet::paintInvalidationForFlowThreadContent(const LayoutRect& paintInvalidationRect) const
    429 {
    430     // Figure out the start and end columns and only check within that range so that we don't walk the
    431     // entire column set. Put the paint invalidation rect into flow thread coordinates by flipping it first.
    432     LayoutRect flowThreadPaintInvalidationRect(paintInvalidationRect);
    433     flowThread()->flipForWritingMode(flowThreadPaintInvalidationRect);
    434 
    435     // Now we can compare this rect with the flow thread portions owned by each column. First let's
    436     // just see if the paint invalidation rect intersects our flow thread portion at all.
    437     LayoutRect clippedRect(flowThreadPaintInvalidationRect);
    438     clippedRect.intersect(RenderRegion::flowThreadPortionOverflowRect());
    439     if (clippedRect.isEmpty())
    440         return;
    441 
    442     // Now we know we intersect at least one column. Let's figure out the logical top and logical
    443     // bottom of the area in which we're issuing paint invalidations.
    444     LayoutUnit paintInvalidationLogicalTop = isHorizontalWritingMode() ? flowThreadPaintInvalidationRect.y() : flowThreadPaintInvalidationRect.x();
    445     LayoutUnit paintInvalidationLogicalBottom = (isHorizontalWritingMode() ? flowThreadPaintInvalidationRect.maxY() : flowThreadPaintInvalidationRect.maxX()) - 1;
    446 
    447     unsigned startColumn = columnIndexAtOffset(paintInvalidationLogicalTop);
    448     unsigned endColumn = columnIndexAtOffset(paintInvalidationLogicalBottom);
    449 
    450     LayoutUnit colGap = columnGap();
    451     unsigned colCount = actualColumnCount();
    452     for (unsigned i = startColumn; i <= endColumn; i++) {
    453         LayoutRect colRect = columnRectAt(i);
    454 
    455         // Get the portion of the flow thread that corresponds to this column.
    456         LayoutRect flowThreadPortion = flowThreadPortionRectAt(i);
    457 
    458         // Now get the overflow rect that corresponds to the column.
    459         LayoutRect flowThreadOverflowPortion = flowThreadPortionOverflowRect(flowThreadPortion, i, colCount, colGap);
    460 
    461         // Do a paint invalidation for this specific column.
    462         paintInvalidationOfFlowThreadContentRectangle(paintInvalidationRect, flowThreadPortion, flowThreadOverflowPortion, colRect.location());
    463     }
    464 }
    465 
    466 void RenderMultiColumnSet::collectLayerFragments(LayerFragments& fragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect)
    467 {
    468     // The two rectangles passed to this method are physical, except that we pretend that there's
    469     // only one long column (that's how a flow thread works).
    470     //
    471     // Then there's the output from this method - the stuff we put into the list of fragments. The
    472     // fragment.paginationOffset point is the actual physical translation required to get from a
    473     // location in the flow thread to a location in a given column. The fragment.paginationClip
    474     // rectangle, on the other hand, is in the same coordinate system as the two rectangles passed
    475     // to this method (flow thread coordinates).
    476     //
    477     // All other rectangles in this method are sized physically, and the inline direction coordinate
    478     // is physical too, but the block direction coordinate is "logical top". This is the same as
    479     // e.g. RenderBox::frameRect(). These rectangles also pretend that there's only one long column,
    480     // i.e. they are for the flow thread.
    481 
    482     // Put the layer bounds into flow thread-local coordinates by flipping it first. Since we're in
    483     // a renderer, most rectangles are represented this way.
    484     LayoutRect layerBoundsInFlowThread(layerBoundingBox);
    485     flowThread()->flipForWritingMode(layerBoundsInFlowThread);
    486 
    487     // Now we can compare with the flow thread portions owned by each column. First let's
    488     // see if the rect intersects our flow thread portion at all.
    489     LayoutRect clippedRect(layerBoundsInFlowThread);
    490     clippedRect.intersect(RenderRegion::flowThreadPortionOverflowRect());
    491     if (clippedRect.isEmpty())
    492         return;
    493 
    494     // Now we know we intersect at least one column. Let's figure out the logical top and logical
    495     // bottom of the area we're checking.
    496     LayoutUnit layerLogicalTop = isHorizontalWritingMode() ? layerBoundsInFlowThread.y() : layerBoundsInFlowThread.x();
    497     LayoutUnit layerLogicalBottom = (isHorizontalWritingMode() ? layerBoundsInFlowThread.maxY() : layerBoundsInFlowThread.maxX()) - 1;
    498 
    499     // Figure out the start and end columns and only check within that range so that we don't walk the
    500     // entire column set.
    501     unsigned startColumn = columnIndexAtOffset(layerLogicalTop);
    502     unsigned endColumn = columnIndexAtOffset(layerLogicalBottom);
    503 
    504     LayoutUnit colLogicalWidth = pageLogicalWidth();
    505     LayoutUnit colGap = columnGap();
    506     unsigned colCount = actualColumnCount();
    507 
    508     RenderMultiColumnFlowThread* flowThread = multiColumnFlowThread();
    509     bool progressionIsInline = flowThread->progressionIsInline();
    510     bool leftToRight = style()->isLeftToRightDirection();
    511 
    512     LayoutUnit initialBlockOffset = logicalTop() - flowThread->logicalTop();
    513 
    514     for (unsigned i = startColumn; i <= endColumn; i++) {
    515         // Get the portion of the flow thread that corresponds to this column.
    516         LayoutRect flowThreadPortion = flowThreadPortionRectAt(i);
    517 
    518         // Now get the overflow rect that corresponds to the column.
    519         LayoutRect flowThreadOverflowPortion = flowThreadPortionOverflowRect(flowThreadPortion, i, colCount, colGap);
    520 
    521         // In order to create a fragment we must intersect the portion painted by this column.
    522         LayoutRect clippedRect(layerBoundsInFlowThread);
    523         clippedRect.intersect(flowThreadOverflowPortion);
    524         if (clippedRect.isEmpty())
    525             continue;
    526 
    527         // We also need to intersect the dirty rect. We have to apply a translation and shift based off
    528         // our column index.
    529         LayoutPoint translationOffset;
    530         LayoutUnit inlineOffset = progressionIsInline ? i * (colLogicalWidth + colGap) : LayoutUnit();
    531         if (!leftToRight)
    532             inlineOffset = -inlineOffset;
    533         translationOffset.setX(inlineOffset);
    534         LayoutUnit blockOffset;
    535         if (progressionIsInline) {
    536             blockOffset = initialBlockOffset + (isHorizontalWritingMode() ? -flowThreadPortion.y() : -flowThreadPortion.x());
    537         } else {
    538             // Column gap can apply in the block direction for page fragmentainers.
    539             // There is currently no spec which calls for column-gap to apply
    540             // for page fragmentainers at all, but it's applied here for compatibility
    541             // with the old multicolumn implementation.
    542             blockOffset = i * colGap;
    543         }
    544         if (isFlippedBlocksWritingMode(style()->writingMode()))
    545             blockOffset = -blockOffset;
    546         translationOffset.setY(blockOffset);
    547         if (!isHorizontalWritingMode())
    548             translationOffset = translationOffset.transposedPoint();
    549         // FIXME: The translation needs to include the multicolumn set's content offset within the
    550         // multicolumn block as well. This won't be an issue until we start creating multiple multicolumn sets.
    551 
    552         // Shift the dirty rect to be in flow thread coordinates with this translation applied.
    553         LayoutRect translatedDirtyRect(dirtyRect);
    554         translatedDirtyRect.moveBy(-translationOffset);
    555 
    556         // See if we intersect the dirty rect.
    557         clippedRect = layerBoundingBox;
    558         clippedRect.intersect(translatedDirtyRect);
    559         if (clippedRect.isEmpty())
    560             continue;
    561 
    562         // Something does need to paint in this column. Make a fragment now and supply the physical translation
    563         // offset and the clip rect for the column with that offset applied.
    564         LayerFragment fragment;
    565         fragment.paginationOffset = translationOffset;
    566 
    567         LayoutRect flippedFlowThreadOverflowPortion(flowThreadOverflowPortion);
    568         // Flip it into more a physical (RenderLayer-style) rectangle.
    569         flowThread->flipForWritingMode(flippedFlowThreadOverflowPortion);
    570         fragment.paginationClip = flippedFlowThreadOverflowPortion;
    571         fragments.append(fragment);
    572     }
    573 }
    574 
    575 void RenderMultiColumnSet::addOverflowFromChildren()
    576 {
    577     unsigned colCount = actualColumnCount();
    578     if (!colCount)
    579         return;
    580 
    581     LayoutRect lastRect = columnRectAt(colCount - 1);
    582     addLayoutOverflow(lastRect);
    583     if (!hasOverflowClip())
    584         addVisualOverflow(lastRect);
    585 }
    586 
    587 const char* RenderMultiColumnSet::renderName() const
    588 {
    589     return "RenderMultiColumnSet";
    590 }
    591 
    592 void RenderMultiColumnSet::insertedIntoTree()
    593 {
    594     RenderRegion::insertedIntoTree();
    595 
    596     attachRegion();
    597 }
    598 
    599 void RenderMultiColumnSet::willBeRemovedFromTree()
    600 {
    601     RenderRegion::willBeRemovedFromTree();
    602 
    603     detachRegion();
    604 }
    605 
    606 void RenderMultiColumnSet::attachRegion()
    607 {
    608     if (documentBeingDestroyed())
    609         return;
    610 
    611     // A region starts off invalid.
    612     setIsValid(false);
    613 
    614     if (!m_flowThread)
    615         return;
    616 
    617     // Only after adding the region to the thread, the region is marked to be valid.
    618     m_flowThread->addRegionToThread(this);
    619 }
    620 
    621 void RenderMultiColumnSet::detachRegion()
    622 {
    623     if (m_flowThread) {
    624         m_flowThread->removeRegionFromThread(this);
    625         m_flowThread = 0;
    626     }
    627 }
    628 
    629 }
    630