Bug 1443358 - Consider that the target frame is scrolled out if scrollable parent frame size is empty. r?birtles draft
authorHiroyuki Ikezoe <hikezoe@mozilla.com>
Mon, 02 Apr 2018 13:34:14 +0900
changeset 775975 9c4659b0db34cd8bc5cffefbf9828b641dc1ac35
parent 775974 28bb6a2438aeb88b387f10aace4870ef179dd78c
push id104775
push userhikezoe@mozilla.com
push dateMon, 02 Apr 2018 07:32:20 +0000
reviewersbirtles
bugs1443358
milestone61.0a1
Bug 1443358 - Consider that the target frame is scrolled out if scrollable parent frame size is empty. r?birtles This patch adds three test cases; 1) Animation on position:absolute element in a zero-height iframe This animation should be throttled. 2) Animation on a non-zero width and hight position:absolute element but whose parent has a zero height This animation should NOT be throttled since the animation is visible 3) Animation on a zero-height position:absolute element whose parent also has zero height. This animation should be throttled since the animation is invisible The first test fails without this fix and passes with the fix. The second one passes regardless of the fix The third one is marked as 'todo' since it doesn't pass with this fix. MozReview-Commit-ID: 8pNUFQ71ivj
dom/animation/test/mozilla/file_restyles.html
layout/generic/nsFrame.cpp
--- a/dom/animation/test/mozilla/file_restyles.html
+++ b/dom/animation/test/mozilla/file_restyles.html
@@ -1640,16 +1640,88 @@ waitForAllPaints(() => {
       is(markers.length, 0,
          'CSS animation on an out-of-view position:fixed element should be ' +
          'throttled');
 
       await ensureElementRemoval(div);
     }
   );
 
+  add_task(
+    async function throttling_position_absolute_animations_in_collapsed_iframe() {
+      var iframe = document.createElement('iframe');
+      iframe.setAttribute('srcdoc', '<div id="target"></div>');
+      iframe.style.height = '0px';
+      document.documentElement.appendChild(iframe);
+
+      await new Promise(resolve => {
+        iframe.addEventListener('load', () => {
+          resolve();
+        });
+      });
+
+      const target = iframe.contentDocument.getElementById("target");
+      target.style= 'position: absolute; top: 50%; width: 100px; height: 100px';
+
+      var animation = target.animate({ opacity: [0, 1] },
+                                     { duration: 100 * MS_PER_SEC,
+                                       iterations: Infinity });
+      await animation.ready;
+
+      var markers = await observeStylingInTargetWindow(iframe.contentWindow, 5);
+      is(markers.length, 0,
+         'Animation on position:absolute element in collapsed iframe should ' +
+         'be throttled');
+
+      await ensureElementRemoval(iframe);
+    }
+  );
+
+  add_task(
+    async function position_absolute_animations_in_collapsed_element() {
+      var parent = addDiv(null, { style: 'overflow: scroll; height: 0px;' });
+      var target = addDiv(null,
+                          { style: 'animation: background-color 100s infinite;' +
+                                   'position: absolute; top: 50%;' +
+                                   'width: 100px; height: 100px;' });
+      parent.appendChild(target);
+
+      var animation = target.getAnimations()[0];
+      await animation.ready;
+
+      const expectedRestyleCount = tweakExpectedRestyleCount(animation, 5);
+      var markers = await observeStyling(5);
+      is(markers.length, expectedRestyleCount,
+         'Animation on position:absolute element in collapsed element ' +
+         'should not be throttled');
+
+      await ensureElementRemoval(parent);
+    }
+  );
+
+  add_task(
+    async function throttling_position_absolute_animations_in_collapsed_element() {
+      var parent = addDiv(null, { style: 'overflow: scroll; height: 0px;' });
+      var target = addDiv(null,
+                          { style: 'animation: background-color 100s infinite;' +
+                                   'position: absolute; top: 50%;' });
+      parent.appendChild(target);
+
+      var animation = target.getAnimations()[0];
+      await animation.ready;
+
+      var markers = await observeStyling(5);
+      todo_is(markers.length, 0,
+              'Animation on collapsed position:absolute element in collapsed ' +
+              'element should be throttled');
+
+      await ensureElementRemoval(parent);
+    }
+  );
+
   add_task_if_omta_enabled(
     async function no_restyling_for_compositor_animation_on_unrelated_style_change() {
       var div = addDiv(null);
       var animation = div.animate({ opacity: [0, 1] }, 100 * MS_PER_SEC);
 
       await animation.ready;
       ok(SpecialPowers.wrap(animation).isRunningOnCompositor,
          'The opacity animation is running on the compositor');
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -11002,24 +11002,29 @@ IsFrameScrolledOutOfView(nsIFrame* aTarg
       nsLayoutUtils::SCROLLABLE_SAME_DOC |
       nsLayoutUtils::SCROLLABLE_FIXEDPOS_FINDS_ROOT |
       nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
   if (!scrollableFrame) {
     return false;
   }
 
   nsIFrame *scrollableParent = do_QueryFrame(scrollableFrame);
+  nsRect scrollableRect =
+    scrollableParent->GetVisualOverflowRectRelativeToSelf();
+  // We consider that the target is scrolled out if the scrollable frame is
+  // empty.
+  if (scrollableRect.IsEmpty()) {
+    return true;
+  }
 
   nsRect transformedRect =
     nsLayoutUtils::TransformFrameRectToAncestor(aTarget,
                                                 aTargetRect,
                                                 scrollableParent);
 
-  nsRect scrollableRect =
-    scrollableParent->GetVisualOverflowRectRelativeToSelf();
   if (transformedRect.IsEmpty()) {
     // If the transformed rect is empty it represents a line or a point that we
     // should check is outside the the scrollable rect.
     if (transformedRect.x > scrollableRect.XMost() ||
         transformedRect.y > scrollableRect.YMost() ||
         scrollableRect.x > transformedRect.XMost() ||
         scrollableRect.y > transformedRect.YMost()) {
       return true;