зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1037316 part 1 - Return the same object when updating animations; r=dbaron
Previously when updating animations we'd generate a new list of animation objects then try to match up animations from the existing list and copy across state such as start times and notification flags. However, this means that from the API we end up returning different objects. This patch makes us maintain the same object identity when updating an existing animation. It does this by looking for matching animations in both lists. If it finds a match it copies the necessary information from the *new* animation to the *existing* animation (but preserving the start time, last notification etc.). Then, finally, it puts the *existing* animation in the list of *new* animations and removes the corresponding *new* animation. The existing animation is also removed from the list of existing animations so that it only matches once. The method used for matching is probably not intuitive but this is addressed in a subsequent patch in this series.
This commit is contained in:
Родитель
dcaa28eaea
Коммит
8e8054d839
|
@ -0,0 +1,50 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
@keyframes anim1 {
|
||||
to { left: 100px }
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
function addDiv() {
|
||||
var div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
return div;
|
||||
}
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
div.style.animation = 'anim1 100s';
|
||||
window.getComputedStyle(div).animationName;
|
||||
|
||||
var originalPlayer = div.getAnimationPlayers()[0];
|
||||
var originalStartTime = originalPlayer.startTime;
|
||||
var originalCurrentTime = originalPlayer.currentTime;
|
||||
|
||||
// Wait a moment so we can confirm the startTime doesn't change (and doesn't
|
||||
// simply reflect the current time).
|
||||
window.requestAnimationFrame(t.step_func(function() {
|
||||
div.style.animationDuration = '200s';
|
||||
window.getComputedStyle(div).animationDuration;
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
assert_equals(player, originalPlayer,
|
||||
'The same AnimationPlayer is returned after updating'
|
||||
+ ' animation duration');
|
||||
assert_equals(player.startTime, originalStartTime,
|
||||
'AnimationPlayers returned by getAnimationPlayers preserve'
|
||||
+ ' their startTime even when they are updated');
|
||||
// Sanity check
|
||||
assert_not_equals(player.currentTime, originalCurrentTime,
|
||||
'AnimationPlayer.currentTime has updated in next'
|
||||
+ ' requestAnimationFrame callback');
|
||||
div.remove();
|
||||
t.done();
|
||||
}));
|
||||
}, 'AnimationPlayers preserve their startTime when changed');
|
||||
|
||||
</script>
|
|
@ -74,7 +74,7 @@ test(function() {
|
|||
'getAnimationPlayers returns only one player for a CSS Animation'
|
||||
+ ' that targets multiple properties');
|
||||
div.remove();
|
||||
}, 'getAnimationsPlayers for multi-property animations');
|
||||
}, 'getAnimationPlayers for multi-property animations');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
|
@ -321,4 +321,34 @@ test(function() {
|
|||
div.remove();
|
||||
}, 'getAnimationPlayers for transition on unsupported property');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
div.style.animation = 'anim1 100s';
|
||||
window.getComputedStyle(div).animationName;
|
||||
|
||||
var originalPlayer = div.getAnimationPlayers()[0];
|
||||
|
||||
// Update pause state (an AnimationPlayer change)
|
||||
div.style.animationPlayState = 'paused';
|
||||
window.getComputedStyle(div).animationPlayState;
|
||||
var pausedPlayer = div.getAnimationPlayers()[0];
|
||||
// FIXME: Check pausedPlayer.playState has changed once the API is available
|
||||
// (bug 1037321)
|
||||
assert_equals(originalPlayer, pausedPlayer,
|
||||
'getAnimationPlayers returns the same objects even when their'
|
||||
+ ' play state changes');
|
||||
|
||||
// Update duration (an Animation change)
|
||||
div.style.animationDuration = '200s';
|
||||
window.getComputedStyle(div).animationDuration
|
||||
var extendedPlayer = div.getAnimationPlayers()[0];
|
||||
// FIXME: Check extendedPlayer.source.timing.duration has changed once the
|
||||
// API is available
|
||||
assert_equals(originalPlayer, extendedPlayer,
|
||||
'getAnimationPlayers returns the same objects even when their'
|
||||
+ ' duration changes');
|
||||
|
||||
div.remove();
|
||||
}, 'getAnimationPlayers returns objects with the same identity');
|
||||
|
||||
</script>
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
[animation-timeline/test_animation-timeline.html]
|
||||
[css-integration/test_element-get-animation-players.html]
|
||||
[css-integration/test_animations-dynamic-changes.html]
|
||||
|
|
|
@ -269,7 +269,7 @@ nsAnimationManager::CheckAnimationRule(nsStyleContext* aStyleContext,
|
|||
if (!collection->mAnimations.IsEmpty()) {
|
||||
for (uint32_t newIdx = 0, newEnd = newAnimations.Length();
|
||||
newIdx != newEnd; ++newIdx) {
|
||||
nsRefPtr<ElementAnimation> newAnim = newAnimations[newIdx];
|
||||
ElementAnimation* newAnim = newAnimations[newIdx];
|
||||
|
||||
// Find the matching animation with this name in the old list
|
||||
// of animations. Because of this code, they must all have
|
||||
|
@ -279,10 +279,14 @@ nsAnimationManager::CheckAnimationRule(nsStyleContext* aStyleContext,
|
|||
// different pause states, they, well, get what they deserve.
|
||||
// We'll use the last one since it's more likely to be the one
|
||||
// doing something.
|
||||
const ElementAnimation* oldAnim = nullptr;
|
||||
for (uint32_t oldIdx = collection->mAnimations.Length();
|
||||
oldIdx-- != 0; ) {
|
||||
const ElementAnimation* a = collection->mAnimations[oldIdx];
|
||||
//
|
||||
// FIXME: This is wrong: we should iterate through both lists in the
|
||||
// same direction. We'll fix that in a subsequent patch in this
|
||||
// series.
|
||||
nsRefPtr<ElementAnimation> oldAnim;
|
||||
size_t oldIdx = collection->mAnimations.Length();
|
||||
while (oldIdx-- != 0) {
|
||||
ElementAnimation* a = collection->mAnimations[oldIdx];
|
||||
if (a->mName == newAnim->mName) {
|
||||
oldAnim = a;
|
||||
break;
|
||||
|
@ -292,25 +296,41 @@ nsAnimationManager::CheckAnimationRule(nsStyleContext* aStyleContext,
|
|||
continue;
|
||||
}
|
||||
|
||||
newAnim->mStartTime = oldAnim->mStartTime;
|
||||
newAnim->mLastNotification = oldAnim->mLastNotification;
|
||||
// Update the old from the new so we can keep the original object
|
||||
// identity (and any expando properties attached to it).
|
||||
oldAnim->mTiming = newAnim->mTiming;
|
||||
oldAnim->mProperties = newAnim->mProperties;
|
||||
|
||||
if (oldAnim->IsPaused()) {
|
||||
if (newAnim->IsPaused()) {
|
||||
// Copy pause start just like start time.
|
||||
newAnim->mPauseStart = oldAnim->mPauseStart;
|
||||
} else {
|
||||
// Handle change in pause state by adjusting start
|
||||
// time to unpause.
|
||||
const TimeStamp& now = timeline->GetCurrentTimeStamp();
|
||||
if (!now.IsNull()) {
|
||||
// FIXME: Once we store the start time and pause start as
|
||||
// offsets (not timestamps) we should be able to update the
|
||||
// start time to something more appropriate when now IsNull.
|
||||
newAnim->mStartTime += now - oldAnim->mPauseStart;
|
||||
}
|
||||
// Reset compositor state so animation will be re-synchronized.
|
||||
oldAnim->mIsRunningOnCompositor = false;
|
||||
|
||||
// Handle changes in play state.
|
||||
if (!oldAnim->IsPaused() && newAnim->IsPaused()) {
|
||||
// Start pause at current time.
|
||||
oldAnim->mPauseStart = timeline->GetCurrentTimeStamp();
|
||||
} else if (oldAnim->IsPaused() && !newAnim->IsPaused()) {
|
||||
const TimeStamp& now = timeline->GetCurrentTimeStamp();
|
||||
if (!now.IsNull()) {
|
||||
// FIXME: Once we store the start time and pause start as
|
||||
// offsets (not timestamps) we should be able to update the
|
||||
// start time to something more appropriate when now IsNull.
|
||||
// Handle change in pause state by adjusting start time to
|
||||
// unpause.
|
||||
oldAnim->mStartTime += now - oldAnim->mPauseStart;
|
||||
}
|
||||
oldAnim->mPauseStart = TimeStamp();
|
||||
}
|
||||
oldAnim->mPlayState = newAnim->mPlayState;
|
||||
|
||||
// Replace new animation with the (updated) old one and remove the
|
||||
// old one from the array so we don't try to match it any more.
|
||||
//
|
||||
// Although we're doing this while iterating this is safe because
|
||||
// we're not changing the length of newAnimations and we've finished
|
||||
// iterating over the list of old iterations.
|
||||
newAnim = nullptr;
|
||||
newAnimations.ReplaceElementAt(newIdx, oldAnim);
|
||||
collection->mAnimations.RemoveElementAt(oldIdx);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
Загрузка…
Ссылка в новой задаче