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