[LayoutReloaded] Introduce needsLayout flag
authorzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 12 Apr 2018 04:19:30 +0000 (04:19 +0000)
committerzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 12 Apr 2018 04:19:30 +0000 (04:19 +0000)
https://bugs.webkit.org/show_bug.cgi?id=184527

Reviewed by Antti Koivisto.

Use the formatting state to mark boxes dirty.

* LayoutReloaded/FormattingContext/BlockFormatting/BlockFormattingContext.js:
(BlockFormattingContext.prototype.layout):
(BlockFormattingContext.prototype._firstInFlowChildWithNeedsLayout):
(BlockFormattingContext.prototype._nextInFlowSiblingWithNeedsLayout):
(BlockFormattingContext):
* LayoutReloaded/FormattingContext/FormattingContext.js:
(FormattingContext.prototype._layoutOutOfFlowDescendants):
* LayoutReloaded/FormattingState/FormattingState.js:
(FormattingState):
(FormattingState.prototype.markNeedsLayout):
(FormattingState.prototype.clearNeedsLayout):
(FormattingState.prototype.needsLayout):
(FormattingState.prototype.layoutNeeded):
(FormattingState.prototype._markSubTreeNeedsLayout):
* LayoutReloaded/LayoutState.js:
(LayoutState.prototype.formattingStateForBox):
(LayoutState.prototype.setNeedsLayout):
(LayoutState.prototype.needsLayout):
* LayoutReloaded/LayoutTree/Box.js:
(Layout.Box.prototype.isFormattingContextDescendant):
* LayoutReloaded/LayoutTree/Container.js:
(Layout.Container.prototype.isContainingBlockDescendant):

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

Tools/ChangeLog
Tools/LayoutReloaded/FormattingContext/BlockFormatting/BlockFormattingContext.js
Tools/LayoutReloaded/FormattingContext/FormattingContext.js
Tools/LayoutReloaded/FormattingState/FormattingState.js
Tools/LayoutReloaded/LayoutState.js
Tools/LayoutReloaded/LayoutTree/Box.js
Tools/LayoutReloaded/LayoutTree/Container.js

index 1906f5e..3f17c12 100644 (file)
@@ -1,3 +1,35 @@
+2018-04-11  Zalan Bujtas  <zalan@apple.com>
+
+        [LayoutReloaded] Introduce needsLayout flag
+        https://bugs.webkit.org/show_bug.cgi?id=184527
+
+        Reviewed by Antti Koivisto.
+
+        Use the formatting state to mark boxes dirty.
+
+        * LayoutReloaded/FormattingContext/BlockFormatting/BlockFormattingContext.js:
+        (BlockFormattingContext.prototype.layout):
+        (BlockFormattingContext.prototype._firstInFlowChildWithNeedsLayout):
+        (BlockFormattingContext.prototype._nextInFlowSiblingWithNeedsLayout):
+        (BlockFormattingContext):
+        * LayoutReloaded/FormattingContext/FormattingContext.js:
+        (FormattingContext.prototype._layoutOutOfFlowDescendants):
+        * LayoutReloaded/FormattingState/FormattingState.js:
+        (FormattingState):
+        (FormattingState.prototype.markNeedsLayout):
+        (FormattingState.prototype.clearNeedsLayout):
+        (FormattingState.prototype.needsLayout):
+        (FormattingState.prototype.layoutNeeded):
+        (FormattingState.prototype._markSubTreeNeedsLayout):
+        * LayoutReloaded/LayoutState.js:
+        (LayoutState.prototype.formattingStateForBox):
+        (LayoutState.prototype.setNeedsLayout):
+        (LayoutState.prototype.needsLayout):
+        * LayoutReloaded/LayoutTree/Box.js:
+        (Layout.Box.prototype.isFormattingContextDescendant):
+        * LayoutReloaded/LayoutTree/Container.js:
+        (Layout.Container.prototype.isContainingBlockDescendant):
+
 2018-04-11  Jonathan Bedard  <jbedard@apple.com>
 
         Fix TestInvocation class for newer versions of clang.
