Use Optional::valueOr() instead of Optional::value_or()
[WebKit-https.git] / Source / WebCore / layout / FormattingContextGeometry.cpp
1 /*
2  * Copyright (C) 2018 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "FormattingContext.h"
28
29 #if ENABLE(LAYOUT_FORMATTING_CONTEXT)
30
31 #include "FloatingState.h"
32 #include "FormattingState.h"
33 #include "InlineFormattingState.h"
34
35 namespace WebCore {
36 namespace Layout {
37
38 static inline bool isHeightAuto(const Box& layoutBox)
39 {
40     // 10.5 Content height: the 'height' property
41     //
42     // The percentage is calculated with respect to the height of the generated box's containing block.
43     // If the height of the containing block is not specified explicitly (i.e., it depends on content height),
44     // and this element is not absolutely positioned, the used height is calculated as if 'auto' was specified.
45
46     auto height = layoutBox.style().logicalHeight();
47     if (height.isAuto())
48         return true;
49
50     if (height.isPercent()) {
51         if (layoutBox.isOutOfFlowPositioned())
52             return false;
53
54         return !layoutBox.containingBlock()->style().logicalHeight().isFixed();
55     }
56
57     return false;
58 }
59
60 Optional<LayoutUnit> FormattingContext::Geometry::computedHeightValue(const LayoutState& layoutState, const Box& layoutBox, HeightType heightType)
61 {
62     auto& style = layoutBox.style();
63     auto height = heightType == HeightType::Normal ? style.logicalHeight() : heightType == HeightType::Min ? style.logicalMinHeight() : style.logicalMaxHeight();
64     if (height.isUndefined() || height.isAuto())
65         return { };
66
67     if (height.isFixed())
68         return { height.value() };
69
70     Optional<LayoutUnit> containingBlockHeightValue;
71     if (layoutBox.isOutOfFlowPositioned()) {
72         // Containing block's height is already computed since we layout the out-of-flow boxes as the last step.
73         containingBlockHeightValue = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock()).height();
74     } else {
75         if (layoutState.inQuirksMode())
76             containingBlockHeightValue = FormattingContext::Quirks::heightValueOfNearestContainingBlockWithFixedHeight(layoutState, layoutBox);
77         else {
78             auto containingBlockHeight = layoutBox.containingBlock()->style().logicalHeight();
79             if (containingBlockHeight.isFixed())
80                 containingBlockHeightValue = { containingBlockHeight.value() };
81         }
82     }
83
84     if (!containingBlockHeightValue)
85         return { };
86
87     return valueForLength(height, *containingBlockHeightValue);
88 }
89
90 static LayoutUnit contentHeightForFormattingContextRoot(const LayoutState& layoutState, const Box& layoutBox)
91 {
92     ASSERT(isHeightAuto(layoutBox) && (layoutBox.establishesFormattingContext() || layoutBox.isDocumentBox()));
93
94     // 10.6.7 'Auto' heights for block formatting context roots
95
96     // 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.
97     // If it has block-level children, the height is the distance between the top margin-edge of the topmost block-level
98     // child box and the bottom margin-edge of the bottommost block-level child box.
99
100     // In addition, if the element has any floating descendants whose bottom margin edge is below the element's bottom content edge,
101     // then the height is increased to include those edges. Only floats that participate in this block formatting context are taken
102     // into account, e.g., floats inside absolutely positioned descendants or other floats are not.
103     if (!is<Container>(layoutBox) || !downcast<Container>(layoutBox).hasInFlowOrFloatingChild())
104         return 0;
105
106     LayoutUnit top;
107     LayoutUnit bottom;
108     auto& formattingRootContainer = downcast<Container>(layoutBox);
109     if (formattingRootContainer.establishesInlineFormattingContext()) {
110         // This is temp and will be replaced by the correct display box once inline runs move over to the display tree.
111         auto& inlineRuns = downcast<InlineFormattingState>(layoutState.establishedFormattingState(layoutBox)).inlineRuns();
112         if (!inlineRuns.isEmpty()) {
113             top = inlineRuns[0].logicalTop();
114             bottom =  inlineRuns.last().logicalBottom();
115         }
116     } else if (formattingRootContainer.establishesBlockFormattingContext() || layoutBox.isDocumentBox()) {
117         auto& firstDisplayBox = layoutState.displayBoxForLayoutBox(*formattingRootContainer.firstInFlowChild());
118         auto& lastDisplayBox = layoutState.displayBoxForLayoutBox(*formattingRootContainer.lastInFlowChild());
119         top = firstDisplayBox.rectWithMargin().top();
120         bottom = lastDisplayBox.rectWithMargin().bottom();
121     }
122
123     auto* formattingContextRoot = &layoutBox;
124     // TODO: The document renderer is not a formatting context root by default at all. Need to find out what it is.
125     if (!layoutBox.establishesFormattingContext()) {
126         ASSERT(layoutBox.isDocumentBox());
127         formattingContextRoot = &layoutBox.formattingContextRoot();
128     }
129
130     auto floatsBottom = layoutState.establishedFormattingState(*formattingContextRoot).floatingState().bottom(*formattingContextRoot);
131     if (floatsBottom)
132         bottom = std::max<LayoutUnit>(*floatsBottom, bottom);
133
134     auto computedHeight = bottom - top;
135     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height] -> content height for formatting context root -> height(" << computedHeight << "px) layoutBox("<< &layoutBox << ")");
136     return computedHeight;
137 }
138
139 Optional<LayoutUnit> FormattingContext::Geometry::computedValueIfNotAuto(const Length& geometryProperty, LayoutUnit containingBlockWidth)
140 {
141     if (geometryProperty.isUndefined())
142         return WTF::nullopt;
143
144     if (geometryProperty.isAuto())
145         return WTF::nullopt;
146
147     return valueForLength(geometryProperty, containingBlockWidth);
148 }
149
150 Optional<LayoutUnit> FormattingContext::Geometry::fixedValue(const Length& geometryProperty)
151 {
152     if (!geometryProperty.isFixed())
153         return WTF::nullopt;
154     return { geometryProperty.value() };
155 }
156
157 // https://www.w3.org/TR/CSS22/visudet.html#min-max-heights
158 // Specifies a percentage for determining the used value. The percentage is calculated with respect to the height of the generated box's containing block.
159 // If the height of the containing block is not specified explicitly (i.e., it depends on content height), and this element is not absolutely positioned,
160 // the percentage value is treated as '0' (for 'min-height') or 'none' (for 'max-height').
161 Optional<LayoutUnit> FormattingContext::Geometry::computedMaxHeight(const LayoutState& layoutState, const Box& layoutBox)
162 {
163     return computedHeightValue(layoutState, layoutBox, HeightType::Max);
164 }
165
166 Optional<LayoutUnit> FormattingContext::Geometry::computedMinHeight(const LayoutState& layoutState, const Box& layoutBox)
167 {
168     if (auto minHeightValue = computedHeightValue(layoutState, layoutBox, HeightType::Min))
169         return minHeightValue;
170
171     return { 0 };
172 }
173
174 static LayoutUnit staticVerticalPositionForOutOfFlowPositioned(const LayoutState& layoutState, const Box& layoutBox)
175 {
176     ASSERT(layoutBox.isOutOfFlowPositioned());
177
178     // 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
179     // 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
180     // 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
181     // '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
182     // computed value for 'display'.) The value is negative if the hypothetical box is above the containing block.
183
184     // Start with this box's border box offset from the parent's border box.
185     LayoutUnit top;
186     if (auto* previousInFlowSibling = layoutBox.previousInFlowSibling()) {
187         // Add sibling offset
188         auto& previousInFlowDisplayBox = layoutState.displayBoxForLayoutBox(*previousInFlowSibling);
189         top += previousInFlowDisplayBox.bottom() + previousInFlowDisplayBox.nonCollapsedMarginAfter();
190     } else {
191         ASSERT(layoutBox.parent());
192         top = layoutState.displayBoxForLayoutBox(*layoutBox.parent()).contentBoxTop();
193     }
194
195     // Resolve top all the way up to the containing block.
196     auto* containingBlock = layoutBox.containingBlock();
197     for (auto* container = layoutBox.parent(); container != containingBlock; container = container->containingBlock()) {
198         auto& displayBox = layoutState.displayBoxForLayoutBox(*container);
199         // Display::Box::top is the border box top position in its containing block's coordinate system.
200         top += displayBox.top();
201         ASSERT(!container->isPositioned());
202     }
203     // FIXME: floatings need to be taken into account.
204     return top;
205 }
206
207 static LayoutUnit staticHorizontalPositionForOutOfFlowPositioned(const LayoutState& layoutState, const Box& layoutBox)
208 {
209     ASSERT(layoutBox.isOutOfFlowPositioned());
210     // See staticVerticalPositionForOutOfFlowPositioned for the definition of the static position.
211
212     // Start with this box's border box offset from the parent's border box.
213     ASSERT(layoutBox.parent());
214     auto left = layoutState.displayBoxForLayoutBox(*layoutBox.parent()).contentBoxLeft();
215
216     // Resolve left all the way up to the containing block.
217     auto* containingBlock = layoutBox.containingBlock();
218     for (auto* container = layoutBox.parent(); container != containingBlock; container = container->containingBlock()) {
219         auto& displayBox = layoutState.displayBoxForLayoutBox(*container);
220         // Display::Box::left is the border box left position in its containing block's coordinate system.
221         left += displayBox.left();
222         ASSERT(!container->isPositioned());
223     }
224     // FIXME: floatings need to be taken into account.
225     return left;
226 }
227
228 LayoutUnit FormattingContext::Geometry::shrinkToFitWidth(LayoutState& layoutState, const Box& formattingRoot)
229 {
230     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width] -> shrink to fit -> unsupported -> width(" << LayoutUnit { } << "px) layoutBox: " << &formattingRoot << ")");
231     ASSERT(formattingRoot.establishesFormattingContext());
232
233     // Calculation of the shrink-to-fit width is similar to calculating the width of a table cell using the automatic table layout algorithm.
234     // Roughly: calculate the preferred width by formatting the content without breaking lines other than where explicit line breaks occur,
235     // and also calculate the preferred minimum width, e.g., by trying all possible line breaks. CSS 2.2 does not define the exact algorithm.
236     // 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',
237     // 'padding-left', 'padding-right', 'border-right-width', 'margin-right', and the widths of any relevant scroll bars.
238
239     // Then the shrink-to-fit width is: min(max(preferred minimum width, available width), preferred width).
240     auto availableWidth = layoutState.displayBoxForLayoutBox(*formattingRoot.containingBlock()).width();
241     auto& formattingState = layoutState.createFormattingStateForFormattingRootIfNeeded(formattingRoot);
242     auto instrinsicWidthConstraints = formattingState.createFormattingContext(formattingRoot)->instrinsicWidthConstraints();
243     return std::min(std::max(instrinsicWidthConstraints.minimum, availableWidth), instrinsicWidthConstraints.maximum);
244 }
245
246 VerticalGeometry FormattingContext::Geometry::outOfFlowNonReplacedVerticalGeometry(const LayoutState& layoutState, const Box& layoutBox, Optional<LayoutUnit> usedHeight)
247 {
248     ASSERT(layoutBox.isOutOfFlowPositioned() && !layoutBox.replaced());
249
250     // 10.6.4 Absolutely positioned, non-replaced elements
251     //
252     // For absolutely positioned elements, the used values of the vertical dimensions must satisfy this constraint:
253     // 'top' + 'margin-top' + 'border-top-width' + 'padding-top' + 'height' + 'padding-bottom' + 'border-bottom-width' + 'margin-bottom' + 'bottom'
254     // = height of containing block
255
256     // If all three of 'top', 'height', and 'bottom' are auto, set 'top' to the static position and apply rule number three below.
257
258     // If none of the three are 'auto': If both 'margin-top' and 'margin-bottom' are 'auto', solve the equation under the extra
259     // constraint that the two margins get equal values. If one of 'margin-top' or 'margin-bottom' is 'auto', solve the equation for that value.
260     // If the values are over-constrained, ignore the value for 'bottom' and solve for that value.
261
262     // Otherwise, pick the one of the following six rules that applies.
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
274     auto& style = layoutBox.style();
275     auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
276     auto& containingBlockDisplayBox = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock());
277     auto containingBlockHeight = containingBlockDisplayBox.height();
278     auto containingBlockWidth = containingBlockDisplayBox.width();
279
280     auto top = computedValueIfNotAuto(style.logicalTop(), containingBlockWidth);
281     auto bottom = computedValueIfNotAuto(style.logicalBottom(), containingBlockWidth);
282     auto height = usedHeight ? usedHeight.value() : computedHeightValue(layoutState, layoutBox, HeightType::Normal);
283     auto marginBefore = computedValueIfNotAuto(style.marginBefore(), containingBlockWidth);
284     auto marginAfter = computedValueIfNotAuto(style.marginAfter(), containingBlockWidth);
285     auto paddingTop = displayBox.paddingTop().valueOr(0);
286     auto paddingBottom = displayBox.paddingBottom().valueOr(0);
287     auto borderTop = displayBox.borderTop();
288     auto borderBottom = displayBox.borderBottom();
289
290     if (!top && !height && !bottom)
291         top = staticVerticalPositionForOutOfFlowPositioned(layoutState, layoutBox);
292
293     if (top && height && bottom) {
294         if (!marginBefore && !marginAfter) {
295             auto marginBeforeAndAfter = containingBlockHeight - (*top + borderTop + paddingTop + *height + paddingBottom + borderBottom + *bottom);
296             marginBefore = marginAfter = marginBeforeAndAfter / 2;
297         } else if (!marginBefore)
298             marginBefore = containingBlockHeight - (*top + borderTop + paddingTop + *height + paddingBottom + borderBottom + *marginAfter + *bottom);
299         else
300             marginAfter = containingBlockHeight - (*top + *marginBefore + borderTop + paddingTop + *height + paddingBottom + borderBottom + *bottom);
301         // Over-constrained?
302         auto boxHeight = *top + *marginBefore + borderTop + paddingTop + *height + paddingBottom + borderBottom + *marginAfter + *bottom;
303         if (boxHeight > containingBlockHeight)
304             bottom = containingBlockHeight - (*top + *marginBefore + borderTop + paddingTop + *height + paddingBottom + borderBottom + *marginAfter); 
305     }
306
307     if (!top && !height && bottom) {
308         // #1
309         height = contentHeightForFormattingContextRoot(layoutState, layoutBox);
310         marginBefore = marginBefore.valueOr(0);
311         marginAfter = marginAfter.valueOr(0);
312         top = containingBlockHeight - (*marginBefore + borderTop + paddingTop + *height + paddingBottom + borderBottom + *marginAfter + *bottom); 
313     }
314
315     if (!top && !bottom && height) {
316         // #2
317         top = staticVerticalPositionForOutOfFlowPositioned(layoutState, layoutBox);
318         marginBefore = marginBefore.valueOr(0);
319         marginAfter = marginAfter.valueOr(0);
320         bottom = containingBlockHeight - (*top + *marginBefore + borderTop + paddingTop + *height + paddingBottom + borderBottom + *marginAfter); 
321     }
322
323     if (!height && !bottom && top) {
324         // #3
325         height = contentHeightForFormattingContextRoot(layoutState, layoutBox);
326         marginBefore = marginBefore.valueOr(0);
327         marginAfter = marginAfter.valueOr(0);
328         bottom = containingBlockHeight - (*top + *marginBefore + borderTop + paddingTop + *height + paddingBottom + borderBottom + *marginAfter); 
329     }
330
331     if (!top && height && bottom) {
332         // #4
333         marginBefore = marginBefore.valueOr(0);
334         marginAfter = marginAfter.valueOr(0);
335         top = containingBlockHeight - (*marginBefore + borderTop + paddingTop + *height + paddingBottom + borderBottom + *marginAfter + *bottom); 
336     }
337
338     if (!height && top && bottom) {
339         // #5
340         marginBefore = marginBefore.valueOr(0);
341         marginAfter = marginAfter.valueOr(0);
342         height = containingBlockHeight - (*top + *marginBefore + borderTop + paddingTop + paddingBottom + borderBottom + *marginAfter + *bottom); 
343     }
344
345     if (!bottom && top && height) {
346         // #6
347         marginBefore = marginBefore.valueOr(0);
348         marginAfter = marginAfter.valueOr(0);
349         bottom = containingBlockHeight - (*top + *marginBefore + borderTop + paddingTop + *height + paddingBottom + borderBottom + *marginAfter); 
350     }
351
352     ASSERT(top);
353     ASSERT(bottom);
354     ASSERT(height);
355     ASSERT(marginBefore);
356     ASSERT(marginAfter);
357
358     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position][Height][Margin] -> out-of-flow non-replaced -> top(" << *top << "px) bottom("  << *bottom << "px) height(" << *height << "px) margin(" << *marginBefore << "px, "  << *marginAfter << "px) layoutBox(" << &layoutBox << ")");
359     return { *top, *bottom, { *height, { { *marginBefore, *marginAfter }, { } } } };
360 }
361
362 HorizontalGeometry FormattingContext::Geometry::outOfFlowNonReplacedHorizontalGeometry(LayoutState& layoutState, const Box& layoutBox, Optional<LayoutUnit> usedWidth)
363 {
364     ASSERT(layoutBox.isOutOfFlowPositioned() && !layoutBox.replaced());
365     
366     // 10.3.7 Absolutely positioned, non-replaced elements
367     //
368     // 'left' + 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' + 'right'
369     // = width of containing block
370
371     // If all three of 'left', 'width', and 'right' are 'auto': First set any 'auto' values for 'margin-left' and 'margin-right' to 0.
372     // Then, if the 'direction' property of the element establishing the static-position containing block is 'ltr' set 'left' to the static
373     // position and apply rule number three below; otherwise, set 'right' to the static position and apply rule number one below.
374     //
375     // 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,
376     // 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
377     // solve for 'margin-right' ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto', solve the equation for that value.
378     // If the values are over-constrained, ignore the value for 'left' (in case the 'direction' property of the containing block is 'rtl') or 'right'
379     // (in case 'direction' is 'ltr') and solve for that value.
380     //
381     // Otherwise, set 'auto' values for 'margin-left' and 'margin-right' to 0, and pick the one of the following six rules that applies.
382     //
383     // 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the width is shrink-to-fit. Then solve for 'left'
384     // 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if the 'direction' property of the element establishing the static-position 
385     //    containing block is 'ltr' set 'left' to the static position, otherwise set 'right' to the static position.
386     //    Then solve for 'left' (if 'direction is 'rtl') or 'right' (if 'direction' is 'ltr').
387     // 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the width is shrink-to-fit . Then solve for 'right'
388     // 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve for 'left'
389     // 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve for 'width'
390     // 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve for 'right'
391
392     auto& style = layoutBox.style();
393     auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
394     auto& containingBlock = *layoutBox.containingBlock();
395     auto containingBlockWidth = layoutState.displayBoxForLayoutBox(containingBlock).contentBoxWidth();
396     auto isLeftToRightDirection = containingBlock.style().isLeftToRightDirection();
397     
398     auto left = computedValueIfNotAuto(style.logicalLeft(), containingBlockWidth);
399     auto right = computedValueIfNotAuto(style.logicalRight(), containingBlockWidth);
400     auto width = computedValueIfNotAuto(usedWidth ? Length { usedWidth.value(), Fixed } : style.logicalWidth(), containingBlockWidth);
401     auto marginStart = computedValueIfNotAuto(style.marginStart(), containingBlockWidth);
402     auto marginEnd = computedValueIfNotAuto(style.marginEnd(), containingBlockWidth);
403     auto nonComputedMarginStart = marginStart.valueOr(0);
404     auto nonComputedMarginEnd = marginEnd.valueOr(0);
405     auto paddingLeft = displayBox.paddingLeft().valueOr(0);
406     auto paddingRight = displayBox.paddingRight().valueOr(0);
407     auto borderLeft = displayBox.borderLeft();
408     auto borderRight = displayBox.borderRight();
409
410     if (!left && !width && !right) {
411         // If all three of 'left', 'width', and 'right' are 'auto': First set any 'auto' values for 'margin-left' and 'margin-right' to 0.
412         // Then, if the 'direction' property of the element establishing the static-position containing block is 'ltr' set 'left' to the static
413         // position and apply rule number three below; otherwise, set 'right' to the static position and apply rule number one below.
414         marginStart = marginStart.valueOr(0);
415         marginEnd = marginEnd.valueOr(0);
416
417         auto staticHorizontalPosition = staticHorizontalPositionForOutOfFlowPositioned(layoutState, layoutBox);
418         if (isLeftToRightDirection)
419             left = staticHorizontalPosition;
420         else
421             right = staticHorizontalPosition;
422     } else if (left && width && right) {
423         // 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,
424         // 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
425         // solve for 'margin-right' ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto', solve the equation for that value.
426         // If the values are over-constrained, ignore the value for 'left' (in case the 'direction' property of the containing block is 'rtl') or 'right'
427         // (in case 'direction' is 'ltr') and solve for that value.
428         if (!marginStart && !marginEnd) {
429             auto marginStartAndEnd = containingBlockWidth - (*left + borderLeft + paddingLeft + *width + paddingRight + borderRight + *right);
430             if (marginStartAndEnd >= 0)
431                 marginStart = marginEnd = marginStartAndEnd / 2;  
432             else {
433                 if (isLeftToRightDirection) {
434                     marginStart = 0_lu;
435                     marginEnd = containingBlockWidth - (*left + *marginStart + borderLeft + paddingLeft + *width + paddingRight + borderRight + *right);
436                 } else {
437                     marginEnd = 0_lu;
438                     marginStart = containingBlockWidth - (*left + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginEnd + *right);
439                 }
440             }
441         } else if (!marginStart) {
442             marginStart = containingBlockWidth - (*left + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginEnd + *right);
443             // Overconstrained? Ignore right (left).
444             if (*marginStart < 0) {
445                 if (isLeftToRightDirection)
446                     marginStart = containingBlockWidth - (*left + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginEnd);
447                 else
448                     marginStart = containingBlockWidth - (borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginEnd + *right);
449             }
450         } else if (!marginEnd) {
451             marginEnd = containingBlockWidth - (*left + *marginStart + borderLeft + paddingLeft + *width + paddingRight + borderRight + *right);
452             // Overconstrained? Ignore right (left).
453             if (*marginEnd < 0) {
454                 if (isLeftToRightDirection)
455                     marginEnd = containingBlockWidth - (*left + *marginStart + borderLeft + paddingLeft + *width + paddingRight + borderRight);
456                 else
457                     marginEnd = containingBlockWidth - (*marginStart + borderLeft + paddingLeft + *width + paddingRight + borderRight + *right);
458             }
459         }
460     } else {
461         // Otherwise, set 'auto' values for 'margin-left' and 'margin-right' to 0, and pick the one of the following six rules that applies.
462         marginStart = marginStart.valueOr(0);
463         marginEnd = marginEnd.valueOr(0);
464     }
465
466     ASSERT(marginStart);
467     ASSERT(marginEnd);
468
469     if (!left && !width && right) {
470         // #1
471         width = shrinkToFitWidth(layoutState, layoutBox);
472         left = containingBlockWidth - (*marginStart + borderLeft + paddingLeft + *width + paddingRight  + borderRight + *marginEnd + *right);
473     } else if (!left && !right && width) {
474         // #2
475         auto staticHorizontalPosition = staticHorizontalPositionForOutOfFlowPositioned(layoutState, layoutBox);
476         if (isLeftToRightDirection) {
477             left = staticHorizontalPosition;
478             right = containingBlockWidth - (*left + *marginStart + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginEnd);
479         } else {
480             right = staticHorizontalPosition;
481             left = containingBlockWidth - (*marginStart + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginEnd + *right);
482         }
483     } else if (!width && !right && left) {
484         // #3
485         width = shrinkToFitWidth(layoutState, layoutBox);
486         right = containingBlockWidth - (*left + *marginStart + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginEnd);
487     } else if (!left && width && right) {
488         // #4
489         left = containingBlockWidth - (*marginStart + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginEnd + *right);
490     } else if (!width && left && right) {
491         // #5
492         width = containingBlockWidth - (*left + *marginStart + borderLeft + paddingLeft + paddingRight  + borderRight + *marginEnd + *right);
493     } else if (!right && left && width) {
494         // #6
495         right = containingBlockWidth - (*left + *marginStart + borderLeft + paddingLeft + *width + paddingRight + borderRight + *marginEnd);
496     }
497
498     ASSERT(left);
499     ASSERT(right);
500     ASSERT(width);
501     ASSERT(marginStart);
502     ASSERT(marginEnd);
503
504     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position][Width][Margin] -> out-of-flow non-replaced -> left(" << *left << "px) right("  << *right << "px) width(" << *width << "px) margin(" << *marginStart << "px, "  << *marginEnd << "px) layoutBox(" << &layoutBox << ")");
505     return { *left, *right, { *width, { *marginStart, *marginEnd }, { nonComputedMarginStart, nonComputedMarginEnd } } };
506 }
507
508 VerticalGeometry FormattingContext::Geometry::outOfFlowReplacedVerticalGeometry(const LayoutState& layoutState, const Box& layoutBox, Optional<LayoutUnit> usedHeight)
509 {
510     ASSERT(layoutBox.isOutOfFlowPositioned() && layoutBox.replaced());
511
512     // 10.6.5 Absolutely positioned, replaced elements
513     //
514     // The used value of 'height' is determined as for inline replaced elements.
515     // If 'margin-top' or 'margin-bottom' is specified as 'auto' its used value is determined by the rules below.
516     // 1. If both 'top' and 'bottom' have the value 'auto', replace 'top' with the element's static position.
517     // 2. If 'bottom' is 'auto', replace any 'auto' on 'margin-top' or 'margin-bottom' with '0'.
518     // 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.
519     // 4. If at this point there is only one 'auto' left, solve the equation for that value.
520     // 5. If at this point the values are over-constrained, ignore the value for 'bottom' and solve for that value.
521
522     auto& style = layoutBox.style();
523     auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
524     auto& containingBlockDisplayBox = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock());
525     auto containingBlockHeight = containingBlockDisplayBox.height();
526     auto containingBlockWidth = containingBlockDisplayBox.width();
527
528     auto top = computedValueIfNotAuto(style.logicalTop(), containingBlockWidth);
529     auto bottom = computedValueIfNotAuto(style.logicalBottom(), containingBlockWidth);
530     auto height = inlineReplacedHeightAndMargin(layoutState, layoutBox, usedHeight).height;
531     auto marginBefore = computedValueIfNotAuto(style.marginBefore(), containingBlockWidth);
532     auto marginAfter = computedValueIfNotAuto(style.marginAfter(), containingBlockWidth);
533     auto paddingTop = displayBox.paddingTop().valueOr(0);
534     auto paddingBottom = displayBox.paddingBottom().valueOr(0);
535     auto borderTop = displayBox.borderTop();
536     auto borderBottom = displayBox.borderBottom();
537
538     if (!top && !bottom) {
539         // #1
540         top = staticVerticalPositionForOutOfFlowPositioned(layoutState, layoutBox);
541     }
542
543     if (!bottom) {
544         // #2
545         marginBefore = marginBefore.valueOr(0);
546         marginAfter = marginAfter.valueOr(0);
547     }
548
549     if (!marginBefore && !marginAfter) {
550         // #3
551         auto marginBeforeAndAfter = containingBlockHeight - (*top + borderTop + paddingTop + height + paddingBottom + borderBottom + *bottom);
552         marginBefore = marginAfter = marginBeforeAndAfter / 2;
553     }
554
555     // #4
556     if (!top)
557         top = containingBlockHeight - (*marginBefore + borderTop + paddingTop + height + paddingBottom + borderBottom + *marginAfter + *bottom);
558
559     if (!bottom)
560         bottom = containingBlockHeight - (*top + *marginBefore + borderTop + paddingTop + height + paddingBottom + borderBottom + *marginAfter);
561
562     if (!marginBefore)
563         marginBefore = containingBlockHeight - (*top + borderTop + paddingTop + height + paddingBottom + borderBottom + *marginAfter + *bottom);
564
565     if (!marginAfter)
566         marginAfter = containingBlockHeight - (*top + *marginBefore + borderTop + paddingTop + height + paddingBottom + borderBottom + *bottom);
567
568     // #5
569     auto boxHeight = *top + *marginBefore + borderTop + paddingTop + height + paddingBottom + borderBottom + *marginAfter + *bottom;
570     if (boxHeight > containingBlockHeight)
571         bottom = containingBlockHeight - (*top + *marginBefore + borderTop + paddingTop + height + paddingBottom + borderBottom + *marginAfter); 
572
573     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position][Height][Margin] -> out-of-flow replaced -> top(" << *top << "px) bottom("  << *bottom << "px) height(" << height << "px) margin(" << *marginBefore << "px, "  << *marginAfter << "px) layoutBox(" << &layoutBox << ")");
574     return { *top, *bottom, { height, { { *marginBefore, *marginAfter }, { } } } };
575 }
576
577 HorizontalGeometry FormattingContext::Geometry::outOfFlowReplacedHorizontalGeometry(const LayoutState& layoutState, const Box& layoutBox, Optional<LayoutUnit> usedWidth)
578 {
579     ASSERT(layoutBox.isOutOfFlowPositioned() && layoutBox.replaced());
580
581     // 10.3.8 Absolutely positioned, replaced elements
582     // 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:
583     //
584     // 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.
585     // 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',
586     //   set 'left' to the static position; else if 'direction' is 'rtl', set 'right' to the static position.
587     // 2. If 'left' or 'right' are 'auto', replace any 'auto' on 'margin-left' or 'margin-right' with '0'.
588     // 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,
589     //   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
590     //   solve for 'margin-right' ('margin-left').
591     // 4. If at this point there is an 'auto' left, solve the equation for that value.
592     // 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
593     //   'right' (in case 'direction' is 'ltr') and solve for that value.
594
595     auto& style = layoutBox.style();
596     auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
597     auto& containingBlock = *layoutBox.containingBlock();
598     auto containingBlockWidth = layoutState.displayBoxForLayoutBox(containingBlock).contentBoxWidth();
599     auto isLeftToRightDirection = containingBlock.style().isLeftToRightDirection();
600
601     auto left = computedValueIfNotAuto(style.logicalLeft(), containingBlockWidth);
602     auto right = computedValueIfNotAuto(style.logicalRight(), containingBlockWidth);
603     auto marginStart = computedValueIfNotAuto(style.marginStart(), containingBlockWidth);
604     auto marginEnd = computedValueIfNotAuto(style.marginEnd(), containingBlockWidth);
605     auto nonComputedMarginStart = marginStart.valueOr(0);
606     auto nonComputedMarginEnd = marginEnd.valueOr(0);
607     auto width = inlineReplacedWidthAndMargin(layoutState, layoutBox, usedWidth).width;
608     auto paddingLeft = displayBox.paddingLeft().valueOr(0);
609     auto paddingRight = displayBox.paddingRight().valueOr(0);
610     auto borderLeft = displayBox.borderLeft();
611     auto borderRight = displayBox.borderRight();
612
613     if (!left && !right) {
614         // #1
615         auto staticHorizontalPosition = staticHorizontalPositionForOutOfFlowPositioned(layoutState, layoutBox);
616         if (isLeftToRightDirection)
617             left = staticHorizontalPosition;
618         else
619             right = staticHorizontalPosition;
620     }
621
622     if (!left || !right) {
623         // #2
624         marginStart = marginStart.valueOr(0); 
625         marginEnd = marginEnd.valueOr(0); 
626     }
627
628     if (!marginStart && !marginEnd) {
629         // #3
630         auto marginStartAndEnd = containingBlockWidth - (*left + borderLeft + paddingLeft + width + paddingRight + borderRight + *right);
631         if (marginStartAndEnd >= 0)
632             marginStart = marginEnd = marginStartAndEnd / 2;
633         else {
634             if (isLeftToRightDirection) {
635                 marginStart = 0_lu;
636                 marginEnd = containingBlockWidth - (*left + *marginStart + borderLeft + paddingLeft + width + paddingRight + borderRight + *right);
637             } else {
638                 marginEnd = 0_lu;
639                 marginStart = containingBlockWidth - (*left + borderLeft + paddingLeft + width + paddingRight + borderRight + *marginEnd + *right);
640             }
641         }
642     }
643
644     // #4
645     if (!left)
646         left = containingBlockWidth - (*marginStart + borderLeft + paddingLeft + width + paddingRight + borderRight + *marginEnd + *right);
647
648     if (!right)
649         right = containingBlockWidth - (*left + *marginStart + borderLeft + paddingLeft + width + paddingRight + borderRight + *marginEnd);
650
651     if (!marginStart)
652         marginStart = containingBlockWidth - (*left + borderLeft + paddingLeft + width + paddingRight + borderRight + *marginEnd + *right);
653
654     if (!marginEnd)
655         marginEnd = containingBlockWidth - (*left + *marginStart + borderLeft + paddingLeft + width + paddingRight + borderRight + *right);
656
657     auto boxWidth = (*left + *marginStart + borderLeft + paddingLeft + width + paddingRight + borderRight + *marginEnd + *right);
658     if (boxWidth > containingBlockWidth) {
659         // #5 Over-constrained?
660         if (isLeftToRightDirection)
661             right = containingBlockWidth - (*left + *marginStart + borderLeft + paddingLeft + width + paddingRight + borderRight + *marginEnd);
662         else
663             left = containingBlockWidth - (*marginStart + borderLeft + paddingLeft + width + paddingRight + borderRight + *marginEnd + *right);
664     }
665
666     ASSERT(left);
667     ASSERT(right);
668     ASSERT(marginStart);
669     ASSERT(marginEnd);
670
671     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position][Width][Margin] -> out-of-flow replaced -> left(" << *left << "px) right("  << *right << "px) width(" << width << "px) margin(" << *marginStart << "px, "  << *marginEnd << "px) layoutBox(" << &layoutBox << ")");
672     return { *left, *right, { width, { *marginStart, *marginEnd }, { nonComputedMarginStart, nonComputedMarginEnd } } };
673 }
674
675 HeightAndMargin FormattingContext::Geometry::complicatedCases(const LayoutState& layoutState, const Box& layoutBox, Optional<LayoutUnit> usedHeight)
676 {
677     ASSERT(!layoutBox.replaced());
678     // TODO: Use complicated-case for document renderer for now (see BlockFormattingContext::Geometry::inFlowHeightAndMargin).
679     ASSERT((layoutBox.isBlockLevelBox() && layoutBox.isInFlow() && !layoutBox.isOverflowVisible()) || layoutBox.isInlineBlockBox() || layoutBox.isFloatingPositioned() || layoutBox.isDocumentBox());
680
681     // 10.6.6 Complicated cases
682     //
683     // 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).
684     // 'Inline-block', non-replaced elements.
685     // Floating, non-replaced elements.
686     //
687     // 1. If 'margin-top', or 'margin-bottom' are 'auto', their used value is 0.
688     // 2. If 'height' is 'auto', the height depends on the element's descendants per 10.6.7.
689
690     auto& style = layoutBox.style();
691     auto& containingBlock = *layoutBox.containingBlock();
692     auto& containingBlockDisplayBox = layoutState.displayBoxForLayoutBox(containingBlock);
693     auto containingBlockWidth = containingBlockDisplayBox.contentBoxWidth();
694
695     auto height = usedHeight ? usedHeight.value() : computedHeightValue(layoutState, layoutBox, HeightType::Normal);
696     auto marginBefore = computedValueIfNotAuto(style.marginBefore(), containingBlockWidth);
697     auto marginAfter = computedValueIfNotAuto(style.marginAfter(), containingBlockWidth);
698
699     // #1
700     marginBefore = marginBefore.valueOr(0);
701     marginAfter = marginAfter.valueOr(0);
702     // #2
703     if (!height) {
704         ASSERT(isHeightAuto(layoutBox));
705         height = contentHeightForFormattingContextRoot(layoutState, layoutBox);
706     }
707
708     ASSERT(height);
709     ASSERT(marginBefore);
710     ASSERT(marginAfter);
711
712     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> floating non-replaced -> height(" << *height << "px) margin(" << *marginBefore << "px, " << *marginAfter << "px) -> layoutBox(" << &layoutBox << ")");
713     return HeightAndMargin { *height, { { *marginBefore, *marginAfter }, { } } };
714 }
715
716 WidthAndMargin FormattingContext::Geometry::floatingNonReplacedWidthAndMargin(LayoutState& layoutState, const Box& layoutBox, Optional<LayoutUnit> usedWidth)
717 {
718     ASSERT(layoutBox.isFloatingPositioned() && !layoutBox.replaced());
719
720     // 10.3.5 Floating, non-replaced elements
721     //
722     // 1. If 'margin-left', or 'margin-right' are computed as 'auto', their used value is '0'.
723     // 2. If 'width' is computed as 'auto', the used value is the "shrink-to-fit" width.
724
725     auto& containingBlock = *layoutBox.containingBlock();
726     auto containingBlockWidth = layoutState.displayBoxForLayoutBox(containingBlock).contentBoxWidth();
727
728     // #1
729     auto margin = computedNonCollapsedHorizontalMarginValue(layoutState, layoutBox);
730     // #2
731     auto width = computedValueIfNotAuto(usedWidth ? Length { usedWidth.value(), Fixed } : layoutBox.style().logicalWidth(), containingBlockWidth);
732     if (!width)
733         width = shrinkToFitWidth(layoutState, layoutBox);
734
735     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width][Margin] -> floating non-replaced -> width(" << *width << "px) margin(" << margin.start << "px, " << margin.end << "px) -> layoutBox(" << &layoutBox << ")");
736     return WidthAndMargin { *width, margin, margin };
737 }
738
739 HeightAndMargin FormattingContext::Geometry::floatingReplacedHeightAndMargin(const LayoutState& layoutState, const Box& layoutBox, Optional<LayoutUnit> usedHeight)
740 {
741     ASSERT(layoutBox.isFloatingPositioned() && layoutBox.replaced());
742
743     // 10.6.2 Inline replaced elements, block-level replaced elements in normal flow, 'inline-block'
744     // replaced elements in normal flow and floating replaced elements
745     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> floating replaced -> redirected to inline replaced");
746     return inlineReplacedHeightAndMargin(layoutState, layoutBox, usedHeight);
747 }
748
749 WidthAndMargin FormattingContext::Geometry::floatingReplacedWidthAndMargin(const LayoutState& layoutState, const Box& layoutBox, Optional<LayoutUnit> usedWidth)
750 {
751     ASSERT(layoutBox.isFloatingPositioned() && layoutBox.replaced());
752
753     // 10.3.6 Floating, replaced elements
754     //
755     // 1. If 'margin-left' or 'margin-right' are computed as 'auto', their used value is '0'.
756     // 2. The used value of 'width' is determined as for inline replaced elements.
757     auto margin = computedNonCollapsedHorizontalMarginValue(layoutState, layoutBox);
758
759     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> floating replaced -> redirected to inline replaced");
760     return inlineReplacedWidthAndMargin(layoutState, layoutBox, usedWidth, margin.start, margin.end);
761 }
762
763 VerticalGeometry FormattingContext::Geometry::outOfFlowVerticalGeometry(const LayoutState& layoutState, const Box& layoutBox, Optional<LayoutUnit> usedHeight)
764 {
765     ASSERT(layoutBox.isOutOfFlowPositioned());
766
767     if (!layoutBox.replaced())
768         return outOfFlowNonReplacedVerticalGeometry(layoutState, layoutBox, usedHeight);
769     return outOfFlowReplacedVerticalGeometry(layoutState, layoutBox, usedHeight);
770 }
771
772 HorizontalGeometry FormattingContext::Geometry::outOfFlowHorizontalGeometry(LayoutState& layoutState, const Box& layoutBox, Optional<LayoutUnit> usedWidth)
773 {
774     ASSERT(layoutBox.isOutOfFlowPositioned());
775
776     if (!layoutBox.replaced())
777         return outOfFlowNonReplacedHorizontalGeometry(layoutState, layoutBox, usedWidth);
778     return outOfFlowReplacedHorizontalGeometry(layoutState, layoutBox, usedWidth);
779 }
780
781 HeightAndMargin FormattingContext::Geometry::floatingHeightAndMargin(const LayoutState& layoutState, const Box& layoutBox, Optional<LayoutUnit> usedHeight)
782 {
783     ASSERT(layoutBox.isFloatingPositioned());
784
785     if (!layoutBox.replaced())
786         return complicatedCases(layoutState, layoutBox, usedHeight);
787     return floatingReplacedHeightAndMargin(layoutState, layoutBox, usedHeight);
788 }
789
790 WidthAndMargin FormattingContext::Geometry::floatingWidthAndMargin(LayoutState& layoutState, const Box& layoutBox, Optional<LayoutUnit> usedWidth)
791 {
792     ASSERT(layoutBox.isFloatingPositioned());
793
794     if (!layoutBox.replaced())
795         return floatingNonReplacedWidthAndMargin(layoutState, layoutBox, usedWidth);
796     return floatingReplacedWidthAndMargin(layoutState, layoutBox, usedWidth);
797 }
798
799 HeightAndMargin FormattingContext::Geometry::inlineReplacedHeightAndMargin(const LayoutState& layoutState, const Box& layoutBox, Optional<LayoutUnit> usedHeight)
800 {
801     ASSERT((layoutBox.isOutOfFlowPositioned() || layoutBox.isFloatingPositioned() || layoutBox.isInFlow()) && layoutBox.replaced());
802
803     // 10.6.2 Inline replaced elements, block-level replaced elements in normal flow, 'inline-block' replaced elements in normal flow and floating replaced elements
804     //
805     // 1. If 'margin-top', or 'margin-bottom' are 'auto', their used value is 0.
806     // 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'.
807     // 3. Otherwise, if 'height' has a computed value of 'auto', and the element has an intrinsic ratio then the used value of 'height' is:
808     //    (used width) / (intrinsic ratio)
809     // 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'.
810     // 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
811     //    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.
812
813     // #1
814     auto margin = computedNonCollapsedVerticalMarginValue(layoutState, layoutBox);
815
816     auto& style = layoutBox.style();
817     auto replaced = layoutBox.replaced();
818
819     auto height = usedHeight ? usedHeight.value() : computedHeightValue(layoutState, layoutBox, HeightType::Normal);
820     auto heightIsAuto = !usedHeight && isHeightAuto(layoutBox);
821     auto widthIsAuto = style.logicalWidth().isAuto();
822
823     if (heightIsAuto && widthIsAuto && replaced->hasIntrinsicHeight()) {
824         // #2
825         height = replaced->intrinsicHeight();
826     } else if (heightIsAuto && replaced->hasIntrinsicRatio()) {
827         // #3
828         auto usedWidth = layoutState.displayBoxForLayoutBox(layoutBox).width();
829         height = usedWidth / replaced->intrinsicRatio();
830     } else if (heightIsAuto && replaced->hasIntrinsicHeight()) {
831         // #4
832         height = replaced->intrinsicHeight();
833     } else if (heightIsAuto) {
834         // #5
835         height = { 150 };
836     }
837
838     ASSERT(height);
839
840     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> inflow replaced -> height(" << *height << "px) margin(" << margin.before << "px, " << margin.after << "px) -> layoutBox(" << &layoutBox << ")");
841     return { *height, { margin, { } } };
842 }
843
844 WidthAndMargin FormattingContext::Geometry::inlineReplacedWidthAndMargin(const LayoutState& layoutState, const Box& layoutBox,
845     Optional<LayoutUnit> usedWidth, Optional<LayoutUnit> precomputedMarginStart, Optional<LayoutUnit> precomputedMarginEnd)
846 {
847     ASSERT((layoutBox.isOutOfFlowPositioned() || layoutBox.isFloatingPositioned() || layoutBox.isInFlow()) && layoutBox.replaced());
848
849     // 10.3.2 Inline, replaced elements
850     //
851     // A computed value of 'auto' for 'margin-left' or 'margin-right' becomes a used value of '0'.
852     //
853     // 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'.
854     //
855     // 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;
856     //    or if 'width' has a computed value of 'auto', 'height' has some other computed value, and the element does have an intrinsic ratio;
857     //    then the used value of 'width' is: (used height) * (intrinsic ratio)
858     //
859     // 3. If 'height' and 'width' both have computed values of 'auto' and the element has an intrinsic ratio but no intrinsic height or width,
860     //    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
861     //    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.
862     //
863     // 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'.
864     //
865     // 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.
866     //    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.
867
868     auto& style = layoutBox.style();
869     auto& containingBlockDisplayBox = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock());
870     auto containingBlockWidth = containingBlockDisplayBox.width();
871
872     auto computeMarginEnd = [&]() {
873         if (precomputedMarginEnd)
874             return precomputedMarginEnd.value();
875         auto marginEnd = computedValueIfNotAuto(style.marginEnd(), containingBlockWidth);
876         return marginEnd.valueOr(0_lu);
877     };
878
879     auto computeMarginStart = [&]() {
880         if (precomputedMarginStart)
881             return precomputedMarginStart.value();
882         auto marginStart = computedValueIfNotAuto(style.marginStart(), containingBlockWidth);
883         return marginStart.valueOr(0_lu);
884     };
885
886     auto replaced = layoutBox.replaced();
887     ASSERT(replaced);
888
889     auto marginStart = computeMarginStart();
890     auto marginEnd = computeMarginEnd();
891     auto nonComputedMarginStart = computedValueIfNotAuto(style.marginStart(), containingBlockWidth).valueOr(0);
892     auto nonComputedMarginEnd = computedValueIfNotAuto(style.marginEnd(), containingBlockWidth).valueOr(0);
893     auto width = computedValueIfNotAuto(usedWidth ? Length { usedWidth.value(), Fixed } : style.logicalWidth(), containingBlockWidth);
894
895     auto heightIsAuto = isHeightAuto(layoutBox);
896     auto height = computedHeightValue(layoutState, layoutBox, HeightType::Normal);
897
898     if (!width && heightIsAuto && replaced->hasIntrinsicWidth()) {
899         // #1
900         width = replaced->intrinsicWidth();
901     } else if ((!width && heightIsAuto && !replaced->hasIntrinsicWidth() && replaced->hasIntrinsicHeight() && replaced->hasIntrinsicRatio())
902         || (!width && height && replaced->hasIntrinsicRatio())) {
903         // #2
904         width = height.valueOr(replaced->hasIntrinsicHeight()) * replaced->intrinsicRatio();
905     } else if (!width && heightIsAuto && replaced->hasIntrinsicRatio() && !replaced->hasIntrinsicWidth() && !replaced->hasIntrinsicHeight()) {
906         // #3
907         // FIXME: undefined but surely doable.
908         ASSERT_NOT_IMPLEMENTED_YET();
909     } else if (!width && replaced->hasIntrinsicWidth()) {
910         // #4
911         width = replaced->intrinsicWidth();
912     } else if (!width) {
913         // #5
914         width = { 300 };
915     }
916
917     ASSERT(width);
918
919     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width][Margin] -> inflow replaced -> width(" << *width << "px) margin(" << marginStart << "px, " << marginEnd << "px) -> layoutBox(" << &layoutBox << ")");
920     return { *width, { marginStart, marginEnd }, { nonComputedMarginStart, nonComputedMarginEnd } };
921 }
922
923 LayoutSize FormattingContext::Geometry::inFlowPositionedPositionOffset(const LayoutState& layoutState, const Box& layoutBox)
924 {
925     ASSERT(layoutBox.isInFlowPositioned());
926
927     // 9.4.3 Relative positioning
928     //
929     // The 'top' and 'bottom' properties move relatively positioned element(s) up or down without changing their size.
930     // Top' moves the boxes down, and 'bottom' moves them up. Since boxes are not split or stretched as a result of 'top' or 'bottom', the used values are always: top = -bottom.
931     //
932     // 1. If both are 'auto', their used values are both '0'.
933     // 2. If one of them is 'auto', it becomes the negative of the other.
934     // 3. If neither is 'auto', 'bottom' is ignored (i.e., the used value of 'bottom' will be minus the value of 'top').
935
936     auto& style = layoutBox.style();
937     auto& containingBlock = *layoutBox.containingBlock();
938     auto containingBlockWidth = layoutState.displayBoxForLayoutBox(containingBlock).contentBoxWidth();
939
940     auto top = computedValueIfNotAuto(style.logicalTop(), containingBlockWidth);
941     auto bottom = computedValueIfNotAuto(style.logicalBottom(), containingBlockWidth);
942
943     if (!top && !bottom) {
944         // #1
945         top = bottom = { 0 };
946     } else if (!top) {
947         // #2
948         top = -*bottom;
949     } else if (!bottom) {
950         // #3
951         bottom = -*top;
952     } else {
953         // #4
954         bottom = WTF::nullopt;
955     }
956
957     // For relatively positioned elements, 'left' and 'right' move the box(es) horizontally, without changing their size.
958     // 'Left' moves the boxes to the right, and 'right' moves them to the left.
959     // Since boxes are not split or stretched as a result of 'left' or 'right', the used values are always: left = -right.
960     //
961     // 1. If both 'left' and 'right' are 'auto' (their initial values), the used values are '0' (i.e., the boxes stay in their original position).
962     // 2. If 'left' is 'auto', its used value is minus the value of 'right' (i.e., the boxes move to the left by the value of 'right').
963     // 3. If 'right' is specified as 'auto', its used value is minus the value of 'left'.
964     // 4. If neither 'left' nor 'right' is 'auto', the position is over-constrained, and one of them has to be ignored.
965     //    If the 'direction' property of the containing block is 'ltr', the value of 'left' wins and 'right' becomes -'left'.
966     //    If 'direction' of the containing block is 'rtl', 'right' wins and 'left' is ignored.
967
968     auto left = computedValueIfNotAuto(style.logicalLeft(), containingBlockWidth);
969     auto right = computedValueIfNotAuto(style.logicalRight(), containingBlockWidth);
970
971     if (!left && !right) {
972         // #1
973         left = right = { 0 };
974     } else if (!left) {
975         // #2
976         left = -*right;
977     } else if (!right) {
978         // #3
979         right = -*left;
980     } else {
981         // #4
982         auto isLeftToRightDirection = containingBlock.style().isLeftToRightDirection();
983         if (isLeftToRightDirection)
984             right = -*left;
985         else
986             left = WTF::nullopt;
987     }
988
989     ASSERT(!bottom || *top == -*bottom);
990     ASSERT(!left || *left == -*right);
991
992     auto topPositionOffset = *top;
993     auto leftPositionOffset = left.valueOr(-*right);
994
995     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position] -> positioned inflow -> top offset(" << topPositionOffset << "px) left offset(" << leftPositionOffset << "px) layoutBox(" << &layoutBox << ")");
996     return { leftPositionOffset, topPositionOffset };
997 }
998
999 Edges FormattingContext::Geometry::computedBorder(const LayoutState&, const Box& layoutBox)
1000 {
1001     auto& style = layoutBox.style();
1002     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Border] -> layoutBox: " << &layoutBox);
1003     return {
1004         { style.borderLeft().boxModelWidth(), style.borderRight().boxModelWidth() },
1005         { style.borderTop().boxModelWidth(), style.borderBottom().boxModelWidth() }
1006     };
1007 }
1008
1009 Optional<Edges> FormattingContext::Geometry::computedPadding(const LayoutState& layoutState, const Box& layoutBox)
1010 {
1011     if (!layoutBox.isPaddingApplicable())
1012         return WTF::nullopt;
1013
1014     auto& style = layoutBox.style();
1015     auto containingBlockWidth = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock()).contentBoxWidth();
1016     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Padding] -> layoutBox: " << &layoutBox);
1017     return Edges {
1018         { valueForLength(style.paddingLeft(), containingBlockWidth), valueForLength(style.paddingRight(), containingBlockWidth) },
1019         { valueForLength(style.paddingTop(), containingBlockWidth), valueForLength(style.paddingBottom(), containingBlockWidth) }
1020     };
1021 }
1022
1023 HorizontalMargin FormattingContext::Geometry::computedNonCollapsedHorizontalMarginValue(const LayoutState& layoutState, const Box& layoutBox)
1024 {
1025     auto& style = layoutBox.style();
1026     auto containingBlockWidth = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock()).contentBoxWidth();
1027
1028     auto marginStart = computedValueIfNotAuto(style.marginStart(), containingBlockWidth).valueOr(0_lu);
1029     auto marginEnd = computedValueIfNotAuto(style.marginEnd(), containingBlockWidth).valueOr(0_lu);
1030
1031     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Margin] -> non collapsed horizontal -> margin(" << marginStart << "px, " << marginEnd << "px) -> layoutBox: " << &layoutBox);
1032     return { marginStart, marginEnd };
1033 }
1034
1035 VerticalMargin::ComputedValues FormattingContext::Geometry::computedNonCollapsedVerticalMarginValue(const LayoutState& layoutState, const Box& layoutBox)
1036 {
1037     auto& style = layoutBox.style();
1038     auto containingBlockWidth = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock()).contentBoxWidth();
1039
1040     auto marginBefore = computedValueIfNotAuto(style.marginBefore(), containingBlockWidth).valueOr(0_lu);
1041     auto marginAfter = computedValueIfNotAuto(style.marginAfter(), containingBlockWidth).valueOr(0_lu);
1042
1043     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Margin] -> non collapsed vertical -> margin(" << marginBefore << "px, " << marginAfter << "px) -> layoutBox: " << &layoutBox);
1044     return { marginBefore, marginAfter };
1045 }
1046
1047 }
1048 }
1049 #endif