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 |
11 namespace blink { | 11 namespace blink { |
12 | 12 |
13 ColumnBalancer::ColumnBalancer(const LayoutMultiColumnSet& columnSet, | 13 ColumnBalancer::ColumnBalancer(const LayoutMultiColumnSet& columnSet, |
14 LayoutUnit logicalTopInFlowThread, | 14 LayoutUnit logicalTopInFlowThread, |
15 LayoutUnit logicalBottomInFlowThread) | 15 LayoutUnit logicalBottomInFlowThread) |
16 : m_columnSet(columnSet), | 16 : m_columnSet(columnSet), |
17 m_logicalTopInFlowThread(logicalTopInFlowThread), | 17 m_logicalTopInFlowThread(logicalTopInFlowThread), |
18 m_logicalBottomInFlowThread(logicalBottomInFlowThread) {} | 18 m_logicalBottomInFlowThread(logicalBottomInFlowThread) { |
| 19 DCHECK_GE(columnSet.usedColumnCount(), 1U); |
| 20 } |
19 | 21 |
20 void ColumnBalancer::traverse() { | 22 void ColumnBalancer::traverse() { |
21 traverseSubtree(*columnSet().flowThread()); | 23 traverseSubtree(*columnSet().flowThread()); |
22 ASSERT(!flowThreadOffset()); | 24 ASSERT(!flowThreadOffset()); |
23 } | 25 } |
24 | 26 |
25 void ColumnBalancer::traverseSubtree(const LayoutBox& box) { | 27 void ColumnBalancer::traverseSubtree(const LayoutBox& box) { |
26 if (box.childrenInline() && box.isLayoutBlockFlow()) { | 28 if (box.childrenInline() && box.isLayoutBlockFlow()) { |
27 // Look for breaks between lines. | 29 // Look for breaks between lines. |
28 traverseLines(toLayoutBlockFlow(box)); | 30 traverseLines(toLayoutBlockFlow(box)); |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
136 strut = LayoutUnit::max(); | 138 strut = LayoutUnit::max(); |
137 traverse(); | 139 traverse(); |
138 // We have now found each explicit / forced break, and their location. Now we | 140 // We have now found each explicit / forced break, and their location. Now we |
139 // need to figure out how many additional implicit / soft breaks we need and | 141 // need to figure out how many additional implicit / soft breaks we need and |
140 // guess where they will occur, in order | 142 // guess where they will occur, in order |
141 // to provide an initial column height. | 143 // to provide an initial column height. |
142 distributeImplicitBreaks(); | 144 distributeImplicitBreaks(); |
143 } | 145 } |
144 | 146 |
145 LayoutUnit InitialColumnHeightFinder::initialMinimalBalancedHeight() const { | 147 LayoutUnit InitialColumnHeightFinder::initialMinimalBalancedHeight() const { |
| 148 LayoutUnit rowLogicalTop; |
| 149 if (m_contentRuns.size() > columnSet().usedColumnCount()) { |
| 150 // We have not inserted additional fragmentainer groups yet (because we |
| 151 // aren't able to calculate their constraints yet), but we already know for |
| 152 // sure that there'll be more than one of them, due to the number of forced |
| 153 // breaks in a nested multicol container. We will now attempt to take all |
| 154 // the imaginary rows into account and calculate a minimal balanced logical |
| 155 // height for everything. |
| 156 unsigned stride = columnSet().usedColumnCount(); |
| 157 LayoutUnit rowStartOffset = logicalTopInFlowThread(); |
| 158 for (unsigned i = 0; i < firstContentRunIndexInLastRow(); i += stride) { |
| 159 LayoutUnit rowEndOffset = m_contentRuns[i + stride - 1].breakOffset(); |
| 160 float rowHeight = float(rowEndOffset - rowStartOffset) / float(stride); |
| 161 rowLogicalTop += LayoutUnit::fromFloatCeil(rowHeight); |
| 162 rowStartOffset = rowEndOffset; |
| 163 } |
| 164 } |
146 unsigned index = contentRunIndexWithTallestColumns(); | 165 unsigned index = contentRunIndexWithTallestColumns(); |
147 LayoutUnit startOffset = index > 0 ? m_contentRuns[index - 1].breakOffset() | 166 LayoutUnit startOffset = index > 0 ? m_contentRuns[index - 1].breakOffset() |
148 : logicalTopInFlowThread(); | 167 : logicalTopInFlowThread(); |
149 return m_contentRuns[index].columnLogicalHeight(startOffset); | 168 LayoutUnit height = m_contentRuns[index].columnLogicalHeight(startOffset); |
| 169 return rowLogicalTop + std::max(height, m_tallestUnbreakableLogicalHeight); |
150 } | 170 } |
151 | 171 |
152 void InitialColumnHeightFinder::examineBoxAfterEntering( | 172 void InitialColumnHeightFinder::examineBoxAfterEntering( |
153 const LayoutBox& box, | 173 const LayoutBox& box, |
154 LayoutUnit childLogicalHeight, | 174 LayoutUnit childLogicalHeight, |
155 EBreak previousBreakAfterValue) { | 175 EBreak previousBreakAfterValue) { |
156 if (isLogicalTopWithinBounds(flowThreadOffset() - box.paginationStrut())) { | 176 if (isLogicalTopWithinBounds(flowThreadOffset() - box.paginationStrut())) { |
157 if (box.needsForcedBreakBefore(previousBreakAfterValue)) { | 177 if (box.needsForcedBreakBefore(previousBreakAfterValue)) { |
158 addContentRun(flowThreadOffset()); | 178 addContentRun(flowThreadOffset()); |
159 } else { | 179 } else { |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
213 ASSERT( | 233 ASSERT( |
214 isFirstAfterBreak(lineTopInFlowThread) || !line.paginationStrut() || | 234 isFirstAfterBreak(lineTopInFlowThread) || !line.paginationStrut() || |
215 !isLogicalTopWithinBounds(lineTopInFlowThread - line.paginationStrut())); | 235 !isLogicalTopWithinBounds(lineTopInFlowThread - line.paginationStrut())); |
216 if (isFirstAfterBreak(lineTopInFlowThread)) | 236 if (isFirstAfterBreak(lineTopInFlowThread)) |
217 recordStrutBeforeOffset(lineTopInFlowThread, line.paginationStrut()); | 237 recordStrutBeforeOffset(lineTopInFlowThread, line.paginationStrut()); |
218 } | 238 } |
219 | 239 |
220 void InitialColumnHeightFinder::recordStrutBeforeOffset( | 240 void InitialColumnHeightFinder::recordStrutBeforeOffset( |
221 LayoutUnit offsetInFlowThread, | 241 LayoutUnit offsetInFlowThread, |
222 LayoutUnit strut) { | 242 LayoutUnit strut) { |
223 ASSERT(columnSet().usedColumnCount() >= 1); | |
224 unsigned columnCount = columnSet().usedColumnCount(); | 243 unsigned columnCount = columnSet().usedColumnCount(); |
225 ASSERT(m_shortestStruts.size() == columnCount); | 244 ASSERT(m_shortestStruts.size() == columnCount); |
226 unsigned index = groupAtOffset(offsetInFlowThread) | 245 unsigned index = groupAtOffset(offsetInFlowThread) |
227 .columnIndexAtOffset(offsetInFlowThread - strut, | 246 .columnIndexAtOffset(offsetInFlowThread - strut, |
228 LayoutBox::AssociateWithLatterPage); | 247 LayoutBox::AssociateWithLatterPage); |
229 if (index >= columnCount) | 248 if (index >= columnCount) |
230 return; | 249 return; |
231 m_shortestStruts[index] = std::min(m_shortestStruts[index], strut); | 250 m_shortestStruts[index] = std::min(m_shortestStruts[index], strut); |
232 } | 251 } |
233 | 252 |
(...skipping 14 matching lines...) Expand all Loading... |
248 return totalStrutSpace; | 267 return totalStrutSpace; |
249 } | 268 } |
250 | 269 |
251 void InitialColumnHeightFinder::addContentRun( | 270 void InitialColumnHeightFinder::addContentRun( |
252 LayoutUnit endOffsetInFlowThread) { | 271 LayoutUnit endOffsetInFlowThread) { |
253 endOffsetInFlowThread -= spaceUsedByStrutsAt(endOffsetInFlowThread); | 272 endOffsetInFlowThread -= spaceUsedByStrutsAt(endOffsetInFlowThread); |
254 if (!m_contentRuns.isEmpty() && | 273 if (!m_contentRuns.isEmpty() && |
255 endOffsetInFlowThread <= m_contentRuns.last().breakOffset()) | 274 endOffsetInFlowThread <= m_contentRuns.last().breakOffset()) |
256 return; | 275 return; |
257 // Append another item as long as we haven't exceeded used column count. What | 276 // Append another item as long as we haven't exceeded used column count. What |
258 // ends up in the overflow area shouldn't affect column balancing. | 277 // ends up in the overflow area shouldn't affect column balancing. However, if |
259 if (m_contentRuns.size() < columnSet().usedColumnCount()) | 278 // we're in a nested fragmentation context, we may still need to record all |
260 m_contentRuns.append(ContentRun(endOffsetInFlowThread)); | 279 // runs, since there'll be no overflow area in the inline direction then, but |
| 280 // rather additional rows of columns in multiple outer fragmentainers. |
| 281 if (m_contentRuns.size() >= columnSet().usedColumnCount()) { |
| 282 const auto* flowThread = columnSet().multiColumnFlowThread(); |
| 283 if (!flowThread->enclosingFragmentationContext() || |
| 284 columnSet().newFragmentainerGroupsAllowed()) |
| 285 return; |
| 286 } |
| 287 m_contentRuns.append(ContentRun(endOffsetInFlowThread)); |
261 } | 288 } |
262 | 289 |
263 unsigned InitialColumnHeightFinder::contentRunIndexWithTallestColumns() const { | 290 unsigned InitialColumnHeightFinder::contentRunIndexWithTallestColumns() const { |
264 unsigned indexWithLargestHeight = 0; | 291 unsigned indexWithLargestHeight = 0; |
265 LayoutUnit largestHeight; | 292 LayoutUnit largestHeight; |
266 LayoutUnit previousOffset = logicalTopInFlowThread(); | 293 LayoutUnit previousOffset = logicalTopInFlowThread(); |
267 size_t runCount = m_contentRuns.size(); | 294 size_t runCount = m_contentRuns.size(); |
268 ASSERT(runCount); | 295 ASSERT(runCount); |
269 for (size_t i = 0; i < runCount; i++) { | 296 for (size_t i = firstContentRunIndexInLastRow(); i < runCount; i++) { |
270 const ContentRun& run = m_contentRuns[i]; | 297 const ContentRun& run = m_contentRuns[i]; |
271 LayoutUnit height = run.columnLogicalHeight(previousOffset); | 298 LayoutUnit height = run.columnLogicalHeight(previousOffset); |
272 if (largestHeight < height) { | 299 if (largestHeight < height) { |
273 largestHeight = height; | 300 largestHeight = height; |
274 indexWithLargestHeight = i; | 301 indexWithLargestHeight = i; |
275 } | 302 } |
276 previousOffset = run.breakOffset(); | 303 previousOffset = run.breakOffset(); |
277 } | 304 } |
278 return indexWithLargestHeight; | 305 return indexWithLargestHeight; |
279 } | 306 } |
280 | 307 |
281 void InitialColumnHeightFinder::distributeImplicitBreaks() { | 308 void InitialColumnHeightFinder::distributeImplicitBreaks() { |
282 // Insert a final content run to encompass all content. This will include | 309 // Insert a final content run to encompass all content. This will include |
283 // overflow if we're at the end of the multicol container. | 310 // overflow if we're at the end of the multicol container. |
284 addContentRun(logicalBottomInFlowThread()); | 311 addContentRun(logicalBottomInFlowThread()); |
285 unsigned columnCount = m_contentRuns.size(); | 312 unsigned columnCount = m_contentRuns.size(); |
286 | 313 |
287 // If there is room for more breaks (to reach the used value of column-count), | 314 // If there is room for more breaks (to reach the used value of column-count), |
288 // imagine that we insert implicit breaks at suitable locations. At any given | 315 // imagine that we insert implicit breaks at suitable locations. At any given |
289 // time, the content run with the currently tallest columns will get another | 316 // time, the content run with the currently tallest columns will get another |
290 // implicit break "inserted", which will increase its column count by one and | 317 // implicit break "inserted", which will increase its column count by one and |
291 // shrink its columns' height. Repeat until we have the desired total number | 318 // shrink its columns' height. Repeat until we have the desired total number |
292 // of breaks. The largest column height among the runs will then be the | 319 // of breaks. The largest column height among the runs will then be the |
293 // initial column height for the balancer to use. | 320 // initial column height for the balancer to use. |
| 321 if (columnCount > columnSet().usedColumnCount()) { |
| 322 // If we exceed used column-count (which we are allowed to do if we're at |
| 323 // the initial balancing pass for a multicol that lives inside another |
| 324 // to-be-balanced outer multicol container), we only care about content that |
| 325 // could end up in the last row. We need to pad up the number of columns, so |
| 326 // that all rows will contain as many columns as used column-count dictates. |
| 327 columnCount %= columnSet().usedColumnCount(); |
| 328 // If there are just enough explicit breaks to fill all rows with the right |
| 329 // amount of columns, we won't be needing any implicit breaks. |
| 330 if (!columnCount) |
| 331 return; |
| 332 } |
294 while (columnCount < columnSet().usedColumnCount()) { | 333 while (columnCount < columnSet().usedColumnCount()) { |
295 unsigned index = contentRunIndexWithTallestColumns(); | 334 unsigned index = contentRunIndexWithTallestColumns(); |
296 m_contentRuns[index].assumeAnotherImplicitBreak(); | 335 m_contentRuns[index].assumeAnotherImplicitBreak(); |
297 columnCount++; | 336 columnCount++; |
298 } | 337 } |
299 } | 338 } |
300 | 339 |
301 MinimumSpaceShortageFinder::MinimumSpaceShortageFinder( | 340 MinimumSpaceShortageFinder::MinimumSpaceShortageFinder( |
302 const LayoutMultiColumnSet& columnSet, | 341 const LayoutMultiColumnSet& columnSet, |
303 LayoutUnit logicalTopInFlowThread, | 342 LayoutUnit logicalTopInFlowThread, |
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
435 if (group.columnLogicalTopForOffset(lineTopInFlowThread) != | 474 if (group.columnLogicalTopForOffset(lineTopInFlowThread) != |
436 group.columnLogicalTopForOffset(lineBottomWithOverflow)) { | 475 group.columnLogicalTopForOffset(lineBottomWithOverflow)) { |
437 LayoutUnit shortage = | 476 LayoutUnit shortage = |
438 lineBottomWithOverflow - | 477 lineBottomWithOverflow - |
439 group.columnLogicalTopForOffset(lineBottomWithOverflow); | 478 group.columnLogicalTopForOffset(lineBottomWithOverflow); |
440 recordSpaceShortage(shortage); | 479 recordSpaceShortage(shortage); |
441 } | 480 } |
442 } | 481 } |
443 | 482 |
444 } // namespace blink | 483 } // namespace blink |
OLD | NEW |