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