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 "SVGAnimatedPreserveAspectRatio.h"
8 
9 #include "mozAutoDocUpdate.h"
10 #include "mozilla/ArrayUtils.h"
11 #include "mozilla/Maybe.h"
12 #include "mozilla/SMILValue.h"
13 #include "mozilla/SVGContentUtils.h"
14 #include "mozilla/dom/SVGAnimatedPreserveAspectRatioBinding.h"
15 #include "SMILEnumType.h"
16 #include "SVGAttrTearoffTable.h"
17 
18 using namespace mozilla::dom;
19 
20 namespace mozilla {
21 
22 ////////////////////////////////////////////////////////////////////////
23 // SVGAnimatedPreserveAspectRatio class
NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(DOMSVGAnimatedPreserveAspectRatio,mSVGElement)24 NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(
25     DOMSVGAnimatedPreserveAspectRatio, mSVGElement)
26 
27 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOMSVGAnimatedPreserveAspectRatio, AddRef)
28 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(DOMSVGAnimatedPreserveAspectRatio,
29                                        Release)
30 
31 JSObject* DOMSVGAnimatedPreserveAspectRatio::WrapObject(
32     JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
33   return SVGAnimatedPreserveAspectRatio_Binding::Wrap(aCx, this, aGivenProto);
34 }
35 
36 /* Implementation */
37 
38 //----------------------------------------------------------------------
39 // Helper class: AutoChangePreserveAspectRatioNotifier
40 // Stack-based helper class to pair calls to WillChangePreserveAspectRatio and
41 // DidChangePreserveAspectRatio.
42 class MOZ_RAII AutoChangePreserveAspectRatioNotifier {
43  public:
AutoChangePreserveAspectRatioNotifier(SVGAnimatedPreserveAspectRatio * aPreserveAspectRatio,SVGElement * aSVGElement,bool aDoSetAttr=true)44   AutoChangePreserveAspectRatioNotifier(
45       SVGAnimatedPreserveAspectRatio* aPreserveAspectRatio,
46       SVGElement* aSVGElement, bool aDoSetAttr = true)
47       : mPreserveAspectRatio(aPreserveAspectRatio),
48         mSVGElement(aSVGElement),
49         mDoSetAttr(aDoSetAttr) {
50     MOZ_ASSERT(mPreserveAspectRatio, "Expecting non-null preserveAspectRatio");
51     MOZ_ASSERT(mSVGElement, "Expecting non-null element");
52     if (mDoSetAttr) {
53       mUpdateBatch.emplace(aSVGElement->GetComposedDoc(), true);
54       mEmptyOrOldValue =
55           mSVGElement->WillChangePreserveAspectRatio(mUpdateBatch.ref());
56     }
57   }
58 
~AutoChangePreserveAspectRatioNotifier()59   ~AutoChangePreserveAspectRatioNotifier() {
60     if (mDoSetAttr) {
61       mSVGElement->DidChangePreserveAspectRatio(mEmptyOrOldValue,
62                                                 mUpdateBatch.ref());
63     }
64     if (mPreserveAspectRatio->mIsAnimated) {
65       mSVGElement->AnimationNeedsResample();
66     }
67   }
68 
69  private:
70   SVGAnimatedPreserveAspectRatio* const mPreserveAspectRatio;
71   SVGElement* const mSVGElement;
72   Maybe<mozAutoDocUpdate> mUpdateBatch;
73   nsAttrValue mEmptyOrOldValue;
74   bool mDoSetAttr;
75 };
76 
77 static SVGAttrTearoffTable<SVGAnimatedPreserveAspectRatio,
78                            DOMSVGAnimatedPreserveAspectRatio>
79     sSVGAnimatedPAspectRatioTearoffTable;
80 static SVGAttrTearoffTable<SVGAnimatedPreserveAspectRatio,
81                            DOMSVGPreserveAspectRatio>
82     sBaseSVGPAspectRatioTearoffTable;
83 static SVGAttrTearoffTable<SVGAnimatedPreserveAspectRatio,
84                            DOMSVGPreserveAspectRatio>
85     sAnimSVGPAspectRatioTearoffTable;
86 
87 already_AddRefed<DOMSVGPreserveAspectRatio>
BaseVal()88 DOMSVGAnimatedPreserveAspectRatio::BaseVal() {
89   RefPtr<DOMSVGPreserveAspectRatio> domBaseVal =
90       sBaseSVGPAspectRatioTearoffTable.GetTearoff(mVal);
91   if (!domBaseVal) {
92     domBaseVal = new DOMSVGPreserveAspectRatio(mVal, mSVGElement, true);
93     sBaseSVGPAspectRatioTearoffTable.AddTearoff(mVal, domBaseVal);
94   }
95 
96   return domBaseVal.forget();
97 }
98 
~DOMSVGPreserveAspectRatio()99 DOMSVGPreserveAspectRatio::~DOMSVGPreserveAspectRatio() {
100   if (mIsBaseValue) {
101     sBaseSVGPAspectRatioTearoffTable.RemoveTearoff(mVal);
102   } else {
103     sAnimSVGPAspectRatioTearoffTable.RemoveTearoff(mVal);
104   }
105 }
106 
107 already_AddRefed<DOMSVGPreserveAspectRatio>
AnimVal()108 DOMSVGAnimatedPreserveAspectRatio::AnimVal() {
109   RefPtr<DOMSVGPreserveAspectRatio> domAnimVal =
110       sAnimSVGPAspectRatioTearoffTable.GetTearoff(mVal);
111   if (!domAnimVal) {
112     domAnimVal = new DOMSVGPreserveAspectRatio(mVal, mSVGElement, false);
113     sAnimSVGPAspectRatioTearoffTable.AddTearoff(mVal, domAnimVal);
114   }
115 
116   return domAnimVal.forget();
117 }
118 
SetBaseValueString(const nsAString & aValueAsString,SVGElement * aSVGElement,bool aDoSetAttr)119 nsresult SVGAnimatedPreserveAspectRatio::SetBaseValueString(
120     const nsAString& aValueAsString, SVGElement* aSVGElement, bool aDoSetAttr) {
121   SVGPreserveAspectRatio val;
122   nsresult res = SVGPreserveAspectRatio::FromString(aValueAsString, &val);
123   if (NS_FAILED(res)) {
124     return res;
125   }
126 
127   AutoChangePreserveAspectRatioNotifier notifier(this, aSVGElement, aDoSetAttr);
128 
129   mBaseVal = val;
130   mIsBaseSet = true;
131   if (!mIsAnimated) {
132     mAnimVal = mBaseVal;
133   }
134 
135   return NS_OK;
136 }
137 
GetBaseValueString(nsAString & aValueAsString) const138 void SVGAnimatedPreserveAspectRatio::GetBaseValueString(
139     nsAString& aValueAsString) const {
140   mBaseVal.ToString(aValueAsString);
141 }
142 
SetBaseValue(const SVGPreserveAspectRatio & aValue,SVGElement * aSVGElement)143 void SVGAnimatedPreserveAspectRatio::SetBaseValue(
144     const SVGPreserveAspectRatio& aValue, SVGElement* aSVGElement) {
145   if (mIsBaseSet && mBaseVal == aValue) {
146     return;
147   }
148 
149   AutoChangePreserveAspectRatioNotifier notifier(this, aSVGElement);
150 
151   mBaseVal = aValue;
152   mIsBaseSet = true;
153   if (!mIsAnimated) {
154     mAnimVal = mBaseVal;
155   }
156 }
157 
PackPreserveAspectRatio(const SVGPreserveAspectRatio & par)158 static uint64_t PackPreserveAspectRatio(const SVGPreserveAspectRatio& par) {
159   // All preserveAspectRatio values are enum values (do not interpolate), so we
160   // can safely collate them and treat them as a single enum as for SMIL.
161   uint64_t packed = 0;
162   packed |= uint64_t(par.GetAlign()) << 8;
163   packed |= uint64_t(par.GetMeetOrSlice());
164   return packed;
165 }
166 
SetAnimValue(uint64_t aPackedValue,SVGElement * aSVGElement)167 void SVGAnimatedPreserveAspectRatio::SetAnimValue(uint64_t aPackedValue,
168                                                   SVGElement* aSVGElement) {
169   if (mIsAnimated && PackPreserveAspectRatio(mAnimVal) == aPackedValue) {
170     return;
171   }
172   mAnimVal.SetAlign(uint16_t((aPackedValue & 0xff00) >> 8));
173   mAnimVal.SetMeetOrSlice(uint16_t(aPackedValue & 0xff));
174   mIsAnimated = true;
175   aSVGElement->DidAnimatePreserveAspectRatio();
176 }
177 
178 already_AddRefed<DOMSVGAnimatedPreserveAspectRatio>
ToDOMAnimatedPreserveAspectRatio(SVGElement * aSVGElement)179 SVGAnimatedPreserveAspectRatio::ToDOMAnimatedPreserveAspectRatio(
180     SVGElement* aSVGElement) {
181   RefPtr<DOMSVGAnimatedPreserveAspectRatio> domAnimatedPAspectRatio =
182       sSVGAnimatedPAspectRatioTearoffTable.GetTearoff(this);
183   if (!domAnimatedPAspectRatio) {
184     domAnimatedPAspectRatio =
185         new DOMSVGAnimatedPreserveAspectRatio(this, aSVGElement);
186     sSVGAnimatedPAspectRatioTearoffTable.AddTearoff(this,
187                                                     domAnimatedPAspectRatio);
188   }
189   return domAnimatedPAspectRatio.forget();
190 }
191 
~DOMSVGAnimatedPreserveAspectRatio()192 DOMSVGAnimatedPreserveAspectRatio::~DOMSVGAnimatedPreserveAspectRatio() {
193   sSVGAnimatedPAspectRatioTearoffTable.RemoveTearoff(mVal);
194 }
195 
ToSMILAttr(SVGElement * aSVGElement)196 UniquePtr<SMILAttr> SVGAnimatedPreserveAspectRatio::ToSMILAttr(
197     SVGElement* aSVGElement) {
198   return MakeUnique<SMILPreserveAspectRatio>(this, aSVGElement);
199 }
200 
201 // typedef for inner class, to make function signatures shorter below:
202 using SMILPreserveAspectRatio =
203     SVGAnimatedPreserveAspectRatio::SMILPreserveAspectRatio;
204 
ValueFromString(const nsAString & aStr,const SVGAnimationElement *,SMILValue & aValue,bool & aPreventCachingOfSandwich) const205 nsresult SMILPreserveAspectRatio::ValueFromString(
206     const nsAString& aStr, const SVGAnimationElement* /*aSrcElement*/,
207     SMILValue& aValue, bool& aPreventCachingOfSandwich) const {
208   SVGPreserveAspectRatio par;
209   nsresult res = SVGPreserveAspectRatio::FromString(aStr, &par);
210   NS_ENSURE_SUCCESS(res, res);
211 
212   SMILValue val(SMILEnumType::Singleton());
213   val.mU.mUint = PackPreserveAspectRatio(par);
214   aValue = val;
215   aPreventCachingOfSandwich = false;
216   return NS_OK;
217 }
218 
GetBaseValue() const219 SMILValue SMILPreserveAspectRatio::GetBaseValue() const {
220   SMILValue val(SMILEnumType::Singleton());
221   val.mU.mUint = PackPreserveAspectRatio(mVal->GetBaseValue());
222   return val;
223 }
224 
ClearAnimValue()225 void SMILPreserveAspectRatio::ClearAnimValue() {
226   if (mVal->mIsAnimated) {
227     mVal->mIsAnimated = false;
228     mVal->mAnimVal = mVal->mBaseVal;
229     mSVGElement->DidAnimatePreserveAspectRatio();
230   }
231 }
232 
SetAnimValue(const SMILValue & aValue)233 nsresult SMILPreserveAspectRatio::SetAnimValue(const SMILValue& aValue) {
234   NS_ASSERTION(aValue.mType == SMILEnumType::Singleton(),
235                "Unexpected type to assign animated value");
236   if (aValue.mType == SMILEnumType::Singleton()) {
237     mVal->SetAnimValue(aValue.mU.mUint, mSVGElement);
238   }
239   return NS_OK;
240 }
241 
242 }  // namespace mozilla
243