[LayoutReloaded] Add support for incremental layout
authorzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 13 Apr 2018 05:27:52 +0000 (05:27 +0000)
committerzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 13 Apr 2018 05:27:52 +0000 (05:27 +0000)
https://bugs.webkit.org/show_bug.cgi?id=184578

Reviewed by Antti Koivisto.

* LayoutReloaded/Layout.js:
(layout):
* LayoutReloaded/LayoutState.js:
(LayoutState.prototype.rootContainer):
(LayoutState.prototype.setNeedsLayoutById):
* LayoutReloaded/Utils.js:
(Utils.layoutTreeDump):
* LayoutReloaded/test/TestHarness.js:
(verifyLayout):
(runLayout):
(verifyLayoutTreeDump): Deleted.
* LayoutReloaded/test/index.html:
* LayoutReloaded/test/simple-incremental-layout-with-static-content.html: Added.

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

Tools/ChangeLog
Tools/LayoutReloaded/Layout.js
Tools/LayoutReloaded/LayoutState.js
Tools/LayoutReloaded/Utils.js
Tools/LayoutReloaded/test/TestHarness.js
Tools/LayoutReloaded/test/index.html
Tools/LayoutReloaded/test/simple-incremental-layout-with-static-content.html [new file with mode: 0644]

index b55a0e9743043c94af67337ee2cb5f600e8802ba..35c3f744945eac491bc8f77ddf752af5f43303d2 100644 (file)
@@ -1,3 +1,24 @@
+2018-04-12  Zalan Bujtas  <zalan@apple.com>
+
+        [LayoutReloaded] Add support for incremental layout
+        https://bugs.webkit.org/show_bug.cgi?id=184578
+
+        Reviewed by Antti Koivisto.
+
+        * LayoutReloaded/Layout.js:
+        (layout):
+        * LayoutReloaded/LayoutState.js:
+        (LayoutState.prototype.rootContainer):
+        (LayoutState.prototype.setNeedsLayoutById):
+        * LayoutReloaded/Utils.js:
+        (Utils.layoutTreeDump):
+        * LayoutReloaded/test/TestHarness.js:
+        (verifyLayout):
+        (runLayout):
+        (verifyLayoutTreeDump): Deleted.
+        * LayoutReloaded/test/index.html:
+        * LayoutReloaded/test/simple-incremental-layout-with-static-content.html: Added.
+
 2018-04-12  Zalan Bujtas  <zalan@apple.com>
 
         [LayoutReloaded] Move root container ownership to layout state
