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 "SVGAnimatedPathSegList.h"
8 :
9 : #include "DOMSVGPathSegList.h"
10 : #include "mozilla/Move.h"
11 : #include "nsSVGElement.h"
12 : #include "nsSVGAttrTearoffTable.h"
13 : #include "nsSMILValue.h"
14 : #include "SVGPathSegListSMILType.h"
15 :
16 : // See the comments in this file's header!
17 :
18 : namespace mozilla {
19 :
20 : nsresult
21 44 : SVGAnimatedPathSegList::SetBaseValueString(const nsAString& aValue)
22 : {
23 88 : SVGPathData newBaseValue;
24 :
25 : // The spec says that the path data is parsed and accepted up to the first
26 : // error encountered, so we don't return early if an error occurs. However,
27 : // we do want to throw any error code from setAttribute if there's a problem.
28 :
29 44 : nsresult rv = newBaseValue.SetValueFromString(aValue);
30 :
31 : // We must send these notifications *before* changing mBaseVal! Our baseVal's
32 : // DOM wrapper list may have to remove DOM items from itself, and any removed
33 : // DOM items need to copy their internal counterpart's values *before* we
34 : // change them. See the comments in
35 : // DOMSVGPathSegList::InternalListWillChangeTo().
36 :
37 : DOMSVGPathSegList *baseValWrapper =
38 44 : DOMSVGPathSegList::GetDOMWrapperIfExists(GetBaseValKey());
39 44 : if (baseValWrapper) {
40 0 : baseValWrapper->InternalListWillChangeTo(newBaseValue);
41 : }
42 :
43 44 : DOMSVGPathSegList* animValWrapper = nullptr;
44 44 : if (!IsAnimating()) { // DOM anim val wraps our base val too!
45 44 : animValWrapper = DOMSVGPathSegList::GetDOMWrapperIfExists(GetAnimValKey());
46 44 : if (animValWrapper) {
47 0 : animValWrapper->InternalListWillChangeTo(newBaseValue);
48 : }
49 : }
50 :
51 : // Only now may we modify mBaseVal!
52 :
53 : // We don't need to call DidChange* here - we're only called by
54 : // nsSVGElement::ParseAttribute under Element::SetAttr,
55 : // which takes care of notifying.
56 :
57 44 : nsresult rv2 = mBaseVal.CopyFrom(newBaseValue);
58 44 : if (NS_FAILED(rv2)) {
59 : // Attempting to increase mBaseVal's length failed (mBaseVal is left
60 : // unmodified). We MUST keep any DOM wrappers in sync:
61 0 : if (baseValWrapper) {
62 0 : baseValWrapper->InternalListWillChangeTo(mBaseVal);
63 : }
64 0 : if (animValWrapper) {
65 0 : animValWrapper->InternalListWillChangeTo(mBaseVal);
66 : }
67 0 : return rv2;
68 : }
69 44 : return rv;
70 : }
71 :
72 : void
73 0 : SVGAnimatedPathSegList::ClearBaseValue()
74 : {
75 : // We must send these notifications *before* changing mBaseVal! (See above.)
76 :
77 : DOMSVGPathSegList *baseValWrapper =
78 0 : DOMSVGPathSegList::GetDOMWrapperIfExists(GetBaseValKey());
79 0 : if (baseValWrapper) {
80 0 : baseValWrapper->InternalListWillChangeTo(SVGPathData());
81 : }
82 :
83 0 : if (!IsAnimating()) { // DOM anim val wraps our base val too!
84 : DOMSVGPathSegList *animValWrapper =
85 0 : DOMSVGPathSegList::GetDOMWrapperIfExists(GetAnimValKey());
86 0 : if (animValWrapper) {
87 0 : animValWrapper->InternalListWillChangeTo(SVGPathData());
88 : }
89 : }
90 :
91 0 : mBaseVal.Clear();
92 : // Caller notifies
93 0 : }
94 :
95 : nsresult
96 0 : SVGAnimatedPathSegList::SetAnimValue(const SVGPathData& aNewAnimValue,
97 : nsSVGElement *aElement)
98 : {
99 : // Note that a new animation may totally change the number of items in the
100 : // animVal list, either replacing what was essentially a mirror of the
101 : // baseVal list, or else replacing and overriding an existing animation.
102 : // Unfortunately it is not possible for us to reliably distinguish between
103 : // calls to this method that are setting a new sample for an existing
104 : // animation, and calls that are setting the first sample of an animation
105 : // that will override an existing animation. In the case of DOMSVGPathSegList
106 : // the InternalListWillChangeTo method is not virtually free as it is for the
107 : // other DOM list classes, so this is a shame. We'd quite like to be able to
108 : // skip the call if possible.
109 :
110 : // We must send these notifications *before* changing mAnimVal! (See above.)
111 :
112 : DOMSVGPathSegList *domWrapper =
113 0 : DOMSVGPathSegList::GetDOMWrapperIfExists(GetAnimValKey());
114 0 : if (domWrapper) {
115 0 : domWrapper->InternalListWillChangeTo(aNewAnimValue);
116 : }
117 0 : if (!mAnimVal) {
118 0 : mAnimVal = new SVGPathData();
119 : }
120 0 : nsresult rv = mAnimVal->CopyFrom(aNewAnimValue);
121 0 : if (NS_FAILED(rv)) {
122 : // OOM. We clear the animation and, importantly, ClearAnimValue() ensures
123 : // that mAnimVal's DOM wrapper (if any) is kept in sync!
124 0 : ClearAnimValue(aElement);
125 : }
126 0 : aElement->DidAnimatePathSegList();
127 0 : return rv;
128 : }
129 :
130 : void
131 0 : SVGAnimatedPathSegList::ClearAnimValue(nsSVGElement *aElement)
132 : {
133 : // We must send these notifications *before* changing mAnimVal! (See above.)
134 :
135 : DOMSVGPathSegList *domWrapper =
136 0 : DOMSVGPathSegList::GetDOMWrapperIfExists(GetAnimValKey());
137 0 : if (domWrapper) {
138 : // When all animation ends, animVal simply mirrors baseVal, which may have
139 : // a different number of items to the last active animated value.
140 : //
141 0 : domWrapper->InternalListWillChangeTo(mBaseVal);
142 : }
143 0 : mAnimVal = nullptr;
144 0 : aElement->DidAnimatePathSegList();
145 0 : }
146 :
147 : UniquePtr<nsISMILAttr>
148 0 : SVGAnimatedPathSegList::ToSMILAttr(nsSVGElement *aElement)
149 : {
150 0 : return MakeUnique<SMILAnimatedPathSegList>(this, aElement);
151 : }
152 :
153 : nsresult
154 0 : SVGAnimatedPathSegList::
155 : SMILAnimatedPathSegList::ValueFromString(const nsAString& aStr,
156 : const dom::SVGAnimationElement* /*aSrcElement*/,
157 : nsSMILValue& aValue,
158 : bool& aPreventCachingOfSandwich) const
159 : {
160 0 : nsSMILValue val(SVGPathSegListSMILType::Singleton());
161 0 : SVGPathDataAndInfo *list = static_cast<SVGPathDataAndInfo*>(val.mU.mPtr);
162 0 : nsresult rv = list->SetValueFromString(aStr);
163 0 : if (NS_SUCCEEDED(rv)) {
164 0 : list->SetElement(mElement);
165 0 : aValue = Move(val);
166 : }
167 0 : aPreventCachingOfSandwich = false;
168 0 : return rv;
169 : }
170 :
171 : nsSMILValue
172 0 : SVGAnimatedPathSegList::SMILAnimatedPathSegList::GetBaseValue() const
173 : {
174 : // To benefit from Return Value Optimization and avoid copy constructor calls
175 : // due to our use of return-by-value, we must return the exact same object
176 : // from ALL return points. This function must only return THIS variable:
177 0 : nsSMILValue val;
178 :
179 0 : nsSMILValue tmp(SVGPathSegListSMILType::Singleton());
180 0 : SVGPathDataAndInfo *list = static_cast<SVGPathDataAndInfo*>(tmp.mU.mPtr);
181 0 : nsresult rv = list->CopyFrom(mVal->mBaseVal);
182 0 : if (NS_SUCCEEDED(rv)) {
183 0 : list->SetElement(mElement);
184 0 : val = Move(tmp);
185 : }
186 0 : return val;
187 : }
188 :
189 : nsresult
190 0 : SVGAnimatedPathSegList::SMILAnimatedPathSegList::SetAnimValue(const nsSMILValue& aValue)
191 : {
192 0 : NS_ASSERTION(aValue.mType == SVGPathSegListSMILType::Singleton(),
193 : "Unexpected type to assign animated value");
194 0 : if (aValue.mType == SVGPathSegListSMILType::Singleton()) {
195 0 : mVal->SetAnimValue(*static_cast<SVGPathDataAndInfo*>(aValue.mU.mPtr),
196 0 : mElement);
197 : }
198 0 : return NS_OK;
199 : }
200 :
201 : void
202 0 : SVGAnimatedPathSegList::SMILAnimatedPathSegList::ClearAnimValue()
203 : {
204 0 : if (mVal->mAnimVal) {
205 0 : mVal->ClearAnimValue(mElement);
206 : }
207 0 : }
208 :
209 : size_t
210 33 : SVGAnimatedPathSegList::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
211 : {
212 33 : size_t total = mBaseVal.SizeOfExcludingThis(aMallocSizeOf);
213 33 : if (mAnimVal) {
214 0 : mAnimVal->SizeOfIncludingThis(aMallocSizeOf);
215 : }
216 33 : return total;
217 : }
218 :
219 : } // namespace mozilla
|