SVGAnimatedPointList.cpp (6704B)
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 <utility> 10 11 #include "DOMSVGPointList.h" 12 #include "SVGPointListSMILType.h" 13 #include "mozilla/SMILValue.h" 14 #include "mozilla/dom/SVGElement.h" 15 16 using namespace mozilla::dom; 17 18 // See the comments in this file's header! 19 20 namespace mozilla { 21 22 nsresult SVGAnimatedPointList::SetBaseValueString(const nsAString& aValue) { 23 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 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 DOMSVGPointList::GetDOMWrapperIfExists(GetBaseValKey()); 39 if (baseValWrapper) { 40 baseValWrapper->InternalListWillChangeTo(newBaseValue); 41 } 42 43 DOMSVGPointList* animValWrapper = nullptr; 44 if (!IsAnimating()) { // DOM anim val wraps our base val too! 45 animValWrapper = DOMSVGPointList::GetDOMWrapperIfExists(GetAnimValKey()); 46 if (animValWrapper) { 47 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 // SVGElement::ParseAttribute under Element::SetAttr, 55 // which takes care of notifying. 56 57 mBaseVal.SwapWith(newBaseValue); 58 return rv; 59 } 60 61 void SVGAnimatedPointList::ClearBaseValue() { 62 // We must send these notifications *before* changing mBaseVal! (See above.) 63 64 DOMSVGPointList* baseValWrapper = 65 DOMSVGPointList::GetDOMWrapperIfExists(GetBaseValKey()); 66 if (baseValWrapper) { 67 baseValWrapper->InternalListWillChangeTo(SVGPointList()); 68 } 69 70 if (!IsAnimating()) { // DOM anim val wraps our base val too! 71 DOMSVGPointList* animValWrapper = 72 DOMSVGPointList::GetDOMWrapperIfExists(GetAnimValKey()); 73 if (animValWrapper) { 74 animValWrapper->InternalListWillChangeTo(SVGPointList()); 75 } 76 } 77 78 mBaseVal.Clear(); 79 // Caller notifies 80 } 81 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. 96 97 // We must send this notification *before* changing mAnimVal! (See above.) 98 99 DOMSVGPointList* domWrapper = 100 DOMSVGPointList::GetDOMWrapperIfExists(GetAnimValKey()); 101 if (domWrapper) { 102 domWrapper->InternalListWillChangeTo(aNewAnimValue); 103 } 104 if (!mAnimVal) { 105 mAnimVal = MakeUnique<SVGPointList>(); 106 } 107 nsresult rv = mAnimVal->CopyFrom(aNewAnimValue); 108 if (NS_FAILED(rv)) { 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); 112 return rv; 113 } 114 aElement->DidAnimatePointList(); 115 return NS_OK; 116 } 117 118 void SVGAnimatedPointList::ClearAnimValue(SVGElement* aElement) { 119 // We must send these notifications *before* changing mAnimVal! (See above.) 120 121 DOMSVGPointList* domWrapper = 122 DOMSVGPointList::GetDOMWrapperIfExists(GetAnimValKey()); 123 if (domWrapper) { 124 // When all animation ends, animVal simply mirrors baseVal, which may have 125 // a different number of items to the last active animated value. 126 // 127 domWrapper->InternalListWillChangeTo(mBaseVal); 128 } 129 mAnimVal = nullptr; 130 aElement->DidAnimatePointList(); 131 } 132 133 UniquePtr<SMILAttr> SVGAnimatedPointList::ToSMILAttr(SVGElement* aElement) { 134 return MakeUnique<SMILAnimatedPointList>(this, aElement); 135 } 136 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); 146 } 147 return rv; 148 } 149 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: 154 SMILValue val; 155 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); 161 std::swap(val, tmp); 162 } 163 return val; 164 } 165 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), 172 mElement); 173 } 174 return NS_OK; 175 } 176 177 void SVGAnimatedPointList::SMILAnimatedPointList::ClearAnimValue() { 178 if (mVal->mAnimVal) { 179 mVal->ClearAnimValue(mElement); 180 } 181 } 182 183 } // namespace mozilla