OLD | NEW |
---|---|
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "core/layout/ColumnBalancer.h" | 5 #include "core/layout/ColumnBalancer.h" |
6 | 6 |
7 #include "core/layout/LayoutMultiColumnFlowThread.h" | 7 #include "core/layout/LayoutMultiColumnFlowThread.h" |
8 #include "core/layout/LayoutMultiColumnSet.h" | 8 #include "core/layout/LayoutMultiColumnSet.h" |
9 #include "core/layout/api/LineLayoutBlockFlow.h" | 9 #include "core/layout/api/LineLayoutBlockFlow.h" |
10 | 10 |
(...skipping 11 matching lines...) Expand all Loading... | |
22 ASSERT(!flowThreadOffset()); | 22 ASSERT(!flowThreadOffset()); |
23 } | 23 } |
24 | 24 |
25 void ColumnBalancer::traverseSubtree(const LayoutBox& box) { | 25 void ColumnBalancer::traverseSubtree(const LayoutBox& box) { |
26 if (box.childrenInline() && box.isLayoutBlockFlow()) { | 26 if (box.childrenInline() && box.isLayoutBlockFlow()) { |
27 // Look for breaks between lines. | 27 // Look for breaks between lines. |
28 for (const RootInlineBox* line = toLayoutBlockFlow(box).firstRootBox(); | 28 for (const RootInlineBox* line = toLayoutBlockFlow(box).firstRootBox(); |
29 line; line = line->nextRootBox()) { | 29 line; line = line->nextRootBox()) { |
30 LayoutUnit lineTopInFlowThread = | 30 LayoutUnit lineTopInFlowThread = |
31 m_flowThreadOffset + line->lineTopWithLeading(); | 31 m_flowThreadOffset + line->lineTopWithLeading(); |
32 if (lineTopInFlowThread < logicalTopInFlowThread()) | 32 if (lineTopInFlowThread < logicalTopInFlowThread()) continue; |
33 continue; | 33 if (lineTopInFlowThread >= logicalBottomInFlowThread()) break; |
34 if (lineTopInFlowThread >= logicalBottomInFlowThread()) | |
35 break; | |
36 examineLine(*line); | 34 examineLine(*line); |
37 } | 35 } |
38 } | 36 } |
39 | 37 |
40 const LayoutFlowThread* flowThread = columnSet().flowThread(); | 38 const LayoutFlowThread* flowThread = columnSet().flowThread(); |
41 bool isHorizontalWritingMode = flowThread->isHorizontalWritingMode(); | 39 bool isHorizontalWritingMode = flowThread->isHorizontalWritingMode(); |
42 | 40 |
43 // The break-after value from the previous in-flow block-level object to be jo ined with the | 41 // The break-after value from the previous in-flow block-level object to be |
42 // joined with the | |
44 // break-before value of the next in-flow block-level sibling. | 43 // break-before value of the next in-flow block-level sibling. |
ikilpatrick
2016/10/04 16:39:02
bad.
| |
45 EBreak previousBreakAfterValue = BreakAuto; | 44 EBreak previousBreakAfterValue = BreakAuto; |
46 | 45 |
47 // Look for breaks between and inside block-level children. Even if this is a block flow with | 46 // Look for breaks between and inside block-level children. Even if this is a |
47 // block flow with | |
48 // inline children, there may be interesting floats to examine here. | 48 // inline children, there may be interesting floats to examine here. |
49 for (const LayoutObject* child = box.slowFirstChild(); child; | 49 for (const LayoutObject* child = box.slowFirstChild(); child; |
50 child = child->nextSibling()) { | 50 child = child->nextSibling()) { |
51 if (!child->isBox() || child->isInline()) | 51 if (!child->isBox() || child->isInline()) continue; |
52 continue; | |
53 const LayoutBox& childBox = toLayoutBox(*child); | 52 const LayoutBox& childBox = toLayoutBox(*child); |
54 LayoutRect overflowRect = childBox.layoutOverflowRect(); | 53 LayoutRect overflowRect = childBox.layoutOverflowRect(); |
55 LayoutUnit childLogicalBottomWithOverflow = | 54 LayoutUnit childLogicalBottomWithOverflow = |
56 childBox.logicalTop() + | 55 childBox.logicalTop() + |
57 (isHorizontalWritingMode ? overflowRect.maxY() : overflowRect.maxX()); | 56 (isHorizontalWritingMode ? overflowRect.maxY() : overflowRect.maxX()); |
58 if (m_flowThreadOffset + childLogicalBottomWithOverflow <= | 57 if (m_flowThreadOffset + childLogicalBottomWithOverflow <= |
59 logicalTopInFlowThread()) { | 58 logicalTopInFlowThread()) { |
60 // This child is fully above the flow thread portion we're examining. | 59 // This child is fully above the flow thread portion we're examining. |
61 continue; | 60 continue; |
62 } | 61 } |
63 LayoutUnit childLogicalTopWithOverflow = | 62 LayoutUnit childLogicalTopWithOverflow = |
64 childBox.logicalTop() + | 63 childBox.logicalTop() + |
65 (isHorizontalWritingMode ? overflowRect.y() : overflowRect.x()); | 64 (isHorizontalWritingMode ? overflowRect.y() : overflowRect.x()); |
66 if (m_flowThreadOffset + childLogicalTopWithOverflow >= | 65 if (m_flowThreadOffset + childLogicalTopWithOverflow >= |
67 logicalBottomInFlowThread()) { | 66 logicalBottomInFlowThread()) { |
68 // This child is fully below the flow thread portion we're examining. We c annot just | 67 // This child is fully below the flow thread portion we're examining. We |
68 // cannot just | |
ikilpatrick
2016/10/04 16:39:02
bad.
| |
69 // stop here, though, thanks to negative margins. So keep looking. | 69 // stop here, though, thanks to negative margins. So keep looking. |
70 continue; | 70 continue; |
71 } | 71 } |
72 if (childBox.isOutOfFlowPositioned() || childBox.isColumnSpanAll()) | 72 if (childBox.isOutOfFlowPositioned() || childBox.isColumnSpanAll()) |
73 continue; | 73 continue; |
74 | 74 |
75 // Tables are wicked. Both table rows and table cells are relative to their table section. | 75 // Tables are wicked. Both table rows and table cells are relative to their |
76 // table section. | |
76 LayoutUnit offsetForThisChild = | 77 LayoutUnit offsetForThisChild = |
77 childBox.isTableRow() ? LayoutUnit() : childBox.logicalTop(); | 78 childBox.isTableRow() ? LayoutUnit() : childBox.logicalTop(); |
78 m_flowThreadOffset += offsetForThisChild; | 79 m_flowThreadOffset += offsetForThisChild; |
79 | 80 |
80 examineBoxAfterEntering(childBox, previousBreakAfterValue); | 81 examineBoxAfterEntering(childBox, previousBreakAfterValue); |
81 // Unless the child is unsplittable, or if the child establishes an inner mu lticol | 82 // Unless the child is unsplittable, or if the child establishes an inner |
83 // multicol | |
ikilpatrick
2016/10/04 16:39:02
bad.
| |
82 // container, we descend into its subtree for further examination. | 84 // container, we descend into its subtree for further examination. |
83 if (childBox.getPaginationBreakability() != LayoutBox::ForbidBreaks && | 85 if (childBox.getPaginationBreakability() != LayoutBox::ForbidBreaks && |
84 (!childBox.isLayoutBlockFlow() || | 86 (!childBox.isLayoutBlockFlow() || |
85 !toLayoutBlockFlow(childBox).multiColumnFlowThread())) | 87 !toLayoutBlockFlow(childBox).multiColumnFlowThread())) |
86 traverseSubtree(childBox); | 88 traverseSubtree(childBox); |
87 previousBreakAfterValue = childBox.breakAfter(); | 89 previousBreakAfterValue = childBox.breakAfter(); |
88 examineBoxBeforeLeaving(childBox); | 90 examineBoxBeforeLeaving(childBox); |
89 | 91 |
90 m_flowThreadOffset -= offsetForThisChild; | 92 m_flowThreadOffset -= offsetForThisChild; |
91 } | 93 } |
92 } | 94 } |
93 | 95 |
94 InitialColumnHeightFinder::InitialColumnHeightFinder( | 96 InitialColumnHeightFinder::InitialColumnHeightFinder( |
95 const LayoutMultiColumnSet& columnSet, | 97 const LayoutMultiColumnSet& columnSet, LayoutUnit logicalTopInFlowThread, |
96 LayoutUnit logicalTopInFlowThread, | |
97 LayoutUnit logicalBottomInFlowThread) | 98 LayoutUnit logicalBottomInFlowThread) |
98 : ColumnBalancer(columnSet, | 99 : ColumnBalancer(columnSet, logicalTopInFlowThread, |
99 logicalTopInFlowThread, | |
100 logicalBottomInFlowThread) { | 100 logicalBottomInFlowThread) { |
101 m_shortestStruts.resize(columnSet.usedColumnCount()); | 101 m_shortestStruts.resize(columnSet.usedColumnCount()); |
102 for (auto& strut : m_shortestStruts) | 102 for (auto& strut : m_shortestStruts) strut = LayoutUnit::max() { } |
103 strut = LayoutUnit::max(); | |
104 traverse(); | 103 traverse(); |
105 // We have now found each explicit / forced break, and their location. Now we need to figure out | 104 // We have now found each explicit / forced break, and their location. Now we |
106 // how many additional implicit / soft breaks we need and guess where they wil l occur, in order | 105 // need to figure out how many additional implicit / soft breaks we need and |
106 // guess where they will occur, in order | |
107 // to provide an initial column height. | 107 // to provide an initial column height. |
108 distributeImplicitBreaks(); | 108 distributeImplicitBreaks(); |
109 } | 109 } |
110 | 110 |
111 LayoutUnit InitialColumnHeightFinder::initialMinimalBalancedHeight() const { | 111 LayoutUnit InitialColumnHeightFinder::initialMinimalBalancedHeight() const { |
112 unsigned index = contentRunIndexWithTallestColumns(); | 112 unsigned index = contentRunIndexWithTallestColumns(); |
113 LayoutUnit startOffset = index > 0 ? m_contentRuns[index - 1].breakOffset() | 113 LayoutUnit startOffset = index > 0 ? m_contentRuns[index - 1].breakOffset() |
114 : logicalTopInFlowThread(); | 114 : logicalTopInFlowThread(); |
115 return m_contentRuns[index].columnLogicalHeight(startOffset); | 115 return m_contentRuns[index].columnLogicalHeight(startOffset); |
116 } | 116 } |
117 | 117 |
118 void InitialColumnHeightFinder::examineBoxAfterEntering( | 118 void InitialColumnHeightFinder::examineBoxAfterEntering( |
119 const LayoutBox& box, | 119 const LayoutBox& box, EBreak previousBreakAfterValue) { |
120 EBreak previousBreakAfterValue) { | |
121 if (isLogicalTopWithinBounds(flowThreadOffset() - box.paginationStrut())) { | 120 if (isLogicalTopWithinBounds(flowThreadOffset() - box.paginationStrut())) { |
122 if (box.needsForcedBreakBefore(previousBreakAfterValue)) { | 121 if (box.needsForcedBreakBefore(previousBreakAfterValue)) { |
123 addContentRun(flowThreadOffset()); | 122 addContentRun(flowThreadOffset()); |
124 } else { | 123 } else { |
125 ASSERT(isFirstAfterBreak(flowThreadOffset()) || !box.paginationStrut()); | 124 ASSERT(isFirstAfterBreak(flowThreadOffset()) || !box.paginationStrut()); |
126 if (isFirstAfterBreak(flowThreadOffset())) { | 125 if (isFirstAfterBreak(flowThreadOffset())) { |
127 // This box is first after a soft break. | 126 // This box is first after a soft break. |
128 recordStrutBeforeOffset(flowThreadOffset(), box.paginationStrut()); | 127 recordStrutBeforeOffset(flowThreadOffset(), box.paginationStrut()); |
129 } | 128 } |
130 } | 129 } |
131 } | 130 } |
132 | 131 |
133 if (box.getPaginationBreakability() != LayoutBox::AllowAnyBreaks) { | 132 if (box.getPaginationBreakability() != LayoutBox::AllowAnyBreaks) { |
134 LayoutUnit unsplittableLogicalHeight = box.logicalHeight(); | 133 LayoutUnit unsplittableLogicalHeight = box.logicalHeight(); |
135 if (box.isFloating()) | 134 if (box.isFloating()) |
136 unsplittableLogicalHeight += box.marginBefore() + box.marginAfter(); | 135 unsplittableLogicalHeight += box.marginBefore() + box.marginAfter(); |
137 m_tallestUnbreakableLogicalHeight = | 136 m_tallestUnbreakableLogicalHeight = |
138 std::max(m_tallestUnbreakableLogicalHeight, unsplittableLogicalHeight); | 137 std::max(m_tallestUnbreakableLogicalHeight, unsplittableLogicalHeight); |
139 return; | 138 return; |
140 } | 139 } |
141 // Need to examine inner multicol containers to find their tallest unbreakable piece of content. | 140 // Need to examine inner multicol containers to find their tallest unbreakable |
142 if (!box.isLayoutBlockFlow()) | 141 // piece of content. |
143 return; | 142 if (!box.isLayoutBlockFlow()) return; |
144 LayoutMultiColumnFlowThread* innerFlowThread = | 143 LayoutMultiColumnFlowThread* innerFlowThread = |
145 toLayoutBlockFlow(box).multiColumnFlowThread(); | 144 toLayoutBlockFlow(box).multiColumnFlowThread(); |
146 if (!innerFlowThread || innerFlowThread->isLayoutPagedFlowThread()) | 145 if (!innerFlowThread || innerFlowThread->isLayoutPagedFlowThread()) return; |
147 return; | |
148 LayoutUnit offsetInInnerFlowThread = | 146 LayoutUnit offsetInInnerFlowThread = |
149 flowThreadOffset() - | 147 flowThreadOffset() - |
150 innerFlowThread->blockOffsetInEnclosingFragmentationContext(); | 148 innerFlowThread->blockOffsetInEnclosingFragmentationContext(); |
151 LayoutUnit innerUnbreakableHeight = | 149 LayoutUnit innerUnbreakableHeight = |
152 innerFlowThread->tallestUnbreakableLogicalHeight(offsetInInnerFlowThread); | 150 innerFlowThread->tallestUnbreakableLogicalHeight(offsetInInnerFlowThread); |
153 m_tallestUnbreakableLogicalHeight = | 151 m_tallestUnbreakableLogicalHeight = |
154 std::max(m_tallestUnbreakableLogicalHeight, innerUnbreakableHeight); | 152 std::max(m_tallestUnbreakableLogicalHeight, innerUnbreakableHeight); |
155 } | 153 } |
156 | 154 |
157 void InitialColumnHeightFinder::examineBoxBeforeLeaving(const LayoutBox& box) {} | 155 void InitialColumnHeightFinder::examineBoxBeforeLeaving(const LayoutBox& box) {} |
158 | 156 |
159 static inline LayoutUnit columnLogicalHeightRequirementForLine( | 157 static inline LayoutUnit columnLogicalHeightRequirementForLine( |
160 const ComputedStyle& style, | 158 const ComputedStyle& style, const RootInlineBox& lastLine) { |
161 const RootInlineBox& lastLine) { | 159 // We may require a certain minimum number of lines per page in order to |
162 // We may require a certain minimum number of lines per page in order to satis fy | 160 // satisfy |
163 // orphans and widows, and that may affect the minimum page height. | 161 // orphans and widows, and that may affect the minimum page height. |
ikilpatrick
2016/10/04 16:39:02
bad.
| |
164 unsigned minimumLineCount = | 162 unsigned minimumLineCount = |
165 std::max<unsigned>(style.orphans(), style.widows()); | 163 std::max<unsigned>(style.orphans(), style.widows()); |
166 const RootInlineBox* firstLine = &lastLine; | 164 const RootInlineBox* firstLine = &lastLine; |
167 for (unsigned i = 1; i < minimumLineCount && firstLine->prevRootBox(); i++) | 165 for (unsigned i = 1; i < minimumLineCount && firstLine->prevRootBox(); i++) |
168 firstLine = firstLine->prevRootBox(); | 166 firstLine = firstLine->prevRootBox(); |
169 return lastLine.lineBottomWithLeading() - firstLine->lineTopWithLeading(); | 167 return lastLine.lineBottomWithLeading() - firstLine->lineTopWithLeading(); |
170 } | 168 } |
171 | 169 |
172 void InitialColumnHeightFinder::examineLine(const RootInlineBox& line) { | 170 void InitialColumnHeightFinder::examineLine(const RootInlineBox& line) { |
173 LayoutUnit lineTop = line.lineTopWithLeading(); | 171 LayoutUnit lineTop = line.lineTopWithLeading(); |
174 LayoutUnit lineTopInFlowThread = flowThreadOffset() + lineTop; | 172 LayoutUnit lineTopInFlowThread = flowThreadOffset() + lineTop; |
175 LayoutUnit minimumLogialHeight = | 173 LayoutUnit minimumLogialHeight = |
176 columnLogicalHeightRequirementForLine(line.block().styleRef(), line); | 174 columnLogicalHeightRequirementForLine(line.block().styleRef(), line); |
177 m_tallestUnbreakableLogicalHeight = | 175 m_tallestUnbreakableLogicalHeight = |
178 std::max(m_tallestUnbreakableLogicalHeight, minimumLogialHeight); | 176 std::max(m_tallestUnbreakableLogicalHeight, minimumLogialHeight); |
179 ASSERT( | 177 ASSERT( |
180 isFirstAfterBreak(lineTopInFlowThread) || !line.paginationStrut() || | 178 isFirstAfterBreak(lineTopInFlowThread) || !line.paginationStrut() || |
181 !isLogicalTopWithinBounds(lineTopInFlowThread - line.paginationStrut())); | 179 !isLogicalTopWithinBounds(lineTopInFlowThread - line.paginationStrut())); |
182 if (isFirstAfterBreak(lineTopInFlowThread)) | 180 if (isFirstAfterBreak(lineTopInFlowThread)) |
183 recordStrutBeforeOffset(lineTopInFlowThread, line.paginationStrut()); | 181 recordStrutBeforeOffset(lineTopInFlowThread, line.paginationStrut()); |
184 } | 182 } |
185 | 183 |
186 void InitialColumnHeightFinder::recordStrutBeforeOffset( | 184 void InitialColumnHeightFinder::recordStrutBeforeOffset( |
187 LayoutUnit offsetInFlowThread, | 185 LayoutUnit offsetInFlowThread, LayoutUnit strut) { |
188 LayoutUnit strut) { | |
189 ASSERT(columnSet().usedColumnCount() >= 1); | 186 ASSERT(columnSet().usedColumnCount() >= 1); |
190 unsigned columnCount = columnSet().usedColumnCount(); | 187 unsigned columnCount = columnSet().usedColumnCount(); |
191 ASSERT(m_shortestStruts.size() == columnCount); | 188 ASSERT(m_shortestStruts.size() == columnCount); |
192 unsigned index = groupAtOffset(offsetInFlowThread) | 189 unsigned index = groupAtOffset(offsetInFlowThread) |
193 .columnIndexAtOffset(offsetInFlowThread - strut, | 190 .columnIndexAtOffset(offsetInFlowThread - strut, |
194 LayoutBox::AssociateWithLatterPage); | 191 LayoutBox::AssociateWithLatterPage); |
195 if (index >= columnCount) | 192 if (index >= columnCount) return; |
196 return; | |
197 m_shortestStruts[index] = std::min(m_shortestStruts[index], strut); | 193 m_shortestStruts[index] = std::min(m_shortestStruts[index], strut); |
198 } | 194 } |
199 | 195 |
200 LayoutUnit InitialColumnHeightFinder::spaceUsedByStrutsAt( | 196 LayoutUnit InitialColumnHeightFinder::spaceUsedByStrutsAt( |
201 LayoutUnit offsetInFlowThread) const { | 197 LayoutUnit offsetInFlowThread) const { |
202 unsigned stopBeforeColumn = | 198 unsigned stopBeforeColumn = |
203 groupAtOffset(offsetInFlowThread) | 199 groupAtOffset(offsetInFlowThread) |
204 .columnIndexAtOffset(offsetInFlowThread, | 200 .columnIndexAtOffset(offsetInFlowThread, |
205 LayoutBox::AssociateWithLatterPage) + | 201 LayoutBox::AssociateWithLatterPage) + |
206 1; | 202 1; |
207 stopBeforeColumn = std::min(stopBeforeColumn, columnSet().usedColumnCount()); | 203 stopBeforeColumn = std::min(stopBeforeColumn, columnSet().usedColumnCount()); |
208 ASSERT(stopBeforeColumn <= m_shortestStruts.size()); | 204 ASSERT(stopBeforeColumn <= m_shortestStruts.size()); |
209 LayoutUnit totalStrutSpace; | 205 LayoutUnit totalStrutSpace; |
210 for (unsigned i = 0; i < stopBeforeColumn; i++) { | 206 for (unsigned i = 0; i < stopBeforeColumn; i++) { |
211 if (m_shortestStruts[i] != LayoutUnit::max()) | 207 if (m_shortestStruts[i] != LayoutUnit::max()) |
212 totalStrutSpace += m_shortestStruts[i]; | 208 totalStrutSpace += m_shortestStruts[i]; |
213 } | 209 } |
214 return totalStrutSpace; | 210 return totalStrutSpace; |
215 } | 211 } |
216 | 212 |
217 void InitialColumnHeightFinder::addContentRun( | 213 void InitialColumnHeightFinder::addContentRun( |
218 LayoutUnit endOffsetInFlowThread) { | 214 LayoutUnit endOffsetInFlowThread) { |
219 endOffsetInFlowThread -= spaceUsedByStrutsAt(endOffsetInFlowThread); | 215 endOffsetInFlowThread -= spaceUsedByStrutsAt(endOffsetInFlowThread); |
220 if (!m_contentRuns.isEmpty() && | 216 if (!m_contentRuns.isEmpty() && |
221 endOffsetInFlowThread <= m_contentRuns.last().breakOffset()) | 217 endOffsetInFlowThread <= m_contentRuns.last().breakOffset()) |
222 return; | 218 return; |
223 // Append another item as long as we haven't exceeded used column count. What ends up in the | 219 // Append another item as long as we haven't exceeded used column count. What |
220 // ends up in the | |
224 // overflow area shouldn't affect column balancing. | 221 // overflow area shouldn't affect column balancing. |
ikilpatrick
2016/10/04 16:39:02
bad.
| |
225 if (m_contentRuns.size() < columnSet().usedColumnCount()) | 222 if (m_contentRuns.size() < columnSet().usedColumnCount()) |
226 m_contentRuns.append(ContentRun(endOffsetInFlowThread)); | 223 m_contentRuns.append(ContentRun(endOffsetInFlowThread)); |
227 } | 224 } |
228 | 225 |
229 unsigned InitialColumnHeightFinder::contentRunIndexWithTallestColumns() const { | 226 unsigned InitialColumnHeightFinder::contentRunIndexWithTallestColumns() const { |
230 unsigned indexWithLargestHeight = 0; | 227 unsigned indexWithLargestHeight = 0; |
231 LayoutUnit largestHeight; | 228 LayoutUnit largestHeight; |
232 LayoutUnit previousOffset = logicalTopInFlowThread(); | 229 LayoutUnit previousOffset = logicalTopInFlowThread(); |
233 size_t runCount = m_contentRuns.size(); | 230 size_t runCount = m_contentRuns.size(); |
234 ASSERT(runCount); | 231 ASSERT(runCount); |
235 for (size_t i = 0; i < runCount; i++) { | 232 for (size_t i = 0; i < runCount; i++) { |
236 const ContentRun& run = m_contentRuns[i]; | 233 const ContentRun& run = m_contentRuns[i]; |
237 LayoutUnit height = run.columnLogicalHeight(previousOffset); | 234 LayoutUnit height = run.columnLogicalHeight(previousOffset); |
238 if (largestHeight < height) { | 235 if (largestHeight < height) { |
239 largestHeight = height; | 236 largestHeight = height; |
240 indexWithLargestHeight = i; | 237 indexWithLargestHeight = i; |
241 } | 238 } |
242 previousOffset = run.breakOffset(); | 239 previousOffset = run.breakOffset(); |
243 } | 240 } |
244 return indexWithLargestHeight; | 241 return indexWithLargestHeight; |
245 } | 242 } |
246 | 243 |
247 void InitialColumnHeightFinder::distributeImplicitBreaks() { | 244 void InitialColumnHeightFinder::distributeImplicitBreaks() { |
248 // Insert a final content run to encompass all content. This will include over flow if we're at | 245 // Insert a final content run to encompass all content. This will include |
246 // overflow if we're at | |
249 // the end of the multicol container. | 247 // the end of the multicol container. |
ikilpatrick
2016/10/04 16:39:02
bad.
| |
250 addContentRun(logicalBottomInFlowThread()); | 248 addContentRun(logicalBottomInFlowThread()); |
251 unsigned columnCount = m_contentRuns.size(); | 249 unsigned columnCount = m_contentRuns.size(); |
252 | 250 |
253 // If there is room for more breaks (to reach the used value of column-count), imagine that we | 251 // If there is room for more breaks (to reach the used value of column-count), |
254 // insert implicit breaks at suitable locations. At any given time, the conten t run with the | 252 // imagine that we insert implicit breaks at suitable locations. At any given |
255 // currently tallest columns will get another implicit break "inserted", which will increase its | 253 // time, the content run with the currently tallest columns will get another |
256 // column count by one and shrink its columns' height. Repeat until we have th e desired total | 254 // implicit break "inserted", which will increase its column count by one and |
257 // number of breaks. The largest column height among the runs will then be the initial column | 255 // shrink its columns' height. Repeat until we have the desired total number |
258 // height for the balancer to use. | 256 // of breaks. The largest column height among the runs will then be the |
257 // initial column height for the balancer to use. | |
259 while (columnCount < columnSet().usedColumnCount()) { | 258 while (columnCount < columnSet().usedColumnCount()) { |
260 unsigned index = contentRunIndexWithTallestColumns(); | 259 unsigned index = contentRunIndexWithTallestColumns(); |
261 m_contentRuns[index].assumeAnotherImplicitBreak(); | 260 m_contentRuns[index].assumeAnotherImplicitBreak(); |
262 columnCount++; | 261 columnCount++; |
263 } | 262 } |
264 } | 263 } |
265 | 264 |
266 MinimumSpaceShortageFinder::MinimumSpaceShortageFinder( | 265 MinimumSpaceShortageFinder::MinimumSpaceShortageFinder( |
267 const LayoutMultiColumnSet& columnSet, | 266 const LayoutMultiColumnSet& columnSet, LayoutUnit logicalTopInFlowThread, |
268 LayoutUnit logicalTopInFlowThread, | |
269 LayoutUnit logicalBottomInFlowThread) | 267 LayoutUnit logicalBottomInFlowThread) |
270 : ColumnBalancer(columnSet, | 268 : ColumnBalancer(columnSet, logicalTopInFlowThread, |
271 logicalTopInFlowThread, | |
272 logicalBottomInFlowThread), | 269 logicalBottomInFlowThread), |
273 m_minimumSpaceShortage(LayoutUnit::max()), | 270 m_minimumSpaceShortage(LayoutUnit::max()), |
274 m_pendingStrut(LayoutUnit::min()), | 271 m_pendingStrut(LayoutUnit::min()), |
275 m_forcedBreaksCount(0) { | 272 m_forcedBreaksCount(0) { |
276 traverse(); | 273 traverse(); |
277 } | 274 } |
278 | 275 |
279 void MinimumSpaceShortageFinder::examineBoxAfterEntering( | 276 void MinimumSpaceShortageFinder::examineBoxAfterEntering( |
280 const LayoutBox& box, | 277 const LayoutBox& box, EBreak previousBreakAfterValue) { |
281 EBreak previousBreakAfterValue) { | |
282 LayoutBox::PaginationBreakability breakability = | 278 LayoutBox::PaginationBreakability breakability = |
283 box.getPaginationBreakability(); | 279 box.getPaginationBreakability(); |
284 | 280 |
285 // Look for breaks before the child box. | 281 // Look for breaks before the child box. |
286 if (isLogicalTopWithinBounds(flowThreadOffset() - box.paginationStrut())) { | 282 if (isLogicalTopWithinBounds(flowThreadOffset() - box.paginationStrut())) { |
287 if (box.needsForcedBreakBefore(previousBreakAfterValue)) { | 283 if (box.needsForcedBreakBefore(previousBreakAfterValue)) { |
288 m_forcedBreaksCount++; | 284 m_forcedBreaksCount++; |
289 } else { | 285 } else { |
290 ASSERT(isFirstAfterBreak(flowThreadOffset()) || !box.paginationStrut()); | 286 ASSERT(isFirstAfterBreak(flowThreadOffset()) || !box.paginationStrut()); |
291 if (isFirstAfterBreak(flowThreadOffset())) { | 287 if (isFirstAfterBreak(flowThreadOffset())) { |
292 // This box is first after a soft break. | 288 // This box is first after a soft break. |
293 LayoutUnit strut = box.paginationStrut(); | 289 LayoutUnit strut = box.paginationStrut(); |
294 // Figure out how much more space we would need to prevent it from being pushed to the next column. | 290 // Figure out how much more space we would need to prevent it from being |
291 // pushed to the next column. | |
295 recordSpaceShortage(box.logicalHeight() - strut); | 292 recordSpaceShortage(box.logicalHeight() - strut); |
296 if (breakability != LayoutBox::ForbidBreaks && | 293 if (breakability != LayoutBox::ForbidBreaks && |
297 m_pendingStrut == LayoutUnit::min()) { | 294 m_pendingStrut == LayoutUnit::min()) { |
298 // We now want to look for the first piece of unbreakable content (e.g . a line or a | 295 // We now want to look for the first piece of unbreakable content |
299 // block-displayed image) inside this block. That ought to be a good c andidate for | 296 // (e.g. a line or a block-displayed image) inside this block. That |
300 // minimum space shortage; a much better one than reporting space shor tage for the | 297 // ought to be a good candidate for minimum space shortage; a much |
301 // entire block (which we'll also do (further down), in case we couldn 't find anything | 298 // better one than reporting space shortage for the entire block |
302 // more suitable). | 299 // (which we'll also do (further down), in case we couldn't find |
300 // anything more suitable). | |
303 m_pendingStrut = strut; | 301 m_pendingStrut = strut; |
304 } | 302 } |
305 } | 303 } |
306 } | 304 } |
307 } | 305 } |
308 | 306 |
309 if (breakability != LayoutBox::ForbidBreaks) { | 307 if (breakability != LayoutBox::ForbidBreaks) { |
310 // See if this breakable box crosses column boundaries. | 308 // See if this breakable box crosses column boundaries. |
311 LayoutUnit bottomInFlowThread = flowThreadOffset() + box.logicalHeight(); | 309 LayoutUnit bottomInFlowThread = flowThreadOffset() + box.logicalHeight(); |
312 const MultiColumnFragmentainerGroup& group = | 310 const MultiColumnFragmentainerGroup& group = |
313 groupAtOffset(flowThreadOffset()); | 311 groupAtOffset(flowThreadOffset()); |
314 if (isFirstAfterBreak(flowThreadOffset()) || | 312 if (isFirstAfterBreak(flowThreadOffset()) || |
315 group.columnLogicalTopForOffset(flowThreadOffset()) != | 313 group.columnLogicalTopForOffset(flowThreadOffset()) != |
316 group.columnLogicalTopForOffset(bottomInFlowThread)) { | 314 group.columnLogicalTopForOffset(bottomInFlowThread)) { |
317 // If the child crosses a column boundary, record space shortage, in case nothing | 315 // If the child crosses a column boundary, record space shortage, in case |
318 // inside it has already done so. The column balancer needs to know by how much it | 316 // nothing inside it has already done so. The column balancer needs to |
319 // has to stretch the columns to make more content fit. If no breaks are r eported | 317 // know by how much it has to stretch the columns to make more content |
320 // (but do occur), the balancer will have no clue. Only measure the space after the | 318 // fit. If no breaks are reported (but do occur), the balancer will have |
321 // last column boundary, in case it crosses more than one. | 319 // no clue. Only measure the space after the last column boundary, in case |
320 // it crosses more than one. | |
322 LayoutUnit spaceUsedInLastColumn = | 321 LayoutUnit spaceUsedInLastColumn = |
323 bottomInFlowThread - | 322 bottomInFlowThread - |
324 group.columnLogicalTopForOffset(bottomInFlowThread); | 323 group.columnLogicalTopForOffset(bottomInFlowThread); |
325 recordSpaceShortage(spaceUsedInLastColumn); | 324 recordSpaceShortage(spaceUsedInLastColumn); |
326 } | 325 } |
327 } | 326 } |
328 | 327 |
329 // If this is an inner multicol container, look for space shortage inside it. | 328 // If this is an inner multicol container, look for space shortage inside it. |
330 if (!box.isLayoutBlockFlow()) | 329 if (!box.isLayoutBlockFlow()) return; |
331 return; | |
332 LayoutMultiColumnFlowThread* flowThread = | 330 LayoutMultiColumnFlowThread* flowThread = |
333 toLayoutBlockFlow(box).multiColumnFlowThread(); | 331 toLayoutBlockFlow(box).multiColumnFlowThread(); |
334 if (!flowThread || flowThread->isLayoutPagedFlowThread()) | 332 if (!flowThread || flowThread->isLayoutPagedFlowThread()) return; |
335 return; | |
336 for (const LayoutMultiColumnSet* columnSet = | 333 for (const LayoutMultiColumnSet* columnSet = |
337 flowThread->firstMultiColumnSet(); | 334 flowThread->firstMultiColumnSet(); |
338 columnSet; columnSet = columnSet->nextSiblingMultiColumnSet()) { | 335 columnSet; columnSet = columnSet->nextSiblingMultiColumnSet()) { |
339 // Establish an inner shortage finder for this column set in the inner multi col | 336 // Establish an inner shortage finder for this column set in the inner |
340 // container. We need to let it walk through all fragmentainer groups in one go, or we'd | 337 // multicol container. We need to let it walk through all fragmentainer |
341 // miss the column boundaries between each fragmentainer group. We need to r ecord space | 338 // groups in one go, or we'd miss the column boundaries between each |
342 // shortage there too. | 339 // fragmentainer group. We need to record space shortage there too. |
343 MinimumSpaceShortageFinder innerFinder( | 340 MinimumSpaceShortageFinder innerFinder( |
344 *columnSet, columnSet->logicalTopInFlowThread(), | 341 *columnSet, columnSet->logicalTopInFlowThread(), |
345 columnSet->logicalBottomInFlowThread()); | 342 columnSet->logicalBottomInFlowThread()); |
346 recordSpaceShortage(innerFinder.minimumSpaceShortage()); | 343 recordSpaceShortage(innerFinder.minimumSpaceShortage()); |
347 } | 344 } |
348 } | 345 } |
349 | 346 |
350 void MinimumSpaceShortageFinder::examineBoxBeforeLeaving(const LayoutBox& box) { | 347 void MinimumSpaceShortageFinder::examineBoxBeforeLeaving(const LayoutBox& box) { |
351 if (m_pendingStrut == LayoutUnit::min() || | 348 if (m_pendingStrut == LayoutUnit::min() || |
352 box.getPaginationBreakability() != LayoutBox::ForbidBreaks) | 349 box.getPaginationBreakability() != LayoutBox::ForbidBreaks) |
353 return; | 350 return; |
354 | 351 |
355 // The previous break was before a breakable block. Here's the first piece of unbreakable | 352 // The previous break was before a breakable block. Here's the first piece of |
356 // content after / inside that block. We want to record the distance from the top of the column | 353 // unbreakable content after / inside that block. We want to record the |
357 // to the bottom of this box as space shortage. | 354 // distance from the top of the column to the bottom of this box as space |
355 // shortage. | |
358 LayoutUnit logicalOffsetFromCurrentColumn = | 356 LayoutUnit logicalOffsetFromCurrentColumn = |
359 offsetFromColumnLogicalTop(flowThreadOffset()); | 357 offsetFromColumnLogicalTop(flowThreadOffset()); |
360 recordSpaceShortage(logicalOffsetFromCurrentColumn + box.logicalHeight() - | 358 recordSpaceShortage(logicalOffsetFromCurrentColumn + box.logicalHeight() - |
361 m_pendingStrut); | 359 m_pendingStrut); |
362 m_pendingStrut = LayoutUnit::min(); | 360 m_pendingStrut = LayoutUnit::min(); |
363 } | 361 } |
364 | 362 |
365 void MinimumSpaceShortageFinder::examineLine(const RootInlineBox& line) { | 363 void MinimumSpaceShortageFinder::examineLine(const RootInlineBox& line) { |
366 LayoutUnit lineTop = line.lineTopWithLeading(); | 364 LayoutUnit lineTop = line.lineTopWithLeading(); |
367 LayoutUnit lineTopInFlowThread = flowThreadOffset() + lineTop; | 365 LayoutUnit lineTopInFlowThread = flowThreadOffset() + lineTop; |
368 LayoutUnit lineHeight = line.lineBottomWithLeading() - lineTop; | 366 LayoutUnit lineHeight = line.lineBottomWithLeading() - lineTop; |
369 if (m_pendingStrut != LayoutUnit::min()) { | 367 if (m_pendingStrut != LayoutUnit::min()) { |
370 // The previous break was before a breakable block. Here's the first line af ter / inside | 368 // The previous break was before a breakable block. Here's the first line |
371 // that block. We want to record the distance from the top of the column to the bottom of | 369 // after / inside that block. We want to record the distance from the top of |
372 // this box as space shortage. | 370 // the column to the bottom of this box as space shortage. |
373 LayoutUnit logicalOffsetFromCurrentColumn = | 371 LayoutUnit logicalOffsetFromCurrentColumn = |
374 offsetFromColumnLogicalTop(lineTopInFlowThread); | 372 offsetFromColumnLogicalTop(lineTopInFlowThread); |
375 recordSpaceShortage(logicalOffsetFromCurrentColumn + lineHeight - | 373 recordSpaceShortage(logicalOffsetFromCurrentColumn + lineHeight - |
376 m_pendingStrut); | 374 m_pendingStrut); |
377 m_pendingStrut = LayoutUnit::min(); | 375 m_pendingStrut = LayoutUnit::min(); |
378 return; | 376 return; |
379 } | 377 } |
380 ASSERT( | 378 ASSERT( |
381 isFirstAfterBreak(lineTopInFlowThread) || !line.paginationStrut() || | 379 isFirstAfterBreak(lineTopInFlowThread) || !line.paginationStrut() || |
382 !isLogicalTopWithinBounds(lineTopInFlowThread - line.paginationStrut())); | 380 !isLogicalTopWithinBounds(lineTopInFlowThread - line.paginationStrut())); |
383 if (isFirstAfterBreak(lineTopInFlowThread)) | 381 if (isFirstAfterBreak(lineTopInFlowThread)) |
384 recordSpaceShortage(lineHeight - line.paginationStrut()); | 382 recordSpaceShortage(lineHeight - line.paginationStrut()); |
385 | 383 |
386 // Even if the line box itself fits fine inside a column, some content may ove rflow the line | 384 // Even if the line box itself fits fine inside a column, some content may |
387 // box bottom (due to restrictive line-height, for instance). We should check if some portion | 385 // overflow the line box bottom (due to restrictive line-height, for |
388 // of said overflow ends up in the next column. That counts as space shortage. | 386 // instance). We should check if some portion of said overflow ends up in the |
387 // next column. That counts as space shortage. | |
389 const MultiColumnFragmentainerGroup& group = | 388 const MultiColumnFragmentainerGroup& group = |
390 groupAtOffset(lineTopInFlowThread); | 389 groupAtOffset(lineTopInFlowThread); |
391 LayoutUnit lineBottomWithOverflow = | 390 LayoutUnit lineBottomWithOverflow = |
392 lineTopInFlowThread + line.lineBottom() - lineTop; | 391 lineTopInFlowThread + line.lineBottom() - lineTop; |
393 if (group.columnLogicalTopForOffset(lineTopInFlowThread) != | 392 if (group.columnLogicalTopForOffset(lineTopInFlowThread) != |
394 group.columnLogicalTopForOffset(lineBottomWithOverflow)) { | 393 group.columnLogicalTopForOffset(lineBottomWithOverflow)) { |
395 LayoutUnit shortage = | 394 LayoutUnit shortage = |
396 lineBottomWithOverflow - | 395 lineBottomWithOverflow - |
397 group.columnLogicalTopForOffset(lineBottomWithOverflow); | 396 group.columnLogicalTopForOffset(lineBottomWithOverflow); |
398 recordSpaceShortage(shortage); | 397 recordSpaceShortage(shortage); |
399 } | 398 } |
400 } | 399 } |
401 | 400 |
402 } // namespace blink | 401 } // namespace blink |
OLD | NEW |