diff options
Diffstat (limited to 'dom/svg/SVGAnimatedLengthList.cpp')
-rw-r--r-- | dom/svg/SVGAnimatedLengthList.cpp | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/dom/svg/SVGAnimatedLengthList.cpp b/dom/svg/SVGAnimatedLengthList.cpp new file mode 100644 index 000000000..91702a16b --- /dev/null +++ b/dom/svg/SVGAnimatedLengthList.cpp @@ -0,0 +1,212 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SVGAnimatedLengthList.h" + +#include "DOMSVGAnimatedLengthList.h" +#include "mozilla/Move.h" +#include "nsSVGElement.h" +#include "nsSVGAttrTearoffTable.h" +#include "nsSMILValue.h" +#include "SVGLengthListSMILType.h" + +namespace mozilla { + +nsresult +SVGAnimatedLengthList::SetBaseValueString(const nsAString& aValue) +{ + SVGLengthList newBaseValue; + nsresult rv = newBaseValue.SetValueFromString(aValue); + if (NS_FAILED(rv)) { + return rv; + } + + DOMSVGAnimatedLengthList *domWrapper = + DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(this); + if (domWrapper) { + // We must send this notification *before* changing mBaseVal! If the length + // of our baseVal is being reduced, our baseVal's DOM wrapper list may have + // to remove DOM items from itself, and any removed DOM items need to copy + // their internal counterpart values *before* we change them. + // + domWrapper->InternalBaseValListWillChangeTo(newBaseValue); + } + + // We don't need to call DidChange* here - we're only called by + // nsSVGElement::ParseAttribute under Element::SetAttr, + // which takes care of notifying. + + rv = mBaseVal.CopyFrom(newBaseValue); + if (NS_FAILED(rv) && domWrapper) { + // Attempting to increase mBaseVal's length failed - reduce domWrapper + // back to the same length: + domWrapper->InternalBaseValListWillChangeTo(mBaseVal); + } + return rv; +} + +void +SVGAnimatedLengthList::ClearBaseValue(uint32_t aAttrEnum) +{ + DOMSVGAnimatedLengthList *domWrapper = + DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(this); + if (domWrapper) { + // We must send this notification *before* changing mBaseVal! (See above.) + domWrapper->InternalBaseValListWillChangeTo(SVGLengthList()); + } + mBaseVal.Clear(); + // Caller notifies +} + +nsresult +SVGAnimatedLengthList::SetAnimValue(const SVGLengthList& aNewAnimValue, + nsSVGElement *aElement, + uint32_t aAttrEnum) +{ + DOMSVGAnimatedLengthList *domWrapper = + DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(this); + if (domWrapper) { + // A new animation may totally change the number of items in the animVal + // list, replacing what was essentially a mirror of the baseVal list, or + // else replacing and overriding an existing animation. When this happens + // we must try and keep our animVal's DOM wrapper in sync (see the comment + // in DOMSVGAnimatedLengthList::InternalBaseValListWillChangeTo). + // + // It's not possible for us to reliably distinguish between calls to this + // method that are setting a new sample for an existing animation, and + // calls that are setting the first sample of an animation that will + // override an existing animation. Happily it's cheap to just blindly + // notify our animVal's DOM wrapper of its internal counterpart's new value + // each time this method is called, so that's what we do. + // + // Note that we must send this notification *before* setting or changing + // mAnimVal! (See the comment in SetBaseValueString above.) + // + domWrapper->InternalAnimValListWillChangeTo(aNewAnimValue); + } + if (!mAnimVal) { + mAnimVal = new SVGLengthList(); + } + nsresult rv = mAnimVal->CopyFrom(aNewAnimValue); + if (NS_FAILED(rv)) { + // OOM. We clear the animation, and, importantly, ClearAnimValue() ensures + // that mAnimVal and its DOM wrapper (if any) will have the same length! + ClearAnimValue(aElement, aAttrEnum); + return rv; + } + aElement->DidAnimateLengthList(aAttrEnum); + return NS_OK; +} + +void +SVGAnimatedLengthList::ClearAnimValue(nsSVGElement *aElement, + uint32_t aAttrEnum) +{ + DOMSVGAnimatedLengthList *domWrapper = + DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(this); + if (domWrapper) { + // When all animation ends, animVal simply mirrors baseVal, which may have + // a different number of items to the last active animated value. We must + // keep the length of our animVal's DOM wrapper list in sync, and again we + // must do that before touching mAnimVal. See comments above. + // + domWrapper->InternalAnimValListWillChangeTo(mBaseVal); + } + mAnimVal = nullptr; + aElement->DidAnimateLengthList(aAttrEnum); +} + +nsISMILAttr* +SVGAnimatedLengthList::ToSMILAttr(nsSVGElement *aSVGElement, + uint8_t aAttrEnum, + uint8_t aAxis, + bool aCanZeroPadList) +{ + return new SMILAnimatedLengthList(this, aSVGElement, aAttrEnum, aAxis, aCanZeroPadList); +} + +nsresult +SVGAnimatedLengthList:: + SMILAnimatedLengthList::ValueFromString(const nsAString& aStr, + const dom::SVGAnimationElement* /*aSrcElement*/, + nsSMILValue& aValue, + bool& aPreventCachingOfSandwich) const +{ + nsSMILValue val(&SVGLengthListSMILType::sSingleton); + SVGLengthListAndInfo *llai = static_cast<SVGLengthListAndInfo*>(val.mU.mPtr); + nsresult rv = llai->SetValueFromString(aStr); + if (NS_SUCCEEDED(rv)) { + llai->SetInfo(mElement, mAxis, mCanZeroPadList); + aValue = Move(val); + + // If any of the lengths in the list depend on their context, then we must + // prevent caching of the entire animation sandwich. This is because the + // units of a length at a given index can change from sandwich layer to + // layer, and indeed even be different within a single sandwich layer. If + // any length in the result of an animation sandwich is the result of the + // addition of lengths where one or more of those lengths is context + // dependent, then naturally the resultant length is also context + // dependent, regardless of whether its actual unit is context dependent or + // not. Unfortunately normal invalidation mechanisms won't cause us to + // recalculate the result of the sandwich if the context changes, so we + // take the (substantial) performance hit of preventing caching of the + // sandwich layer, causing the animation sandwich to be recalculated every + // single sample. + + aPreventCachingOfSandwich = false; + for (uint32_t i = 0; i < llai->Length(); ++i) { + uint8_t unit = (*llai)[i].GetUnit(); + if (unit == nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE || + unit == nsIDOMSVGLength::SVG_LENGTHTYPE_EMS || + unit == nsIDOMSVGLength::SVG_LENGTHTYPE_EXS) { + aPreventCachingOfSandwich = true; + break; + } + } + } + return rv; +} + +nsSMILValue +SVGAnimatedLengthList::SMILAnimatedLengthList::GetBaseValue() const +{ + // To benefit from Return Value Optimization and avoid copy constructor calls + // due to our use of return-by-value, we must return the exact same object + // from ALL return points. This function must only return THIS variable: + nsSMILValue val; + + nsSMILValue tmp(&SVGLengthListSMILType::sSingleton); + SVGLengthListAndInfo *llai = static_cast<SVGLengthListAndInfo*>(tmp.mU.mPtr); + nsresult rv = llai->CopyFrom(mVal->mBaseVal); + if (NS_SUCCEEDED(rv)) { + llai->SetInfo(mElement, mAxis, mCanZeroPadList); + val = Move(tmp); + } + return val; +} + +nsresult +SVGAnimatedLengthList::SMILAnimatedLengthList::SetAnimValue(const nsSMILValue& aValue) +{ + NS_ASSERTION(aValue.mType == &SVGLengthListSMILType::sSingleton, + "Unexpected type to assign animated value"); + if (aValue.mType == &SVGLengthListSMILType::sSingleton) { + mVal->SetAnimValue(*static_cast<SVGLengthListAndInfo*>(aValue.mU.mPtr), + mElement, + mAttrEnum); + } + return NS_OK; +} + +void +SVGAnimatedLengthList::SMILAnimatedLengthList::ClearAnimValue() +{ + if (mVal->mAnimVal) { + mVal->ClearAnimValue(mElement, mAttrEnum); + } +} + +} // namespace mozilla |