Bug 1593170. Make the reftest-content.js functions FlushRendering and SynchronizeForSnapshot work on Fission child oop iframes. r=mattwoodrow,kmag
☠☠ backed out by bdca724cf3a2 ☠ ☠
authorTimothy Nikkel <tnikkel@gmail.com>
Mon, 18 Nov 2019 00:45:07 +0000
changeset 502361 78d380a2241afa5c60a90ac8ceca9b3108b99345
parent 502360 a78c989f5538874bf4c5a0c98b199441ed6921f6
child 502362 a004de6493422688953b5b8ea2fc5e1522240f47
push id114172
push userdluca@mozilla.com
push dateTue, 19 Nov 2019 11:31:10 +0000
treeherdermozilla-inbound@b5c5ba07d3db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow, kmag
bugs1593170
milestone72.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1593170. Make the reftest-content.js functions FlushRendering and SynchronizeForSnapshot work on Fission child oop iframes. r=mattwoodrow,kmag This changes them to return a promise that resolves when the work is done, but we still need to change the callers to handle this new return type and do the right thing when these functions do their work async-ly. To do this we add a JSWindowActor called ReftestFission. reftest-content.js communicates with this actor via reftest.jsm. Differential Revision: https://phabricator.services.mozilla.com/D51343
layout/tools/reftest/ReftestFissionChild.jsm
layout/tools/reftest/ReftestFissionParent.jsm
layout/tools/reftest/jar.mn
layout/tools/reftest/reftest-content.js
layout/tools/reftest/reftest.jsm
new file mode 100644
--- /dev/null
+++ b/layout/tools/reftest/ReftestFissionChild.jsm
@@ -0,0 +1,75 @@
+var EXPORTED_SYMBOLS = ["ReftestFissionChild"];
+
+class ReftestFissionChild extends JSWindowActorChild {
+
+  receiveMessage(msg) {
+    switch (msg.name) {
+      case "UpdateLayerTree":
+        let errorString = null;
+        try {
+          if (this.manager.isProcessRoot) {
+            this.contentWindow.windowUtils.updateLayerTree();
+          }
+        } catch (e) {
+          errorString = "updateLayerTree failed: " + e;
+        }
+        return Promise.resolve({errorString});
+      case "FlushRendering":
+        let errorStrings = [];
+        let warningStrings = [];
+        let infoStrings = [];
+
+        try {
+          let ignoreThrottledAnimations = msg.data.ignoreThrottledAnimations;
+
+          if (this.manager.isProcessRoot) {
+            var anyPendingPaintsGeneratedInDescendants = false;
+
+            function flushWindow(win) {
+              var utils = win.windowUtils;
+              var afterPaintWasPending = utils.isMozAfterPaintPending;
+
+              var root = win.document.documentElement;
+              if (root && !root.classList.contains("reftest-no-flush")) {
+                try {
+                  if (ignoreThrottledAnimations) {
+                    utils.flushLayoutWithoutThrottledAnimations();
+                  } else {
+                    root.getBoundingClientRect();
+                  }
+                } catch (e) {
+                  warningStrings.push("flushWindow failed: " + e + "\n")
+                }
+              }
+
+              if (!afterPaintWasPending && utils.isMozAfterPaintPending) {
+                infoStrings.push("FlushRendering generated paint for window " + win.location.href)
+                anyPendingPaintsGeneratedInDescendants = true;
+              }
+
+              for (let i = 0; i < win.frames.length; ++i) {
+                try {
+                  if (!Cu.isRemoteProxy(win.frames[i])) {
+                    flushWindow(win.frames[i]);
+                  }
+                } catch (e) {
+                  Cu.reportError(e);
+                }
+              }
+            }
+
+            flushWindow(this.contentWindow);
+
+            if (anyPendingPaintsGeneratedInDescendants &&
+                !this.contentWindow.windowUtils.isMozAfterPaintPending) {
+              warningStrings.push("Internal error: descendant frame generated a MozAfterPaint event, but the root document doesn't have one!");
+            }
+
+          }
+        } catch (e) {
+          errorStrings.push("flushWindow failed: " + e);
+        }
+        return Promise.resolve({errorStrings, warningStrings, infoStrings});
+    }
+  }
+}
new file mode 100644
--- /dev/null
+++ b/layout/tools/reftest/ReftestFissionParent.jsm
@@ -0,0 +1,102 @@
+var EXPORTED_SYMBOLS = ["ReftestFissionParent"];
+
+class ReftestFissionParent extends JSWindowActorParent {
+
+  tellChildrenToFlushRendering(browsingContext, ignoreThrottledAnimations) {
+    let promises = [];
+    this.tellChildrenToFlushRenderingRecursive(browsingContext, ignoreThrottledAnimations, promises);
+    return Promise.allSettled(promises);
+  }
+
+  tellChildrenToFlushRenderingRecursive(browsingContext, ignoreThrottledAnimations, promises) {
+    let cwg = browsingContext.currentWindowGlobal;
+    if (cwg && cwg.isProcessRoot) {
+      let a = cwg.getActor("ReftestFission");
+      if (a) {
+        let responsePromise = a.sendQuery("FlushRendering", {ignoreThrottledAnimations});
+        promises.push(responsePromise);
+      }
+    }
+
+    for (let context of browsingContext.getChildren()) {
+      this.tellChildrenToFlushRenderingRecursive(context, ignoreThrottledAnimations, promises);
+    }
+  }
+
+  tellChildrenToUpdateLayerTree(browsingContext) {
+    let promises = [];
+    this.tellChildrenToUpdateLayerTreeRecursive(browsingContext, promises);
+    return Promise.allSettled(promises);
+  }
+
+  tellChildrenToUpdateLayerTreeRecursive(browsingContext, promises) {
+    let cwg = browsingContext.currentWindowGlobal;
+    if (cwg && cwg.isProcessRoot) {
+      let a = cwg.getActor("ReftestFission");
+      if (a) {
+        let responsePromise = a.sendQuery("UpdateLayerTree");
+        promises.push(responsePromise);
+      }
+    }
+
+    for (let context of browsingContext.getChildren()) {
+      this.tellChildrenToUpdateLayerTreeRecursive(context, promises);
+    }
+  }
+
+  receiveMessage(msg) {
+    switch (msg.name) {
+      case "FlushRendering":
+      {
+        let promise = this.tellChildrenToFlushRendering(msg.data.browsingContext, msg.data.ignoreThrottledAnimations);
+        return promise.then(function (results) {
+          let errorStrings = [];
+          let warningStrings = [];
+          let infoStrings = [];
+          for (let r of results) {
+            if (r.status != "fulfilled") {
+              if (r.status == "pending") {
+                errorStrings.push("FlushRendering sendQuery to child promise still pending?");
+              } else {
+                // We expect actors to go away causing sendQuery's to fail, so
+                // just note it.
+                infoStrings.push("FlushRendering sendQuery to child promise rejected: " + r.reason);
+              }
+              continue;
+            }
+
+            errorStrings.concat(r.value.errorStrings);
+            warningStrings.concat(r.value.warningStrings);
+            infoStrings.concat(r.value.infoStrings);
+          }
+          return {errorStrings, warningStrings, infoStrings};
+        });
+      }
+      case "UpdateLayerTree":
+      {
+        let promise = this.tellChildrenToUpdateLayerTree(msg.data.browsingContext);
+        return promise.then(function (results) {
+          let errorStrings = [];
+          for (let r of results) {
+            if (r.status != "fulfilled") {
+              if (r.status == "pending") {
+                errorStrings.push("UpdateLayerTree sendQuery to child promise still pending?");
+              } else {
+                // We expect actors to go away causing sendQuery's to fail, so
+                // just note it.
+                infoStrings.push("UpdateLayerTree sendQuery to child promise rejected: " + r.reason);
+              }
+              continue;
+            }
+
+            if (r.value.errorString != null) {
+              errorStrings.push(r.value.errorString);
+            }
+          }
+          return errorStrings;
+        });
+      }
+    }
+  }
+
+}
--- a/layout/tools/reftest/jar.mn
+++ b/layout/tools/reftest/jar.mn
@@ -43,16 +43,18 @@ reftest.jar:
   content/crashtests/layout/style/crashtests (../../../layout/style/crashtests/*)
   content/crashtests/gfx/tests/crashtests (../../../gfx/tests/crashtests/*)
   content/crashtests/accessible/tests/crashtests (../../../accessible/tests/crashtests/*)
   content/crashtests/view/crashtests (../../../view/crashtests/*)
   content/crashtests/widget/cocoa/crashtests (../../../widget/cocoa/crashtests/*)
 
   res/globals.jsm (globals.jsm)
   res/reftest-content.js (reftest-content.js)
+  res/ReftestFissionParent.jsm (ReftestFissionParent.jsm)
+  res/ReftestFissionChild.jsm (ReftestFissionChild.jsm)
   res/AsyncSpellCheckTestHelper.jsm (../../../editor/AsyncSpellCheckTestHelper.jsm)
   res/httpd.jsm (../../../netwerk/test/httpserver/httpd.js)
   res/StructuredLog.jsm (../../../testing/modules/StructuredLog.jsm)
   res/PerTestCoverageUtils.jsm (../../../tools/code-coverage/PerTestCoverageUtils.jsm)
   res/input.css (../../../editor/reftests/xul/input.css)
   res/progress.css (../../../layout/reftests/forms/progress/style.css)
 *  res/manifest.jsm (manifest.jsm)
 *  res/reftest.jsm (reftest.jsm)
--- a/layout/tools/reftest/reftest-content.js
+++ b/layout/tools/reftest/reftest-content.js
@@ -504,55 +504,34 @@ const STATE_WAITING_FOR_SPELL_CHECKS = 2
 // move to the next state.
 const STATE_WAITING_FOR_APZ_FLUSH = 3;
 // When all MozAfterPaint events and all explicit paint waits are flushed, we're
 // done and can move to the COMPLETED state.
 const STATE_WAITING_TO_FINISH = 4;
 const STATE_COMPLETED = 5;
 
 function FlushRendering(aFlushMode) {
-    var anyPendingPaintsGeneratedInDescendants = false;
-
-    function flushWindow(win) {
-        var utils = win.windowUtils;
-        var afterPaintWasPending = utils.isMozAfterPaintPending;
-
-        var root = win.document.documentElement;
-        if (root && !root.classList.contains("reftest-no-flush")) {
-            try {
-                if (aFlushMode === FlushMode.IGNORE_THROTTLED_ANIMATIONS) {
-                    utils.flushLayoutWithoutThrottledAnimations();
-                } else {
-                    root.getBoundingClientRect();
-                }
-            } catch (e) {
-                LogWarning("flushWindow failed: " + e + "\n");
-            }
+    let browsingContext = content.docShell.browsingContext;
+    let ignoreThrottledAnimations = (aFlushMode === FlushMode.IGNORE_THROTTLED_ANIMATIONS);
+    let promise = content.getWindowGlobalChild().getActor("ReftestFission").sendQuery("FlushRendering", {browsingContext, ignoreThrottledAnimations});
+    return promise.then(function(result) {
+        for (let errorString of result.errorStrings) {
+            LogError(errorString);
         }
-
-        if (!afterPaintWasPending && utils.isMozAfterPaintPending) {
-            LogInfo("FlushRendering generated paint for window " + win.location.href);
-            anyPendingPaintsGeneratedInDescendants = true;
+        for (let warningString of result.warningStrings) {
+            LogWarning(warningString);
+        }
+        for (let infoString of result.infoStrings) {
+            LogInfo(infoString);
         }
-
-        for (var i = 0; i < win.frames.length; ++i) {
-            try {
-                flushWindow(win.frames[i]);
-            } catch (e) {
-                Cu.reportError(e);
-            }
-        }
-    }
-
-    flushWindow(content);
-
-    if (anyPendingPaintsGeneratedInDescendants &&
-        !windowUtils().isMozAfterPaintPending) {
-        LogWarning("Internal error: descendant frame generated a MozAfterPaint event, but the root document doesn't have one!");
-    }
+    }, function(reason) {
+        // We expect actors to go away causing sendQuery's to fail, so
+        // just note it.
+        LogInfo("FlushRendering sendQuery to parent rejected: " + reason);
+    });
 }
 
 function WaitForTestEnd(contentRootElement, inPrintMode, spellCheckedElements, forURL) {
     var stopAfterPaintReceived = false;
     var currentDoc = content.document;
     var state = STATE_WAITING_TO_FIRE_INVALIDATE_EVENT;
 
     function AfterPaintListener(event) {
@@ -1117,16 +1096,25 @@ function DoAssertionCheck()
 function LoadURI(uri)
 {
     let loadURIOptions = {
       triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
     };
     webNavigation().loadURI(uri, loadURIOptions);
 }
 
+function LogError(str)
+{
+    if (gVerbose) {
+        sendSyncMessage("reftest:Log", { type: "error", msg: str });
+    } else {
+        sendAsyncMessage("reftest:Log", { type: "error", msg: str });
+    }
+}
+
 function LogWarning(str)
 {
     if (gVerbose) {
         sendSyncMessage("reftest:Log", { type: "warning", msg: str });
     } else {
         sendAsyncMessage("reftest:Log", { type: "warning", msg: str });
     }
 }
@@ -1161,22 +1149,37 @@ function SynchronizeForSnapshot(flags)
         if (docElt &&
             (docElt.hasAttribute("reftest-no-sync-layers") ||
              docElt.classList.contains("reftest-no-flush"))) {
             LogInfo("Test file chose to skip SynchronizeForSnapshot");
             return;
         }
     }
 
-    windowUtils().updateLayerTree();
+    let browsingContext = content.docShell.browsingContext;
+    let promise = content.getWindowGlobalChild().getActor("ReftestFission").sendQuery("UpdateLayerTree", {browsingContext});
+    return promise.then(function (result) {
+        for (let errorString of result) {
+            LogError(errorString);
+        }
 
-    // Setup async scroll offsets now, because any scrollable layers should
-    // have had their AsyncPanZoomControllers created.
-    setupAsyncScrollOffsets({allowFailure:false});
-    setupAsyncZoom({allowFailure:false});
+        // Setup async scroll offsets now, because any scrollable layers should
+        // have had their AsyncPanZoomControllers created.
+        setupAsyncScrollOffsets({allowFailure:false});
+        setupAsyncZoom({allowFailure:false});
+    }, function(reason) {
+        // We expect actors to go away causing sendQuery's to fail, so
+        // just note it.
+        LogInfo("UpdateLayerTree sendQuery to parent rejected: " + reason);
+
+        // Setup async scroll offsets now, because any scrollable layers should
+        // have had their AsyncPanZoomControllers created.
+        setupAsyncScrollOffsets({allowFailure:false});
+        setupAsyncZoom({allowFailure:false});
+    });
 }
 
 function RegisterMessageListeners()
 {
     addMessageListener(
         "reftest:Clear",
         function (m) { RecvClear() }
     );
--- a/layout/tools/reftest/reftest.jsm
+++ b/layout/tools/reftest/reftest.jsm
@@ -1505,16 +1505,27 @@ function RegisterMessageListenersAndLoad
         function (m) { RecvUpdateWholeCanvasForInvalidation(); }
     );
     g.browserMessageManager.addMessageListener(
         "reftest:ExpectProcessCrash",
         function (m) { RecvExpectProcessCrash(); }
     );
 
     g.browserMessageManager.loadFrameScript("resource://reftest/reftest-content.js", true, true);
+
+    ChromeUtils.registerWindowActor("ReftestFission", {
+        parent: {
+          moduleURI: "resource://reftest/ReftestFissionParent.jsm",
+        },
+        child: {
+          moduleURI: "resource://reftest/ReftestFissionChild.jsm",
+        },
+        allFrames: true,
+        includeChrome: true,
+    });
 }
 
 function RecvAssertionCount(count)
 {
     DoAssertionCheck(count);
 }
 
 function RecvContentReady(info)
@@ -1568,16 +1579,19 @@ function RecvInitCanvasWithSnapshot()
 
 function RecvLog(type, msg)
 {
     msg = "[CONTENT] " + msg;
     if (type == "info") {
         TestBuffer(msg);
     } else if (type == "warning") {
         logger.warning(msg);
+    } else if (type == "error") {
+        logger.error("REFTEST TEST-UNEXPECTED-FAIL | " + g.currentURL + " | " + msg + "\n");
+        ++g.testResults.Exception;
     } else {
         logger.error("REFTEST TEST-UNEXPECTED-FAIL | " + g.currentURL + " | unknown log type " + type + "\n");
         ++g.testResults.Exception;
     }
 }
 
 function RecvScriptResults(runtimeMs, error, results)
 {