OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2012 Apple Inc. All rights reserved. | 2 * Copyright (C) 2012 Apple Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
11 * documentation and/or other materials provided with the distribution. | 11 * documentation and/or other materials provided with the distribution. |
12 * | 12 * |
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR | 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR |
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
20 * PROFITS; OR BUSINESS IN..0TERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | 20 * PROFITS; OR BUSINESS IN..0TERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
24 */ | 24 */ |
25 | 25 |
26 #include "config.h" | 26 #include "config.h" |
27 #include "core/rendering/RenderMultiColumnFlowThread.h" | 27 #include "core/rendering/RenderMultiColumnFlowThread.h" |
28 | 28 |
29 #include "core/rendering/RenderMultiColumnBlock.h" | |
30 #include "core/rendering/RenderMultiColumnSet.h" | 29 #include "core/rendering/RenderMultiColumnSet.h" |
31 | 30 |
32 namespace WebCore { | 31 namespace WebCore { |
33 | 32 |
34 RenderMultiColumnFlowThread::RenderMultiColumnFlowThread() | 33 RenderMultiColumnFlowThread::RenderMultiColumnFlowThread() |
| 34 : m_columnCount(1) |
| 35 , m_columnWidth(0) |
| 36 , m_columnHeightAvailable(0) |
| 37 , m_inBalancingPass(false) |
| 38 , m_needsRebalancing(false) |
35 { | 39 { |
36 setFlowThreadState(InsideInFlowThread); | 40 setFlowThreadState(InsideInFlowThread); |
37 } | 41 } |
38 | 42 |
39 RenderMultiColumnFlowThread::~RenderMultiColumnFlowThread() | 43 RenderMultiColumnFlowThread::~RenderMultiColumnFlowThread() |
40 { | 44 { |
41 } | 45 } |
42 | 46 |
43 RenderMultiColumnFlowThread* RenderMultiColumnFlowThread::createAnonymous(Docume
nt* document) | 47 RenderMultiColumnFlowThread* RenderMultiColumnFlowThread::createAnonymous(Docume
nt& document, RenderStyle* parentStyle) |
44 { | 48 { |
45 RenderMultiColumnFlowThread* renderer = new RenderMultiColumnFlowThread(); | 49 RenderMultiColumnFlowThread* renderer = new RenderMultiColumnFlowThread(); |
46 renderer->setDocumentForAnonymous(document); | 50 renderer->setDocumentForAnonymous(&document); |
| 51 renderer->setStyle(RenderStyle::createAnonymousStyleWithDisplay(parentStyle,
BLOCK)); |
47 return renderer; | 52 return renderer; |
48 } | 53 } |
49 | 54 |
| 55 void RenderMultiColumnFlowThread::layoutColumns(bool relayoutChildren, SubtreeLa
youtScope& layoutScope) |
| 56 { |
| 57 // Update the dimensions of our regions before we lay out the flow thread. |
| 58 // FIXME: Eventually this is going to get way more complicated, and we will
be destroying regions |
| 59 // instead of trying to keep them around. |
| 60 RenderBlockFlow* container = multiColumnBlockFlow(); |
| 61 bool shouldInvalidateRegions = false; |
| 62 for (RenderBox* childBox = container->firstChildBox(); childBox; childBox =
childBox->nextSiblingBox()) { |
| 63 if (childBox == this) |
| 64 continue; |
| 65 |
| 66 if (relayoutChildren || childBox->needsLayout()) { |
| 67 if (!m_inBalancingPass && childBox->isRenderMultiColumnSet()) |
| 68 toRenderMultiColumnSet(childBox)->prepareForLayout(); |
| 69 shouldInvalidateRegions = true; |
| 70 } |
| 71 } |
| 72 |
| 73 if (shouldInvalidateRegions) |
| 74 invalidateRegions(); |
| 75 |
| 76 if (relayoutChildren) |
| 77 layoutScope.setChildNeedsLayout(this); |
| 78 |
| 79 if (requiresBalancing()) { |
| 80 // At the end of multicol layout, relayoutForPagination() is called unco
nditionally, but if |
| 81 // no children are to be laid out (e.g. fixed width with layout already
being up-to-date), |
| 82 // we want to prevent it from doing any work, so that the column balanci
ng machinery doesn't |
| 83 // kick in and trigger additional unnecessary layout passes. Actually, i
t's not just a good |
| 84 // idea in general to not waste time on balancing content that hasn't be
en re-laid out; we |
| 85 // are actually required to guarantee this. The calculation of implicit
breaks needs to be |
| 86 // preceded by a proper layout pass, since it's layout that sets up cont
ent runs, and the |
| 87 // runs get deleted right after every pass. |
| 88 m_needsRebalancing = shouldInvalidateRegions || needsLayout(); |
| 89 } |
| 90 |
| 91 layoutIfNeeded(); |
| 92 } |
| 93 |
| 94 bool RenderMultiColumnFlowThread::computeColumnCountAndWidth() |
| 95 { |
| 96 RenderBlock* columnBlock = multiColumnBlockFlow(); |
| 97 LayoutUnit oldColumnWidth = m_columnWidth; |
| 98 |
| 99 // Calculate our column width and column count. |
| 100 m_columnCount = 1; |
| 101 m_columnWidth = columnBlock->contentLogicalWidth(); |
| 102 |
| 103 const RenderStyle* columnStyle = columnBlock->style(); |
| 104 ASSERT(!columnStyle->hasAutoColumnCount() || !columnStyle->hasAutoColumnWidt
h()); |
| 105 |
| 106 LayoutUnit availWidth = m_columnWidth; |
| 107 LayoutUnit colGap = columnBlock->columnGap(); |
| 108 LayoutUnit colWidth = max<LayoutUnit>(1, LayoutUnit(columnStyle->columnWidth
())); |
| 109 int colCount = max<int>(1, columnStyle->columnCount()); |
| 110 |
| 111 if (columnStyle->hasAutoColumnWidth() && !columnStyle->hasAutoColumnCount())
{ |
| 112 m_columnCount = colCount; |
| 113 m_columnWidth = std::max<LayoutUnit>(0, (availWidth - ((m_columnCount -
1) * colGap)) / m_columnCount); |
| 114 } else if (!columnStyle->hasAutoColumnWidth() && columnStyle->hasAutoColumnC
ount()) { |
| 115 m_columnCount = std::max<LayoutUnit>(1, (availWidth + colGap) / (colWidt
h + colGap)); |
| 116 m_columnWidth = ((availWidth + colGap) / m_columnCount) - colGap; |
| 117 } else { |
| 118 m_columnCount = std::max<LayoutUnit>(std::min<LayoutUnit>(colCount, (ava
ilWidth + colGap) / (colWidth + colGap)), 1); |
| 119 m_columnWidth = ((availWidth + colGap) / m_columnCount) - colGap; |
| 120 } |
| 121 |
| 122 return m_columnWidth != oldColumnWidth; |
| 123 } |
| 124 |
| 125 bool RenderMultiColumnFlowThread::recalculateColumnHeights() |
| 126 { |
| 127 if (!m_needsRebalancing) |
| 128 return false; |
| 129 |
| 130 // Column heights may change here because of balancing. We may have to do mu
ltiple layout |
| 131 // passes, depending on how the contents is fitted to the changed column hei
ghts. In most |
| 132 // cases, laying out again twice or even just once will suffice. Sometimes w
e need more |
| 133 // passes than that, though, but the number of retries should not exceed the
number of |
| 134 // columns, unless we have a bug. |
| 135 bool needsRelayout = false; |
| 136 for (RenderBox* childBox = multiColumnBlockFlow()->firstChildBox(); childBox
; childBox = childBox->nextSiblingBox()) { |
| 137 if (childBox != this && childBox->isRenderMultiColumnSet()) { |
| 138 RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(childBox)
; |
| 139 if (multicolSet->recalculateBalancedHeight(!m_inBalancingPass)) { |
| 140 multicolSet->setChildNeedsLayout(MarkOnlyThis); |
| 141 needsRelayout = true; |
| 142 } |
| 143 } |
| 144 } |
| 145 |
| 146 if (needsRelayout) |
| 147 setChildNeedsLayout(MarkOnlyThis); |
| 148 |
| 149 m_inBalancingPass = needsRelayout; |
| 150 return needsRelayout; |
| 151 } |
| 152 |
50 const char* RenderMultiColumnFlowThread::renderName() const | 153 const char* RenderMultiColumnFlowThread::renderName() const |
51 { | 154 { |
52 return "RenderMultiColumnFlowThread"; | 155 return "RenderMultiColumnFlowThread"; |
53 } | 156 } |
54 | 157 |
55 void RenderMultiColumnFlowThread::computeLogicalHeight(LayoutUnit logicalHeight,
LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const | 158 void RenderMultiColumnFlowThread::computeLogicalHeight(LayoutUnit logicalHeight,
LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const |
56 { | 159 { |
57 // We simply remain at our intrinsic height. | 160 // We simply remain at our intrinsic height. |
58 computedValues.m_extent = logicalHeight; | 161 computedValues.m_extent = logicalHeight; |
59 computedValues.m_position = logicalTop; | 162 computedValues.m_position = logicalTop; |
60 } | 163 } |
61 | 164 |
62 LayoutUnit RenderMultiColumnFlowThread::initialLogicalWidth() const | 165 LayoutUnit RenderMultiColumnFlowThread::initialLogicalWidth() const |
63 { | 166 { |
64 RenderMultiColumnBlock* parentBlock = toRenderMultiColumnBlock(parent()); | 167 return columnWidth(); |
65 return parentBlock->columnWidth(); | |
66 } | 168 } |
67 | 169 |
68 void RenderMultiColumnFlowThread::autoGenerateRegionsToBlockOffset(LayoutUnit /*
offset*/) | 170 void RenderMultiColumnFlowThread::autoGenerateRegionsToBlockOffset(LayoutUnit /*
offset*/) |
69 { | 171 { |
70 // This function ensures we have the correct column set information at all t
imes. | 172 // This function ensures we have the correct column set information at all t
imes. |
71 // For a simple multi-column layout in continuous media, only one column set
child is required. | 173 // For a simple multi-column layout in continuous media, only one column set
child is required. |
72 // Once a column is nested inside an enclosing pagination context, the numbe
r of column sets | 174 // Once a column is nested inside an enclosing pagination context, the numbe
r of column sets |
73 // required becomes 2n-1, where n is the total number of nested pagination c
ontexts. For example: | 175 // required becomes 2n-1, where n is the total number of nested pagination c
ontexts. For example: |
74 // | 176 // |
75 // Column layout with no enclosing pagination model = 2 * 1 - 1 = 1 column s
et. | 177 // Column layout with no enclosing pagination model = 2 * 1 - 1 = 1 column s
et. |
76 // Columns inside pages = 2 * 2 - 1 = 3 column sets (bottom of first page, a
ll the subsequent pages, then the last page). | 178 // Columns inside pages = 2 * 2 - 1 = 3 column sets (bottom of first page, a
ll the subsequent pages, then the last page). |
77 // Columns inside columns inside pages = 2 * 3 - 1 = 5 column sets. | 179 // Columns inside columns inside pages = 2 * 3 - 1 = 5 column sets. |
78 // | 180 // |
79 // In addition, column spans will force a column set to "split" into before/
after sets around the spanning element. | 181 // In addition, column spans will force a column set to "split" into before/
after sets around the spanning element. |
80 // | 182 // |
81 // Finally, we will need to deal with columns inside regions. If regions hav
e variable widths, then there will need | 183 // Finally, we will need to deal with columns inside regions. If regions hav
e variable widths, then there will need |
82 // to be unique column sets created inside any region whose width is differe
nt from its surrounding regions. This is | 184 // to be unique column sets created inside any region whose width is differe
nt from its surrounding regions. This is |
83 // actually pretty similar to the spanning case, in that we break up the col
umn sets whenever the width varies. | 185 // actually pretty similar to the spanning case, in that we break up the col
umn sets whenever the width varies. |
84 // | 186 // |
85 // FIXME: For now just make one column set. This matches the old multi-colum
n code. | 187 // FIXME: For now just make one column set. This matches the old multi-colum
n code. |
86 // Right now our goal is just feature parity with the old multi-column code
so that we can switch over to the | 188 // Right now our goal is just feature parity with the old multi-column code
so that we can switch over to the |
87 // new code as soon as possible. | 189 // new code as soon as possible. |
88 RenderMultiColumnSet* firstSet = toRenderMultiColumnSet(firstRegion()); | 190 RenderMultiColumnSet* firstSet = toRenderMultiColumnSet(firstRegion()); |
89 if (firstSet) | 191 if (firstSet) |
90 return; | 192 return; |
91 | 193 |
92 invalidateRegions(); | 194 invalidateRegions(); |
93 | 195 |
94 RenderMultiColumnBlock* parentBlock = toRenderMultiColumnBlock(parent()); | 196 RenderBlockFlow* parentBlock = multiColumnBlockFlow(); |
95 firstSet = RenderMultiColumnSet::createAnonymous(this); | 197 firstSet = RenderMultiColumnSet::createAnonymous(this); |
96 firstSet->setStyle(RenderStyle::createAnonymousStyleWithDisplay(parentBlock-
>style(), BLOCK)); | 198 firstSet->setStyle(RenderStyle::createAnonymousStyleWithDisplay(parentBlock-
>style(), BLOCK)); |
97 parentBlock->RenderBlock::addChild(firstSet); | 199 parentBlock->RenderBlock::addChild(firstSet); |
98 | 200 |
99 // Even though we aren't placed yet, we can go ahead and set up our size. At
this point we're | 201 // Even though we aren't placed yet, we can go ahead and set up our size. At
this point we're |
100 // typically in the middle of laying out the thread, attempting to paginate,
and we need to do | 202 // typically in the middle of laying out the thread, attempting to paginate,
and we need to do |
101 // some rudimentary "layout" of the set now, so that pagination will work. | 203 // some rudimentary "layout" of the set now, so that pagination will work. |
102 firstSet->prepareForLayout(); | 204 firstSet->prepareForLayout(); |
103 | 205 |
104 validateRegions(); | 206 validateRegions(); |
(...skipping 16 matching lines...) Expand all Loading... |
121 if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlock
Offset(offset))) { | 223 if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlock
Offset(offset))) { |
122 multicolSet->addForcedBreak(offset); | 224 multicolSet->addForcedBreak(offset); |
123 if (offsetBreakAdjustment) | 225 if (offsetBreakAdjustment) |
124 *offsetBreakAdjustment = pageLogicalHeightForOffset(offset) ? pageRe
mainingLogicalHeightForOffset(offset, IncludePageBoundary) : LayoutUnit(); | 226 *offsetBreakAdjustment = pageLogicalHeightForOffset(offset) ? pageRe
mainingLogicalHeightForOffset(offset, IncludePageBoundary) : LayoutUnit(); |
125 return true; | 227 return true; |
126 } | 228 } |
127 return false; | 229 return false; |
128 } | 230 } |
129 | 231 |
130 } | 232 } |
OLD | NEW |