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