[LFC][IFC] Take nonBreakableStart/End into use.
[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 "LayoutFormattingState.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 = [&](std::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.left);
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 = [&](std::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     displayBox.setTop(verticalGeometry.top + verticalGeometry.heightAndMargin.margin.top);
119     displayBox.setContentBoxHeight(verticalGeometry.heightAndMargin.height);
120     ASSERT(!verticalGeometry.heightAndMargin.collapsedMargin);
121     displayBox.setVerticalMargin(verticalGeometry.heightAndMargin.margin);
122     displayBox.setVerticalNonCollapsedMargin(verticalGeometry.heightAndMargin.margin);
123 }
124
125 void FormattingContext::computeBorderAndPadding(const Box& layoutBox) const
126 {
127     auto& layoutState = this->layoutState();
128     auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
129     displayBox.setBorder(Geometry::computedBorder(layoutState, layoutBox));
130     displayBox.setPadding(Geometry::computedPadding(layoutState, layoutBox));
131 }
132
133 void FormattingContext::layoutOutOfFlowDescendants(const Box& layoutBox) const
134 {
135     // Initial containing block by definition is a containing block.
136     if (!layoutBox.isPositioned() && !layoutBox.isInitialContainingBlock())
137         return;
138
139     if (!is<Container>(layoutBox))
140         return;
141
142     auto& container = downcast<Container>(layoutBox);
143     if (!container.hasChild())
144         return;
145
146     auto& layoutState = this->layoutState();
147     LOG_WITH_STREAM(FormattingContextLayout, stream << "Start: layout out-of-flow descendants -> context: " << &layoutState << " root: " << &root());
148
149     for (auto& outOfFlowBox : container.outOfFlowDescendants()) {
150         auto& layoutBox = *outOfFlowBox;
151
152         ASSERT(layoutBox.establishesFormattingContext());
153
154         computeBorderAndPadding(layoutBox);
155         computeOutOfFlowHorizontalGeometry(layoutBox);
156
157         layoutState.createFormattingStateForFormattingRootIfNeeded(layoutBox).formattingContext(layoutBox)->layout();
158
159         computeOutOfFlowVerticalGeometry(layoutBox);
160         layoutOutOfFlowDescendants(layoutBox);
161     }
162     LOG_WITH_STREAM(FormattingContextLayout, stream << "End: layout out-of-flow descendants -> context: " << &layoutState << " root: " << &root());
163 }
164
165 Display::Box FormattingContext::mapBoxToAncestor(const LayoutState& layoutState, const Box& layoutBox, const Container& ancestor)
166 {
167     ASSERT(layoutBox.isDescendantOf(ancestor));
168
169     auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
170     auto topLeft = displayBox.topLeft();
171
172     auto* containingBlock = layoutBox.containingBlock();
173     for (; containingBlock && containingBlock != &ancestor; containingBlock = containingBlock->containingBlock())
174         topLeft.moveBy(layoutState.displayBoxForLayoutBox(*containingBlock).topLeft());
175
176     if (!containingBlock) {
177         ASSERT_NOT_REACHED();
178         return Display::Box(displayBox);
179     }
180
181     auto mappedDisplayBox = Display::Box(displayBox);
182     mappedDisplayBox.setTopLeft(topLeft);
183     return mappedDisplayBox;
184 }
185
186 Position FormattingContext::mapTopLeftToAncestor(const LayoutState& layoutState, const Box& layoutBox, const Container& ancestor)
187 {
188     ASSERT(layoutBox.isDescendantOf(ancestor));
189     return mapCoordinateToAncestor(layoutState, layoutState.displayBoxForLayoutBox(layoutBox).topLeft(), *layoutBox.containingBlock(), ancestor);
190 }
191
192 Position FormattingContext::mapCoordinateToAncestor(const LayoutState& layoutState, Position position, const Container& containingBlock, const Container& ancestor)
193 {
194     auto mappedPosition = position;
195     auto* container = &containingBlock;
196     for (; container && container != &ancestor; container = container->containingBlock())
197         mappedPosition.moveBy(layoutState.displayBoxForLayoutBox(*container).topLeft());
198
199     if (!container) {
200         ASSERT_NOT_REACHED();
201         return position;
202     }
203
204     return mappedPosition;
205 }
206
207 #ifndef NDEBUG
208 void FormattingContext::validateGeometryConstraintsAfterLayout() const
209 {
210     if (!is<Container>(root()))
211         return;
212     auto& formattingContextRoot = downcast<Container>(root());
213     auto& layoutState = this->layoutState();
214     // FIXME: add a descendantsOfType<> flavor that stops at nested formatting contexts
215     for (auto& layoutBox : descendantsOfType<Box>(formattingContextRoot)) {
216         if (&layoutBox.formattingContextRoot() != &formattingContextRoot)
217             continue;
218         auto& containingBlockDisplayBox = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock());
219         auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
220
221         // 10.3.3 Block-level, non-replaced elements in normal flow
222         // 10.3.7 Absolutely positioned, non-replaced elements
223         if ((layoutBox.isBlockLevelBox() || layoutBox.isOutOfFlowPositioned()) && !layoutBox.replaced()) {
224             // margin-left + border-left-width + padding-left + width + padding-right + border-right-width + margin-right = width of containing block
225             auto containingBlockWidth = containingBlockDisplayBox.contentBoxWidth();
226             ASSERT(displayBox.marginLeft() + displayBox.borderLeft() + displayBox.paddingLeft().value_or(0) + displayBox.contentBoxWidth()
227                 + displayBox.paddingRight().value_or(0) + displayBox.borderRight() + displayBox.marginRight() == containingBlockWidth);
228         }
229
230         // 10.6.4 Absolutely positioned, non-replaced elements
231         if (layoutBox.isOutOfFlowPositioned() && !layoutBox.replaced()) {
232             // top + margin-top + border-top-width + padding-top + height + padding-bottom + border-bottom-width + margin-bottom + bottom = height of containing block
233             auto containingBlockHeight = containingBlockDisplayBox.contentBoxHeight();
234             ASSERT(displayBox.top() + displayBox.marginTop() + displayBox.borderTop() + displayBox.paddingTop().value_or(0) + displayBox.contentBoxHeight()
235                 + displayBox.paddingBottom().value_or(0) + displayBox.borderBottom() + displayBox.marginBottom() == containingBlockHeight);
236         }
237     }
238 }
239 #endif
240
241 }
242 }
243 #endif