tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

AnimationCommon.h (10656B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef mozilla_css_AnimationCommon_h
      8 #define mozilla_css_AnimationCommon_h
      9 
     10 #include "mozilla/AnimationCollection.h"
     11 #include "mozilla/Assertions.h"
     12 #include "mozilla/LinkedList.h"
     13 #include "mozilla/Maybe.h"
     14 #include "mozilla/TimingParams.h"
     15 #include "mozilla/dom/Animation.h"
     16 #include "mozilla/dom/BaseKeyframeTypesBinding.h"
     17 #include "mozilla/dom/Nullable.h"
     18 #include "nsContentUtils.h"
     19 #include "nsDOMMutationObserver.h"  // For nsAutoAnimationMutationBatch
     20 
     21 class nsPresContext;
     22 
     23 namespace mozilla {
     24 enum class PseudoStyleType : uint8_t;
     25 
     26 namespace dom {
     27 class Element;
     28 }
     29 
     30 template <class AnimationType>
     31 class CommonAnimationManager {
     32 public:
     33  explicit CommonAnimationManager(nsPresContext* aPresContext)
     34      : mPresContext(aPresContext) {}
     35 
     36  // NOTE:  This can return null after Disconnect().
     37  nsPresContext* PresContext() const { return mPresContext; }
     38 
     39  /**
     40   * Notify the manager that the pres context is going away.
     41   */
     42  void Disconnect() {
     43    // Content nodes might outlive the transition or animation manager.
     44    RemoveAllElementCollections();
     45 
     46    mPresContext = nullptr;
     47  }
     48 
     49  /**
     50   * Stop animations on the element. This method takes the real element
     51   * rather than the element for the generated content for animations on
     52   * ::before, ::after and ::marker.
     53   */
     54  void StopAnimationsForElement(dom::Element* aElement,
     55                                const PseudoStyleRequest& aPseudoRequest) {
     56    MOZ_ASSERT(aElement);
     57    auto* collection =
     58        AnimationCollection<AnimationType>::Get(aElement, aPseudoRequest);
     59    if (!collection) {
     60      return;
     61    }
     62 
     63    nsAutoAnimationMutationBatch mb(aElement->OwnerDoc());
     64    collection->Destroy();
     65  }
     66 
     67 protected:
     68  virtual ~CommonAnimationManager() {
     69    MOZ_ASSERT(!mPresContext, "Disconnect should have been called");
     70  }
     71 
     72  void AddElementCollection(AnimationCollection<AnimationType>* aCollection) {
     73    mElementCollections.insertBack(aCollection);
     74  }
     75  void RemoveAllElementCollections() {
     76    while (AnimationCollection<AnimationType>* head =
     77               mElementCollections.getFirst()) {
     78      head->Destroy();  // Note: this removes 'head' from mElementCollections.
     79    }
     80  }
     81 
     82  LinkedList<AnimationCollection<AnimationType>> mElementCollections;
     83  nsPresContext* mPresContext;  // weak (non-null from ctor to Disconnect)
     84 };
     85 
     86 /**
     87 * Utility class for referencing the element that created a CSS animation or
     88 * transition. It is non-owning (i.e. it uses a raw pointer) since it is only
     89 * expected to be set by the owned animation while it actually being managed
     90 * by the owning element.
     91 *
     92 * This class also abstracts the comparison of an element/pseudo-class pair
     93 * for the sake of composite ordering since this logic is common to both CSS
     94 * animations and transitions.
     95 *
     96 * (We call this OwningElementRef instead of just OwningElement so that we can
     97 * call the getter on CSSAnimation/CSSTransition OwningElement() without
     98 * clashing with this object's contructor.)
     99 */
    100 class OwningElementRef final {
    101 public:
    102  OwningElementRef() = default;
    103 
    104  explicit OwningElementRef(const NonOwningAnimationTarget& aTarget)
    105      : mTarget(aTarget) {}
    106 
    107  OwningElementRef(dom::Element& aElement,
    108                   const PseudoStyleRequest& aPseudoRequest)
    109      : mTarget(&aElement, aPseudoRequest) {}
    110 
    111  bool Equals(const OwningElementRef& aOther) const {
    112    return mTarget == aOther.mTarget;
    113  }
    114 
    115  int32_t Compare(const OwningElementRef& aOther,
    116                  nsContentUtils::NodeIndexCache& aCache) const {
    117    MOZ_ASSERT(mTarget.mElement && aOther.mTarget.mElement,
    118               "Elements to compare should not be null");
    119 
    120    if (mTarget.mElement != aOther.mTarget.mElement) {
    121      const bool connected = mTarget.mElement->IsInComposedDoc();
    122      if (connected != aOther.mTarget.mElement->IsInComposedDoc()) {
    123        // Disconnected elements sort last.
    124        return connected ? -1 : 1;
    125      }
    126      if (!connected) {
    127        auto* thisRoot = mTarget.mElement->SubtreeRoot();
    128        auto* otherRoot = aOther.mTarget.mElement->SubtreeRoot();
    129        if (thisRoot != otherRoot) {
    130          // We need some consistent ordering across disconnected subtrees. This
    131          // is kind of arbitrary.
    132          return uintptr_t(thisRoot) < uintptr_t(otherRoot) ? -1 : 1;
    133        }
    134      }
    135      return nsContentUtils::CompareTreePosition<TreeKind::ShadowIncludingDOM>(
    136          mTarget.mElement, aOther.mTarget.mElement, nullptr, &aCache);
    137    }
    138 
    139    enum SortingIndex : uint8_t {
    140      NotPseudo,
    141      Backdrop,
    142      Marker,
    143      Before,
    144      After,
    145      ViewTransition,
    146      ViewTransitionGroup,
    147      ViewTransitionImagePair,
    148      ViewTransitionOld,
    149      ViewTransitionNew,
    150      Other
    151    };
    152    auto sortingIndex =
    153        [](const PseudoStyleRequest& aPseudoRequest) -> SortingIndex {
    154      switch (aPseudoRequest.mType) {
    155        case PseudoStyleType::NotPseudo:
    156          return SortingIndex::NotPseudo;
    157        case PseudoStyleType::backdrop:
    158          return SortingIndex::Backdrop;
    159        case PseudoStyleType::marker:
    160          return SortingIndex::Marker;
    161        case PseudoStyleType::before:
    162          return SortingIndex::Before;
    163        case PseudoStyleType::after:
    164          return SortingIndex::After;
    165        case PseudoStyleType::viewTransition:
    166          return SortingIndex::ViewTransition;
    167        case PseudoStyleType::viewTransitionGroup:
    168          return SortingIndex::ViewTransitionGroup;
    169        case PseudoStyleType::viewTransitionImagePair:
    170          return SortingIndex::ViewTransitionImagePair;
    171        case PseudoStyleType::viewTransitionOld:
    172          return SortingIndex::ViewTransitionOld;
    173        case PseudoStyleType::viewTransitionNew:
    174          return SortingIndex::ViewTransitionNew;
    175        default:
    176          MOZ_ASSERT_UNREACHABLE("Unexpected pseudo type");
    177          return SortingIndex::Other;
    178      }
    179    };
    180    auto cmp = sortingIndex(mTarget.mPseudoRequest) -
    181               sortingIndex(aOther.mTarget.mPseudoRequest);
    182    if (cmp != 0) {
    183      return cmp;
    184    }
    185    auto* ident = mTarget.mPseudoRequest.mIdentifier.get();
    186    auto* otherIdent = aOther.mTarget.mPseudoRequest.mIdentifier.get();
    187    MOZ_ASSERT(!!ident == !!otherIdent);
    188    if (ident == otherIdent) {
    189      return 0;
    190    }
    191    // FIXME(emilio, bug 1956219): This compares ::view-transition-* pseudos
    192    // with string comparison, which is not terrible but probably not quite
    193    // intended? It seems we should probably compare the pseudo-element tree
    194    // position or something if available, at least...
    195    return nsDependentAtomString(ident) < nsDependentAtomString(otherIdent) ? -1
    196                                                                            : 1;
    197  }
    198 
    199  bool IsSet() const { return !!mTarget.mElement; }
    200 
    201  bool ShouldFireEvents() const {
    202    // NOTE(emilio): Pseudo-elements are represented with a non-native animation
    203    // target, and a pseudo-element separately, so the check is also correct for
    204    // them.
    205    return IsSet() && !mTarget.mElement->IsInNativeAnonymousSubtree();
    206  }
    207 
    208  void GetElement(dom::Element*& aElement,
    209                  PseudoStyleRequest& aPseudoRequest) const {
    210    aElement = mTarget.mElement;
    211    aPseudoRequest = mTarget.mPseudoRequest;
    212  }
    213 
    214  const NonOwningAnimationTarget& Target() const { return mTarget; }
    215 
    216  nsPresContext* GetPresContext() const {
    217    return nsContentUtils::GetContextForContent(mTarget.mElement);
    218  }
    219 
    220 private:
    221  NonOwningAnimationTarget mTarget;
    222 };
    223 
    224 // Return the TransitionPhase or AnimationPhase to use when the animation
    225 // doesn't have a target effect.
    226 template <typename PhaseType>
    227 PhaseType GetAnimationPhaseWithoutEffect(const dom::Animation& aAnimation) {
    228  MOZ_ASSERT(!aAnimation.GetEffect(),
    229             "Should only be called when we do not have an effect");
    230 
    231  dom::Nullable<TimeDuration> currentTime =
    232      aAnimation.GetCurrentTimeAsDuration();
    233  if (currentTime.IsNull()) {
    234    return PhaseType::Idle;
    235  }
    236 
    237  // If we don't have a target effect, the duration will be zero so the phase is
    238  // 'before' if the current time is less than zero.
    239  return currentTime.Value() < TimeDuration() ? PhaseType::Before
    240                                              : PhaseType::After;
    241 };
    242 
    243 inline dom::PlaybackDirection StyleToDom(StyleAnimationDirection aDirection) {
    244  switch (aDirection) {
    245    case StyleAnimationDirection::Normal:
    246      return dom::PlaybackDirection::Normal;
    247    case StyleAnimationDirection::Reverse:
    248      return dom::PlaybackDirection::Reverse;
    249    case StyleAnimationDirection::Alternate:
    250      return dom::PlaybackDirection::Alternate;
    251    case StyleAnimationDirection::AlternateReverse:
    252      return dom::PlaybackDirection::Alternate_reverse;
    253  }
    254  MOZ_ASSERT_UNREACHABLE("Wrong style value?");
    255  return dom::PlaybackDirection::Normal;
    256 }
    257 
    258 inline dom::FillMode StyleToDom(StyleAnimationFillMode aFillMode) {
    259  switch (aFillMode) {
    260    case StyleAnimationFillMode::None:
    261      return dom::FillMode::None;
    262    case StyleAnimationFillMode::Both:
    263      return dom::FillMode::Both;
    264    case StyleAnimationFillMode::Forwards:
    265      return dom::FillMode::Forwards;
    266    case StyleAnimationFillMode::Backwards:
    267      return dom::FillMode::Backwards;
    268  }
    269  MOZ_ASSERT_UNREACHABLE("Wrong style value?");
    270  return dom::FillMode::None;
    271 }
    272 
    273 inline dom::CompositeOperation StyleToDom(StyleAnimationComposition aStyle) {
    274  switch (aStyle) {
    275    case StyleAnimationComposition::Replace:
    276      return dom::CompositeOperation::Replace;
    277    case StyleAnimationComposition::Add:
    278      return dom::CompositeOperation::Add;
    279    case StyleAnimationComposition::Accumulate:
    280      return dom::CompositeOperation::Accumulate;
    281  }
    282  MOZ_ASSERT_UNREACHABLE("Invalid style composite operation?");
    283  return dom::CompositeOperation::Replace;
    284 }
    285 
    286 inline TimingParams TimingParamsFromCSSParams(
    287    Maybe<float> aDuration, float aDelay, float aIterationCount,
    288    StyleAnimationDirection aDirection, StyleAnimationFillMode aFillMode) {
    289  MOZ_ASSERT(aIterationCount >= 0.0 && !std::isnan(aIterationCount),
    290             "aIterations should be nonnegative & finite, as ensured by "
    291             "CSSParser");
    292  return TimingParams{aDuration, aDelay, aIterationCount,
    293                      StyleToDom(aDirection), StyleToDom(aFillMode)};
    294 }
    295 
    296 }  // namespace mozilla
    297 
    298 #endif /* !defined(mozilla_css_AnimationCommon_h) */