This patch does basically throttle animations on visibility:hidden element
and unthrottle it once the animating element became visible or a child of the
animating element became visible. But still there are some cases that we don't
throttle such animations perfectly. For example;
div.style.visibility = 'hidden'; // the 'div' has no children at this moment
div.animate(..);
// The animation is throttled
div.appendChild(visibleChild);
// The animation isn't throttled
visibleChild.style.visibility = 'hidden';
// Now the animation should be throttled again, but actually it's not.
To throttle this case properly, when the |visibleChild|'s visibility changed
to hidden, we would need to do either
1) Check all siblings of the |visibleChild| have no visible children
or
2) The parent element stores visible children count somewhere and decrease it
and check whether the count is zero
To achieve 1) we need to walk up ancestors and their siblings, actually it's
inefficient.
2) is somewhat similar to what we already do for animating images but it's hard
to reuse it for CSS animations since it does not take into account that
descendants' visibilities.
Another example that this patch does not optimize is the the case where
animating element has children whose visibility is inherited and the element
itself initially visible something like this;
let child = document.createElement('div'); // child visibility is 'inherit'
div.appendChild(child);
div.animate(..); // the 'div' is visible
// The animation isn't throttled since the animating element is visible
div.style.visiblily = 'hidden';
// Now the animation should be throttled, but it's not since this patch does
// not descend down all descendants to check they are invisible or not when the
// animating element visibility changed to hidden.
This patch adds a test case for this case introduced with todo_is().
Another test case added in this patch fails if we don't use
nsPlaceholderFrame::GetRealFrameFor() in HasNoVisibleDescendants().
MozReview-Commit-ID: BJwzQvP9Yc4
--HG--
extra : rebase_source : ceb95bdce1042cbfc16751d6d023fc6feee5845e
This new change hint doesn't influence layout so that it can be regarded
as nsChangeHint_Hints_CanIgnoreIfNotVisible. Note that if visibility changed
from collapse or to collapse, we set NS_STYLE_HINT_REFLOW separetely.
MozReview-Commit-ID: AirDWeBYVKG
--HG--
extra : rebase_source : 1cd03a78a522b1a6965ba73ebf002ddacb0ab4f2
tweakExpectedRestyleCount() is supposed to work with observeStyle() which is
called right after tweakExpectedRestyleCount(). That's because
tweakExpectedRestyleCount() adjusts the expected restyle count which will
happen in continuous frames that begins from the startsRightNow() call,
especially a redundant restyle might be observed in the last frame if the
animation did not begin at the current timeline time and if the Promise inside
the last requestAnimationFrame is fulfilled after restyling happened.
Instead of using tweakExpectedRestyleCount(), we need to check whether the
animation begun at the current timeline time when the animation started, and if
the animation begun at the current timeline time, we don't observe the
superfluous restyle in a frame after the animation element was re-attached.
MozReview-Commit-ID: 6TLQERSSbjU
--HG--
extra : rebase_source : 1a1dcb56b889bebedb44346bbc315ec01870cf79
Since the function assumes that both of actual and expected values
have the same precision requirements.
MozReview-Commit-ID: 4C3TAH6mUVg
--HG--
extra : rebase_source : 1e40e489745b0d9047d34e851a5f043db616323e
This assertion is supposed to be used where the first argument has a tolerance
but the second argument doesn't have such tolerance. Whereas
assert_times_equal() is supposed to be used for the case both arguments have
the same tolerance, actually it hasn't, it will be fixed in a subsequent patch
in this patch series.
MozReview-Commit-ID: FEDHilbX2rm
--HG--
extra : rebase_source : e773902b474bd9a411e7bb3f234702a93547ebba
For opacity property, we only generate nsChangeHint_UpdateUsesOpacity,
nsChangeHint_UpdateOpacityLayer and nsChangeHint_RepaintFrame. All of them are
included in nsChangeHint_Hints_CanIgnoreIfNotVisible. So we can throttle
opacity animations on out-of-view elements whatever underlying opacity value is.
MozReview-Commit-ID: FdQJbItAndG
--HG--
extra : rebase_source : d011270e4e3e1adc1782665a592fb3fac60f9174
The animation in the test case is not actually additive animation, it's just a
missing keyframe animation and its composite operation is actually 'replace'.
MozReview-Commit-ID: 4A29V5Ke2hF
--HG--
extra : rebase_source : 63b6c7f52943786d06c52b9baa7e0f4f151781ac
The pref has been enabled by default since firefox 49, so it's not worth
checking the pref in test.
MozReview-Commit-ID: 5ADIFaV1ue
--HG--
extra : rebase_source : 8490cc7988cc1e7fe3a650c4f1334b4ed11c7105
It's already specified to true in test_restyles.html.
MozReview-Commit-ID: JMItunKYwIs
--HG--
extra : rebase_source : d54368a93857d8d2a86220be55091735caa074e9
This patch reflects the following change to the Web Animations spec:
abf76745b5
MozReview-Commit-ID: A2GD1igUf5f
--HG--
extra : rebase_source : 8129f6386b144adebc3bf0320ca7d6bfbba7a2e9
These tests can be passed now, I don't know what fixed them, presumably
bug 1421476 and bug 1379515 fixed it.
MozReview-Commit-ID: 2srFKTWWvK
--HG--
extra : rebase_source : 418d366ade78d5a9994cb9bbbab39c4c0b42a2a4
Animation.finish() in a micro task for Animation.ready leads to a restyle and
a redundant restyle due to bug 1415457. The redundant restyle has been observed
in a single frame without the conformant Promise handling. But once we have the
conformant Promise handling we can tell it in a later frame.
(see https://bugzilla.mozilla.org/show_bug.cgi?id=1415457#c1 to know what's
going on there)
MozReview-Commit-ID: FoojunfYZ6k
--HG--
extra : rebase_source : 2820dce4513329fb648ef387d58cac469f680a6d
The expected restyle count depends both on animation state and micro task
handling.
If we have the conformant Promise handling and if the animation begins at the
current time, we will observe 1 less restyles than observed frames since we
skip the first restyle in the initial frame. This represents correctly what
we do and what we *should* do.
If we don't have the conformant Promise handling and if the animation doesn't
begin at the current time, we will observe 1 more restyles than obsered frames
since the Promise in observeStyling (precisely the Promise is inside the
callback for requestAnimationFrame) is fulfilled once after a restyling process
followed by the requestAnimationFrame.
MozReview-Commit-ID: FLhSRx4y1V7
--HG--
extra : rebase_source : 59e2bb2439f69bc1415a441c0b84a81463d8229f
The test case checks document timeline's currentTime to make sure that we are
in the first frame which happened after 200ms since the animation started. It
is unfortunate for us from the point of view of restyling that the first frame
includes the restyling process in the frame without the conformant Promise
handling, doesn't include the process with the Promise handling. So we need
to tweak there depends on the Promise handling.
I did intentionally add the forked version of the function with the same name
since branching depending on the conformant flag inside the original function
will be hard to understand and also we can easily remove the original version
once we have the conformant Promise handling.
FWIW, here is the diff between them:
@@ -365,6 +365,9 @@ 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,
@@ -379,13 +382,17 @@ waitForAllPaints(() => {
var markers;
var now;
while (true) {
- markers = await observeStyling(1);
- // Check restyle markers until 200ms is elapsed.
now = document.timeline.currentTime;
if ((now - timeAtStart) >= 200) {
+ // If the current time has elapsed over 200ms since the animation was
+ // created, it means that the animation should have already
+ // unthrottled in this tick, let's see what observes in this tick's
+ // restyling process.
+ markers = await observeStyling(1);
break;
}
+ markers = await observeStyling(1);
is(markers.length, 0,
'Transform animation running on the element which is scrolled out ' +
'should be throttled until 200ms is elapsed');
MozReview-Commit-ID: 3WfY6aVnsXk
--HG--
extra : rebase_source : ddf51217f03fc1bfe421c344a7a7811dc591a9af
Animation.finish() in a micro task for Animation.ready leads to a restyle and
a redundant restyle due to bug 1415457. The redundant restyle has been observed
in a single frame without the conformant Promise handling. But once we have the
conformant Promise handling we can tell it in a later frame.
(see https://bugzilla.mozilla.org/show_bug.cgi?id=1415457#c1 to know what's
going on there)
MozReview-Commit-ID: FoojunfYZ6k
--HG--
extra : rebase_source : 2820dce4513329fb648ef387d58cac469f680a6d
The expected restyle count depends both on animation state and micro task
handling.
If we have the conformant Promise handling and if the animation begins at the
current time, we will observe 1 less restyles than observed frames since we
skip the first restyle in the initial frame. This represents correctly what
we do and what we *should* do.
If we don't have the conformant Promise handling and if the animation doesn't
begin at the current time, we will observe 1 more restyles than obsered frames
since the Promise in observeStyling (precisely the Promise is inside the
callback for requestAnimationFrame) is fulfilled once after a restyling process
followed by the requestAnimationFrame.
MozReview-Commit-ID: FLhSRx4y1V7
--HG--
extra : rebase_source : 59e2bb2439f69bc1415a441c0b84a81463d8229f
The test case checks document timeline's currentTime to make sure that we are
in the first frame which happened after 200ms since the animation started. It
is unfortunate for us from the point of view of restyling that the first frame
includes the restyling process in the frame without the conformant Promise
handling, doesn't include the process with the Promise handling. So we need
to tweak there depends on the Promise handling.
I did intentionally add the forked version of the function with the same name
since branching depending on the conformant flag inside the original function
will be hard to understand and also we can easily remove the original version
once we have the conformant Promise handling.
FWIW, here is the diff between them:
@@ -365,6 +365,9 @@ 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,
@@ -379,13 +382,17 @@ waitForAllPaints(() => {
var markers;
var now;
while (true) {
- markers = await observeStyling(1);
- // Check restyle markers until 200ms is elapsed.
now = document.timeline.currentTime;
if ((now - timeAtStart) >= 200) {
+ // If the current time has elapsed over 200ms since the animation was
+ // created, it means that the animation should have already
+ // unthrottled in this tick, let's see what observes in this tick's
+ // restyling process.
+ markers = await observeStyling(1);
break;
}
+ markers = await observeStyling(1);
is(markers.length, 0,
'Transform animation running on the element which is scrolled out ' +
'should be throttled until 200ms is elapsed');
MozReview-Commit-ID: 3WfY6aVnsXk
--HG--
extra : rebase_source : ddf51217f03fc1bfe421c344a7a7811dc591a9af
It is possible that animating computed style becomes the same value as the
value in the previous tick if Animation.mPendingReadyTime is pretty close
to the timeline current time.
MozReview-Commit-ID: COuur4Wlufx
--HG--
extra : rebase_source : 5dea9ef0bce7cb606dc290277e04b779fe608bbd
The 'is' check there will be replaced 'todo_is' after the commit for the new
micro task checkpoint.
MozReview-Commit-ID: EGFZWy8BD3O
--HG--
extra : rebase_source : 034717a9e7aac0edb82613321ec056ed07cdb996
If element which has script animations is detached between Animation::Tick and
styling process, the element persists in EffectCompositor::mElementsToRestyle
(bug 1417354). When the element is attached to the document again, the element
in mElementsToRestyle is considered as a target element that needs to be
traversed in the first animation-only restyling. Thus the remaining restyle
request can be observed with the new micro task checkpoint for animations.
MozReview-Commit-ID: F6gs2QXcZ8X
--HG--
extra : rebase_source : 09ccf76e4d28ee55facc5a62f3e8f08e0eeb3e03
Similar to the previous commit, if Animation.pause() is called between
restyling process and the next refresh driver's tick and if we wait for
Animation.ready after the pause(), there should be one restyle that hasn't
yet processed.
MozReview-Commit-ID: JnpwhOuDvPz
--HG--
extra : rebase_source : b6f14e05837754d1bd51c8a50ce8d82a0c7d751a
If Animation.finished promise was fulfilled in Animation::Tick, at the moment we
haven't yet restyled the final animation value. So once we have new micro task
checkpoint there we can observe a restyle marker after Animation.finished was
fulfilled.
MozReview-Commit-ID: LiEl4Iu2Cbr
--HG--
extra : rebase_source : d88b037eba2fd07d75a3ab429c028e23e5137b52
'finish_from_pause' supposes that finish() is called after pause pending state
is finished. Whereas 'finish_from_pause_pending' supposes that finish() is
called during pause pending state. We should check the state respectively.
MozReview-Commit-ID: DUq2ghK6h9I
--HG--
extra : rebase_source : 2d4ded71387e75efcff9bb184b8e51475bc8bc56
With this patch we still use a global MutationObserver and a single target
element and re-use them in all test cases. Eventually each test should create
a target element and a MutationObserver to avoid explicit cleanup jobs (e.g.
clearing styles and flushing styles for the global target element), but it's
not in the scope of this bug.
MozReview-Commit-ID: IqEjMbTrpAK
--HG--
extra : rebase_source : 7463c21ce946f5c15f2b7bc4a71c90dba784385d