2f3c954fcfde3162714167d28fe41fd300c2203d
[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     // 10.6.7 'Auto' heights for block formatting context roots
38
39     // 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.
40     // If it has block-level children, the height is the distance between the top margin-edge of the topmost block-level
41     // child box and the bottom margin-edge of the bottommost block-level child box.
42
43     // In addition, if the element has any floating descendants whose bottom margin edge is below the element's bottom content edge,
44     // then the height is increased to include those edges. Only floats that participate in this block formatting context are taken
45     // into account, e.g., floats inside absolutely positioned descendants or other floats are not.
46     if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowOrFloatingChild())
47         return 0;
48
49     auto& formattingRootContainer = downcast<Container>(layoutBox);
50     if (formattingRootContainer.establishesInlineFormattingContext())
51         return 0;
52
53     auto* firstDisplayBox = layoutContext.displayBoxForLayoutBox(*formattingRootContainer.firstInFlowChild());
54     auto* lastDisplayBox = layoutContext.displayBoxForLayoutBox(*formattingRootContainer.lastInFlowChild());
55
56     auto top = firstDisplayBox->marginBox().y();
57     auto bottom = lastDisplayBox->marginBox().maxY();
58     // FIXME: add floating support.
59     return bottom - top;
60 }
61
62 static LayoutUnit shrinkToFitWidth(LayoutContext&, const Box&)
63 {
64     return { };
65 }
66
67 LayoutUnit FormattingContext::Geometry::outOfFlowNonReplacedHeight(LayoutContext& layoutContext, const Box& layoutBox)
68 {
69     ASSERT(layoutBox.isOutOfFlowPositioned() && !layoutBox.replaced());
70
71     // 10.6.4 Absolutely positioned, non-replaced elements
72     //
73     // For absolutely positioned elements, the used values of the vertical dimensions must satisfy this constraint:
74     // 'top' + 'margin-top' + 'border-top-width' + 'padding-top' + 'height' + 'padding-bottom' + 'border-bottom-width' + 'margin-bottom' + 'bottom'
75     // = height of containing block
76
77     // If all three of 'top', 'height', and 'bottom' are auto, set 'top' to the static position and apply rule number three below.
78
79     // If none of the three are 'auto': If both 'margin-top' and 'margin-bottom' are 'auto', solve the equation under the extra
80     // constraint that the two margins get equal values. If one of 'margin-top' or 'margin-bottom' is 'auto', solve the equation for that value.
81     // If the values are over-constrained, ignore the value for 'bottom' and solve for that value.
82
83     // Otherwise, pick the one of the following six rules that applies.
84
85     // 1. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then the height is based on the content per 10.6.7,
86     //     set 'auto' values for 'margin-top' and 'margin-bottom' to 0, and solve for 'top'
87     // 2. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then set 'top' to the static position, set 'auto' values for
88     //    'margin-top' and 'margin-bottom' to 0, and solve for 'bottom'
89     // 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'
90     //     values for 'margin-top' and 'margin-bottom' to 0, and solve for 'bottom'
91     // 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'
92     // 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'
93     // 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'
94     auto& style = layoutBox.style();
95     auto top = style.logicalTop();
96     auto bottom = style.logicalBottom();
97     auto height = style.logicalHeight(); 
98
99     auto containingBlockHeight = layoutContext.displayBoxForLayoutBox(*layoutBox.containingBlock())->height();
100     LayoutUnit computedHeightValue;
101
102     if (!height.isAuto())
103         computedHeightValue = valueForLength(height, containingBlockHeight);
104     else if ((top.isAuto() && bottom.isAuto())
105         || (top.isAuto() && !bottom.isAuto())
106         || (!top.isAuto() && bottom.isAuto())) {
107         // All auto (#3), #1 and #3
108         computedHeightValue = contentHeightForFormattingContextRoot(layoutContext, layoutBox);
109     } else if (!top.isAuto() && !bottom.isAuto()) {
110         // #5
111         auto& displayBox = *layoutContext.displayBoxForLayoutBox(layoutBox);
112
113         auto marginTop = displayBox.marginTop();
114         auto marginBottom = displayBox.marginBottom();
115     
116         auto paddingTop = displayBox.paddingTop();
117         auto paddingBottom = displayBox.paddingBottom();
118
119         auto borderTop = displayBox.borderTop();
120         auto borderBottom = displayBox.borderBottom();
121
122         computedHeightValue = containingBlockHeight - (top.value() + marginTop + borderTop + paddingTop + paddingBottom + borderBottom + marginBottom + bottom.value());
123     } else {
124         // #2 #4 #6 have height != auto
125         ASSERT_NOT_REACHED();
126     }
127
128     return computedHeightValue;
129 }
130
131 LayoutUnit FormattingContext::Geometry::outOfFlowNonReplacedWidth(LayoutContext& layoutContext, const Box& layoutBox)
132 {
133     ASSERT(layoutBox.isOutOfFlowPositioned() && !layoutBox.replaced());
134     
135     // 10.3.7 Absolutely positioned, non-replaced elements
136     //
137     // 'left' + 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' + 'right'
138     // = width of containing block
139
140     // If all three of 'left', 'width', and 'right' are 'auto': First set any 'auto' values for 'margin-left' and 'margin-right' to 0.
141     // Then, if the 'direction' property of the element establishing the static-position containing block is 'ltr' set 'left' to the static
142     // position and apply rule number three below; otherwise, set 'right' to the static position and apply rule number one below.
143
144     // 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the width is shrink-to-fit. Then solve for 'left'
145     // 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if the 'direction' property of the element establishing the static-position 
146     //    containing block is 'ltr' set 'left' to the static position, otherwise set 'right' to the static position.
147     //    Then solve for 'left' (if 'direction is 'rtl') or 'right' (if 'direction' is 'ltr').
148     // 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the width is shrink-to-fit . Then solve for 'right'
149     // 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve for 'left'
150     // 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve for 'width'
151     // 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve for 'right'
152     auto& style = layoutBox.style();
153     auto left = style.logicalLeft();
154     auto right = style.logicalRight();
155     auto width = style.logicalWidth();
156
157     auto containingBlockWidth = layoutContext.displayBoxForLayoutBox(*layoutBox.containingBlock())->width();
158     LayoutUnit computedWidthValue;
159
160     if (!width.isAuto())
161         computedWidthValue = valueForLength(width, containingBlockWidth);
162     else if ((left.isAuto() && right.isAuto())
163         || (left.isAuto() && !right.isAuto())
164         || (!left.isAuto() && right.isAuto())) {
165         // All auto (#1), #1 and #3
166         computedWidthValue = shrinkToFitWidth(layoutContext, layoutBox);
167     } else if (!left.isAuto() && !right.isAuto()) {
168         // #5
169         auto& displayBox = *layoutContext.displayBoxForLayoutBox(layoutBox);
170
171         auto marginLeft = displayBox.marginLeft();
172         auto marginRight = displayBox.marginRight();
173     
174         auto paddingLeft = displayBox.paddingLeft();
175         auto paddingRight = displayBox.paddingRight();
176
177         auto borderLeft = displayBox.borderLeft();
178         auto borderRight = displayBox.borderRight();
179
180         computedWidthValue = containingBlockWidth - (left.value() + marginLeft + borderLeft + paddingLeft + paddingRight + borderRight + marginRight + right.value());
181     } else {
182         // #2 #4 #6 have width != auto
183         ASSERT_NOT_REACHED();
184     }
185
186     return computedWidthValue;
187 }
188
189 LayoutUnit FormattingContext::Geometry::outOfFlowReplacedHeight(LayoutContext& layoutContext, const Box& layoutBox)
190 {
191     ASSERT(layoutBox.isOutOfFlowPositioned() && layoutBox.replaced());
192     // 10.6.5 Absolutely positioned, replaced elements
193     //
194     // The used value of 'height' is determined as for inline replaced elements.
195     return replacedHeight(layoutContext, layoutBox);
196 }
197
198 LayoutUnit FormattingContext::Geometry::outOfFlowReplacedWidth(LayoutContext& layoutContext, const Box& layoutBox)
199 {
200     ASSERT(layoutBox.isOutOfFlowPositioned() && layoutBox.replaced());
201     // 10.3.8 Absolutely positioned, replaced elements
202     //
203     // The used value of 'width' is determined as for inline replaced elements.
204     return replacedWidth(layoutContext, layoutBox);
205 }
206
207 LayoutUnit FormattingContext::Geometry::floatingNonReplacedHeight(LayoutContext& layoutContext, const Box& layoutBox)
208 {
209     ASSERT(layoutBox.isFloatingPositioned() && !layoutBox.replaced());
210     // 10.6.6 Complicated cases
211     //
212     // Floating, non-replaced elements.
213     //
214     // If 'height' is 'auto', the height depends on the element's descendants per 10.6.7.
215     auto height = layoutBox.style().logicalHeight();
216     return height.isAuto() ? contentHeightForFormattingContextRoot(layoutContext, layoutBox) : LayoutUnit(height.value());
217 }
218
219 LayoutUnit FormattingContext::Geometry::floatingNonReplacedWidth(LayoutContext& layoutContext, const Box& layoutBox)
220 {
221     ASSERT(layoutBox.isFloatingPositioned() && !layoutBox.replaced());
222     // 10.3.5 Floating, non-replaced elements
223
224     // If 'width' is computed as 'auto', the used value is the "shrink-to-fit" width.
225     auto width = layoutBox.style().logicalWidth();
226     return width.isAuto() ? shrinkToFitWidth(layoutContext, layoutBox) : LayoutUnit(width.value());
227 }
228
229 LayoutUnit FormattingContext::Geometry::floatingReplacedHeight(LayoutContext& layoutContext, const Box& layoutBox)
230 {
231     ASSERT(layoutBox.isFloatingPositioned() && layoutBox.replaced());
232     // 10.6.2 Inline replaced elements, block-level replaced elements in normal flow, 'inline-block'
233     // replaced elements in normal flow and floating replaced elements
234     return replacedHeight(layoutContext, layoutBox);
235 }
236
237 LayoutUnit FormattingContext::Geometry::floatingReplacedWidth(LayoutContext& layoutContext, const Box& layoutBox)
238 {
239     ASSERT(layoutBox.isFloatingPositioned() && layoutBox.replaced());
240     // 10.3.6 Floating, replaced elements
241     //
242     // The used value of 'width' is determined as for inline replaced elements.
243     return replacedWidth(layoutContext, layoutBox);
244 }
245
246 LayoutPoint FormattingContext::Geometry::outOfFlowNonReplacedPosition(LayoutContext& layoutContext, const Box& layoutBox)
247 {
248     // 10.3.7 Absolutely positioned, non-replaced elements (left/right)
249     // 10.6.4 Absolutely positioned, non-replaced elements (top/bottom)
250
251     // At this point we've the size computed.
252     auto& displayBox = *layoutContext.displayBoxForLayoutBox(layoutBox);
253     auto size = displayBox.size();
254     auto& style = layoutBox.style();
255
256     // 10.6.4 Absolutely positioned, non-replaced elements
257     auto top = style.logicalTop();
258     auto bottom = style.logicalBottom();
259     auto containingBlockHeight = layoutContext.displayBoxForLayoutBox(*layoutBox.containingBlock())->height();
260
261     // 'top' + 'margin-top' + 'border-top-width' + 'padding-top' + 'height' + 'padding-bottom' + 'border-bottom-width' + 'margin-bottom' + 'bottom'
262     // = height of containing block
263     //
264     // 1. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then the height is based on the content per 10.6.7,
265     //     set 'auto' values for 'margin-top' and 'margin-bottom' to 0, and solve for 'top'
266     // 2. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then set 'top' to the static position, set 'auto' values for
267     //    'margin-top' and 'margin-bottom' to 0, and solve for 'bottom'
268     // 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'
269     //     values for 'margin-top' and 'margin-bottom' to 0, and solve for 'bottom'
270     // 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'
271     // 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'
272     // 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'
273     LayoutUnit computedTopValue;
274     if (top.isAuto() && !bottom.isAuto()) {
275         // #1 #4
276         auto marginTop = displayBox.marginTop();
277         auto marginBottom = displayBox.marginBottom();
278     
279         auto paddingTop = displayBox.paddingTop();
280         auto paddingBottom = displayBox.paddingBottom();
281
282         auto borderTop = displayBox.borderTop();
283         auto borderBottom = displayBox.borderBottom();
284
285         computedTopValue = containingBlockHeight - (marginTop + borderTop + paddingTop + size.height() + paddingBottom + borderBottom + marginBottom + bottom.value());
286     } else if (top.isAuto() && bottom.isAuto()) {
287         // #2
288         // Already computed as part of the computeStaticPosition();
289         computedTopValue = displayBox.top();
290     } else {
291         // #3 #5 #6 have top != auto
292         computedTopValue = valueForLength(top, containingBlockHeight);
293     }
294
295
296     // 10.3.7 Absolutely positioned, non-replaced elements
297     auto left = style.logicalLeft();
298     auto right = style.logicalRight();
299     auto containingBlockWidth = layoutContext.displayBoxForLayoutBox(*layoutBox.containingBlock())->width();
300
301     // 'left' + 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' + 'right'
302     // = width of containing block
303     //
304     // If all three of 'left', 'width', and 'right' are 'auto': First set any 'auto' values for 'margin-left' and 'margin-right' to 0.
305     // Then, if the 'direction' property of the element establishing the static-position containing block is 'ltr' set 'left' to the static
306     // position and apply rule number three below; otherwise, set 'right' to the static position and apply rule number one below.
307
308     // 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the width is shrink-to-fit. Then solve for 'left'
309     // 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if the 'direction' property of the element establishing the static-position 
310     //    containing block is 'ltr' set 'left' to the static position, otherwise set 'right' to the static position.
311     //    Then solve for 'left' (if 'direction is 'rtl') or 'right' (if 'direction' is 'ltr').
312     // 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the width is shrink-to-fit . Then solve for 'right'
313     // 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve for 'left'
314     // 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve for 'width'
315     // 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve for 'right'
316     LayoutUnit computedLeftValue;
317     if (left.isAuto() && !right.isAuto()) {
318         // #1 #4
319         auto marginLeft = displayBox.marginLeft();
320         auto marginRight = displayBox.marginRight();
321     
322         auto paddingLeft = displayBox.paddingLeft();
323         auto paddingRight = displayBox.paddingRight();
324
325         auto borderLeft = displayBox.borderLeft();
326         auto borderRight = displayBox.borderRight();
327
328         computedLeftValue = containingBlockWidth - (marginLeft + borderLeft + paddingLeft + size.width() + paddingRight + borderRight + marginRight + right.value());
329     } else if (left.isAuto() && right.isAuto()) {
330         // #2
331         // FIXME: rtl
332         computedLeftValue = displayBox.left();
333     } else {
334         // #3 #5 #6 have left != auto
335         computedLeftValue = valueForLength(left, containingBlockWidth);
336     }
337
338     return { computedLeftValue, computedTopValue };
339 }
340
341 LayoutPoint FormattingContext::Geometry::outOfFlowReplacedPosition(LayoutContext& layoutContext, const Box& layoutBox)
342 {
343     // 10.6.5 Absolutely positioned, replaced elements (top/bottom)
344     // 10.3.8 Absolutely positioned, replaced elements (left/right)
345
346     // At this point we've the size computed.
347     auto& displayBox = *layoutContext.displayBoxForLayoutBox(layoutBox);
348     auto size = displayBox.size();
349     auto& style = layoutBox.style();
350
351     // 10.6.5 Absolutely positioned, replaced elements
352     //
353     // This situation is similar to the previous one, except that the element has an intrinsic height. The sequence of substitutions is now:
354     // The used value of 'height' is determined as for inline replaced elements. If 'margin-top' or 'margin-bottom' is specified as 'auto'
355     // its used value is determined by the rules below.
356     //
357     // 1. If both 'top' and 'bottom' have the value 'auto', replace 'top' with the element's static position.
358     // 2. If 'bottom' is 'auto', replace any 'auto' on 'margin-top' or 'margin-bottom' with '0'.
359     // 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.
360     // 4. If at this point there is only one 'auto' left, solve the equation for that value.
361     // 5. If at this point the values are over-constrained, ignore the value for 'bottom' and solve for that value.
362     auto top = style.logicalTop();
363     auto bottom = style.logicalBottom();
364     auto containingBlockHeight = layoutContext.displayBoxForLayoutBox(*layoutBox.containingBlock())->height();
365     LayoutUnit computedTopValue;
366
367     if (!top.isAuto())
368         computedTopValue = valueForLength(top, containingBlockHeight);
369     else if (bottom.isAuto()) {
370         // #1
371         computedTopValue = displayBox.top();
372     } else {
373         // #4
374         auto marginTop = displayBox.marginTop();
375         auto marginBottom = displayBox.marginBottom();
376
377         auto paddingTop = displayBox.paddingTop();
378         auto paddingBottom = displayBox.paddingBottom();
379
380         auto borderTop = displayBox.borderTop();
381         auto borderBottom = displayBox.borderBottom();
382
383         computedTopValue = containingBlockHeight - (marginTop + borderTop + paddingTop + size.height() + paddingBottom + borderBottom + marginBottom + bottom.value());
384     }
385
386
387     // 10.3.8 Absolutely positioned, replaced elements
388     //
389     // 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:
390     //
391     // The used value of 'width' is determined as for inline replaced elements. 
392     //
393     // 1. If 'margin-left' or 'margin-right' is specified as 'auto' its used value is determined by the rules below.
394     // 2. If both 'left' and 'right' have the value 'auto', then if the 'direction' property of the element establishing the
395     //    static-position containing block is 'ltr', set 'left' to the static position; else if 'direction' is 'rtl', set 'right' to the static position.
396     // 3. If 'left' or 'right' are 'auto', replace any 'auto' on 'margin-left' or 'margin-right' with '0'.
397     // 4. If at this point both 'margin-left' and 'margin-right' are still 'auto', solve the equation under the extra constraint
398     //    that the two margins must get equal values, unless this would make them negative, in which case when the direction of
399     //    the containing block is 'ltr' ('rtl'), set 'margin-left' ('margin-right') to zero and solve for 'margin-right' ('margin-left').
400     // 5. If at this point there is an 'auto' left, solve the equation for that value.
401     // 6. If at this point the values are over-constrained, ignore the value for either 'left' (in case the 'direction'
402     //    property of the containing block is 'rtl') or 'right' (in case 'direction' is 'ltr') and solve for that value.
403     auto left = style.logicalLeft();
404     auto right = style.logicalRight();
405     auto containingBlockWidth = layoutContext.displayBoxForLayoutBox(*layoutBox.containingBlock())->width();
406     LayoutUnit computedLeftValue;
407
408     if (!left.isAuto())   
409         computedLeftValue = valueForLength(left, containingBlockWidth);
410     else if (right.isAuto()) {
411         // FIXME: take direction into account
412         computedLeftValue = displayBox.left();
413     } else {
414         // #5
415         auto marginLeft = displayBox.marginLeft();
416         auto marginRight = displayBox.marginRight();
417     
418         auto paddingLeft = displayBox.paddingLeft();
419         auto paddingRight = displayBox.paddingRight();
420
421         auto borderLeft = displayBox.borderLeft();
422         auto borderRight = displayBox.borderRight();
423
424         computedLeftValue = containingBlockWidth - (marginLeft + borderLeft + paddingLeft + size.width() + paddingRight + borderRight + marginRight + right.value());
425     }
426
427     return { computedLeftValue, computedTopValue };
428 }
429
430 LayoutUnit FormattingContext::Geometry::replacedHeight(LayoutContext&, const Box& layoutBox)
431 {
432     ASSERT((layoutBox.isOutOfFlowPositioned() || layoutBox.isFloatingPositioned() || layoutBox.isInFlow()) && layoutBox.replaced());
433     // 10.6.5 Absolutely positioned, replaced elements. The used value of 'height' is determined as for inline replaced elements.
434
435     // 10.6.2 Inline replaced elements, block-level replaced elements in normal flow, 'inline-block' replaced elements in normal flow and floating replaced elements
436     //
437     // 1. 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'.
438     //
439     // 2. Otherwise, if 'height' has a computed value of 'auto', and the element has an intrinsic ratio then the used value of 'height' is:
440     //    (used width) / (intrinsic ratio)
441     //
442     // 3. 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'.
443     //
444     // 4. 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
445     //    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.
446     auto& style = layoutBox.style();
447     auto width = style.logicalWidth();
448     auto height = style.logicalHeight();
449
450     LayoutUnit computedHeightValue;
451     auto replaced = layoutBox.replaced();
452     ASSERT(replaced);
453
454     if (height.isAuto()) {
455         if (width.isAuto() && replaced->hasIntrinsicHeight()) {
456             // #1
457             computedHeightValue = replaced->intrinsicHeight();
458         } else if (replaced->hasIntrinsicRatio()) {
459             // #2
460             computedHeightValue = width.value() / replaced->intrinsicRatio();
461         } else if (replaced->hasIntrinsicHeight()) {
462             // #3
463             computedHeightValue = replaced->intrinsicHeight();
464         } else {
465             // #4
466             computedHeightValue = 150;
467         }
468     } else
469         computedHeightValue = height.value();
470
471     return computedHeightValue;
472 }
473
474 LayoutUnit FormattingContext::Geometry::replacedWidth(LayoutContext&, const Box& layoutBox)
475 {
476     ASSERT((layoutBox.isOutOfFlowPositioned() || layoutBox.isFloatingPositioned() || layoutBox.isInFlow()) && layoutBox.replaced());
477
478     // 10.3.4 Block-level, replaced elements in normal flow: The used value of 'width' is determined as for inline replaced elements.
479     // 10.3.6 Floating, replaced elements: The used value of 'width' is determined as for inline replaced elements.
480     // 10.3.8 Absolutely positioned, replaced elements: The used value of 'width' is determined as for inline replaced elements.
481
482     // 10.3.2 Inline, replaced elements
483     //
484     // 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'.
485     //
486     // 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;
487     //    or if 'width' has a computed value of 'auto', 'height' has some other computed value, and the element does have an intrinsic ratio;
488     //    then the used value of 'width' is: (used height) * (intrinsic ratio)
489     //
490     // 3. If 'height' and 'width' both have computed values of 'auto' and the element has an intrinsic ratio but no intrinsic height or width,
491     //    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
492     //    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.
493     //
494     // 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'.
495     //
496     // 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.
497     //    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.
498     auto& style = layoutBox.style();
499     auto width = style.logicalWidth();
500     auto height = style.logicalHeight();
501
502     LayoutUnit computedWidthValue;
503     auto replaced = layoutBox.replaced();
504     ASSERT(replaced);
505
506     if (width.isAuto() && height.isAuto() && replaced->hasIntrinsicWidth()) {
507         // #1
508         computedWidthValue = replaced->intrinsicWidth();
509     } else if (width.isAuto() && (height.isCalculated() || replaced->hasIntrinsicHeight()) && replaced->hasIntrinsicRatio()) {
510         // #2
511         auto usedHeight = height.isCalculated() ? LayoutUnit(height.value()) : replaced->intrinsicHeight();
512         computedWidthValue = usedHeight * replaced->intrinsicRatio();
513     } else if (width.isAuto() && height.isAuto() && replaced->hasIntrinsicRatio()) {
514         // #3
515         // FIXME: undefined but surely doable.
516         ASSERT_NOT_IMPLEMENTED_YET();
517     } else if (width.isAuto() && replaced->hasIntrinsicWidth()) {
518         // #4
519         computedWidthValue = replaced->intrinsicWidth();
520     } else {
521         // #5
522         computedWidthValue = 300;
523     }
524
525     return computedWidthValue;
526 }
527
528 Display::Box::Edges FormattingContext::Geometry::computedBorder(LayoutContext&, const Box& layoutBox)
529 {
530     auto& style = layoutBox.style();
531     return {
532         style.borderTop().width(),
533         style.borderLeft().width(),
534         style.borderBottom().width(),
535         style.borderRight().width()
536     };
537 }
538
539 std::optional<Display::Box::Edges> FormattingContext::Geometry::computedPadding(LayoutContext& layoutContext, const Box& layoutBox)
540 {
541     if (!layoutBox.isPaddingApplicable())
542         return std::nullopt;
543
544     auto& style = layoutBox.style();
545     auto containingBlockWidth = layoutContext.displayBoxForLayoutBox(*layoutBox.containingBlock())->width();
546     return Display::Box::Edges(
547         valueForLength(style.paddingTop(), containingBlockWidth),
548         valueForLength(style.paddingLeft(), containingBlockWidth),
549         valueForLength(style.paddingBottom(), containingBlockWidth),
550         valueForLength(style.paddingRight(), containingBlockWidth)
551     );
552 }
553
554 }
555 }
556 #endif