layout/style/AnimationCommon.h
author Cosmin Sabou <csabou@mozilla.com>
Thu, 02 Aug 2018 19:59:53 +0300
changeset 484986 f7f4ba3486b5e44b6ccdc3b150f5aa0aab76ffb7
parent 471709 30687af2ab0670c3afb7ee69953f3e8591e665eb
child 489733 1b236230b856b1aaf499723efce2dd1b7ca83a89
permissions -rw-r--r--
Backed out changeset 5950c9d63c3b (bug 1090497) for build bustages on several files. CLOSED TREE

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_css_AnimationCommon_h
#define mozilla_css_AnimationCommon_h

#include "mozilla/AnimationCollection.h"
#include "mozilla/LinkedList.h"
#include "mozilla/dom/Animation.h"
#include "mozilla/Attributes.h" // For MOZ_NON_OWNING_REF
#include "mozilla/Assertions.h"
#include "mozilla/TimingParams.h"
#include "mozilla/dom/Nullable.h"
#include "nsContentUtils.h"

class nsPresContext;

namespace mozilla {
enum class CSSPseudoElementType : uint8_t;

namespace dom {
class Element;
}

template <class AnimationType>
class CommonAnimationManager {
public:
  explicit CommonAnimationManager(nsPresContext *aPresContext)
    : mPresContext(aPresContext)
  {
  }

  // NOTE:  This can return null after Disconnect().
  nsPresContext* PresContext() const { return mPresContext; }

  /**
   * Notify the manager that the pres context is going away.
   */
  void Disconnect()
  {
    // Content nodes might outlive the transition or animation manager.
    RemoveAllElementCollections();

    mPresContext = nullptr;
  }

  /**
   * Stop animations on the element. This method takes the real element
   * rather than the element for the generated content for animations on
   * ::before and ::after.
   */
  void StopAnimationsForElement(dom::Element* aElement,
                                CSSPseudoElementType aPseudoType)
  {
    MOZ_ASSERT(aElement);
    AnimationCollection<AnimationType>* collection =
      AnimationCollection<AnimationType>::GetAnimationCollection(aElement,
                                                                 aPseudoType);
    if (!collection) {
      return;
    }

    nsAutoAnimationMutationBatch mb(aElement->OwnerDoc());
    collection->Destroy();
  }

protected:
  virtual ~CommonAnimationManager()
  {
    MOZ_ASSERT(!mPresContext, "Disconnect should have been called");
  }

  void AddElementCollection(AnimationCollection<AnimationType>* aCollection)
  {
    mElementCollections.insertBack(aCollection);
  }
  void RemoveAllElementCollections()
  {
    while (AnimationCollection<AnimationType>* head =
           mElementCollections.getFirst()) {
      head->Destroy(); // Note: this removes 'head' from mElementCollections.
    }
  }

  LinkedList<AnimationCollection<AnimationType>> mElementCollections;
  nsPresContext *mPresContext; // weak (non-null from ctor to Disconnect)
};

/**
 * Utility class for referencing the element that created a CSS animation or
 * transition. It is non-owning (i.e. it uses a raw pointer) since it is only
 * expected to be set by the owned animation while it actually being managed
 * by the owning element.
 *
 * This class also abstracts the comparison of an element/pseudo-class pair
 * for the sake of composite ordering since this logic is common to both CSS
 * animations and transitions.
 *
 * (We call this OwningElementRef instead of just OwningElement so that we can
 * call the getter on CSSAnimation/CSSTransition OwningElement() without
 * clashing with this object's contructor.)
 */
class OwningElementRef final
{
public:
  OwningElementRef() = default;

  explicit OwningElementRef(const NonOwningAnimationTarget& aTarget)
    : mTarget(aTarget)
  { }

  OwningElementRef(dom::Element& aElement,
                   CSSPseudoElementType aPseudoType)
    : mTarget(&aElement, aPseudoType)
  { }

  bool Equals(const OwningElementRef& aOther) const
  {
    return mTarget == aOther.mTarget;
  }

  bool LessThan(const OwningElementRef& aOther) const
  {
    MOZ_ASSERT(mTarget.mElement && aOther.mTarget.mElement,
               "Elements to compare should not be null");

    if (mTarget.mElement != aOther.mTarget.mElement) {
      return nsContentUtils::PositionIsBefore(mTarget.mElement,
                                              aOther.mTarget.mElement);
    }

    return mTarget.mPseudoType == CSSPseudoElementType::NotPseudo ||
          (mTarget.mPseudoType == CSSPseudoElementType::before &&
           aOther.mTarget.mPseudoType == CSSPseudoElementType::after);
  }

  bool IsSet() const { return !!mTarget.mElement; }

  void GetElement(dom::Element*& aElement,
                  CSSPseudoElementType& aPseudoType) const
  {
    aElement = mTarget.mElement;
    aPseudoType = mTarget.mPseudoType;
  }

  const NonOwningAnimationTarget& Target() const { return mTarget; }

  nsPresContext* GetPresContext() const
  {
    return nsContentUtils::GetContextForContent(mTarget.mElement);
  }

private:
  NonOwningAnimationTarget mTarget;
};

// Return the TransitionPhase or AnimationPhase to use when the animation
// doesn't have a target effect.
template <typename PhaseType>
PhaseType GetAnimationPhaseWithoutEffect(const dom::Animation& aAnimation)
{
  MOZ_ASSERT(!aAnimation.GetEffect(),
             "Should only be called when we do not have an effect");

// GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
// GetTickCount().
#ifdef GetCurrentTime
#undef GetCurrentTime
#endif

  dom::Nullable<TimeDuration> currentTime = aAnimation.GetCurrentTime();
  if (currentTime.IsNull()) {
    return PhaseType::Idle;
  }

  // If we don't have a target effect, the duration will be zero so the phase is
  // 'before' if the current time is less than zero.
  return currentTime.Value() < TimeDuration()
         ? PhaseType::Before
         : PhaseType::After;
};

inline TimingParams
TimingParamsFromCSSParams(float aDuration, float aDelay,
                          float aIterationCount,
                          dom::PlaybackDirection aDirection,
                          dom::FillMode aFillMode)
{
  MOZ_ASSERT(aIterationCount >= 0.0 && !IsNaN(aIterationCount),
             "aIterations should be nonnegative & finite, as ensured by "
             "CSSParser");

  return TimingParams {
    aDuration,
    aDelay,
    aIterationCount,
    aDirection,
    aFillMode
  };
}

} // namespace mozilla

#endif /* !defined(mozilla_css_AnimationCommon_h) */