index a243c28..524c264 100644 (file)
@@ -35,8 +35,7 @@ class BlockFormattingContext extends FormattingContext {
 
         // This is a post-order tree traversal layout.
         // The root container layout is done in the formatting context it lives in, not that one it creates, so let's start with the first child.
-        if (this.formattingRoot().firstInFlowOrFloatChild())
-            this._addToLayoutQueue(this.formattingRoot().firstInFlowOrFloatChild());
+        this._addToLayoutQueue(this._firstInFlowChildWithNeedsLayout(this.formattingRoot()));
         // 1. Go all the way down to the leaf node
         // 2. Compute static position and width as we travers down
         // 3. As we climb back on the tree, compute height and finialize position
@@ -51,9 +50,10 @@ class BlockFormattingContext extends FormattingContext {
                     this.layoutState().layout(layoutBox);
                     break;
                 }
-                if (!layoutBox.isContainer() || !layoutBox.hasInFlowOrFloatChild())
+                let childToLayout = this._firstInFlowChildWithNeedsLayout(layoutBox);
+                if (!childToLayout)
                     break;
-                this._addToLayoutQueue(layoutBox.firstInFlowOrFloatChild());
+                this._addToLayoutQueue(childToLayout);
             }
 
             // Climb back on the ancestors and compute height/final position.
@@ -67,8 +67,10 @@ class BlockFormattingContext extends FormattingContext {
                 this._placeInFlowPositionedChildren(layoutBox);
                 // We are done with laying out this box.
                 this._removeFromLayoutQueue(layoutBox);
-                if (layoutBox.nextInFlowOrFloatSibling()) {
-                    this._addToLayoutQueue(layoutBox.nextInFlowOrFloatSibling());
+                this.formattingState().clearNeedsLayout(layoutBox);
+                let nextSiblingToLayout = this._nextInFlowSiblingWithNeedsLayout(layoutBox);
+                if (nextSiblingToLayout) {
+                    this._addToLayoutQueue(nextSiblingToLayout);
                     break;
                 }
             }
@@ -77,6 +79,7 @@ class BlockFormattingContext extends FormattingContext {
         this._placeInFlowPositionedChildren(this.formattingRoot());
         // And take care of out-of-flow boxes as the final step.
         this._layoutOutOfFlowDescendants();
+        ASSERT(!this.formattingState().layoutNeeded());
    }
 
     computeWidth(layoutBox) {
@@ -175,4 +178,22 @@ class BlockFormattingContext extends FormattingContext {
         }
         return bottom;
     }
+
+    _firstInFlowChildWithNeedsLayout(layoutBox) {
+        if (!layoutBox.isContainer())
+            return null;
+        for (let child = layoutBox.firstInFlowOrFloatChild(); child; child = child.nextInFlowOrFloatSibling()) {
+            if (this.formattingState().needsLayout(child))
+                return child;
+        }
+        return null;
+    }
+
+    _nextInFlowSiblingWithNeedsLayout(layoutBox) {
+        for (let sibling = layoutBox.nextInFlowOrFloatSibling(); sibling; sibling = sibling.nextInFlowOrFloatSibling()) {
+            if (this.formattingState().needsLayout(sibling))
+                return sibling;
+        }
+        return null;
+    }
 }
index 425e6a9..e2c18d9 100644 (file)
@@ -165,6 +165,7 @@ class FormattingContext {
             this._computeOutOfFlowHeight(outOfFlowBox);
             this._computeOutOfFlowPosition(outOfFlowBox);
             this._removeFromLayoutQueue(outOfFlowBox);
+            this.formattingState().clearNeedsLayout(outOfFlowBox);
         }
     }
 
