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 |
(...skipping 25 matching lines...) Expand all Loading... |
36 namespace WebCore { | 36 namespace WebCore { |
37 | 37 |
38 RenderMultiColumnSet::RenderMultiColumnSet(RenderFlowThread* flowThread) | 38 RenderMultiColumnSet::RenderMultiColumnSet(RenderFlowThread* flowThread) |
39 : RenderRegionSet(0, flowThread) | 39 : RenderRegionSet(0, flowThread) |
40 , m_computedColumnCount(1) | 40 , m_computedColumnCount(1) |
41 , m_computedColumnWidth(0) | 41 , m_computedColumnWidth(0) |
42 , m_computedColumnHeight(0) | 42 , m_computedColumnHeight(0) |
43 , m_maxColumnHeight(LayoutUnit::max()) | 43 , m_maxColumnHeight(LayoutUnit::max()) |
44 , m_minSpaceShortage(LayoutUnit::max()) | 44 , m_minSpaceShortage(LayoutUnit::max()) |
45 , m_minimumColumnHeight(0) | 45 , m_minimumColumnHeight(0) |
46 , m_forcedBreaksCount(0) | |
47 , m_maximumDistanceBetweenForcedBreaks(0) | |
48 , m_forcedBreakOffset(0) | |
49 { | 46 { |
50 } | 47 } |
51 | 48 |
52 RenderMultiColumnSet* RenderMultiColumnSet::createAnonymous(RenderFlowThread* fl
owThread) | 49 RenderMultiColumnSet* RenderMultiColumnSet::createAnonymous(RenderFlowThread* fl
owThread) |
53 { | 50 { |
54 Document& document = flowThread->document(); | 51 Document& document = flowThread->document(); |
55 RenderMultiColumnSet* renderer = new RenderMultiColumnSet(flowThread); | 52 RenderMultiColumnSet* renderer = new RenderMultiColumnSet(flowThread); |
56 renderer->setDocumentForAnonymous(&document); | 53 renderer->setDocumentForAnonymous(&document); |
57 return renderer; | 54 return renderer; |
58 } | 55 } |
(...skipping 15 matching lines...) Expand all Loading... |
74 } | 71 } |
75 | 72 |
76 void RenderMultiColumnSet::setAndConstrainColumnHeight(LayoutUnit newHeight) | 73 void RenderMultiColumnSet::setAndConstrainColumnHeight(LayoutUnit newHeight) |
77 { | 74 { |
78 m_computedColumnHeight = newHeight; | 75 m_computedColumnHeight = newHeight; |
79 if (m_computedColumnHeight > m_maxColumnHeight) | 76 if (m_computedColumnHeight > m_maxColumnHeight) |
80 m_computedColumnHeight = m_maxColumnHeight; | 77 m_computedColumnHeight = m_maxColumnHeight; |
81 // FIXME: the height may also be affected by the enclosing pagination contex
t, if any. | 78 // FIXME: the height may also be affected by the enclosing pagination contex
t, if any. |
82 } | 79 } |
83 | 80 |
84 bool RenderMultiColumnSet::calculateBalancedHeight(bool initial) | 81 unsigned RenderMultiColumnSet::findRunWithTallestColumns() const |
85 { | 82 { |
86 ASSERT(toRenderMultiColumnBlock(parent())->requiresBalancing()); | 83 unsigned indexWithLargestHeight = 0; |
87 LayoutUnit oldColumnHeight = m_computedColumnHeight; | 84 LayoutUnit largestHeight; |
88 LayoutUnit currentMinSpaceShortage = m_minSpaceShortage; | 85 LayoutUnit previousOffset; |
89 m_minSpaceShortage = LayoutUnit::max(); | 86 size_t runCount = m_contentRuns.size(); |
| 87 ASSERT(runCount); |
| 88 for (size_t i = 0; i < runCount; i++) { |
| 89 const ContentRun& run = m_contentRuns[i]; |
| 90 LayoutUnit height = run.columnLogicalHeight(previousOffset); |
| 91 if (largestHeight < height) { |
| 92 largestHeight = height; |
| 93 indexWithLargestHeight = i; |
| 94 } |
| 95 previousOffset = run.breakOffset(); |
| 96 } |
| 97 return indexWithLargestHeight; |
| 98 } |
90 | 99 |
| 100 void RenderMultiColumnSet::distributeImplicitBreaks() |
| 101 { |
| 102 unsigned breakCount = forcedBreaksCount(); |
| 103 |
| 104 #ifndef NDEBUG |
| 105 // There should be no implicit breaks assumed at this point. |
| 106 for (unsigned i = 0; i < breakCount; i++) |
| 107 ASSERT(!m_contentRuns[i].assumedImplicitBreaks()); |
| 108 #endif // NDEBUG |
| 109 |
| 110 // There will always be at least one break, since the flow thread reports a
"forced break" at |
| 111 // end of content. |
| 112 ASSERT(breakCount >= 1); |
| 113 |
| 114 // If there is room for more breaks (to reach the used value of column-count
), imagine that we |
| 115 // insert implicit breaks at suitable locations. At any given time, the cont
ent run with the |
| 116 // currently tallest columns will get another implicit break "inserted", whi
ch will increase its |
| 117 // column count by one and shrink its columns' height. Repeat until we have
the desired total |
| 118 // number of breaks. The largest column height among the runs will then be t
he initial column |
| 119 // height for the balancer to use. |
| 120 while (breakCount < m_computedColumnCount) { |
| 121 unsigned index = findRunWithTallestColumns(); |
| 122 m_contentRuns[index].assumeAnotherImplicitBreak(); |
| 123 breakCount++; |
| 124 } |
| 125 } |
| 126 |
| 127 LayoutUnit RenderMultiColumnSet::calculateBalancedHeight(bool initial) const |
| 128 { |
91 if (initial) { | 129 if (initial) { |
92 // Start with the lowest imaginable column height. | 130 // Start with the lowest imaginable column height. |
93 LayoutUnit logicalHeightGuess = ceilf(float(flowThread()->logicalHeight(
)) / float(m_computedColumnCount)); | 131 unsigned index = findRunWithTallestColumns(); |
94 logicalHeightGuess = max(logicalHeightGuess, m_minimumColumnHeight); | 132 LayoutUnit startOffset = index > 0 ? m_contentRuns[index - 1].breakOffse
t() : LayoutUnit(); |
95 setAndConstrainColumnHeight(logicalHeightGuess); | 133 return std::max<LayoutUnit>(m_contentRuns[index].columnLogicalHeight(sta
rtOffset), m_minimumColumnHeight); |
96 | |
97 // The multicol container now typically needs at least one more layout p
ass with a new | |
98 // column height, but if height was specified, we only need to do this i
f we found that we | |
99 // might need less space than that. On the other hand, if we determined
that the columns | |
100 // need to be as tall as the specified height of the container, we have
already laid it out | |
101 // correctly, and there's no need for another pass. | |
102 return m_computedColumnHeight != oldColumnHeight; | |
103 } | 134 } |
104 | 135 |
105 if (columnCount() <= computedColumnCount()) { | 136 if (columnCount() <= computedColumnCount()) { |
106 // With the current column height, the content fits without creating ove
rflowing columns. We're done. | 137 // With the current column height, the content fits without creating ove
rflowing columns. We're done. |
107 return false; | 138 return m_computedColumnHeight; |
| 139 } |
| 140 |
| 141 if (forcedBreaksCount() > 1 && forcedBreaksCount() >= computedColumnCount())
{ |
| 142 // Too many forced breaks to allow any implicit breaks. Initial balancin
g should already |
| 143 // have set a good height. There's nothing more we should do. |
| 144 return m_computedColumnHeight; |
108 } | 145 } |
109 | 146 |
110 // If the initial guessed column height wasn't enough, stretch it now. Stret
ch by the lowest | 147 // If the initial guessed column height wasn't enough, stretch it now. Stret
ch by the lowest |
111 // amount of space shortage found during layout. | 148 // amount of space shortage found during layout. |
112 | 149 |
113 ASSERT(currentMinSpaceShortage != LayoutUnit::max()); // If this can actuall
y happen, we probably have a bug. | 150 ASSERT(m_minSpaceShortage > 0); // We should never _shrink_ the height! |
114 if (currentMinSpaceShortage == LayoutUnit::max()) | 151 ASSERT(m_minSpaceShortage != LayoutUnit::max()); // If this happens, we prob
ably have a bug. |
115 return false; // So bail out rather than looping infinitely. | 152 if (m_minSpaceShortage == LayoutUnit::max()) |
| 153 return m_computedColumnHeight; // So bail out rather than looping infini
tely. |
116 | 154 |
117 setAndConstrainColumnHeight(m_computedColumnHeight + currentMinSpaceShortage
); | 155 return m_computedColumnHeight + m_minSpaceShortage; |
| 156 } |
118 | 157 |
119 // If we reach the maximum column height (typically set by the height or max
-height property), | 158 void RenderMultiColumnSet::clearForcedBreaks() |
120 // we may not be allowed to stretch further. Return true only if stretching | 159 { |
121 // succeeded. Otherwise, we're done. | 160 m_contentRuns.clear(); |
122 ASSERT(m_computedColumnHeight >= oldColumnHeight); // We shouldn't be able t
o shrink the height! | 161 } |
123 return m_computedColumnHeight > oldColumnHeight; | 162 |
| 163 void RenderMultiColumnSet::addForcedBreak(LayoutUnit offsetFromFirstPage) |
| 164 { |
| 165 if (!toRenderMultiColumnBlock(parent())->requiresBalancing()) |
| 166 return; |
| 167 if (!m_contentRuns.isEmpty() && offsetFromFirstPage <= m_contentRuns.last().
breakOffset()) |
| 168 return; |
| 169 // Append another item as long as we haven't exceeded used column count. Wha
t ends up in the |
| 170 // overflow area shouldn't affect column balancing. |
| 171 if (m_contentRuns.size() < m_computedColumnCount) |
| 172 m_contentRuns.append(ContentRun(offsetFromFirstPage)); |
| 173 } |
| 174 |
| 175 bool RenderMultiColumnSet::recalculateBalancedHeight(bool initial) |
| 176 { |
| 177 ASSERT(toRenderMultiColumnBlock(parent())->requiresBalancing()); |
| 178 |
| 179 LayoutUnit oldColumnHeight = m_computedColumnHeight; |
| 180 if (initial) |
| 181 distributeImplicitBreaks(); |
| 182 LayoutUnit newColumnHeight = calculateBalancedHeight(initial); |
| 183 setAndConstrainColumnHeight(newColumnHeight); |
| 184 |
| 185 // After having calculated an initial column height, the multicol container
typically needs at |
| 186 // least one more layout pass with a new column height, but if a height was
specified, we only |
| 187 // need to do this if we think that we need less space than specified. Conve
rsely, if we |
| 188 // determined that the columns need to be as tall as the specified height of
the container, we |
| 189 // have already laid it out correctly, and there's no need for another pass. |
| 190 |
| 191 if (m_computedColumnHeight == oldColumnHeight) |
| 192 return false; // No change. We're done. |
| 193 |
| 194 m_minSpaceShortage = LayoutUnit::max(); |
| 195 clearForcedBreaks(); |
| 196 return true; // Need another pass. |
124 } | 197 } |
125 | 198 |
126 void RenderMultiColumnSet::recordSpaceShortage(LayoutUnit spaceShortage) | 199 void RenderMultiColumnSet::recordSpaceShortage(LayoutUnit spaceShortage) |
127 { | 200 { |
128 if (spaceShortage >= m_minSpaceShortage) | 201 if (spaceShortage >= m_minSpaceShortage) |
129 return; | 202 return; |
130 | 203 |
131 // The space shortage is what we use as our stretch amount. We need a positi
ve number here in | 204 // The space shortage is what we use as our stretch amount. We need a positi
ve number here in |
132 // order to get anywhere. | 205 // order to get anywhere. |
133 ASSERT(spaceShortage > 0); | 206 ASSERT(spaceShortage > 0); |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
178 LayoutUnit logicalMaxHeight = multicolBlock->computeContentLogicalHe
ight(multicolStyle->logicalMaxHeight(), -1); | 251 LayoutUnit logicalMaxHeight = multicolBlock->computeContentLogicalHe
ight(multicolStyle->logicalMaxHeight(), -1); |
179 if (m_maxColumnHeight > logicalMaxHeight) | 252 if (m_maxColumnHeight > logicalMaxHeight) |
180 m_maxColumnHeight = logicalMaxHeight; | 253 m_maxColumnHeight = logicalMaxHeight; |
181 } | 254 } |
182 m_maxColumnHeight = heightAdjustedForSetOffset(m_maxColumnHeight); | 255 m_maxColumnHeight = heightAdjustedForSetOffset(m_maxColumnHeight); |
183 m_computedColumnHeight = 0; // Restart balancing. | 256 m_computedColumnHeight = 0; // Restart balancing. |
184 } else { | 257 } else { |
185 setAndConstrainColumnHeight(heightAdjustedForSetOffset(multicolBlock->co
lumnHeightAvailable())); | 258 setAndConstrainColumnHeight(heightAdjustedForSetOffset(multicolBlock->co
lumnHeightAvailable())); |
186 } | 259 } |
187 | 260 |
| 261 clearForcedBreaks(); |
| 262 |
188 // Nuke previously stored minimum column height. Contents may have changed f
or all we know. | 263 // Nuke previously stored minimum column height. Contents may have changed f
or all we know. |
189 m_minimumColumnHeight = 0; | 264 m_minimumColumnHeight = 0; |
190 } | 265 } |
191 | 266 |
192 void RenderMultiColumnSet::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTo
p, LogicalExtentComputedValues& computedValues) const | 267 void RenderMultiColumnSet::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTo
p, LogicalExtentComputedValues& computedValues) const |
193 { | 268 { |
194 computedValues.m_extent = m_computedColumnHeight; | 269 computedValues.m_extent = m_computedColumnHeight; |
195 computedValues.m_position = logicalTop; | 270 computedValues.m_position = logicalTop; |
196 } | 271 } |
197 | 272 |
(...skipping 308 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
506 fragments.append(fragment); | 581 fragments.append(fragment); |
507 } | 582 } |
508 } | 583 } |
509 | 584 |
510 const char* RenderMultiColumnSet::renderName() const | 585 const char* RenderMultiColumnSet::renderName() const |
511 { | 586 { |
512 return "RenderMultiColumnSet"; | 587 return "RenderMultiColumnSet"; |
513 } | 588 } |
514 | 589 |
515 } | 590 } |
OLD | NEW |