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