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/RenderMultiColumnBlock.h"
     28 
     29 #include "core/rendering/RenderMultiColumnFlowThread.h"
     30 #include "core/rendering/RenderMultiColumnSet.h"
     31 #include "core/rendering/RenderView.h"
     32 
     33 using namespace std;
     34 
     35 namespace WebCore {
     36 
     37 RenderMultiColumnBlock::RenderMultiColumnBlock(Element* element)
     38     : RenderBlock(element)
     39     , m_flowThread(0)
     40     , m_columnCount(1)
     41     , m_columnWidth(0)
     42     , m_columnHeightAvailable(0)
     43     , m_inBalancingPass(false)
     44 {
     45 }
     46 
     47 void RenderMultiColumnBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
     48 {
     49     RenderBlock::styleDidChange(diff, oldStyle);
     50     for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox())
     51         child->setStyle(RenderStyle::createAnonymousStyleWithDisplay(style(), BLOCK));
     52 }
     53 
     54 void RenderMultiColumnBlock::computeColumnCountAndWidth()
     55 {
     56     // Calculate our column width and column count.
     57     // FIXME: Can overflow on fast/block/float/float-not-removed-from-next-sibling4.html, see https://bugs.webkit.org/show_bug.cgi?id=68744
     58     m_columnCount = 1;
     59     m_columnWidth = contentLogicalWidth();
     60 
     61     ASSERT(!style()->hasAutoColumnCount() || !style()->hasAutoColumnWidth());
     62 
     63     LayoutUnit availWidth = m_columnWidth;
     64     LayoutUnit colGap = columnGap();
     65     LayoutUnit colWidth = max<LayoutUnit>(1, LayoutUnit(style()->columnWidth()));
     66     int colCount = max<int>(1, style()->columnCount());
     67 
     68     if (style()->hasAutoColumnWidth() && !style()->hasAutoColumnCount()) {
     69         m_columnCount = colCount;
     70         m_columnWidth = max<LayoutUnit>(0, (availWidth - ((m_columnCount - 1) * colGap)) / m_columnCount);
     71     } else if (!style()->hasAutoColumnWidth() && style()->hasAutoColumnCount()) {
     72         m_columnCount = max<LayoutUnit>(1, (availWidth + colGap) / (colWidth + colGap));
     73         m_columnWidth = ((availWidth + colGap) / m_columnCount) - colGap;
     74     } else {
     75         m_columnCount = max<LayoutUnit>(min<LayoutUnit>(colCount, (availWidth + colGap) / (colWidth + colGap)), 1);
     76         m_columnWidth = ((availWidth + colGap) / m_columnCount) - colGap;
     77     }
     78 }
     79 
     80 bool RenderMultiColumnBlock::updateLogicalWidthAndColumnWidth()
     81 {
     82     bool relayoutChildren = RenderBlock::updateLogicalWidthAndColumnWidth();
     83     LayoutUnit oldColumnWidth = m_columnWidth;
     84     computeColumnCountAndWidth();
     85     if (m_columnWidth != oldColumnWidth)
     86         relayoutChildren = true;
     87     return relayoutChildren;
     88 }
     89 
     90 void RenderMultiColumnBlock::checkForPaginationLogicalHeightChange(LayoutUnit& /*pageLogicalHeight*/, bool& /*pageLogicalHeightChanged*/, bool& /*hasSpecifiedPageLogicalHeight*/)
     91 {
     92     // We don't actually update any of the variables. We just subclassed to adjust our column height.
     93     updateLogicalHeight();
     94     m_columnHeightAvailable = max<LayoutUnit>(contentLogicalHeight(), 0);
     95     setLogicalHeight(0);
     96 }
     97 
     98 bool RenderMultiColumnBlock::relayoutForPagination(bool, LayoutUnit, LayoutStateMaintainer& statePusher)
     99 {
    100     if (m_inBalancingPass || !requiresBalancing())
    101         return false;
    102     m_inBalancingPass = true; // Prevent re-entering this method (and recursion into layout).
    103 
    104     bool needsRelayout;
    105     bool neededRelayout = false;
    106     bool firstPass = true;
    107     do {
    108         // Column heights may change here because of balancing. We may have to do multiple layout
    109         // passes, depending on how the contents is fitted to the changed column heights. In most
    110         // cases, laying out again twice or even just once will suffice. Sometimes we need more
    111         // passes than that, though, but the number of retries should not exceed the number of
    112         // columns, unless we have a bug.
    113         needsRelayout = false;
    114         for (RenderBox* childBox = firstChildBox(); childBox; childBox = childBox->nextSiblingBox()) {
    115             if (childBox != m_flowThread && childBox->isRenderMultiColumnSet()) {
    116                 RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(childBox);
    117                 if (multicolSet->calculateBalancedHeight(firstPass)) {
    118                     multicolSet->setChildNeedsLayout(MarkOnlyThis);
    119                     needsRelayout = true;
    120                 }
    121             }
    122         }
    123 
    124         if (needsRelayout) {
    125             // Layout again. Column balancing resulted in a new height.
    126             neededRelayout = true;
    127             m_flowThread->setChildNeedsLayout(MarkOnlyThis);
    128             setChildNeedsLayout(MarkOnlyThis);
    129             if (firstPass)
    130                 statePusher.pop();
    131             layoutBlock(false);
    132         }
    133         firstPass = false;
    134     } while (needsRelayout);
    135     m_inBalancingPass = false;
    136     return neededRelayout;
    137 }
    138 
    139 void RenderMultiColumnBlock::addChild(RenderObject* newChild, RenderObject* beforeChild)
    140 {
    141     if (!m_flowThread) {
    142         m_flowThread = RenderMultiColumnFlowThread::createAnonymous(document());
    143         m_flowThread->setStyle(RenderStyle::createAnonymousStyleWithDisplay(style(), BLOCK));
    144         RenderBlock::addChild(m_flowThread);
    145     }
    146     m_flowThread->addChild(newChild, beforeChild);
    147 }
    148 
    149 RenderObject* RenderMultiColumnBlock::layoutSpecialExcludedChild(bool relayoutChildren)
    150 {
    151     if (!m_flowThread)
    152         return 0;
    153 
    154     // Update the dimensions of our regions before we lay out the flow thread.
    155     // FIXME: Eventually this is going to get way more complicated, and we will be destroying regions
    156     // instead of trying to keep them around.
    157     bool shouldInvalidateRegions = false;
    158     for (RenderBox* childBox = firstChildBox(); childBox; childBox = childBox->nextSiblingBox()) {
    159         if (childBox == m_flowThread)
    160             continue;
    161 
    162         if (relayoutChildren || childBox->needsLayout()) {
    163             if (!m_inBalancingPass && childBox->isRenderMultiColumnSet())
    164                 toRenderMultiColumnSet(childBox)->prepareForLayout();
    165             shouldInvalidateRegions = true;
    166         }
    167     }
    168 
    169     if (shouldInvalidateRegions)
    170         m_flowThread->invalidateRegions();
    171 
    172     if (relayoutChildren)
    173         m_flowThread->setChildNeedsLayout(MarkOnlyThis);
    174 
    175     setLogicalTopForChild(m_flowThread, borderBefore() + paddingBefore());
    176     m_flowThread->layoutIfNeeded();
    177     determineLogicalLeftPositionForChild(m_flowThread);
    178 
    179     return m_flowThread;
    180 }
    181 
    182 const char* RenderMultiColumnBlock::renderName() const
    183 {
    184     if (isFloating())
    185         return "RenderMultiColumnBlock (floating)";
    186     if (isOutOfFlowPositioned())
    187         return "RenderMultiColumnBlock (positioned)";
    188     if (isAnonymousBlock())
    189         return "RenderMultiColumnBlock (anonymous)";
    190     // FIXME: Temporary hack while the new generated content system is being implemented.
    191     if (isPseudoElement())
    192         return "RenderMultiColumnBlock (generated)";
    193     if (isAnonymous())
    194         return "RenderMultiColumnBlock (generated)";
    195     if (isRelPositioned())
    196         return "RenderMultiColumnBlock (relative positioned)";
    197     return "RenderMultiColumnBlock";
    198 }
    199 
    200 }
    201