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 #include "mozilla/AnimationCollection.h"
8 
9 #include "mozilla/RestyleManager.h"
10 #include "nsDOMMutationObserver.h"      // For nsAutoAnimationMutationBatch
11 #include "mozilla/dom/CSSAnimation.h"   // For dom::CSSAnimation
12 #include "mozilla/dom/CSSTransition.h"  // For dom::CSSTransition
13 
14 namespace mozilla {
15 
16 template <class AnimationType>
PropertyDtor(void * aObject,nsAtom * aPropertyName,void * aPropertyValue,void * aData)17 /* static */ void AnimationCollection<AnimationType>::PropertyDtor(
18     void* aObject, nsAtom* aPropertyName, void* aPropertyValue, void* aData) {
19   AnimationCollection* collection =
20       static_cast<AnimationCollection*>(aPropertyValue);
21 #ifdef DEBUG
22   MOZ_ASSERT(!collection->mCalledPropertyDtor, "can't call dtor twice");
23   collection->mCalledPropertyDtor = true;
24 #endif
25 
26   PostRestyleMode postRestyle = collection->mCalledDestroy
27                                     ? PostRestyleMode::IfNeeded
28                                     : PostRestyleMode::Never;
29   {
30     nsAutoAnimationMutationBatch mb(collection->mElement->OwnerDoc());
31 
32     for (size_t animIdx = collection->mAnimations.Length(); animIdx-- != 0;) {
33       collection->mAnimations[animIdx]->CancelFromStyle(postRestyle);
34     }
35   }
36   delete collection;
37 }
38 
39 template <class AnimationType>
40 /* static */ AnimationCollection<AnimationType>*
GetAnimationCollection(const dom::Element * aElement,PseudoStyleType aPseudoType)41 AnimationCollection<AnimationType>::GetAnimationCollection(
42     const dom::Element* aElement, PseudoStyleType aPseudoType) {
43   if (!aElement->MayHaveAnimations()) {
44     // Early return for the most common case.
45     return nullptr;
46   }
47 
48   nsAtom* propName = GetPropertyAtomForPseudoType(aPseudoType);
49   if (!propName) {
50     return nullptr;
51   }
52 
53   return static_cast<AnimationCollection<AnimationType>*>(
54       aElement->GetProperty(propName));
55 }
56 
57 template <class AnimationType>
58 /* static */ AnimationCollection<AnimationType>*
GetAnimationCollection(const nsIFrame * aFrame)59 AnimationCollection<AnimationType>::GetAnimationCollection(
60     const nsIFrame* aFrame) {
61   Maybe<NonOwningAnimationTarget> pseudoElement =
62       EffectCompositor::GetAnimationElementAndPseudoForFrame(aFrame);
63   if (!pseudoElement) {
64     return nullptr;
65   }
66 
67   if (!pseudoElement->mElement->MayHaveAnimations()) {
68     return nullptr;
69   }
70 
71   return GetAnimationCollection(pseudoElement->mElement,
72                                 pseudoElement->mPseudoType);
73 }
74 
75 template <class AnimationType>
76 /* static */ AnimationCollection<AnimationType>*
GetOrCreateAnimationCollection(dom::Element * aElement,PseudoStyleType aPseudoType,bool * aCreatedCollection)77 AnimationCollection<AnimationType>::GetOrCreateAnimationCollection(
78     dom::Element* aElement, PseudoStyleType aPseudoType,
79     bool* aCreatedCollection) {
80   MOZ_ASSERT(aCreatedCollection);
81   *aCreatedCollection = false;
82 
83   nsAtom* propName = GetPropertyAtomForPseudoType(aPseudoType);
84   MOZ_ASSERT(propName,
85              "Should only try to create animations for one of the"
86              " recognized pseudo types");
87 
88   auto collection = static_cast<AnimationCollection<AnimationType>*>(
89       aElement->GetProperty(propName));
90   if (!collection) {
91     // FIXME: Consider arena-allocating?
92     collection = new AnimationCollection<AnimationType>(aElement, propName);
93     nsresult rv = aElement->SetProperty(
94         propName, collection, &AnimationCollection<AnimationType>::PropertyDtor,
95         false);
96     if (NS_FAILED(rv)) {
97       NS_WARNING("SetProperty failed");
98       // The collection must be destroyed via PropertyDtor, otherwise
99       // mCalledPropertyDtor assertion is triggered in destructor.
100       AnimationCollection<AnimationType>::PropertyDtor(aElement, propName,
101                                                        collection, nullptr);
102       return nullptr;
103     }
104 
105     *aCreatedCollection = true;
106     aElement->SetMayHaveAnimations();
107   }
108 
109   return collection;
110 }
111 
112 template <class AnimationType>
113 /*static*/ nsAtom*
GetPropertyAtomForPseudoType(PseudoStyleType aPseudoType)114 AnimationCollection<AnimationType>::GetPropertyAtomForPseudoType(
115     PseudoStyleType aPseudoType) {
116   nsAtom* propName = nullptr;
117 
118   if (aPseudoType == PseudoStyleType::NotPseudo) {
119     propName = TraitsType::ElementPropertyAtom();
120   } else if (aPseudoType == PseudoStyleType::before) {
121     propName = TraitsType::BeforePropertyAtom();
122   } else if (aPseudoType == PseudoStyleType::after) {
123     propName = TraitsType::AfterPropertyAtom();
124   } else if (aPseudoType == PseudoStyleType::marker) {
125     propName = TraitsType::MarkerPropertyAtom();
126   }
127 
128   return propName;
129 }
130 
131 template <class AnimationType>
Destroy()132 void AnimationCollection<AnimationType>::Destroy() {
133   mCalledDestroy = true;
134 
135   // This will call our destructor.
136   mElement->RemoveProperty(mElementProperty);
137 }
138 
139 // Explicit class instantiations
140 
141 template class AnimationCollection<dom::CSSAnimation>;
142 template class AnimationCollection<dom::CSSTransition>;
143 
144 }  // namespace mozilla
145