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 : 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 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, 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 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