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 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
45 break; | 45 break; |
46 examineLine(*line); | 46 examineLine(*line); |
47 } | 47 } |
48 } | 48 } |
49 | 49 |
50 void ColumnBalancer::traverseChildren(const LayoutObject& object) { | 50 void ColumnBalancer::traverseChildren(const LayoutObject& object) { |
51 // The break-after value from the previous in-flow block-level object to be | 51 // The break-after value from the previous in-flow block-level object to be |
52 // joined with the break-before value of the next in-flow block-level sibling. | 52 // joined with the break-before value of the next in-flow block-level sibling. |
53 EBreak previousBreakAfterValue = BreakAuto; | 53 EBreak previousBreakAfterValue = BreakAuto; |
54 | 54 |
| 55 const LayoutFlowThread* flowThread = columnSet().flowThread(); |
| 56 bool isHorizontalWritingMode = flowThread->isHorizontalWritingMode(); |
| 57 |
55 for (const LayoutObject* child = object.slowFirstChild(); child; | 58 for (const LayoutObject* child = object.slowFirstChild(); child; |
56 child = child->nextSibling()) { | 59 child = child->nextSibling()) { |
57 if (!child->isBox()) { | 60 if (!child->isBox()) { |
58 // Keep traversing inside inlines. There may be floats there. | 61 // Keep traversing inside inlines. There may be floats there. |
59 if (child->isLayoutInline()) | 62 if (child->isLayoutInline()) |
60 traverseChildren(*child); | 63 traverseChildren(*child); |
61 continue; | 64 continue; |
62 } | 65 } |
63 | 66 |
64 const LayoutBox& childBox = toLayoutBox(*child); | 67 const LayoutBox& childBox = toLayoutBox(*child); |
65 | 68 LayoutRect overflowRect = childBox.layoutOverflowRect(); |
66 LayoutUnit borderEdgeOffset; | 69 LayoutUnit childLogicalBottomWithOverflow = |
67 LayoutUnit logicalTop = childBox.logicalTop(); | 70 childBox.logicalTop() + |
68 LayoutUnit logicalHeight = childBox.logicalHeightWithVisibleOverflow(); | 71 (isHorizontalWritingMode ? overflowRect.maxY() : overflowRect.maxX()); |
69 // Floats' margins don't collapse with column boundaries, and we don't want | 72 if (m_flowThreadOffset + childLogicalBottomWithOverflow <= |
70 // to break inside them, or separate them from the float's border box. Set | |
71 // the offset to the margin-before edge (rather than border-before edge), | |
72 // and include the block direction margins in the child height. | |
73 if (childBox.isFloating()) { | |
74 LayoutUnit marginBefore = childBox.marginBefore(object.style()); | |
75 LayoutUnit marginAfter = childBox.marginAfter(object.style()); | |
76 logicalHeight = | |
77 std::max(logicalHeight, childBox.logicalHeight() + marginAfter); | |
78 logicalTop -= marginBefore; | |
79 logicalHeight += marginBefore; | |
80 | |
81 // As soon as we want to process content inside this child, though, we | |
82 // need to get to its border-before edge. | |
83 borderEdgeOffset = marginBefore; | |
84 } | |
85 | |
86 if (m_flowThreadOffset + logicalTop + logicalHeight <= | |
87 logicalTopInFlowThread()) { | 73 logicalTopInFlowThread()) { |
88 // This child is fully above the flow thread portion we're examining. | 74 // This child is fully above the flow thread portion we're examining. |
89 continue; | 75 continue; |
90 } | 76 } |
91 if (m_flowThreadOffset + logicalTop >= logicalBottomInFlowThread()) { | 77 LayoutUnit childLogicalTopWithOverflow = |
| 78 childBox.logicalTop() + |
| 79 (isHorizontalWritingMode ? overflowRect.y() : overflowRect.x()); |
| 80 if (m_flowThreadOffset + childLogicalTopWithOverflow >= |
| 81 logicalBottomInFlowThread()) { |
92 // This child is fully below the flow thread portion we're examining. We | 82 // This child is fully below the flow thread portion we're examining. We |
93 // cannot just stop here, though, thanks to negative margins. | 83 // cannot just stop here, though, thanks to negative margins. |
94 // So keep looking. | 84 // So keep looking. |
95 continue; | 85 continue; |
96 } | 86 } |
97 if (childBox.isOutOfFlowPositioned() || childBox.isColumnSpanAll()) | 87 if (childBox.isOutOfFlowPositioned() || childBox.isColumnSpanAll()) |
98 continue; | 88 continue; |
99 | 89 |
100 // Tables are wicked. Both table rows and table cells are relative to their | 90 // Tables are wicked. Both table rows and table cells are relative to their |
101 // table section. | 91 // table section. |
102 LayoutUnit offsetForThisChild = | 92 LayoutUnit offsetForThisChild = |
103 childBox.isTableRow() ? LayoutUnit() : logicalTop; | 93 childBox.isTableRow() ? LayoutUnit() : childBox.logicalTop(); |
104 | |
105 m_flowThreadOffset += offsetForThisChild; | 94 m_flowThreadOffset += offsetForThisChild; |
106 | 95 |
107 examineBoxAfterEntering(childBox, logicalHeight, previousBreakAfterValue); | 96 examineBoxAfterEntering(childBox, previousBreakAfterValue); |
108 // Unless the child is unsplittable, or if the child establishes an inner | 97 // Unless the child is unsplittable, or if the child establishes an inner |
109 // multicol container, we descend into its subtree for further examination. | 98 // multicol container, we descend into its subtree for further examination. |
110 if (childBox.getPaginationBreakability() != LayoutBox::ForbidBreaks && | 99 if (childBox.getPaginationBreakability() != LayoutBox::ForbidBreaks && |
111 (!childBox.isLayoutBlockFlow() || | 100 (!childBox.isLayoutBlockFlow() || |
112 !toLayoutBlockFlow(childBox).multiColumnFlowThread())) { | 101 !toLayoutBlockFlow(childBox).multiColumnFlowThread())) |
113 // We need to get to the border edge before processing content inside | |
114 // this child. If the child is floated, we're currently at the margin | |
115 // edge. | |
116 m_flowThreadOffset += borderEdgeOffset; | |
117 traverseSubtree(childBox); | 102 traverseSubtree(childBox); |
118 m_flowThreadOffset -= borderEdgeOffset; | |
119 } | |
120 previousBreakAfterValue = childBox.breakAfter(); | 103 previousBreakAfterValue = childBox.breakAfter(); |
121 examineBoxBeforeLeaving(childBox, logicalHeight); | 104 examineBoxBeforeLeaving(childBox); |
122 | 105 |
123 m_flowThreadOffset -= offsetForThisChild; | 106 m_flowThreadOffset -= offsetForThisChild; |
124 } | 107 } |
125 } | 108 } |
126 | 109 |
127 InitialColumnHeightFinder::InitialColumnHeightFinder( | 110 InitialColumnHeightFinder::InitialColumnHeightFinder( |
128 const LayoutMultiColumnSet& columnSet, | 111 const LayoutMultiColumnSet& columnSet, |
129 LayoutUnit logicalTopInFlowThread, | 112 LayoutUnit logicalTopInFlowThread, |
130 LayoutUnit logicalBottomInFlowThread) | 113 LayoutUnit logicalBottomInFlowThread) |
131 : ColumnBalancer(columnSet, | 114 : ColumnBalancer(columnSet, |
(...skipping 12 matching lines...) Expand all Loading... |
144 | 127 |
145 LayoutUnit InitialColumnHeightFinder::initialMinimalBalancedHeight() const { | 128 LayoutUnit InitialColumnHeightFinder::initialMinimalBalancedHeight() const { |
146 unsigned index = contentRunIndexWithTallestColumns(); | 129 unsigned index = contentRunIndexWithTallestColumns(); |
147 LayoutUnit startOffset = index > 0 ? m_contentRuns[index - 1].breakOffset() | 130 LayoutUnit startOffset = index > 0 ? m_contentRuns[index - 1].breakOffset() |
148 : logicalTopInFlowThread(); | 131 : logicalTopInFlowThread(); |
149 return m_contentRuns[index].columnLogicalHeight(startOffset); | 132 return m_contentRuns[index].columnLogicalHeight(startOffset); |
150 } | 133 } |
151 | 134 |
152 void InitialColumnHeightFinder::examineBoxAfterEntering( | 135 void InitialColumnHeightFinder::examineBoxAfterEntering( |
153 const LayoutBox& box, | 136 const LayoutBox& box, |
154 LayoutUnit childLogicalHeight, | |
155 EBreak previousBreakAfterValue) { | 137 EBreak previousBreakAfterValue) { |
156 if (isLogicalTopWithinBounds(flowThreadOffset() - box.paginationStrut())) { | 138 if (isLogicalTopWithinBounds(flowThreadOffset() - box.paginationStrut())) { |
157 if (box.needsForcedBreakBefore(previousBreakAfterValue)) { | 139 if (box.needsForcedBreakBefore(previousBreakAfterValue)) { |
158 addContentRun(flowThreadOffset()); | 140 addContentRun(flowThreadOffset()); |
159 } else { | 141 } else { |
160 if (isFirstAfterBreak(flowThreadOffset())) { | 142 if (isFirstAfterBreak(flowThreadOffset())) { |
161 // This box is first after a soft break. | 143 // This box is first after a soft break. |
162 recordStrutBeforeOffset(flowThreadOffset(), box.paginationStrut()); | 144 recordStrutBeforeOffset(flowThreadOffset(), box.paginationStrut()); |
163 } | 145 } |
164 } | 146 } |
165 } | 147 } |
166 | 148 |
167 if (box.getPaginationBreakability() != LayoutBox::AllowAnyBreaks) { | 149 if (box.getPaginationBreakability() != LayoutBox::AllowAnyBreaks) { |
| 150 LayoutUnit unsplittableLogicalHeight = box.logicalHeight(); |
| 151 if (box.isFloating()) |
| 152 unsplittableLogicalHeight += box.marginBefore() + box.marginAfter(); |
168 m_tallestUnbreakableLogicalHeight = | 153 m_tallestUnbreakableLogicalHeight = |
169 std::max(m_tallestUnbreakableLogicalHeight, childLogicalHeight); | 154 std::max(m_tallestUnbreakableLogicalHeight, unsplittableLogicalHeight); |
170 return; | 155 return; |
171 } | 156 } |
172 // Need to examine inner multicol containers to find their tallest unbreakable | 157 // Need to examine inner multicol containers to find their tallest unbreakable |
173 // piece of content. | 158 // piece of content. |
174 if (!box.isLayoutBlockFlow()) | 159 if (!box.isLayoutBlockFlow()) |
175 return; | 160 return; |
176 LayoutMultiColumnFlowThread* innerFlowThread = | 161 LayoutMultiColumnFlowThread* innerFlowThread = |
177 toLayoutBlockFlow(box).multiColumnFlowThread(); | 162 toLayoutBlockFlow(box).multiColumnFlowThread(); |
178 if (!innerFlowThread || innerFlowThread->isLayoutPagedFlowThread()) | 163 if (!innerFlowThread || innerFlowThread->isLayoutPagedFlowThread()) |
179 return; | 164 return; |
180 LayoutUnit offsetInInnerFlowThread = | 165 LayoutUnit offsetInInnerFlowThread = |
181 flowThreadOffset() - | 166 flowThreadOffset() - |
182 innerFlowThread->blockOffsetInEnclosingFragmentationContext(); | 167 innerFlowThread->blockOffsetInEnclosingFragmentationContext(); |
183 LayoutUnit innerUnbreakableHeight = | 168 LayoutUnit innerUnbreakableHeight = |
184 innerFlowThread->tallestUnbreakableLogicalHeight(offsetInInnerFlowThread); | 169 innerFlowThread->tallestUnbreakableLogicalHeight(offsetInInnerFlowThread); |
185 m_tallestUnbreakableLogicalHeight = | 170 m_tallestUnbreakableLogicalHeight = |
186 std::max(m_tallestUnbreakableLogicalHeight, innerUnbreakableHeight); | 171 std::max(m_tallestUnbreakableLogicalHeight, innerUnbreakableHeight); |
187 } | 172 } |
188 | 173 |
189 void InitialColumnHeightFinder::examineBoxBeforeLeaving( | 174 void InitialColumnHeightFinder::examineBoxBeforeLeaving(const LayoutBox& box) {} |
190 const LayoutBox& box, | |
191 LayoutUnit childLogicalHeight) {} | |
192 | 175 |
193 static inline LayoutUnit columnLogicalHeightRequirementForLine( | 176 static inline LayoutUnit columnLogicalHeightRequirementForLine( |
194 const ComputedStyle& style, | 177 const ComputedStyle& style, |
195 const RootInlineBox& lastLine) { | 178 const RootInlineBox& lastLine) { |
196 // We may require a certain minimum number of lines per page in order to | 179 // We may require a certain minimum number of lines per page in order to |
197 // satisfy orphans and widows, and that may affect the minimum page height. | 180 // satisfy orphans and widows, and that may affect the minimum page height. |
198 unsigned minimumLineCount = | 181 unsigned minimumLineCount = |
199 std::max<unsigned>(style.orphans(), style.widows()); | 182 std::max<unsigned>(style.orphans(), style.widows()); |
200 const RootInlineBox* firstLine = &lastLine; | 183 const RootInlineBox* firstLine = &lastLine; |
201 for (unsigned i = 1; i < minimumLineCount && firstLine->prevRootBox(); i++) | 184 for (unsigned i = 1; i < minimumLineCount && firstLine->prevRootBox(); i++) |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
306 logicalTopInFlowThread, | 289 logicalTopInFlowThread, |
307 logicalBottomInFlowThread), | 290 logicalBottomInFlowThread), |
308 m_minimumSpaceShortage(LayoutUnit::max()), | 291 m_minimumSpaceShortage(LayoutUnit::max()), |
309 m_pendingStrut(LayoutUnit::min()), | 292 m_pendingStrut(LayoutUnit::min()), |
310 m_forcedBreaksCount(0) { | 293 m_forcedBreaksCount(0) { |
311 traverse(); | 294 traverse(); |
312 } | 295 } |
313 | 296 |
314 void MinimumSpaceShortageFinder::examineBoxAfterEntering( | 297 void MinimumSpaceShortageFinder::examineBoxAfterEntering( |
315 const LayoutBox& box, | 298 const LayoutBox& box, |
316 LayoutUnit childLogicalHeight, | |
317 EBreak previousBreakAfterValue) { | 299 EBreak previousBreakAfterValue) { |
318 LayoutBox::PaginationBreakability breakability = | 300 LayoutBox::PaginationBreakability breakability = |
319 box.getPaginationBreakability(); | 301 box.getPaginationBreakability(); |
320 | 302 |
321 // Look for breaks before the child box. | 303 // Look for breaks before the child box. |
322 if (isLogicalTopWithinBounds(flowThreadOffset() - box.paginationStrut())) { | 304 if (isLogicalTopWithinBounds(flowThreadOffset() - box.paginationStrut())) { |
323 if (box.needsForcedBreakBefore(previousBreakAfterValue)) { | 305 if (box.needsForcedBreakBefore(previousBreakAfterValue)) { |
324 m_forcedBreaksCount++; | 306 m_forcedBreaksCount++; |
325 } else { | 307 } else { |
326 if (isFirstAfterBreak(flowThreadOffset())) { | 308 if (isFirstAfterBreak(flowThreadOffset())) { |
327 // This box is first after a soft break. | 309 // This box is first after a soft break. |
328 LayoutUnit strut = box.paginationStrut(); | 310 LayoutUnit strut = box.paginationStrut(); |
329 // Figure out how much more space we would need to prevent it from being | 311 // Figure out how much more space we would need to prevent it from being |
330 // pushed to the next column. | 312 // pushed to the next column. |
331 recordSpaceShortage(childLogicalHeight - strut); | 313 recordSpaceShortage(box.logicalHeight() - strut); |
332 if (breakability != LayoutBox::ForbidBreaks && | 314 if (breakability != LayoutBox::ForbidBreaks && |
333 m_pendingStrut == LayoutUnit::min()) { | 315 m_pendingStrut == LayoutUnit::min()) { |
334 // We now want to look for the first piece of unbreakable content | 316 // We now want to look for the first piece of unbreakable content |
335 // (e.g. a line or a block-displayed image) inside this block. That | 317 // (e.g. a line or a block-displayed image) inside this block. That |
336 // ought to be a good candidate for minimum space shortage; a much | 318 // ought to be a good candidate for minimum space shortage; a much |
337 // better one than reporting space shortage for the entire block | 319 // better one than reporting space shortage for the entire block |
338 // (which we'll also do (further down), in case we couldn't find | 320 // (which we'll also do (further down), in case we couldn't find |
339 // anything more suitable). | 321 // anything more suitable). |
340 m_pendingStrut = strut; | 322 m_pendingStrut = strut; |
341 } | 323 } |
342 } | 324 } |
343 } | 325 } |
344 } | 326 } |
345 | 327 |
346 if (breakability != LayoutBox::ForbidBreaks) { | 328 if (breakability != LayoutBox::ForbidBreaks) { |
347 // See if this breakable box crosses column boundaries. | 329 // See if this breakable box crosses column boundaries. |
348 LayoutUnit bottomInFlowThread = flowThreadOffset() + childLogicalHeight; | 330 LayoutUnit bottomInFlowThread = flowThreadOffset() + box.logicalHeight(); |
349 const MultiColumnFragmentainerGroup& group = | 331 const MultiColumnFragmentainerGroup& group = |
350 groupAtOffset(flowThreadOffset()); | 332 groupAtOffset(flowThreadOffset()); |
351 if (isFirstAfterBreak(flowThreadOffset()) || | 333 if (isFirstAfterBreak(flowThreadOffset()) || |
352 group.columnLogicalTopForOffset(flowThreadOffset()) != | 334 group.columnLogicalTopForOffset(flowThreadOffset()) != |
353 group.columnLogicalTopForOffset(bottomInFlowThread)) { | 335 group.columnLogicalTopForOffset(bottomInFlowThread)) { |
354 // If the child crosses a column boundary, record space shortage, in case | 336 // If the child crosses a column boundary, record space shortage, in case |
355 // nothing inside it has already done so. The column balancer needs to | 337 // nothing inside it has already done so. The column balancer needs to |
356 // know by how much it has to stretch the columns to make more content | 338 // know by how much it has to stretch the columns to make more content |
357 // fit. If no breaks are reported (but do occur), the balancer will have | 339 // fit. If no breaks are reported (but do occur), the balancer will have |
358 // no clue. Only measure the space after the last column boundary, in case | 340 // no clue. Only measure the space after the last column boundary, in case |
(...skipping 19 matching lines...) Expand all Loading... |
378 // multicol container. We need to let it walk through all fragmentainer | 360 // multicol container. We need to let it walk through all fragmentainer |
379 // groups in one go, or we'd miss the column boundaries between each | 361 // groups in one go, or we'd miss the column boundaries between each |
380 // fragmentainer group. We need to record space shortage there too. | 362 // fragmentainer group. We need to record space shortage there too. |
381 MinimumSpaceShortageFinder innerFinder( | 363 MinimumSpaceShortageFinder innerFinder( |
382 *columnSet, columnSet->logicalTopInFlowThread(), | 364 *columnSet, columnSet->logicalTopInFlowThread(), |
383 columnSet->logicalBottomInFlowThread()); | 365 columnSet->logicalBottomInFlowThread()); |
384 recordSpaceShortage(innerFinder.minimumSpaceShortage()); | 366 recordSpaceShortage(innerFinder.minimumSpaceShortage()); |
385 } | 367 } |
386 } | 368 } |
387 | 369 |
388 void MinimumSpaceShortageFinder::examineBoxBeforeLeaving( | 370 void MinimumSpaceShortageFinder::examineBoxBeforeLeaving(const LayoutBox& box) { |
389 const LayoutBox& box, | |
390 LayoutUnit childLogicalHeight) { | |
391 if (m_pendingStrut == LayoutUnit::min() || | 371 if (m_pendingStrut == LayoutUnit::min() || |
392 box.getPaginationBreakability() != LayoutBox::ForbidBreaks) | 372 box.getPaginationBreakability() != LayoutBox::ForbidBreaks) |
393 return; | 373 return; |
394 | 374 |
395 // The previous break was before a breakable block. Here's the first piece of | 375 // The previous break was before a breakable block. Here's the first piece of |
396 // unbreakable content after / inside that block. We want to record the | 376 // unbreakable content after / inside that block. We want to record the |
397 // distance from the top of the column to the bottom of this box as space | 377 // distance from the top of the column to the bottom of this box as space |
398 // shortage. | 378 // shortage. |
399 LayoutUnit logicalOffsetFromCurrentColumn = | 379 LayoutUnit logicalOffsetFromCurrentColumn = |
400 offsetFromColumnLogicalTop(flowThreadOffset()); | 380 offsetFromColumnLogicalTop(flowThreadOffset()); |
401 recordSpaceShortage(logicalOffsetFromCurrentColumn + childLogicalHeight - | 381 recordSpaceShortage(logicalOffsetFromCurrentColumn + box.logicalHeight() - |
402 m_pendingStrut); | 382 m_pendingStrut); |
403 m_pendingStrut = LayoutUnit::min(); | 383 m_pendingStrut = LayoutUnit::min(); |
404 } | 384 } |
405 | 385 |
406 void MinimumSpaceShortageFinder::examineLine(const RootInlineBox& line) { | 386 void MinimumSpaceShortageFinder::examineLine(const RootInlineBox& line) { |
407 LayoutUnit lineTop = line.lineTopWithLeading(); | 387 LayoutUnit lineTop = line.lineTopWithLeading(); |
408 LayoutUnit lineTopInFlowThread = flowThreadOffset() + lineTop; | 388 LayoutUnit lineTopInFlowThread = flowThreadOffset() + lineTop; |
409 LayoutUnit lineHeight = line.lineBottomWithLeading() - lineTop; | 389 LayoutUnit lineHeight = line.lineBottomWithLeading() - lineTop; |
410 if (m_pendingStrut != LayoutUnit::min()) { | 390 if (m_pendingStrut != LayoutUnit::min()) { |
411 // The previous break was before a breakable block. Here's the first line | 391 // The previous break was before a breakable block. Here's the first line |
(...skipping 23 matching lines...) Expand all Loading... |
435 if (group.columnLogicalTopForOffset(lineTopInFlowThread) != | 415 if (group.columnLogicalTopForOffset(lineTopInFlowThread) != |
436 group.columnLogicalTopForOffset(lineBottomWithOverflow)) { | 416 group.columnLogicalTopForOffset(lineBottomWithOverflow)) { |
437 LayoutUnit shortage = | 417 LayoutUnit shortage = |
438 lineBottomWithOverflow - | 418 lineBottomWithOverflow - |
439 group.columnLogicalTopForOffset(lineBottomWithOverflow); | 419 group.columnLogicalTopForOffset(lineBottomWithOverflow); |
440 recordSpaceShortage(shortage); | 420 recordSpaceShortage(shortage); |
441 } | 421 } |
442 } | 422 } |
443 | 423 |
444 } // namespace blink | 424 } // namespace blink |
OLD | NEW |