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