/* virtual */ void nsAnimationManager::WillRefresh(mozilla::TimeStamp aTime) { NS_ABORT_IF_FALSE(mPresContext, "refresh driver should not notify additional observers " "after pres context has been destroyed"); if (!mPresContext->GetPresShell()) { // Someone might be keeping mPresContext alive past the point // where it has been torn down; don't bother doing anything in // this case. But do get rid of all our transitions so we stop // triggering refreshes. RemoveAllElementData(); return; } // FIXME: check that there's at least one style rule that's not // in its "done" state, and if there isn't, remove ourselves from // the refresh driver (but leave the animations!). for (PRCList *l = PR_LIST_HEAD(&mElementData); l != &mElementData; l = PR_NEXT_LINK(l)) { ElementAnimations *ea = static_cast<ElementAnimations*>(l); nsRefPtr<css::AnimValuesStyleRule> oldStyleRule = ea->mStyleRule; bool shouldInterpolate = !ea->CanPerformOnCompositorThread(); ea->EnsureStyleRuleFor(mPresContext->RefreshDriver()->MostRecentRefresh(), mPendingEvents, shouldInterpolate); if (oldStyleRule != ea->mStyleRule && shouldInterpolate) { ea->PostRestyleForAnimation(mPresContext); } } DispatchEvents(); // may destroy us }
void nsAnimationManager::FlushAnimations(FlushFlags aFlags) { // FIXME: check that there's at least one style rule that's not // in its "done" state, and if there isn't, remove ourselves from // the refresh driver (but leave the animations!). TimeStamp now = mPresContext->RefreshDriver()->MostRecentRefresh(); bool didThrottle = false; for (PRCList *l = PR_LIST_HEAD(&mElementData); l != &mElementData; l = PR_NEXT_LINK(l)) { ElementAnimations *ea = static_cast<ElementAnimations*>(l); bool canThrottleTick = aFlags == Can_Throttle && ea->CanPerformOnCompositorThread( CommonElementAnimationData::CanAnimateFlags(0)) && ea->CanThrottleAnimation(now); nsRefPtr<css::AnimValuesStyleRule> oldStyleRule = ea->mStyleRule; ea->EnsureStyleRuleFor(now, mPendingEvents, canThrottleTick); if (oldStyleRule != ea->mStyleRule) { ea->PostRestyleForAnimation(mPresContext); } else { didThrottle = true; } } if (didThrottle) { mPresContext->Document()->SetNeedStyleFlush(); } DispatchEvents(); // may destroy us }
nsIStyleRule* nsAnimationManager::CheckAnimationRule(nsStyleContext* aStyleContext, mozilla::dom::Element* aElement) { if (!mPresContext->IsProcessingAnimationStyleChange()) { // 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->GetStyleDisplay(); ElementAnimations *ea = GetElementAnimations(aElement, aStyleContext->GetPseudoType(), false); if (!ea && disp->mAnimations.Length() == 1 && disp->mAnimations[0].GetName().IsEmpty()) { return nsnull; } // build the animations list InfallibleTArray<ElementAnimation> newAnimations; BuildAnimations(aStyleContext, newAnimations); if (newAnimations.IsEmpty()) { if (ea) { ea->Destroy(); } return nsnull; } TimeStamp refreshTime = mPresContext->RefreshDriver()->MostRecentRefresh(); if (ea) { if (ea->CanPerformOnCompositorThread()) { // We must invalidate to ensure that we rebuild the layer, // since we are not interpolating on the main thread. nsIFrame* frame = mPresContext->GetRootPresContext()->PresShell()->GetRootFrame(); frame->InvalidateWithFlags(frame->GetRect(), nsIFrame::INVALIDATE_NO_THEBES_LAYERS); } // The cached style rule is invalid. ea->mStyleRule = nsnull; ea->mStyleRuleRefreshTime = TimeStamp(); // 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 (PRUint32 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 = nsnull; for (PRUint32 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; } } } } ea->mAnimations.SwapElements(newAnimations); } else { ea = GetElementAnimations(aElement, aStyleContext->GetPseudoType(), true); ea->mAnimations.SwapElements(newAnimations); AddElementData(ea); } ea->mNeedsRefreshes = true; ea->EnsureStyleRuleFor(refreshTime, mPendingEvents); // 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()); }