index 5d9df098ac0f079f4d677090749dde32c84f9c74..1775794a7cf8f8d376cf54cd702cf7f6c7c2b7ae 100644 (file)
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-function layout(window, viewportSize) {
-    let treeBuilder = new TreeBuilder();
-    let initialContainingBlock = treeBuilder.createTree(window.document, window.renderTreeStructure);
-    let displayBox = new Display.Box();
-    displayBox.setSize(viewportSize);
-    let layoutState = new LayoutState(initialContainingBlock, displayBox);
-    layoutState.formattingContext(initialContainingBlock).layout();
-    return Utils.layoutTreeDump(initialContainingBlock, layoutState);
+function layout(window, viewportSize, layoutState) {
+    let rootContainer = null;
+    if (layoutState)
+        rootContainer = layoutState.rootContainer();
+    else {
+        // This is top level layout (initial containing block, etc).
+        let treeBuilder = new TreeBuilder();
+        rootContainer = treeBuilder.createTree(window.document, window.renderTreeStructure);
+        let displayBox = new Display.Box();
+        displayBox.setSize(viewportSize);
+        layoutState = new LayoutState(rootContainer, displayBox);
+    }
+    layoutState.formattingContext(rootContainer).layout();
+    return layoutState;
 }
index deb3b3754497213d3e7765edac30a2a1dc6df440..9a20e974f99e5417886cff91f3c2844bcbe542ee 100644 (file)
@@ -36,6 +36,10 @@ class LayoutState {
         return this._formattingContext(this.establishedFormattingState(formattingRoot));
     }
 
+    rootContainer() {
+        return this.m_rootContainer;
+    }
+
     establishedFormattingState(formattingRoot) {
         ASSERT(formattingRoot.establishesFormattingContext());
         let formattingState = this.m_formattingStates.get(formattingRoot);
@@ -57,6 +61,10 @@ class LayoutState {
         return null;
     }
 
+    // This is for testing only.
+    setNeedsLayoutById(layoutBoxId) {
+    }
+
     setNeedsLayout(layoutBox) {
         let formattingState = this.formattingStateForBox(layoutBox);
         // Newly created formatting state will obviously mark all the boxes dirty.
index 0f6a8b4a526b1373979f724347522f4fd44a8491..88abfb47a5e0eb8ccb3d87147958a23516766307 100644 (file)
@@ -549,8 +549,8 @@ class Utils {
     }
 
     // "RenderView at (0,0) size 1317x366\n HTML RenderBlock at (0,0) size 1317x116\n  BODY RenderBody at (8,8) size 1301x100\n   DIV RenderBlock at (0,0) size 100x100\n";
-    static layoutTreeDump(initialContainingBlock, layoutState) {
-        return this._dumpBox(layoutState, initialContainingBlock, 1) + this._dumpTree(layoutState, initialContainingBlock, 2);
+    static layoutTreeDump(layoutState) {
+        return this._dumpBox(layoutState, layoutState.rootContainer(), 1) + this._dumpTree(layoutState, layoutState.rootContainer(), 2);
     }
 
     static _dumpBox(layoutState, box, level) {
index eac4adfba18f1810c2dfd441f7a653d7f4367e5b..91aed4a6d9d7cdc382b505050683c0a36816f4db 100644 (file)
@@ -23,8 +23,9 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-function verifyLayoutTreeDump(renderTreeDump, layoutTreeDump)
+function verifyLayout(renderTreeDump)
 {
+    let layoutTreeDump = Utils.layoutTreeDump(layoutState);
     console.log("WebKit:\n" + renderTreeDump + renderTreeDump.length);
     console.log("Reloaded:\n" + layoutTreeDump + layoutTreeDump.length);
     if (renderTreeDump == layoutTreeDump)
@@ -32,9 +33,8 @@ function verifyLayoutTreeDump(renderTreeDump, layoutTreeDump)
     return false;
 }
 
-function runLayout(testWindow) {
+function runLayout(testWindow, layoutState) {
     testWindow.document.body.offsetHeight;
     let viewportSize = new LayoutSize(parseFloat(testWindow.innerWidth), parseFloat(testWindow.innerHeight));
-    let layoutTreeDump = layout(testWindow, viewportSize);
-    return verifyLayoutTreeDump(testWindow.simplifiedRenderTree, layoutTreeDump);
+    return layout(testWindow, viewportSize, layoutState);
 }
index a7a8c87e81d2c5ee5b94a63cf615d937ebb00089..6d471774383fbe2a640071808642767840fc2d96 100644 (file)
@@ -8,6 +8,7 @@ iframe {
 </style>
 <script>
 let testFiles = [
+    "simple-incremental-layout-with-static-content.html",
     "relative-simple.html",
     "relative-bottom.html",
     "relative-right.html",
@@ -116,45 +117,116 @@ addJS("../FormattingContext/BlockFormatting/BlockMarginCollapse.js");
 addJS("../FormattingContext/InlineFormatting/InlineFormattingContext.js");
 addJS("../FormattingContext/InlineFormatting/Line.js");
 
-let failedTests = [];
+let failedTests = new Array();
 
-function printResult(files, currentTestIndex) {
-    let resultContent = currentTestIndex != null ? ("Testing..." + currentTestIndex + " out of " + files.length) : ("Number of tests: " + files.length) ;
-    resultContent += "<br><br>Passed: " + ((currentTestIndex != null ? currentTestIndex : files.length) - failedTests.length) + "<br>Failed: " + failedTests.length;
+let isRunningAsyncTest = false;
+let tests;
+let currentTestIndex = 0;
+let subTests = 0;
+let layoutState = null;
+
+function collectRenderersWithNeedsLayout() {
+    // TODO: implement it in WebKit.
+    return null;
+}
+
+function runAndVerifyLayout(window) {
+    layoutState = runLayout(window, layoutState);
+    return verifyLayout(window.simplifiedRenderTree);
+}
+
+class TestRunner {
+    waitUntilDone() {
+        isRunningAsyncTest = true;
+    }
+
+    notifyDone() {
+        let testFrame = document.getElementById("testFrame");
+        let passed = runAndVerifyLayout(testFrame.contentWindow);
+        finishAndMoveToNextTest(passed);
+    }
+
+    forceLayout() {
+        let testFrame = document.getElementById("testFrame");
+        let needsLayoutRenderers = collectRenderersWithNeedsLayout(); //testFrame.contentWindow.collectRenderersWithNeedsLayout();
+        testFrame.contentWindow.document.body.offsetWidth;
+        if (layoutState) {
+            for (let rendererId of needsLayoutRenderers)
+                layoutState.setNeedsLayoutById(rendererId);
+        }
+        let passed = runAndVerifyLayout(testFrame.contentWindow);
+        ++subTests;
+        if (!passed && failedTests.indexOf(currentTestIndex) == -1)
+            failedTests.push(currentTestIndex);
+        printIntermediateResult();
+    }
+
+}
+
+function printFinalResult() {
+    let resultContent = "Number of tests: " + tests.length;
+    resultContent += "<br><br>Passed: " + (tests.length - failedTests.length) + "<br>Failed: " + failedTests.length;
     if (failedTests.length > 0) {
         resultContent += "<br><br>Failed cases:"
         failedTests.forEach(function(item) {
-            resultContent += "<br><a href=\"" + files[item] + "\">" + files[item] + "</a>";
+            resultContent += "<br><a href=\"" + tests[item] + "\">" + tests[item] + "</a>";
         });
     }
     result.innerHTML = resultContent;
 }
 
-function runTest(files, currentTestIndex) {
-    printResult(files, currentTestIndex);
+function printIntermediateResult() {
+    let resultContent = "Testing..." + currentTestIndex + (subTests > 0 ? "(" + subTests + ")" : "") + " out of " + tests.length;
+    resultContent += "<br><br>Passed: " + (currentTestIndex - failedTests.length) + "<br>Failed: " + failedTests.length;
+    if (failedTests.length > 0) {
+        resultContent += "<br><br>Failed cases:"
+        failedTests.forEach(function(item) {
+            resultContent += "<br><a href=\"" + tests[item] + "\">" + tests[item] + "</a>";
+        });
+    }
+    result.innerHTML = resultContent;
+}
+
+function finishAndMoveToNextTest(passed) {
+    let testFrame = document.getElementById("testFrame");
+    testFrame.remove();
+    initialContainingBlock = null;
+    layoutState = null;
+    subTests = 0;
+    isRunningAsyncTest = false;
+    if (!passed)
+        failedTests.push(currentTestIndex);
+    setTimeout(function() {
+        if (currentTestIndex < tests.length - 1) {
+            printIntermediateResult();
+            ++currentTestIndex;
+            runTest(tests);
+            return;
+        }
+        printFinalResult();
+    }, 0);
+}
+
+function runTest() {
     let iframe = document.createElement("iframe");
-    iframe.src = files[currentTestIndex];
+    iframe.src = tests[currentTestIndex];
     iframe.id = "testFrame";
     iframe.width = window.innerWidth;
     iframe.height = window.innerHeight;
     iframe.onload = function() {
+        if (isRunningAsyncTest)
+            return;
         let testFrame = document.getElementById("testFrame");
-        let passed = runLayout(testFrame.contentWindow);
-        testFrame.remove();
-        if (!passed)
-            failedTests.push(currentTestIndex);
-        setTimeout(function() {
-            if (currentTestIndex < files.length - 1)
-                runTest(files, ++currentTestIndex);
-            else
-                printResult(files);
-        }, 0);
+        let passed = runAndVerifyLayout(testFrame.contentWindow);
+        finishAndMoveToNextTest(passed, tests);
     };
     document.body.appendChild(iframe);
+    iframe.contentWindow.testRunner = new TestRunner();
 }
 
 function runTests() {
-    runTest(debugThis.length ? debugThis : testFiles, 0);
+    tests = debugThis.length ? debugThis : testFiles;
+    runTest();
 }
 </script>
 </head>
diff --git a/Tools/LayoutReloaded/test/simple-incremental-layout-with-static-content.html b/Tools/LayoutReloaded/test/simple-incremental-layout-with-static-content.html
new file mode 100644 (file)
index 0000000..aac8dd9
--- /dev/null
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div style="width: 100px; height: 100px; border: 1px solid green"></div>
+<script>
+testRunner.waitUntilDone();
+setInterval(function() {
+    testRunner.forceLayout();
+}, 10);
+
+setTimeout(function() {
+    testRunner.notifyDone();
+}, 100);
+</script>
+</body>
+</html>