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 "SVGAnimatedNumberList.h"
8 
9 #include <utility>
10 
11 #include "DOMSVGAnimatedNumberList.h"
12 #include "SVGNumberListSMILType.h"
13 #include "mozilla/SMILValue.h"
14 #include "mozilla/dom/SVGElement.h"
15 
16 using namespace mozilla::dom;
17 
18 namespace mozilla {
19 
SetBaseValueString(const nsAString & aValue)20 nsresult SVGAnimatedNumberList::SetBaseValueString(const nsAString& aValue) {
21   SVGNumberList newBaseValue;
22   nsresult rv = newBaseValue.SetValueFromString(aValue);
23   if (NS_FAILED(rv)) {
24     return rv;
25   }
26 
27   DOMSVGAnimatedNumberList* domWrapper =
28       DOMSVGAnimatedNumberList::GetDOMWrapperIfExists(this);
29   if (domWrapper) {
30     // We must send this notification *before* changing mBaseVal! If the length
31     // of our baseVal is being reduced, our baseVal's DOM wrapper list may have
32     // to remove DOM items from itself, and any removed DOM items need to copy
33     // their internal counterpart values *before* we change them.
34     //
35     domWrapper->InternalBaseValListWillChangeTo(newBaseValue);
36   }
37 
38   // We don't need to call DidChange* here - we're only called by
39   // SVGElement::ParseAttribute under Element::SetAttr,
40   // which takes care of notifying.
41 
42   mIsBaseSet = true;
43   rv = mBaseVal.CopyFrom(newBaseValue);
44   if (NS_FAILED(rv) && domWrapper) {
45     // Attempting to increase mBaseVal's length failed - reduce domWrapper
46     // back to the same length:
47     domWrapper->InternalBaseValListWillChangeTo(mBaseVal);
48   }
49   return rv;
50 }
51 
ClearBaseValue(uint32_t aAttrEnum)52 void SVGAnimatedNumberList::ClearBaseValue(uint32_t aAttrEnum) {
53   DOMSVGAnimatedNumberList* domWrapper =
54       DOMSVGAnimatedNumberList::GetDOMWrapperIfExists(this);
55   if (domWrapper) {
56     // We must send this notification *before* changing mBaseVal! (See above.)
57     domWrapper->InternalBaseValListWillChangeTo(SVGNumberList());
58   }
59   mBaseVal.Clear();
60   mIsBaseSet = false;
61   // Caller notifies
62 }
63 
SetAnimValue(const SVGNumberList & aNewAnimValue,SVGElement * aElement,uint32_t aAttrEnum)64 nsresult SVGAnimatedNumberList::SetAnimValue(const SVGNumberList& aNewAnimValue,
65                                              SVGElement* aElement,
66                                              uint32_t aAttrEnum) {
67   DOMSVGAnimatedNumberList* domWrapper =
68       DOMSVGAnimatedNumberList::GetDOMWrapperIfExists(this);
69   if (domWrapper) {
70     // A new animation may totally change the number of items in the animVal
71     // list, replacing what was essentially a mirror of the baseVal list, or
72     // else replacing and overriding an existing animation. When this happens
73     // we must try and keep our animVal's DOM wrapper in sync (see the comment
74     // in DOMSVGAnimatedNumberList::InternalBaseValListWillChangeTo).
75     //
76     // It's not possible for us to reliably distinguish between calls to this
77     // method that are setting a new sample for an existing animation, and
78     // calls that are setting the first sample of an animation that will
79     // override an existing animation. Happily it's cheap to just blindly
80     // notify our animVal's DOM wrapper of its internal counterpart's new value
81     // each time this method is called, so that's what we do.
82     //
83     // Note that we must send this notification *before* setting or changing
84     // mAnimVal! (See the comment in SetBaseValueString above.)
85     //
86     domWrapper->InternalAnimValListWillChangeTo(aNewAnimValue);
87   }
88   if (!mAnimVal) {
89     mAnimVal = MakeUnique<SVGNumberList>();
90   }
91   nsresult rv = mAnimVal->CopyFrom(aNewAnimValue);
92   if (NS_FAILED(rv)) {
93     // OOM. We clear the animation, and, importantly, ClearAnimValue() ensures
94     // that mAnimVal and its DOM wrapper (if any) will have the same length!
95     ClearAnimValue(aElement, aAttrEnum);
96     return rv;
97   }
98   aElement->DidAnimateNumberList(aAttrEnum);
99   return NS_OK;
100 }
101 
ClearAnimValue(SVGElement * aElement,uint32_t aAttrEnum)102 void SVGAnimatedNumberList::ClearAnimValue(SVGElement* aElement,
103                                            uint32_t aAttrEnum) {
104   DOMSVGAnimatedNumberList* domWrapper =
105       DOMSVGAnimatedNumberList::GetDOMWrapperIfExists(this);
106   if (domWrapper) {
107     // When all animation ends, animVal simply mirrors baseVal, which may have
108     // a different number of items to the last active animated value. We must
109     // keep the length of our animVal's DOM wrapper list in sync, and again we
110     // must do that before touching mAnimVal. See comments above.
111     //
112     domWrapper->InternalAnimValListWillChangeTo(mBaseVal);
113   }
114   mAnimVal = nullptr;
115   aElement->DidAnimateNumberList(aAttrEnum);
116 }
117 
ToSMILAttr(SVGElement * aSVGElement,uint8_t aAttrEnum)118 UniquePtr<SMILAttr> SVGAnimatedNumberList::ToSMILAttr(SVGElement* aSVGElement,
119                                                       uint8_t aAttrEnum) {
120   return MakeUnique<SMILAnimatedNumberList>(this, aSVGElement, aAttrEnum);
121 }
122 
ValueFromString(const nsAString & aStr,const dom::SVGAnimationElement *,SMILValue & aValue,bool & aPreventCachingOfSandwich) const123 nsresult SVGAnimatedNumberList::SMILAnimatedNumberList::ValueFromString(
124     const nsAString& aStr, const dom::SVGAnimationElement* /*aSrcElement*/,
125     SMILValue& aValue, bool& aPreventCachingOfSandwich) const {
126   SMILValue val(&SVGNumberListSMILType::sSingleton);
127   SVGNumberListAndInfo* nlai = static_cast<SVGNumberListAndInfo*>(val.mU.mPtr);
128   nsresult rv = nlai->SetValueFromString(aStr);
129   if (NS_SUCCEEDED(rv)) {
130     nlai->SetInfo(mElement);
131     aValue = std::move(val);
132   }
133   aPreventCachingOfSandwich = false;
134   return rv;
135 }
136 
GetBaseValue() const137 SMILValue SVGAnimatedNumberList::SMILAnimatedNumberList::GetBaseValue() const {
138   // To benefit from Return Value Optimization and avoid copy constructor calls
139   // due to our use of return-by-value, we must return the exact same object
140   // from ALL return points. This function must only return THIS variable:
141   SMILValue val;
142 
143   SMILValue tmp(&SVGNumberListSMILType::sSingleton);
144   SVGNumberListAndInfo* nlai = static_cast<SVGNumberListAndInfo*>(tmp.mU.mPtr);
145   nsresult rv = nlai->CopyFrom(mVal->mBaseVal);
146   if (NS_SUCCEEDED(rv)) {
147     nlai->SetInfo(mElement);
148     std::swap(val, tmp);
149   }
150   return val;
151 }
152 
SetAnimValue(const SMILValue & aValue)153 nsresult SVGAnimatedNumberList::SMILAnimatedNumberList::SetAnimValue(
154     const SMILValue& aValue) {
155   NS_ASSERTION(aValue.mType == &SVGNumberListSMILType::sSingleton,
156                "Unexpected type to assign animated value");
157   if (aValue.mType == &SVGNumberListSMILType::sSingleton) {
158     mVal->SetAnimValue(*static_cast<SVGNumberListAndInfo*>(aValue.mU.mPtr),
159                        mElement, mAttrEnum);
160   }
161   return NS_OK;
162 }
163 
ClearAnimValue()164 void SVGAnimatedNumberList::SMILAnimatedNumberList::ClearAnimValue() {
165   if (mVal->mAnimVal) {
166     mVal->ClearAnimValue(mElement, mAttrEnum);
167   }
168 }
169 
170 }  // namespace mozilla
171