Bug 1258904 - Part 4: Set performance warning for small content. r?birtles draft
authorHiroyuki Ikezoe <hiikezoe@mozilla-japan.org>
Sat, 07 May 2016 17:37:58 +0900
changeset 364661 45a442e62c32a5e7e26f76b370e789402ba9795e
parent 364660 09d44fc8b6b9cf071e477f6a246ac381bd07edca
child 364662 83a84ada738a0ace4fb1bd763910a0beba531ba1
push id17525
push userhiikezoe@mozilla-japan.org
push dateSat, 07 May 2016 10:01:04 +0000
reviewersbirtles
bugs1258904
milestone49.0a1
Bug 1258904 - Part 4: Set performance warning for small content. r?birtles MozReview-Commit-ID: 7GyGkPog1Bo
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/base/nsDisplayList.cpp
--- a/dom/animation/AnimationPerformanceWarning.cpp
+++ b/dom/animation/AnimationPerformanceWarning.cpp
@@ -28,16 +28,23 @@ AnimationPerformanceWarning::ToLocalized
 
 bool
 AnimationPerformanceWarning::ToLocalizedString(
   nsXPIDLString& aLocalizedString) const
 {
   const char* key = nullptr;
 
   switch (mType) {
+    case Type::ContentTooSmall:
+      MOZ_ASSERT(mParams && mParams->Length() == 2,
+                 "Parameter's length should be 2 for ContentTooSmall");
+
+      return NS_SUCCEEDED(
+        ToLocalizedStringWithIntParams<2>("AnimationWarningContentTooSmall",
+                                          aLocalizedString));
     case Type::ContentTooLarge:
       MOZ_ASSERT(mParams && mParams->Length() == 7,
                  "Parameter's length should be 7 for ContentTooLarge");
 
       return NS_SUCCEEDED(
         ToLocalizedStringWithIntParams<7>("AnimationWarningContentTooLarge",
                                           aLocalizedString));
     case Type::TransformBackfaceVisibilityHidden:
--- a/dom/animation/AnimationPerformanceWarning.h
+++ b/dom/animation/AnimationPerformanceWarning.h
@@ -12,16 +12,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 {
+    ContentTooSmall,
     ContentTooLarge,
     TransformBackfaceVisibilityHidden,
     TransformPreserve3D,
     TransformSVG,
     TransformWithGeometricProperties,
     TransformFrameInactive,
     OpacityFrameInactive,
   };
--- a/dom/animation/test/chrome/test_animation_performance_warning.html
+++ b/dom/animation/test/chrome/test_animation_performance_warning.html
@@ -24,17 +24,23 @@
   target="_blank">Mozilla Bug 1196114</a>
 <div id="log"></div>
 <script>
 'use strict';
 
 // This is used for obtaining localized strings.
 var gStringBundle;
 
-SpecialPowers.pushPrefEnv({ "set": [["general.useragent.locale", "en-US"]] },
+SpecialPowers.pushPrefEnv({ "set": [
+                            ["general.useragent.locale", "en-US"],
+                            // Need to set devPixelsPerPx explicitly to gain
+                            // consistent pixel values in warning messages
+                            // regardless of platform DPIs.
+                            ["layout.css.devPixelsPerPx", 1],
+                          ] },
                           start);
 
 function compare_property_state(a, b) {
   if (a.property > b.property) {
     return -1;
   } else if (a.property < b.property) {
     return 1;
   }
@@ -489,16 +495,54 @@ var gMultipleAsyncAnimationsWithGeometri
             runningOnCompositor: true,
           }
         ]
       }
     ],
   },
 ];
 
+var gAnimationsOnTooSmallElementTests = [
+  {
+    desc: 'opacity on too small element',
+    frames: {
+      opacity: [0, 1]
+    },
+    style: { style: 'width: 8px; height: 8px; background-color: red;' +
+                    // We need to set transform here to try creating an
+                    // individual frame for this opacity element.
+                    // Without this, this small element is created on the same
+                    // nsIFrame of mochitest iframe, i.e. the document which are
+                    // running this test, as a result the layer corresponding
+                    // to the frame is sent to compositor.
+                    'transform: translateX(100px);' },
+    expected: [
+      {
+        property: 'opacity',
+        runningOnCompositor: false,
+        warning: /Animation cannot be run on the compositor because frame size \(8, 8\) is smaller than \(16, 16\)/
+      }
+    ]
+  },
+  {
+    desc: 'transform on too small element',
+    frames: {
+      transform: ['translate(0px)', 'translate(100px)']
+    },
+    style: { style: 'width: 8px; height: 8px; background-color: red;' },
+    expected: [
+      {
+        property: 'transform',
+        runningOnCompositor: false,
+        warning: /Animation cannot be run on the compositor because frame size \(8, 8\) is smaller than \(16, 16\)/
+      }
+    ]
+  },
+];
+
 function start() {
   var bundleService = SpecialPowers.Cc['@mozilla.org/intl/stringbundle;1']
     .getService(SpecialPowers.Ci.nsIStringBundleService);
   gStringBundle = bundleService
     .createBundle("chrome://global/locale/layout_errors.properties");
 
   gAnimationsTests.forEach(function(subtest) {
     promise_test(function(t) {
@@ -635,16 +679,28 @@ function start() {
           assert_property_state_on_compositor(
             anim.effect.getProperties(),
             anim.expected);
         });
       });
     }, 'Multiple async animations and geometric animation: ' + subtest.desc);
   });
 
