gecko-dev/dom/svg/SVGAnimatedIntegerPair.cpp
Robert Longson f543d3efcd Bug 616362 - do not set aPreventCachingOfSandwich to false in methods that override SMILAttr::ValueFromString r=emilio
Currently SMILAnimationFunction::ParseAttr only sets its aPreventCachingOfSandwich outparam when it needs to be set to true.  (This lets us pass the same initially-false outparam into ParseAttr multiple times, and then check it for trueness at the end to see if any of the parsed values need us to prevent caching the sandwich.)

Our impls for ISMILAttr::ValueFromString should behave like that, too.  Then, we can pass ParseAttr's outparam directly to ValueFromString.

Differential Revision: https://phabricator.services.mozilla.com/D174353
2023-04-01 12:44:07 +00:00

250 lines
7.7 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 "SVGAnimatedIntegerPair.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsError.h"
#include "nsMathUtils.h"
#include "SVGAttrTearoffTable.h"
#include "SVGIntegerPairSMILType.h"
#include "mozAutoDocUpdate.h"
#include "mozilla/SMILValue.h"
#include "mozilla/SVGContentUtils.h"
using namespace mozilla::dom;
namespace mozilla {
//----------------------------------------------------------------------
// Helper class: AutoChangeIntegerPairNotifier
// Stack-based helper class to pair calls to WillChangeIntegerPair and
// DidChangeIntegerPair.
class MOZ_RAII AutoChangeIntegerPairNotifier {
public:
AutoChangeIntegerPairNotifier(SVGAnimatedIntegerPair* aIntegerPair,
SVGElement* aSVGElement, bool aDoSetAttr = true)
: mIntegerPair(aIntegerPair),
mSVGElement(aSVGElement),
mDoSetAttr(aDoSetAttr) {
MOZ_ASSERT(mIntegerPair, "Expecting non-null integerPair");
MOZ_ASSERT(mSVGElement, "Expecting non-null element");
if (mDoSetAttr) {
mUpdateBatch.emplace(aSVGElement->GetComposedDoc(), true);
mEmptyOrOldValue = mSVGElement->WillChangeIntegerPair(
mIntegerPair->mAttrEnum, mUpdateBatch.ref());
}
}
~AutoChangeIntegerPairNotifier() {
if (mDoSetAttr) {
mSVGElement->DidChangeIntegerPair(mIntegerPair->mAttrEnum,
mEmptyOrOldValue, mUpdateBatch.ref());
}
if (mIntegerPair->mIsAnimated) {
mSVGElement->AnimationNeedsResample();
}
}
private:
SVGAnimatedIntegerPair* const mIntegerPair;
SVGElement* const mSVGElement;
Maybe<mozAutoDocUpdate> mUpdateBatch;
nsAttrValue mEmptyOrOldValue;
bool mDoSetAttr;
};
static SVGAttrTearoffTable<SVGAnimatedIntegerPair,
SVGAnimatedIntegerPair::DOMAnimatedInteger>
sSVGFirstAnimatedIntegerTearoffTable;
static SVGAttrTearoffTable<SVGAnimatedIntegerPair,
SVGAnimatedIntegerPair::DOMAnimatedInteger>
sSVGSecondAnimatedIntegerTearoffTable;
/* Implementation */
static nsresult ParseIntegerOptionalInteger(const nsAString& aValue,
int32_t aValues[2]) {
nsCharSeparatedTokenizerTemplate<nsContentUtils::IsHTMLWhitespace,
nsTokenizerFlags::SeparatorOptional>
tokenizer(aValue, ',');
uint32_t i;
for (i = 0; i < 2 && tokenizer.hasMoreTokens(); ++i) {
if (!SVGContentUtils::ParseInteger(tokenizer.nextToken(), aValues[i])) {
return NS_ERROR_DOM_SYNTAX_ERR;
}
}
if (i == 1) {
aValues[1] = aValues[0];
}
if (i == 0 || // Too few values.
tokenizer.hasMoreTokens() || // Too many values.
tokenizer.separatorAfterCurrentToken()) { // Trailing comma.
return NS_ERROR_DOM_SYNTAX_ERR;
}
return NS_OK;
}
nsresult SVGAnimatedIntegerPair::SetBaseValueString(
const nsAString& aValueAsString, SVGElement* aSVGElement) {
int32_t val[2];
nsresult rv = ParseIntegerOptionalInteger(aValueAsString, val);
if (NS_FAILED(rv)) {
return rv;
}
// We don't need to call DidChange* here - we're only called by
// SVGElement::ParseAttribute under Element::SetAttr,
// which takes care of notifying.
AutoChangeIntegerPairNotifier notifier(this, aSVGElement, false);
mBaseVal[0] = val[0];
mBaseVal[1] = val[1];
mIsBaseSet = true;
if (!mIsAnimated) {
mAnimVal[0] = mBaseVal[0];
mAnimVal[1] = mBaseVal[1];
}
return NS_OK;
}
void SVGAnimatedIntegerPair::GetBaseValueString(
nsAString& aValueAsString) const {
aValueAsString.Truncate();
aValueAsString.AppendInt(mBaseVal[0]);
if (mBaseVal[0] != mBaseVal[1]) {
aValueAsString.AppendLiteral(", ");
aValueAsString.AppendInt(mBaseVal[1]);
}
}
void SVGAnimatedIntegerPair::SetBaseValue(int32_t aValue, PairIndex aPairIndex,
SVGElement* aSVGElement) {
uint32_t index = (aPairIndex == eFirst ? 0 : 1);
if (mIsBaseSet && mBaseVal[index] == aValue) {
return;
}
AutoChangeIntegerPairNotifier notifier(this, aSVGElement);
mBaseVal[index] = aValue;
mIsBaseSet = true;
if (!mIsAnimated) {
mAnimVal[index] = aValue;
}
}
void SVGAnimatedIntegerPair::SetBaseValues(int32_t aValue1, int32_t aValue2,
SVGElement* aSVGElement) {
if (mIsBaseSet && mBaseVal[0] == aValue1 && mBaseVal[1] == aValue2) {
return;
}
AutoChangeIntegerPairNotifier notifier(this, aSVGElement);
mBaseVal[0] = aValue1;
mBaseVal[1] = aValue2;
mIsBaseSet = true;
if (!mIsAnimated) {
mAnimVal[0] = aValue1;
mAnimVal[1] = aValue2;
}
}
void SVGAnimatedIntegerPair::SetAnimValue(const int32_t aValue[2],
SVGElement* aSVGElement) {
if (mIsAnimated && mAnimVal[0] == aValue[0] && mAnimVal[1] == aValue[1]) {
return;
}
mAnimVal[0] = aValue[0];
mAnimVal[1] = aValue[1];
mIsAnimated = true;
aSVGElement->DidAnimateIntegerPair(mAttrEnum);
}
already_AddRefed<DOMSVGAnimatedInteger>
SVGAnimatedIntegerPair::ToDOMAnimatedInteger(PairIndex aIndex,
SVGElement* aSVGElement) {
RefPtr<DOMAnimatedInteger> domAnimatedInteger =
aIndex == eFirst ? sSVGFirstAnimatedIntegerTearoffTable.GetTearoff(this)
: sSVGSecondAnimatedIntegerTearoffTable.GetTearoff(this);
if (!domAnimatedInteger) {
domAnimatedInteger = new DOMAnimatedInteger(this, aIndex, aSVGElement);
if (aIndex == eFirst) {
sSVGFirstAnimatedIntegerTearoffTable.AddTearoff(this, domAnimatedInteger);
} else {
sSVGSecondAnimatedIntegerTearoffTable.AddTearoff(this,
domAnimatedInteger);
}
}
return domAnimatedInteger.forget();
}
SVGAnimatedIntegerPair::DOMAnimatedInteger::~DOMAnimatedInteger() {
if (mIndex == eFirst) {
sSVGFirstAnimatedIntegerTearoffTable.RemoveTearoff(mVal);
} else {
sSVGSecondAnimatedIntegerTearoffTable.RemoveTearoff(mVal);
}
}
UniquePtr<SMILAttr> SVGAnimatedIntegerPair::ToSMILAttr(
SVGElement* aSVGElement) {
return MakeUnique<SMILIntegerPair>(this, aSVGElement);
}
nsresult SVGAnimatedIntegerPair::SMILIntegerPair::ValueFromString(
const nsAString& aStr, const dom::SVGAnimationElement* /*aSrcElement*/,
SMILValue& aValue, bool& aPreventCachingOfSandwich) const {
int32_t values[2];
nsresult rv = ParseIntegerOptionalInteger(aStr, values);
if (NS_FAILED(rv)) {
return rv;
}
SMILValue val(SVGIntegerPairSMILType::Singleton());
val.mU.mIntPair[0] = values[0];
val.mU.mIntPair[1] = values[1];
aValue = val;
return NS_OK;
}
SMILValue SVGAnimatedIntegerPair::SMILIntegerPair::GetBaseValue() const {
SMILValue val(SVGIntegerPairSMILType::Singleton());
val.mU.mIntPair[0] = mVal->mBaseVal[0];
val.mU.mIntPair[1] = mVal->mBaseVal[1];
return val;
}
void SVGAnimatedIntegerPair::SMILIntegerPair::ClearAnimValue() {
if (mVal->mIsAnimated) {
mVal->mIsAnimated = false;
mVal->mAnimVal[0] = mVal->mBaseVal[0];
mVal->mAnimVal[1] = mVal->mBaseVal[1];
mSVGElement->DidAnimateIntegerPair(mVal->mAttrEnum);
}
}
nsresult SVGAnimatedIntegerPair::SMILIntegerPair::SetAnimValue(
const SMILValue& aValue) {
NS_ASSERTION(aValue.mType == SVGIntegerPairSMILType::Singleton(),
"Unexpected type to assign animated value");
if (aValue.mType == SVGIntegerPairSMILType::Singleton()) {
mVal->SetAnimValue(aValue.mU.mIntPair, mSVGElement);
}
return NS_OK;
}
} // namespace mozilla