[LFC] Layout::Box::containingBlock should return a const ContainerBox&
[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 "InlineFormattingState.h"
33 #include "LayoutContext.h"
34 #include "LayoutReplacedBox.h"
35 #include "Logging.h"
36 #include "TableFormattingState.h"
37
38 namespace WebCore {
39 namespace Layout {
40
41 static inline bool isHeightAuto(const Box& layoutBox)
42 {
43     // 10.5 Content height: the 'height' property
44     //
45     // The percentage is calculated with respect to the height of the generated box's containing block.
46     // If the height of the containing block is not specified explicitly (i.e., it depends on content height),
47     // and this element is not absolutely positioned, the used height is calculated as if 'auto' was specified.
48
49     auto height = layoutBox.style().logicalHeight();
50     if (height.isAuto())
51         return true;
52
53     if (height.isPercent()) {
54         if (layoutBox.isOutOfFlowPositioned())
55             return false;
56
57         return !layoutBox.containingBlock().style().logicalHeight().isFixed();
58     }
59
60     return false;
61 }
62
63 Optional<LayoutUnit> FormattingContext::Geometry::computedHeightValue(const Box& layoutBox, HeightType heightType, Optional<LayoutUnit> containingBlockHeight) const
64 {
65     auto& style = layoutBox.style();
66     auto height = heightType == HeightType::Normal ? style.logicalHeight() : heightType == HeightType::Min ? style.logicalMinHeight() : style.logicalMaxHeight();
67     if (height.isUndefined() || height.isAuto())
68         return { };
69
70     if (height.isFixed())
71         return LayoutUnit { height.value() };
72
73     if (!containingBlockHeight) {
74         // Containing block's height is already computed since we layout the out-of-flow boxes as the last step.
75         ASSERT(!layoutBox.isOutOfFlowPositioned());
76         if (layoutState().inQuirksMode())
77             containingBlockHeight = formattingContext().quirks().heightValueOfNearestContainingBlockWithFixedHeight(layoutBox);
78         else {
79             auto containingBlockHeightFromStyle = layoutBox.containingBlock().style().logicalHeight();
80             if (containingBlockHeightFromStyle.isFixed())
81                 containingBlockHeight = LayoutUnit { containingBlockHeightFromStyle.value() };
82         }
83     }
84
85     if (!containingBlockHeight)
86         return { };
87
88     return valueForLength(height, *containingBlockHeight);
89 }
90
91 Optional<LayoutUnit> FormattingContext::Geometry::computedContentHeight(const Box& layoutBox, Optional<LayoutUnit> containingBlockHeight) const
92 {
93     if (auto height = computedHeightValue(layoutBox, HeightType::Normal, containingBlockHeight)) {
94         if (layoutBox.style().boxSizing() == BoxSizing::ContentBox)
95             return height;
96         auto& boxGeometry = formattingContext().geometryForBox(layoutBox);
97         return *height - (boxGeometry.verticalBorder() + boxGeometry.verticalPadding().valueOr(0));
98     }
99     return { };
100 }
101
102 Optional<LayoutUnit> FormattingContext::Geometry::computedContentWidth(const Box& layoutBox, LayoutUnit containingBlockWidth) const
103 {
104     if (auto width = computedValueIfNotAuto(layoutBox.style().logicalWidth(), containingBlockWidth)) {
105         if (layoutBox.style().boxSizing() == BoxSizing::ContentBox)
106             return width;
107         auto& boxGeometry = formattingContext().geometryForBox(layoutBox);
108         return *width - (boxGeometry.horizontalBorder() + boxGeometry.horizontalPadding().valueOr(0));
109     }
110     return { };
111 }
112
113 LayoutUnit FormattingContext::Geometry::contentHeightForFormattingContextRoot(const Box& layoutBox) const
114 {
115     ASSERT(isHeightAuto(layoutBox) && (layoutBox.establishesFormattingContext() || layoutBox.isDocumentBox()));
116
117     // 10.6.7 'Auto' heights for block formatting context roots
118
119     // 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.
120     // If it has block-level children, the height is the distance between the top margin-edge of the topmost block-level
121     // child box and the bottom margin-edge of the bottommost block-level child box.
122
123     // In addition, if the element has any floating descendants whose bottom margin edge is below the element's bottom content edge,
124     // then the height is increased to include those edges. Only floats that participate in this block formatting context are taken
125     // into account, e.g., floats inside absolutely positioned descendants or other floats are not.
126     if (!is<ContainerBox>(layoutBox) || !downcast<ContainerBox>(layoutBox).hasInFlowOrFloatingChild())
127         return { };
128
129     auto& layoutState = this->layoutState();
130     auto& formattingContext = this->formattingContext();
131     auto& boxGeometry = formattingContext.geometryForBox(layoutBox);
132     auto borderAndPaddingTop = boxGeometry.borderTop() + boxGeometry.paddingTop().valueOr(0);
133     auto top = borderAndPaddingTop;
134     auto bottom = borderAndPaddingTop;
135     auto& formattingRootContainer = downcast<ContainerBox>(layoutBox);
136     if (formattingRootContainer.establishesInlineFormattingContext()) {
137         auto& lineBoxes = layoutState.establishedInlineFormattingState(formattingRootContainer).displayInlineContent()->lineBoxes;
138         // Even empty containers generate one line. 
139         ASSERT(!lineBoxes.isEmpty());
140         top = lineBoxes.first().top();
141         bottom = lineBoxes.last().bottom();
142     } else if (formattingRootContainer.establishesBlockFormattingContext() || formattingRootContainer.establishesTableFormattingContext() || formattingRootContainer.isDocumentBox()) {
143         if (formattingRootContainer.hasInFlowChild()) {
144             auto& firstBoxGeometry = formattingContext.geometryForBox(*formattingRootContainer.firstInFlowChild(), EscapeReason::NeedsGeometryFromEstablishedFormattingContext);
145             auto& lastBoxGeometry = formattingContext.geometryForBox(*formattingRootContainer.lastInFlowChild(), EscapeReason::NeedsGeometryFromEstablishedFormattingContext);
146             top = firstBoxGeometry.rectWithMargin().top();
147             bottom = lastBoxGeometry.rectWithMargin().bottom();
148         }
149     } else
150         ASSERT_NOT_REACHED();
151
152     auto* formattingContextRoot = &formattingRootContainer;
153     // TODO: The document renderer is not a formatting context root by default at all. Need to find out what it is.
154     if (!layoutBox.establishesFormattingContext()) {
155         ASSERT(layoutBox.isDocumentBox());
156         formattingContextRoot = &layoutBox.formattingContextRoot();
157     }
158
159     auto& floatingState = layoutState.establishedFormattingState(*formattingContextRoot).floatingState();
160     auto floatBottom = floatingState.bottom(*formattingContextRoot);
161     if (floatBottom) {
162         bottom = std::max<LayoutUnit>(*floatBottom, bottom);
163         auto floatTop = floatingState.top(*formattingContextRoot);
164         ASSERT(floatTop);
165         top = std::min<LayoutUnit>(*floatTop, top);
166     }
167
168     auto computedHeight = bottom - top;
169     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height] -> content height for formatting context root -> height(" << computedHeight << "px) layoutBox("<< &layoutBox << ")");
170     return computedHeight;
171 }
172
173 Optional<LayoutUnit> FormattingContext::Geometry::computedValueIfNotAuto(const Length& geometryProperty, LayoutUnit containingBlockWidth) const
174 {
175     if (geometryProperty.isUndefined())
176         return WTF::nullopt;
177
178     if (geometryProperty.isAuto())
179         return WTF::nullopt;
180
181     return valueForLength(geometryProperty, containingBlockWidth);
182 }
183
184 Optional<LayoutUnit> FormattingContext::Geometry::fixedValue(const Length& geometryProperty) const
185 {
186     if (!geometryProperty.isFixed())
187         return WTF::nullopt;
188     return LayoutUnit(geometryProperty.value());
189 }
190
191 // https://www.w3.org/TR/CSS22/visudet.html#min-max-heights
192 // Specifies a percentage for determining the used value. The percentage is calculated with respect to the height of the generated box's containing block.
193 // 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,
194 // the percentage value is treated as '0' (for 'min-height') or 'none' (for 'max-height').
195 Optional<LayoutUnit> FormattingContext::Geometry::computedMaxHeight(const Box& layoutBox, Optional<LayoutUnit> containingBlockHeight) const
196 {
197     return computedHeightValue(layoutBox, HeightType::Max, containingBlockHeight);
198 }
199
200 Optional<LayoutUnit> FormattingContext::Geometry::computedMinHeight(const Box& layoutBox, Optional<LayoutUnit> containingBlockHeight) const
201 {
202     if (auto minHeightValue = computedHeightValue(layoutBox, HeightType::Min, containingBlockHeight))
203         return minHeightValue;
204
205     return { LayoutUnit { } };
206 }
207
208 Optional<LayoutUnit> FormattingContext::Geometry::computedMinWidth(const Box& layoutBox, LayoutUnit containingBlockWidth) const
209 {
210     return computedValueIfNotAuto(layoutBox.style().logicalMinWidth(), containingBlockWidth);
211 }
212
213 Optional<LayoutUnit> FormattingContext::Geometry::computedMaxWidth(const Box& layoutBox, LayoutUnit containingBlockWidth) const
214 {
215     return computedValueIfNotAuto(layoutBox.style().logicalMaxWidth(), containingBlockWidth);
216 }
217
218 LayoutUnit FormattingContext::Geometry::staticVerticalPositionForOutOfFlowPositioned(const Box& layoutBox, const VerticalConstraints& verticalConstraints) const
219 {
220     ASSERT(layoutBox.isOutOfFlowPositioned());
221
222     // 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
223     // 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
224     // 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
225     // '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
226     // computed value for 'display'.) The value is negative if the hypothetical box is above the containing block.
227
228     // Start with this box's border box offset from the parent's border box.
229     auto& formattingContext = this->formattingContext();
230     LayoutUnit top;
231     if (layoutBox.previousInFlowSibling() && layoutBox.previousInFlowSibling()->isBlockLevelBox()) {
232         // Add sibling offset
233         auto& previousInFlowBoxGeometry = formattingContext.geometryForBox(*layoutBox.previousInFlowSibling(), EscapeReason::OutOfFlowBoxNeedsInFlowGeometry);
234         top += previousInFlowBoxGeometry.bottom() + previousInFlowBoxGeometry.nonCollapsedMarginAfter();
235     } else {
236         ASSERT(layoutBox.parent());
237         top = formattingContext.geometryForBox(*layoutBox.parent(), EscapeReason::OutOfFlowBoxNeedsInFlowGeometry).contentBoxTop();
238     }
239
240     // Resolve top all the way up to the containing block.
241     auto& containingBlock = layoutBox.containingBlock();
242     // Start with the parent since we pretend that this box is normal flow.
243     for (auto* ancestor = layoutBox.parent(); ancestor != &containingBlock; ancestor = &ancestor->containingBlock()) {
244         auto& boxGeometry = formattingContext.geometryForBox(*ancestor, EscapeReason::OutOfFlowBoxNeedsInFlowGeometry);
245         // Display::Box::top is the border box top position in its containing block's coordinate system.
246         top += boxGeometry.top();
247         ASSERT(!ancestor->isPositioned() || layoutBox.isFixedPositioned());
248     }
249     // Move the static position relative to the padding box. This is very specific to abolutely positioned boxes.
250     return top - verticalConstraints.logicalTop;
251 }
252
253 LayoutUnit FormattingContext::Geometry::staticHorizontalPositionForOutOfFlowPositioned(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints) const
254 {
255     ASSERT(layoutBox.isOutOfFlowPositioned());
256     // See staticVerticalPositionForOutOfFlowPositioned for the definition of the static position.
257
258     // Start with this box's border box offset from the parent's border box.
259     auto& formattingContext = this->formattingContext();
260     ASSERT(layoutBox.parent());
261     auto left = formattingContext.geometryForBox(*layoutBox.parent(), EscapeReason::OutOfFlowBoxNeedsInFlowGeometry).contentBoxLeft();
262
263     // Resolve left all the way up to the containing block.
264     auto& containingBlock = layoutBox.containingBlock();
265     // Start with the parent since we pretend that this box is normal flow.
266     for (auto* ancestor = layoutBox.parent(); ancestor != &containingBlock; ancestor = &ancestor->containingBlock()) {
267         auto& boxGeometry = formattingContext.geometryForBox(*ancestor, EscapeReason::OutOfFlowBoxNeedsInFlowGeometry);
268         // Display::Box::left is the border box left position in its containing block's coordinate system.
269         left += boxGeometry.left();
270         ASSERT(!ancestor->isPositioned() || layoutBox.isFixedPositioned());
271     }
272     // Move the static position relative to the padding box. This is very specific to abolutely positioned boxes.
273     return left - horizontalConstraints.logicalLeft;
274 }
275
276 LayoutUnit FormattingContext::Geometry::shrinkToFitWidth(const Box& formattingRoot, LayoutUnit availableWidth)
277 {
278     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width] -> shrink to fit -> unsupported -> width(" << LayoutUnit { } << "px) layoutBox: " << &formattingRoot << ")");
279     ASSERT(formattingRoot.establishesFormattingContext());
280
281     // Calculation of the shrink-to-fit width is similar to calculating the width of a table cell using the automatic table layout algorithm.
282     // Roughly: calculate the preferred width by formatting the content without breaking lines other than where explicit line breaks occur,
283     // and also calculate the preferred minimum width, e.g., by trying all possible line breaks. CSS 2.2 does not define the exact algorithm.
284     // 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',
285     // 'padding-left', 'padding-right', 'border-right-width', 'margin-right', and the widths of any relevant scroll bars.
286
287     // Then the shrink-to-fit width is: min(max(preferred minimum width, available width), preferred width).
288     auto intrinsicWidthConstraints = IntrinsicWidthConstraints { };
289     if (is<ContainerBox>(formattingRoot) && downcast<ContainerBox>(formattingRoot).hasInFlowOrFloatingChild()) {
290         auto& root = downcast<ContainerBox>(formattingRoot);
291         auto& formattingStateForRoot = layoutState().ensureFormattingState(root);
292         auto precomputedIntrinsicWidthConstraints = formattingStateForRoot.intrinsicWidthConstraints();
293         if (!precomputedIntrinsicWidthConstraints)
294             intrinsicWidthConstraints = LayoutContext::createFormattingContext(root, layoutState())->computedIntrinsicWidthConstraints();
295         else
296             intrinsicWidthConstraints = *precomputedIntrinsicWidthConstraints;
297     }
298     return std::min(std::max(intrinsicWidthConstraints.minimum, availableWidth), intrinsicWidthConstraints.maximum);
299 }
300
301 VerticalGeometry FormattingContext::Geometry::outOfFlowNonReplacedVerticalGeometry(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints, const VerticalConstraints& verticalConstraints, const OverrideVerticalValues& overrideVerticalValues) const
302 {
303     ASSERT(layoutBox.isOutOfFlowPositioned() && !layoutBox.isReplacedBox());
304     ASSERT(verticalConstraints.logicalHeight);
305
306     // 10.6.4 Absolutely positioned, non-replaced elements
307     //
308     // For absolutely positioned elements, the used values of the vertical dimensions must satisfy this constraint:
309     // 'top' + 'margin-top' + 'border-top-width' + 'padding-top' + 'height' + 'padding-bottom' + 'border-bottom-width' + 'margin-bottom' + 'bottom'
310     // = height of containing block
311
312     // If all three of 'top', 'height', and 'bottom' are auto, set 'top' to the static position and apply rule number three below.
313
314     // If none of the three are 'auto': If both 'margin-top' and 'margin-bottom' are 'auto', solve the equation under the extra
315     // constraint that the two margins get equal values. If one of 'margin-top' or 'margin-bottom' is 'auto', solve the equation for that value.
316     // If the values are over-constrained, ignore the value for 'bottom' and solve for that value.
317
318     // Otherwise, pick the one of the following six rules that applies.
319
320     // 1. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then the height is based on the content per 10.6.7,
321     //     set 'auto' values for 'margin-top' and 'margin-bottom' to 0, and solve for 'top'
322     // 2. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then set 'top' to the static position, set 'auto' values for
323     //    'margin-top' and 'margin-bottom' to 0, and solve for 'bottom'
324     // 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'
325     //     values for 'margin-top' and 'margin-bottom' to 0, and solve for 'bottom'
326     // 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'
327     // 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'
328     // 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'
329
330     auto& formattingContext = this->formattingContext();
331     auto& style = layoutBox.style();
332     auto& boxGeometry = formattingContext.geometryForBox(layoutBox);
333     auto containingBlockHeight = *verticalConstraints.logicalHeight;
334     auto containingBlockWidth = horizontalConstraints.logicalWidth;
335
336     auto top = computedValueIfNotAuto(style.logicalTop(), containingBlockWidth);
337     auto bottom = computedValueIfNotAuto(style.logicalBottom(), containingBlockWidth);
338     auto height = overrideVerticalValues.height ? overrideVerticalValues.height.value() : computedContentHeight(layoutBox, containingBlockHeight);
339     auto computedVerticalMargin = Geometry::computedVerticalMargin(layoutBox, horizontalConstraints);
340     UsedVerticalMargin::NonCollapsedValues usedVerticalMargin; 
341     auto paddingTop = boxGeometry.paddingTop().valueOr(0);
342     auto paddingBottom = boxGeometry.paddingBottom().valueOr(0);
343     auto borderTop = boxGeometry.borderTop();
344     auto borderBottom = boxGeometry.borderBottom();
345
346     if (!top && !height && !bottom)
347         top = staticVerticalPositionForOutOfFlowPositioned(layoutBox, verticalConstraints);
348
349     if (top && height && bottom) {
350         if (!computedVerticalMargin.before && !computedVerticalMargin.after) {
351             auto marginBeforeAndAfter = containingBlockHeight - (*top + borderTop + paddingTop + *height + paddingBottom + borderBottom + *bottom);
352             usedVerticalMargin = { marginBeforeAndAfter / 2, marginBeforeAndAfter / 2 };
353         } else if (!computedVerticalMargin.before) {
354             usedVerticalMargin.after = *computedVerticalMargin.after;
355             usedVerticalMargin.before = containingBlockHeight - (*top + borderTop + paddingTop + *height + paddingBottom + borderBottom + usedVerticalMargin.after + *bottom);
356         } else if (!computedVerticalMargin.after) {
357             usedVerticalMargin.before = *computedVerticalMargin.before;
358             usedVerticalMargin.after = containingBlockHeight - (*top + usedVerticalMargin.before + borderTop + paddingTop + *height + paddingBottom + borderBottom + *bottom);
359         } else
360             usedVerticalMargin = { *computedVerticalMargin.before, *computedVerticalMargin.after };
361         // Over-constrained?
362         auto boxHeight = *top + usedVerticalMargin.before + borderTop + paddingTop + *height + paddingBottom + borderBottom + usedVerticalMargin.after + *bottom;
363         if (boxHeight != containingBlockHeight)
364             bottom = containingBlockHeight - (*top + usedVerticalMargin.before + borderTop + paddingTop + *height + paddingBottom + borderBottom + usedVerticalMargin.after);
365     }
366
367     if (!top && !height && bottom) {
368         // #1
369         height = contentHeightForFormattingContextRoot(layoutBox);
370         usedVerticalMargin = { computedVerticalMargin.before.valueOr(0), computedVerticalMargin.after.valueOr(0) };
371         top = containingBlockHeight - (usedVerticalMargin.before + borderTop + paddingTop + *height + paddingBottom + borderBottom + usedVerticalMargin.after + *bottom); 
372     }
373
374     if (!top && !bottom && height) {
375         // #2
376         top = staticVerticalPositionForOutOfFlowPositioned(layoutBox, verticalConstraints);
377         usedVerticalMargin = { computedVerticalMargin.before.valueOr(0), computedVerticalMargin.after.valueOr(0) };
378         bottom = containingBlockHeight - (*top + usedVerticalMargin.before + borderTop + paddingTop + *height + paddingBottom + borderBottom + usedVerticalMargin.after);
379     }
380
381     if (!height && !bottom && top) {
382         // #3
383         height = contentHeightForFormattingContextRoot(layoutBox);
384         usedVerticalMargin = { computedVerticalMargin.before.valueOr(0), computedVerticalMargin.after.valueOr(0) };
385         bottom = containingBlockHeight - (*top + usedVerticalMargin.before + borderTop + paddingTop + *height + paddingBottom + borderBottom + usedVerticalMargin.after);
386     }
387
388     if (!top && height && bottom) {
389         // #4
390         usedVerticalMargin = { computedVerticalMargin.before.valueOr(0), computedVerticalMargin.after.valueOr(0) };
391         top = containingBlockHeight - (usedVerticalMargin.before + borderTop + paddingTop + *height + paddingBottom + borderBottom + usedVerticalMargin.after + *bottom);
392     }
393
394     if (!height && top && bottom) {
395         // #5
396         usedVerticalMargin = { computedVerticalMargin.before.valueOr(0), computedVerticalMargin.after.valueOr(0) };
397         height = containingBlockHeight - (*top + usedVerticalMargin.before + borderTop + paddingTop + paddingBottom + borderBottom + usedVerticalMargin.after + *bottom);
398     }
399
400     if (!bottom && top && height) {
401         // #6
402         usedVerticalMargin = { computedVerticalMargin.before.valueOr(0), computedVerticalMargin.after.valueOr(0) };
403         bottom = containingBlockHeight - (*top + usedVerticalMargin.before + borderTop + paddingTop + *height + paddingBottom + borderBottom + usedVerticalMargin.after);
404     }
405
406     ASSERT(top);
407     ASSERT(bottom);
408     ASSERT(height);
409
410     // For out-of-flow elements the containing block is formed by the padding edge of the ancestor.
411     // At this point the positioned value is in the coordinate system of the padding box. Let's convert it to border box coordinate system.
412     auto containingBlockPaddingVerticalEdge = verticalConstraints.logicalTop;
413     *top += containingBlockPaddingVerticalEdge;
414     *bottom += containingBlockPaddingVerticalEdge;
415
416     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position][Height][Margin] -> out-of-flow non-replaced -> top(" << *top << "px) bottom("  << *bottom << "px) height(" << *height << "px) margin(" << usedVerticalMargin.before << "px, "  << usedVerticalMargin.after << "px) layoutBox(" << &layoutBox << ")");
417     return { *top, *bottom, { *height, usedVerticalMargin } };
418 }
419
420 HorizontalGeometry FormattingContext::Geometry::outOfFlowNonReplacedHorizontalGeometry(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints, const OverrideHorizontalValues& overrideHorizontalValues)
421 {
422     ASSERT(layoutBox.isOutOfFlowPositioned() && !layoutBox.isReplacedBox());
423     
424     // 10.3.7 Absolutely positioned, non-replaced elements
425     //
426     // 'left' + 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' + 'right'
427     // = width of containing block
428
429     // If all three of 'left', 'width', and 'right' are 'auto': First set any 'auto' values for 'margin-left' and 'margin-right' to 0.
430     // Then, if the 'direction' property of the element establishing the static-position containing block is 'ltr' set 'left' to the static
431     // position and apply rule number three below; otherwise, set 'right' to the static position and apply rule number one below.
432     //
433     // 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,
434     // 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
435     // solve for 'margin-right' ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto', solve the equation for that value.
436     // If the values are over-constrained, ignore the value for 'left' (in case the 'direction' property of the containing block is 'rtl') or 'right'
437     // (in case 'direction' is 'ltr') and solve for that value.
438     //
439     // Otherwise, set 'auto' values for 'margin-left' and 'margin-right' to 0, and pick the one of the following six rules that applies.
440     //
441     // 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the width is shrink-to-fit. Then solve for 'left'
442     // 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if the 'direction' property of the element establishing the static-position 
443     //    containing block is 'ltr' set 'left' to the static position, otherwise set 'right' to the static position.
444     //    Then solve for 'left' (if 'direction is 'rtl') or 'right' (if 'direction' is 'ltr').
445     // 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the width is shrink-to-fit . Then solve for 'right'
446     // 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve for 'left'
447     // 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve for 'width'
448     // 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve for 'right'
449
450     auto& formattingContext = this->formattingContext();
451     auto& style = layoutBox.style();
452     auto& boxGeometry = formattingContext.geometryForBox(layoutBox);
453     auto containingBlockWidth = horizontalConstraints.logicalWidth;
454     auto isLeftToRightDirection = layoutBox.containingBlock().style().isLeftToRightDirection();
455     
456     auto left = computedValueIfNotAuto(style.logicalLeft(), containingBlockWidth);
457     auto right = computedValueIfNotAuto(style.logicalRight(), containingBlockWidth);
458     auto width = overrideHorizontalValues.width ? overrideHorizontalValues.width : computedContentWidth(layoutBox, containingBlockWidth);
459     auto computedHorizontalMargin = Geometry::computedHorizontalMargin(layoutBox, horizontalConstraints);
460     UsedHorizontalMargin usedHorizontalMargin;
461     auto paddingLeft = boxGeometry.paddingLeft().valueOr(0);
462     auto paddingRight = boxGeometry.paddingRight().valueOr(0);
463     auto borderLeft = boxGeometry.borderLeft();
464     auto borderRight = boxGeometry.borderRight();
465     if (!left && !width && !right) {
466         // If all three of 'left', 'width', and 'right' are 'auto': First set any 'auto' values for 'margin-left' and 'margin-right' to 0.
467         // Then, if the 'direction' property of the element establishing the static-position containing block is 'ltr' set 'left' to the static
468         // position and apply rule number three below; otherwise, set 'right' to the static position and apply rule number one below.
469         usedHorizontalMargin = { computedHorizontalMargin.start.valueOr(0), computedHorizontalMargin.end.valueOr(0) };
470
471         auto staticHorizontalPosition = staticHorizontalPositionForOutOfFlowPositioned(layoutBox, horizontalConstraints);
472         if (isLeftToRightDirection)
473             left = staticHorizontalPosition;
474         else
475             right = staticHorizontalPosition;
476     } else if (left && width && right) {
477         // 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,
478         // 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
479         // solve for 'margin-right' ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto', solve the equation for that value.
480         // If the values are over-constrained, ignore the value for 'left' (in case the 'direction' property of the containing block is 'rtl') or 'right'
481         // (in case 'direction' is 'ltr') and solve for that value.
482         if (!computedHorizontalMargin.start && !computedHorizontalMargin.end) {
483             auto marginStartAndEnd = containingBlockWidth - (*left + borderLeft + paddingLeft + *width + paddingRight + borderRight + *right);
484             if (marginStartAndEnd >= 0)
485                 usedHorizontalMargin = { marginStartAndEnd / 2, marginStartAndEnd / 2 };
486             else {
487                 if (isLeftToRightDirection) {
488                     usedHorizontalMargin.start = 0_lu;
489                     usedHorizontalMargin.end = containingBlockWidth - (*left + usedHorizontalMargin.start + borderLeft + paddingLeft + *width + paddingRight + borderRight + *right);
490                 } else {
491                     usedHorizontalMargin.end = 0_lu;
492                     usedHorizontalMargin.start = containingBlockWidth - (*left + borderLeft + paddingLeft + *width + paddingRight + borderRight + usedHorizontalMargin.end + *right);
493                 }
494             }
495         } else if (!computedHorizontalMargin.start) {
496             usedHorizontalMargin.end = *computedHorizontalMargin.end;
497             usedHorizontalMargin.start = containingBlockWidth - (*left + borderLeft + paddingLeft + *width + paddingRight + borderRight + usedHorizontalMargin.end + *right);
498         } else if (!computedHorizontalMargin.end) {
499             usedHorizontalMargin.start = *computedHorizontalMargin.start;
500             usedHorizontalMargin.end = containingBlockWidth - (*left + usedHorizontalMargin.start + borderLeft + paddingLeft + *width + paddingRight + borderRight + *right);
501         } else {
502             usedHorizontalMargin = { *computedHorizontalMargin.start, *computedHorizontalMargin.end };
503             // Overconstrained? Ignore right (left).
504             if (isLeftToRightDirection)
505                 right = containingBlockWidth - (usedHorizontalMargin.start + *left + borderLeft + paddingLeft + *width + paddingRight + borderRight + usedHorizontalMargin.end);
506             else
507                 left = containingBlockWidth - (usedHorizontalMargin.start + borderLeft + paddingLeft + *width + paddingRight + borderRight + usedHorizontalMargin.end + *right);
508         }
509     } else {
510         // Otherwise, set 'auto' values for 'margin-left' and 'margin-right' to 0, and pick the one of the following six rules that applies.
511         usedHorizontalMargin = { computedHorizontalMargin.start.valueOr(0), computedHorizontalMargin.end.valueOr(0) };
512     }
513
514     if (!left && !width && right) {
515         // #1
516         // Calculate the available width by solving for 'width' after setting 'left' (in case 1) to 0
517         left = LayoutUnit { 0 };
518         auto availableWidth = containingBlockWidth - (*left + usedHorizontalMargin.start + borderLeft + paddingLeft + paddingRight + borderRight + usedHorizontalMargin.end + *right);
519         width = shrinkToFitWidth(layoutBox, availableWidth);
520         left = containingBlockWidth - (usedHorizontalMargin.start + borderLeft + paddingLeft + *width + paddingRight  + borderRight + usedHorizontalMargin.end + *right);
521     } else if (!left && !right && width) {
522         // #2
523         auto staticHorizontalPosition = staticHorizontalPositionForOutOfFlowPositioned(layoutBox, horizontalConstraints);
524         if (isLeftToRightDirection) {
525             left = staticHorizontalPosition;
526             right = containingBlockWidth - (*left + usedHorizontalMargin.start + borderLeft + paddingLeft + *width + paddingRight + borderRight + usedHorizontalMargin.end);
527         } else {
528             right = staticHorizontalPosition;
529             left = containingBlockWidth - (usedHorizontalMargin.start + borderLeft + paddingLeft + *width + paddingRight + borderRight + usedHorizontalMargin.end + *right);
530         }
531     } else if (!width && !right && left) {
532         // #3
533         // Calculate the available width by solving for 'width' after setting 'right' (in case 3) to 0
534         right = LayoutUnit { 0 };
535         auto availableWidth = containingBlockWidth - (*left + usedHorizontalMargin.start + borderLeft + paddingLeft + paddingRight + borderRight + usedHorizontalMargin.end + *right);
536         width = shrinkToFitWidth(layoutBox, availableWidth);
537         right = containingBlockWidth - (*left + usedHorizontalMargin.start + borderLeft + paddingLeft + *width + paddingRight + borderRight + usedHorizontalMargin.end);
538     } else if (!left && width && right) {
539         // #4
540         left = containingBlockWidth - (usedHorizontalMargin.start + borderLeft + paddingLeft + *width + paddingRight + borderRight + usedHorizontalMargin.end + *right);
541     } else if (!width && left && right) {
542         // #5
543         width = containingBlockWidth - (*left + usedHorizontalMargin.start + borderLeft + paddingLeft + paddingRight + borderRight + usedHorizontalMargin.end + *right);
544     } else if (!right && left && width) {
545         // #6
546         right = containingBlockWidth - (*left + usedHorizontalMargin.start + borderLeft + paddingLeft + *width + paddingRight + borderRight + usedHorizontalMargin.end);
547     }
548
549     ASSERT(left);
550     ASSERT(right);
551     ASSERT(width);
552
553     // For out-of-flow elements the containing block is formed by the padding edge of the ancestor.
554     // At this point the positioned value is in the coordinate system of the padding box. Let's convert it to border box coordinate system.
555     auto containingBlockPaddingVerticalEdge = horizontalConstraints.logicalLeft;
556     *left += containingBlockPaddingVerticalEdge;
557     *right += containingBlockPaddingVerticalEdge;
558
559     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position][Width][Margin] -> out-of-flow non-replaced -> left(" << *left << "px) right("  << *right << "px) width(" << *width << "px) margin(" << usedHorizontalMargin.start << "px, "  << usedHorizontalMargin.end << "px) layoutBox(" << &layoutBox << ")");
560     return { *left, *right, { *width, usedHorizontalMargin, computedHorizontalMargin } };
561 }
562
563 VerticalGeometry FormattingContext::Geometry::outOfFlowReplacedVerticalGeometry(const ReplacedBox& replacedBox, const HorizontalConstraints& horizontalConstraints, const VerticalConstraints& verticalConstraints, const OverrideVerticalValues& overrideVerticalValues) const
564 {
565     ASSERT(replacedBox.isOutOfFlowPositioned());
566     ASSERT(verticalConstraints.logicalHeight);
567
568     // 10.6.5 Absolutely positioned, replaced elements
569     //
570     // The used value of 'height' is determined as for inline replaced elements.
571     // If 'margin-top' or 'margin-bottom' is specified as 'auto' its used value is determined by the rules below.
572     // 1. If both 'top' and 'bottom' have the value 'auto', replace 'top' with the element's static position.
573     // 2. If 'bottom' is 'auto', replace any 'auto' on 'margin-top' or 'margin-bottom' with '0'.
574     // 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.
575     // 4. If at this point there is only one 'auto' left, solve the equation for that value.
576     // 5. If at this point the values are over-constrained, ignore the value for 'bottom' and solve for that value.
577
578     auto& formattingContext = this->formattingContext();
579     auto& style = replacedBox.style();
580     auto& boxGeometry = formattingContext.geometryForBox(replacedBox);
581     auto containingBlockHeight = *verticalConstraints.logicalHeight;
582     auto containingBlockWidth = horizontalConstraints.logicalWidth;
583
584     auto top = computedValueIfNotAuto(style.logicalTop(), containingBlockWidth);
585     auto bottom = computedValueIfNotAuto(style.logicalBottom(), containingBlockWidth);
586     auto height = inlineReplacedHeightAndMargin(replacedBox, horizontalConstraints, verticalConstraints, overrideVerticalValues).contentHeight;
587     auto computedVerticalMargin = Geometry::computedVerticalMargin(replacedBox, horizontalConstraints);
588     Optional<LayoutUnit> usedMarginBefore = computedVerticalMargin.before;
589     Optional<LayoutUnit> usedMarginAfter = computedVerticalMargin.after;
590     auto paddingTop = boxGeometry.paddingTop().valueOr(0);
591     auto paddingBottom = boxGeometry.paddingBottom().valueOr(0);
592     auto borderTop = boxGeometry.borderTop();
593     auto borderBottom = boxGeometry.borderBottom();
594
595     if (!top && !bottom) {
596         // #1
597         top = staticVerticalPositionForOutOfFlowPositioned(replacedBox, verticalConstraints);
598     }
599
600     if (!bottom) {
601         // #2
602         usedMarginBefore = computedVerticalMargin.before.valueOr(0);
603         usedMarginAfter = usedMarginBefore;
604     }
605
606     if (!usedMarginBefore && !usedMarginAfter) {
607         // #3
608         auto marginBeforeAndAfter = containingBlockHeight - (*top + borderTop + paddingTop + height + paddingBottom + borderBottom + *bottom);
609         usedMarginBefore = marginBeforeAndAfter / 2;
610         usedMarginAfter = usedMarginBefore;
611     }
612
613     // #4
614     if (!top)
615         top = containingBlockHeight - (*usedMarginBefore + borderTop + paddingTop + height + paddingBottom + borderBottom + *usedMarginAfter + *bottom);
616
617     if (!bottom)
618         bottom = containingBlockHeight - (*top + *usedMarginBefore + borderTop + paddingTop + height + paddingBottom + borderBottom + *usedMarginAfter);
619
620     if (!usedMarginBefore)
621         usedMarginBefore = containingBlockHeight - (*top + borderTop + paddingTop + height + paddingBottom + borderBottom + *usedMarginAfter + *bottom);
622
623     if (!usedMarginAfter)
624         usedMarginAfter = containingBlockHeight - (*top + *usedMarginBefore + borderTop + paddingTop + height + paddingBottom + borderBottom + *bottom);
625
626     // #5
627     auto boxHeight = *top + *usedMarginBefore + borderTop + paddingTop + height + paddingBottom + borderBottom + *usedMarginAfter + *bottom;
628     if (boxHeight > containingBlockHeight)
629         bottom = containingBlockHeight - (*top + *usedMarginBefore + borderTop + paddingTop + height + paddingBottom + borderBottom + *usedMarginAfter); 
630
631     // For out-of-flow elements the containing block is formed by the padding edge of the ancestor.
632     // At this point the positioned value is in the coordinate system of the padding box. Let's convert it to border box coordinate system.
633     auto containingBlockPaddingVerticalEdge = verticalConstraints.logicalTop;
634     *top += containingBlockPaddingVerticalEdge;
635     *bottom += containingBlockPaddingVerticalEdge;
636
637     ASSERT(top);
638     ASSERT(bottom);
639     ASSERT(usedMarginBefore);
640     ASSERT(usedMarginAfter);
641     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position][Height][Margin] -> out-of-flow replaced -> top(" << *top << "px) bottom("  << *bottom << "px) height(" << height << "px) margin(" << *usedMarginBefore << "px, "  << *usedMarginAfter << "px) layoutBox(" << &replacedBox << ")");
642     return { *top, *bottom, { height, { *usedMarginBefore, *usedMarginAfter } } };
643 }
644
645 HorizontalGeometry FormattingContext::Geometry::outOfFlowReplacedHorizontalGeometry(const ReplacedBox& replacedBox, const HorizontalConstraints& horizontalConstraints, const VerticalConstraints& verticalConstraints, const OverrideHorizontalValues& overrideHorizontalValues) const
646 {
647     ASSERT(replacedBox.isOutOfFlowPositioned());
648
649     // 10.3.8 Absolutely positioned, replaced elements
650     // 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:
651     //
652     // 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.
653     // 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',
654     //   set 'left' to the static position; else if 'direction' is 'rtl', set 'right' to the static position.
655     // 2. If 'left' or 'right' are 'auto', replace any 'auto' on 'margin-left' or 'margin-right' with '0'.
656     // 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,
657     //   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
658     //   solve for 'margin-right' ('margin-left').
659     // 4. If at this point there is an 'auto' left, solve the equation for that value.
660     // 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
661     //   'right' (in case 'direction' is 'ltr') and solve for that value.
662
663     auto& formattingContext = this->formattingContext();
664     auto& style = replacedBox.style();
665     auto& boxGeometry = formattingContext.geometryForBox(replacedBox);
666     auto containingBlockWidth = horizontalConstraints.logicalWidth;
667     auto isLeftToRightDirection = replacedBox.containingBlock().style().isLeftToRightDirection();
668
669     auto left = computedValueIfNotAuto(style.logicalLeft(), containingBlockWidth);
670     auto right = computedValueIfNotAuto(style.logicalRight(), containingBlockWidth);
671     auto computedHorizontalMargin = Geometry::computedHorizontalMargin(replacedBox, horizontalConstraints);
672     Optional<LayoutUnit> usedMarginStart = computedHorizontalMargin.start;
673     Optional<LayoutUnit> usedMarginEnd = computedHorizontalMargin.end;
674     auto width = inlineReplacedWidthAndMargin(replacedBox, horizontalConstraints, verticalConstraints, overrideHorizontalValues).contentWidth;
675     auto paddingLeft = boxGeometry.paddingLeft().valueOr(0);
676     auto paddingRight = boxGeometry.paddingRight().valueOr(0);
677     auto borderLeft = boxGeometry.borderLeft();
678     auto borderRight = boxGeometry.borderRight();
679
680     if (!left && !right) {
681         // #1
682         auto staticHorizontalPosition = staticHorizontalPositionForOutOfFlowPositioned(replacedBox, horizontalConstraints);
683         if (isLeftToRightDirection)
684             left = staticHorizontalPosition;
685         else
686             right = staticHorizontalPosition;
687     }
688
689     if (!left || !right) {
690         // #2
691         usedMarginStart = computedHorizontalMargin.start.valueOr(0);
692         usedMarginEnd = computedHorizontalMargin.end.valueOr(0);
693     }
694
695     if (!usedMarginStart && !usedMarginEnd) {
696         // #3
697         auto marginStartAndEnd = containingBlockWidth - (*left + borderLeft + paddingLeft + width + paddingRight + borderRight + *right);
698         if (marginStartAndEnd >= 0) {
699             usedMarginStart = marginStartAndEnd / 2;
700             usedMarginEnd = usedMarginStart;
701         } else {
702             if (isLeftToRightDirection) {
703                 usedMarginStart = 0_lu;
704                 usedMarginEnd = containingBlockWidth - (*left + *usedMarginStart + borderLeft + paddingLeft + width + paddingRight + borderRight + *right);
705             } else {
706                 usedMarginEnd = 0_lu;
707                 usedMarginStart = containingBlockWidth - (*left + borderLeft + paddingLeft + width + paddingRight + borderRight + *usedMarginEnd + *right);
708             }
709         }
710     }
711
712     // #4
713     if (!left)
714         left = containingBlockWidth - (*usedMarginStart + borderLeft + paddingLeft + width + paddingRight + borderRight + *usedMarginEnd + *right);
715
716     if (!right)
717         right = containingBlockWidth - (*left + *usedMarginStart + borderLeft + paddingLeft + width + paddingRight + borderRight + *usedMarginEnd);
718
719     if (!usedMarginStart)
720         usedMarginStart = containingBlockWidth - (*left + borderLeft + paddingLeft + width + paddingRight + borderRight + *usedMarginEnd + *right);
721
722     if (!usedMarginEnd)
723         usedMarginEnd = containingBlockWidth - (*left + *usedMarginStart + borderLeft + paddingLeft + width + paddingRight + borderRight + *right);
724
725     auto boxWidth = (*left + *usedMarginStart + borderLeft + paddingLeft + width + paddingRight + borderRight + *usedMarginEnd + *right);
726     if (boxWidth > containingBlockWidth) {
727         // #5 Over-constrained?
728         if (isLeftToRightDirection)
729             right = containingBlockWidth - (*left + *usedMarginStart + borderLeft + paddingLeft + width + paddingRight + borderRight + *usedMarginEnd);
730         else
731             left = containingBlockWidth - (*usedMarginStart + borderLeft + paddingLeft + width + paddingRight + borderRight + *usedMarginEnd + *right);
732     }
733
734     ASSERT(left);
735     ASSERT(right);
736     ASSERT(usedMarginStart);
737     ASSERT(usedMarginEnd);
738
739     // For out-of-flow elements the containing block is formed by the padding edge of the ancestor.
740     // At this point the positioned value is in the coordinate system of the padding box. Let's convert it to border box coordinate system.
741     auto containingBlockPaddingVerticalEdge = horizontalConstraints.logicalLeft;
742     *left += containingBlockPaddingVerticalEdge;
743     *right += containingBlockPaddingVerticalEdge;
744
745     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position][Width][Margin] -> out-of-flow replaced -> left(" << *left << "px) right("  << *right << "px) width(" << width << "px) margin(" << *usedMarginStart << "px, "  << *usedMarginEnd << "px) layoutBox(" << &replacedBox << ")");
746     return { *left, *right, { width, { *usedMarginStart, *usedMarginEnd }, computedHorizontalMargin } };
747 }
748
749 ContentHeightAndMargin FormattingContext::Geometry::complicatedCases(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints, const OverrideVerticalValues& overrideVerticalValues) const
750 {
751     ASSERT(!layoutBox.isReplacedBox());
752     // TODO: Use complicated-case for document renderer for now (see BlockFormattingContext::Geometry::inFlowHeightAndMargin).
753     ASSERT((layoutBox.isBlockLevelBox() && layoutBox.isInFlow() && !layoutBox.isOverflowVisible()) || layoutBox.isInlineBlockBox() || layoutBox.isFloatingPositioned() || layoutBox.isDocumentBox() || layoutBox.isTableBox());
754
755     // 10.6.6 Complicated cases
756     //
757     // 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).
758     // 'Inline-block', non-replaced elements.
759     // Floating, non-replaced elements.
760     //
761     // 1. If 'margin-top', or 'margin-bottom' are 'auto', their used value is 0.
762     // 2. If 'height' is 'auto', the height depends on the element's descendants per 10.6.7.
763
764     auto height = overrideVerticalValues.height ? overrideVerticalValues.height.value() : computedContentHeight(layoutBox);
765     auto computedVerticalMargin = Geometry::computedVerticalMargin(layoutBox, horizontalConstraints);
766     // #1
767     auto usedVerticalMargin = UsedVerticalMargin::NonCollapsedValues { computedVerticalMargin.before.valueOr(0), computedVerticalMargin.after.valueOr(0) }; 
768     // #2
769     if (!height) {
770         ASSERT(isHeightAuto(layoutBox));
771         height = contentHeightForFormattingContextRoot(layoutBox);
772     }
773
774     ASSERT(height);
775
776     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> floating non-replaced -> height(" << *height << "px) margin(" << usedVerticalMargin.before << "px, " << usedVerticalMargin.after << "px) -> layoutBox(" << &layoutBox << ")");
777     return ContentHeightAndMargin { *height, usedVerticalMargin };
778 }
779
780 ContentWidthAndMargin FormattingContext::Geometry::floatingNonReplacedWidthAndMargin(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints, const OverrideHorizontalValues& overrideHorizontalValues)
781 {
782     ASSERT(layoutBox.isFloatingPositioned() && !layoutBox.isReplacedBox());
783
784     // 10.3.5 Floating, non-replaced elements
785     //
786     // 1. If 'margin-left', or 'margin-right' are computed as 'auto', their used value is '0'.
787     // 2. If 'width' is computed as 'auto', the used value is the "shrink-to-fit" width.
788
789     auto computedHorizontalMargin = Geometry::computedHorizontalMargin(layoutBox, horizontalConstraints);
790
791     // #1
792     auto usedHorizontallMargin = UsedHorizontalMargin { computedHorizontalMargin.start.valueOr(0), computedHorizontalMargin.end.valueOr(0) };
793     // #2
794     auto width = overrideHorizontalValues.width ? overrideHorizontalValues.width : computedContentWidth(layoutBox, horizontalConstraints.logicalWidth);
795     if (!width)
796         width = shrinkToFitWidth(layoutBox, horizontalConstraints.logicalWidth);
797
798     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width][Margin] -> floating non-replaced -> width(" << *width << "px) margin(" << usedHorizontallMargin.start << "px, " << usedHorizontallMargin.end << "px) -> layoutBox(" << &layoutBox << ")");
799     return ContentWidthAndMargin { *width, usedHorizontallMargin, computedHorizontalMargin };
800 }
801
802 ContentHeightAndMargin FormattingContext::Geometry::floatingReplacedHeightAndMargin(const ReplacedBox& replacedBox, const HorizontalConstraints& horizontalConstraints, const OverrideVerticalValues& overrideVerticalValues) const
803 {
804     ASSERT(replacedBox.isFloatingPositioned());
805
806     // 10.6.2 Inline replaced elements, block-level replaced elements in normal flow, 'inline-block'
807     // replaced elements in normal flow and floating replaced elements
808     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> floating replaced -> redirected to inline replaced");
809     return inlineReplacedHeightAndMargin(replacedBox, horizontalConstraints, { }, overrideVerticalValues);
810 }
811
812 ContentWidthAndMargin FormattingContext::Geometry::floatingReplacedWidthAndMargin(const ReplacedBox& replacedBox, const HorizontalConstraints& horizontalConstraints, const OverrideHorizontalValues& overrideHorizontalValues) const
813 {
814     ASSERT(replacedBox.isFloatingPositioned());
815
816     // 10.3.6 Floating, replaced elements
817     //
818     // 1. If 'margin-left' or 'margin-right' are computed as 'auto', their used value is '0'.
819     // 2. The used value of 'width' is determined as for inline replaced elements.
820     auto computedHorizontalMargin = Geometry::computedHorizontalMargin(replacedBox, horizontalConstraints);
821
822     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> floating replaced -> redirected to inline replaced");
823     auto usedMargin = UsedHorizontalMargin { computedHorizontalMargin.start.valueOr(0), computedHorizontalMargin.end.valueOr(0) };
824     return inlineReplacedWidthAndMargin(replacedBox, horizontalConstraints, { }, { overrideHorizontalValues.width, usedMargin });
825 }
826
827 VerticalGeometry FormattingContext::Geometry::outOfFlowVerticalGeometry(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints, const VerticalConstraints& verticalConstraints, const OverrideVerticalValues& overrideVerticalValues) const
828 {
829     ASSERT(layoutBox.isOutOfFlowPositioned());
830
831     if (!layoutBox.isReplacedBox())
832         return outOfFlowNonReplacedVerticalGeometry(layoutBox, horizontalConstraints, verticalConstraints, overrideVerticalValues);
833     return outOfFlowReplacedVerticalGeometry(downcast<ReplacedBox>(layoutBox), horizontalConstraints, verticalConstraints, overrideVerticalValues);
834 }
835
836 HorizontalGeometry FormattingContext::Geometry::outOfFlowHorizontalGeometry(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints, const VerticalConstraints& verticalConstraints, const OverrideHorizontalValues& overrideHorizontalValues)
837 {
838     ASSERT(layoutBox.isOutOfFlowPositioned());
839
840     if (!layoutBox.isReplacedBox())
841         return outOfFlowNonReplacedHorizontalGeometry(layoutBox, horizontalConstraints, overrideHorizontalValues);
842     return outOfFlowReplacedHorizontalGeometry(downcast<ReplacedBox>(layoutBox), horizontalConstraints, verticalConstraints, overrideHorizontalValues);
843 }
844
845 ContentHeightAndMargin FormattingContext::Geometry::floatingHeightAndMargin(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints, const OverrideVerticalValues& overrideVerticalValues) const
846 {
847     ASSERT(layoutBox.isFloatingPositioned());
848
849     if (!layoutBox.isReplacedBox())
850         return complicatedCases(layoutBox, horizontalConstraints, overrideVerticalValues);
851     return floatingReplacedHeightAndMargin(downcast<ReplacedBox>(layoutBox), horizontalConstraints, overrideVerticalValues);
852 }
853
854 ContentWidthAndMargin FormattingContext::Geometry::floatingWidthAndMargin(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints, const OverrideHorizontalValues& overrideHorizontalValues)
855 {
856     ASSERT(layoutBox.isFloatingPositioned());
857
858     if (!layoutBox.isReplacedBox())
859         return floatingNonReplacedWidthAndMargin(layoutBox, horizontalConstraints, overrideHorizontalValues);
860     return floatingReplacedWidthAndMargin(downcast<ReplacedBox>(layoutBox), horizontalConstraints, overrideHorizontalValues);
861 }
862
863 ContentHeightAndMargin FormattingContext::Geometry::inlineReplacedHeightAndMargin(const ReplacedBox& replacedBox, const HorizontalConstraints& horizontalConstraints, Optional<VerticalConstraints> verticalConstraints, const OverrideVerticalValues& overrideVerticalValues) const
864 {
865     // 10.6.2 Inline replaced elements, block-level replaced elements in normal flow, 'inline-block' replaced elements in normal flow and floating replaced elements
866     //
867     // 1. If 'margin-top', or 'margin-bottom' are 'auto', their used value is 0.
868     // 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'.
869     // 3. Otherwise, if 'height' has a computed value of 'auto', and the element has an intrinsic ratio then the used value of 'height' is:
870     //    (used width) / (intrinsic ratio)
871     // 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'.
872     // 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
873     //    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.
874
875     // #1
876     auto& formattingContext = this->formattingContext();
877     auto computedVerticalMargin = Geometry::computedVerticalMargin(replacedBox, horizontalConstraints);
878     auto usedVerticalMargin = UsedVerticalMargin::NonCollapsedValues { computedVerticalMargin.before.valueOr(0), computedVerticalMargin.after.valueOr(0) };
879     auto& style = replacedBox.style();
880
881     auto height = overrideVerticalValues.height ? overrideVerticalValues.height.value() : computedContentHeight(replacedBox, verticalConstraints ? verticalConstraints->logicalHeight : WTF::nullopt);
882     auto heightIsAuto = !overrideVerticalValues.height && isHeightAuto(replacedBox);
883     auto widthIsAuto = style.logicalWidth().isAuto();
884
885     if (heightIsAuto && widthIsAuto && replacedBox.hasIntrinsicHeight()) {
886         // #2
887         height = replacedBox.intrinsicHeight();
888     } else if (heightIsAuto && replacedBox.hasIntrinsicRatio()) {
889         // #3
890         auto usedWidth = formattingContext.geometryForBox(replacedBox).width();
891         height = usedWidth / replacedBox.intrinsicRatio();
892     } else if (heightIsAuto && replacedBox.hasIntrinsicHeight()) {
893         // #4
894         height = replacedBox.intrinsicHeight();
895     } else if (heightIsAuto) {
896         // #5
897         height = { 150 };
898     }
899
900     ASSERT(height);
901
902     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Height][Margin] -> inflow replaced -> height(" << *height << "px) margin(" << usedVerticalMargin.before << "px, " << usedVerticalMargin.after << "px) -> layoutBox(" << &replacedBox << ")");
903     return { *height, usedVerticalMargin };
904 }
905
906 ContentWidthAndMargin FormattingContext::Geometry::inlineReplacedWidthAndMargin(const ReplacedBox& replacedBox, const HorizontalConstraints& horizontalConstraints, Optional<VerticalConstraints> verticalConstraints, const OverrideHorizontalValues& overrideHorizontalValues) const
907 {
908     // 10.3.2 Inline, replaced elements
909     //
910     // A computed value of 'auto' for 'margin-left' or 'margin-right' becomes a used value of '0'.
911     //
912     // 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'.
913     //
914     // 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;
915     //    or if 'width' has a computed value of 'auto', 'height' has some other computed value, and the element does have an intrinsic ratio;
916     //    then the used value of 'width' is: (used height) * (intrinsic ratio)
917     //
918     // 3. If 'height' and 'width' both have computed values of 'auto' and the element has an intrinsic ratio but no intrinsic height or width,
919     //    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
920     //    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.
921     //
922     // 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'.
923     //
924     // 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.
925     //    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.
926
927     auto computedHorizontalMargin = Geometry::computedHorizontalMargin(replacedBox, horizontalConstraints);
928
929     auto usedMarginStart = [&] {
930         if (overrideHorizontalValues.margin)
931             return overrideHorizontalValues.margin->start;
932         return computedHorizontalMargin.start.valueOr(0_lu);
933     };
934
935     auto usedMarginEnd = [&] {
936         if (overrideHorizontalValues.margin)
937             return overrideHorizontalValues.margin->end;
938         return computedHorizontalMargin.end.valueOr(0_lu);
939     };
940
941     auto width = overrideHorizontalValues.width ? overrideHorizontalValues.width : computedContentWidth(replacedBox, horizontalConstraints.logicalWidth);
942     auto heightIsAuto = isHeightAuto(replacedBox);
943     auto height = computedContentHeight(replacedBox, verticalConstraints ? verticalConstraints->logicalHeight : WTF::nullopt);
944
945     if (!width && heightIsAuto && replacedBox.hasIntrinsicWidth()) {
946         // #1
947         width = replacedBox.intrinsicWidth();
948     } else if ((!width && heightIsAuto && !replacedBox.hasIntrinsicWidth() && replacedBox.hasIntrinsicHeight() && replacedBox.hasIntrinsicRatio())
949         || (!width && height && replacedBox.hasIntrinsicRatio())) {
950         // #2
951         width = height.valueOr(replacedBox.hasIntrinsicHeight()) * replacedBox.intrinsicRatio();
952     } else if (!width && heightIsAuto && replacedBox.hasIntrinsicRatio() && !replacedBox.hasIntrinsicWidth() && !replacedBox.hasIntrinsicHeight()) {
953         // #3
954         // FIXME: undefined but surely doable.
955         ASSERT_NOT_IMPLEMENTED_YET();
956     } else if (!width && replacedBox.hasIntrinsicWidth()) {
957         // #4
958         width = replacedBox.intrinsicWidth();
959     } else if (!width) {
960         // #5
961         width = { 300 };
962     }
963
964     ASSERT(width);
965
966     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Width][Margin] -> inflow replaced -> width(" << *width << "px) margin(" << usedMarginStart() << "px, " << usedMarginEnd() << "px) -> layoutBox(" << &replacedBox << ")");
967     return { *width, { usedMarginStart(), usedMarginEnd() }, computedHorizontalMargin };
968 }
969
970 LayoutSize FormattingContext::Geometry::inFlowPositionedPositionOffset(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints) const
971 {
972     ASSERT(layoutBox.isInFlowPositioned());
973
974     // 9.4.3 Relative positioning
975     //
976     // The 'top' and 'bottom' properties move relatively positioned element(s) up or down without changing their size.
977     // 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.
978     //
979     // 1. If both are 'auto', their used values are both '0'.
980     // 2. If one of them is 'auto', it becomes the negative of the other.
981     // 3. If neither is 'auto', 'bottom' is ignored (i.e., the used value of 'bottom' will be minus the value of 'top').
982
983     auto& style = layoutBox.style();
984     auto containingBlockWidth = horizontalConstraints.logicalWidth;
985
986     auto top = computedValueIfNotAuto(style.logicalTop(), containingBlockWidth);
987     auto bottom = computedValueIfNotAuto(style.logicalBottom(), containingBlockWidth);
988
989     if (!top && !bottom) {
990         // #1
991         top = bottom = { 0 };
992     } else if (!top) {
993         // #2
994         top = -*bottom;
995     } else if (!bottom) {
996         // #3
997         bottom = -*top;
998     } else {
999         // #4
1000         bottom = WTF::nullopt;
1001     }
1002
1003     // For relatively positioned elements, 'left' and 'right' move the box(es) horizontally, without changing their size.
1004     // 'Left' moves the boxes to the right, and 'right' moves them to the left.
1005     // Since boxes are not split or stretched as a result of 'left' or 'right', the used values are always: left = -right.
1006     //
1007     // 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).
1008     // 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').
1009     // 3. If 'right' is specified as 'auto', its used value is minus the value of 'left'.
1010     // 4. If neither 'left' nor 'right' is 'auto', the position is over-constrained, and one of them has to be ignored.
1011     //    If the 'direction' property of the containing block is 'ltr', the value of 'left' wins and 'right' becomes -'left'.
1012     //    If 'direction' of the containing block is 'rtl', 'right' wins and 'left' is ignored.
1013
1014     auto left = computedValueIfNotAuto(style.logicalLeft(), containingBlockWidth);
1015     auto right = computedValueIfNotAuto(style.logicalRight(), containingBlockWidth);
1016
1017     if (!left && !right) {
1018         // #1
1019         left = right = { 0 };
1020     } else if (!left) {
1021         // #2
1022         left = -*right;
1023     } else if (!right) {
1024         // #3
1025         right = -*left;
1026     } else {
1027         // #4
1028         auto isLeftToRightDirection = layoutBox.containingBlock().style().isLeftToRightDirection();
1029         if (isLeftToRightDirection)
1030             right = -*left;
1031         else
1032             left = WTF::nullopt;
1033     }
1034
1035     ASSERT(!bottom || *top == -*bottom);
1036     ASSERT(!left || *left == -*right);
1037
1038     auto topPositionOffset = *top;
1039     auto leftPositionOffset = left.valueOr(-*right);
1040
1041     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position] -> positioned inflow -> top offset(" << topPositionOffset << "px) left offset(" << leftPositionOffset << "px) layoutBox(" << &layoutBox << ")");
1042     return { leftPositionOffset, topPositionOffset };
1043 }
1044
1045 Edges FormattingContext::Geometry::computedBorder(const Box& layoutBox) const
1046 {
1047     auto& style = layoutBox.style();
1048     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Border] -> layoutBox: " << &layoutBox);
1049     return {
1050         { LayoutUnit(style.borderLeft().boxModelWidth()), LayoutUnit(style.borderRight().boxModelWidth()) },
1051         { LayoutUnit(style.borderTop().boxModelWidth()), LayoutUnit(style.borderBottom().boxModelWidth()) }
1052     };
1053 }
1054
1055 Optional<Edges> FormattingContext::Geometry::computedPadding(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints) const
1056 {
1057     if (!layoutBox.isPaddingApplicable())
1058         return WTF::nullopt;
1059
1060     auto& style = layoutBox.style();
1061     auto containingBlockWidth = horizontalConstraints.logicalWidth;
1062     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Padding] -> layoutBox: " << &layoutBox);
1063     return Edges {
1064         { valueForLength(style.paddingLeft(), containingBlockWidth), valueForLength(style.paddingRight(), containingBlockWidth) },
1065         { valueForLength(style.paddingTop(), containingBlockWidth), valueForLength(style.paddingBottom(), containingBlockWidth) }
1066     };
1067 }
1068
1069 ComputedHorizontalMargin FormattingContext::Geometry::computedHorizontalMargin(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints) const
1070 {
1071     auto& style = layoutBox.style();
1072     auto containingBlockWidth = horizontalConstraints.logicalWidth;
1073     return { computedValueIfNotAuto(style.marginStart(), containingBlockWidth), computedValueIfNotAuto(style.marginEnd(), containingBlockWidth) };
1074 }
1075
1076 ComputedVerticalMargin FormattingContext::Geometry::computedVerticalMargin(const Box& layoutBox, const HorizontalConstraints& horizontalConstraints) const
1077 {
1078     auto& style = layoutBox.style();
1079     auto containingBlockWidth = horizontalConstraints.logicalWidth;
1080     return { computedValueIfNotAuto(style.marginBefore(), containingBlockWidth), computedValueIfNotAuto(style.marginAfter(), containingBlockWidth) };
1081 }
1082
1083 FormattingContext::IntrinsicWidthConstraints FormattingContext::Geometry::constrainByMinMaxWidth(const Box& layoutBox, IntrinsicWidthConstraints intrinsicWidth) const
1084 {
1085     auto& style = layoutBox.style();
1086     auto minWidth = fixedValue(style.logicalMinWidth());
1087     auto maxWidth = fixedValue(style.logicalMaxWidth());
1088     if (!minWidth && !maxWidth)
1089         return intrinsicWidth;
1090
1091     if (maxWidth) {
1092         intrinsicWidth.minimum = std::min(*maxWidth, intrinsicWidth.minimum);
1093         intrinsicWidth.maximum = std::min(*maxWidth, intrinsicWidth.maximum);
1094     }
1095
1096     if (minWidth) {
1097         intrinsicWidth.minimum = std::max(*minWidth, intrinsicWidth.minimum);
1098         intrinsicWidth.maximum = std::max(*minWidth, intrinsicWidth.maximum);
1099     }
1100
1101     ASSERT(intrinsicWidth.minimum <= intrinsicWidth.maximum);
1102     return intrinsicWidth;
1103 }
1104
1105 OutOfFlowHorizontalConstraints FormattingContext::Geometry::horizontalConstraintsForOutOfFlow(const Display::Box& containingBlockGeometry)
1106 {
1107     return OutOfFlowHorizontalConstraints {
1108         HorizontalConstraints { containingBlockGeometry.paddingBoxLeft(), containingBlockGeometry.paddingBoxWidth() },
1109         containingBlockGeometry.contentBoxWidth() };
1110 }
1111
1112 VerticalConstraints FormattingContext::Geometry::verticalConstraintsForOutOfFlow(const Display::Box& containingBlockGeometry)
1113 {
1114     return VerticalConstraints { containingBlockGeometry.paddingBoxTop(), containingBlockGeometry.paddingBoxHeight() };
1115 }
1116
1117 HorizontalConstraints FormattingContext::Geometry::horizontalConstraintsForInFlow(const Display::Box& containingBlockGeometry)
1118 {
1119     return HorizontalConstraints { containingBlockGeometry.contentBoxLeft(), containingBlockGeometry.contentBoxWidth() };
1120 }
1121
1122 VerticalConstraints FormattingContext::Geometry::verticalConstraintsForInFlow(const Display::Box& containingBlockGeometry)
1123 {
1124     return VerticalConstraints { containingBlockGeometry.contentBoxTop(), { } };
1125 }
1126
1127 }
1128 }
1129 #endif