index 1e28faa..406f2ed 100644 (file)
@@ -29,6 +29,8 @@ class FormattingState {
         this.m_formattingRoot = formattingRoot;
         this.m_floatingState = null;
         this.m_displayToLayout = new Map();
+        this.m_needsLayoutBoxList = new Map();
+        this._markSubTreeNeedsLayout(formattingRoot);
     }
 
     formattingRoot() {
@@ -86,4 +88,34 @@ class FormattingState {
         ASSERT(!layoutBox.parent());
         return this.layoutState().initialDisplayBox();
     }
+
+    markNeedsLayout(layoutBox) {
+        this.m_needsLayoutBoxList.set(layoutBox);
+    }
+
+    clearNeedsLayout(layoutBox) {
+        this.m_needsLayoutBoxList.delete(layoutBox);
+    }
+
+    needsLayout(layoutBox) {
+        return this.m_needsLayoutBoxList.has(layoutBox);
+    }
+
+    // This should just be needsLayout()
+    layoutNeeded() {
+        return this.m_needsLayoutBoxList.size;
+    }
+
+    _markSubTreeNeedsLayout(subTreeRoot) {
+        if (!subTreeRoot)
+            return;
+        // Only mark children that actually belong to this formatting context/state.
+        if (this.formattingRoot().isFormattingContextDescendant(subTreeRoot))
+            this.markNeedsLayout(subTreeRoot);
+        if (!subTreeRoot.isContainer() || !subTreeRoot.hasChild())
+            return;
+        for (let child = subTreeRoot.firstChild(); child; child = child.nextSibling())
+            this._markSubTreeNeedsLayout(child);
+    }
+
 }
index 92c29e2..533d7c9 100644 (file)
@@ -64,14 +64,28 @@ class LayoutState {
     }
 
     formattingStateForBox(layoutBox) {
-        // FIXME: We should probably cache this somewhere
-        let formattingState = null;
-        let ancestor = layoutBox.containingBlock();
-        do {
-            formattingState = this.m_formattingStates.get(ancestor);
-            ancestor = ancestor.containingBlock();
-        } while (!formattingState);
-        return formattingState;
+        for (let formattingState of this.formattingStates()) {
+            if (formattingState[0].isFormattingContextDescendant(layoutBox))
+                return formattingState[1];
+        }
+        ASSERT_NOT_REACHED();
+        return null;
+    }
+
+    setNeedsLayout(layoutBox) {
+        let formattingState = this.formattingStateForBox(layoutBox);
+        // Newly created formatting state will obviously mark all the boxes dirty.
+        if (!formattingState)
+            return;
+        formattingState.setNeedsLayout(layoutBox);
+    }
+
+    needsLayout() {
+        for (let formattingState of this.formattingStates()) {
+            if (formattingState[1].layoutNeeded())
+                return true;
+        }
+        return false;
     }
 
     initialDisplayBox() {
index dc4ebce..f2087d4 100644 (file)
@@ -165,6 +165,18 @@ Layout.Box = class Box {
         return false;
     }
 
+    isFormattingContextDescendant(layoutBox) {
+        ASSERT(this.establishesFormattingContext());
+        // If we hit the "this" while climbing up on the containing block chain and we don't pass a formatting context root -> box is part of this box's formatting context.
+        for (let containingBlock = layoutBox.containingBlock(); containingBlock; containingBlock = containingBlock.containingBlock()) {
+            if (containingBlock == this)
+                return true;
+            if (containingBlock.establishesFormattingContext())
+                return false;
+        }
+        return false;
+    }
+
     isPositioned() {
         return this.isOutOfFlowPositioned() || this.isRelativelyPositioned();
     }
index 01bf73d..d164802 100644 (file)
@@ -85,6 +85,18 @@ Layout.Container = class Container extends Layout.Box {
         return !!this.firstInFlowOrFloatChild();
     }
 
+    isContainingBlockDescendant(layoutBox) {
+        ASSERT(layoutBox);
+        // If we hit the "this" while climbing up on the containing block chain -> box is part of this box's containing block subtree.
+        let containingBlock = layoutBox.containingBlock();
+        while (containingBlock) {
+            if (containingBlock == this)
+                return true;
+            containingBlock = containingBlock.containingBlock();
+        }
+        return false;
+    }
+
     outOfFlowDescendants() {
         if (!this.isPositioned())
             return new Array();