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 22 matching lines...) Expand all Loading... |
33 | 33 |
34 using namespace std; | 34 using namespace std; |
35 | 35 |
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_requiresBalancing(false) | 43 , m_maxColumnHeight(LayoutUnit::max()) |
| 44 , m_minSpaceShortage(LayoutUnit::max()) |
44 , m_minimumColumnHeight(0) | 45 , m_minimumColumnHeight(0) |
45 , m_forcedBreaksCount(0) | 46 , m_forcedBreaksCount(0) |
46 , m_maximumDistanceBetweenForcedBreaks(0) | 47 , m_maximumDistanceBetweenForcedBreaks(0) |
47 , m_forcedBreakOffset(0) | 48 , m_forcedBreakOffset(0) |
48 { | 49 { |
49 } | 50 } |
50 | 51 |
51 RenderMultiColumnSet* RenderMultiColumnSet::createAnonymous(RenderFlowThread* fl
owThread) | 52 RenderMultiColumnSet* RenderMultiColumnSet::createAnonymous(RenderFlowThread* fl
owThread) |
52 { | 53 { |
53 Document* document = flowThread->document(); | 54 Document* document = flowThread->document(); |
54 RenderMultiColumnSet* renderer = new (document->renderArena()) RenderMultiCo
lumnSet(flowThread); | 55 RenderMultiColumnSet* renderer = new (document->renderArena()) RenderMultiCo
lumnSet(flowThread); |
55 renderer->setDocumentForAnonymous(document); | 56 renderer->setDocumentForAnonymous(document); |
56 return renderer; | 57 return renderer; |
57 } | 58 } |
58 | 59 |
| 60 LayoutUnit RenderMultiColumnSet::heightAdjustedForSetOffset(LayoutUnit height) c
onst |
| 61 { |
| 62 RenderMultiColumnBlock* multicolBlock = toRenderMultiColumnBlock(parent()); |
| 63 LayoutUnit contentLogicalTop = logicalTop() - multicolBlock->borderBefore()
- multicolBlock->paddingBefore(); |
| 64 |
| 65 height -= contentLogicalTop; |
| 66 return max(height, LayoutUnit(1)); // Let's avoid zero height, as that would
probably cause an infinite amount of columns to be created. |
| 67 } |
| 68 |
59 LayoutUnit RenderMultiColumnSet::pageLogicalTopForOffset(LayoutUnit offset) cons
t | 69 LayoutUnit RenderMultiColumnSet::pageLogicalTopForOffset(LayoutUnit offset) cons
t |
60 { | 70 { |
61 LayoutUnit portionLogicalTop = (isHorizontalWritingMode() ? flowThreadPortio
nRect().y() : flowThreadPortionRect().x()); | 71 LayoutUnit portionLogicalTop = (isHorizontalWritingMode() ? flowThreadPortio
nRect().y() : flowThreadPortionRect().x()); |
62 unsigned columnIndex = (offset - portionLogicalTop) / computedColumnHeight()
; | 72 unsigned columnIndex = columnIndexAtOffset(offset, AssumeNewColumns); |
63 return portionLogicalTop + columnIndex * computedColumnHeight(); | 73 return portionLogicalTop + columnIndex * computedColumnHeight(); |
64 } | 74 } |
65 | 75 |
| 76 void RenderMultiColumnSet::setAndConstrainColumnHeight(LayoutUnit newHeight) |
| 77 { |
| 78 m_computedColumnHeight = newHeight; |
| 79 if (m_computedColumnHeight > m_maxColumnHeight) |
| 80 m_computedColumnHeight = m_maxColumnHeight; |
| 81 // FIXME: the height may also be affected by the enclosing pagination contex
t, if any. |
| 82 } |
| 83 |
| 84 bool RenderMultiColumnSet::calculateBalancedHeight(bool initial) |
| 85 { |
| 86 ASSERT(toRenderMultiColumnBlock(parent())->requiresBalancing()); |
| 87 LayoutUnit oldColumnHeight = m_computedColumnHeight; |
| 88 LayoutUnit currentMinSpaceShortage = m_minSpaceShortage; |
| 89 m_minSpaceShortage = LayoutUnit::max(); |
| 90 |
| 91 if (initial) { |
| 92 // Start with the lowest imaginable column height. |
| 93 LayoutUnit logicalHeightGuess = ceilf(float(flowThread()->logicalHeight(
)) / float(m_computedColumnCount)); |
| 94 logicalHeightGuess = max(logicalHeightGuess, m_minimumColumnHeight); |
| 95 setAndConstrainColumnHeight(logicalHeightGuess); |
| 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 } |
| 104 |
| 105 if (columnCount() <= computedColumnCount()) { |
| 106 // With the current column height, the content fits without creating ove
rflowing columns. We're done. |
| 107 return false; |
| 108 } |
| 109 |
| 110 // 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. |
| 112 |
| 113 ASSERT(currentMinSpaceShortage != LayoutUnit::max()); // If this can actuall
y happen, we probably have a bug. |
| 114 if (currentMinSpaceShortage == LayoutUnit::max()) |
| 115 return false; // So bail out rather than looping infinitely. |
| 116 |
| 117 setAndConstrainColumnHeight(m_computedColumnHeight + currentMinSpaceShortage
); |
| 118 |
| 119 // If we reach the maximum column height (typically set by the height or max
-height property), |
| 120 // we may not be allowed to stretch further. Return true only if stretching |
| 121 // succeeded. Otherwise, we're done. |
| 122 ASSERT(m_computedColumnHeight >= oldColumnHeight); // We shouldn't be able t
o shrink the height! |
| 123 return m_computedColumnHeight > oldColumnHeight; |
| 124 } |
| 125 |
| 126 void RenderMultiColumnSet::recordSpaceShortage(LayoutUnit spaceShortage) |
| 127 { |
| 128 if (spaceShortage >= m_minSpaceShortage) |
| 129 return; |
| 130 |
| 131 // The space shortage is what we use as our stretch amount. We need a positi
ve number here in |
| 132 // order to get anywhere. |
| 133 ASSERT(spaceShortage > 0); |
| 134 |
| 135 m_minSpaceShortage = spaceShortage; |
| 136 } |
| 137 |
66 void RenderMultiColumnSet::updateLogicalWidth() | 138 void RenderMultiColumnSet::updateLogicalWidth() |
67 { | 139 { |
68 RenderMultiColumnBlock* parentBlock = toRenderMultiColumnBlock(parent()); | 140 RenderMultiColumnBlock* parentBlock = toRenderMultiColumnBlock(parent()); |
69 setComputedColumnWidthAndCount(parentBlock->columnWidth(), parentBlock->colu
mnCount()); // FIXME: This will eventually vary if we are contained inside regio
ns. | 141 setComputedColumnWidthAndCount(parentBlock->columnWidth(), parentBlock->colu
mnCount()); // FIXME: This will eventually vary if we are contained inside regio
ns. |
70 | 142 |
71 // FIXME: When we add regions support, we'll start it off at the width of th
e multi-column | 143 // FIXME: When we add regions support, we'll start it off at the width of th
e multi-column |
72 // block in that particular region. | 144 // block in that particular region. |
73 setLogicalWidth(parentBox()->contentLogicalWidth()); | 145 setLogicalWidth(parentBox()->contentLogicalWidth()); |
74 | 146 |
75 // If we overflow, increase our logical width. | 147 // If we overflow, increase our logical width. |
76 unsigned colCount = columnCount(); | 148 unsigned colCount = columnCount(); |
77 if (!colCount) | |
78 return; | |
79 | |
80 LayoutUnit colGap = columnGap(); | 149 LayoutUnit colGap = columnGap(); |
81 LayoutUnit minimumContentLogicalWidth = colCount * computedColumnWidth() + (
colCount - 1) * colGap; | 150 LayoutUnit minimumContentLogicalWidth = colCount * computedColumnWidth() + (
colCount - 1) * colGap; |
82 LayoutUnit currentContentLogicalWidth = contentLogicalWidth(); | 151 LayoutUnit currentContentLogicalWidth = contentLogicalWidth(); |
83 LayoutUnit delta = max(LayoutUnit(), minimumContentLogicalWidth - currentCon
tentLogicalWidth); | 152 LayoutUnit delta = max(LayoutUnit(), minimumContentLogicalWidth - currentCon
tentLogicalWidth); |
84 if (!delta) | 153 if (!delta) |
85 return; | 154 return; |
86 | 155 |
87 // Increase our logical width by the delta. | 156 // Increase our logical width by the delta. |
88 setLogicalWidth(logicalWidth() + delta); | 157 setLogicalWidth(logicalWidth() + delta); |
89 } | 158 } |
90 | 159 |
91 void RenderMultiColumnSet::updateLogicalHeight() | 160 void RenderMultiColumnSet::prepareForLayout() |
92 { | 161 { |
93 // FIXME: This is the only class that overrides updateLogicalHeight. If we d
idn't have to set computedColumnHeight, | 162 RenderMultiColumnBlock* multicolBlock = toRenderMultiColumnBlock(parent()); |
94 // we could remove this and make updateLogicalHeight non-virtual. https://bu
gs.webkit.org/show_bug.cgi?id=96804 | 163 RenderStyle* multicolStyle = multicolBlock->style(); |
95 // Make sure our column height is up to date. | 164 |
96 LogicalExtentComputedValues computedValues; | 165 // Set box logical top. |
97 computeLogicalHeight(0, 0, computedValues); | 166 ASSERT(!previousSiblingBox() || !previousSiblingBox()->isRenderMultiColumnSe
t()); // FIXME: multiple set not implemented; need to examine previous set to ca
lculate the correct logical top. |
98 setComputedColumnHeight(computedValues.m_extent); // FIXME: Once we make mor
e than one column set, this will become variable. | 167 setLogicalTop(multicolBlock->borderBefore() + multicolBlock->paddingBefore()
); |
99 | 168 |
100 // Our logical height is always just the height of our columns. | 169 // Set box width. |
101 setLogicalHeight(computedColumnHeight()); | 170 updateLogicalWidth(); |
| 171 |
| 172 if (multicolBlock->requiresBalancing()) { |
| 173 // Set maximum column height. We will not stretch beyond this. |
| 174 m_maxColumnHeight = LayoutUnit::max(); |
| 175 if (!multicolStyle->logicalHeight().isAuto()) |
| 176 m_maxColumnHeight = multicolBlock->computeContentLogicalHeight(multi
colStyle->logicalHeight(), -1); |
| 177 if (!multicolStyle->logicalMaxHeight().isUndefined()) { |
| 178 LayoutUnit logicalMaxHeight = multicolBlock->computeContentLogicalHe
ight(multicolStyle->logicalMaxHeight(), -1); |
| 179 if (m_maxColumnHeight > logicalMaxHeight) |
| 180 m_maxColumnHeight = logicalMaxHeight; |
| 181 } |
| 182 m_maxColumnHeight = heightAdjustedForSetOffset(m_maxColumnHeight); |
| 183 m_computedColumnHeight = 0; // Restart balancing. |
| 184 } else { |
| 185 setAndConstrainColumnHeight(heightAdjustedForSetOffset(multicolBlock->co
lumnHeightAvailable())); |
| 186 } |
| 187 |
| 188 // Nuke previously stored minimum column height. Contents may have changed f
or all we know. |
| 189 m_minimumColumnHeight = 0; |
102 } | 190 } |
103 | 191 |
104 void RenderMultiColumnSet::computeLogicalHeight(LayoutUnit, LayoutUnit, LogicalE
xtentComputedValues& computedValues) const | 192 void RenderMultiColumnSet::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTo
p, LogicalExtentComputedValues& computedValues) const |
105 { | 193 { |
106 RenderMultiColumnBlock* parentBlock = toRenderMultiColumnBlock(parent()); | 194 computedValues.m_extent = m_computedColumnHeight; |
107 computedValues.m_extent = parentBlock->columnHeight(); | 195 computedValues.m_position = logicalTop; |
108 } | 196 } |
109 | 197 |
110 LayoutUnit RenderMultiColumnSet::columnGap() const | 198 LayoutUnit RenderMultiColumnSet::columnGap() const |
111 { | 199 { |
112 // FIXME: Eventually we will cache the column gap when the widths of columns
start varying, but for now we just | 200 // FIXME: Eventually we will cache the column gap when the widths of columns
start varying, but for now we just |
113 // go to the parent block to get the gap. | 201 // go to the parent block to get the gap. |
114 RenderMultiColumnBlock* parentBlock = toRenderMultiColumnBlock(parent()); | 202 RenderMultiColumnBlock* parentBlock = toRenderMultiColumnBlock(parent()); |
115 if (parentBlock->style()->hasNormalColumnGap()) | 203 if (parentBlock->style()->hasNormalColumnGap()) |
116 return parentBlock->style()->fontDescription().computedPixelSize(); // "
1em" is recommended as the normal gap setting. Matches <p> margins. | 204 return parentBlock->style()->fontDescription().computedPixelSize(); // "
1em" is recommended as the normal gap setting. Matches <p> margins. |
117 return parentBlock->style()->columnGap(); | 205 return parentBlock->style()->columnGap(); |
118 } | 206 } |
119 | 207 |
120 unsigned RenderMultiColumnSet::columnCount() const | 208 unsigned RenderMultiColumnSet::columnCount() const |
121 { | 209 { |
| 210 // We must always return a value of 1 or greater. Column count = 0 is a mean
ingless situation, |
| 211 // and will confuse and cause problems in other parts of the code. |
122 if (!computedColumnHeight()) | 212 if (!computedColumnHeight()) |
123 return 0; | 213 return 1; |
124 | 214 |
125 // Our portion rect determines our column count. We have as many columns as
needed to fit all the content. | 215 // Our portion rect determines our column count. We have as many columns as
needed to fit all the content. |
126 LayoutUnit logicalHeightInColumns = flowThread()->isHorizontalWritingMode()
? flowThreadPortionRect().height() : flowThreadPortionRect().width(); | 216 LayoutUnit logicalHeightInColumns = flowThread()->isHorizontalWritingMode()
? flowThreadPortionRect().height() : flowThreadPortionRect().width(); |
127 return ceil(static_cast<float>(logicalHeightInColumns) / computedColumnHeigh
t()); | 217 unsigned count = ceil(static_cast<float>(logicalHeightInColumns) / computedC
olumnHeight()); |
| 218 ASSERT(count >= 1); |
| 219 return count; |
128 } | 220 } |
129 | 221 |
130 LayoutRect RenderMultiColumnSet::columnRectAt(unsigned index) const | 222 LayoutRect RenderMultiColumnSet::columnRectAt(unsigned index) const |
131 { | 223 { |
132 LayoutUnit colLogicalWidth = computedColumnWidth(); | 224 LayoutUnit colLogicalWidth = computedColumnWidth(); |
133 LayoutUnit colLogicalHeight = computedColumnHeight(); | 225 LayoutUnit colLogicalHeight = computedColumnHeight(); |
134 LayoutUnit colLogicalTop = borderBefore() + paddingBefore(); | 226 LayoutUnit colLogicalTop = borderBefore() + paddingBefore(); |
135 LayoutUnit colLogicalLeft = borderAndPaddingLogicalLeft(); | 227 LayoutUnit colLogicalLeft = borderAndPaddingLogicalLeft(); |
136 LayoutUnit colGap = columnGap(); | 228 LayoutUnit colGap = columnGap(); |
137 if (style()->isLeftToRightDirection()) | 229 if (style()->isLeftToRightDirection()) |
138 colLogicalLeft += index * (colLogicalWidth + colGap); | 230 colLogicalLeft += index * (colLogicalWidth + colGap); |
139 else | 231 else |
140 colLogicalLeft += contentLogicalWidth() - colLogicalWidth - index * (col
LogicalWidth + colGap); | 232 colLogicalLeft += contentLogicalWidth() - colLogicalWidth - index * (col
LogicalWidth + colGap); |
141 | 233 |
142 if (isHorizontalWritingMode()) | 234 if (isHorizontalWritingMode()) |
143 return LayoutRect(colLogicalLeft, colLogicalTop, colLogicalWidth, colLog
icalHeight); | 235 return LayoutRect(colLogicalLeft, colLogicalTop, colLogicalWidth, colLog
icalHeight); |
144 return LayoutRect(colLogicalTop, colLogicalLeft, colLogicalHeight, colLogica
lWidth); | 236 return LayoutRect(colLogicalTop, colLogicalLeft, colLogicalHeight, colLogica
lWidth); |
145 } | 237 } |
146 | 238 |
147 unsigned RenderMultiColumnSet::columnIndexAtOffset(LayoutUnit offset) const | 239 unsigned RenderMultiColumnSet::columnIndexAtOffset(LayoutUnit offset, ColumnInde
xCalculationMode mode) const |
148 { | 240 { |
149 LayoutRect portionRect(flowThreadPortionRect()); | 241 LayoutRect portionRect(flowThreadPortionRect()); |
| 242 |
| 243 // Handle the offset being out of range. |
150 LayoutUnit flowThreadLogicalTop = isHorizontalWritingMode() ? portionRect.y(
) : portionRect.x(); | 244 LayoutUnit flowThreadLogicalTop = isHorizontalWritingMode() ? portionRect.y(
) : portionRect.x(); |
151 LayoutUnit flowThreadLogicalBottom = isHorizontalWritingMode() ? portionRect
.maxY() : portionRect.maxX(); | |
152 | |
153 // Handle the offset being out of range. | |
154 if (offset < flowThreadLogicalTop) | 245 if (offset < flowThreadLogicalTop) |
155 return 0; | 246 return 0; |
156 if (offset >= flowThreadLogicalBottom) | 247 // If we're laying out right now, we cannot constrain against some logical b
ottom, since it |
157 return columnCount() - 1; | 248 // isn't known yet. Otherwise, just return the last column if we're past the
logical bottom. |
158 | 249 if (mode == ClampToExistingColumns) { |
| 250 LayoutUnit flowThreadLogicalBottom = isHorizontalWritingMode() ? portion
Rect.maxY() : portionRect.maxX(); |
| 251 if (offset >= flowThreadLogicalBottom) |
| 252 return columnCount() - 1; |
| 253 } |
| 254 |
159 // Just divide by the column height to determine the correct column. | 255 // Just divide by the column height to determine the correct column. |
160 return static_cast<float>(offset - flowThreadLogicalTop) / computedColumnHei
ght(); | 256 return static_cast<float>(offset - flowThreadLogicalTop) / computedColumnHei
ght(); |
161 } | 257 } |
162 | 258 |
163 LayoutRect RenderMultiColumnSet::flowThreadPortionRectAt(unsigned index) const | 259 LayoutRect RenderMultiColumnSet::flowThreadPortionRectAt(unsigned index) const |
164 { | 260 { |
165 LayoutRect portionRect = flowThreadPortionRect(); | 261 LayoutRect portionRect = flowThreadPortionRect(); |
166 if (isHorizontalWritingMode()) | 262 if (isHorizontalWritingMode()) |
167 portionRect = LayoutRect(portionRect.x(), portionRect.y() + index * comp
utedColumnHeight(), portionRect.width(), computedColumnHeight()); | 263 portionRect = LayoutRect(portionRect.x(), portionRect.y() + index * comp
utedColumnHeight(), portionRect.width(), computedColumnHeight()); |
168 else | 264 else |
(...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
412 fragments.append(fragment); | 508 fragments.append(fragment); |
413 } | 509 } |
414 } | 510 } |
415 | 511 |
416 const char* RenderMultiColumnSet::renderName() const | 512 const char* RenderMultiColumnSet::renderName() const |
417 { | 513 { |
418 return "RenderMultiColumnSet"; | 514 return "RenderMultiColumnSet"; |
419 } | 515 } |
420 | 516 |
421 } | 517 } |
OLD | NEW |