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/. */
7 #include "SVGAnimatedPointList.h"
11 #include "DOMSVGPointList.h"
12 #include "SVGPointListSMILType.h"
13 #include "mozilla/SMILValue.h"
14 #include "mozilla/dom/SVGElement.h"
16 using namespace mozilla::dom
;
18 // See the comments in this file's header!
22 nsresult
SVGAnimatedPointList::SetBaseValueString(const nsAString
& aValue
) {
23 SVGPointList newBaseValue
;
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.
29 nsresult rv
= newBaseValue
.SetValueFromString(aValue
);
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().
37 DOMSVGPointList
* baseValWrapper
=
38 DOMSVGPointList::GetDOMWrapperIfExists(GetBaseValKey());
40 baseValWrapper
->InternalListWillChangeTo(newBaseValue
);
43 DOMSVGPointList
* animValWrapper
= nullptr;
44 if (!IsAnimating()) { // DOM anim val wraps our base val too!
45 animValWrapper
= DOMSVGPointList::GetDOMWrapperIfExists(GetAnimValKey());
47 animValWrapper
->InternalListWillChangeTo(newBaseValue
);
51 // Only now may we modify mBaseVal!
53 // We don't need to call DidChange* here - we're only called by
54 // SVGElement::ParseAttribute under Element::SetAttr,
55 // which takes care of notifying.
57 nsresult rv2
= mBaseVal
.CopyFrom(newBaseValue
);
59 // Attempting to increase mBaseVal's length failed (mBaseVal is left
60 // unmodified). We MUST keep any DOM wrappers in sync:
62 baseValWrapper
->InternalListWillChangeTo(mBaseVal
);
65 animValWrapper
->InternalListWillChangeTo(mBaseVal
);
72 void SVGAnimatedPointList::ClearBaseValue() {
73 // We must send these notifications *before* changing mBaseVal! (See above.)
75 DOMSVGPointList
* baseValWrapper
=
76 DOMSVGPointList::GetDOMWrapperIfExists(GetBaseValKey());
78 baseValWrapper
->InternalListWillChangeTo(SVGPointList());
81 if (!IsAnimating()) { // DOM anim val wraps our base val too!
82 DOMSVGPointList
* animValWrapper
=
83 DOMSVGPointList::GetDOMWrapperIfExists(GetAnimValKey());
85 animValWrapper
->InternalListWillChangeTo(SVGPointList());
93 nsresult
SVGAnimatedPointList::SetAnimValue(const SVGPointList
& aNewAnimValue
,
94 SVGElement
* aElement
) {
95 // Note that a new animation may totally change the number of items in the
96 // animVal list, either replacing what was essentially a mirror of the
97 // baseVal list, or else replacing and overriding an existing animation.
98 // It is not possible for us to reliably distinguish between calls to this
99 // method that are setting a new sample for an existing animation (in which
100 // case our list length isn't changing and we wouldn't need to notify our DOM
101 // wrapper to keep its length in sync), and calls to this method that are
102 // setting the first sample of a new animation that will override the base
103 // value/an existing animation (in which case our length may be changing and
104 // our DOM wrapper may need to be notified). Happily though, it's cheap to
105 // just blindly notify our animVal's DOM wrapper of our new value each time
106 // this method is called, so that's what we do.
108 // We must send this notification *before* changing mAnimVal! (See above.)
110 DOMSVGPointList
* domWrapper
=
111 DOMSVGPointList::GetDOMWrapperIfExists(GetAnimValKey());
113 domWrapper
->InternalListWillChangeTo(aNewAnimValue
);
116 mAnimVal
= MakeUnique
<SVGPointList
>();
118 nsresult rv
= mAnimVal
->CopyFrom(aNewAnimValue
);
120 // OOM. We clear the animation and, importantly, ClearAnimValue() ensures
121 // that mAnimVal's DOM wrapper (if any) is kept in sync!
122 ClearAnimValue(aElement
);
125 aElement
->DidAnimatePointList();
129 void SVGAnimatedPointList::ClearAnimValue(SVGElement
* aElement
) {
130 // We must send these notifications *before* changing mAnimVal! (See above.)
132 DOMSVGPointList
* domWrapper
=
133 DOMSVGPointList::GetDOMWrapperIfExists(GetAnimValKey());
135 // When all animation ends, animVal simply mirrors baseVal, which may have
136 // a different number of items to the last active animated value.
138 domWrapper
->InternalListWillChangeTo(mBaseVal
);
141 aElement
->DidAnimatePointList();
144 UniquePtr
<SMILAttr
> SVGAnimatedPointList::ToSMILAttr(SVGElement
* aElement
) {
145 return MakeUnique
<SMILAnimatedPointList
>(this, aElement
);
148 nsresult
SVGAnimatedPointList::SMILAnimatedPointList::ValueFromString(
149 const nsAString
& aStr
, const dom::SVGAnimationElement
* /*aSrcElement*/,
150 SMILValue
& aValue
, bool& aPreventCachingOfSandwich
) const {
151 SMILValue
val(&SVGPointListSMILType::sSingleton
);
152 SVGPointListAndInfo
* list
= static_cast<SVGPointListAndInfo
*>(val
.mU
.mPtr
);
153 nsresult rv
= list
->SetValueFromString(aStr
);
154 if (NS_SUCCEEDED(rv
)) {
155 list
->SetInfo(mElement
);
156 aValue
= std::move(val
);
158 aPreventCachingOfSandwich
= false;
162 SMILValue
SVGAnimatedPointList::SMILAnimatedPointList::GetBaseValue() const {
163 // To benefit from Return Value Optimization and avoid copy constructor calls
164 // due to our use of return-by-value, we must return the exact same object
165 // from ALL return points. This function must only return THIS variable:
168 SMILValue
tmp(&SVGPointListSMILType::sSingleton
);
169 SVGPointListAndInfo
* list
= static_cast<SVGPointListAndInfo
*>(tmp
.mU
.mPtr
);
170 nsresult rv
= list
->CopyFrom(mVal
->mBaseVal
);
171 if (NS_SUCCEEDED(rv
)) {
172 list
->SetInfo(mElement
);
178 nsresult
SVGAnimatedPointList::SMILAnimatedPointList::SetAnimValue(
179 const SMILValue
& aValue
) {
180 NS_ASSERTION(aValue
.mType
== &SVGPointListSMILType::sSingleton
,
181 "Unexpected type to assign animated value");
182 if (aValue
.mType
== &SVGPointListSMILType::sSingleton
) {
183 mVal
->SetAnimValue(*static_cast<SVGPointListAndInfo
*>(aValue
.mU
.mPtr
),
189 void SVGAnimatedPointList::SMILAnimatedPointList::ClearAnimValue() {
190 if (mVal
->mAnimVal
) {
191 mVal
->ClearAnimValue(mElement
);
195 } // namespace mozilla