• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
RenderMultiColumnBlock(Element * element)37 RenderMultiColumnBlock::RenderMultiColumnBlock(Element* element)
38     : RenderBlockFlow(element)
39     , m_flowThread(0)
40     , m_columnCount(1)
41     , m_columnWidth(0)
42     , m_columnHeightAvailable(0)
43     , m_inBalancingPass(false)
44 {
45 }
46 
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)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 
computeColumnCountAndWidth()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 
updateLogicalWidthAndColumnWidth()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 
checkForPaginationLogicalHeightChange(LayoutUnit &,bool &,bool &)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 
relayoutForPagination(bool,LayoutUnit,LayoutStateMaintainer & statePusher)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 
addChild(RenderObject * newChild,RenderObject * beforeChild)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 
layoutSpecialExcludedChild(bool relayoutChildren,SubtreeLayoutScope & layoutScope)149 RenderObject* RenderMultiColumnBlock::layoutSpecialExcludedChild(bool relayoutChildren, SubtreeLayoutScope& layoutScope)
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         layoutScope.setChildNeedsLayout(m_flowThread);
174 
175     setLogicalTopForChild(m_flowThread, borderBefore() + paddingBefore());
176     m_flowThread->layoutIfNeeded();
177     determineLogicalLeftPositionForChild(m_flowThread);
178 
179     return m_flowThread;
180 }
181 
renderName() const182 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