71a69c4e600d414b747211dd5ef86c9e86cb313b
[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 "InvalidationState.h"
34 #include "LayoutBox.h"
35 #include "LayoutContainerBox.h"
36 #include "LayoutContext.h"
37 #include "LayoutDescendantIterator.h"
38 #include "LayoutReplacedBox.h"
39 #include "LayoutState.h"
40 #include "Logging.h"
41 #include <wtf/IsoMallocInlines.h>
42 #include <wtf/text/TextStream.h>
43
44 namespace WebCore {
45 namespace Layout {
46
47 WTF_MAKE_ISO_ALLOCATED_IMPL(FormattingContext);
48
49 FormattingContext::FormattingContext(const ContainerBox& formattingContextRoot, FormattingState& formattingState)
50     : m_root(makeWeakPtr(formattingContextRoot))
51     , m_formattingState(formattingState)
52 {
53     ASSERT(formattingContextRoot.hasChild());
54 #ifndef NDEBUG
55     layoutState().registerFormattingContext(*this);
56 #endif
57 }
58
59 FormattingContext::~FormattingContext()
60 {
61 #ifndef NDEBUG
62     layoutState().deregisterFormattingContext(*this);
63 #endif
64 }
65
66 LayoutState& FormattingContext::layoutState() const
67 {
68     return m_formattingState.layoutState();
69 }
70
71 void FormattingContext::computeOutOfFlowHorizontalGeometry(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints, const VerticalConstraints& verticalConstraints)
72 {
73     ASSERT(layoutBox.isOutOfFlowPositioned());
74     auto compute = [&](Optional<LayoutUnit> usedWidth) {
75         return geometry().outOfFlowHorizontalGeometry(layoutBox, horizontalConstraints, verticalConstraints, { usedWidth, { } });
76     };
77
78     auto containingBlockWidth = horizontalConstraints.logicalWidth;
79     auto horizontalGeometry = compute({ });
80     if (auto maxWidth = geometry().computedMaxWidth(layoutBox, containingBlockWidth)) {
81         auto maxHorizontalGeometry = compute(maxWidth);
82         if (horizontalGeometry.contentWidthAndMargin.contentWidth > maxHorizontalGeometry.contentWidthAndMargin.contentWidth)
83             horizontalGeometry = maxHorizontalGeometry;
84     }
85
86     if (auto minWidth = geometry().computedMinWidth(layoutBox, containingBlockWidth)) {
87         auto minHorizontalGeometry = compute(minWidth);
88         if (horizontalGeometry.contentWidthAndMargin.contentWidth < minHorizontalGeometry.contentWidthAndMargin.contentWidth)
89             horizontalGeometry = minHorizontalGeometry;
90     }
91
92     auto& displayBox = formattingState().displayBox(layoutBox);
93     displayBox.setLeft(horizontalGeometry.left + horizontalGeometry.contentWidthAndMargin.usedMargin.start);
94     displayBox.setContentBoxWidth(horizontalGeometry.contentWidthAndMargin.contentWidth);
95     displayBox.setHorizontalMargin(horizontalGeometry.contentWidthAndMargin.usedMargin);
96     displayBox.setHorizontalComputedMargin(horizontalGeometry.contentWidthAndMargin.computedMargin);
97 }
98
99 void FormattingContext::computeOutOfFlowVerticalGeometry(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints, const VerticalConstraints& verticalConstraints)
100 {
101     ASSERT(layoutBox.isOutOfFlowPositioned());
102     auto compute = [&](Optional<LayoutUnit> usedHeight) {
103         return geometry().outOfFlowVerticalGeometry(layoutBox, horizontalConstraints, verticalConstraints, { usedHeight });
104     };
105
106     auto containingBlockHeight = *verticalConstraints.logicalHeight;
107     auto verticalGeometry = compute({ });
108     if (auto maxHeight = geometry().computedMaxHeight(layoutBox, containingBlockHeight)) {
109         auto maxVerticalGeometry = compute(maxHeight);
110         if (verticalGeometry.contentHeightAndMargin.contentHeight > maxVerticalGeometry.contentHeightAndMargin.contentHeight)
111             verticalGeometry = maxVerticalGeometry;
112     }
113
114     if (auto minHeight = geometry().computedMinHeight(layoutBox, containingBlockHeight)) {
115         auto minVerticalGeometry = compute(minHeight);
116         if (verticalGeometry.contentHeightAndMargin.contentHeight < minVerticalGeometry.contentHeightAndMargin.contentHeight)
117             verticalGeometry = minVerticalGeometry;
118     }
119
120     auto& displayBox = formattingState().displayBox(layoutBox);
121     auto nonCollapsedVerticalMargin = verticalGeometry.contentHeightAndMargin.nonCollapsedMargin;
122     displayBox.setTop(verticalGeometry.top + nonCollapsedVerticalMargin.before);
123     displayBox.setContentBoxHeight(verticalGeometry.contentHeightAndMargin.contentHeight);
124     // Margins of absolutely positioned boxes do not collapse
125     displayBox.setVerticalMargin({ nonCollapsedVerticalMargin, { } });
126 }
127
128 void FormattingContext::computeBorderAndPadding(const Box& layoutBox, const HorizontalConstraints& horizontalConstraint)
129 {
130     auto& displayBox = formattingState().displayBox(layoutBox);
131     displayBox.setBorder(geometry().computedBorder(layoutBox));
132     displayBox.setPadding(geometry().computedPadding(layoutBox, horizontalConstraint));
133 }
134
135 void FormattingContext::layoutOutOfFlowContent(InvalidationState& invalidationState, const OutOfFlowHorizontalConstraints& rootHorizontalConstraints, const VerticalConstraints& rootVerticalConstraints)
136 {
137     LOG_WITH_STREAM(FormattingContextLayout, stream << "Start: layout out-of-flow content -> context: " << &layoutState() << " root: " << &root());
138
139     collectOutOfFlowDescendantsIfNeeded();
140
141     auto horizontalConstraintsForLayoutBox = [&] (const auto& outOfFlowBox) {
142         auto* containingBlock = outOfFlowBox.containingBlock();
143         ASSERT(containingBlock);
144         if (containingBlock == &root())
145             return rootHorizontalConstraints;
146         return Geometry::horizontalConstraintsForOutOfFlow(geometryForBox(*containingBlock));
147     };
148
149     auto verticalConstraintsForLayoutBox = [&] (const auto& outOfFlowBox) {
150         auto* containingBlock = outOfFlowBox.containingBlock();
151         ASSERT(containingBlock);
152         if (containingBlock == &root())
153             return rootVerticalConstraints;
154         return Geometry::verticalConstraintsForOutOfFlow(geometryForBox(*containingBlock));
155     };
156
157     for (auto& outOfFlowBox : formattingState().outOfFlowBoxes()) {
158         ASSERT(outOfFlowBox->establishesFormattingContext());
159         if (!invalidationState.needsLayout(*outOfFlowBox))
160             continue;
161
162         auto outOfFlowHorizontalConstraints = horizontalConstraintsForLayoutBox(*outOfFlowBox);
163         auto horizontalConstraintsForBorderAndPadding = HorizontalConstraints { outOfFlowHorizontalConstraints.value.logicalLeft, outOfFlowHorizontalConstraints.borderAndPaddingSpecificWidth };
164         computeBorderAndPadding(*outOfFlowBox, horizontalConstraintsForBorderAndPadding);
165
166         auto horizontalConstraints = outOfFlowHorizontalConstraints.value;
167         auto verticalConstraints = verticalConstraintsForLayoutBox(*outOfFlowBox);
168         computeOutOfFlowHorizontalGeometry(*outOfFlowBox, horizontalConstraints, verticalConstraints);
169         if (is<ContainerBox>(*outOfFlowBox)) {
170             auto& containerBox = downcast<ContainerBox>(*outOfFlowBox);
171             auto& containerDisplayBox = geometryForBox(containerBox);
172
173             if (containerBox.hasInFlowOrFloatingChild()) {
174                 auto horizontalConstraintsForInFlowContent = Geometry::horizontalConstraintsForInFlow(containerDisplayBox);
175                 auto verticalConstraintsForInFlowContent = Geometry::verticalConstraintsForInFlow(containerDisplayBox);
176                 auto formattingContext = LayoutContext::createFormattingContext(containerBox, layoutState());
177                 formattingContext->layoutInFlowContent(invalidationState, horizontalConstraintsForInFlowContent, verticalConstraintsForInFlowContent);
178             }
179             computeOutOfFlowVerticalGeometry(containerBox, horizontalConstraints, verticalConstraints);
180             if (containerBox.hasChild()) {
181                 auto horizontalConstraintsForOutOfFlowContent =  Geometry::horizontalConstraintsForOutOfFlow(containerDisplayBox);
182                 auto verticalConstraintsForOutOfFlowContent = Geometry::verticalConstraintsForOutOfFlow(containerDisplayBox);
183                 auto formattingContext = LayoutContext::createFormattingContext(containerBox, layoutState());
184                 formattingContext->layoutOutOfFlowContent(invalidationState, horizontalConstraintsForOutOfFlowContent, verticalConstraintsForOutOfFlowContent);
185             }
186         } else
187             computeOutOfFlowVerticalGeometry(*outOfFlowBox, horizontalConstraints, verticalConstraints);
188     }
189     LOG_WITH_STREAM(FormattingContextLayout, stream << "End: layout out-of-flow content -> context: " << &layoutState() << " root: " << &root());
190 }
191
192 const Display::Box& FormattingContext::geometryForBox(const Box& layoutBox, Optional<EscapeReason> escapeReason) const
193 {
194     UNUSED_PARAM(escapeReason);
195 #if ASSERT_ENABLED
196     auto isOkToAccessDisplayBox = [&] {
197         if (!layoutBox.isInitialContainingBlock() && &layoutBox.formattingContextRoot() == &root()) {
198             // This is the non-escape case of accessing a box's geometry information within the same formatting context.
199             return true;
200         }
201
202         if (!escapeReason) {
203             // Any geometry access outside of the formatting context without a valid reason is considered an escape.
204             return false;
205         }
206
207         if (*escapeReason == EscapeReason::DocumentBoxStrechesToViewportQuirk) {
208             ASSERT(layoutState().inQuirksMode());
209             return layoutBox.isInitialContainingBlock();
210         }
211
212         if (*escapeReason == EscapeReason::BodyStrechesToViewportQuirk) {
213             ASSERT(layoutState().inQuirksMode());
214             return layoutBox.isInitialContainingBlock() || layoutBox.isDocumentBox();
215
216         }
217
218         if (*escapeReason == EscapeReason::StrokeOverflowNeedsViewportGeometry)
219             return layoutBox.isInitialContainingBlock();
220
221         if (*escapeReason == EscapeReason::NeedsGeometryFromEstablishedFormattingContext) {
222             // This is the case when a formatting root collects geometry information from the established
223             // formatting context to be able to determine width/height.
224             // e.g <div>text content</div>. The <div> is a formatting root of the IFC.
225             // In order to compute the height of the <div>, we need to look inside the IFC and gather geometry information.
226             return &layoutBox.formattingContextRoot().formattingContextRoot() == &root();
227         }
228
229         if (*escapeReason == EscapeReason::OutOfFlowBoxNeedsInFlowGeometry) {
230             // When computing the static position of an out-of-flow box, we need to gather sibling/parent geometry information
231             // as if the out-of-flow box was a simple inflow box.
232             // Now since the out-of-flow and the sibling/parent boxes could very well be in different containing block subtrees
233             // the formatting context they live in could also be very different.
234             return true;
235         }
236
237         if (*escapeReason == EscapeReason::FloatBoxNeedsToBeInAbsoluteCoordinates) {
238             // Float box top/left values are mapped relative to the FloatState's root. Inline formatting contexts(A) inherit floats from parent
239             // block formatting contexts(B). Floats in these inline formatting contexts(A) need to be mapped to the parent, block formatting context(B).
240             auto& formattingContextRoot = layoutBox.formattingContextRoot();
241             return &formattingContextRoot == &root() || &formattingContextRoot == &root().formattingContextRoot();
242         }
243
244         if (*escapeReason == EscapeReason::FindFixedHeightAncestorQuirk) {
245             ASSERT(layoutState().inQuirksMode());
246             // Find the first containing block with fixed height quirk. See Quirks::heightValueOfNearestContainingBlockWithFixedHeight.
247             // This is only to check if the targetFormattingRoot is an ancestor formatting root.
248             if (layoutBox.isInitialContainingBlock())
249                 return true;
250             auto& targetFormattingRoot = layoutBox.formattingContextRoot();
251             auto* ancestorFormattingContextRoot = &root().formattingContextRoot();
252             while (true) {
253                 if (&targetFormattingRoot == ancestorFormattingContextRoot)
254                     return true;
255                 ancestorFormattingContextRoot = &ancestorFormattingContextRoot->formattingContextRoot();
256                 if (ancestorFormattingContextRoot->isInitialContainingBlock())
257                     return true;
258             }
259             return false;
260         }
261
262         if (*escapeReason == EscapeReason::TableNeedsAccessToTableWrapper) {
263             // Tables are wrapped in a 2 level formatting context structure. A <table> element initiates a block formatting context for its principal table box
264             // where the caption and the table content live. It also initiates a table wrapper box which establishes the table formatting context.
265             // In many cases the TFC needs access to the parent (generated) BFC.
266             return &layoutBox == &root().formattingContextRoot();
267         }
268
269         ASSERT_NOT_REACHED();
270         return false;
271     };
272 #endif
273     ASSERT(isOkToAccessDisplayBox());
274     ASSERT(layoutState().hasDisplayBox(layoutBox));
275     return layoutState().displayBoxForLayoutBox(layoutBox);
276 }
277
278 void FormattingContext::collectOutOfFlowDescendantsIfNeeded()
279 {
280     if (!formattingState().outOfFlowBoxes().isEmpty())
281         return;
282     auto& root = this->root();
283     if (!root.hasChild())
284         return;
285     if (!root.isPositioned() && !root.isInitialContainingBlock())
286         return;
287     // Collect the out-of-flow descendants at the formatting root level (as opposed to at the containing block level, though they might be the same).
288     // FIXME: Turn this into a register-self as boxes are being inserted.
289     for (auto& descendant : descendantsOfType<Box>(root)) {
290         if (!descendant.isOutOfFlowPositioned())
291             continue;
292         if (&descendant.formattingContextRoot() != &root)
293             continue;
294         formattingState().addOutOfFlowBox(descendant);
295     }
296 }
297
298 #ifndef NDEBUG
299 void FormattingContext::validateGeometryConstraintsAfterLayout() const
300 {
301     auto& formattingContextRoot = root();
302     // FIXME: add a descendantsOfType<> flavor that stops at nested formatting contexts
303     for (auto& layoutBox : descendantsOfType<Box>(formattingContextRoot)) {
304         if (&layoutBox.formattingContextRoot() != &formattingContextRoot)
305             continue;
306         auto& containingBlockGeometry = geometryForBox(*layoutBox.containingBlock());
307         auto& boxGeometry = geometryForBox(layoutBox);
308
309         // 10.3.3 Block-level, non-replaced elements in normal flow
310         // 10.3.7 Absolutely positioned, non-replaced elements
311         if ((layoutBox.isBlockLevelBox() || layoutBox.isOutOfFlowPositioned()) && !layoutBox.isReplacedBox()) {
312             // margin-left + border-left-width + padding-left + width + padding-right + border-right-width + margin-right = width of containing block
313             auto containingBlockWidth = containingBlockGeometry.contentBoxWidth();
314             ASSERT(boxGeometry.horizontalMarginBorderAndPadding() + boxGeometry.contentBoxWidth() == containingBlockWidth);
315         }
316
317         // 10.6.4 Absolutely positioned, non-replaced elements
318         if (layoutBox.isOutOfFlowPositioned() && !layoutBox.isReplacedBox()) {
319             // top + margin-top + border-top-width + padding-top + height + padding-bottom + border-bottom-width + margin-bottom + bottom = height of containing block
320             auto containingBlockHeight = containingBlockGeometry.contentBoxHeight();
321             ASSERT(boxGeometry.top() + boxGeometry.marginBefore() + boxGeometry.borderTop() + boxGeometry.paddingTop().valueOr(0) + boxGeometry.contentBoxHeight()
322                 + boxGeometry.paddingBottom().valueOr(0) + boxGeometry.borderBottom() + boxGeometry.marginAfter() == containingBlockHeight);
323         }
324     }
325 }
326 #endif
327
328 }
329 }
330 #endif