Bug 1437272 - Don't throttle finite transform animations in out-of-view element. r?birtles
Transform animation in out-of-view element might move in visible area so if we
throttle it the transform animation might come in view suddenly. So we don't
throttle transform animations in ouf-of-view element anymore if it's not
infinite. Finite animations don't last so that they won't consume CPU so much
time.
MozReview-Commit-ID: HaMjmxqXPIK
--- a/dom/animation/AnimationEffectReadOnly.h
+++ b/dom/animation/AnimationEffectReadOnly.h
@@ -46,16 +46,20 @@ public:
{
return nullptr;
}
nsISupports* GetParentObject() const { return mDocument; }
bool IsCurrent() const;
bool IsInEffect() const;
+ bool HasFiniteActiveDuration() const
+ {
+ return SpecifiedTiming().ActiveDuration() != TimeDuration::Forever();
+ }
already_AddRefed<AnimationEffectTimingReadOnly> Timing();
const TimingParams& SpecifiedTiming() const
{
return mTiming->AsTimingParams();
}
void SetSpecifiedTiming(const TimingParams& aTiming);
--- a/dom/animation/KeyframeEffectReadOnly.cpp
+++ b/dom/animation/KeyframeEffectReadOnly.cpp
@@ -1468,16 +1468,23 @@ KeyframeEffectReadOnly::CanThrottle() co
const bool isVisibilityHidden =
!frame->IsVisibleOrMayHaveVisibleDescendants();
if (isVisibilityHidden ||
frame->IsScrolledOutOfView()) {
// If there are transform change hints, unthrottle the animation
// periodically since it might affect the overflow region.
if (HasTransformThatMightAffectOverflow()) {
+ // Don't throttle finite transform animations since the animation might
+ // suddenly come into view and if it was throttled it will be
+ // out-of-sync.
+ if (HasFiniteActiveDuration()) {
+ return false;
+ }
+
return isVisibilityHidden
? CanThrottleTransformChangesInScrollable(*frame)
: CanThrottleTransformChanges(*frame);
}
return true;
}
}
--- a/dom/animation/test/mozilla/file_restyles.html
+++ b/dom/animation/test/mozilla/file_restyles.html
@@ -399,17 +399,17 @@ waitForAllPaints(() => {
return;
}
await SpecialPowers.pushPrefEnv({ set: [["ui.showHideScrollbars", 1]] });
var parentElement = addDiv(null,
{ style: 'overflow-y: scroll; height: 20px;' });
var div = addDiv(null,
- { style: 'animation: rotate 100s; position: relative; top: 100px;' });
+ { style: 'animation: rotate 100s infinite; position: relative; top: 100px;' });
parentElement.appendChild(div);
var animation = div.getAnimations()[0];
var timeAtStart = document.timeline.currentTime;
ok(!animation.isRunningOnCompositor,
'The transform animation is not running on the compositor');
var markers;
@@ -445,17 +445,17 @@ waitForAllPaints(() => {
await SpecialPowers.pushPrefEnv({ set: [["ui.showHideScrollbars", 1]] });
// Make sure we start from the state right after requestAnimationFrame.
await waitForFrame();
var parentElement = addDiv(null,
{ style: 'overflow-y: scroll; height: 20px;' });
var div = addDiv(null,
- { style: 'animation: rotate 100s; position: relative; top: 100px;' });
+ { style: 'animation: rotate 100s infinite; position: relative; top: 100px;' });
parentElement.appendChild(div);
var animation = div.getAnimations()[0];
var timeAtStart = document.timeline.currentTime;
ok(!animation.isRunningOnCompositor,
'The transform animation is not running on the compositor');
var markers;
@@ -498,17 +498,17 @@ waitForAllPaints(() => {
// conformant Promise micro task.
if (isAndroid) {
return;
}
var parentElement = addDiv(null,
{ style: 'overflow: hidden;' });
var div = addDiv(null,
- { style: 'animation: move-in 100s;' });
+ { style: 'animation: move-in 100s infinite;' });
parentElement.appendChild(div);
var animation = div.getAnimations()[0];
var timeAtStart = document.timeline.currentTime;
ok(!animation.isRunningOnCompositor,
'The transform animation on out of view element ' +
'is not running on the compositor');
@@ -543,17 +543,17 @@ waitForAllPaints(() => {
}
// Make sure we start from the state right after requestAnimationFrame.
await waitForFrame();
var parentElement = addDiv(null,
{ style: 'overflow: hidden;' });
var div = addDiv(null,
- { style: 'animation: move-in 100s;' });
+ { style: 'animation: move-in 100s infinite;' });
parentElement.appendChild(div);
var animation = div.getAnimations()[0];
var timeAtStart = document.timeline.currentTime;
ok(!animation.isRunningOnCompositor,
'The transform animation on out of view element ' +
'is not running on the compositor');
@@ -580,16 +580,37 @@ waitForAllPaints(() => {
'Transform animation running on out of view element ' +
'should be unthrottled after around 200ms have elapsed. now: ' +
now + ' start time: ' + timeAtStart);
await ensureElementRemoval(parentElement);
}
);
+ add_task(async function finite_transform_animations_in_out_of_view_element() {
+ var parentElement = addDiv(null, { style: 'overflow: hidden;' });
+ var div = addDiv(null);
+ var animation =
+ div.animate({ transform: [ 'translateX(120%)', 'translateX(100%)' ] },
+ // This animation will move a bit but
+ // will remain out-of-view.
+ 100 * MS_PER_SEC);
+ parentElement.appendChild(div);
+
+ await animation.ready;
+ ok(!SpecialPowers.wrap(animation).isRunningOnCompositor);
+
+ var markers = await observeStyling(20);
+ is(markers.length, 20,
+ 'Finite transform animation in out-of-view element should never be ' +
+ 'throttled');
+
+ await ensureElementRemoval(parentElement);
+ });
+
add_task(async function restyling_main_thread_animations_in_scrolled_out_element() {
var parentElement = addDiv(null,
{ style: 'overflow-y: scroll; height: 20px;' });
var div = addDiv(null,
{ style: 'animation: background-color 100s; position: relative; top: 20px;' });
parentElement.appendChild(div);
var animation = div.getAnimations()[0];