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, LayoutUnit
logicalTopInFlowThread, LayoutUnit logicalBottomInFlowThread) | 13 ColumnBalancer::ColumnBalancer(const LayoutMultiColumnSet& columnSet, LayoutUnit
logicalTopInFlowThread, LayoutUnit logicalBottomInFlowThread) |
14 : m_columnSet(columnSet) | 14 : m_columnSet(columnSet) |
15 , m_logicalTopInFlowThread(logicalTopInFlowThread) | 15 , m_logicalTopInFlowThread(logicalTopInFlowThread) |
16 , m_logicalBottomInFlowThread(logicalBottomInFlowThread) | 16 , m_logicalBottomInFlowThread(logicalBottomInFlowThread) |
17 , m_previousBreakAfterValue(BreakAuto) | |
18 { | 17 { |
19 } | 18 } |
20 | 19 |
21 void ColumnBalancer::traverse() | 20 void ColumnBalancer::traverse() |
22 { | 21 { |
23 traverseSubtree(*columnSet().flowThread()); | 22 traverseSubtree(*columnSet().flowThread()); |
24 ASSERT(!flowThreadOffset()); | 23 ASSERT(!flowThreadOffset()); |
25 } | 24 } |
26 | 25 |
27 void ColumnBalancer::traverseSubtree(const LayoutBox& box) | 26 void ColumnBalancer::traverseSubtree(const LayoutBox& box) |
28 { | 27 { |
29 if (box.childrenInline() && box.isLayoutBlockFlow()) { | 28 if (box.childrenInline() && box.isLayoutBlockFlow()) { |
30 // Look for breaks between lines. | 29 // Look for breaks between lines. |
31 for (const RootInlineBox* line = toLayoutBlockFlow(box).firstRootBox();
line; line = line->nextRootBox()) { | 30 for (const RootInlineBox* line = toLayoutBlockFlow(box).firstRootBox();
line; line = line->nextRootBox()) { |
32 LayoutUnit lineTopInFlowThread = m_flowThreadOffset + line->lineTopW
ithLeading(); | 31 LayoutUnit lineTopInFlowThread = m_flowThreadOffset + line->lineTopW
ithLeading(); |
33 if (lineTopInFlowThread < logicalTopInFlowThread()) | 32 if (lineTopInFlowThread < logicalTopInFlowThread()) |
34 continue; | 33 continue; |
35 if (lineTopInFlowThread >= logicalBottomInFlowThread()) | 34 if (lineTopInFlowThread >= logicalBottomInFlowThread()) |
36 break; | 35 break; |
37 examineLine(*line); | 36 examineLine(*line); |
38 } | 37 } |
39 } | 38 } |
40 | 39 |
41 const LayoutFlowThread* flowThread = columnSet().flowThread(); | 40 const LayoutFlowThread* flowThread = columnSet().flowThread(); |
42 bool isHorizontalWritingMode = flowThread->isHorizontalWritingMode(); | 41 bool isHorizontalWritingMode = flowThread->isHorizontalWritingMode(); |
43 | 42 |
| 43 // The break-after value from the previous in-flow block-level object to be
joined with the |
| 44 // break-before value of the next in-flow block-level sibling. |
| 45 EBreak previousBreakAfterValue = BreakAuto; |
| 46 |
44 // Look for breaks between and inside block-level children. Even if this is
a block flow with | 47 // Look for breaks between and inside block-level children. Even if this is
a block flow with |
45 // inline children, there may be interesting floats to examine here. | 48 // inline children, there may be interesting floats to examine here. |
46 for (const LayoutObject* child = box.slowFirstChild(); child; child = child-
>nextSibling()) { | 49 for (const LayoutObject* child = box.slowFirstChild(); child; child = child-
>nextSibling()) { |
47 if (!child->isBox() || child->isInline()) | 50 if (!child->isBox() || child->isInline()) |
48 continue; | 51 continue; |
49 const LayoutBox& childBox = toLayoutBox(*child); | 52 const LayoutBox& childBox = toLayoutBox(*child); |
50 LayoutRect overflowRect = childBox.layoutOverflowRect(); | 53 LayoutRect overflowRect = childBox.layoutOverflowRect(); |
51 LayoutUnit childLogicalBottomWithOverflow = childBox.logicalTop() + (isH
orizontalWritingMode ? overflowRect.maxY() : overflowRect.maxX()); | 54 LayoutUnit childLogicalBottomWithOverflow = childBox.logicalTop() + (isH
orizontalWritingMode ? overflowRect.maxY() : overflowRect.maxX()); |
52 if (m_flowThreadOffset + childLogicalBottomWithOverflow <= logicalTopInF
lowThread()) { | 55 if (m_flowThreadOffset + childLogicalBottomWithOverflow <= logicalTopInF
lowThread()) { |
53 // This child is fully above the flow thread portion we're examining
. | 56 // This child is fully above the flow thread portion we're examining
. |
54 continue; | 57 continue; |
55 } | 58 } |
56 LayoutUnit childLogicalTopWithOverflow = childBox.logicalTop() + (isHori
zontalWritingMode ? overflowRect.y() : overflowRect.x()); | 59 LayoutUnit childLogicalTopWithOverflow = childBox.logicalTop() + (isHori
zontalWritingMode ? overflowRect.y() : overflowRect.x()); |
57 if (m_flowThreadOffset + childLogicalTopWithOverflow >= logicalBottomInF
lowThread()) { | 60 if (m_flowThreadOffset + childLogicalTopWithOverflow >= logicalBottomInF
lowThread()) { |
58 // This child is fully below the flow thread portion we're examining
. We cannot just | 61 // This child is fully below the flow thread portion we're examining
. We cannot just |
59 // stop here, though, thanks to negative margins. So keep looking. | 62 // stop here, though, thanks to negative margins. So keep looking. |
60 continue; | 63 continue; |
61 } | 64 } |
62 if (childBox.isOutOfFlowPositioned() || childBox.isColumnSpanAll()) | 65 if (childBox.isOutOfFlowPositioned() || childBox.isColumnSpanAll()) |
63 continue; | 66 continue; |
64 | 67 |
65 // Tables are wicked. Both table rows and table cells are relative to th
eir table section. | 68 // Tables are wicked. Both table rows and table cells are relative to th
eir table section. |
66 LayoutUnit offsetForThisChild = childBox.isTableRow() ? LayoutUnit() : c
hildBox.logicalTop(); | 69 LayoutUnit offsetForThisChild = childBox.isTableRow() ? LayoutUnit() : c
hildBox.logicalTop(); |
67 m_flowThreadOffset += offsetForThisChild; | 70 m_flowThreadOffset += offsetForThisChild; |
68 | 71 |
69 examineBoxAfterEntering(childBox); | 72 examineBoxAfterEntering(childBox, previousBreakAfterValue); |
70 // Unless the child is unsplittable, or if the child establishes an inne
r multicol | 73 // Unless the child is unsplittable, or if the child establishes an inne
r multicol |
71 // container, we descend into its subtree for further examination. | 74 // container, we descend into its subtree for further examination. |
72 if (childBox.getPaginationBreakability() != LayoutBox::ForbidBreaks | 75 if (childBox.getPaginationBreakability() != LayoutBox::ForbidBreaks |
73 && (!childBox.isLayoutBlockFlow() || !toLayoutBlockFlow(childBox).mu
ltiColumnFlowThread())) | 76 && (!childBox.isLayoutBlockFlow() || !toLayoutBlockFlow(childBox).mu
ltiColumnFlowThread())) |
74 traverseSubtree(childBox); | 77 traverseSubtree(childBox); |
75 m_previousBreakAfterValue = childBox.breakAfter(); | 78 previousBreakAfterValue = childBox.breakAfter(); |
76 examineBoxBeforeLeaving(childBox); | 79 examineBoxBeforeLeaving(childBox); |
77 | 80 |
78 m_flowThreadOffset -= offsetForThisChild; | 81 m_flowThreadOffset -= offsetForThisChild; |
79 } | 82 } |
80 } | 83 } |
81 | 84 |
82 InitialColumnHeightFinder::InitialColumnHeightFinder(const LayoutMultiColumnSet&
columnSet, LayoutUnit logicalTopInFlowThread, LayoutUnit logicalBottomInFlowThr
ead) | 85 InitialColumnHeightFinder::InitialColumnHeightFinder(const LayoutMultiColumnSet&
columnSet, LayoutUnit logicalTopInFlowThread, LayoutUnit logicalBottomInFlowThr
ead) |
83 : ColumnBalancer(columnSet, logicalTopInFlowThread, logicalBottomInFlowThrea
d) | 86 : ColumnBalancer(columnSet, logicalTopInFlowThread, logicalBottomInFlowThrea
d) |
84 { | 87 { |
85 m_shortestStruts.resize(columnSet.usedColumnCount()); | 88 m_shortestStruts.resize(columnSet.usedColumnCount()); |
86 for (auto& strut : m_shortestStruts) | 89 for (auto& strut : m_shortestStruts) |
87 strut = LayoutUnit::max(); | 90 strut = LayoutUnit::max(); |
88 traverse(); | 91 traverse(); |
89 // We have now found each explicit / forced break, and their location. Now w
e need to figure out | 92 // We have now found each explicit / forced break, and their location. Now w
e need to figure out |
90 // how many additional implicit / soft breaks we need and guess where they w
ill occur, in order | 93 // how many additional implicit / soft breaks we need and guess where they w
ill occur, in order |
91 // to provide an initial column height. | 94 // to provide an initial column height. |
92 distributeImplicitBreaks(); | 95 distributeImplicitBreaks(); |
93 } | 96 } |
94 | 97 |
95 LayoutUnit InitialColumnHeightFinder::initialMinimalBalancedHeight() const | 98 LayoutUnit InitialColumnHeightFinder::initialMinimalBalancedHeight() const |
96 { | 99 { |
97 unsigned index = contentRunIndexWithTallestColumns(); | 100 unsigned index = contentRunIndexWithTallestColumns(); |
98 LayoutUnit startOffset = index > 0 ? m_contentRuns[index - 1].breakOffset()
: logicalTopInFlowThread(); | 101 LayoutUnit startOffset = index > 0 ? m_contentRuns[index - 1].breakOffset()
: logicalTopInFlowThread(); |
99 return m_contentRuns[index].columnLogicalHeight(startOffset); | 102 return m_contentRuns[index].columnLogicalHeight(startOffset); |
100 } | 103 } |
101 | 104 |
102 void InitialColumnHeightFinder::examineBoxAfterEntering(const LayoutBox& box) | 105 void InitialColumnHeightFinder::examineBoxAfterEntering(const LayoutBox& box, EB
reak previousBreakAfterValue) |
103 { | 106 { |
104 if (isLogicalTopWithinBounds(flowThreadOffset() - box.paginationStrut())) { | 107 if (isLogicalTopWithinBounds(flowThreadOffset() - box.paginationStrut())) { |
105 if (box.needsForcedBreakBefore(previousBreakAfterValue())) { | 108 if (box.needsForcedBreakBefore(previousBreakAfterValue)) { |
106 addContentRun(flowThreadOffset()); | 109 addContentRun(flowThreadOffset()); |
107 } else { | 110 } else { |
108 ASSERT(isFirstAfterBreak(flowThreadOffset()) || !box.paginationStrut
()); | 111 ASSERT(isFirstAfterBreak(flowThreadOffset()) || !box.paginationStrut
()); |
109 if (isFirstAfterBreak(flowThreadOffset())) { | 112 if (isFirstAfterBreak(flowThreadOffset())) { |
110 // This box is first after a soft break. | 113 // This box is first after a soft break. |
111 recordStrutBeforeOffset(flowThreadOffset(), box.paginationStrut(
)); | 114 recordStrutBeforeOffset(flowThreadOffset(), box.paginationStrut(
)); |
112 } | 115 } |
113 } | 116 } |
114 } | 117 } |
115 | 118 |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
233 | 236 |
234 MinimumSpaceShortageFinder::MinimumSpaceShortageFinder(const LayoutMultiColumnSe
t& columnSet, LayoutUnit logicalTopInFlowThread, LayoutUnit logicalBottomInFlowT
hread) | 237 MinimumSpaceShortageFinder::MinimumSpaceShortageFinder(const LayoutMultiColumnSe
t& columnSet, LayoutUnit logicalTopInFlowThread, LayoutUnit logicalBottomInFlowT
hread) |
235 : ColumnBalancer(columnSet, logicalTopInFlowThread, logicalBottomInFlowThrea
d) | 238 : ColumnBalancer(columnSet, logicalTopInFlowThread, logicalBottomInFlowThrea
d) |
236 , m_minimumSpaceShortage(LayoutUnit::max()) | 239 , m_minimumSpaceShortage(LayoutUnit::max()) |
237 , m_pendingStrut(LayoutUnit::min()) | 240 , m_pendingStrut(LayoutUnit::min()) |
238 , m_forcedBreaksCount(0) | 241 , m_forcedBreaksCount(0) |
239 { | 242 { |
240 traverse(); | 243 traverse(); |
241 } | 244 } |
242 | 245 |
243 void MinimumSpaceShortageFinder::examineBoxAfterEntering(const LayoutBox& box) | 246 void MinimumSpaceShortageFinder::examineBoxAfterEntering(const LayoutBox& box, E
Break previousBreakAfterValue) |
244 { | 247 { |
245 LayoutBox::PaginationBreakability breakability = box.getPaginationBreakabili
ty(); | 248 LayoutBox::PaginationBreakability breakability = box.getPaginationBreakabili
ty(); |
246 | 249 |
247 // Look for breaks before the child box. | 250 // Look for breaks before the child box. |
248 if (isLogicalTopWithinBounds(flowThreadOffset() - box.paginationStrut())) { | 251 if (isLogicalTopWithinBounds(flowThreadOffset() - box.paginationStrut())) { |
249 if (box.needsForcedBreakBefore(previousBreakAfterValue())) { | 252 if (box.needsForcedBreakBefore(previousBreakAfterValue)) { |
250 m_forcedBreaksCount++; | 253 m_forcedBreaksCount++; |
251 } else { | 254 } else { |
252 ASSERT(isFirstAfterBreak(flowThreadOffset()) || !box.paginationStrut
()); | 255 ASSERT(isFirstAfterBreak(flowThreadOffset()) || !box.paginationStrut
()); |
253 if (isFirstAfterBreak(flowThreadOffset())) { | 256 if (isFirstAfterBreak(flowThreadOffset())) { |
254 // This box is first after a soft break. | 257 // This box is first after a soft break. |
255 LayoutUnit strut = box.paginationStrut(); | 258 LayoutUnit strut = box.paginationStrut(); |
256 // Figure out how much more space we would need to prevent it fr
om being pushed to the next column. | 259 // Figure out how much more space we would need to prevent it fr
om being pushed to the next column. |
257 recordSpaceShortage(box.logicalHeight() - strut); | 260 recordSpaceShortage(box.logicalHeight() - strut); |
258 if (breakability != LayoutBox::ForbidBreaks && m_pendingStrut ==
LayoutUnit::min()) { | 261 if (breakability != LayoutBox::ForbidBreaks && m_pendingStrut ==
LayoutUnit::min()) { |
259 // We now want to look for the first piece of unbreakable co
ntent (e.g. a line or a | 262 // We now want to look for the first piece of unbreakable co
ntent (e.g. a line or a |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
335 // of said overflow ends up in the next column. That counts as space shortag
e. | 338 // of said overflow ends up in the next column. That counts as space shortag
e. |
336 const MultiColumnFragmentainerGroup& group = groupAtOffset(lineTopInFlowThre
ad); | 339 const MultiColumnFragmentainerGroup& group = groupAtOffset(lineTopInFlowThre
ad); |
337 LayoutUnit lineBottomWithOverflow = lineTopInFlowThread + line.lineBottom()
- lineTop; | 340 LayoutUnit lineBottomWithOverflow = lineTopInFlowThread + line.lineBottom()
- lineTop; |
338 if (group.columnLogicalTopForOffset(lineTopInFlowThread) != group.columnLogi
calTopForOffset(lineBottomWithOverflow)) { | 341 if (group.columnLogicalTopForOffset(lineTopInFlowThread) != group.columnLogi
calTopForOffset(lineBottomWithOverflow)) { |
339 LayoutUnit shortage = lineBottomWithOverflow - group.columnLogicalTopFor
Offset(lineBottomWithOverflow); | 342 LayoutUnit shortage = lineBottomWithOverflow - group.columnLogicalTopFor
Offset(lineBottomWithOverflow); |
340 recordSpaceShortage(shortage); | 343 recordSpaceShortage(shortage); |
341 } | 344 } |
342 } | 345 } |
343 | 346 |
344 } // namespace blink | 347 } // namespace blink |
OLD | NEW |