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