Bug 1646629 - Set updateCanvasPending explicitly when reftest-wait was removed and take a snapshot of the whole screen if reftest-no-flush is specified. r=mattwoodrow,tnikkel
authorHiroyuki Ikezoe <hikezoe.birchill@mozilla.com>
Sat, 04 Jul 2020 08:01:40 +0000
changeset 538796 1729ebd90c6849b512df63c91fe65b6f7441eeb3
parent 538795 4e24f7b970651dc87aa1449450f4eba8695e46db
child 538797 10c9e57fb4f8c70d4772a7809ba3d652536b984d
push id37569
push userncsoregi@mozilla.com
push dateSat, 04 Jul 2020 21:34:51 +0000
treeherdermozilla-central@ec01c146f756 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow, tnikkel
bugs1646629
milestone80.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 1646629 - Set updateCanvasPending explicitly when reftest-wait was removed and take a snapshot of the whole screen if reftest-no-flush is specified. r=mattwoodrow,tnikkel That's because we no longer fire MozAfterPaint event for changes by animations on the compositor. Differential Revision: https://phabricator.services.mozilla.com/D80157
layout/reftests/reftest-sanity/reftest-no-flush-ref.html
layout/reftests/reftest-sanity/reftest-no-flush.html
layout/reftests/reftest-sanity/reftest.list
layout/tools/reftest/reftest-content.js
new file mode 100644
--- /dev/null
+++ b/layout/reftests/reftest-sanity/reftest-no-flush-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<style>
+html {
+  overflow: hidden;
+}
+body {
+  margin: 0px;
+  padding: 0px;
+}
+#target {
+  width: 100px;
+  height: 100px;
+  position: absolute;
+  transform: translateX(100px);
+  background-color: green;
+}
+</style>
+<div id="target"></div>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/reftest-sanity/reftest-no-flush.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html class="reftest-wait reftest-no-flush">
+<style>
+html {
+  /* Suppress scrollbars to avoid periodical unthrottling for transform */
+  /* animations on the compositor. */
+  overflow: hidden;
+}
+body {
+  margin: 0px;
+  padding: 0px;
+}
+@keyframes anim {
+  0% { transform: translateX(100px); }
+  100% { transform: translateX(100px); }
+}
+#target {
+  width: 100px;
+  height: 100px;
+  position: absolute;
+  background-color: green;
+}
+</style>
+<div id="target"></div>
+<script>
+document.addEventListener('MozReftestInvalidate', () => {
+  // Set a bit longer animation delay to avoid painting the initial animation
+  // style on the main thread.
+  target.style.animation = "anim 100s 1s";
+  target.addEventListener("animationstart", () => {
+    requestAnimationFrame(() => {
+      requestAnimationFrame(() => {
+        document.documentElement.classList.remove("reftest-wait");
+      });
+    });
+  });
+}, { once: true });
+</script>
+</html>
--- a/layout/reftests/reftest-sanity/reftest.list
+++ b/layout/reftests/reftest-sanity/reftest.list
@@ -206,8 +206,10 @@ fails-if(layerChecksEnabled) != reftest-
 != reftest-assigned-layer-pass.html about:blank
 fails-if(layerChecksEnabled) != reftest-assigned-layer-fail-1.html about:blank
 fails-if(layerChecksEnabled) != reftest-assigned-layer-fail-2.html about:blank
 fails-if(layerChecksEnabled) != reftest-assigned-layer-fail-3.html about:blank
 fails-if(layerChecksEnabled) != reftest-assigned-layer-fail-4.html about:blank
 
 # reftest-resolution
 pref(apz.allow_zooming,true) == reftest-resolution.html reftest-resolution-ref.html
+
+== reftest-no-flush.html reftest-no-flush-ref.html
--- a/layout/tools/reftest/reftest-content.js
+++ b/layout/tools/reftest/reftest-content.js
@@ -438,16 +438,24 @@ function shouldWaitForReftestWaitRemoval
 function shouldSnapshotWholePage(contentRootElement) {
     // use getAttribute because className works differently in HTML and SVG
     return contentRootElement &&
            contentRootElement.hasAttribute('class') &&
            contentRootElement.getAttribute('class').split(/\s+/)
                              .includes("reftest-snapshot-all");
 }
 
