mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
Bug 1196114 - Part 3: Set AnimationPerformanceWarning messages. r=birtles
Those message will be modified in part 4 (localization). MozReview-Commit-ID: 6TMUxemVLcu --HG-- extra : rebase_source : 65ef1879b3e606ae6dc279981b1e995c7b2cd40b
This commit is contained in:
parent
e3df4388fc
commit
d7ac86e556
@ -109,10 +109,14 @@ FindAnimationsForCompositor(const nsIFrame* aFrame,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (effect->ShouldBlockCompositorAnimations(aFrame)) {
|
||||
nsAutoString performanceWarning;
|
||||
if (effect->ShouldBlockCompositorAnimations(aFrame,
|
||||
performanceWarning)) {
|
||||
if (aMatches) {
|
||||
aMatches->Clear();
|
||||
}
|
||||
effect->SetPerformanceWarning(aProperty,
|
||||
performanceWarning);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2099,19 +2099,16 @@ KeyframeEffectReadOnly::IsGeometricProperty(
|
||||
/* static */ bool
|
||||
KeyframeEffectReadOnly::CanAnimateTransformOnCompositor(
|
||||
const nsIFrame* aFrame,
|
||||
const nsIContent* aContent)
|
||||
nsAString& aPerformanceWarning)
|
||||
{
|
||||
// Disallow OMTA for preserve-3d transform. Note that we check the style property
|
||||
// rather than Extend3DContext() since that can recurse back into this function
|
||||
// via HasOpacity().
|
||||
if (aFrame->Combines3DTransformWithAncestors() ||
|
||||
aFrame->StyleDisplay()->mTransformStyle == NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D) {
|
||||
if (aContent) {
|
||||
nsCString message;
|
||||
message.AppendLiteral("Gecko bug: Async animation of 'preserve-3d' "
|
||||
"transforms is not supported. See bug 779598");
|
||||
AnimationUtils::LogAsyncAnimationFailure(message, aContent);
|
||||
}
|
||||
aPerformanceWarning.AssignLiteral(
|
||||
"Gecko bug: Async animation of 'preserve-3d' "
|
||||
"transforms is not supported. See bug 779598");
|
||||
return false;
|
||||
}
|
||||
// Note that testing BackfaceIsHidden() is not a sufficient test for
|
||||
@ -2119,22 +2116,16 @@ KeyframeEffectReadOnly::CanAnimateTransformOnCompositor(
|
||||
// remove the above test for Extend3DContext(); that would require
|
||||
// looking at backface-visibility on descendants as well.
|
||||
if (aFrame->StyleDisplay()->BackfaceIsHidden()) {
|
||||
if (aContent) {
|
||||
nsCString message;
|
||||
message.AppendLiteral("Gecko bug: Async animation of "
|
||||
"'backface-visibility: hidden' transforms is not supported."
|
||||
" See bug 1186204.");
|
||||
AnimationUtils::LogAsyncAnimationFailure(message, aContent);
|
||||
}
|
||||
aPerformanceWarning.AssignLiteral(
|
||||
"Gecko bug: Async animation of "
|
||||
"'backface-visibility: hidden' transforms is not supported."
|
||||
" See bug 1186204");
|
||||
return false;
|
||||
}
|
||||
if (aFrame->IsSVGTransformed()) {
|
||||
if (aContent) {
|
||||
nsCString message;
|
||||
message.AppendLiteral("Gecko bug: Async 'transform' animations of "
|
||||
"aFrames with SVG transforms is not supported. See bug 779599");
|
||||
AnimationUtils::LogAsyncAnimationFailure(message, aContent);
|
||||
}
|
||||
aPerformanceWarning.AssignLiteral(
|
||||
"Gecko bug: Async 'transform' animations of "
|
||||
"aFrames with SVG transforms is not supported. See bug 779599");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2142,8 +2133,9 @@ KeyframeEffectReadOnly::CanAnimateTransformOnCompositor(
|
||||
}
|
||||
|
||||
bool
|
||||
KeyframeEffectReadOnly::ShouldBlockCompositorAnimations(const nsIFrame*
|
||||
aFrame) const
|
||||
KeyframeEffectReadOnly::ShouldBlockCompositorAnimations(
|
||||
const nsIFrame* aFrame,
|
||||
nsAString& aPerformanceWarning) const
|
||||
{
|
||||
// We currently only expect this method to be called when this effect
|
||||
// is attached to a playing Animation. If that ever changes we'll need
|
||||
@ -2152,8 +2144,6 @@ KeyframeEffectReadOnly::ShouldBlockCompositorAnimations(const nsIFrame*
|
||||
// running on the compositor.
|
||||
MOZ_ASSERT(mAnimation && mAnimation->IsPlaying());
|
||||
|
||||
bool shouldLog = nsLayoutUtils::IsAnimationLoggingEnabled();
|
||||
|
||||
for (const AnimationProperty& property : mProperties) {
|
||||
// If a property is overridden in the CSS cascade, it should not block other
|
||||
// animations from running on the compositor.
|
||||
@ -2162,20 +2152,17 @@ KeyframeEffectReadOnly::ShouldBlockCompositorAnimations(const nsIFrame*
|
||||
}
|
||||
// Check for geometric properties
|
||||
if (IsGeometricProperty(property.mProperty)) {
|
||||
if (shouldLog) {
|
||||
nsCString message;
|
||||
message.AppendLiteral("Performance warning: Async animation of "
|
||||
"'transform' or 'opacity' not possible due to animation of geometric"
|
||||
"properties on the same element");
|
||||
AnimationUtils::LogAsyncAnimationFailure(message, aFrame->GetContent());
|
||||
}
|
||||
aPerformanceWarning.AssignLiteral(
|
||||
"Performance warning: Async animation of "
|
||||
"'transform' or 'opacity' not possible due to animation of geometric "
|
||||
"properties on the same element");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for unsupported transform animations
|
||||
if (property.mProperty == eCSSProperty_transform) {
|
||||
if (!CanAnimateTransformOnCompositor(aFrame,
|
||||
shouldLog ? aFrame->GetContent() : nullptr)) {
|
||||
aPerformanceWarning)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -2190,7 +2177,13 @@ KeyframeEffectReadOnly::SetPerformanceWarning(nsCSSProperty aProperty,
|
||||
{
|
||||
for (AnimationProperty& property : mProperties) {
|
||||
if (property.mProperty == aProperty) {
|
||||
property.mPerformanceWarning.reset();
|
||||
property.mPerformanceWarning.emplace(aMessage);
|
||||
|
||||
if (nsLayoutUtils::IsAnimationLoggingEnabled()) {
|
||||
nsAutoCString logMessage = NS_ConvertUTF16toUTF8(aMessage);
|
||||
AnimationUtils::LogAsyncAnimationFailure(logMessage, mTarget);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -324,7 +324,11 @@ public:
|
||||
//
|
||||
// Bug 1218620 - It seems like we don't need to be this restrictive. Wouldn't
|
||||
// it be ok to do 'opacity' animations on the compositor in either case?
|
||||
bool ShouldBlockCompositorAnimations(const nsIFrame* aFrame) const;
|
||||
//
|
||||
// When returning true, |aOutPerformanceWarning| stores the reason why
|
||||
// we shouldn't run the compositor animations.
|
||||
bool ShouldBlockCompositorAnimations(const nsIFrame* aFrame,
|
||||
nsAString& aPerformanceWarning) const;
|
||||
|
||||
nsIDocument* GetRenderedDocument() const;
|
||||
nsPresContext* GetPresContext() const;
|
||||
@ -396,11 +400,10 @@ private:
|
||||
bool CanThrottleTransformChanges(nsIFrame& aFrame) const;
|
||||
|
||||
// Returns true unless Gecko limitations prevent performing transform
|
||||
// animations for |aFrame|. Any limitations that are encountered are
|
||||
// logged using |aContent| to describe the affected content.
|
||||
// If |aContent| is nullptr, no logging is performed
|
||||
// animations for |aFrame|. When returning true, the reason for the
|
||||
// limitation is stored in |aOutPerformanceWarning|.
|
||||
static bool CanAnimateTransformOnCompositor(const nsIFrame* aFrame,
|
||||
const nsIContent* aContent);
|
||||
nsAString& aPerformanceWarning);
|
||||
static bool IsGeometricProperty(const nsCSSProperty aProperty);
|
||||
|
||||
static const TimeDuration OverflowRegionRefreshInterval();
|
||||
|
@ -7,7 +7,7 @@
|
||||
<script type="application/javascript" src="../testharnessreport.js"></script>
|
||||
<script type="application/javascript" src="../testcommon.js"></script>
|
||||
<style>
|
||||
div {
|
||||
.compositable {
|
||||
/* Element needs geometry to be eligible for layerization */
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
@ -47,6 +47,35 @@ function assert_animation_property_state_equals(actual, expected) {
|
||||
assert_equals(sortedActual[i].runningOnCompositor,
|
||||
sortedExpected[i].runningOnCompositor,
|
||||
'runningOnCompositor property should match');
|
||||
if (sortedExpected[i].warning instanceof RegExp) {
|
||||
assert_regexp_match(sortedActual[i].warning,
|
||||
sortedExpected[i].warning,
|
||||
'warning message should match');
|
||||
} else if (sortedExpected[i].warning) {
|
||||
assert_equals(sortedActual[i].warning,
|
||||
sortedExpected[i].warning,
|
||||
'warning message should match');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the animation is running on compositor and
|
||||
// warning property is not set for the CSS property regardless
|
||||
// expected values.
|
||||
function assert_property_state_on_compositor(actual, expected) {
|
||||
assert_equals(actual.length, expected.length);
|
||||
|
||||
var sortedActual = actual.sort(compare_property_state);
|
||||
var sortedExpected = expected.sort(compare_property_state);
|
||||
|
||||
for (var i = 0; i < sortedActual.length; i++) {
|
||||
assert_equals(sortedActual[i].property,
|
||||
sortedExpected[i].property,
|
||||
'CSS property name should match');
|
||||
assert_true(sortedActual[i].runningOnCompositor,
|
||||
'runningOnCompositor property should be true');
|
||||
assert_not_exists(sortedActual[i], 'warning',
|
||||
'warning property should not be set');
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,12 +137,35 @@ var gAnimationsTests = [
|
||||
runningOnCompositor: true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
// FIXME: Once we have KeyframeEffect.setFrames, we should rewrite
|
||||
// this test case to check that runningOnCompositor is restored to true
|
||||
// after 'width' keyframe is removed from the keyframes.
|
||||
desc: 'animation on compositor with animation of geometric properties',
|
||||
frames: {
|
||||
width: ['100px', '200px'],
|
||||
transform: ['translate(0px)', 'translate(100px)']
|
||||
},
|
||||
expected: [
|
||||
{
|
||||
property: 'width',
|
||||
runningOnCompositor: false
|
||||
},
|
||||
{
|
||||
property: 'transform',
|
||||
runningOnCompositor: false,
|
||||
warning: "Performance warning: Async animation of " +
|
||||
"'transform' or 'opacity' not possible due to animation " +
|
||||
"of geometric properties on the same element"
|
||||
}
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
gAnimationsTests.forEach(function(subtest) {
|
||||
promise_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var div = addDiv(t, { class: 'compositable' });
|
||||
var animation = div.animate(subtest.frames, 100000);
|
||||
return animation.ready.then(t.step_func(function() {
|
||||
assert_animation_property_state_equals(
|
||||
@ -123,5 +175,138 @@ gAnimationsTests.forEach(function(subtest) {
|
||||
}, subtest.desc);
|
||||
});
|
||||
|
||||
var gPerformanceWarningTests = [
|
||||
{
|
||||
desc: 'preserve-3d transform',
|
||||
frames: {
|
||||
transform: ['translate(0px)', 'translate(100px)']
|
||||
},
|
||||
style: 'transform-style: preserve-3d',
|
||||
expected: [
|
||||
{
|
||||
property: 'transform',
|
||||
runningOnCompositor: false,
|
||||
warning: "Gecko bug: Async animation of 'preserve-3d' " +
|
||||
"transforms is not supported. See bug 779598"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
desc: 'transform with backface-visibility:hidden',
|
||||
frames: {
|
||||
transform: ['translate(0px)', 'translate(100px)']
|
||||
},
|
||||
style: 'backface-visibility: hidden;',
|
||||
expected: [
|
||||
{
|
||||
property: 'transform',
|
||||
runningOnCompositor: false,
|
||||
warning: "Gecko bug: Async animation of " +
|
||||
"'backface-visibility: hidden' transforms is not " +
|
||||
"supported. See bug 1186204"
|
||||
}
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
gPerformanceWarningTests.forEach(function(subtest) {
|
||||
promise_test(function(t) {
|
||||
var div = addDiv(t, { class: 'compositable' });
|
||||
var animation = div.animate(subtest.frames, 100000);
|
||||
return animation.ready.then(t.step_func(function() {
|
||||
assert_property_state_on_compositor(
|
||||
animation.effect.getPropertyState(),
|
||||
subtest.expected);
|
||||
div.style = subtest.style;
|
||||
return waitForFrame();
|
||||
})).then(t.step_func(function() {
|
||||
assert_animation_property_state_equals(
|
||||
animation.effect.getPropertyState(),
|
||||
subtest.expected);
|
||||
div.style = '';
|
||||
return waitForFrame();
|
||||
})).then(t.step_func(function() {
|
||||
assert_property_state_on_compositor(
|
||||
animation.effect.getPropertyState(),
|
||||
subtest.expected);
|
||||
}));
|
||||
}, subtest.desc);
|
||||
});
|
||||
|
||||
promise_test(function(t) {
|
||||
var div = addDiv(t, { class: 'compositable' });
|
||||
var animation = div.animate(
|
||||
{ transform: ['translate(0px)', 'translate(100px)'] }, 100000);
|
||||
return animation.ready.then(t.step_func(function() {
|
||||
assert_animation_property_state_equals(
|
||||
animation.effect.getPropertyState(),
|
||||
[ { property: 'transform', runningOnCompositor: true } ]);
|
||||
div.style = 'width: 10000px; height: 10000px';
|
||||
return waitForFrame();
|
||||
})).then(t.step_func(function() {
|
||||
// viewport depends on test environment.
|
||||
var expectedWarning = new RegExp(
|
||||
"Performance warning: Async animation disabled because frame size " +
|
||||
"\\(10000, 10000\\) is bigger than the viewport \\(\\d+, \\d+\\) " +
|
||||
"or the visual rectangle \\(10000, 10000\\) is larger than the max " +
|
||||
"allowable value \\(\\d+\\)");
|
||||
assert_animation_property_state_equals(
|
||||
animation.effect.getPropertyState(),
|
||||
[ {
|
||||
property: 'transform',
|
||||
runningOnCompositor: false,
|
||||
warning: expectedWarning
|
||||
} ]);
|
||||
div.style = 'width: 100px; height: 100px';
|
||||
return waitForFrame();
|
||||
})).then(t.step_func(function() {
|
||||
// FIXME: Bug 1253164: the animation should get back on compositor.
|
||||
assert_animation_property_state_equals(
|
||||
animation.effect.getPropertyState(),
|
||||
[ { property: 'transform', runningOnCompositor: false } ]);
|
||||
}));
|
||||
}, 'transform on too big element');
|
||||
|
||||
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');
|
||||
rect.setAttribute('fill', 'red');
|
||||
svg.appendChild(rect);
|
||||
document.body.appendChild(svg);
|
||||
t.add_cleanup(function() {
|
||||
svg.remove();
|
||||
});
|
||||
|
||||
var animation = svg.animate(
|
||||
{ transform: ['translate(0px)', 'translate(100px)'] }, 100000);
|
||||
return animation.ready.then(t.step_func(function() {
|
||||
assert_animation_property_state_equals(
|
||||
animation.effect.getPropertyState(),
|
||||
[ { property: 'transform', runningOnCompositor: true } ]);
|
||||
svg.setAttribute('transform', 'translate(10, 20)');
|
||||
return waitForFrame();
|
||||
})).then(t.step_func(function() {
|
||||
assert_animation_property_state_equals(
|
||||
animation.effect.getPropertyState(),
|
||||
[ {
|
||||
property: 'transform',
|
||||
runningOnCompositor: false,
|
||||
warning: "Gecko bug: Async 'transform' animations of aFrames " +
|
||||
"with SVG transforms is not supported. See bug 779599"
|
||||
} ]);
|
||||
svg.removeAttribute('transform');
|
||||
return waitForFrame();
|
||||
})).then(t.step_func(function() {
|
||||
assert_animation_property_state_equals(
|
||||
animation.effect.getPropertyState(),
|
||||
[ { property: 'transform', runningOnCompositor: true } ]);
|
||||
}));
|
||||
}, 'transform of nsIFrame with SVG transform');
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
@ -5585,11 +5585,14 @@ nsDisplayOpacity::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (nsLayoutUtils::IsAnimationLoggingEnabled()) {
|
||||
nsCString message;
|
||||
message.AppendLiteral("Performance warning: Async animation disabled because frame was not marked active for opacity animation");
|
||||
AnimationUtils::LogAsyncAnimationFailure(message, Frame()->GetContent());
|
||||
}
|
||||
nsString message;
|
||||
message.AppendLiteral(
|
||||
"Performance warning: Async animation disabled because frame was not "
|
||||
"marked active for opacity animation");
|
||||
EffectCompositor::SetPerformanceWarning(mFrame,
|
||||
eCSSProperty_opacity,
|
||||
message);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -5620,15 +5623,14 @@ nsDisplayTransform::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder)
|
||||
// pre-render even if we're out of will change budget.
|
||||
return true;
|
||||
}
|
||||
DebugOnly<bool> prerender = ShouldPrerenderTransformedContent(aBuilder, mFrame, true);
|
||||
DebugOnly<bool> prerender = ShouldPrerenderTransformedContent(aBuilder, mFrame);
|
||||
NS_ASSERTION(!prerender, "Something changed under us!");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
nsDisplayTransform::ShouldPrerenderTransformedContent(nsDisplayListBuilder* aBuilder,
|
||||
nsIFrame* aFrame,
|
||||
bool aLogAnimations)
|
||||
nsIFrame* aFrame)
|
||||
{
|
||||
// Elements whose transform has been modified recently, or which
|
||||
// have a compositor-animated transform, can be prerendered. An element
|
||||
@ -5637,11 +5639,13 @@ nsDisplayTransform::ShouldPrerenderTransformedContent(nsDisplayListBuilder* aBui
|
||||
if (!ActiveLayerTracker::IsStyleMaybeAnimated(aFrame, eCSSProperty_transform) &&
|
||||
!EffectCompositor::HasAnimationsForCompositor(aFrame,
|
||||
eCSSProperty_transform)) {
|
||||
if (aLogAnimations) {
|
||||
nsCString message;
|
||||
message.AppendLiteral("Performance warning: Async animation disabled because frame was not marked active for transform animation");
|
||||
AnimationUtils::LogAsyncAnimationFailure(message, aFrame->GetContent());
|
||||
}
|
||||
nsString message;
|
||||
message.AppendLiteral(
|
||||
"Performance warning: Async animation disabled because frame was not "
|
||||
"marked active for transform animation");
|
||||
EffectCompositor::SetPerformanceWarning(aFrame,
|
||||
eCSSProperty_transform,
|
||||
message);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -5660,27 +5664,28 @@ nsDisplayTransform::ShouldPrerenderTransformedContent(nsDisplayListBuilder* aBui
|
||||
}
|
||||
}
|
||||
|
||||
if (aLogAnimations) {
|
||||
nsRect visual = aFrame->GetVisualOverflowRect();
|
||||
nsRect visual = aFrame->GetVisualOverflowRect();
|
||||
|
||||
nsCString message;
|
||||
message.AppendLiteral("Performance warning: Async animation disabled because frame size (");
|
||||
message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(frameSize.width));
|
||||
message.AppendLiteral(", ");
|
||||
message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(frameSize.height));
|
||||
message.AppendLiteral(") is bigger than the viewport (");
|
||||
message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(refSize.width));
|
||||
message.AppendLiteral(", ");
|
||||
message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(refSize.height));
|
||||
message.AppendLiteral(") or the visual rectangle (");
|
||||
message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(visual.width));
|
||||
message.AppendLiteral(", ");
|
||||
message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(visual.height));
|
||||
message.AppendLiteral(") is larger than the max allowable value (");
|
||||
message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(maxInAppUnits));
|
||||
message.Append(')');
|
||||
AnimationUtils::LogAsyncAnimationFailure(message, aFrame->GetContent());
|
||||
}
|
||||
nsAutoString message;
|
||||
message.AppendLiteral(
|
||||
"Performance warning: Async animation disabled because frame size (");
|
||||
message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(frameSize.width));
|
||||
message.AppendLiteral(", ");
|
||||
message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(frameSize.height));
|
||||
message.AppendLiteral(") is bigger than the viewport (");
|
||||
message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(refSize.width));
|
||||
message.AppendLiteral(", ");
|
||||
message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(refSize.height));
|
||||
message.AppendLiteral(") or the visual rectangle (");
|
||||
message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(visual.width));
|
||||
message.AppendLiteral(", ");
|
||||
message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(visual.height));
|
||||
message.AppendLiteral(") is larger than the max allowable value (");
|
||||
message.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(maxInAppUnits));
|
||||
message.Append(')');
|
||||
EffectCompositor::SetPerformanceWarning(aFrame,
|
||||
eCSSProperty_transform,
|
||||
message);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -4080,8 +4080,7 @@ public:
|
||||
* transformed frame even when it's not completely visible (yet).
|
||||
*/
|
||||
static bool ShouldPrerenderTransformedContent(nsDisplayListBuilder* aBuilder,
|
||||
nsIFrame* aFrame,
|
||||
bool aLogAnimations = false);
|
||||
nsIFrame* aFrame);
|
||||
bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) override;
|
||||
|
||||
bool MayBeAnimated(nsDisplayListBuilder* aBuilder);
|
||||
|
Loading…
Reference in New Issue
Block a user