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