Bug 1258904 - Part 4: Set performance warning for small content. r?birtles
MozReview-Commit-ID: 7GyGkPog1Bo
--- 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) {