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 mBaseVal
.SwapWith(newBaseValue
);
61 void SVGAnimatedPointList::ClearBaseValue() {
62 // We must send these notifications *before* changing mBaseVal! (See above.)
64 DOMSVGPointList
* baseValWrapper
=
65 DOMSVGPointList::GetDOMWrapperIfExists(GetBaseValKey());
67 baseValWrapper
->InternalListWillChangeTo(SVGPointList());
70 if (!IsAnimating()) { // DOM anim val wraps our base val too!
71 DOMSVGPointList
* animValWrapper
=
72 DOMSVGPointList::GetDOMWrapperIfExists(GetAnimValKey());
74 animValWrapper
->InternalListWillChangeTo(SVGPointList());
82 nsresult
SVGAnimatedPointList::SetAnimValue(const SVGPointList
& aNewAnimValue
,
83 SVGElement
* aElement
) {
84 // Note that a new animation may totally change the number of items in the
85 // animVal list, either replacing what was essentially a mirror of the
86 // baseVal list, or else replacing and overriding an existing animation.
87 // It is not possible for us to reliably distinguish between calls to this
88 // method that are setting a new sample for an existing animation (in which
89 // case our list length isn't changing and we wouldn't need to notify our DOM
90 // wrapper to keep its length in sync), and calls to this method that are
91 // setting the first sample of a new animation that will override the base
92 // value/an existing animation (in which case our length may be changing and
93 // our DOM wrapper may need to be notified). Happily though, it's cheap to
94 // just blindly notify our animVal's DOM wrapper of our new value each time
95 // this method is called, so that's what we do.
97 // We must send this notification *before* changing mAnimVal! (See above.)
99 DOMSVGPointList
* domWrapper
=
100 DOMSVGPointList::GetDOMWrapperIfExists(GetAnimValKey());
102 domWrapper
->InternalListWillChangeTo(aNewAnimValue
);
105 mAnimVal
= MakeUnique
<SVGPointList
>();
107 nsresult rv
= mAnimVal
->CopyFrom(aNewAnimValue
);
109 // OOM. We clear the animation and, importantly, ClearAnimValue() ensures
110 // that mAnimVal's DOM wrapper (if any) is kept in sync!
111 ClearAnimValue(aElement
);
114 aElement
->DidAnimatePointList();
118 void SVGAnimatedPointList::ClearAnimValue(SVGElement
* aElement
) {
119 // We must send these notifications *before* changing mAnimVal! (See above.)
121 DOMSVGPointList
* domWrapper
=
122 DOMSVGPointList::GetDOMWrapperIfExists(GetAnimValKey());
124 // When all animation ends, animVal simply mirrors baseVal, which may have
125 // a different number of items to the last active animated value.
127 domWrapper
->InternalListWillChangeTo(mBaseVal
);
130 aElement
->DidAnimatePointList();
133 UniquePtr
<SMILAttr
> SVGAnimatedPointList::ToSMILAttr(SVGElement
* aElement
) {
134 return MakeUnique
<SMILAnimatedPointList
>(this, aElement
);
137 nsresult
SVGAnimatedPointList::SMILAnimatedPointList::ValueFromString(
138 const nsAString
& aStr
, const dom::SVGAnimationElement
* /*aSrcElement*/,
139 SMILValue
& aValue
, bool& aPreventCachingOfSandwich
) const {
140 SMILValue
val(&SVGPointListSMILType::sSingleton
);
141 SVGPointListAndInfo
* list
= static_cast<SVGPointListAndInfo
*>(val
.mU
.mPtr
);
142 nsresult rv
= list
->SetValueFromString(aStr
);
143 if (NS_SUCCEEDED(rv
)) {
144 list
->SetInfo(mElement
);
145 aValue
= std::move(val
);
150 SMILValue
SVGAnimatedPointList::SMILAnimatedPointList::GetBaseValue() const {
151 // To benefit from Return Value Optimization and avoid copy constructor calls
152 // due to our use of return-by-value, we must return the exact same object
153 // from ALL return points. This function must only return THIS variable:
156 SMILValue
tmp(&SVGPointListSMILType::sSingleton
);
157 auto* list
= static_cast<SVGPointListAndInfo
*>(tmp
.mU
.mPtr
);
158 nsresult rv
= list
->CopyFrom(mVal
->mBaseVal
);
159 if (NS_SUCCEEDED(rv
)) {
160 list
->SetInfo(mElement
);
166 nsresult
SVGAnimatedPointList::SMILAnimatedPointList::SetAnimValue(
167 const SMILValue
& aValue
) {
168 NS_ASSERTION(aValue
.mType
== &SVGPointListSMILType::sSingleton
,
169 "Unexpected type to assign animated value");
170 if (aValue
.mType
== &SVGPointListSMILType::sSingleton
) {
171 mVal
->SetAnimValue(*static_cast<SVGPointListAndInfo
*>(aValue
.mU
.mPtr
),
177 void SVGAnimatedPointList::SMILAnimatedPointList::ClearAnimValue() {
178 if (mVal
->mAnimVal
) {
179 mVal
->ClearAnimValue(mElement
);
183 } // namespace mozilla