[LayoutReloaded] Add support for out-of-flow descendants in inline formatting context.
authorzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 10 Apr 2018 17:21:50 +0000 (17:21 +0000)
committerzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 10 Apr 2018 17:21:50 +0000 (17:21 +0000)
https://bugs.webkit.org/show_bug.cgi?id=184454

Reviewed by Antti Koivisto.

When the containing block of an out-of-flow positioned element establishes an inline formatting context.
This is mostly about moving out-of-flow logic from BlockFormattingContext to the base class.

* LayoutReloaded/FormattingContext/BlockFormatting/BlockFormattingContext.js:
(BlockFormattingContext.prototype._adjustBottomWithFIXME):
(BlockFormattingContext):
(BlockFormattingContext.prototype._layoutOutOfFlowDescendants): Deleted.
(BlockFormattingContext.prototype._computeOutOfFlowWidth): Deleted.
(BlockFormattingContext.prototype._computeInFlowWidth): Deleted.
(BlockFormattingContext.prototype._computeOutOfFlowHeight): Deleted.
(BlockFormattingContext.prototype._computeOutOfFlowPosition): Deleted.
(BlockFormattingContext.prototype._shrinkToFitWidth): Deleted.
* LayoutReloaded/FormattingContext/FormattingContext.js:
(FormattingContext.prototype._placeInFlowPositionedChildren):
(FormattingContext.prototype._computeInFlowWidth):
(FormattingContext.prototype._layoutOutOfFlowDescendants):
(FormattingContext.prototype._computeOutOfFlowWidth):
(FormattingContext.prototype._computeOutOfFlowHeight):
(FormattingContext.prototype._computeOutOfFlowPosition):
(FormattingContext.prototype._shrinkToFitWidth):
* LayoutReloaded/FormattingContext/InlineFormatting/InlineFormattingContext.js:
(InlineFormattingContext.prototype.layout):
(InlineFormattingContext.prototype._handleInlineContainer):
(InlineFormattingContext.prototype._handleInlineBlockContainer):
(InlineFormattingContext.prototype._placeOutOfFlowDescendants): Deleted.
* LayoutReloaded/test/index.html:
* LayoutReloaded/test/inline-with-out-of-flow-descendant.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@230484 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Tools/ChangeLog
Tools/LayoutReloaded/FormattingContext/BlockFormatting/BlockFormattingContext.js
Tools/LayoutReloaded/FormattingContext/FormattingContext.js
Tools/LayoutReloaded/FormattingContext/InlineFormatting/InlineFormattingContext.js
Tools/LayoutReloaded/test/index.html
Tools/LayoutReloaded/test/inline-with-out-of-flow-descendant.html [new file with mode: 0644]

index f537a45c88e64f02b44ce579ad692366ddbfe29f..1d6e541379bfceb65a66d296b91c4e77ca2eddca 100644 (file)
@@ -1,3 +1,38 @@
+2018-04-10  Zalan Bujtas  <zalan@apple.com>
+
+        [LayoutReloaded] Add support for out-of-flow descendants in inline formatting context.
+        https://bugs.webkit.org/show_bug.cgi?id=184454
+
+        Reviewed by Antti Koivisto.
+
+        When the containing block of an out-of-flow positioned element establishes an inline formatting context.
+        This is mostly about moving out-of-flow logic from BlockFormattingContext to the base class.
+
+        * LayoutReloaded/FormattingContext/BlockFormatting/BlockFormattingContext.js:
+        (BlockFormattingContext.prototype._adjustBottomWithFIXME):
+        (BlockFormattingContext):
+        (BlockFormattingContext.prototype._layoutOutOfFlowDescendants): Deleted.
+        (BlockFormattingContext.prototype._computeOutOfFlowWidth): Deleted.
+        (BlockFormattingContext.prototype._computeInFlowWidth): Deleted.
+        (BlockFormattingContext.prototype._computeOutOfFlowHeight): Deleted.
+        (BlockFormattingContext.prototype._computeOutOfFlowPosition): Deleted.
+        (BlockFormattingContext.prototype._shrinkToFitWidth): Deleted.
+        * LayoutReloaded/FormattingContext/FormattingContext.js:
+        (FormattingContext.prototype._placeInFlowPositionedChildren):
+        (FormattingContext.prototype._computeInFlowWidth):
+        (FormattingContext.prototype._layoutOutOfFlowDescendants):
+        (FormattingContext.prototype._computeOutOfFlowWidth):
+        (FormattingContext.prototype._computeOutOfFlowHeight):
+        (FormattingContext.prototype._computeOutOfFlowPosition):
+        (FormattingContext.prototype._shrinkToFitWidth):
+        * LayoutReloaded/FormattingContext/InlineFormatting/InlineFormattingContext.js:
+        (InlineFormattingContext.prototype.layout):
+        (InlineFormattingContext.prototype._handleInlineContainer):
+        (InlineFormattingContext.prototype._handleInlineBlockContainer):
+        (InlineFormattingContext.prototype._placeOutOfFlowDescendants): Deleted.
+        * LayoutReloaded/test/index.html:
+        * LayoutReloaded/test/inline-with-out-of-flow-descendant.html: Added.
+
 2018-04-09  Zan Dobersek  <zdobersek@igalia.com>
 
         WKTR: Enable DOM cache, SW registration directory cleanup on non-Cocoa platforms
