/src/mozilla-central/dom/svg/SVGAnimatedLengthList.cpp
Line | Count | Source (jump to first uncovered line) |
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 "SVGAnimatedLengthList.h" |
8 | | |
9 | | #include "DOMSVGAnimatedLengthList.h" |
10 | | #include "mozilla/Move.h" |
11 | | #include "nsSVGElement.h" |
12 | | #include "nsSVGAttrTearoffTable.h" |
13 | | #include "nsSMILValue.h" |
14 | | #include "SVGLengthListSMILType.h" |
15 | | #include "mozilla/dom/SVGLengthBinding.h" |
16 | | |
17 | | namespace mozilla { |
18 | | |
19 | | using namespace dom; |
20 | | |
21 | | nsresult |
22 | | SVGAnimatedLengthList::SetBaseValueString(const nsAString& aValue) |
23 | 0 | { |
24 | 0 | SVGLengthList newBaseValue; |
25 | 0 | nsresult rv = newBaseValue.SetValueFromString(aValue); |
26 | 0 | if (NS_FAILED(rv)) { |
27 | 0 | return rv; |
28 | 0 | } |
29 | 0 | |
30 | 0 | DOMSVGAnimatedLengthList *domWrapper = |
31 | 0 | DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(this); |
32 | 0 | if (domWrapper) { |
33 | 0 | // We must send this notification *before* changing mBaseVal! If the length |
34 | 0 | // of our baseVal is being reduced, our baseVal's DOM wrapper list may have |
35 | 0 | // to remove DOM items from itself, and any removed DOM items need to copy |
36 | 0 | // their internal counterpart values *before* we change them. |
37 | 0 | // |
38 | 0 | domWrapper->InternalBaseValListWillChangeTo(newBaseValue); |
39 | 0 | } |
40 | 0 |
|
41 | 0 | // We don't need to call DidChange* here - we're only called by |
42 | 0 | // nsSVGElement::ParseAttribute under Element::SetAttr, |
43 | 0 | // which takes care of notifying. |
44 | 0 |
|
45 | 0 | rv = mBaseVal.CopyFrom(newBaseValue); |
46 | 0 | if (NS_FAILED(rv) && domWrapper) { |
47 | 0 | // Attempting to increase mBaseVal's length failed - reduce domWrapper |
48 | 0 | // back to the same length: |
49 | 0 | domWrapper->InternalBaseValListWillChangeTo(mBaseVal); |
50 | 0 | } |
51 | 0 | return rv; |
52 | 0 | } |
53 | | |
54 | | void |
55 | | SVGAnimatedLengthList::ClearBaseValue(uint32_t aAttrEnum) |
56 | 0 | { |
57 | 0 | DOMSVGAnimatedLengthList *domWrapper = |
58 | 0 | DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(this); |
59 | 0 | if (domWrapper) { |
60 | 0 | // We must send this notification *before* changing mBaseVal! (See above.) |
61 | 0 | domWrapper->InternalBaseValListWillChangeTo(SVGLengthList()); |
62 | 0 | } |
63 | 0 | mBaseVal.Clear(); |
64 | 0 | // Caller notifies |
65 | 0 | } |
66 | | |
67 | | nsresult |
68 | | SVGAnimatedLengthList::SetAnimValue(const SVGLengthList& aNewAnimValue, |
69 | | nsSVGElement *aElement, |
70 | | uint32_t aAttrEnum) |
71 | 0 | { |
72 | 0 | DOMSVGAnimatedLengthList *domWrapper = |
73 | 0 | DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(this); |
74 | 0 | if (domWrapper) { |
75 | 0 | // A new animation may totally change the number of items in the animVal |
76 | 0 | // list, replacing what was essentially a mirror of the baseVal list, or |
77 | 0 | // else replacing and overriding an existing animation. When this happens |
78 | 0 | // we must try and keep our animVal's DOM wrapper in sync (see the comment |
79 | 0 | // in DOMSVGAnimatedLengthList::InternalBaseValListWillChangeTo). |
80 | 0 | // |
81 | 0 | // It's not possible for us to reliably distinguish between calls to this |
82 | 0 | // method that are setting a new sample for an existing animation, and |
83 | 0 | // calls that are setting the first sample of an animation that will |
84 | 0 | // override an existing animation. Happily it's cheap to just blindly |
85 | 0 | // notify our animVal's DOM wrapper of its internal counterpart's new value |
86 | 0 | // each time this method is called, so that's what we do. |
87 | 0 | // |
88 | 0 | // Note that we must send this notification *before* setting or changing |
89 | 0 | // mAnimVal! (See the comment in SetBaseValueString above.) |
90 | 0 | // |
91 | 0 | domWrapper->InternalAnimValListWillChangeTo(aNewAnimValue); |
92 | 0 | } |
93 | 0 | if (!mAnimVal) { |
94 | 0 | mAnimVal = new SVGLengthList(); |
95 | 0 | } |
96 | 0 | nsresult rv = mAnimVal->CopyFrom(aNewAnimValue); |
97 | 0 | if (NS_FAILED(rv)) { |
98 | 0 | // OOM. We clear the animation, and, importantly, ClearAnimValue() ensures |
99 | 0 | // that mAnimVal and its DOM wrapper (if any) will have the same length! |
100 | 0 | ClearAnimValue(aElement, aAttrEnum); |
101 | 0 | return rv; |
102 | 0 | } |
103 | 0 | aElement->DidAnimateLengthList(aAttrEnum); |
104 | 0 | return NS_OK; |
105 | 0 | } |
106 | | |
107 | | void |
108 | | SVGAnimatedLengthList::ClearAnimValue(nsSVGElement *aElement, |
109 | | uint32_t aAttrEnum) |
110 | 0 | { |
111 | 0 | DOMSVGAnimatedLengthList *domWrapper = |
112 | 0 | DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(this); |
113 | 0 | if (domWrapper) { |
114 | 0 | // When all animation ends, animVal simply mirrors baseVal, which may have |
115 | 0 | // a different number of items to the last active animated value. We must |
116 | 0 | // keep the length of our animVal's DOM wrapper list in sync, and again we |
117 | 0 | // must do that before touching mAnimVal. See comments above. |
118 | 0 | // |
119 | 0 | domWrapper->InternalAnimValListWillChangeTo(mBaseVal); |
120 | 0 | } |
121 | 0 | mAnimVal = nullptr; |
122 | 0 | aElement->DidAnimateLengthList(aAttrEnum); |
123 | 0 | } |
124 | | |
125 | | UniquePtr<nsISMILAttr> |
126 | | SVGAnimatedLengthList::ToSMILAttr(nsSVGElement *aSVGElement, |
127 | | uint8_t aAttrEnum, |
128 | | uint8_t aAxis, |
129 | | bool aCanZeroPadList) |
130 | 0 | { |
131 | 0 | return MakeUnique<SMILAnimatedLengthList>(this, aSVGElement, aAttrEnum, |
132 | 0 | aAxis, aCanZeroPadList); |
133 | 0 | } |
134 | | |
135 | | nsresult |
136 | | SVGAnimatedLengthList:: |
137 | | SMILAnimatedLengthList::ValueFromString(const nsAString& aStr, |
138 | | const dom::SVGAnimationElement* /*aSrcElement*/, |
139 | | nsSMILValue& aValue, |
140 | | bool& aPreventCachingOfSandwich) const |
141 | 0 | { |
142 | 0 | nsSMILValue val(&SVGLengthListSMILType::sSingleton); |
143 | 0 | SVGLengthListAndInfo *llai = static_cast<SVGLengthListAndInfo*>(val.mU.mPtr); |
144 | 0 | nsresult rv = llai->SetValueFromString(aStr); |
145 | 0 | if (NS_SUCCEEDED(rv)) { |
146 | 0 | llai->SetInfo(mElement, mAxis, mCanZeroPadList); |
147 | 0 | aValue = std::move(val); |
148 | 0 |
|
149 | 0 | // If any of the lengths in the list depend on their context, then we must |
150 | 0 | // prevent caching of the entire animation sandwich. This is because the |
151 | 0 | // units of a length at a given index can change from sandwich layer to |
152 | 0 | // layer, and indeed even be different within a single sandwich layer. If |
153 | 0 | // any length in the result of an animation sandwich is the result of the |
154 | 0 | // addition of lengths where one or more of those lengths is context |
155 | 0 | // dependent, then naturally the resultant length is also context |
156 | 0 | // dependent, regardless of whether its actual unit is context dependent or |
157 | 0 | // not. Unfortunately normal invalidation mechanisms won't cause us to |
158 | 0 | // recalculate the result of the sandwich if the context changes, so we |
159 | 0 | // take the (substantial) performance hit of preventing caching of the |
160 | 0 | // sandwich layer, causing the animation sandwich to be recalculated every |
161 | 0 | // single sample. |
162 | 0 |
|
163 | 0 | aPreventCachingOfSandwich = false; |
164 | 0 | for (uint32_t i = 0; i < llai->Length(); ++i) { |
165 | 0 | uint8_t unit = (*llai)[i].GetUnit(); |
166 | 0 | if (unit == SVGLength_Binding::SVG_LENGTHTYPE_PERCENTAGE || |
167 | 0 | unit == SVGLength_Binding::SVG_LENGTHTYPE_EMS || |
168 | 0 | unit == SVGLength_Binding::SVG_LENGTHTYPE_EXS) { |
169 | 0 | aPreventCachingOfSandwich = true; |
170 | 0 | break; |
171 | 0 | } |
172 | 0 | } |
173 | 0 | } |
174 | 0 | return rv; |
175 | 0 | } |
176 | | |
177 | | nsSMILValue |
178 | | SVGAnimatedLengthList::SMILAnimatedLengthList::GetBaseValue() const |
179 | 0 | { |
180 | 0 | // To benefit from Return Value Optimization and avoid copy constructor calls |
181 | 0 | // due to our use of return-by-value, we must return the exact same object |
182 | 0 | // from ALL return points. This function must only return THIS variable: |
183 | 0 | nsSMILValue val; |
184 | 0 |
|
185 | 0 | nsSMILValue tmp(&SVGLengthListSMILType::sSingleton); |
186 | 0 | SVGLengthListAndInfo *llai = static_cast<SVGLengthListAndInfo*>(tmp.mU.mPtr); |
187 | 0 | nsresult rv = llai->CopyFrom(mVal->mBaseVal); |
188 | 0 | if (NS_SUCCEEDED(rv)) { |
189 | 0 | llai->SetInfo(mElement, mAxis, mCanZeroPadList); |
190 | 0 | val = std::move(tmp); |
191 | 0 | } |
192 | 0 | return val; |
193 | 0 | } |
194 | | |
195 | | nsresult |
196 | | SVGAnimatedLengthList::SMILAnimatedLengthList::SetAnimValue(const nsSMILValue& aValue) |
197 | 0 | { |
198 | 0 | NS_ASSERTION(aValue.mType == &SVGLengthListSMILType::sSingleton, |
199 | 0 | "Unexpected type to assign animated value"); |
200 | 0 | if (aValue.mType == &SVGLengthListSMILType::sSingleton) { |
201 | 0 | mVal->SetAnimValue(*static_cast<SVGLengthListAndInfo*>(aValue.mU.mPtr), |
202 | 0 | mElement, |
203 | 0 | mAttrEnum); |
204 | 0 | } |
205 | 0 | return NS_OK; |
206 | 0 | } |
207 | | |
208 | | void |
209 | | SVGAnimatedLengthList::SMILAnimatedLengthList::ClearAnimValue() |
210 | 0 | { |
211 | 0 | if (mVal->mAnimVal) { |
212 | 0 | mVal->ClearAnimValue(mElement, mAttrEnum); |
213 | 0 | } |
214 | 0 | } |
215 | | |
216 | | } // namespace mozilla |