nsIStyleRule* nsAnimationManager::CheckAnimationRule(nsStyleContext* aStyleContext, mozilla::dom::Element* aElement) { if (!mPresContext->IsProcessingAnimationStyleChange()) { if (!mPresContext->IsDynamic()) { // For print or print preview, ignore animations. return nullptr; } // Everything that causes our animation data to change triggers a // style change, which in turn triggers a non-animation restyle. // Likewise, when we initially construct frames, we're not in a // style change, but also not in an animation restyle. const nsStyleDisplay *disp = aStyleContext->StyleDisplay(); ElementAnimations *ea = GetElementAnimations(aElement, aStyleContext->GetPseudoType(), false); if (!ea && disp->mAnimationNameCount == 1 && disp->mAnimations[0].GetName().IsEmpty()) { return nullptr; } // build the animations list InfallibleTArray<ElementAnimation> newAnimations; BuildAnimations(aStyleContext, newAnimations); if (newAnimations.IsEmpty()) { if (ea) { ea->Destroy(); } return nullptr; } TimeStamp refreshTime = mPresContext->RefreshDriver()->MostRecentRefresh(); if (ea) { ea->mStyleRule = nullptr; ea->mStyleRuleRefreshTime = TimeStamp(); ea->UpdateAnimationGeneration(mPresContext); // Copy over the start times and (if still paused) pause starts // for each animation (matching on name only) that was also in the // old list of animations. // This means that we honor dynamic changes, which isn't what the // spec says to do, but WebKit seems to honor at least some of // them. See // http://lists.w3.org/Archives/Public/www-style/2011Apr/0079.html // In order to honor what the spec said, we'd copy more data over // (or potentially optimize BuildAnimations to avoid rebuilding it // in the first place). if (!ea->mAnimations.IsEmpty()) { for (uint32_t newIdx = 0, newEnd = newAnimations.Length(); newIdx != newEnd; ++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 // the same start time, though they might differ in pause // state. So if a page uses multiple copies of the same // animation in one element's animation list, and gives them // 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 = ea->mAnimations.Length(); oldIdx-- != 0; ) { const ElementAnimation *a = &ea->mAnimations[oldIdx]; if (a->mName == newAnim->mName) { oldAnim = a; break; } } if (!oldAnim) { continue; } newAnim->mStartTime = oldAnim->mStartTime; newAnim->mLastNotification = oldAnim->mLastNotification; 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. newAnim->mStartTime += refreshTime - oldAnim->mPauseStart; } } } } } else { ea = GetElementAnimations(aElement, aStyleContext->GetPseudoType(), true); } ea->mAnimations.SwapElements(newAnimations); ea->mNeedsRefreshes = true; ea->EnsureStyleRuleFor(refreshTime, mPendingEvents, false); // We don't actually dispatch the mPendingEvents now. We'll either // dispatch them the next time we get a refresh driver notification // or the next time somebody calls // nsPresShell::FlushPendingNotifications. if (!mPendingEvents.IsEmpty()) { mPresContext->Document()->SetNeedStyleFlush(); } } return GetAnimationRule(aElement, aStyleContext->GetPseudoType()); }
nsIStyleRule* nsAnimationManager::CheckAnimationRule(nsStyleContext* aStyleContext, mozilla::dom::Element* aElement) { if (!mPresContext->IsProcessingAnimationStyleChange()) { if (!mPresContext->IsDynamic()) { // For print or print preview, ignore animations. return nullptr; } // Everything that causes our animation data to change triggers a // style change, which in turn triggers a non-animation restyle. // Likewise, when we initially construct frames, we're not in a // style change, but also not in an animation restyle. const nsStyleDisplay* disp = aStyleContext->StyleDisplay(); ElementAnimationCollection* collection = GetElementAnimations(aElement, aStyleContext->GetPseudoType(), false); if (!collection && disp->mAnimationNameCount == 1 && disp->mAnimations[0].GetName().IsEmpty()) { return nullptr; } // build the animations list dom::AnimationTimeline* timeline = aElement->OwnerDoc()->Timeline(); ElementAnimationPtrArray newAnimations; BuildAnimations(aStyleContext, timeline, newAnimations); if (newAnimations.IsEmpty()) { if (collection) { collection->Destroy(); } return nullptr; } if (collection) { collection->mStyleRule = nullptr; collection->mStyleRuleRefreshTime = TimeStamp(); collection->UpdateAnimationGeneration(mPresContext); // Copy over the start times and (if still paused) pause starts // for each animation (matching on name only) that was also in the // old list of animations. // This means that we honor dynamic changes, which isn't what the // spec says to do, but WebKit seems to honor at least some of // them. See // http://lists.w3.org/Archives/Public/www-style/2011Apr/0079.html // In order to honor what the spec said, we'd copy more data over // (or potentially optimize BuildAnimations to avoid rebuilding it // in the first place). if (!collection->mAnimations.IsEmpty()) { for (size_t newIdx = newAnimations.Length(); newIdx-- != 0;) { ElementAnimation* newAnim = newAnimations[newIdx]; // Find the matching animation with this name in the old list // of animations. We iterate through both lists in a backwards // direction which means that if there are more animations in // the new list of animations with a given name than in the old // list, it will be the animations towards the of the beginning of // the list that do not match and are treated as new animations. 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; } } if (!oldAnim) { continue; } // 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; // 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 { collection = GetElementAnimations(aElement, aStyleContext->GetPseudoType(), true); } collection->mAnimations.SwapElements(newAnimations); collection->mNeedsRefreshes = true; TimeStamp refreshTime = mPresContext->RefreshDriver()->MostRecentRefresh(); UpdateStyleAndEvents(collection, refreshTime, EnsureStyleRule_IsNotThrottled); // We don't actually dispatch the mPendingEvents now. We'll either // dispatch them the next time we get a refresh driver notification // or the next time somebody calls // nsPresShell::FlushPendingNotifications. if (!mPendingEvents.IsEmpty()) { mPresContext->Document()->SetNeedStyleFlush(); } } return GetAnimationRule(aElement, aStyleContext->GetPseudoType()); }