[LFC][BCF] Add support for block level non-replaced inflow height percentage
[WebKit-https.git] / Source / WebCore / layout / FormattingContextGeometry.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 "FloatingState.h"
32 #include "FormattingState.h"
33 #include "InlineFormattingState.h"
34
35 namespace WebCore {
36 namespace Layout {
37
38 static inline bool isHeightAuto(const Box& layoutBox)
39 {
40     // 10.5 Content height: the 'height' property
41     //
42     // The percentage is calculated with respect to the height of the generated box's containing block.
43     // If the height of the containing block is not specified explicitly (i.e., it depends on content height),
44     // and this element is not absolutely positioned, the used height is calculated as if 'auto' was specified.
45
46     auto height = layoutBox.style().logicalHeight();
47     if (height.isAuto())
48         return true;
49
50     if (height.isPercent()) {
51         if (layoutBox.isOutOfFlowPositioned())
52             return false;
53
54         return !layoutBox.containingBlock()->style().logicalHeight().isFixed();
55     }
56
57     return false;
58 }
59
60 std::optional<LayoutUnit> FormattingContext::Geometry::computedHeightValue(const LayoutState& layoutState, const Box& layoutBox, HeightType heightType)
61 {
62     auto& style = layoutBox.style();
63     auto height = heightType == HeightType::Normal ? style.logicalHeight() : heightType == HeightType::Min ? style.logicalMinHeight() : style.logicalMaxHeight();
64     if (height.isUndefined() || height.isAuto())
65         return { };
66
67     if (height.isFixed())
68         return { height.value() };
69
70     std::optional<LayoutUnit> containingBlockHeightValue;
71     auto containingBlockHeight = layoutBox.containingBlock()->style().logicalHeight();
72     if (containingBlockHeight.isFixed())
73         containingBlockHeightValue = { containingBlockHeight.value() };
74     else if (layoutBox.isOutOfFlowPositioned()) {
75         // Containing block's height is already computed.
76         containingBlockHeightValue = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock()).height();
77     }
78
79     if (!containingBlockHeightValue)
80         return { };
81
82     return valueForLength(height, *containingBlockHeightValue);
83 }
84
85 static LayoutUnit contentHeightForFormattingContextRoot(const LayoutState& layoutState, const Box& layoutBox)
86 {
87     ASSERT(isHeightAuto(layoutBox) && (layoutBox.establishesFormattingContext() || layoutBox.isDocumentBox()));
88
89     // 10.6.7 'Auto' heights for block formatting context roots
90
91     // If it only has inline-level children, the height is the distance between the top of the topmost line box and the bottom of the bottommost line box.
92     // If it has block-level children, the height is the distance between the top margin-edge of the topmost block-level
93     // child box and the bottom margin-edge of the bottommost block-level child box.
94
95     // In addition, if the element has any floating descendants whose bottom margin edge is below the element's bottom content edge,
96     // then the height is increased to include those edges. Only floats that participate in this block formatting context are taken
97     // into account, e.g., floats inside absolutely positioned descendants or other floats are not.
98     if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowOrFloatingChild())
99         return 0;
100
101     LayoutUnit top;
102     LayoutUnit bottom;
103     auto& formattingRootContainer = downcast<Container>(layoutBox);
104     if (formattingRootContainer.establishesInlineFormattingContext()) {
105         // This is temp and will be replaced by the correct display box once inline runs move over to the display tree.
106         auto& inlineRuns = downcast<InlineFormattingState>(layoutState.establishedFormattingState(layoutBox)).inlineRuns();
107         if (!inlineRuns.isEmpty()) {
108             top = inlineRuns[0].logicalTop();
109             bottom =  inlineRuns.last().logicalBottom();
110         }
111     } else if (formattingRootContainer.establishesBlockFormattingContext() || layoutBox.isDocumentBox()) {
112         auto& firstDisplayBox = layoutState.displayBoxForLayoutBox(*formattingRootContainer.firstInFlowChild());
113         auto& lastDisplayBox = layoutState.displayBoxForLayoutBox(*formattingRootContainer.lastInFlowChild());
114         top = firstDisplayBox.rectWithMargin().top();
115         bottom = lastDisplayBox.rectWithMargin().bottom();
116     }
117
118     auto* formattingContextRoot = &layoutBox;
119     // TODO: The document renderer is not a formatting context root by default at all. Need to find out what it is.
120     if (!layoutBox.establishesFormattingContext()) {
121         ASSERT(layoutBox.isDocumentBox());
122         formattingContextRoot = &layoutBox.formattingContextRoot();
123     }
124
125     auto floatsBottom = layoutState.establishedFormattingState(*formattingContextRoot).floatingState().bottom(*formattingContextRoot);
126     if (floatsBottom)
127         bottom = std::max(*floatsBottom, bottom);
128
129     auto computedHeight = bottom - top;
130     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height] -> content height for formatting context root -> height(" << computedHeight << "px) layoutBox("<< &layoutBox << ")");
131     return computedHeight;
132 }
133
134 std::optional<LayoutUnit> FormattingContext::Geometry::computedValueIfNotAuto(const Length& geometryProperty, LayoutUnit containingBlockWidth)
135 {
136     if (geometryProperty.isUndefined())
137         return std::nullopt;
138
139     if (geometryProperty.isAuto())
140         return std::nullopt;
141
142     return valueForLength(geometryProperty, containingBlockWidth);
143 }
144
145 std::optional<LayoutUnit> FormattingContext::Geometry::fixedValue(const Length& geometryProperty)
146 {
147     if (!geometryProperty.isFixed())
148         return std::nullopt;
149     return { geometryProperty.value() };
150 }
151
152 // https://www.w3.org/TR/CSS22/visudet.html#min-max-heights
153 // Specifies a percentage for determining the used value. The percentage is calculated with respect to the height of the generated box's containing block.
154 // If the height of the containing block is not specified explicitly (i.e., it depends on content height), and this element is not absolutely positioned,
155 // the percentage value is treated as '0' (for 'min-height') or 'none' (for 'max-height').
156 std::optional<LayoutUnit> FormattingContext::Geometry::computedMaxHeight(const LayoutState& layoutState, const Box& layoutBox)
157 {
158     return computedHeightValue(layoutState, layoutBox, HeightType::Max);
159 }
160
161 std::optional<LayoutUnit> FormattingContext::Geometry::computedMinHeight(const LayoutState& layoutState, const Box& layoutBox)
162 {
163     if (auto minHeightValue = computedHeightValue(layoutState, layoutBox, HeightType::Min))
164         return minHeightValue;
165
166     return { 0 };
167 }
168
169 static LayoutUnit staticVerticalPositionForOutOfFlowPositioned(const LayoutState& layoutState, const Box& layoutBox)
170 {
171     ASSERT(layoutBox.isOutOfFlowPositioned());
172
173     // For the purposes of this section and the next, the term "static position" (of an element) refers, roughly, to the position an element would have
174     // had in the normal flow. More precisely, the static position for 'top' is the distance from the top edge of the containing block to the top margin
175     // edge of a hypothetical box that would have been the first box of the element if its specified 'position' value had been 'static' and its specified
176     // 'float' had been 'none' and its specified 'clear' had been 'none'. (Note that due to the rules in section 9.7 this might require also assuming a different
177     // computed value for 'display'.) The value is negative if the hypothetical box is above the containing block.
178
179     // Start with this box's border box offset from the parent's border box.
180     LayoutUnit top;
181     if (auto* previousInFlowSibling = layoutBox.previousInFlowSibling()) {
182         // Add sibling offset
183         auto& previousInFlowDisplayBox = layoutState.displayBoxForLayoutBox(*previousInFlowSibling);
184         top += previousInFlowDisplayBox.bottom() + previousInFlowDisplayBox.nonCollapsedMarginBottom();
185     } else {
186         ASSERT(layoutBox.parent());
187         top = layoutState.displayBoxForLayoutBox(*layoutBox.parent()).contentBoxTop();
188     }
189
190     // Resolve top all the way up to the containing block.
191     auto* containingBlock = layoutBox.containingBlock();
192     for (auto* container = layoutBox.parent(); container != containingBlock; container = container->containingBlock()) {
193         auto& displayBox = layoutState.displayBoxForLayoutBox(*container);
194         // Display::Box::top is the border box top position in its containing block's coordinate system.
195         top += displayBox.top();
196         ASSERT(!container->isPositioned());
197     }
198     // FIXME: floatings need to be taken into account.
199     return top;
200 }
201
202 static LayoutUnit staticHorizontalPositionForOutOfFlowPositioned(const LayoutState& layoutState, const Box& layoutBox)
203 {
204     ASSERT(layoutBox.isOutOfFlowPositioned());
205     // See staticVerticalPositionForOutOfFlowPositioned for the definition of the static position.
206
207     // Start with this box's border box offset from the parent's border box.
208     ASSERT(layoutBox.parent());
209     auto left = layoutState.displayBoxForLayoutBox(*layoutBox.parent()).contentBoxLeft();
210
211     // Resolve left all the way up to the containing block.
212     auto* containingBlock = layoutBox.containingBlock();
213     for (auto* container = layoutBox.parent(); container != containingBlock; container = container->containingBlock()) {
214         auto& displayBox = layoutState.displayBoxForLayoutBox(*container);
215         // Display::Box::left is the border box left position in its containing block's coordinate system.
216         left += displayBox.left();
217         ASSERT(!container->isPositioned());
218     }
219     // FIXME: floatings need to be taken into account.
220     return left;
221 }
222
223 LayoutUnit FormattingContext::Geometry::shrinkToFitWidth(LayoutState& layoutState, const Box& formattingRoot)
224 {
225     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width] -> shrink to fit -> unsupported -> width(" << LayoutUnit { } << "px) layoutBox: " << &formattingRoot << ")");
226     ASSERT(formattingRoot.establishesFormattingContext());
227
228     // Calculation of the shrink-to-fit width is similar to calculating the width of a table cell using the automatic table layout algorithm.
229     // Roughly: calculate the preferred width by formatting the content without breaking lines other than where explicit line breaks occur,
230     // and also calculate the preferred minimum width, e.g., by trying all possible line breaks. CSS 2.2 does not define the exact algorithm.
231     // Thirdly, find the available width: in this case, this is the width of the containing block minus the used values of 'margin-left', 'border-left-width',
232     // 'padding-left', 'padding-right', 'border-right-width', 'margin-right', and the widths of any relevant scroll bars.
233
234     // Then the shrink-to-fit width is: min(max(preferred minimum width, available width), preferred width).
235     auto availableWidth = layoutState.displayBoxForLayoutBox(*formattingRoot.containingBlock()).width();
236     auto& formattingState = layoutState.createFormattingStateForFormattingRootIfNeeded(formattingRoot);
237     auto instrinsicWidthConstraints = formattingState.formattingContext(formattingRoot)->instrinsicWidthConstraints();
238     return std::min(std::max(instrinsicWidthConstraints.minimum, availableWidth), instrinsicWidthConstraints.maximum);
239 }
240
241 VerticalGeometry FormattingContext::Geometry::outOfFlowNonReplacedVerticalGeometry(const LayoutState& layoutState, const Box& layoutBox, std::optional<LayoutUnit> usedHeight)
242 {
243     ASSERT(layoutBox.isOutOfFlowPositioned() && !layoutBox.replaced());
244
245     // 10.6.4 Absolutely positioned, non-replaced elements
246     //
247     // For absolutely positioned elements, the used values of the vertical dimensions must satisfy this constraint:
248     // 'top' + 'margin-top' + 'border-top-width' + 'padding-top' + 'height' + 'padding-bottom' + 'border-bottom-width' + 'margin-bottom' + 'bottom'
249     // = height of containing block
250
251     // If all three of 'top', 'height', and 'bottom' are auto, set 'top' to the static position and apply rule number three below.
252
253     // If none of the three are 'auto': If both 'margin-top' and 'margin-bottom' are 'auto', solve the equation under the extra
254     // constraint that the two margins get equal values. If one of 'margin-top' or 'margin-bottom' is 'auto', solve the equation for that value.
255     // If the values are over-constrained, ignore the value for 'bottom' and solve for that value.
256
257     // Otherwise, pick the one of the following six rules that applies.
258
259     // 1. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then the height is based on the content per 10.6.7,
260     //     set 'auto' values for 'margin-top' and 'margin-bottom' to 0, and solve for 'top'
261     // 2. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then set 'top' to the static position, set 'auto' values for
262     //    'margin-top' and 'margin-bottom' to 0, and solve for 'bottom'
263     // 3. 'height' and 'bottom' are 'auto' and 'top' is not 'auto', then the height is based on the content per 10.6.7, set 'auto'
264     //     values for 'margin-top' and 'margin-bottom' to 0, and solve for 'bottom'
265     // 4. 'top' is 'auto', 'height' and 'bottom' are not 'auto', then set 'auto' values for 'margin-top' and 'margin-bottom' to 0, and solve for 'top'
266     // 5. 'height' is 'auto', 'top' and 'bottom' are not 'auto', then 'auto' values for 'margin-top' and 'margin-bottom' are set to 0 and solve for 'height'
267     // 6. 'bottom' is 'auto', 'top' and 'height' are not 'auto', then set 'auto' values for 'margin-top' and 'margin-bottom' to 0 and solve for 'bottom'
268
269     auto& style = layoutBox.style();
270     auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
271     auto& containingBlockDisplayBox = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock());
272     auto containingBlockHeight = containingBlockDisplayBox.height();
273     auto containingBlockWidth = containingBlockDisplayBox.width();
274
275     auto top = computedValueIfNotAuto(style.logicalTop(), containingBlockWidth);
276     auto bottom = computedValueIfNotAuto(style.logicalBottom(), containingBlockWidth);
277     auto height = usedHeight ? usedHeight.value() : computedHeightValue(layoutState, layoutBox, HeightType::Normal);
278     auto marginTop = computedValueIfNotAuto(style.marginTop(), containingBlockWidth);
279     auto marginBottom = computedValueIfNotAuto(style.marginBottom(), containingBlockWidth);
280     auto paddingTop = displayBox.paddingTop().value_or(0);
281     auto paddingBottom = displayBox.paddingBottom().value_or(0);
282     auto borderTop = displayBox.borderTop();
283     auto borderBottom = displayBox.borderBottom();
284
285     if (!top && !height && !bottom)
286         top = staticVerticalPositionForOutOfFlowPositioned(layoutState, layoutBox);
287
288     if (top && height && bottom) {
289         if (!marginTop && !marginBottom) {
290             auto marginTopAndBottom = containingBlockHeight - (*top + borderTop + paddingTop + *height + paddingBottom + borderBottom + *bottom);
291             marginTop = marginBottom = marginTopAndBottom / 2;
292         } else if (!marginTop)
293             marginTop = containingBlockHeight - (*top + borderTop + paddingTop + *height + paddingBottom + borderBottom + *marginBottom + *bottom);
294         else
295             marginBottom = containingBlockHeight - (*top + *marginTop + borderTop + paddingTop + *height + paddingBottom + borderBottom + *bottom);
296         // Over-constrained?
297         auto boxHeight = *top + *marginTop + borderTop + paddingTop + *height + paddingBottom + borderBottom + *marginBottom + *bottom;
298         if (boxHeight > containingBlockHeight)
299             bottom = containingBlockHeight - (*top + *marginTop + borderTop + paddingTop + *height + paddingBottom + borderBottom + *marginBottom); 
300     }
301
302     if (!top && !height && bottom) {
303         // #1
304         height = contentHeightForFormattingContextRoot(layoutState, layoutBox);
305         marginTop = marginTop.value_or(0);
306         marginBottom = marginBottom.value_or(0);
307         top = containingBlockHeight - (*marginTop + borderTop + paddingTop + *height + paddingBottom + borderBottom + *marginBottom + *bottom); 
308     }
309
310     if (!top && !bottom && height) {
311         // #2
312         top = staticVerticalPositionForOutOfFlowPositioned(layoutState, layoutBox);
313         marginTop = marginTop.value_or(0);
314         marginBottom = marginBottom.value_or(0);
315         bottom = containingBlockHeight - (*top + *marginTop + borderTop + paddingTop + *height + paddingBottom + borderBottom + *marginBottom); 
316     }
317
318     if (!height && !bottom && top) {
319         // #3
320         height = contentHeightForFormattingContextRoot(layoutState, layoutBox);
321         marginTop = marginTop.value_or(0);
322         marginBottom = marginBottom.value_or(0);
323         bottom = containingBlockHeight - (*top + *marginTop + borderTop + paddingTop + *height + paddingBottom + borderBottom + *marginBottom); 
324     }
325
326     if (!top && height && bottom) {
327         // #4
328         marginTop = marginTop.value_or(0);
329         marginBottom = marginBottom.value_or(0);
330         top = containingBlockHeight - (*marginTop + borderTop + paddingTop + *height + paddingBottom + borderBottom + *marginBottom + *bottom); 
331     }
332
333     if (!height && top && bottom) {
334         // #5
335         marginTop = marginTop.value_or(0);
336         marginBottom = marginBottom.value_or(0);
337         height = containingBlockHeight - (*top + *marginTop + borderTop + paddingTop + paddingBottom + borderBottom + *marginBottom + *bottom); 
338     }
339
340     if (!bottom && top && height) {
341         // #6
342         marginTop = marginTop.value_or(0);
343         marginBottom = marginBottom.value_or(0);
344         bottom = containingBlockHeight - (*top + *marginTop + borderTop + paddingTop + *height + paddingBottom + borderBottom + *marginBottom); 
345     }
346
347     ASSERT(top);
348     ASSERT(bottom);
349     ASSERT(height);
350     ASSERT(marginTop);
351     ASSERT(marginBottom);
352
353     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position][Height][Margin] -> out-of-flow non-replaced -> top(" << *top << "px) bottom("  << *bottom << "px) height(" << *height << "px) margin(" << *marginTop << "px, "  << *marginBottom << "px) layoutBox(" << &layoutBox << ")");
354     return { *top, *bottom, { *height, { *marginTop, *marginBottom }, { } } };
355 }
356
357 HorizontalGeometry FormattingContext::Geometry::outOfFlowNonReplacedHorizontalGeometry(LayoutState& layoutState, const Box& layoutBox, std::optional<LayoutUnit> usedWidth)
358 {
359     ASSERT(layoutBox.isOutOfFlowPositioned() && !layoutBox.replaced());
360     
361     // 10.3.7 Absolutely positioned, non-replaced elements
362     //
363     // 'left' + 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' + 'right'
364     // = width of containing block
365
366     // If all three of 'left', 'width', and 'right' are 'auto': First set any 'auto' values for 'margin-left' and 'margin-right' to 0.
367     // Then, if the 'direction' property of the element establishing the static-position containing block is 'ltr' set 'left' to the static
368     // position and apply rule number three below; otherwise, set 'right' to the static position and apply rule number one below.
369     //
370     // If none of the three is 'auto': If both 'margin-left' and 'margin-right' are 'auto', solve the equation under the extra constraint that the two margins get equal values,
371     // unless this would make them negative, in which case when direction of the containing block is 'ltr' ('rtl'), set 'margin-left' ('margin-right') to zero and
372     // solve for 'margin-right' ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto', solve the equation for that value.
373     // If the values are over-constrained, ignore the value for 'left' (in case the 'direction' property of the containing block is 'rtl') or 'right'
374     // (in case 'direction' is 'ltr') and solve for that value.
375     //
376     // Otherwise, set 'auto' values for 'margin-left' and 'margin-right' to 0, and pick the one of the following six rules that applies.
377     //
378     // 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the width is shrink-to-fit. Then solve for 'left'
379     // 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if the 'direction' property of the element establishing the static-position 
380     //    containing block is 'ltr' set 'left' to the static position, otherwise set 'right' to the static position.
381     //    Then solve for 'left' (if 'direction is 'rtl') or 'right' (if 'direction' is 'ltr').
382     // 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the width is shrink-to-fit . Then solve for 'right'
383     // 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve for 'left'
384     // 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve for 'width'
385     // 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve for 'right'
386
387     auto& style = layoutBox.style();
388     auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
389     auto& containingBlock = *layoutBox.containingBlock();
390     auto containingBlockWidth = layoutState.displayBoxForLayoutBox(containingBlock).contentBoxWidth();
391     auto isLeftToRightDirection = containingBlock.style().isLeftToRightDirection();
392     
393     auto left = computedValueIfNotAuto(style.logicalLeft(), containingBlockWidth);
394     auto right = computedValueIfNotAuto(style.logicalRight(), containingBlockWidth);
395     auto width = computedValueIfNotAuto(usedWidth ? Length { usedWidth.value(), Fixed } : style.logicalWidth(), containingBlockWidth);
396     auto marginLeft = computedValueIfNotAuto(style.marginLeft(), containingBlockWidth);
397     auto marginRight = computedValueIfNotAuto(style.marginRight(), containingBlockWidth);
398     auto nonComputedMarginLeft = marginLeft.value_or(0);
399     auto nonComputedMarginRight = marginRight.value_or(0);
400     auto paddingLeft = displayBox.paddingLeft().value_or(0);
401     auto paddingRight = displayBox.paddingRight().value_or(0);
402     auto borderLeft = displayBox.borderLeft();
403     auto borderRight = displayBox.borderRight();
404
405     if (!left && !width && !right) {
406         // If all three of 'left', 'width', and 'right' are 'auto': First set any 'auto' values for 'margin-left' and 'margin-right' to 0.
407         // Then, if the 'direction' property of the element establishing the static-position containing block is 'ltr' set 'left' to the static
408         // position and apply rule number three below; otherwise, set 'right' to the static position and apply rule number one below.
409         marginLeft = marginLeft.value_or(0);
410         marginRight = marginRight.value_or(0);
411
412         auto staticHorizontalPosition = staticHorizontalPositionForOutOfFlowPositioned(layoutState, layoutBox);
413         if (isLeftToRightDirection)
414             left = staticHorizontalPosition;
415         else
416             right = staticHorizontalPosition;
417     } else if (left && width && right) {
418         // If none of the three is 'auto': If both 'margin-left' and 'margin-right' are 'auto', solve the equation under the extra constraint that the two margins get equal values,
419         // unless this would make them negative, in which case when direction of the containing block is 'ltr' ('rtl'), set 'margin-left' ('margin-right') to zero and
420         // solve for 'margin-right' ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto', solve the equation for that value.
421         // If the values are over-constrained, ignore the value for 'left' (in case the 'direction' property of the containing block is 'rtl') or 'right'
422         // (in case 'direction' is 'ltr') and solve for that value.
423         if (!marginLeft && !marginRight) {
424             auto marginLeftAndRight = containingBlockWidth - (*left + borderLeft + paddingLeft + *width + paddingRight + borderRight + *right);
425             if (marginLeftAndRight >= 0)
426                 marginLeft = marginRight = marginLeftAndRight / 2;  
427             else {
428                 if (isLeftToRightDirection) {
429                     marginLeft = LayoutUnit { 0 };
430                     marginRight = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight + *right);
431                 } else {
432                     marginRight = LayoutUnit { 0 };
433                     marginLeft = containingBlockWidth - (*left + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight + *right);
434                 }
435             }
436         } else if (!marginLeft) {
437             marginLeft = containingBlockWidth - (*left + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight + *right);
438             // Overconstrained? Ignore right (left).
439             if (*marginLeft < 0) {
440                 if (isLeftToRightDirection)
441                     marginLeft = containingBlockWidth - (*left + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight);
442                 else
443                     marginLeft = containingBlockWidth - (borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight + *right);
444             }
445         } else if (!marginRight) {
446             marginRight = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight + *right);
447             // Overconstrained? Ignore right (left).
448             if (*marginRight < 0) {
449                 if (isLeftToRightDirection)
450                     marginRight = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight);
451                 else
452                     marginRight = containingBlockWidth - (*marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight + *right);
453             }
454         }
455     } else {
456         // Otherwise, set 'auto' values for 'margin-left' and 'margin-right' to 0, and pick the one of the following six rules that applies.
457         marginLeft = marginLeft.value_or(0);
458         marginRight = marginRight.value_or(0);
459     }
460
461     ASSERT(marginLeft);
462     ASSERT(marginRight);
463
464     if (!left && !width && right) {
465         // #1
466         width = shrinkToFitWidth(layoutState, layoutBox);
467         left = containingBlockWidth - (*marginLeft + borderLeft + paddingLeft + *width + paddingRight  + borderRight + *marginRight + *right);
468     } else if (!left && !right && width) {
469         // #2
470         auto staticHorizontalPosition = staticHorizontalPositionForOutOfFlowPositioned(layoutState, layoutBox);
471         if (isLeftToRightDirection) {
472             left = staticHorizontalPosition;
473             right = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight);
474         } else {
475             right = staticHorizontalPosition;
476             left = containingBlockWidth - (*marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight + *right);
477         }
478     } else if (!width && !right && left) {
479         // #3
480         width = shrinkToFitWidth(layoutState, layoutBox);
481         right = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight);
482     } else if (!left && width && right) {
483         // #4
484         left = containingBlockWidth - (*marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight + *right);
485     } else if (!width && left && right) {
486         // #5
487         width = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + paddingRight  + borderRight + *marginRight + *right);
488     } else if (!right && left && width) {
489         // #6
490         right = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginRight);
491     }
492
493     ASSERT(left);
494     ASSERT(right);
495     ASSERT(width);
496     ASSERT(marginLeft);
497     ASSERT(marginRight);
498
499     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position][Width][Margin] -> out-of-flow non-replaced -> left(" << *left << "px) right("  << *right << "px) width(" << *width << "px) margin(" << *marginLeft << "px, "  << *marginRight << "px) layoutBox(" << &layoutBox << ")");
500     return { *left, *right, { *width, { *marginLeft, *marginRight }, { nonComputedMarginLeft, nonComputedMarginRight } } };
501 }
502
503 VerticalGeometry FormattingContext::Geometry::outOfFlowReplacedVerticalGeometry(const LayoutState& layoutState, const Box& layoutBox, std::optional<LayoutUnit> usedHeight)
504 {
505     ASSERT(layoutBox.isOutOfFlowPositioned() && layoutBox.replaced());
506
507     // 10.6.5 Absolutely positioned, replaced elements
508     //
509     // The used value of 'height' is determined as for inline replaced elements.
510     // If 'margin-top' or 'margin-bottom' is specified as 'auto' its used value is determined by the rules below.
511     // 1. If both 'top' and 'bottom' have the value 'auto', replace 'top' with the element's static position.
512     // 2. If 'bottom' is 'auto', replace any 'auto' on 'margin-top' or 'margin-bottom' with '0'.
513     // 3. If at this point both 'margin-top' and 'margin-bottom' are still 'auto', solve the equation under the extra constraint that the two margins must get equal values.
514     // 4. If at this point there is only one 'auto' left, solve the equation for that value.
515     // 5. If at this point the values are over-constrained, ignore the value for 'bottom' and solve for that value.
516
517     auto& style = layoutBox.style();
518     auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
519     auto& containingBlockDisplayBox = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock());
520     auto containingBlockHeight = containingBlockDisplayBox.height();
521     auto containingBlockWidth = containingBlockDisplayBox.width();
522
523     auto top = computedValueIfNotAuto(style.logicalTop(), containingBlockWidth);
524     auto bottom = computedValueIfNotAuto(style.logicalBottom(), containingBlockWidth);
525     auto height = inlineReplacedHeightAndMargin(layoutState, layoutBox, usedHeight).height;
526     auto marginTop = computedValueIfNotAuto(style.marginTop(), containingBlockWidth);
527     auto marginBottom = computedValueIfNotAuto(style.marginBottom(), containingBlockWidth);
528     auto paddingTop = displayBox.paddingTop().value_or(0);
529     auto paddingBottom = displayBox.paddingBottom().value_or(0);
530     auto borderTop = displayBox.borderTop();
531     auto borderBottom = displayBox.borderBottom();
532
533     if (!top && !bottom) {
534         // #1
535         top = staticVerticalPositionForOutOfFlowPositioned(layoutState, layoutBox);
536     }
537
538     if (!bottom) {
539         // #2
540         marginTop = marginTop.value_or(0);
541         marginBottom = marginBottom.value_or(0);
542     }
543
544     if (!marginTop && !marginBottom) {
545         // #3
546         auto marginTopAndBottom = containingBlockHeight - (*top + borderTop + paddingTop + height + paddingBottom + borderBottom + *bottom);
547         marginTop = marginBottom = marginTopAndBottom / 2;
548     }
549
550     // #4
551     if (!top)
552         top = containingBlockHeight - (*marginTop + borderTop + paddingTop + height + paddingBottom + borderBottom + *marginBottom + *bottom);
553
554     if (!bottom)
555         bottom = containingBlockHeight - (*top + *marginTop + borderTop + paddingTop + height + paddingBottom + borderBottom + *marginBottom);
556
557     if (!marginTop)
558         marginTop = containingBlockHeight - (*top + borderTop + paddingTop + height + paddingBottom + borderBottom + *marginBottom + *bottom);
559
560     if (!marginBottom)
561         marginBottom = containingBlockHeight - (*top + *marginTop + borderTop + paddingTop + height + paddingBottom + borderBottom + *bottom);
562
563     // #5
564     auto boxHeight = *top + *marginTop + borderTop + paddingTop + height + paddingBottom + borderBottom + *marginBottom + *bottom;
565     if (boxHeight > containingBlockHeight)
566         bottom = containingBlockHeight - (*top + *marginTop + borderTop + paddingTop + height + paddingBottom + borderBottom + *marginBottom); 
567
568     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position][Height][Margin] -> out-of-flow replaced -> top(" << *top << "px) bottom("  << *bottom << "px) height(" << height << "px) margin(" << *marginTop << "px, "  << *marginBottom << "px) layoutBox(" << &layoutBox << ")");
569     return { *top, *bottom, { height, { *marginTop, *marginBottom }, { } } };
570 }
571
572 HorizontalGeometry FormattingContext::Geometry::outOfFlowReplacedHorizontalGeometry(const LayoutState& layoutState, const Box& layoutBox, std::optional<LayoutUnit> usedWidth)
573 {
574     ASSERT(layoutBox.isOutOfFlowPositioned() && layoutBox.replaced());
575
576     // 10.3.8 Absolutely positioned, replaced elements
577     // In this case, section 10.3.7 applies up through and including the constraint equation, but the rest of section 10.3.7 is replaced by the following rules:
578     //
579     // The used value of 'width' is determined as for inline replaced elements. If 'margin-left' or 'margin-right' is specified as 'auto' its used value is determined by the rules below.
580     // 1. If both 'left' and 'right' have the value 'auto', then if the 'direction' property of the element establishing the static-position containing block is 'ltr',
581     //   set 'left' to the static position; else if 'direction' is 'rtl', set 'right' to the static position.
582     // 2. If 'left' or 'right' are 'auto', replace any 'auto' on 'margin-left' or 'margin-right' with '0'.
583     // 3. If at this point both 'margin-left' and 'margin-right' are still 'auto', solve the equation under the extra constraint that the two margins must get equal values,
584     //   unless this would make them negative, in which case when the direction of the containing block is 'ltr' ('rtl'), set 'margin-left' ('margin-right') to zero and
585     //   solve for 'margin-right' ('margin-left').
586     // 4. If at this point there is an 'auto' left, solve the equation for that value.
587     // 5. If at this point the values are over-constrained, ignore the value for either 'left' (in case the 'direction' property of the containing block is 'rtl') or
588     //   'right' (in case 'direction' is 'ltr') and solve for that value.
589
590     auto& style = layoutBox.style();
591     auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
592     auto& containingBlock = *layoutBox.containingBlock();
593     auto containingBlockWidth = layoutState.displayBoxForLayoutBox(containingBlock).contentBoxWidth();
594     auto isLeftToRightDirection = containingBlock.style().isLeftToRightDirection();
595
596     auto left = computedValueIfNotAuto(style.logicalLeft(), containingBlockWidth);
597     auto right = computedValueIfNotAuto(style.logicalRight(), containingBlockWidth);
598     auto marginLeft = computedValueIfNotAuto(style.marginLeft(), containingBlockWidth);
599     auto marginRight = computedValueIfNotAuto(style.marginRight(), containingBlockWidth);
600     auto nonComputedMarginLeft = marginLeft.value_or(0);
601     auto nonComputedMarginRight = marginRight.value_or(0);
602     auto width = inlineReplacedWidthAndMargin(layoutState, layoutBox, usedWidth).width;
603     auto paddingLeft = displayBox.paddingLeft().value_or(0);
604     auto paddingRight = displayBox.paddingRight().value_or(0);
605     auto borderLeft = displayBox.borderLeft();
606     auto borderRight = displayBox.borderRight();
607
608     if (!left && !right) {
609         // #1
610         auto staticHorizontalPosition = staticHorizontalPositionForOutOfFlowPositioned(layoutState, layoutBox);
611         if (isLeftToRightDirection)
612             left = staticHorizontalPosition;
613         else
614             right = staticHorizontalPosition;
615     }
616
617     if (!left || !right) {
618         // #2
619         marginLeft = marginLeft.value_or(0); 
620         marginRight = marginRight.value_or(0); 
621     }
622
623     if (!marginLeft && !marginRight) {
624         // #3
625         auto marginLeftAndRight = containingBlockWidth - (*left + borderLeft + paddingLeft + width + paddingRight + borderRight + *right);
626         if (marginLeftAndRight >= 0)
627             marginLeft = marginRight = marginLeftAndRight / 2;
628         else {
629             if (isLeftToRightDirection) {
630                 marginLeft = LayoutUnit { 0 };
631                 marginRight = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + width + paddingRight + borderRight + *right);
632             } else {
633                 marginRight = LayoutUnit { 0 };
634                 marginLeft = containingBlockWidth - (*left + borderLeft + paddingLeft + width + paddingRight + borderRight + *marginRight + *right);
635             }
636         }
637     }
638
639     // #4
640     if (!left)
641         left = containingBlockWidth - (*marginLeft + borderLeft + paddingLeft + width + paddingRight + borderRight + *marginRight + *right);
642
643     if (!right)
644         right = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + width + paddingRight + borderRight + *marginRight);
645
646     if (!marginLeft)
647         marginLeft = containingBlockWidth - (*left + borderLeft + paddingLeft + width + paddingRight + borderRight + *marginRight + *right);
648
649     if (!marginRight)
650         marginRight = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + width + paddingRight + borderRight + *right);
651
652     auto boxWidth = (*left + *marginLeft + borderLeft + paddingLeft + width + paddingRight + borderRight + *marginRight + *right);
653     if (boxWidth > containingBlockWidth) {
654         // #5 Over-constrained?
655         if (isLeftToRightDirection)
656             right = containingBlockWidth - (*left + *marginLeft + borderLeft + paddingLeft + width + paddingRight + borderRight + *marginRight);
657         else
658             left = containingBlockWidth - (*marginLeft + borderLeft + paddingLeft + width + paddingRight + borderRight + *marginRight + *right);
659     }
660
661     ASSERT(left);
662     ASSERT(right);
663     ASSERT(marginLeft);
664     ASSERT(marginRight);
665
666     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position][Width][Margin] -> out-of-flow replaced -> left(" << *left << "px) right("  << *right << "px) width(" << width << "px) margin(" << *marginLeft << "px, "  << *marginRight << "px) layoutBox(" << &layoutBox << ")");
667     return { *left, *right, { width, { *marginLeft, *marginRight }, { nonComputedMarginLeft, nonComputedMarginRight } } };
668 }
669
670 HeightAndMargin FormattingContext::Geometry::complicatedCases(const LayoutState& layoutState, const Box& layoutBox, std::optional<LayoutUnit> usedHeight)
671 {
672     ASSERT(!layoutBox.replaced());
673     // TODO: Use complicated-case for document renderer for now (see BlockFormattingContext::Geometry::inFlowHeightAndMargin).
674     ASSERT((layoutBox.isBlockLevelBox() && layoutBox.isInFlow() && !layoutBox.isOverflowVisible()) || layoutBox.isInlineBlockBox() || layoutBox.isFloatingPositioned() || layoutBox.isDocumentBox());
675
676     // 10.6.6 Complicated cases
677     //
678     // 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).
679     // 'Inline-block', non-replaced elements.
680     // Floating, non-replaced elements.
681     //
682     // 1. If 'margin-top', or 'margin-bottom' are 'auto', their used value is 0.
683     // 2. If 'height' is 'auto', the height depends on the element's descendants per 10.6.7.
684
685     auto& style = layoutBox.style();
686     auto& containingBlock = *layoutBox.containingBlock();
687     auto& containingBlockDisplayBox = layoutState.displayBoxForLayoutBox(containingBlock);
688     auto containingBlockWidth = containingBlockDisplayBox.contentBoxWidth();
689
690     auto height = usedHeight ? usedHeight.value() : computedHeightValue(layoutState, layoutBox, HeightType::Normal);
691     auto marginTop = computedValueIfNotAuto(style.marginTop(), containingBlockWidth);
692     auto marginBottom = computedValueIfNotAuto(style.marginBottom(), containingBlockWidth);
693
694     // #1
695     marginTop = marginTop.value_or(0);
696     marginBottom = marginBottom.value_or(0);
697     // #2
698     if (!height) {
699         ASSERT(isHeightAuto(layoutBox));
700         height = contentHeightForFormattingContextRoot(layoutState, layoutBox);
701     }
702
703     ASSERT(height);
704     ASSERT(marginTop);
705     ASSERT(marginBottom);
706
707     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> floating non-replaced -> height(" << *height << "px) margin(" << *marginTop << "px, " << *marginBottom << "px) -> layoutBox(" << &layoutBox << ")");
708     return HeightAndMargin { *height, { *marginTop, *marginBottom }, { } };
709 }
710
711 WidthAndMargin FormattingContext::Geometry::floatingNonReplacedWidthAndMargin(LayoutState& layoutState, const Box& layoutBox, std::optional<LayoutUnit> usedWidth)
712 {
713     ASSERT(layoutBox.isFloatingPositioned() && !layoutBox.replaced());
714
715     // 10.3.5 Floating, non-replaced elements
716     //
717     // 1. If 'margin-left', or 'margin-right' are computed as 'auto', their used value is '0'.
718     // 2. If 'width' is computed as 'auto', the used value is the "shrink-to-fit" width.
719
720     auto& containingBlock = *layoutBox.containingBlock();
721     auto containingBlockWidth = layoutState.displayBoxForLayoutBox(containingBlock).contentBoxWidth();
722
723     // #1
724     auto margin = computedNonCollapsedHorizontalMarginValue(layoutState, layoutBox);
725     // #2
726     auto width = computedValueIfNotAuto(usedWidth ? Length { usedWidth.value(), Fixed } : layoutBox.style().logicalWidth(), containingBlockWidth);
727     if (!width)
728         width = shrinkToFitWidth(layoutState, layoutBox);
729
730     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width][Margin] -> floating non-replaced -> width(" << *width << "px) margin(" << margin.left << "px, " << margin.right << "px) -> layoutBox(" << &layoutBox << ")");
731     return WidthAndMargin { *width, margin, margin };
732 }
733
734 HeightAndMargin FormattingContext::Geometry::floatingReplacedHeightAndMargin(const LayoutState& layoutState, const Box& layoutBox, std::optional<LayoutUnit> usedHeight)
735 {
736     ASSERT(layoutBox.isFloatingPositioned() && layoutBox.replaced());
737
738     // 10.6.2 Inline replaced elements, block-level replaced elements in normal flow, 'inline-block'
739     // replaced elements in normal flow and floating replaced elements
740     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> floating replaced -> redirected to inline replaced");
741     return inlineReplacedHeightAndMargin(layoutState, layoutBox, usedHeight);
742 }
743
744 WidthAndMargin FormattingContext::Geometry::floatingReplacedWidthAndMargin(const LayoutState& layoutState, const Box& layoutBox, std::optional<LayoutUnit> usedWidth)
745 {
746     ASSERT(layoutBox.isFloatingPositioned() && layoutBox.replaced());
747
748     // 10.3.6 Floating, replaced elements
749     //
750     // 1. If 'margin-left' or 'margin-right' are computed as 'auto', their used value is '0'.
751     // 2. The used value of 'width' is determined as for inline replaced elements.
752     auto margin = computedNonCollapsedHorizontalMarginValue(layoutState, layoutBox);
753
754     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> floating replaced -> redirected to inline replaced");
755     return inlineReplacedWidthAndMargin(layoutState, layoutBox, usedWidth, margin.left, margin.right);
756 }
757
758 VerticalGeometry FormattingContext::Geometry::outOfFlowVerticalGeometry(const LayoutState& layoutState, const Box& layoutBox, std::optional<LayoutUnit> usedHeight)
759 {
760     ASSERT(layoutBox.isOutOfFlowPositioned());
761
762     if (!layoutBox.replaced())
763         return outOfFlowNonReplacedVerticalGeometry(layoutState, layoutBox, usedHeight);
764     return outOfFlowReplacedVerticalGeometry(layoutState, layoutBox, usedHeight);
765 }
766
767 HorizontalGeometry FormattingContext::Geometry::outOfFlowHorizontalGeometry(LayoutState& layoutState, const Box& layoutBox, std::optional<LayoutUnit> usedWidth)
768 {
769     ASSERT(layoutBox.isOutOfFlowPositioned());
770
771     if (!layoutBox.replaced())
772         return outOfFlowNonReplacedHorizontalGeometry(layoutState, layoutBox, usedWidth);
773     return outOfFlowReplacedHorizontalGeometry(layoutState, layoutBox, usedWidth);
774 }
775
776 HeightAndMargin FormattingContext::Geometry::floatingHeightAndMargin(const LayoutState& layoutState, const Box& layoutBox, std::optional<LayoutUnit> usedHeight)
777 {
778     ASSERT(layoutBox.isFloatingPositioned());
779
780     if (!layoutBox.replaced())
781         return complicatedCases(layoutState, layoutBox, usedHeight);
782     return floatingReplacedHeightAndMargin(layoutState, layoutBox, usedHeight);
783 }
784
785 WidthAndMargin FormattingContext::Geometry::floatingWidthAndMargin(LayoutState& layoutState, const Box& layoutBox, std::optional<LayoutUnit> usedWidth)
786 {
787     ASSERT(layoutBox.isFloatingPositioned());
788
789     if (!layoutBox.replaced())
790         return floatingNonReplacedWidthAndMargin(layoutState, layoutBox, usedWidth);
791     return floatingReplacedWidthAndMargin(layoutState, layoutBox, usedWidth);
792 }
793
794 HeightAndMargin FormattingContext::Geometry::inlineReplacedHeightAndMargin(const LayoutState& layoutState, const Box& layoutBox, std::optional<LayoutUnit> usedHeight)
795 {
796     ASSERT((layoutBox.isOutOfFlowPositioned() || layoutBox.isFloatingPositioned() || layoutBox.isInFlow()) && layoutBox.replaced());
797
798     // 10.6.2 Inline replaced elements, block-level replaced elements in normal flow, 'inline-block' replaced elements in normal flow and floating replaced elements
799     //
800     // 1. If 'margin-top', or 'margin-bottom' are 'auto', their used value is 0.
801     // 2. If 'height' and 'width' both have computed values of 'auto' and the element also has an intrinsic height, then that intrinsic height is the used value of 'height'.
802     // 3. Otherwise, if 'height' has a computed value of 'auto', and the element has an intrinsic ratio then the used value of 'height' is:
803     //    (used width) / (intrinsic ratio)
804     // 4. Otherwise, if 'height' has a computed value of 'auto', and the element has an intrinsic height, then that intrinsic height is the used value of 'height'.
805     // 5. Otherwise, if 'height' has a computed value of 'auto', but none of the conditions above are met, then the used value of 'height' must be set to
806     //    the height of the largest rectangle that has a 2:1 ratio, has a height not greater than 150px, and has a width not greater than the device width.
807
808     // #1
809     auto margin = computedNonCollapsedVerticalMarginValue(layoutState, layoutBox);
810
811     auto& style = layoutBox.style();
812     auto replaced = layoutBox.replaced();
813
814     auto height = usedHeight ? usedHeight.value() : computedHeightValue(layoutState, layoutBox, HeightType::Normal);
815     auto heightIsAuto = !usedHeight && isHeightAuto(layoutBox);
816     auto widthIsAuto = style.logicalWidth().isAuto();
817
818     if (heightIsAuto && widthIsAuto && replaced->hasIntrinsicHeight()) {
819         // #2
820         height = replaced->intrinsicHeight();
821     } else if (heightIsAuto && replaced->hasIntrinsicRatio()) {
822         // #3
823         auto usedWidth = layoutState.displayBoxForLayoutBox(layoutBox).width();
824         height = usedWidth / replaced->intrinsicRatio();
825     } else if (heightIsAuto && replaced->hasIntrinsicHeight()) {
826         // #4
827         height = replaced->intrinsicHeight();
828     } else if (heightIsAuto) {
829         // #5
830         height = { 150 };
831     }
832
833     ASSERT(height);
834
835     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> inflow replaced -> height(" << *height << "px) margin(" << margin.top << "px, " << margin.bottom << "px) -> layoutBox(" << &layoutBox << ")");
836     return { *height, margin, { } };
837 }
838
839 WidthAndMargin FormattingContext::Geometry::inlineReplacedWidthAndMargin(const LayoutState& layoutState, const Box& layoutBox,
840     std::optional<LayoutUnit> usedWidth, std::optional<LayoutUnit> precomputedMarginLeft, std::optional<LayoutUnit> precomputedMarginRight)
841 {
842     ASSERT((layoutBox.isOutOfFlowPositioned() || layoutBox.isFloatingPositioned() || layoutBox.isInFlow()) && layoutBox.replaced());
843
844     // 10.3.2 Inline, replaced elements
845     //
846     // A computed value of 'auto' for 'margin-left' or 'margin-right' becomes a used value of '0'.
847     //
848     // 1. If 'height' and 'width' both have computed values of 'auto' and the element also has an intrinsic width, then that intrinsic width is the used value of 'width'.
849     //
850     // 2. If 'height' and 'width' both have computed values of 'auto' and the element has no intrinsic width, but does have an intrinsic height and intrinsic ratio;
851     //    or if 'width' has a computed value of 'auto', 'height' has some other computed value, and the element does have an intrinsic ratio;
852     //    then the used value of 'width' is: (used height) * (intrinsic ratio)
853     //
854     // 3. If 'height' and 'width' both have computed values of 'auto' and the element has an intrinsic ratio but no intrinsic height or width,
855     //    then the used value of 'width' is undefined in CSS 2.2. However, it is suggested that, if the containing block's width does not itself depend on the replaced
856     //    element's width, then the used value of 'width' is calculated from the constraint equation used for block-level, non-replaced elements in normal flow.
857     //
858     // 4. Otherwise, if 'width' has a computed value of 'auto', and the element has an intrinsic width, then that intrinsic width is the used value of 'width'.
859     //
860     // 5. Otherwise, if 'width' has a computed value of 'auto', but none of the conditions above are met, then the used value of 'width' becomes 300px.
861     //    If 300px is too wide to fit the device, UAs should use the width of the largest rectangle that has a 2:1 ratio and fits the device instead.
862
863     auto& style = layoutBox.style();
864     auto& containingBlockDisplayBox = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock());
865     auto containingBlockWidth = containingBlockDisplayBox.width();
866
867     auto computeMarginRight = [&]() {
868         if (precomputedMarginRight)
869             return precomputedMarginRight.value();
870         auto marginRight = computedValueIfNotAuto(style.marginRight(), containingBlockWidth);
871         return marginRight.value_or(LayoutUnit { 0 });
872     };
873
874     auto computeMarginLeft = [&]() {
875         if (precomputedMarginLeft)
876             return precomputedMarginLeft.value();
877         auto marginLeft = computedValueIfNotAuto(style.marginLeft(), containingBlockWidth);
878         return marginLeft.value_or(LayoutUnit { 0 });
879     };
880
881     auto replaced = layoutBox.replaced();
882     ASSERT(replaced);
883
884     auto marginLeft = computeMarginLeft();
885     auto marginRight = computeMarginRight();
886     auto nonComputedMarginLeft = computedValueIfNotAuto(style.marginLeft(), containingBlockWidth).value_or(0);
887     auto nonComputedMarginRight = computedValueIfNotAuto(style.marginRight(), containingBlockWidth).value_or(0);
888     auto width = computedValueIfNotAuto(usedWidth ? Length { usedWidth.value(), Fixed } : style.logicalWidth(), containingBlockWidth);
889
890     auto heightIsAuto = isHeightAuto(layoutBox);
891     auto height = computedHeightValue(layoutState, layoutBox, HeightType::Normal);
892
893     if (!width && heightIsAuto && replaced->hasIntrinsicWidth()) {
894         // #1
895         width = replaced->intrinsicWidth();
896     } else if ((!width && heightIsAuto && !replaced->hasIntrinsicWidth() && replaced->hasIntrinsicHeight() && replaced->hasIntrinsicRatio())
897         || (!width && height && replaced->hasIntrinsicRatio())) {
898         // #2
899         width = height.value_or(replaced->hasIntrinsicHeight()) * replaced->intrinsicRatio();
900     } else if (!width && heightIsAuto && replaced->hasIntrinsicRatio() && !replaced->hasIntrinsicWidth() && !replaced->hasIntrinsicHeight()) {
901         // #3
902         // FIXME: undefined but surely doable.
903         ASSERT_NOT_IMPLEMENTED_YET();
904     } else if (!width && replaced->hasIntrinsicWidth()) {
905         // #4
906         width = replaced->intrinsicWidth();
907     } else if (!width) {
908         // #5
909         width = { 300 };
910     }
911
912     ASSERT(width);
913
914     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width][Margin] -> inflow replaced -> width(" << *width << "px) margin(" << marginLeft << "px, " << marginRight << "px) -> layoutBox(" << &layoutBox << ")");
915     return { *width, { marginLeft, marginRight }, { nonComputedMarginLeft, nonComputedMarginRight } };
916 }
917
918 Edges FormattingContext::Geometry::computedBorder(const LayoutState&, const Box& layoutBox)
919 {
920     auto& style = layoutBox.style();
921     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Border] -> layoutBox: " << &layoutBox);
922     return {
923         { style.borderLeft().boxModelWidth(), style.borderRight().boxModelWidth() },
924         { style.borderTop().boxModelWidth(), style.borderBottom().boxModelWidth() }
925     };
926 }
927
928 std::optional<Edges> FormattingContext::Geometry::computedPadding(const LayoutState& layoutState, const Box& layoutBox)
929 {
930     if (!layoutBox.isPaddingApplicable())
931         return std::nullopt;
932
933     auto& style = layoutBox.style();
934     auto containingBlockWidth = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock()).contentBoxWidth();
935     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Padding] -> layoutBox: " << &layoutBox);
936     return Edges {
937         { valueForLength(style.paddingLeft(), containingBlockWidth), valueForLength(style.paddingRight(), containingBlockWidth) },
938         { valueForLength(style.paddingTop(), containingBlockWidth), valueForLength(style.paddingBottom(), containingBlockWidth) }
939     };
940 }
941
942 HorizontalEdges FormattingContext::Geometry::computedNonCollapsedHorizontalMarginValue(const LayoutState& layoutState, const Box& layoutBox)
943 {
944     auto& style = layoutBox.style();
945     auto containingBlockWidth = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock()).contentBoxWidth();
946
947     auto marginLeft = computedValueIfNotAuto(style.marginLeft(), containingBlockWidth).value_or(LayoutUnit { 0 });
948     auto marginRight = computedValueIfNotAuto(style.marginRight(), containingBlockWidth).value_or(LayoutUnit { 0 });
949
950     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Margin] -> non collapsed horizontal -> margin(" << marginLeft << "px, " << marginRight << "px) -> layoutBox: " << &layoutBox);
951     return { marginLeft, marginRight };
952 }
953
954 VerticalEdges FormattingContext::Geometry::computedNonCollapsedVerticalMarginValue(const LayoutState& layoutState, const Box& layoutBox)
955 {
956     auto& style = layoutBox.style();
957     auto containingBlockWidth = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock()).contentBoxWidth();
958
959     auto marginTop = computedValueIfNotAuto(style.marginTop(), containingBlockWidth).value_or(LayoutUnit { 0 });
960     auto marginBottom = computedValueIfNotAuto(style.marginBottom(), containingBlockWidth).value_or(LayoutUnit { 0 });
961
962     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Margin] -> non collapsed vertical -> margin(" << marginTop << "px, " << marginBottom << "px) -> layoutBox: " << &layoutBox);
963     return { marginTop, marginBottom };
964 }
965
966 }
967 }
968 #endif