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 "SVGAnimatedPointList.h"
8 :
9 : #include "DOMSVGPointList.h"
10 : #include "mozilla/Move.h"
11 : #include "nsSVGElement.h"
12 : #include "nsSVGAttrTearoffTable.h"
13 : #include "nsSMILValue.h"
14 : #include "SVGPointListSMILType.h"
15 :
16 : // See the comments in this file's header!
17 :
18 : namespace mozilla {
19 :
20 : nsresult
21 14 : SVGAnimatedPointList::SetBaseValueString(const nsAString& aValue)
22 : {
23 28 : SVGPointList newBaseValue;
24 :
25 : // The spec says that the point 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 14 : 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 : // DOMSVGPointList::InternalListWillChangeTo().
36 :
37 : DOMSVGPointList *baseValWrapper =
38 14 : DOMSVGPointList::GetDOMWrapperIfExists(GetBaseValKey());
39 14 : if (baseValWrapper) {
40 0 : baseValWrapper->InternalListWillChangeTo(newBaseValue);
41 : }
42 :
43 14 : DOMSVGPointList* animValWrapper = nullptr;
44 14 : if (!IsAnimating()) { // DOM anim val wraps our base val too!
45 14 : animValWrapper = DOMSVGPointList::GetDOMWrapperIfExists(GetAnimValKey());
46 14 : 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 14 : nsresult rv2 = mBaseVal.CopyFrom(newBaseValue);
58 14 : 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 14 : return rv;
70 : }
71 :
72 : void
73 0 : SVGAnimatedPointList::ClearBaseValue()
74 : {
75 : // We must send these notifications *before* changing mBaseVal! (See above.)
76 :
77 : DOMSVGPointList *baseValWrapper =
78 0 : DOMSVGPointList::GetDOMWrapperIfExists(GetBaseValKey());
79 0 : if (baseValWrapper) {
80 0 : baseValWrapper->InternalListWillChangeTo(SVGPointList());
81 : }
82 :
83 0 : if (!IsAnimating()) { // DOM anim val wraps our base val too!
84 : DOMSVGPointList *animValWrapper =
85 0 : DOMSVGPointList::GetDOMWrapperIfExists(GetAnimValKey());
86 0 : if (animValWrapper) {
87 0 : animValWrapper->InternalListWillChangeTo(SVGPointList());
88 : }
89 : }
90 :
91 0 : mBaseVal.Clear();
92 : // Caller notifies
93 0 : }
94 :
95 : nsresult
96 0 : SVGAnimatedPointList::SetAnimValue(const SVGPointList& 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 : // It is not possible for us to reliably distinguish between calls to this
103 : // method that are setting a new sample for an existing animation (in which
104 : // case our list length isn't changing and we wouldn't need to notify our DOM
105 : // wrapper to keep its length in sync), and calls to this method that are
106 : // setting the first sample of a new animation that will override the base
107 : // value/an existing animation (in which case our length may be changing and
108 : // our DOM wrapper may need to be notified). Happily though, it's cheap to
109 : // just blindly notify our animVal's DOM wrapper of our new value each time
110 : // this method is called, so that's what we do.
111 :
112 : // We must send this notification *before* changing mAnimVal! (See above.)
113 :
114 : DOMSVGPointList *domWrapper =
115 0 : DOMSVGPointList::GetDOMWrapperIfExists(GetAnimValKey());
116 0 : if (domWrapper) {
117 0 : domWrapper->InternalListWillChangeTo(aNewAnimValue);
118 : }
119 0 : if (!mAnimVal) {
120 0 : mAnimVal = new SVGPointList();
121 : }
122 0 : nsresult rv = mAnimVal->CopyFrom(aNewAnimValue);
123 0 : if (NS_FAILED(rv)) {
124 : // OOM. We clear the animation and, importantly, ClearAnimValue() ensures
125 : // that mAnimVal's DOM wrapper (if any) is kept in sync!
126 0 : ClearAnimValue(aElement);
127 0 : return rv;
128 : }
129 0 : aElement->DidAnimatePointList();
130 0 : return NS_OK;
131 : }
132 :
133 : void
134 0 : SVGAnimatedPointList::ClearAnimValue(nsSVGElement *aElement)
135 : {
136 : // We must send these notifications *before* changing mAnimVal! (See above.)
137 :
138 : DOMSVGPointList *domWrapper =
139 0 : DOMSVGPointList::GetDOMWrapperIfExists(GetAnimValKey());
140 0 : if (domWrapper) {
141 : // When all animation ends, animVal simply mirrors baseVal, which may have
142 : // a different number of items to the last active animated value.
143 : //
144 0 : domWrapper->InternalListWillChangeTo(mBaseVal);
145 : }
146 0 : mAnimVal = nullptr;
147 0 : aElement->DidAnimatePointList();
148 0 : }
149 :
150 : UniquePtr<nsISMILAttr>
151 0 : SVGAnimatedPointList::ToSMILAttr(nsSVGElement *aElement)
152 : {
153 0 : return MakeUnique<SMILAnimatedPointList>(this, aElement);
154 : }
155 :
156 : nsresult
157 0 : SVGAnimatedPointList::
158 : SMILAnimatedPointList::ValueFromString(const nsAString& aStr,
159 : const dom::SVGAnimationElement* /*aSrcElement*/,
160 : nsSMILValue& aValue,
161 : bool& aPreventCachingOfSandwich) const
162 : {
163 0 : nsSMILValue val(&SVGPointListSMILType::sSingleton);
164 0 : SVGPointListAndInfo *list = static_cast<SVGPointListAndInfo*>(val.mU.mPtr);
165 0 : nsresult rv = list->SetValueFromString(aStr);
166 0 : if (NS_SUCCEEDED(rv)) {
167 0 : list->SetInfo(mElement);
168 0 : aValue = Move(val);
169 : }
170 0 : aPreventCachingOfSandwich = false;
171 0 : return rv;
172 : }
173 :
174 : nsSMILValue
175 0 : SVGAnimatedPointList::SMILAnimatedPointList::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(&SVGPointListSMILType::sSingleton);
183 0 : SVGPointListAndInfo *list = static_cast<SVGPointListAndInfo*>(tmp.mU.mPtr);
184 0 : nsresult rv = list->CopyFrom(mVal->mBaseVal);
185 0 : if (NS_SUCCEEDED(rv)) {
186 0 : list->SetInfo(mElement);
187 0 : Swap(val, tmp);
188 : }
189 0 : return val;
190 : }
191 :
192 : nsresult
193 0 : SVGAnimatedPointList::SMILAnimatedPointList::SetAnimValue(const nsSMILValue& aValue)
194 : {
195 0 : NS_ASSERTION(aValue.mType == &SVGPointListSMILType::sSingleton,
196 : "Unexpected type to assign animated value");
197 0 : if (aValue.mType == &SVGPointListSMILType::sSingleton) {
198 0 : mVal->SetAnimValue(*static_cast<SVGPointListAndInfo*>(aValue.mU.mPtr),
199 0 : mElement);
200 : }
201 0 : return NS_OK;
202 : }
203 :
204 : void
205 0 : SVGAnimatedPointList::SMILAnimatedPointList::ClearAnimValue()
206 : {
207 0 : if (mVal->mAnimVal) {
208 0 : mVal->ClearAnimValue(mElement);
209 : }
210 0 : }
211 :
212 : } // namespace mozilla
|