Exemplo n.º 1
0
nsIStyleRule*
nsAnimationManager::CheckAnimationRule(nsStyleContext* aStyleContext,
                                       mozilla::dom::Element* aElement)
{
  // FIXME (bug 960465): This test should go away.
  if (!mPresContext->RestyleManager()->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();
    AnimationPlayerCollection* collection =
      GetAnimationPlayers(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();
    AnimationPlayerPtrArray newPlayers;
    BuildAnimations(aStyleContext, aElement, timeline, newPlayers);

    if (newPlayers.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->mPlayers.IsEmpty()) {

        for (size_t newIdx = newPlayers.Length(); newIdx-- != 0;) {
          AnimationPlayer* newPlayer = newPlayers[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<CSSAnimationPlayer> oldPlayer;
          size_t oldIdx = collection->mPlayers.Length();
          while (oldIdx-- != 0) {
            CSSAnimationPlayer* a =
              collection->mPlayers[oldIdx]->AsCSSAnimationPlayer();
            MOZ_ASSERT(a, "All players in the CSS Animation collection should"
                          " be CSSAnimationPlayer objects");
            if (a->Name() == newPlayer->Name()) {
              oldPlayer = a;
              break;
            }
          }
          if (!oldPlayer) {
            continue;
          }

          // Update the old from the new so we can keep the original object
          // identity (and any expando properties attached to it).
          if (oldPlayer->GetSource() && newPlayer->GetSource()) {
            Animation* oldAnim = oldPlayer->GetSource();
            Animation* newAnim = newPlayer->GetSource();
            oldAnim->Timing() = newAnim->Timing();
            oldAnim->Properties() = newAnim->Properties();
          }

          // Reset compositor state so animation will be re-synchronized.
          oldPlayer->ClearIsRunningOnCompositor();

          // Handle changes in play state.
          // CSSAnimationPlayer takes care of override behavior so that,
          // for example, if the author has called pause(), that will
          // override the animation-play-state.
          // (We should check newPlayer->IsStylePaused() but that requires
          //  downcasting to CSSAnimationPlayer and we happen to know that
          //  newPlayer will only ever be paused by calling PauseFromStyle
          //  making IsPaused synonymous in this case.)
          if (!oldPlayer->IsStylePaused() && newPlayer->IsPaused()) {
            oldPlayer->PauseFromStyle();
          } else if (oldPlayer->IsStylePaused() && !newPlayer->IsPaused()) {
            oldPlayer->PlayFromStyle();
          }

          // 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 newPlayers and we've finished
          // iterating over the list of old iterations.
          newPlayer = nullptr;
          newPlayers.ReplaceElementAt(newIdx, oldPlayer);
          collection->mPlayers.RemoveElementAt(oldIdx);
        }
      }
    } else {
      collection =
        GetAnimationPlayers(aElement, aStyleContext->GetPseudoType(), true);
    }
    collection->mPlayers.SwapElements(newPlayers);
    collection->mNeedsRefreshes = true;
    collection->Tick();

    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());
}
Exemplo n.º 2
0
void
nsAnimationManager::GetEventsForCurrentTime(AnimationPlayerCollection*
                                              aCollection,
                                            EventArray& aEventsToDispatch)
{
  for (size_t playerIdx = aCollection->mPlayers.Length(); playerIdx-- != 0; ) {
    AnimationPlayer* player = aCollection->mPlayers[playerIdx];
    Animation* anim = player->GetSource();
    if (!anim) {
      continue;
    }

    ComputedTiming computedTiming = anim->GetComputedTiming();

    switch (computedTiming.mPhase) {
      case ComputedTiming::AnimationPhase_Null:
      case ComputedTiming::AnimationPhase_Before:
        // Do nothing
        break;

      case ComputedTiming::AnimationPhase_Active:
        // Dispatch 'animationstart' or 'animationiteration' when needed.
        if (computedTiming.mCurrentIteration != anim->LastNotification()) {
          // Notify 'animationstart' even if a negative delay puts us
          // past the first iteration.
          // Note that when somebody changes the animation-duration
          // dynamically, this will fire an extra iteration event
          // immediately in many cases.  It's not clear to me if that's the
          // right thing to do.
          uint32_t message =
            anim->LastNotification() == Animation::LAST_NOTIFICATION_NONE
                                        ? NS_ANIMATION_START
                                        : NS_ANIMATION_ITERATION;
          anim->SetLastNotification(computedTiming.mCurrentIteration);
          TimeDuration iterationStart =
            anim->Timing().mIterationDuration *
            computedTiming.mCurrentIteration;
          TimeDuration elapsedTime =
            std::max(iterationStart, anim->InitialAdvance());
          AnimationEventInfo ei(aCollection->mElement, player->Name(), message,
                                StickyTimeDuration(elapsedTime),
                                aCollection->PseudoElement());
          aEventsToDispatch.AppendElement(ei);
        }
        break;

      case ComputedTiming::AnimationPhase_After:
        // If we skipped the animation interval entirely, dispatch
        // 'animationstart' first
        if (anim->LastNotification() == Animation::LAST_NOTIFICATION_NONE) {
          // Notifying for start of 0th iteration.
          // (This is overwritten below but we set it here to maintain
          // internal consistency.)
          anim->SetLastNotification(0);
          StickyTimeDuration elapsedTime =
            std::min(StickyTimeDuration(anim->InitialAdvance()),
                     computedTiming.mActiveDuration);
          AnimationEventInfo ei(aCollection->mElement,
                                player->Name(), NS_ANIMATION_START,
                                elapsedTime, aCollection->PseudoElement());
          aEventsToDispatch.AppendElement(ei);
        }
        // Dispatch 'animationend' when needed.
        if (anim->LastNotification() != Animation::LAST_NOTIFICATION_END) {
          anim->SetLastNotification(Animation::LAST_NOTIFICATION_END);
          AnimationEventInfo ei(aCollection->mElement,
                                player->Name(), NS_ANIMATION_END,
                                computedTiming.mActiveDuration,
                                aCollection->PseudoElement());
          aEventsToDispatch.AppendElement(ei);
        }
        break;
    }
  }
}