index 47e1da71fcf811db405dc6f27be49973471a7264..a243c28398caa2a973af6e787b21874d9b361d67 100644 (file)
@@ -118,95 +118,6 @@ class BlockFormattingContext extends FormattingContext {
         this.displayBox(layoutBox).setTopLeft(position);
     }
 
-    _layoutOutOfFlowDescendants() {
-        // This lays out all the out-of-flow boxes that belong to this formatting context even if
-        // the root container is not the containing block.
-        let outOfFlowDescendants = this._outOfFlowDescendants();
-        for (let outOfFlowBox of outOfFlowDescendants) {
-            this._addToLayoutQueue(outOfFlowBox);
-            this.computeWidth(outOfFlowBox);
-            this.layoutState().layout(outOfFlowBox);
-            this.computeHeight(outOfFlowBox);
-            this._computeOutOfFlowPosition(outOfFlowBox);
-            this._removeFromLayoutQueue(outOfFlowBox);
-        }
-    }
-
-    _computeOutOfFlowWidth(layoutBox) {
-        // 10.3.7 Absolutely positioned, non-replaced elements
-
-        // 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the width is shrink-to-fit. Then solve for 'left'
-        // 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if the 'direction' property of the element establishing
-        //     the static-position containing block is 'ltr' set 'left' to the static position, otherwise set 'right' to the static position.
-        //     Then solve for 'left' (if 'direction is 'rtl') or 'right' (if 'direction' is 'ltr').
-        // 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the width is shrink-to-fit . Then solve for 'right'
-        // 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve for 'left'
-        // 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve for 'width'
-        // 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve for 'right'
-        let width = Number.NaN;
-        if (Utils.isWidthAuto(layoutBox) && Utils.isLeftAuto(layoutBox) && Utils.isRightAuto(layoutBox))
-            width = this._shrinkToFitWidth(layoutBox);
-        else if (Utils.isLeftAuto(layoutBox) && Utils.isWidthAuto(layoutBox) && !Utils.isRightAuto(layoutBox))
-            width = this._shrinkToFitWidth(layoutBox); // 1
-        else if (Utils.isLeftAuto(layoutBox) && Utils.isRightAuto(layoutBox) && !Utils.isWidthAuto(layoutBox))
-            width = Utils.width(layoutBox); // 2
-        else if (Utils.isWidthAuto(layoutBox) && Utils.isRightAuto(layoutBox) && !Utils.isLeftAuto(layoutBox))
-            width = this._shrinkToFitWidth(layoutBox); // 3
-        else if (Utils.isLeftAuto(layoutBox) && !Utils.isWidthAuto(layoutBox) && !Utils.isRightAuto(layoutBox))
-            width = Utils.width(layoutBox); // 4
-        else if (Utils.isWidthAuto(layoutBox) && !Utils.isLeftAuto(layoutBox) && !Utils.isRightAuto(layoutBox))
-            width = Math.max(0, this.displayBox(layoutBox.containingBlock()).contentBox().width() - Utils.right(layoutBox) - Utils.left(layoutBox)); // 5
-        else if (Utils.isRightAuto(layoutBox) && !Utils.isLeftAuto(layoutBox) && !Utils.isWidthAuto(layoutBox))
-            width = Utils.width(layoutBox); // 6
-        else
-            ASSERT_NOT_REACHED();
-        width += Utils.computedHorizontalBorderAndPadding(layoutBox.node());
-        this.displayBox(layoutBox).setWidth(width);
-    }
-
-    _computeInFlowWidth(layoutBox) {
-        if (Utils.isWidthAuto(layoutBox))
-            return this.displayBox(layoutBox).setWidth(this._horizontalConstraint(layoutBox));
-        return this.displayBox(layoutBox).setWidth(Utils.width(layoutBox) + Utils.computedHorizontalBorderAndPadding(layoutBox.node()));
-    }
-
-    _computeOutOfFlowHeight(layoutBox) {
-        // 1. If all three of 'top', 'height', and 'bottom' are auto, set 'top' to the static position and apply rule number three below.
-        // 2. If none of the three are 'auto': If both 'margin-top' and 'margin-bottom' are 'auto', solve the equation under
-        //    the extra constraint that the two margins get equal values. If one of 'margin-top' or 'margin-bottom' is 'auto',
-        //    solve the equation for that value. If the values are over-constrained, ignore the value for 'bottom' and solve for that value.
-        // Otherwise, pick the one of the following six rules that applies.
-
-        // 3. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then the height is based on the content per 10.6.7,
-        //    set 'auto' values for 'margin-top' and 'margin-bottom' to 0, and solve for 'top'
-        // 4. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then set 'top' to the static position, set 'auto' values
-        //    for 'margin-top' and 'margin-bottom' to 0, and solve for 'bottom'
-        // 5. 'height' and 'bottom' are 'auto' and 'top' is not 'auto', then the height is based on the content per 10.6.7,
-        //    set 'auto' values for 'margin-top' and 'margin-bottom' to 0, and solve for 'bottom'
-        // 6. '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'
-        // 7. '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'
-        // 8. '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'
-        let height = Number.NaN;
-        if (Utils.isHeightAuto(layoutBox) && Utils.isBottomAuto(layoutBox) && Utils.isTopAuto(layoutBox))
-            height = this._contentHeight(layoutBox); // 1
-        else if (Utils.isTopAuto((layoutBox)) && Utils.isHeightAuto((layoutBox)) && !Utils.isBottomAuto((layoutBox)))
-            height = this._contentHeight(layoutBox); // 3
-        else if (Utils.isTopAuto((layoutBox)) && Utils.isBottomAuto((layoutBox)) && !Utils.isHeightAuto((layoutBox)))
-            height = Utils.height(layoutBox); // 4
-        else if (Utils.isHeightAuto((layoutBox)) && Utils.isBottomAuto((layoutBox)) && !Utils.isTopAuto((layoutBox)))
-            height = this._contentHeight(layoutBox); // 5
-        else if (Utils.isTopAuto((layoutBox)) && !Utils.isHeightAuto((layoutBox)) && !Utils.isBottomAuto((layoutBox)))
-            height = Utils.height(layoutBox); // 6
-        else if (Utils.isHeightAuto((layoutBox)) && !Utils.isTopAuto((layoutBox)) && !Utils.isBottomAuto((layoutBox)))
-            height = Math.max(0, this.displayBox(layoutBox.containingBlock()).contentBox().height() - Utils.bottom(layoutBox) - Utils.top(layoutBox)); // 7
-        else if (Utils.isBottomAuto((layoutBox)) && !Utils.isTopAuto((layoutBox)) && !Utils.isHeightAuto((layoutBox)))
-            height = Utils.height(layoutBox); // 8
-        else
-            ASSERT_NOT_REACHED();
-        height += Utils.computedVerticalBorderAndPadding(layoutBox.node());
-        this.displayBox(layoutBox).setHeight(height);
-    }
-
     _computeInFlowHeight(layoutBox) {
         if (Utils.isHeightAuto(layoutBox)) {
             // Only children in the normal flow are taken into account (i.e., floating boxes and absolutely positioned boxes are ignored,
@@ -264,65 +175,4 @@ class BlockFormattingContext extends FormattingContext {
         }
         return bottom;
     }
-
-    _computeOutOfFlowPosition(layoutBox) {
-        let displayBox = this.displayBox(layoutBox);
-        let top = Number.NaN;
-        let containerSize = this.displayBox(layoutBox.containingBlock()).contentBox().size();
-        // Top/bottom
-        if (Utils.isTopAuto(layoutBox) && Utils.isBottomAuto(layoutBox)) {
-            ASSERT(Utils.isStaticallyPositioned(layoutBox));
-            // Vertically statically positioned.
-            // FIXME: Figure out if it is actually valid that we use the parent box as the container (which is not even in this formatting context).
-            let parent = layoutBox.parent();
-            let parentDisplayBox = this.displayBox(parent);
-            let previousInFlowSibling = layoutBox.previousInFlowSibling();
-            let contentBottom = previousInFlowSibling ? this.displayBox(previousInFlowSibling).bottom() : parentDisplayBox.contentBox().top();
-            top = contentBottom + this.marginTop(layoutBox);
-            // Convert static position (in parent coordinate system) to absolute (in containing block coordindate system).
-            if (parent != layoutBox.containingBlock()) {
-                ASSERT(displayBox.parent() == this.displayBox(layoutBox.containingBlock()));
-                top += Utils.mapPosition(parentDisplayBox.topLeft(), parentDisplayBox, displayBox.parent()).top();
-            }
-        } else if (!Utils.isTopAuto(layoutBox))
-            top = Utils.top(layoutBox) + this.marginTop(layoutBox);
-        else if (!Utils.isBottomAuto(layoutBox))
-            top = containerSize.height() - Utils.bottom(layoutBox) - displayBox.height() - this.marginBottom(layoutBox);
-        else
-            ASSERT_NOT_REACHED();
-        // Left/right
-        let left = Number.NaN;
-        if (Utils.isLeftAuto(layoutBox) && Utils.isRightAuto(layoutBox)) {
-            ASSERT(Utils.isStaticallyPositioned(layoutBox));
-            // Horizontally statically positioned.
-            // FIXME: Figure out if it is actually valid that we use the parent box as the container (which is not even in this formatting context).
-            let parent = layoutBox.parent();
-            let parentDisplayBox = this.displayBox(parent);
-            left = parentDisplayBox.contentBox().left() + this.marginLeft(layoutBox);
-            // Convert static position (in parent coordinate system) to absolute (in containing block coordindate system).
-            if (parent != layoutBox.containingBlock()) {
-                ASSERT(displayBox.parent() == this.displayBox(layoutBox.containingBlock()));
-                left += Utils.mapPosition(parentDisplayBox.topLeft(), parentDisplayBox, displayBox.parent()).left();
-            }
-        } else if (!Utils.isLeftAuto(layoutBox))
-            left = Utils.left(layoutBox) + this.marginLeft(layoutBox);
-        else if (!Utils.isRightAuto(layoutBox))
-            left = containerSize.width() - Utils.right(layoutBox) - displayBox.width() - this.marginRight(layoutBox);
-        else
-            ASSERT_NOT_REACHED();
-        displayBox.setTopLeft(new LayoutPoint(top, left));
-    }
-
-    _shrinkToFitWidth(layoutBox) {
-        // FIXME: this is naive.
-        ASSERT(Utils.isWidthAuto(layoutBox));
-        if (!layoutBox.isContainer() || !layoutBox.hasChild())
-            return 0;
-        let width = 0;
-        for (let inFlowChild = layoutBox.firstInFlowChild(); inFlowChild; inFlowChild = inFlowChild.nextInFlowSibling()) {
-            let widthCandidate = Utils.isWidthAuto(inFlowChild) ? this._shrinkToFitWidth(inFlowChild) : Utils.width(inFlowChild);
-            width = Math.max(width, widthCandidate + Utils.computedHorizontalBorderAndPadding(inFlowChild.node()));
-        }
-        return width;
-    }
 }
