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 27 #ifndef RenderMultiColumnSet_h 28 #define RenderMultiColumnSet_h 29 30 #include "core/rendering/RenderMultiColumnFlowThread.h" 31 #include "core/rendering/RenderRegion.h" 32 #include "wtf/Vector.h" 33 34 namespace blink { 35 36 // RenderMultiColumnSet represents a set of columns that all have the same width and height. By 37 // combining runs of same-size columns into a single object, we significantly reduce the number of 38 // unique RenderObjects required to represent columns. 39 // 40 // Column sets are inserted as anonymous children of the actual multicol container (i.e. the 41 // renderer whose style computes to non-auto column-count and/or column-width). 42 // 43 // Being a "region", a column set has no children on its own, but is merely used to slice a portion 44 // of the tall "single-column" flow thread into actual columns visually, to convert from flow thread 45 // coordinates to visual ones. It is in charge of both positioning columns correctly relatively to 46 // the parent multicol container, and to calculate the correct translation for each column's 47 // contents, and to paint any rules between them. RenderMultiColumnSet objects are used for 48 // painting, hit testing, and any other type of operation that requires mapping from flow thread 49 // coordinates to visual coordinates. 50 // 51 // Column spans result in the creation of new column sets, since a spanning renderer has to be 52 // placed in between the column sets that come before and after the span. 53 class RenderMultiColumnSet FINAL : public RenderRegion { 54 public: 55 enum BalancedHeightCalculation { GuessFromFlowThreadPortion, StretchBySpaceShortage }; 56 57 static RenderMultiColumnSet* createAnonymous(RenderFlowThread*, RenderStyle* parentStyle); 58 59 virtual bool isRenderMultiColumnSet() const OVERRIDE { return true; } 60 61 virtual LayoutUnit pageLogicalWidth() const OVERRIDE FINAL { return flowThread()->logicalWidth(); } 62 virtual LayoutUnit pageLogicalHeight() const OVERRIDE FINAL { return m_columnHeight; } 63 64 RenderBlockFlow* multiColumnBlockFlow() const { return toRenderBlockFlow(parent()); } 65 RenderMultiColumnFlowThread* multiColumnFlowThread() const 66 { 67 ASSERT_WITH_SECURITY_IMPLICATION(!flowThread() || flowThread()->isRenderMultiColumnFlowThread()); 68 return static_cast<RenderMultiColumnFlowThread*>(flowThread()); 69 } 70 71 RenderMultiColumnSet* nextSiblingMultiColumnSet() const; 72 RenderMultiColumnSet* previousSiblingMultiColumnSet() const; 73 74 LayoutUnit logicalTopInFlowThread() const { return isHorizontalWritingMode() ? flowThreadPortionRect().y() : flowThreadPortionRect().x(); } 75 LayoutUnit logicalBottomInFlowThread() const { return isHorizontalWritingMode() ? flowThreadPortionRect().maxY() : flowThreadPortionRect().maxX(); } 76 77 LayoutUnit logicalHeightInFlowThread() const { return isHorizontalWritingMode() ? flowThreadPortionRect().height() : flowThreadPortionRect().width(); } 78 79 // The used CSS value of column-count, i.e. how many columns there are room for without overflowing. 80 unsigned usedColumnCount() const { return multiColumnFlowThread()->columnCount(); } 81 82 // Find the column that contains the given block offset, and return the translation needed to 83 // get from flow thread coordinates to visual coordinates. 84 LayoutSize flowThreadTranslationAtOffset(LayoutUnit) const; 85 86 LayoutUnit heightAdjustedForSetOffset(LayoutUnit height) const; 87 88 void updateMinimumColumnHeight(LayoutUnit height) { m_minimumColumnHeight = std::max(height, m_minimumColumnHeight); } 89 LayoutUnit minimumColumnHeight() const { return m_minimumColumnHeight; } 90 91 // Add a content run, specified by its end position. A content run is appended at every 92 // forced/explicit break and at the end of the column set. The content runs are used to 93 // determine where implicit/soft breaks will occur, in order to calculate an initial column 94 // height. 95 void addContentRun(LayoutUnit endOffsetFromFirstPage); 96 97 // (Re-)calculate the column height if it's auto. 98 bool recalculateColumnHeight(BalancedHeightCalculation); 99 100 // Record space shortage (the amount of space that would have been enough to prevent some 101 // element from being moved to the next column) at a column break. The smallest amount of space 102 // shortage we find is the amount with which we will stretch the column height, if it turns out 103 // after layout that the columns weren't tall enough. 104 void recordSpaceShortage(LayoutUnit spaceShortage); 105 106 // Reset previously calculated column height. Will mark for layout if needed. 107 void resetColumnHeight(); 108 109 // Expand this set's flow thread portion rectangle to contain all trailing flow thread 110 // overflow. Only to be called on the last set. 111 void expandToEncompassFlowThreadContentsIfNeeded(); 112 113 void attachRegion(); 114 void detachRegion(); 115 116 // This method represents the logical height of the entire flow thread portion used by the region or set. 117 // For RenderRegions it matches logicalPaginationHeight(), but for sets it is the height of all the pages 118 // or columns added together. 119 LayoutUnit logicalHeightOfAllFlowThreadContent() const { return logicalHeightInFlowThread(); } 120 121 void paintInvalidationForFlowThreadContent(const LayoutRect& paintInvalidationRect) const; 122 123 // The top of the nearest page inside the region. For RenderRegions, this is just the logical top of the 124 // flow thread portion we contain. For sets, we have to figure out the top of the nearest column or 125 // page. 126 LayoutUnit pageLogicalTopForOffset(LayoutUnit offset) const; 127 128 void collectLayerFragments(LayerFragments&, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect); 129 130 LayoutUnit columnGap() const; 131 132 // The "CSS actual" value of column-count. This includes overflowing columns, if any. 133 unsigned actualColumnCount() const; 134 135 private: 136 RenderMultiColumnSet(RenderFlowThread*); 137 138 virtual void insertedIntoTree() OVERRIDE FINAL; 139 virtual void willBeRemovedFromTree() OVERRIDE FINAL; 140 141 virtual void computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues&) const OVERRIDE; 142 143 virtual void paintObject(PaintInfo&, const LayoutPoint& paintOffset) OVERRIDE; 144 145 virtual void addOverflowFromChildren() OVERRIDE; 146 147 virtual const char* renderName() const OVERRIDE; 148 149 LayoutUnit calculateMaxColumnHeight() const; 150 LayoutRect columnRectAt(unsigned index) const; 151 152 153 LayoutRect flowThreadPortionRectAt(unsigned index) const; 154 LayoutRect flowThreadPortionOverflowRect(const LayoutRect& flowThreadPortion, unsigned index, unsigned colCount, LayoutUnit colGap) const; 155 156 enum ColumnIndexCalculationMode { 157 ClampToExistingColumns, // Stay within the range of already existing columns. 158 AssumeNewColumns // Allow column indices outside the range of already existing columns. 159 }; 160 unsigned columnIndexAtOffset(LayoutUnit, ColumnIndexCalculationMode = ClampToExistingColumns) const; 161 162 void setAndConstrainColumnHeight(LayoutUnit); 163 164 // Return the index of the content run with the currently tallest columns, taking all implicit 165 // breaks assumed so far into account. 166 unsigned findRunWithTallestColumns() const; 167 168 // Given the current list of content runs, make assumptions about where we need to insert 169 // implicit breaks (if there's room for any at all; depending on the number of explicit breaks), 170 // and store the results. This is needed in order to balance the columns. 171 void distributeImplicitBreaks(); 172 173 LayoutUnit calculateColumnHeight(BalancedHeightCalculation) const; 174 175 LayoutUnit m_columnHeight; 176 177 // The following variables are used when balancing the column set. 178 LayoutUnit m_maxColumnHeight; // Maximum column height allowed. 179 LayoutUnit m_minSpaceShortage; // The smallest amout of space shortage that caused a column break. 180 LayoutUnit m_minimumColumnHeight; 181 182 // A run of content without explicit (forced) breaks; i.e. a flow thread portion between two 183 // explicit breaks, between flow thread start and an explicit break, between an explicit break 184 // and flow thread end, or, in cases when there are no explicit breaks at all: between flow 185 // thread portion start and flow thread portion end. We need to know where the explicit breaks 186 // are, in order to figure out where the implicit breaks will end up, so that we get the columns 187 // properly balanced. A content run starts out as representing one single column, and will 188 // represent one additional column for each implicit break "inserted" there. 189 class ContentRun { 190 public: 191 ContentRun(LayoutUnit breakOffset) 192 : m_breakOffset(breakOffset) 193 , m_assumedImplicitBreaks(0) { } 194 195 unsigned assumedImplicitBreaks() const { return m_assumedImplicitBreaks; } 196 void assumeAnotherImplicitBreak() { m_assumedImplicitBreaks++; } 197 LayoutUnit breakOffset() const { return m_breakOffset; } 198 199 // Return the column height that this content run would require, considering the implicit 200 // breaks assumed so far. 201 LayoutUnit columnLogicalHeight(LayoutUnit startOffset) const { return ceilf((m_breakOffset - startOffset).toFloat() / float(m_assumedImplicitBreaks + 1)); } 202 203 private: 204 LayoutUnit m_breakOffset; // Flow thread offset where this run ends. 205 unsigned m_assumedImplicitBreaks; // Number of implicit breaks in this run assumed so far. 206 }; 207 Vector<ContentRun, 1> m_contentRuns; 208 }; 209 210 DEFINE_RENDER_OBJECT_TYPE_CASTS(RenderMultiColumnSet, isRenderMultiColumnSet()); 211 212 } // namespace blink 213 214 #endif // RenderMultiColumnSet_h 215 216