072e590649cfb1c8d5c3a4236b6c3345f7c57296
[WebKit-https.git] / Source / WebCore / layout / blockformatting / BlockFormattingContextGeometry.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 "BlockFormattingContext.h"
28
29 #if ENABLE(LAYOUT_FORMATTING_CONTEXT)
30
31 #include "FormattingContext.h"
32 #include "LayoutChildIterator.h"
33 #include "Logging.h"
34 #include <wtf/text/TextStream.h>
35
36 namespace WebCore {
37 namespace Layout {
38
39 static const Container& initialContainingBlock(const Box& layoutBox)
40 {
41     auto* containingBlock = layoutBox.containingBlock();
42     while (containingBlock->containingBlock())
43         containingBlock = containingBlock->containingBlock();
44     return *containingBlock;
45 }
46
47 static bool isStretchedToInitialContainingBlock(const LayoutContext& layoutContext, const Box& layoutBox)
48 {
49     ASSERT(layoutBox.isInFlow());
50     // In quirks mode, body and html stretch to the viewport.
51     if (!layoutContext.inQuirksMode())
52         return false;
53
54     if (!layoutBox.isDocumentBox() && !layoutBox.isBodyBox())
55         return false;
56
57     return layoutBox.style().logicalHeight().isAuto();
58 }
59
60 static HeightAndMargin stretchHeightToInitialContainingBlock(HeightAndMargin heightAndMargin, LayoutUnit initialContainingBlockHeight)
61 {
62     auto verticalMargins = heightAndMargin.margin.top + heightAndMargin.margin.bottom;
63     // Stretch but never overstretch with the margins.
64     if (heightAndMargin.height + verticalMargins < initialContainingBlockHeight)
65         heightAndMargin.height = initialContainingBlockHeight - verticalMargins;
66
67     return heightAndMargin;
68 }
69
70 static WidthAndMargin stretchWidthToInitialContainingBlock(WidthAndMargin widthAndMargin, LayoutUnit initialContainingBlockWidth)
71 {
72     auto horizontalMargins = widthAndMargin.margin.left + widthAndMargin.margin.right;
73     // Stretch but never overstretch with the margins.
74     if (widthAndMargin.width + horizontalMargins < initialContainingBlockWidth)
75         widthAndMargin.width = initialContainingBlockWidth - horizontalMargins;
76
77     return widthAndMargin;
78 }
79
80 HeightAndMargin BlockFormattingContext::Geometry::inFlowNonReplacedHeightAndMargin(const LayoutContext& layoutContext, const Box& layoutBox)
81 {
82     ASSERT(layoutBox.isInFlow() && !layoutBox.replaced());
83     ASSERT(layoutBox.isOverflowVisible());
84
85     auto compute = [&]() -> HeightAndMargin {
86
87         // 10.6.3 Block-level non-replaced elements in normal flow when 'overflow' computes to 'visible'
88         //
89         // If 'margin-top', or 'margin-bottom' are 'auto', their used value is 0.
90         // If 'height' is 'auto', the height depends on whether the element has any block-level children and whether it has padding or borders:
91         // The element's height is the distance from its top content edge to the first applicable of the following:
92         // 1. the bottom edge of the last line box, if the box establishes a inline formatting context with one or more lines
93         // 2. the bottom edge of the bottom (possibly collapsed) margin of its last in-flow child, if the child's bottom margin
94         //    does not collapse with the element's bottom margin
95         // 3. the bottom border edge of the last in-flow child whose top margin doesn't collapse with the element's bottom margin
96         // 4. zero, otherwise
97         // Only children in the normal flow are taken into account (i.e., floating boxes and absolutely positioned boxes are ignored,
98         // and relatively positioned boxes are considered without their offset). Note that the child box may be an anonymous block box.
99
100         auto& style = layoutBox.style();
101         auto containingBlockWidth = layoutContext.displayBoxForLayoutBox(*layoutBox.containingBlock()).contentBoxWidth();
102         auto& displayBox = layoutContext.displayBoxForLayoutBox(layoutBox);
103
104         VerticalEdges nonCollapsedMargin = { computedValueIfNotAuto(style.marginTop(), containingBlockWidth).value_or(0),
105             computedValueIfNotAuto(style.marginBottom(), containingBlockWidth).value_or(0) }; 
106         VerticalEdges collapsedMargin = { MarginCollapse::marginTop(layoutContext, layoutBox), MarginCollapse::marginBottom(layoutContext, layoutBox) };
107         auto borderAndPaddingTop = displayBox.borderTop() + displayBox.paddingTop().value_or(0);
108         
109         auto height = style.logicalHeight();
110         if (!height.isAuto()) {
111             if (height.isFixed())
112                 return { style.logicalHeight().value(), nonCollapsedMargin, collapsedMargin };
113
114             // Most notably height percentage.
115             ASSERT_NOT_IMPLEMENTED_YET();
116         }
117
118         if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowChild())
119             return { 0, nonCollapsedMargin, collapsedMargin };
120
121         // 1. the bottom edge of the last line box, if the box establishes a inline formatting context with one or more lines
122         if (layoutBox.establishesInlineFormattingContext()) {
123             // height = lastLineBox().bottom();
124             return { 0, nonCollapsedMargin, collapsedMargin };
125         }
126
127         // 2. the bottom edge of the bottom (possibly collapsed) margin of its last in-flow child, if the child's bottom margin...
128         auto* lastInFlowChild = downcast<Container>(layoutBox).lastInFlowChild();
129         ASSERT(lastInFlowChild);
130         if (!MarginCollapse::isMarginBottomCollapsedWithParent(layoutContext, *lastInFlowChild)) {
131             auto& lastInFlowDisplayBox = layoutContext.displayBoxForLayoutBox(*lastInFlowChild);
132             return { lastInFlowDisplayBox.bottom() + lastInFlowDisplayBox.marginBottom() - borderAndPaddingTop, nonCollapsedMargin, collapsedMargin };
133         }
134
135         // 3. the bottom border edge of the last in-flow child whose top margin doesn't collapse with the element's bottom margin
136         auto* inFlowChild = lastInFlowChild;
137         while (inFlowChild && MarginCollapse::isMarginTopCollapsedWithParentMarginBottom(*inFlowChild))
138             inFlowChild = inFlowChild->previousInFlowSibling();
139         if (inFlowChild) {
140             auto& inFlowDisplayBox = layoutContext.displayBoxForLayoutBox(*inFlowChild);
141             return { inFlowDisplayBox.top() + inFlowDisplayBox.borderBox().height() - borderAndPaddingTop, nonCollapsedMargin, collapsedMargin };
142         }
143
144         // 4. zero, otherwise
145         return { 0, nonCollapsedMargin, collapsedMargin };
146     };
147
148     auto heightAndMargin = compute();
149
150     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> inflow non-replaced -> height(" << heightAndMargin.height << "px) margin(" << heightAndMargin.margin.top << "px, " << heightAndMargin.margin.bottom << "px) -> layoutBox(" << &layoutBox << ")");
151     return heightAndMargin;
152 }
153
154 WidthAndMargin BlockFormattingContext::Geometry::inFlowNonReplacedWidthAndMargin(const LayoutContext& layoutContext, const Box& layoutBox, std::optional<LayoutUnit> precomputedWidth)
155 {
156     ASSERT(layoutBox.isInFlow() && !layoutBox.replaced());
157
158     auto compute = [&]() {
159
160         // 10.3.3 Block-level, non-replaced elements in normal flow
161         //
162         // The following constraints must hold among the used values of the other properties:
163         // 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' = width of containing block
164         //
165         // 1. If 'width' is not 'auto' and 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' 
166         //    (plus any of 'margin-left' or 'margin-right' that are not 'auto') is larger than the width of the containing block, then
167         //    any 'auto' values for 'margin-left' or 'margin-right' are, for the following rules, treated as zero.
168         //
169         // 2. If all of the above have a computed value other than 'auto', the values are said to be "over-constrained" and one of the used values will
170         //    have to be different from its computed value. If the 'direction' property of the containing block has the value 'ltr', the specified value
171         //    of 'margin-right' is ignored and the value is calculated so as to make the equality true. If the value of 'direction' is 'rtl',
172         //    this happens to 'margin-left' instead.
173         //
174         // 3. If there is exactly one value specified as 'auto', its used value follows from the equality.
175         //
176         // 4. If 'width' is set to 'auto', any other 'auto' values become '0' and 'width' follows from the resulting equality.
177         //
178         // 5. If both 'margin-left' and 'margin-right' are 'auto', their used values are equal. This horizontally centers the element with respect to the
179         //    edges of the containing block.
180
181         auto& style = layoutBox.style();
182         auto* containingBlock = layoutBox.containingBlock();
183         auto containingBlockWidth = layoutContext.displayBoxForLayoutBox(*containingBlock).contentBoxWidth();
184         auto& displayBox = layoutContext.displayBoxForLayoutBox(layoutBox);
185
186         auto width = computedValueIfNotAuto(precomputedWidth ? Length { precomputedWidth.value(), Fixed } : style.logicalWidth(), containingBlockWidth);
187         auto marginLeft = computedValueIfNotAuto(style.marginLeft(), containingBlockWidth);
188         auto marginRight = computedValueIfNotAuto(style.marginRight(), containingBlockWidth);
189         auto nonComputedMarginLeft = marginLeft.value_or(0);
190         auto nonComputedMarginRight = marginRight.value_or(0);
191         auto borderLeft = displayBox.borderLeft();
192         auto borderRight = displayBox.borderRight();
193         auto paddingLeft = displayBox.paddingLeft().value_or(0);
194         auto paddingRight = displayBox.paddingRight().value_or(0);
195
196         // #1
197         if (width) {
198             auto horizontalSpaceForMargin = containingBlockWidth - (marginLeft.value_or(0) + borderLeft + paddingLeft + *width + paddingRight + borderRight + marginRight.value_or(0));
199             if (horizontalSpaceForMargin < 0) {
200                 marginLeft = marginLeft.value_or(0);
201                 marginRight = marginRight.value_or(0);
202             }
203         }
204
205         // #2
206         if (width && marginLeft && marginRight) {
207             if (containingBlock->style().isLeftToRightDirection())
208                 marginRight = containingBlockWidth - (*marginLeft + borderLeft + paddingLeft  + *width + paddingRight + borderRight);
209             else
210                 marginLeft = containingBlockWidth - (borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight);
211         }
212
213         // #3
214         if (!marginLeft && width && marginRight)
215             marginLeft = containingBlockWidth - (borderLeft + paddingLeft  + *width + paddingRight + borderRight + *marginRight);
216         else if (marginLeft && !width && marginRight)
217             width = containingBlockWidth - (*marginLeft + borderLeft + paddingLeft + paddingRight + borderRight + *marginRight);
218         else if (marginLeft && width && !marginRight)
219             marginRight = containingBlockWidth - (*marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight);
220
221         // #4
222         if (!width) {
223             marginLeft = marginLeft.value_or(0);
224             marginRight = marginRight.value_or(0);
225             width = containingBlockWidth - (*marginLeft + borderLeft + paddingLeft + paddingRight + borderRight + *marginRight);
226         }
227
228         // #5
229         if (!marginLeft && !marginRight) {
230             auto horizontalSpaceForMargin = containingBlockWidth - (borderLeft + paddingLeft  + *width + paddingRight + borderRight);
231             marginLeft = marginRight = horizontalSpaceForMargin / 2;
232         }
233
234         ASSERT(width);
235         ASSERT(marginLeft);
236         ASSERT(marginRight);
237
238         return WidthAndMargin { *width, { *marginLeft, *marginRight }, { nonComputedMarginLeft, nonComputedMarginRight } };
239     };
240
241     auto widthAndMargin = compute();
242     if (!isStretchedToInitialContainingBlock(layoutContext, layoutBox)) {
243         LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width][Margin] -> inflow non-replaced -> width(" << widthAndMargin.width << "px) margin(" << widthAndMargin.margin.left << "px, " << widthAndMargin.margin.right << "px) -> layoutBox(" << &layoutBox << ")");
244         return widthAndMargin;
245     }
246
247     auto initialContainingBlockWidth = layoutContext.displayBoxForLayoutBox(initialContainingBlock(layoutBox)).contentBoxWidth();
248     widthAndMargin = stretchWidthToInitialContainingBlock(widthAndMargin, initialContainingBlockWidth);
249
250     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width][Margin] -> inflow non-replaced -> streched to viewport-> width(" << widthAndMargin.width << "px) margin(" << widthAndMargin.margin.left << "px, " << widthAndMargin.margin.right << "px) -> layoutBox(" << &layoutBox << ")");
251     return widthAndMargin;
252 }
253
254 WidthAndMargin BlockFormattingContext::Geometry::inFlowReplacedWidthAndMargin(const LayoutContext& layoutContext, const Box& layoutBox, std::optional<LayoutUnit> precomputedWidth)
255 {
256     ASSERT(layoutBox.isInFlow() && layoutBox.replaced());
257
258     // 10.3.4 Block-level, replaced elements in normal flow
259     //
260     // 1. The used value of 'width' is determined as for inline replaced elements.
261     // 2. Then the rules for non-replaced block-level elements are applied to determine the margins.
262
263     // #1
264     auto width = inlineReplacedWidthAndMargin(layoutContext, layoutBox, precomputedWidth).width;
265     // #2
266     auto nonReplacedWidthAndMargin = inFlowNonReplacedWidthAndMargin(layoutContext, layoutBox, width);
267
268     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width][Margin] -> inflow replaced -> width(" << width << "px) margin(" << nonReplacedWidthAndMargin.margin.left << "px, " << nonReplacedWidthAndMargin.margin.right << "px) -> layoutBox(" << &layoutBox << ")");
269     return { width, nonReplacedWidthAndMargin.margin, nonReplacedWidthAndMargin.nonComputedMargin };
270 }
271
272 Position BlockFormattingContext::Geometry::staticPosition(const LayoutContext& layoutContext, const Box& layoutBox)
273 {
274     // https://www.w3.org/TR/CSS22/visuren.html#block-formatting
275     // In a block formatting context, boxes are laid out one after the other, vertically, beginning at the top of a containing block.
276     // The vertical distance between two sibling boxes is determined by the 'margin' properties.
277     // Vertical margins between adjacent block-level boxes in a block formatting context collapse.
278     // In a block formatting context, each box's left outer edge touches the left edge of the containing block (for right-to-left formatting, right edges touch).
279
280     LayoutUnit top;
281     auto& containingBlockDisplayBox = layoutContext.displayBoxForLayoutBox(*layoutBox.containingBlock());
282     if (auto* previousInFlowSibling = layoutBox.previousInFlowSibling()) {
283         auto& previousInFlowDisplayBox = layoutContext.displayBoxForLayoutBox(*previousInFlowSibling);
284         top = previousInFlowDisplayBox.bottom() + previousInFlowDisplayBox.marginBottom();
285     } else
286         top = containingBlockDisplayBox.contentBoxTop();
287
288     auto left = containingBlockDisplayBox.contentBoxLeft();
289     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position] -> static -> top(" << top << "px) left(" << left << "px) layoutBox(" << &layoutBox << ")");
290     return { left, top };
291 }
292
293 Position BlockFormattingContext::Geometry::inFlowPositionedPosition(const LayoutContext& layoutContext, const Box& layoutBox)
294 {
295     ASSERT(layoutBox.isInFlowPositioned());
296
297     // 9.4.3 Relative positioning
298     //
299     // The 'top' and 'bottom' properties move relatively positioned element(s) up or down without changing their size.
300     // Top' moves the boxes down, and 'bottom' moves them up. Since boxes are not split or stretched as a result of 'top' or 'bottom', the used values are always: top = -bottom.
301     //
302     // 1. If both are 'auto', their used values are both '0'.
303     // 2. If one of them is 'auto', it becomes the negative of the other.
304     // 3. If neither is 'auto', 'bottom' is ignored (i.e., the used value of 'bottom' will be minus the value of 'top').
305
306     auto& style = layoutBox.style();
307     auto& displayBox = layoutContext.displayBoxForLayoutBox(layoutBox);
308     auto& containingBlock = *layoutBox.containingBlock();
309     auto containingBlockWidth = layoutContext.displayBoxForLayoutBox(containingBlock).contentBoxWidth();
310
311     auto top = computedValueIfNotAuto(style.logicalTop(), containingBlockWidth);
312     auto bottom = computedValueIfNotAuto(style.logicalBottom(), containingBlockWidth);
313
314     if (!top && !bottom) {
315         // #1
316         top = bottom = { 0 };
317     } else if (!top) {
318         // #2
319         top = -*bottom;
320     } else if (!bottom) {
321         // #3
322         bottom = -*top;
323     } else {
324         // #4
325         bottom = std::nullopt;
326     }
327
328     // For relatively positioned elements, 'left' and 'right' move the box(es) horizontally, without changing their size.
329     // 'Left' moves the boxes to the right, and 'right' moves them to the left.
330     // Since boxes are not split or stretched as a result of 'left' or 'right', the used values are always: left = -right.
331     //
332     // 1. If both 'left' and 'right' are 'auto' (their initial values), the used values are '0' (i.e., the boxes stay in their original position).
333     // 2. If 'left' is 'auto', its used value is minus the value of 'right' (i.e., the boxes move to the left by the value of 'right').
334     // 3. If 'right' is specified as 'auto', its used value is minus the value of 'left'.
335     // 4. If neither 'left' nor 'right' is 'auto', the position is over-constrained, and one of them has to be ignored.
336     //    If the 'direction' property of the containing block is 'ltr', the value of 'left' wins and 'right' becomes -'left'.
337     //    If 'direction' of the containing block is 'rtl', 'right' wins and 'left' is ignored.
338
339     auto left = computedValueIfNotAuto(style.logicalLeft(), containingBlockWidth);
340     auto right = computedValueIfNotAuto(style.logicalRight(), containingBlockWidth);
341
342     if (!left && !right) {
343         // #1
344         left = right = { 0 };
345     } else if (!left) {
346         // #2
347         left = -*right;
348     } else if (!right) {
349         // #3
350         right = -*left;
351     } else {
352         // #4
353         auto isLeftToRightDirection = containingBlock.style().isLeftToRightDirection();
354         if (isLeftToRightDirection)
355             right = -*left;
356         else
357             left = std::nullopt;
358     }
359
360     ASSERT(!bottom || *top == -*bottom);
361     ASSERT(!left || *left == -*right);
362
363     auto newTopPosition = displayBox.top() + *top;
364     auto newLeftPosition = displayBox.left() + left.value_or(-*right);
365
366     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position] -> positioned inflow -> top(" << newTopPosition << "px) left(" << newLeftPosition << "px) layoutBox(" << &layoutBox << ")");
367     return { newLeftPosition, newTopPosition };
368 }
369
370 HeightAndMargin BlockFormattingContext::Geometry::inFlowHeightAndMargin(const LayoutContext& layoutContext, const Box& layoutBox)
371 {
372     ASSERT(layoutBox.isInFlow());
373
374     // 10.6.2 Inline replaced elements, block-level replaced elements in normal flow, 'inline-block'
375     // replaced elements in normal flow and floating replaced elements
376     if (layoutBox.replaced())
377         return inlineReplacedHeightAndMargin(layoutContext, layoutBox);
378
379     HeightAndMargin heightAndMargin;
380     // TODO: Figure out the case for the document element. Let's just complicated-case it for now.
381     if (layoutBox.isOverflowVisible() && !layoutBox.isDocumentBox())
382         heightAndMargin = inFlowNonReplacedHeightAndMargin(layoutContext, layoutBox);
383     else {
384         // 10.6.6 Complicated cases
385         // Block-level, non-replaced elements in normal flow when 'overflow' does not compute to 'visible' (except if the 'overflow' property's value has been propagated to the viewport).
386         heightAndMargin = complicatedCases(layoutContext, layoutBox);
387     }
388
389     if (!isStretchedToInitialContainingBlock(layoutContext, layoutBox))
390         return heightAndMargin;
391
392     auto initialContainingBlockHeight = layoutContext.displayBoxForLayoutBox(initialContainingBlock(layoutBox)).contentBoxHeight();
393     heightAndMargin = stretchHeightToInitialContainingBlock(heightAndMargin, initialContainingBlockHeight);
394
395     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> inflow non-replaced -> streched to viewport -> height(" << heightAndMargin.height << "px) margin(" << heightAndMargin.margin.top << "px, " << heightAndMargin.margin.bottom << "px) -> layoutBox(" << &layoutBox << ")");
396     return heightAndMargin;
397 }
398
399 WidthAndMargin BlockFormattingContext::Geometry::inFlowWidthAndMargin(const LayoutContext& layoutContext, const Box& layoutBox, std::optional<LayoutUnit> precomputedWidth)
400 {
401     ASSERT(layoutBox.isInFlow());
402
403     if (!layoutBox.replaced())
404         return inFlowNonReplacedWidthAndMargin(layoutContext, layoutBox, precomputedWidth);
405     return inFlowReplacedWidthAndMargin(layoutContext, layoutBox, precomputedWidth);
406 }
407
408 bool BlockFormattingContext::Geometry::instrinsicWidthConstraintsNeedChildrenWidth(const Box& layoutBox)
409 {
410     if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowOrFloatingChild())
411         return false;
412     return layoutBox.style().width().isAuto();
413 }
414
415 FormattingContext::InstrinsicWidthConstraints BlockFormattingContext::Geometry::instrinsicWidthConstraints(const LayoutContext& layoutContext, const Box& layoutBox)
416 {
417     auto& style = layoutBox.style();
418     if (auto width = fixedValue(style.logicalWidth()))
419         return { *width, *width };
420
421     // Minimum/maximum width can't be depending on the containing block's width.
422     if (!style.logicalWidth().isAuto())
423         return { };
424
425     if (!is<Container>(layoutBox))
426         return { };
427
428     LayoutUnit minimumIntrinsicWidth;
429     LayoutUnit maximumIntrinsicWidth;
430
431     for (auto& child : childrenOfType<Box>(downcast<Container>(layoutBox))) {
432         if (child.isOutOfFlowPositioned())
433             continue;
434         auto& formattingState = layoutContext.formattingStateForBox(child);
435         ASSERT(formattingState.isBlockFormattingState());
436         auto childInstrinsicWidthConstraints = formattingState.instrinsicWidthConstraints(child);
437         ASSERT(childInstrinsicWidthConstraints);
438         
439         auto& style = child.style();
440         auto horizontalMarginBorderAndPadding = fixedValue(style.marginLeft()).value_or(0)
441             + LayoutUnit { style.borderLeftWidth() }
442             + fixedValue(style.paddingLeft()).value_or(0)
443             + fixedValue(style.paddingRight()).value_or(0)
444             + LayoutUnit { style.borderRightWidth() }
445             + fixedValue(style.marginRight()).value_or(0);
446
447         minimumIntrinsicWidth = std::max(minimumIntrinsicWidth, childInstrinsicWidthConstraints->minimum + horizontalMarginBorderAndPadding); 
448         maximumIntrinsicWidth = std::max(maximumIntrinsicWidth, childInstrinsicWidthConstraints->maximum + horizontalMarginBorderAndPadding); 
449     }
450
451     return { minimumIntrinsicWidth, maximumIntrinsicWidth };
452 }
453
454 LayoutUnit BlockFormattingContext::Geometry::estimatedMarginTop(const LayoutContext& layoutContext, const Box& layoutBox)
455 {
456     ASSERT(layoutBox.isBlockLevelBox());
457     // Can't estimate vertical margins for out of flow boxes (and we shouldn't need to do it for float boxes).
458     ASSERT(layoutBox.isInFlow());
459     // Can't cross block formatting context boundary.
460     ASSERT(!layoutBox.establishesBlockFormattingContext());
461
462     // Let's just use the normal path for now.
463     return MarginCollapse::marginTop(layoutContext, layoutBox);
464 }
465
466
467 }
468 }
469
470 #endif