index 9c252a054621a645f7aff1c0b5ee56442d4f4558..425e6a93be278a5bd7355ce0242f685301cd31f6 100644 (file)
@@ -117,6 +117,20 @@ class FormattingContext {
         this.displayBox(layoutBox).setHeight(Utils.height(layoutBox) + Utils.computedVerticalBorderAndPadding(layoutBox.node()));
     }
 
+    _placeInFlowPositionedChildren(container) {
+        if (!container.isContainer())
+            return;
+        // If this layoutBox also establishes a formatting context, then positioning already has happend at the formatting context.
+        if (container.establishesFormattingContext() && container != this.formattingRoot())
+            return;
+        ASSERT(container.isContainer());
+        for (let inFlowChild = container.firstInFlowChild(); inFlowChild; inFlowChild = inFlowChild.nextInFlowSibling()) {
+            if (!inFlowChild.isInFlowPositioned())
+                continue;
+            this._computeInFlowPositionedPosition(inFlowChild);
+        }
+    }
+
     _computeInFlowPositionedPosition(layoutBox) {
         // Start with the original, static position.
         let displayBox = this.displayBox(layoutBox);
@@ -134,18 +148,154 @@ class FormattingContext {
         displayBox.setTopLeft(relativePosition);
     }
 
-    _placeInFlowPositionedChildren(container) {
-        if (!container.isContainer())
-            return;
-        // If this layoutBox also establishes a formatting context, then positioning already has happend at the formatting context.
-        if (container.establishesFormattingContext() && container != this.formattingRoot())
-            return;
-        ASSERT(container.isContainer());
-        for (let inFlowChild = container.firstInFlowChild(); inFlowChild; inFlowChild = inFlowChild.nextInFlowSibling()) {
-            if (!inFlowChild.isInFlowPositioned())
-                continue;
-            this._computeInFlowPositionedPosition(inFlowChild);
+    _computeInFlowWidth(layoutBox) {
+        if (Utils.isWidthAuto(layoutBox))
+            return this.displayBox(layoutBox).setWidth(this._horizontalConstraint(layoutBox));
+        return this.displayBox(layoutBox).setWidth(Utils.width(layoutBox) + Utils.computedHorizontalBorderAndPadding(layoutBox.node()));
+    }
+
+    _layoutOutOfFlowDescendants() {
+        // This lays out all the out-of-flow boxes that belong to this formatting context even if
+        // the root container is not the containing block.
+        let outOfFlowDescendants = this._outOfFlowDescendants();
+        for (let outOfFlowBox of outOfFlowDescendants) {
+            this._addToLayoutQueue(outOfFlowBox);
+            this._computeOutOfFlowWidth(outOfFlowBox);
+            this.layoutState().layout(outOfFlowBox);
+            this._computeOutOfFlowHeight(outOfFlowBox);
+            this._computeOutOfFlowPosition(outOfFlowBox);
+            this._removeFromLayoutQueue(outOfFlowBox);
+        }
+    }
+
+    _computeOutOfFlowWidth(layoutBox) {
+        // 10.3.7 Absolutely positioned, non-replaced elements
+
+        // 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the width is shrink-to-fit. Then solve for 'left'
+        // 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if the 'direction' property of the element establishing
+        //     the static-position containing block is 'ltr' set 'left' to the static position, otherwise set 'right' to the static position.
+        //     Then solve for 'left' (if 'direction is 'rtl') or 'right' (if 'direction' is 'ltr').
+        // 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the width is shrink-to-fit . Then solve for 'right'
+        // 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve for 'left'
+        // 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve for 'width'
+        // 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve for 'right'
+        let width = Number.NaN;
+        if (Utils.isWidthAuto(layoutBox) && Utils.isLeftAuto(layoutBox) && Utils.isRightAuto(layoutBox))
+            width = this._shrinkToFitWidth(layoutBox);
+        else if (Utils.isLeftAuto(layoutBox) && Utils.isWidthAuto(layoutBox) && !Utils.isRightAuto(layoutBox))
+            width = this._shrinkToFitWidth(layoutBox); // 1
+        else if (Utils.isLeftAuto(layoutBox) && Utils.isRightAuto(layoutBox) && !Utils.isWidthAuto(layoutBox))
+            width = Utils.width(layoutBox); // 2
+        else if (Utils.isWidthAuto(layoutBox) && Utils.isRightAuto(layoutBox) && !Utils.isLeftAuto(layoutBox))
+            width = this._shrinkToFitWidth(layoutBox); // 3
+        else if (Utils.isLeftAuto(layoutBox) && !Utils.isWidthAuto(layoutBox) && !Utils.isRightAuto(layoutBox))
+            width = Utils.width(layoutBox); // 4
+        else if (Utils.isWidthAuto(layoutBox) && !Utils.isLeftAuto(layoutBox) && !Utils.isRightAuto(layoutBox))
+            width = Math.max(0, this.displayBox(layoutBox.containingBlock()).contentBox().width() - Utils.right(layoutBox) - Utils.left(layoutBox)); // 5
+        else if (Utils.isRightAuto(layoutBox) && !Utils.isLeftAuto(layoutBox) && !Utils.isWidthAuto(layoutBox))
+            width = Utils.width(layoutBox); // 6
+        else
+            ASSERT_NOT_REACHED();
+        width += Utils.computedHorizontalBorderAndPadding(layoutBox.node());
+        this.displayBox(layoutBox).setWidth(width);
+    }
+
+    _computeOutOfFlowHeight(layoutBox) {
+        // 1. If all three of 'top', 'height', and 'bottom' are auto, set 'top' to the static position and apply rule number three below.
+        // 2. If none of the three are 'auto': If both 'margin-top' and 'margin-bottom' are 'auto', solve the equation under
+        //    the extra constraint that the two margins get equal values. If one of 'margin-top' or 'margin-bottom' is 'auto',
+        //    solve the equation for that value. If the values are over-constrained, ignore the value for 'bottom' and solve for that value.
+        // Otherwise, pick the one of the following six rules that applies.
+
+        // 3. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then the height is based on the content per 10.6.7,
+        //    set 'auto' values for 'margin-top' and 'margin-bottom' to 0, and solve for 'top'
+        // 4. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then set 'top' to the static position, set 'auto' values
+        //    for 'margin-top' and 'margin-bottom' to 0, and solve for 'bottom'
+        // 5. 'height' and 'bottom' are 'auto' and 'top' is not 'auto', then the height is based on the content per 10.6.7,
+        //    set 'auto' values for 'margin-top' and 'margin-bottom' to 0, and solve for 'bottom'
+        // 6. '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'
+        // 7. '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'
+        // 8. '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'
+        let height = Number.NaN;
+        if (Utils.isHeightAuto(layoutBox) && Utils.isBottomAuto(layoutBox) && Utils.isTopAuto(layoutBox))
+            height = this._contentHeight(layoutBox); // 1
+        else if (Utils.isTopAuto((layoutBox)) && Utils.isHeightAuto((layoutBox)) && !Utils.isBottomAuto((layoutBox)))
+            height = this._contentHeight(layoutBox); // 3
+        else if (Utils.isTopAuto((layoutBox)) && Utils.isBottomAuto((layoutBox)) && !Utils.isHeightAuto((layoutBox)))
+            height = Utils.height(layoutBox); // 4
+        else if (Utils.isHeightAuto((layoutBox)) && Utils.isBottomAuto((layoutBox)) && !Utils.isTopAuto((layoutBox)))
+            height = this._contentHeight(layoutBox); // 5
+        else if (Utils.isTopAuto((layoutBox)) && !Utils.isHeightAuto((layoutBox)) && !Utils.isBottomAuto((layoutBox)))
+            height = Utils.height(layoutBox); // 6
+        else if (Utils.isHeightAuto((layoutBox)) && !Utils.isTopAuto((layoutBox)) && !Utils.isBottomAuto((layoutBox)))
+            height = Math.max(0, this.displayBox(layoutBox.containingBlock()).contentBox().height() - Utils.bottom(layoutBox) - Utils.top(layoutBox)); // 7
+        else if (Utils.isBottomAuto((layoutBox)) && !Utils.isTopAuto((layoutBox)) && !Utils.isHeightAuto((layoutBox)))
+            height = Utils.height(layoutBox); // 8
+        else
+            ASSERT_NOT_REACHED();
+        height += Utils.computedVerticalBorderAndPadding(layoutBox.node());
+        this.displayBox(layoutBox).setHeight(height);
+    }
+
+    _computeOutOfFlowPosition(layoutBox) {
+        let displayBox = this.displayBox(layoutBox);
+        let top = Number.NaN;
+        let containerSize = this.displayBox(layoutBox.containingBlock()).contentBox().size();
+        // Top/bottom
+        if (Utils.isTopAuto(layoutBox) && Utils.isBottomAuto(layoutBox)) {
+            ASSERT(Utils.isStaticallyPositioned(layoutBox));
+            // Vertically statically positioned.
+            // FIXME: Figure out if it is actually valid that we use the parent box as the container (which is not even in this formatting context).
+            let parent = layoutBox.parent();
+            let parentDisplayBox = this.displayBox(parent);
+            let previousInFlowSibling = layoutBox.previousInFlowSibling();
+            let contentBottom = previousInFlowSibling ? this.displayBox(previousInFlowSibling).bottom() : parentDisplayBox.contentBox().top();
+            top = contentBottom + this.marginTop(layoutBox);
+            // Convert static position (in parent coordinate system) to absolute (in containing block coordindate system).
+            if (parent != layoutBox.containingBlock()) {
+                ASSERT(displayBox.parent() == this.displayBox(layoutBox.containingBlock()));
+                top += Utils.mapPosition(parentDisplayBox.topLeft(), parentDisplayBox, displayBox.parent()).top();
+            }
+        } else if (!Utils.isTopAuto(layoutBox))
+            top = Utils.top(layoutBox) + this.marginTop(layoutBox);
+        else if (!Utils.isBottomAuto(layoutBox))
+            top = containerSize.height() - Utils.bottom(layoutBox) - displayBox.height() - this.marginBottom(layoutBox);
+        else
+            ASSERT_NOT_REACHED();
+        // Left/right
+        let left = Number.NaN;
+        if (Utils.isLeftAuto(layoutBox) && Utils.isRightAuto(layoutBox)) {
+            ASSERT(Utils.isStaticallyPositioned(layoutBox));
+            // Horizontally statically positioned.
+            // FIXME: Figure out if it is actually valid that we use the parent box as the container (which is not even in this formatting context).
+            let parent = layoutBox.parent();
+            let parentDisplayBox = this.displayBox(parent);
+            left = parentDisplayBox.contentBox().left() + this.marginLeft(layoutBox);
+            // Convert static position (in parent coordinate system) to absolute (in containing block coordindate system).
+            if (parent != layoutBox.containingBlock()) {
+                ASSERT(displayBox.parent() == this.displayBox(layoutBox.containingBlock()));
+                left += Utils.mapPosition(parentDisplayBox.topLeft(), parentDisplayBox, displayBox.parent()).left();
+            }
+        } else if (!Utils.isLeftAuto(layoutBox))
+            left = Utils.left(layoutBox) + this.marginLeft(layoutBox);
+        else if (!Utils.isRightAuto(layoutBox))
+            left = containerSize.width() - Utils.right(layoutBox) - displayBox.width() - this.marginRight(layoutBox);
+        else
+            ASSERT_NOT_REACHED();
+        displayBox.setTopLeft(new LayoutPoint(top, left));
+    }
+
+    _shrinkToFitWidth(layoutBox) {
+        // FIXME: this is naive and missing the actual preferred width computation.
+        ASSERT(Utils.isWidthAuto(layoutBox));
+        if (!layoutBox.isContainer() || !layoutBox.hasChild())
+            return 0;
+        let width = 0;
+        for (let inFlowChild = layoutBox.firstInFlowChild(); inFlowChild; inFlowChild = inFlowChild.nextInFlowSibling()) {
+            let widthCandidate = Utils.isWidthAuto(inFlowChild) ? this._shrinkToFitWidth(inFlowChild) : Utils.width(inFlowChild);
+            width = Math.max(width, widthCandidate + Utils.computedHorizontalBorderAndPadding(inFlowChild.node()));
         }
+        return width;
     }
 
     _outOfFlowDescendants() {
index cb42e99a325c0e3c76bf3d2329402b8defe67efb..f7f0fc9e722db9c440ad3523d32d9ab29069149b 100644 (file)
@@ -50,7 +50,7 @@ class InlineFormattingContext extends FormattingContext {
         // Place the inflow positioned children.
         this._placeInFlowPositionedChildren(this.formattingRoot());
         // And take care of out-of-flow boxes as the final step.
-        this._placeOutOfFlowDescendants(this.formattingRoot());
+        this._layoutOutOfFlowDescendants(this.formattingRoot());
         this._commitLine();
         ASSERT(!this.m_inlineContainerStack.length);
    }
@@ -69,22 +69,23 @@ class InlineFormattingContext extends FormattingContext {
         this._adjustLineForInlineContainerEnd(inlineContainer);
         this._removeFromLayoutQueue(inlineContainer);
         this._addToLayoutQueue(inlineContainer.nextInFlowOrFloatSibling());
-        // Place the in- and out-of-flow positioned children.
+        // Place inflow positioned children.
         this._placeInFlowPositionedChildren(inlineContainer);
-        this._placeOutOfFlowDescendants(inlineContainer);
     }
 
 
     _handleInlineBlockContainer(inlineBlockContainer) {
         ASSERT(inlineBlockContainer.establishesFormattingContext());
-        this._adjustLineForInlineContainerStart(inlineBlockContainer);
-        // TODO: auto width/height
         let displayBox = this.displayBox(inlineBlockContainer);
+
+        // TODO: auto width/height
+        this._adjustLineForInlineContainerStart(inlineBlockContainer);
         displayBox.setWidth(Utils.width(inlineBlockContainer) + Utils.computedHorizontalBorderAndPadding(inlineBlockContainer.node()));
-        displayBox.setHeight(Utils.height(inlineBlockContainer) + Utils.computedVerticalBorderAndPadding(inlineBlockContainer.node()));
         this.layoutState().layout(inlineBlockContainer);
-        this._line().addInlineContainerBox(displayBox.size());
+        displayBox.setHeight(Utils.height(inlineBlockContainer) + Utils.computedVerticalBorderAndPadding(inlineBlockContainer.node()));
         this._adjustLineForInlineContainerEnd(inlineBlockContainer);
+
+        this._line().addInlineContainerBox(displayBox.size());
         this._removeFromLayoutQueue(inlineBlockContainer);
         this._addToLayoutQueue(inlineBlockContainer.nextInFlowOrFloatSibling());
     }
@@ -152,10 +153,6 @@ class InlineFormattingContext extends FormattingContext {
         this._line().adjustWithOffset(offset);
     }
 
-    _placeOutOfFlowDescendants(container) {
-
-    }
-
     _commitLine() {
         if (this._line().isEmpty())
             return;
index 9b37a20534c075c32038e3bb1695ed61727c8601..a7a8c87e81d2c5ee5b94a63cf615d937ebb00089 100644 (file)
@@ -80,7 +80,8 @@ let testFiles = [
     "inline-formatting-context-floats2.html",
     "inline-with-padding-border-margin-offsets.html",
     "inline-block-with-fixed-width-height.html",
-    "inline-with-relative-positioning.html"
+    "inline-with-relative-positioning.html",
+    "inline-with-out-of-flow-descendant.html"
 ];
 
 let debugThis = [];
diff --git a/Tools/LayoutReloaded/test/inline-with-out-of-flow-descendant.html b/Tools/LayoutReloaded/test/inline-with-out-of-flow-descendant.html
new file mode 100644 (file)
index 0000000..cb5ae32
--- /dev/null
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div style="position: relative"><span>foo<span style="position: absolute; left: 100px; top: 100px; width: 100px; height: 100px;">foobar</span></span></div>
+</body>
+</html>