[LFC] Remove redundant InlineFormattingContext::computeBorderAndPadding
[WebKit-https.git] / Source / WebCore / layout / FormattingContext.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 "FormattingContext.h"
28
29 #if ENABLE(LAYOUT_FORMATTING_CONTEXT)
30
31 #include "DisplayBox.h"
32 #include "FormattingState.h"
33 #include "LayoutBox.h"
34 #include "LayoutContainer.h"
35 #include "LayoutDescendantIterator.h"
36 #include "LayoutState.h"
37 #include "Logging.h"
38 #include <wtf/IsoMallocInlines.h>
39 #include <wtf/text/TextStream.h>
40
41 namespace WebCore {
42 namespace Layout {
43
44 WTF_MAKE_ISO_ALLOCATED_IMPL(FormattingContext);
45
46 FormattingContext::FormattingContext(const Box& formattingContextRoot, FormattingState& formattingState)
47     : m_root(makeWeakPtr(formattingContextRoot))
48     , m_formattingState(formattingState)
49 {
50 #ifndef NDEBUG
51     layoutState().registerFormattingContext(*this);
52 #endif
53 }
54
55 FormattingContext::~FormattingContext()
56 {
57 #ifndef NDEBUG
58     layoutState().deregisterFormattingContext(*this);
59 #endif
60 }
61
62 LayoutState& FormattingContext::layoutState() const
63 {
64     return m_formattingState.layoutState();
65 }
66
67 void FormattingContext::computeOutOfFlowHorizontalGeometry(const Box& layoutBox) const
68 {
69     auto& layoutState = this->layoutState();
70     auto containingBlockWidth = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock()).paddingBoxWidth();
71
72     auto compute = [&](Optional<LayoutUnit> usedWidth) {
73         auto usedValues = UsedHorizontalValues { containingBlockWidth, usedWidth, { } };
74         return Geometry::outOfFlowHorizontalGeometry(layoutState, layoutBox, usedValues);
75     };
76
77     auto horizontalGeometry = compute({ });
78     if (auto maxWidth = Geometry::computedValueIfNotAuto(layoutBox.style().logicalMaxWidth(), containingBlockWidth)) {
79         auto maxHorizontalGeometry = compute(maxWidth);
80         if (horizontalGeometry.widthAndMargin.width > maxHorizontalGeometry.widthAndMargin.width)
81             horizontalGeometry = maxHorizontalGeometry;
82     }
83
84     if (auto minWidth = Geometry::computedValueIfNotAuto(layoutBox.style().logicalMinWidth(), containingBlockWidth)) {
85         auto minHorizontalGeometry = compute(minWidth);
86         if (horizontalGeometry.widthAndMargin.width < minHorizontalGeometry.widthAndMargin.width)
87             horizontalGeometry = minHorizontalGeometry;
88     }
89
90     auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
91     displayBox.setLeft(horizontalGeometry.left + horizontalGeometry.widthAndMargin.usedMargin.start);
92     displayBox.setContentBoxWidth(horizontalGeometry.widthAndMargin.width);
93     displayBox.setHorizontalMargin(horizontalGeometry.widthAndMargin.usedMargin);
94     displayBox.setHorizontalComputedMargin(horizontalGeometry.widthAndMargin.computedMargin);
95 }
96
97 void FormattingContext::computeOutOfFlowVerticalGeometry(const Box& layoutBox) const
98 {
99     auto& layoutState = this->layoutState();
100
101     auto compute = [&](UsedVerticalValues usedValues) {
102         return Geometry::outOfFlowVerticalGeometry(layoutState, layoutBox, usedValues);
103     };
104
105     auto verticalGeometry = compute({ });
106     if (auto maxHeight = Geometry::computedMaxHeight(layoutState, layoutBox)) {
107         auto maxVerticalGeometry = compute({ *maxHeight });
108         if (verticalGeometry.heightAndMargin.height > maxVerticalGeometry.heightAndMargin.height)
109             verticalGeometry = maxVerticalGeometry;
110     }
111
112     if (auto minHeight = Geometry::computedMinHeight(layoutState, layoutBox)) {
113         auto minVerticalGeometry = compute({ *minHeight });
114         if (verticalGeometry.heightAndMargin.height < minVerticalGeometry.heightAndMargin.height)
115             verticalGeometry = minVerticalGeometry;
116     }
117
118     auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
119     auto nonCollapsedVerticalMargin = verticalGeometry.heightAndMargin.nonCollapsedMargin;
120     displayBox.setTop(verticalGeometry.top + nonCollapsedVerticalMargin.before);
121     displayBox.setContentBoxHeight(verticalGeometry.heightAndMargin.height);
122     // Margins of absolutely positioned boxes do not collapse
123     displayBox.setVerticalMargin({ nonCollapsedVerticalMargin, { } });
124 }
125
126 void FormattingContext::computeBorderAndPadding(const Box& layoutBox, Optional<UsedHorizontalValues> usedValues) const
127 {
128     auto& layoutState = this->layoutState();
129     if (!usedValues)
130         usedValues = UsedHorizontalValues { layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock()).contentBoxWidth() };
131     auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
132     displayBox.setBorder(Geometry::computedBorder(layoutBox));
133     displayBox.setPadding(Geometry::computedPadding(layoutBox, *usedValues));
134 }
135
136 void FormattingContext::layoutOutOfFlowDescendants(const Box& layoutBox) const
137 {
138     // Initial containing block by definition is a containing block.
139     if (!layoutBox.isPositioned() && !layoutBox.isInitialContainingBlock())
140         return;
141
142     if (!is<Container>(layoutBox))
143         return;
144
145     auto& container = downcast<Container>(layoutBox);
146     if (!container.hasChild())
147         return;
148
149     auto& layoutState = this->layoutState();
150     LOG_WITH_STREAM(FormattingContextLayout, stream << "Start: layout out-of-flow descendants -> context: " << &layoutState << " root: " << &root());
151
152     for (auto& outOfFlowBox : container.outOfFlowDescendants()) {
153         auto& layoutBox = *outOfFlowBox;
154
155         ASSERT(layoutBox.establishesFormattingContext());
156
157         computeBorderAndPadding(layoutBox);
158         computeOutOfFlowHorizontalGeometry(layoutBox);
159
160         layoutState.createFormattingContext(layoutBox)->layout();
161
162         computeOutOfFlowVerticalGeometry(layoutBox);
163         layoutOutOfFlowDescendants(layoutBox);
164     }
165     LOG_WITH_STREAM(FormattingContextLayout, stream << "End: layout out-of-flow descendants -> context: " << &layoutState << " root: " << &root());
166 }
167
168 Display::Box FormattingContext::mapBoxToAncestor(const LayoutState& layoutState, const Box& layoutBox, const Container& ancestor)
169 {
170     ASSERT(layoutBox.isDescendantOf(ancestor));
171
172     auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
173     auto topLeft = displayBox.topLeft();
174
175     auto* containingBlock = layoutBox.containingBlock();
176     for (; containingBlock && containingBlock != &ancestor; containingBlock = containingBlock->containingBlock())
177         topLeft.moveBy(layoutState.displayBoxForLayoutBox(*containingBlock).topLeft());
178
179     if (!containingBlock) {
180         ASSERT_NOT_REACHED();
181         return Display::Box(displayBox);
182     }
183
184     auto mappedDisplayBox = Display::Box(displayBox);
185     mappedDisplayBox.setTopLeft(topLeft);
186     return mappedDisplayBox;
187 }
188
189 LayoutUnit FormattingContext::mapTopToAncestor(const LayoutState& layoutState, const Box& layoutBox, const Container& ancestor)
190 {
191     ASSERT(layoutBox.isDescendantOf(ancestor));
192     auto top = layoutState.displayBoxForLayoutBox(layoutBox).top();
193     auto* container = layoutBox.containingBlock();
194     for (; container && container != &ancestor; container = container->containingBlock())
195         top += layoutState.displayBoxForLayoutBox(*container).top();
196     return top;
197 }
198
199 Point FormattingContext::mapCoordinateToAncestor(const LayoutState& layoutState, Point position, const Container& containingBlock, const Container& ancestor)
200 {
201     auto mappedPosition = position;
202     auto* container = &containingBlock;
203     for (; container && container != &ancestor; container = container->containingBlock())
204         mappedPosition.moveBy(layoutState.displayBoxForLayoutBox(*container).topLeft());
205
206     if (!container) {
207         ASSERT_NOT_REACHED();
208         return position;
209     }
210
211     return mappedPosition;
212 }
213
214 #ifndef NDEBUG
215 void FormattingContext::validateGeometryConstraintsAfterLayout() const
216 {
217     if (!is<Container>(root()))
218         return;
219     auto& formattingContextRoot = downcast<Container>(root());
220     auto& layoutState = this->layoutState();
221     // FIXME: add a descendantsOfType<> flavor that stops at nested formatting contexts
222     for (auto& layoutBox : descendantsOfType<Box>(formattingContextRoot)) {
223         if (&layoutBox.formattingContextRoot() != &formattingContextRoot)
224             continue;
225         auto& containingBlockDisplayBox = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock());
226         auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
227
228         // 10.3.3 Block-level, non-replaced elements in normal flow
229         // 10.3.7 Absolutely positioned, non-replaced elements
230         if ((layoutBox.isBlockLevelBox() || layoutBox.isOutOfFlowPositioned()) && !layoutBox.replaced()) {
231             // margin-left + border-left-width + padding-left + width + padding-right + border-right-width + margin-right = width of containing block
232             auto containingBlockWidth = containingBlockDisplayBox.contentBoxWidth();
233             ASSERT(displayBox.marginStart() + displayBox.borderLeft() + displayBox.paddingLeft().valueOr(0) + displayBox.contentBoxWidth()
234                 + displayBox.paddingRight().valueOr(0) + displayBox.borderRight() + displayBox.marginEnd() == containingBlockWidth);
235         }
236
237         // 10.6.4 Absolutely positioned, non-replaced elements
238         if (layoutBox.isOutOfFlowPositioned() && !layoutBox.replaced()) {
239             // top + margin-top + border-top-width + padding-top + height + padding-bottom + border-bottom-width + margin-bottom + bottom = height of containing block
240             auto containingBlockHeight = containingBlockDisplayBox.contentBoxHeight();
241             ASSERT(displayBox.top() + displayBox.marginBefore() + displayBox.borderTop() + displayBox.paddingTop().valueOr(0) + displayBox.contentBoxHeight()
242                 + displayBox.paddingBottom().valueOr(0) + displayBox.borderBottom() + displayBox.marginAfter() == containingBlockHeight);
243         }
244     }
245 }
246 #endif
247
248 }
249 }
250 #endif