[LFC] Align compute functions styles.
[WebKit-https.git] / Source / WebCore / layout / blockformatting / BlockMarginCollapse.cpp
1 /*
2  * Copyright (C) 2018 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "BlockFormattingContext.h"
28
29 #if ENABLE(LAYOUT_FORMATTING_CONTEXT)
30
31 #include "LayoutBox.h"
32 #include "LayoutContainer.h"
33 #include "LayoutUnit.h"
34 #include "RenderStyle.h"
35
36 namespace WebCore {
37 namespace Layout {
38
39 static LayoutUnit marginValue(LayoutUnit currentMarginValue, LayoutUnit candidateMarginValue)
40 {
41     if (!candidateMarginValue)
42         return currentMarginValue;
43     if (!currentMarginValue)
44         return candidateMarginValue;
45     // Both margins are positive.
46     if (candidateMarginValue > 0 && currentMarginValue > 0)
47         return std::max(candidateMarginValue, currentMarginValue);
48     // Both margins are negative.
49     if (candidateMarginValue < 0 && currentMarginValue < 0)
50         return 0 - std::max(std::abs(candidateMarginValue.toFloat()), std::abs(currentMarginValue.toFloat()));
51     // One of the margins is negative.
52     return currentMarginValue + candidateMarginValue;
53 }
54
55 static bool isMarginTopCollapsedWithSibling(const Box& layoutBox)
56 {
57     if (layoutBox.isFloatingPositioned())
58         return false;
59
60     if (!layoutBox.isPositioned() || layoutBox.isInFlowPositioned())
61         return true;
62
63     // Out of flow positioned.
64     ASSERT(layoutBox.isOutOfFlowPositioned());
65     return layoutBox.style().top().isAuto();
66 }
67
68 static bool isMarginBottomCollapsedWithSibling(const Box& layoutBox)
69 {
70     if (layoutBox.isFloatingPositioned())
71         return false;
72
73     if (!layoutBox.isPositioned() || layoutBox.isInFlowPositioned())
74         return true;
75
76     // Out of flow positioned.
77     ASSERT(layoutBox.isOutOfFlowPositioned());
78     return layoutBox.style().bottom().isAuto();
79 }
80
81 static bool isMarginTopCollapsedWithParent(const Box& layoutBox)
82 {
83     // The first inflow child could propagate its top margin to parent.
84     // https://www.w3.org/TR/CSS21/box.html#collapsing-margins
85     if (layoutBox.isAnonymous())
86         return false;
87
88     if (layoutBox.isFloatingOrOutOfFlowPositioned())
89         return false;
90
91     // We never margin collapse the initial containing block.
92     ASSERT(layoutBox.parent());
93     auto& parent = *layoutBox.parent();
94     // Is this box the first inlflow child?
95     if (parent.firstInFlowChild() != &layoutBox)
96         return false;
97
98     if (parent.establishesBlockFormattingContext())
99         return false;
100
101     // Margins of the root element's box do not collapse.
102     if (parent.isInitialContainingBlock())
103         return false;
104
105     if (!parent.style().borderTop().nonZero())
106         return false;
107
108     if (!parent.style().paddingTop().isZero())
109         return false;
110
111     return true;
112 }
113
114 LayoutUnit BlockFormattingContext::MarginCollapse::collapsedMarginTopFromFirstChild(const LayoutContext& layoutContext, const Box& layoutBox)
115 {
116     // Check if the first child collapses its margin top.
117     if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowChild())
118         return 0;
119
120     auto& firstInFlowChild = *downcast<Container>(layoutBox).firstInFlowChild();
121     if (!isMarginTopCollapsedWithParent(firstInFlowChild))
122         return 0;
123
124     // Collect collapsed margin top recursively.
125     return marginValue(computedNonCollapsedMarginTop(layoutContext, firstInFlowChild), collapsedMarginTopFromFirstChild(layoutContext, firstInFlowChild));
126 }
127
128 LayoutUnit BlockFormattingContext::MarginCollapse::nonCollapsedMarginTop(const LayoutContext& layoutContext, const Box& layoutBox)
129 {
130     // Non collapsed margin top includes collapsed margin from inflow first child.
131     return marginValue(computedNonCollapsedMarginTop(layoutContext, layoutBox), collapsedMarginTopFromFirstChild(layoutContext, layoutBox));
132 }
133
134 /*static bool hasAdjoiningMarginTopAndBottom(const Box&)
135 {
136     // Two margins are adjoining if and only if:
137     // 1. both belong to in-flow block-level boxes that participate in the same block formatting context
138     // 2. no line boxes, no clearance, no padding and no border separate them (Note that certain zero-height line boxes (see 9.4.2) are ignored for this purpose.)
139     // 3. both belong to vertically-adjacent box edges, i.e. form one of the following pairs:
140     //        top margin of a box and top margin of its first in-flow child
141     //        bottom margin of box and top margin of its next in-flow following sibling
142     //        bottom margin of a last in-flow child and bottom margin of its parent if the parent has 'auto' computed height
143     //        top and bottom margins of a box that does not establish a new block formatting context and that has zero computed 'min-height',
144     //        zero or 'auto' computed 'height', and no in-flow children
145     // A collapsed margin is considered adjoining to another margin if any of its component margins is adjoining to that margin.
146     return false;
147 }*/
148 LayoutUnit BlockFormattingContext::MarginCollapse::computedNonCollapsedMarginTop(const LayoutContext& layoutContext, const Box& layoutBox)
149 {
150     return FormattingContext::Geometry::computedNonCollapsedVerticalMarginValue(layoutContext, layoutBox).top;
151 }
152
153 LayoutUnit BlockFormattingContext::MarginCollapse::computedNonCollapsedMarginBottom(const LayoutContext& layoutContext, const Box& layoutBox)
154 {
155     return FormattingContext::Geometry::computedNonCollapsedVerticalMarginValue(layoutContext, layoutBox).bottom;
156 }
157
158 LayoutUnit BlockFormattingContext::MarginCollapse::marginTop(const LayoutContext& layoutContext, const Box& layoutBox)
159 {
160     if (layoutBox.isAnonymous())
161         return 0;
162
163     // TODO: take _hasAdjoiningMarginTopAndBottom() into account.
164     if (isMarginTopCollapsedWithParent(layoutBox))
165         return 0;
166
167     // Floats and out of flow positioned boxes do not collapse their margins.
168     if (!isMarginTopCollapsedWithSibling(layoutBox))
169         return nonCollapsedMarginTop(layoutContext, layoutBox);
170
171     // The bottom margin of an in-flow block-level element always collapses with the top margin of its next in-flow block-level sibling,
172     // unless that sibling has clearance.
173     auto* previousInFlowSibling = layoutBox.previousInFlowSibling();
174     if (!previousInFlowSibling)
175         return nonCollapsedMarginTop(layoutContext, layoutBox);
176
177     auto previousSiblingMarginBottom = nonCollapsedMarginBottom(layoutContext, *previousInFlowSibling);
178     auto marginTop = nonCollapsedMarginTop(layoutContext, layoutBox);
179     return marginValue(marginTop, previousSiblingMarginBottom);
180 }
181
182 LayoutUnit BlockFormattingContext::MarginCollapse::marginBottom(const LayoutContext& layoutContext, const Box& layoutBox)
183 {
184     if (layoutBox.isAnonymous())
185         return 0;
186
187     // TODO: take _hasAdjoiningMarginTopAndBottom() into account.
188     if (BlockFormattingContext::MarginCollapse::isMarginBottomCollapsedWithParent(layoutBox))
189         return 0;
190
191     // Floats and out of flow positioned boxes do not collapse their margins.
192     if (!isMarginBottomCollapsedWithSibling(layoutBox))
193         return nonCollapsedMarginBottom(layoutContext, layoutBox);
194
195     // The bottom margin of an in-flow block-level element always collapses with the top margin of its next in-flow block-level sibling,
196     // unless that sibling has clearance.
197     if (layoutBox.nextInFlowSibling())
198         return 0;
199     return nonCollapsedMarginBottom(layoutContext, layoutBox);
200 }
201
202 bool BlockFormattingContext::MarginCollapse::isMarginBottomCollapsedWithParent(const Box& layoutBox)
203 {
204     // last inflow box to parent.
205     // https://www.w3.org/TR/CSS21/box.html#collapsing-margins
206     if (layoutBox.isAnonymous())
207         return false;
208
209     if (layoutBox.isFloatingOrOutOfFlowPositioned())
210         return false;
211
212     // We never margin collapse the initial containing block.
213     ASSERT(layoutBox.parent());
214     auto& parent = *layoutBox.parent();
215     // Is this the last inlflow child?
216     if (parent.lastInFlowChild() != &layoutBox)
217         return false;
218
219     if (parent.establishesBlockFormattingContext())
220         return false;
221
222     // Margins of the root element's box do not collapse.
223     if (parent.isInitialContainingBlock())
224         return false;
225
226     if (!parent.style().borderTop().nonZero())
227         return false;
228
229     if (!parent.style().paddingTop().isZero())
230         return false;
231
232     if (!parent.style().height().isAuto())
233         return false;
234
235     return true;
236 }
237
238 bool BlockFormattingContext::MarginCollapse::isMarginTopCollapsedWithParentMarginBottom(const Box&)
239 {
240     return false;
241 }
242
243 LayoutUnit BlockFormattingContext::MarginCollapse::collapsedMarginBottomFromLastChild(const LayoutContext& layoutContext, const Box& layoutBox)
244 {
245     // Check if the last child propagates its margin bottom.
246     if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowChild())
247         return 0;
248
249     auto& lastInFlowChild = *downcast<Container>(layoutBox).lastInFlowChild();
250     if (!isMarginBottomCollapsedWithParent(lastInFlowChild))
251         return 0;
252
253     // Collect collapsed margin bottom recursively.
254     return marginValue(computedNonCollapsedMarginBottom(layoutContext, lastInFlowChild), collapsedMarginBottomFromLastChild(layoutContext, lastInFlowChild));
255 }
256
257 LayoutUnit BlockFormattingContext::MarginCollapse::nonCollapsedMarginBottom(const LayoutContext& layoutContext, const Box& layoutBox)
258 {
259     // Non collapsed margin bottom includes collapsed margin from inflow last child.
260     return marginValue(computedNonCollapsedMarginBottom(layoutContext, layoutBox), collapsedMarginBottomFromLastChild(layoutContext, layoutBox));
261 }
262
263 }
264 }
265 #endif