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