зеркало из https://github.com/mozilla/gecko-dev.git
197 строки
8.0 KiB
C++
197 строки
8.0 KiB
C++
/* -*- 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 <utility>
|
|
|
|
#include "DOMSVGAnimatedLengthList.h"
|
|
#include "SVGLengthListSMILType.h"
|
|
#include "mozilla/SMILValue.h"
|
|
#include "mozilla/dom/SVGElement.h"
|
|
#include "mozilla/dom/SVGLengthBinding.h"
|
|
|
|
namespace mozilla {
|
|
|
|
using namespace dom;
|
|
|
|
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
|
|
// SVGElement::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,
|
|
SVGElement* 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 = MakeUnique<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(SVGElement* 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);
|
|
}
|
|
|
|
UniquePtr<SMILAttr> SVGAnimatedLengthList::ToSMILAttr(SVGElement* aSVGElement,
|
|
uint8_t aAttrEnum,
|
|
uint8_t aAxis,
|
|
bool aCanZeroPadList) {
|
|
return MakeUnique<SMILAnimatedLengthList>(this, aSVGElement, aAttrEnum, aAxis,
|
|
aCanZeroPadList);
|
|
}
|
|
|
|
nsresult SVGAnimatedLengthList::SMILAnimatedLengthList::ValueFromString(
|
|
const nsAString& aStr, const dom::SVGAnimationElement* /*aSrcElement*/,
|
|
SMILValue& aValue, bool& aPreventCachingOfSandwich) const {
|
|
SMILValue 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 = std::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 == SVGLength_Binding::SVG_LENGTHTYPE_PERCENTAGE ||
|
|
unit == SVGLength_Binding::SVG_LENGTHTYPE_EMS ||
|
|
unit == SVGLength_Binding::SVG_LENGTHTYPE_EXS) {
|
|
aPreventCachingOfSandwich = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
SMILValue 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:
|
|
SMILValue val;
|
|
|
|
SMILValue 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 = std::move(tmp);
|
|
}
|
|
return val;
|
|
}
|
|
|
|
nsresult SVGAnimatedLengthList::SMILAnimatedLengthList::SetAnimValue(
|
|
const SMILValue& 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
|