Bug 1364221 - Allow frames to be prerendered as long as the area of the frame is less than the area of the relative limit and the dimensions are less than the absolute limit. r?mattwoodrow draft
authorJared Wein <jwein@mozilla.com>
Mon, 05 Jun 2017 17:01:19 -0400
changeset 589298 738aa852e30b3398e67eb3e07e479b0f9b4b2702
parent 589297 3cfe81678881ada0245215c8863bc9722925bb44
child 589604 e15293b088e3ae50b2930ae60c1a206fd43ee055
push id62314
push userbmo:jaws@mozilla.com
push dateTue, 06 Jun 2017 00:22:18 +0000
reviewersmattwoodrow
bugs1364221
milestone55.0a1
Bug 1364221 - Allow frames to be prerendered as long as the area of the frame is less than the area of the relative limit and the dimensions are less than the absolute limit. r?mattwoodrow MozReview-Commit-ID: C3QLjaCRbzE
dom/animation/AnimationPerformanceWarning.cpp
dom/animation/AnimationPerformanceWarning.h
dom/animation/test/chrome/test_animation_performance_warning.html
dom/locales/en-US/chrome/layout/layout_errors.properties
layout/painting/nsDisplayList.cpp
--- a/dom/animation/AnimationPerformanceWarning.cpp
+++ b/dom/animation/AnimationPerformanceWarning.cpp
@@ -30,21 +30,28 @@ bool
 AnimationPerformanceWarning::ToLocalizedString(
   nsXPIDLString& aLocalizedString) const
 {
   const char* key = nullptr;
 
   switch (mType) {
     case Type::ContentTooLarge:
       MOZ_ASSERT(mParams && mParams->Length() == 6,
-                 "Parameter's length should be 6 for ContentTooLarge");
+                 "Parameter's length should be 6 for ContentTooLarge2");
 
       return NS_SUCCEEDED(
         ToLocalizedStringWithIntParams<7>(
           "CompositorAnimationWarningContentTooLarge2", aLocalizedString));
+    case Type::ContentTooLargeArea:
+      MOZ_ASSERT(mParams && mParams->Length() == 2,
+                 "Parameter's length should be 2 for ContentTooLargeArea");
+
+      return NS_SUCCEEDED(
+        ToLocalizedStringWithIntParams<3>(
+          "CompositorAnimationWarningContentTooLargeArea", aLocalizedString));
     case Type::TransformBackfaceVisibilityHidden:
       key = "CompositorAnimationWarningTransformBackfaceVisibilityHidden";
       break;
     case Type::TransformPreserve3D:
       key = "CompositorAnimationWarningTransformPreserve3D";
       break;
     case Type::TransformSVG:
       key = "CompositorAnimationWarningTransformSVG";
--- a/dom/animation/AnimationPerformanceWarning.h
+++ b/dom/animation/AnimationPerformanceWarning.h
@@ -16,16 +16,17 @@ class nsXPIDLString;
 
 namespace mozilla {
 
 // Represents the reason why we can't run the CSS property on the compositor.
 struct AnimationPerformanceWarning
 {
   enum class Type : uint8_t {
     ContentTooLarge,
+    ContentTooLargeArea,
     TransformBackfaceVisibilityHidden,
     TransformPreserve3D,
     TransformSVG,
     TransformWithGeometricProperties,
     TransformWithSyncGeometricAnimations,
     TransformFrameInactive,
     OpacityFrameInactive,
     HasRenderingObserver,
--- a/dom/animation/test/chrome/test_animation_performance_warning.html
+++ b/dom/animation/test/chrome/test_animation_performance_warning.html
@@ -1170,23 +1170,58 @@ function start() {
                                      { class: 'compositable' },
                                      { transform: [ 'translate(0px)',
                                                     'translate(100px)'] },
                                      100 * MS_PER_SEC);
     return animation.ready.then(function() {
       assert_animation_property_state_equals(
         animation.effect.getProperties(),
         [ { property: 'transform', runningOnCompositor: true } ]);
-      animation.effect.target.style = 'width: 10000px; height: 10000px';
+      animation.effect.target.style = 'width: 5200px; height: 5200px';
+      return waitForFrame();
+    }).then(function() {
+      // viewport depends on test environment.
+      var expectedWarning = new RegExp(
+        "Animation cannot be run on the compositor because the area of the frame size " +
+        "\\(\\d+\\) is too large relative to the viewport " +
+        "\\(larger than \\d+\\)");
+      assert_animation_property_state_equals(
+        animation.effect.getProperties(),
+        [ {
+          property: 'transform',
+          runningOnCompositor: false,
+          warning: expectedWarning
+        } ]);
+      animation.effect.target.style = 'width: 100px; height: 100px';
+      return waitForFrame();
+    }).then(function() {
+      // FIXME: Bug 1253164: the animation should get back on compositor.
+      assert_animation_property_state_equals(
+        animation.effect.getProperties(),
+        [ { property: 'transform', runningOnCompositor: false } ]);
+    });
+  }, 'transform on too big element - area');
+
+  promise_test(function(t) {
+    var animation = addDivAndAnimate(t,
+                                     { class: 'compositable' },
+                                     { transform: [ 'translate(0px)',
+                                                    'translate(100px)'] },
+                                     100 * MS_PER_SEC);
+    return animation.ready.then(function() {
+      assert_animation_property_state_equals(
+        animation.effect.getProperties(),
+        [ { property: 'transform', runningOnCompositor: true } ]);
+      animation.effect.target.style = 'width: 5200px; height: 1px';
       return waitForFrame();
     }).then(function() {
       // viewport depends on test environment.
       var expectedWarning = new RegExp(
         "Animation cannot be run on the compositor because the frame size " +
-        "\\(10000, 10000\\) is too large relative to the viewport " + 
+        "\\(5200, 1\\) is too large relative to the viewport " +
         "\\(larger than \\(\\d+, \\d+\\)\\) or larger than the " +
         "maximum allowed value \\(\\d+, \\d+\\)");
       assert_animation_property_state_equals(
         animation.effect.getProperties(),
         [ {
           property: 'transform',
           runningOnCompositor: false,
           warning: expectedWarning
@@ -1194,17 +1229,17 @@ function start() {
       animation.effect.target.style = 'width: 100px; height: 100px';
       return waitForFrame();
     }).then(function() {
       // FIXME: Bug 1253164: the animation should get back on compositor.
       assert_animation_property_state_equals(
         animation.effect.getProperties(),
         [ { property: 'transform', runningOnCompositor: false } ]);
     });
-  }, 'transform on too big element');
+  }, 'transform on too big element - dimensions');
 
   promise_test(function(t) {
     var svg  = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
     svg.setAttribute('width', '100');
     svg.setAttribute('height', '100');
     var rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
     rect.setAttribute('width', '100');
     rect.setAttribute('height', '100');
--- a/dom/locales/en-US/chrome/layout/layout_errors.properties
+++ b/dom/locales/en-US/chrome/layout/layout_errors.properties
@@ -6,16 +6,20 @@ ImageMapRectBoundsError=The “coords” attribute of the <area shape="rect"> tag is not in the “left,top,right,bottom” format.
 ImageMapCircleWrongNumberOfCoords=The “coords” attribute of the <area shape="circle"> tag is not in the “center-x,center-y,radius” format.
 ImageMapCircleNegativeRadius=The “coords” attribute of the <area shape="circle"> tag has a negative radius.
 ImageMapPolyWrongNumberOfCoords=The “coords” attribute of the <area shape="poly"> tag is not in the “x1,y1,x2,y2 …” format.
 ImageMapPolyOddNumberOfCoords=The “coords” attribute of the <area shape="poly"> tag is missing the last “y” coordinate (the correct format is “x1,y1,x2,y2 …”).
 
 TablePartRelPosWarning=Relative positioning of table rows and row groups is now supported. This site may need to be updated because it may depend on this feature having no effect.
 ScrollLinkedEffectFound2=This site appears to use a scroll-linked positioning effect. This may not work well with asynchronous panning; see https://developer.mozilla.org/docs/Mozilla/Performance/ScrollLinkedEffects for further details and to join the discussion on related tools and features!
 
+## LOCALIZATION NOTE(CompositorAnimationWarningContentTooLargeArea):
+## %1$S is an integer value of the area of the frame size
+## %2$S is an integer value of the area of a limit based on the viewport size
+CompositorAnimationWarningContentTooLargeArea=Animation cannot be run on the compositor because the area of the frame size (%1$S) is too large relative to the viewport (larger than %2$S)
 ## LOCALIZATION NOTE(CompositorAnimationWarningContentTooLarge2):
 ## (%1$S, %2$S) is a pair of integer values of the frame size
 ## (%3$S, %4$S) is a pair of integer values of a limit based on the viewport size
 ## (%5$S, %6$S) is a pair of integer values of an absolute limit
 CompositorAnimationWarningContentTooLarge2=Animation cannot be run on the compositor because the frame size (%1$S, %2$S) is too large relative to the viewport (larger than (%3$S, %4$S)) or larger than the maximum allowed value (%5$S, %6$S)
 ## LOCALIZATION NOTE(CompositorAnimationWarningTransformBackfaceVisibilityHidden):
 ## 'backface-visibility: hidden' is a CSS property, don't translate it.
 CompositorAnimationWarningTransformBackfaceVisibilityHidden=Animations of ‘backface-visibility: hidden’ transforms cannot be run on the compositor
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -7351,38 +7351,53 @@ nsDisplayTransform::ShouldPrerenderTrans
   // reference frame size (~viewport), and less than an absolute limit.
   // Both the ratio and the absolute limit are configurable.
   nsSize relativeLimit(nscoord(refSize.width * viewportRatioX),
                        nscoord(refSize.height * viewportRatioY));
   nsSize absoluteLimit(aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitX),
                        aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitY));
   nsSize maxSize = Min(relativeLimit, absoluteLimit);
   gfxSize scale = nsLayoutUtils::GetTransformToAncestorScale(aFrame);
-  nsSize frameSize = nsSize(overflow.Size().width * scale.width,
-                            overflow.Size().height * scale.height);
-  if (frameSize <= maxSize) {
+  nsSize frameSize(overflow.Size().width * scale.width,
+                   overflow.Size().height * scale.height);
+  uint64_t maxLimitArea = uint64_t(maxSize.width) * maxSize.height;
+  uint64_t frameArea = uint64_t(frameSize.width) * frameSize.height;
+  if (frameArea <= maxLimitArea && frameSize <= absoluteLimit) {
     *aDirtyRect = overflow;
     return FullPrerender;
   } else if (gfxPrefs::PartiallyPrerenderAnimatedContent()) {
     *aDirtyRect = nsLayoutUtils::ComputePartialPrerenderArea(*aDirtyRect, overflow, maxSize);
     return PartialPrerender;
   }
 
-  EffectCompositor::SetPerformanceWarning(
-    aFrame, eCSSProperty_transform,
-    AnimationPerformanceWarning(
-      AnimationPerformanceWarning::Type::ContentTooLarge,
-      {
-        nsPresContext::AppUnitsToIntCSSPixels(frameSize.width),
-        nsPresContext::AppUnitsToIntCSSPixels(frameSize.height),
-        nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.width),
-        nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.height),
-        nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.width),
-        nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.height),
-      }));
+  if (frameArea > maxLimitArea) {
+    uint64_t appUnitsPerPixel = nsPresContext::AppUnitsPerCSSPixel();
+    EffectCompositor::SetPerformanceWarning(
+      aFrame, eCSSProperty_transform,
+      AnimationPerformanceWarning(
+        AnimationPerformanceWarning::Type::ContentTooLargeArea,
+        {
+          int(frameArea / (appUnitsPerPixel * appUnitsPerPixel)),
+          int(maxLimitArea / (appUnitsPerPixel * appUnitsPerPixel)),
+        }));
+  } else {
+    EffectCompositor::SetPerformanceWarning(
+      aFrame, eCSSProperty_transform,
+      AnimationPerformanceWarning(
+        AnimationPerformanceWarning::Type::ContentTooLarge,
+        {
+          nsPresContext::AppUnitsToIntCSSPixels(frameSize.width),
+          nsPresContext::AppUnitsToIntCSSPixels(frameSize.height),
+          nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.width),
+          nsPresContext::AppUnitsToIntCSSPixels(relativeLimit.height),
+          nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.width),
+          nsPresContext::AppUnitsToIntCSSPixels(absoluteLimit.height),
+        }));
+  }
+
   return NoPrerender;
 }
 
 /* If the matrix is singular, or a hidden backface is shown, the frame won't be visible or hit. */
 static bool IsFrameVisible(nsIFrame* aFrame, const Matrix4x4& aMatrix)
 {
   if (aMatrix.IsSingular()) {
     return false;