+function shouldNotFlush(contentRootElement) {
+    // use getAttribute because className works differently in HTML and SVG
+    return contentRootElement &&
+           contentRootElement.hasAttribute('class') &&
+           contentRootElement.getAttribute('class').split(/\s+/)
+                             .includes("reftest-no-flush");
+}
+
 function getNoPaintElements(contentRootElement) {
     return contentRootElement.getElementsByClassName('reftest-no-paint');
 }
 function getNoDisplayListElements(contentRootElement) {
     return contentRootElement.getElementsByClassName('reftest-no-display-list');
 }
 function getDisplayListElements(contentRootElement) {
     return contentRootElement.getElementsByClassName('reftest-display-list');
@@ -849,16 +857,26 @@ function WaitForTestEnd(contentRootEleme
             LogInfo("MakeProgress: STATE_WAITING_FOR_REFTEST_WAIT_REMOVAL");
             CheckForLivenessOfContentRootElement();
             if (shouldWaitForReftestWaitRemoval(contentRootElement)) {
                 gFailureReason = "timed out waiting for reftest-wait to be removed";
                 LogInfo("MakeProgress: waiting for reftest-wait to be removed");
                 return;
             }
 
+            if (shouldNotFlush(contentRootElement)) {
+              // If reftest-no-flush is specified, we need to set
+              // updateCanvasPending explicitly to take the latest snapshot
+              // since animation changes on the compositor thread don't invoke
+              // any MozAfterPaint events at all.
+              // NOTE: We don't add any rects to updateCanvasRects here since
+              // SendUpdateCanvasForEvent() will handle this case properly
+              // without any rects.
+              updateCanvasPending = true;
+            }
             // Try next state
             state = STATE_WAITING_FOR_SPELL_CHECKS;
             MakeProgress();
             return;
 
         case STATE_WAITING_FOR_SPELL_CHECKS:
             LogInfo("MakeProgress: STATE_WAITING_FOR_SPELL_CHECKS");
             if (numPendingSpellChecks) {
@@ -881,19 +899,17 @@ function WaitForTestEnd(contentRootEleme
                 } else {
                     MakeProgress();
                 }
             };
             os.addObserver(flushWaiter, "apz-repaints-flushed");
 
             var willSnapshot = IsSnapshottableTestType();
             CheckForLivenessOfContentRootElement();
-            var noFlush =
-                !(contentRootElement &&
-                  contentRootElement.classList.contains("reftest-no-flush"));
+            var noFlush = !shouldNotFlush(contentRootElement);
             if (noFlush && willSnapshot && windowUtils().flushApzRepaints()) {
                 LogInfo("MakeProgress: done requesting APZ flush");
             } else {
                 LogInfo("MakeProgress: APZ flush not required");
                 flushWaiter(null, null, null);
             }
             return;
 
@@ -1370,17 +1386,17 @@ function SynchronizeForSnapshot(flags)
     if (!IsSnapshottableTestType()) {
         return Promise.resolve(undefined);
     }
 
     if (flags & SYNC_ALLOW_DISABLE) {
         var docElt = content.document.documentElement;
         if (docElt &&
             (docElt.hasAttribute("reftest-no-sync-layers") ||
-             docElt.classList.contains("reftest-no-flush"))) {
+             shouldNotFlush(docElt))) {
             LogInfo("Test file chose to skip SynchronizeForSnapshot");
             return Promise.resolve(undefined);
         }
     }
 
     let browsingContext = content.docShell.browsingContext;
     let promise = content.windowGlobalChild.getActor("ReftestFission").sendQuery("UpdateLayerTree", {browsingContext});
     return promise.then(function (result) {
@@ -1639,20 +1655,23 @@ function SendUpdateCanvasForEvent(forURL
           return promise.then(function () {
             sendAsyncMessage("reftest:UpdateWholeCanvasForInvalidation");
           });
       }
       return Promise.resolve(undefined);
     }
 
     var message;
-    if (gIsWebRenderEnabled && !windowUtils().isMozAfterPaintPending) {
-        // Webrender doesn't have invalidation, so we just invalidate the whole
-        // screen once we don't have anymore paints pending. This will force
-        // the snapshot.
+
+    if ((gIsWebRenderEnabled || shouldNotFlush(contentRootElement)) &&
+        !windowUtils().isMozAfterPaintPending) {
+        // Webrender doesn't have invalidation, and animations on the compositor
+        // don't invoke any MozAfterEvent which means we have no invalidated
+        // rect so we just invalidate the whole screen once we don't have
+        // anymore paints pending. This will force the snapshot.
 
         LogInfo("Webrender enabled, sending update whole canvas for invalidation");
         message = "reftest:UpdateWholeCanvasForInvalidation";
     } else {
         LogInfo("SendUpdateCanvasForEvent with " + rectList.length + " rects");
         for (var i = 0; i < rectList.length; ++i) {
             var r = rectList[i];
             // Set left/top/right/bottom to "device pixel" boundaries