+  gAnimationsOnTooSmallElementTests.forEach(function(subtest) {
+    promise_test(function(t) {
+    var div = addDiv(t, subtest.style);
+    var animation = div.animate(subtest.frames, 100 * MS_PER_SEC);
+      return animation.ready.then(function() {
+        assert_animation_property_state_equals(
+          animation.effect.getProperties(),
+          subtest.expected);
+      });
+    }, subtest.desc);
+  });
+
   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(
--- a/dom/locales/en-US/chrome/layout/layout_errors.properties
+++ b/dom/locales/en-US/chrome/layout/layout_errors.properties
@@ -6,16 +6,19 @@ 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(AnimationWarningContentTooSmall):
+## (%1$S, %2$S) is a pair of integer values of the frame size
+AnimationWarningContentTooSmall=Animation cannot be run on the compositor because frame size (%1$S, %2$S) is smaller than (16, 16)
 ## LOCALIZATION NOTE(AnimationWarningContentTooLarge):
 ## (%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 the viewport size
 ## (%5$S, %6$S) is a pair of integer values of the visual rectangle size
 ## (%7$S) is an integer value
 AnimationWarningContentTooLarge=Animation cannot be run on the compositor because the frame size (%1$S, %2$S) is bigger than the viewport (%3$S, %4$S) or the visual rectangle (%5$S, %6$S) is larger than the maximum allowed value (%7$S)
 ## LOCALIZATION NOTE(AnimationWarningTransformBackfaceVisibilityHidde):
 ## 'backface-visibility: hidden' is a CSS property, don't translate it.
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -4259,27 +4259,48 @@ IsItemTooSmallForActiveLayer(nsDisplayIt
 {
   nsIntRect visibleDevPixels = aItem->GetVisibleRect().ToOutsidePixels(
           aItem->Frame()->PresContext()->AppUnitsPerDevPixel());
   static const int MIN_ACTIVE_LAYER_SIZE_DEV_PIXELS = 16;
   return visibleDevPixels.Size() <
     nsIntSize(MIN_ACTIVE_LAYER_SIZE_DEV_PIXELS, MIN_ACTIVE_LAYER_SIZE_DEV_PIXELS);
 }
 
+static void
+SetAnimationPerformanceWarningForTooSmallItem(nsDisplayItem* aItem,
+                                              nsCSSProperty aProperty)
+{
+  // We use ToNearestPixels() here since ToOutsidePixels causes some sort of
+  // errors. See https://bugzilla.mozilla.org/show_bug.cgi?id=1258904#c19
+  nsIntRect visibleDevPixels = aItem->GetVisibleRect().ToNearestPixels(
+          aItem->Frame()->PresContext()->AppUnitsPerDevPixel());
+
+  // Set performance warning only if the visible dev pixels is not empty
+  // because dev pixels is empty if the frame has 'preserve-3d' style.
+  if (visibleDevPixels.IsEmpty()) {
+    return;
+  }
+
+  EffectCompositor::SetPerformanceWarning(aItem->Frame(), aProperty,
+      AnimationPerformanceWarning(
+        AnimationPerformanceWarning::Type::ContentTooSmall,
+        { visibleDevPixels.Width(), visibleDevPixels.Height() }));
+}
+
 bool
 nsDisplayOpacity::NeedsActiveLayer(nsDisplayListBuilder* aBuilder)
 {
   if (ActiveLayerTracker::IsStyleAnimated(aBuilder, mFrame,
                                           eCSSProperty_opacity) ||
       EffectCompositor::HasAnimationsForCompositor(mFrame,
                                                    eCSSProperty_opacity)) {
     if (!IsItemTooSmallForActiveLayer(this)) {
       return true;
     }
-    // FIXME: Set animation performance warning here.
+    SetAnimationPerformanceWarningForTooSmallItem(this, eCSSProperty_opacity);
   }
   return false;
 }
 
 void
 nsDisplayOpacity::ApplyOpacity(nsDisplayListBuilder* aBuilder,
                              float aOpacity,
                              const DisplayItemClip* aClip)
@@ -5937,17 +5958,17 @@ nsDisplayTransform::MayBeAnimated(nsDisp
   if (ActiveLayerTracker::IsStyleAnimated(aBuilder,
                                           mFrame,
                                           eCSSProperty_transform) ||
       EffectCompositor::HasAnimationsForCompositor(mFrame,
                                                    eCSSProperty_transform)) {
     if (!IsItemTooSmallForActiveLayer(this)) {
       return true;
     }
-    // FIXME: Set animation performance warning here.
+    SetAnimationPerformanceWarningForTooSmallItem(this, eCSSProperty_transform);
   }
   return false;
 }
 
 nsDisplayItem::LayerState
 nsDisplayTransform::GetLayerState(nsDisplayListBuilder* aBuilder,
                                   LayerManager* aManager,
                                   const ContainerLayerParameters& aParameters) {