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