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