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