forked from mirrors/gecko-dev
Unfortunately, we still have to convert the cpp version into rust version. However, it's still better than parsing it again. If we want to use the css parser to parse d attribute, we may need an extra API to get the whole input as &str from Parser. (expect_string() doesn't work because the SVG attribute value is not a quoted string.) It seems Parser::slice() cannot get the whole input because we don't know the end index of the whole input string. We should fix this in Bug 1714238. Differential Revision: https://phabricator.services.mozilla.com/D115571
2489 lines
83 KiB
C++
2489 lines
83 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 "mozilla/dom/SVGElement.h"
|
|
|
|
#include "mozilla/dom/MutationEventBinding.h"
|
|
#include "mozilla/dom/MutationObservers.h"
|
|
#include "mozilla/dom/CSSRuleBinding.h"
|
|
#include "mozilla/dom/SVGElementBinding.h"
|
|
#include "mozilla/dom/SVGGeometryElement.h"
|
|
#include "mozilla/dom/SVGLengthBinding.h"
|
|
#include "mozilla/dom/SVGSVGElement.h"
|
|
#include "mozilla/dom/SVGTests.h"
|
|
#include "mozilla/dom/SVGUnitTypesBinding.h"
|
|
#include "mozilla/dom/Element.h"
|
|
|
|
#include "mozilla/ArrayUtils.h"
|
|
#include "mozilla/DebugOnly.h"
|
|
#include "mozilla/DeclarationBlock.h"
|
|
#include "mozilla/EventListenerManager.h"
|
|
#include "mozilla/InternalMutationEvent.h"
|
|
#include "mozilla/PresShell.h"
|
|
#include "mozilla/RestyleManager.h"
|
|
#include "mozilla/SMILAnimationController.h"
|
|
#include "mozilla/StaticPrefs_layout.h"
|
|
#include "mozilla/SVGContentUtils.h"
|
|
#include "mozilla/Unused.h"
|
|
|
|
#include "mozAutoDocUpdate.h"
|
|
#include "nsAttrValueOrString.h"
|
|
#include "nsCSSProps.h"
|
|
#include "nsCSSValue.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsDOMCSSAttrDeclaration.h"
|
|
#include "nsICSSDeclaration.h"
|
|
#include "nsIContentInlines.h"
|
|
#include "mozilla/dom/Document.h"
|
|
#include "nsError.h"
|
|
#include "nsGkAtoms.h"
|
|
#include "nsIFrame.h"
|
|
#include "nsQueryObject.h"
|
|
#include "nsLayoutUtils.h"
|
|
#include "SVGAnimatedNumberList.h"
|
|
#include "SVGAnimatedLengthList.h"
|
|
#include "SVGAnimatedPointList.h"
|
|
#include "SVGAnimatedPathSegList.h"
|
|
#include "SVGAnimatedTransformList.h"
|
|
#include "SVGAnimatedBoolean.h"
|
|
#include "SVGAnimatedEnumeration.h"
|
|
#include "SVGAnimatedInteger.h"
|
|
#include "SVGAnimatedIntegerPair.h"
|
|
#include "SVGAnimatedLength.h"
|
|
#include "SVGAnimatedNumber.h"
|
|
#include "SVGAnimatedNumberPair.h"
|
|
#include "SVGAnimatedOrient.h"
|
|
#include "SVGAnimatedString.h"
|
|
#include "SVGAnimatedViewBox.h"
|
|
#include "SVGGeometryProperty.h"
|
|
#include "SVGMotionSMILAttr.h"
|
|
#include <stdarg.h>
|
|
|
|
// This is needed to ensure correct handling of calls to the
|
|
// vararg-list methods in this file:
|
|
// SVGElement::GetAnimated{Length,Number,Integer}Values
|
|
// See bug 547964 for details:
|
|
static_assert(sizeof(void*) == sizeof(nullptr),
|
|
"nullptr should be the correct size");
|
|
|
|
nsresult NS_NewSVGElement(
|
|
mozilla::dom::Element** aResult,
|
|
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) {
|
|
RefPtr<mozilla::dom::NodeInfo> nodeInfo(aNodeInfo);
|
|
auto* nim = nodeInfo->NodeInfoManager();
|
|
RefPtr<mozilla::dom::SVGElement> it =
|
|
new (nim) mozilla::dom::SVGElement(nodeInfo.forget());
|
|
nsresult rv = it->Init();
|
|
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
it.forget(aResult);
|
|
return rv;
|
|
}
|
|
|
|
namespace mozilla {
|
|
namespace dom {
|
|
using namespace SVGUnitTypes_Binding;
|
|
|
|
NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGElement)
|
|
|
|
// Use the CC variant of this, even though this class does not define
|
|
// a new CC participant, to make QIing to the CC interfaces faster.
|
|
NS_IMPL_QUERY_INTERFACE_CYCLE_COLLECTION_INHERITED(SVGElement, SVGElementBase,
|
|
SVGElement)
|
|
|
|
SVGEnumMapping SVGElement::sSVGUnitTypesMap[] = {
|
|
{nsGkAtoms::userSpaceOnUse, SVG_UNIT_TYPE_USERSPACEONUSE},
|
|
{nsGkAtoms::objectBoundingBox, SVG_UNIT_TYPE_OBJECTBOUNDINGBOX},
|
|
{nullptr, 0}};
|
|
|
|
SVGElement::SVGElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
|
|
: SVGElementBase(std::move(aNodeInfo)) {}
|
|
|
|
SVGElement::~SVGElement() {
|
|
OwnerDoc()->UnscheduleSVGForPresAttrEvaluation(this);
|
|
}
|
|
|
|
JSObject* SVGElement::WrapNode(JSContext* aCx,
|
|
JS::Handle<JSObject*> aGivenProto) {
|
|
return SVGElement_Binding::Wrap(aCx, this, aGivenProto);
|
|
}
|
|
|
|
template <typename Value, typename Info>
|
|
void SVGElement::AttributesInfo<Value, Info>::ResetAll() {
|
|
for (uint32_t i = 0; i < mCount; ++i) {
|
|
Reset(i);
|
|
}
|
|
}
|
|
|
|
template <typename Value, typename Info>
|
|
void SVGElement::AttributesInfo<Value, Info>::CopyAllFrom(
|
|
const AttributesInfo& aOther) {
|
|
MOZ_DIAGNOSTIC_ASSERT(mCount == aOther.mCount,
|
|
"Should only be called on clones");
|
|
for (uint32_t i = 0; i < mCount; ++i) {
|
|
mValues[i] = aOther.mValues[i];
|
|
}
|
|
}
|
|
|
|
template <>
|
|
void SVGElement::LengthAttributesInfo::Reset(uint8_t aAttrEnum) {
|
|
mValues[aAttrEnum].Init(mInfos[aAttrEnum].mCtxType, aAttrEnum,
|
|
mInfos[aAttrEnum].mDefaultValue,
|
|
mInfos[aAttrEnum].mDefaultUnitType);
|
|
}
|
|
|
|
template <>
|
|
void SVGElement::LengthListAttributesInfo::Reset(uint8_t aAttrEnum) {
|
|
mValues[aAttrEnum].ClearBaseValue(aAttrEnum);
|
|
// caller notifies
|
|
}
|
|
|
|
template <>
|
|
void SVGElement::NumberListAttributesInfo::Reset(uint8_t aAttrEnum) {
|
|
MOZ_ASSERT(aAttrEnum < mCount, "Bad attr enum");
|
|
mValues[aAttrEnum].ClearBaseValue(aAttrEnum);
|
|
// caller notifies
|
|
}
|
|
|
|
template <>
|
|
void SVGElement::NumberAttributesInfo::Reset(uint8_t aAttrEnum) {
|
|
mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue);
|
|
}
|
|
|
|
template <>
|
|
void SVGElement::NumberPairAttributesInfo::Reset(uint8_t aAttrEnum) {
|
|
mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue1,
|
|
mInfos[aAttrEnum].mDefaultValue2);
|
|
}
|
|
|
|
template <>
|
|
void SVGElement::IntegerAttributesInfo::Reset(uint8_t aAttrEnum) {
|
|
mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue);
|
|
}
|
|
|
|
template <>
|
|
void SVGElement::IntegerPairAttributesInfo::Reset(uint8_t aAttrEnum) {
|
|
mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue1,
|
|
mInfos[aAttrEnum].mDefaultValue2);
|
|
}
|
|
|
|
template <>
|
|
void SVGElement::BooleanAttributesInfo::Reset(uint8_t aAttrEnum) {
|
|
mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue);
|
|
}
|
|
|
|
template <>
|
|
void SVGElement::StringListAttributesInfo::Reset(uint8_t aAttrEnum) {
|
|
mValues[aAttrEnum].Clear();
|
|
// caller notifies
|
|
}
|
|
|
|
template <>
|
|
void SVGElement::EnumAttributesInfo::Reset(uint8_t aAttrEnum) {
|
|
mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue);
|
|
}
|
|
|
|
template <>
|
|
void SVGElement::StringAttributesInfo::Reset(uint8_t aAttrEnum) {
|
|
mValues[aAttrEnum].Init(aAttrEnum);
|
|
}
|
|
|
|
nsresult SVGElement::CopyInnerTo(mozilla::dom::Element* aDest) {
|
|
nsresult rv = Element::CopyInnerTo(aDest);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
auto* dest = static_cast<SVGElement*>(aDest);
|
|
|
|
// cloning a node must retain its internal nonce slot
|
|
if (auto* nonce = static_cast<nsString*>(GetProperty(nsGkAtoms::nonce))) {
|
|
dest->SetNonce(*nonce);
|
|
}
|
|
|
|
// If our destination is a print document, copy all the relevant length values
|
|
// etc so that they match the state of the original node.
|
|
if (aDest->OwnerDoc()->IsStaticDocument()) {
|
|
dest->GetLengthInfo().CopyAllFrom(GetLengthInfo());
|
|
dest->GetNumberInfo().CopyAllFrom(GetNumberInfo());
|
|
dest->GetNumberPairInfo().CopyAllFrom(GetNumberPairInfo());
|
|
dest->GetIntegerInfo().CopyAllFrom(GetIntegerInfo());
|
|
dest->GetIntegerPairInfo().CopyAllFrom(GetIntegerPairInfo());
|
|
dest->GetEnumInfo().CopyAllFrom(GetEnumInfo());
|
|
dest->GetStringInfo().CopyAllFrom(GetStringInfo());
|
|
dest->GetLengthListInfo().CopyAllFrom(GetLengthListInfo());
|
|
dest->GetNumberListInfo().CopyAllFrom(GetNumberListInfo());
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// SVGElement methods
|
|
|
|
void SVGElement::DidAnimateClass() {
|
|
// For Servo, snapshot the element before we change it.
|
|
PresShell* presShell = OwnerDoc()->GetPresShell();
|
|
if (presShell) {
|
|
if (nsPresContext* presContext = presShell->GetPresContext()) {
|
|
presContext->RestyleManager()->ClassAttributeWillBeChangedBySMIL(this);
|
|
}
|
|
}
|
|
|
|
nsAutoString src;
|
|
mClassAttribute.GetAnimValue(src, this);
|
|
if (!mClassAnimAttr) {
|
|
mClassAnimAttr = MakeUnique<nsAttrValue>();
|
|
}
|
|
mClassAnimAttr->ParseAtomArray(src);
|
|
|
|
// FIXME(emilio): This re-selector-matches, but we do the snapshot stuff right
|
|
// above... Is this needed anymore?
|
|
if (presShell) {
|
|
presShell->RestyleForAnimation(this, RestyleHint::RESTYLE_SELF);
|
|
}
|
|
}
|
|
|
|
nsresult SVGElement::Init() {
|
|
// Set up length attributes - can't do this in the constructor
|
|
// because we can't do a virtual call at that point
|
|
|
|
GetLengthInfo().ResetAll();
|
|
GetNumberInfo().ResetAll();
|
|
GetNumberPairInfo().ResetAll();
|
|
GetIntegerInfo().ResetAll();
|
|
GetIntegerPairInfo().ResetAll();
|
|
GetBooleanInfo().ResetAll();
|
|
GetEnumInfo().ResetAll();
|
|
|
|
if (SVGAnimatedOrient* orient = GetAnimatedOrient()) {
|
|
orient->Init();
|
|
}
|
|
|
|
if (SVGAnimatedViewBox* viewBox = GetAnimatedViewBox()) {
|
|
viewBox->Init();
|
|
}
|
|
|
|
if (SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
|
|
GetAnimatedPreserveAspectRatio()) {
|
|
preserveAspectRatio->Init();
|
|
}
|
|
|
|
GetLengthListInfo().ResetAll();
|
|
GetNumberListInfo().ResetAll();
|
|
|
|
// No need to reset SVGPointList since the default value is always the same
|
|
// (an empty list).
|
|
|
|
// No need to reset SVGPathData since the default value is always the same
|
|
// (an empty list).
|
|
|
|
GetStringInfo().ResetAll();
|
|
return NS_OK;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Implementation
|
|
|
|
//----------------------------------------------------------------------
|
|
// nsIContent methods
|
|
|
|
nsresult SVGElement::BindToTree(BindContext& aContext, nsINode& aParent) {
|
|
nsresult rv = SVGElementBase::BindToTree(aContext, aParent);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Hide any nonce from the DOM, but keep the internal value of the
|
|
// nonce by copying and resetting the internal nonce value.
|
|
if (HasFlag(NODE_HAS_NONCE_AND_HEADER_CSP) && IsInComposedDoc() &&
|
|
OwnerDoc()->GetBrowsingContext()) {
|
|
nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
|
|
"SVGElement::ResetNonce::Runnable",
|
|
[self = RefPtr<SVGElement>(this)]() {
|
|
nsAutoString nonce;
|
|
self->GetNonce(nonce);
|
|
self->SetAttr(kNameSpaceID_None, nsGkAtoms::nonce, u""_ns, true);
|
|
self->SetNonce(nonce);
|
|
}));
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult SVGElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
|
|
const nsAttrValue* aValue,
|
|
const nsAttrValue* aOldValue,
|
|
nsIPrincipal* aSubjectPrincipal,
|
|
bool aNotify) {
|
|
// We don't currently use nsMappedAttributes within SVG. If this changes, we
|
|
// need to be very careful because some nsAttrValues used by SVG point to
|
|
// member data of SVG elements and if an nsAttrValue outlives the SVG element
|
|
// whose data it points to (by virtue of being stored in
|
|
// mAttrs->mMappedAttributes, meaning it's shared between
|
|
// elements), the pointer will dangle. See bug 724680.
|
|
MOZ_ASSERT(!mAttrs.HasMappedAttrs(),
|
|
"Unexpected use of nsMappedAttributes within SVG");
|
|
|
|
// If this is an svg presentation attribute we need to map it into
|
|
// the content declaration block.
|
|
// XXX For some reason incremental mapping doesn't work, so for now
|
|
// just delete the style rule and lazily reconstruct it as needed).
|
|
if (aNamespaceID == kNameSpaceID_None && IsAttributeMapped(aName)) {
|
|
mContentDeclarationBlock = nullptr;
|
|
OwnerDoc()->ScheduleSVGForPresAttrEvaluation(this);
|
|
}
|
|
|
|
if (IsEventAttributeName(aName) && aValue) {
|
|
MOZ_ASSERT(aValue->Type() == nsAttrValue::eString,
|
|
"Expected string value for script body");
|
|
SetEventHandler(GetEventNameForAttr(aName), aValue->GetStringValue());
|
|
}
|
|
|
|
// The nonce will be copied over to an internal slot and cleared from the
|
|
// Element within BindToTree to avoid CSS Selector nonce exfiltration if
|
|
// the CSP list contains a header-delivered CSP.
|
|
if (nsGkAtoms::nonce == aName && kNameSpaceID_None == aNamespaceID) {
|
|
if (aValue) {
|
|
SetNonce(aValue->GetStringValue());
|
|
if (OwnerDoc()->GetHasCSPDeliveredThroughHeader()) {
|
|
SetFlags(NODE_HAS_NONCE_AND_HEADER_CSP);
|
|
}
|
|
} else {
|
|
RemoveNonce();
|
|
}
|
|
}
|
|
|
|
return SVGElementBase::AfterSetAttr(aNamespaceID, aName, aValue, aOldValue,
|
|
aSubjectPrincipal, aNotify);
|
|
}
|
|
|
|
bool SVGElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
|
|
const nsAString& aValue,
|
|
nsIPrincipal* aMaybeScriptedPrincipal,
|
|
nsAttrValue& aResult) {
|
|
nsresult rv = NS_OK;
|
|
bool foundMatch = false;
|
|
bool didSetResult = false;
|
|
|
|
if (aNamespaceID == kNameSpaceID_None) {
|
|
// Check for SVGAnimatedLength attribute
|
|
LengthAttributesInfo lengthInfo = GetLengthInfo();
|
|
|
|
uint32_t i;
|
|
for (i = 0; i < lengthInfo.mCount; i++) {
|
|
if (aAttribute == lengthInfo.mInfos[i].mName) {
|
|
rv = lengthInfo.mValues[i].SetBaseValueString(aValue, this, false);
|
|
if (NS_FAILED(rv)) {
|
|
lengthInfo.Reset(i);
|
|
} else {
|
|
aResult.SetTo(lengthInfo.mValues[i], &aValue);
|
|
didSetResult = true;
|
|
}
|
|
foundMatch = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!foundMatch) {
|
|
// Check for SVGAnimatedLengthList attribute
|
|
LengthListAttributesInfo lengthListInfo = GetLengthListInfo();
|
|
for (i = 0; i < lengthListInfo.mCount; i++) {
|
|
if (aAttribute == lengthListInfo.mInfos[i].mName) {
|
|
rv = lengthListInfo.mValues[i].SetBaseValueString(aValue);
|
|
if (NS_FAILED(rv)) {
|
|
lengthListInfo.Reset(i);
|
|
} else {
|
|
aResult.SetTo(lengthListInfo.mValues[i].GetBaseValue(), &aValue);
|
|
didSetResult = true;
|
|
}
|
|
foundMatch = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!foundMatch) {
|
|
// Check for SVGAnimatedNumberList attribute
|
|
NumberListAttributesInfo numberListInfo = GetNumberListInfo();
|
|
for (i = 0; i < numberListInfo.mCount; i++) {
|
|
if (aAttribute == numberListInfo.mInfos[i].mName) {
|
|
rv = numberListInfo.mValues[i].SetBaseValueString(aValue);
|
|
if (NS_FAILED(rv)) {
|
|
numberListInfo.Reset(i);
|
|
} else {
|
|
aResult.SetTo(numberListInfo.mValues[i].GetBaseValue(), &aValue);
|
|
didSetResult = true;
|
|
}
|
|
foundMatch = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!foundMatch) {
|
|
// Check for SVGAnimatedPointList attribute
|
|
if (GetPointListAttrName() == aAttribute) {
|
|
if (SVGAnimatedPointList* pointList = GetAnimatedPointList()) {
|
|
pointList->SetBaseValueString(aValue);
|
|
// The spec says we parse everything up to the failure, so we DON'T
|
|
// need to check the result of SetBaseValueString or call
|
|
// pointList->ClearBaseValue() if it fails
|
|
aResult.SetTo(pointList->GetBaseValue(), &aValue);
|
|
didSetResult = true;
|
|
foundMatch = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!foundMatch) {
|
|
// Check for SVGAnimatedPathSegList attribute
|
|
if (GetPathDataAttrName() == aAttribute) {
|
|
if (SVGAnimatedPathSegList* segList = GetAnimPathSegList()) {
|
|
segList->SetBaseValueString(aValue);
|
|
// The spec says we parse everything up to the failure, so we DON'T
|
|
// need to check the result of SetBaseValueString or call
|
|
// segList->ClearBaseValue() if it fails
|
|
aResult.SetTo(segList->GetBaseValue(), &aValue);
|
|
didSetResult = true;
|
|
foundMatch = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!foundMatch) {
|
|
// Check for SVGAnimatedNumber attribute
|
|
NumberAttributesInfo numberInfo = GetNumberInfo();
|
|
for (i = 0; i < numberInfo.mCount; i++) {
|
|
if (aAttribute == numberInfo.mInfos[i].mName) {
|
|
rv = numberInfo.mValues[i].SetBaseValueString(aValue, this);
|
|
if (NS_FAILED(rv)) {
|
|
numberInfo.Reset(i);
|
|
} else {
|
|
aResult.SetTo(numberInfo.mValues[i].GetBaseValue(), &aValue);
|
|
didSetResult = true;
|
|
}
|
|
foundMatch = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!foundMatch) {
|
|
// Check for SVGAnimatedNumberPair attribute
|
|
NumberPairAttributesInfo numberPairInfo = GetNumberPairInfo();
|
|
for (i = 0; i < numberPairInfo.mCount; i++) {
|
|
if (aAttribute == numberPairInfo.mInfos[i].mName) {
|
|
rv = numberPairInfo.mValues[i].SetBaseValueString(aValue, this);
|
|
if (NS_FAILED(rv)) {
|
|
numberPairInfo.Reset(i);
|
|
} else {
|
|
aResult.SetTo(numberPairInfo.mValues[i], &aValue);
|
|
didSetResult = true;
|
|
}
|
|
foundMatch = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!foundMatch) {
|
|
// Check for SVGAnimatedInteger attribute
|
|
IntegerAttributesInfo integerInfo = GetIntegerInfo();
|
|
for (i = 0; i < integerInfo.mCount; i++) {
|
|
if (aAttribute == integerInfo.mInfos[i].mName) {
|
|
rv = integerInfo.mValues[i].SetBaseValueString(aValue, this);
|
|
if (NS_FAILED(rv)) {
|
|
integerInfo.Reset(i);
|
|
} else {
|
|
aResult.SetTo(integerInfo.mValues[i].GetBaseValue(), &aValue);
|
|
didSetResult = true;
|
|
}
|
|
foundMatch = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!foundMatch) {
|
|
// Check for SVGAnimatedIntegerPair attribute
|
|
IntegerPairAttributesInfo integerPairInfo = GetIntegerPairInfo();
|
|
for (i = 0; i < integerPairInfo.mCount; i++) {
|
|
if (aAttribute == integerPairInfo.mInfos[i].mName) {
|
|
rv = integerPairInfo.mValues[i].SetBaseValueString(aValue, this);
|
|
if (NS_FAILED(rv)) {
|
|
integerPairInfo.Reset(i);
|
|
} else {
|
|
aResult.SetTo(integerPairInfo.mValues[i], &aValue);
|
|
didSetResult = true;
|
|
}
|
|
foundMatch = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!foundMatch) {
|
|
// Check for SVGAnimatedBoolean attribute
|
|
BooleanAttributesInfo booleanInfo = GetBooleanInfo();
|
|
for (i = 0; i < booleanInfo.mCount; i++) {
|
|
if (aAttribute == booleanInfo.mInfos[i].mName) {
|
|
nsAtom* valAtom = NS_GetStaticAtom(aValue);
|
|
rv = valAtom ? booleanInfo.mValues[i].SetBaseValueAtom(valAtom, this)
|
|
: NS_ERROR_DOM_SYNTAX_ERR;
|
|
if (NS_FAILED(rv)) {
|
|
booleanInfo.Reset(i);
|
|
} else {
|
|
aResult.SetTo(valAtom);
|
|
didSetResult = true;
|
|
}
|
|
foundMatch = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!foundMatch) {
|
|
// Check for SVGAnimatedEnumeration attribute
|
|
EnumAttributesInfo enumInfo = GetEnumInfo();
|
|
for (i = 0; i < enumInfo.mCount; i++) {
|
|
if (aAttribute == enumInfo.mInfos[i].mName) {
|
|
RefPtr<nsAtom> valAtom = NS_Atomize(aValue);
|
|
if (!enumInfo.mValues[i].SetBaseValueAtom(valAtom, this)) {
|
|
// Exact error value does not matter; we just need to mark the
|
|
// parse as failed.
|
|
rv = NS_ERROR_FAILURE;
|
|
enumInfo.Reset(i);
|
|
} else {
|
|
aResult.SetTo(valAtom);
|
|
didSetResult = true;
|
|
}
|
|
foundMatch = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!foundMatch) {
|
|
// Check for conditional processing attributes
|
|
nsCOMPtr<SVGTests> tests = do_QueryObject(this);
|
|
if (tests && tests->ParseConditionalProcessingAttribute(
|
|
aAttribute, aValue, aResult)) {
|
|
foundMatch = true;
|
|
}
|
|
}
|
|
|
|
if (!foundMatch) {
|
|
// Check for StringList attribute
|
|
StringListAttributesInfo stringListInfo = GetStringListInfo();
|
|
for (i = 0; i < stringListInfo.mCount; i++) {
|
|
if (aAttribute == stringListInfo.mInfos[i].mName) {
|
|
rv = stringListInfo.mValues[i].SetValue(aValue);
|
|
if (NS_FAILED(rv)) {
|
|
stringListInfo.Reset(i);
|
|
} else {
|
|
aResult.SetTo(stringListInfo.mValues[i], &aValue);
|
|
didSetResult = true;
|
|
}
|
|
foundMatch = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!foundMatch) {
|
|
// Check for orient attribute
|
|
if (aAttribute == nsGkAtoms::orient) {
|
|
SVGAnimatedOrient* orient = GetAnimatedOrient();
|
|
if (orient) {
|
|
rv = orient->SetBaseValueString(aValue, this, false);
|
|
if (NS_FAILED(rv)) {
|
|
orient->Init();
|
|
} else {
|
|
aResult.SetTo(*orient, &aValue);
|
|
didSetResult = true;
|
|
}
|
|
foundMatch = true;
|
|
}
|
|
// Check for viewBox attribute
|
|
} else if (aAttribute == nsGkAtoms::viewBox) {
|
|
SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
|
|
if (viewBox) {
|
|
rv = viewBox->SetBaseValueString(aValue, this, false);
|
|
if (NS_FAILED(rv)) {
|
|
viewBox->Init();
|
|
} else {
|
|
aResult.SetTo(*viewBox, &aValue);
|
|
didSetResult = true;
|
|
}
|
|
foundMatch = true;
|
|
}
|
|
// Check for preserveAspectRatio attribute
|
|
} else if (aAttribute == nsGkAtoms::preserveAspectRatio) {
|
|
SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
|
|
GetAnimatedPreserveAspectRatio();
|
|
if (preserveAspectRatio) {
|
|
rv = preserveAspectRatio->SetBaseValueString(aValue, this, false);
|
|
if (NS_FAILED(rv)) {
|
|
preserveAspectRatio->Init();
|
|
} else {
|
|
aResult.SetTo(*preserveAspectRatio, &aValue);
|
|
didSetResult = true;
|
|
}
|
|
foundMatch = true;
|
|
}
|
|
// Check for SVGAnimatedTransformList attribute
|
|
} else if (GetTransformListAttrName() == aAttribute) {
|
|
// The transform attribute is being set, so we must ensure that the
|
|
// SVGAnimatedTransformList is/has been allocated:
|
|
SVGAnimatedTransformList* transformList =
|
|
GetAnimatedTransformList(DO_ALLOCATE);
|
|
rv = transformList->SetBaseValueString(aValue, this);
|
|
if (NS_FAILED(rv)) {
|
|
transformList->ClearBaseValue();
|
|
} else {
|
|
aResult.SetTo(transformList->GetBaseValue(), &aValue);
|
|
didSetResult = true;
|
|
}
|
|
foundMatch = true;
|
|
} else if (aAttribute == nsGkAtoms::tabindex) {
|
|
didSetResult = aResult.ParseIntValue(aValue);
|
|
foundMatch = true;
|
|
}
|
|
}
|
|
|
|
if (aAttribute == nsGkAtoms::_class) {
|
|
mClassAttribute.SetBaseValue(aValue, this, false);
|
|
aResult.ParseAtomArray(aValue);
|
|
return true;
|
|
}
|
|
|
|
if (aAttribute == nsGkAtoms::rel) {
|
|
aResult.ParseAtomArray(aValue);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (!foundMatch) {
|
|
// Check for SVGAnimatedString attribute
|
|
StringAttributesInfo stringInfo = GetStringInfo();
|
|
for (uint32_t i = 0; i < stringInfo.mCount; i++) {
|
|
if (aNamespaceID == stringInfo.mInfos[i].mNamespaceID &&
|
|
aAttribute == stringInfo.mInfos[i].mName) {
|
|
stringInfo.mValues[i].SetBaseValue(aValue, this, false);
|
|
foundMatch = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (foundMatch) {
|
|
if (NS_FAILED(rv)) {
|
|
ReportAttributeParseFailure(OwnerDoc(), aAttribute, aValue);
|
|
return false;
|
|
}
|
|
if (!didSetResult) {
|
|
aResult.SetTo(aValue);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return SVGElementBase::ParseAttribute(aNamespaceID, aAttribute, aValue,
|
|
aMaybeScriptedPrincipal, aResult);
|
|
}
|
|
|
|
void SVGElement::UnsetAttrInternal(int32_t aNamespaceID, nsAtom* aName,
|
|
bool aNotify) {
|
|
// XXXbz there's a bunch of redundancy here with AfterSetAttr.
|
|
// Maybe consolidate?
|
|
|
|
if (aNamespaceID == kNameSpaceID_None) {
|
|
// If this is an svg presentation attribute, remove declaration block to
|
|
// force an update
|
|
if (IsAttributeMapped(aName)) {
|
|
mContentDeclarationBlock = nullptr;
|
|
}
|
|
|
|
if (IsEventAttributeName(aName)) {
|
|
EventListenerManager* manager = GetExistingListenerManager();
|
|
if (manager) {
|
|
nsAtom* eventName = GetEventNameForAttr(aName);
|
|
manager->RemoveEventHandler(eventName);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Check if this is a length attribute going away
|
|
LengthAttributesInfo lenInfo = GetLengthInfo();
|
|
|
|
for (uint32_t i = 0; i < lenInfo.mCount; i++) {
|
|
if (aName == lenInfo.mInfos[i].mName) {
|
|
MaybeSerializeAttrBeforeRemoval(aName, aNotify);
|
|
lenInfo.Reset(i);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check if this is a length list attribute going away
|
|
LengthListAttributesInfo lengthListInfo = GetLengthListInfo();
|
|
|
|
for (uint32_t i = 0; i < lengthListInfo.mCount; i++) {
|
|
if (aName == lengthListInfo.mInfos[i].mName) {
|
|
MaybeSerializeAttrBeforeRemoval(aName, aNotify);
|
|
lengthListInfo.Reset(i);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check if this is a number list attribute going away
|
|
NumberListAttributesInfo numberListInfo = GetNumberListInfo();
|
|
|
|
for (uint32_t i = 0; i < numberListInfo.mCount; i++) {
|
|
if (aName == numberListInfo.mInfos[i].mName) {
|
|
MaybeSerializeAttrBeforeRemoval(aName, aNotify);
|
|
numberListInfo.Reset(i);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check if this is a point list attribute going away
|
|
if (GetPointListAttrName() == aName) {
|
|
SVGAnimatedPointList* pointList = GetAnimatedPointList();
|
|
if (pointList) {
|
|
MaybeSerializeAttrBeforeRemoval(aName, aNotify);
|
|
pointList->ClearBaseValue();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check if this is a path segment list attribute going away
|
|
if (GetPathDataAttrName() == aName) {
|
|
SVGAnimatedPathSegList* segList = GetAnimPathSegList();
|
|
if (segList) {
|
|
MaybeSerializeAttrBeforeRemoval(aName, aNotify);
|
|
segList->ClearBaseValue();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check if this is a number attribute going away
|
|
NumberAttributesInfo numInfo = GetNumberInfo();
|
|
|
|
for (uint32_t i = 0; i < numInfo.mCount; i++) {
|
|
if (aName == numInfo.mInfos[i].mName) {
|
|
numInfo.Reset(i);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check if this is a number pair attribute going away
|
|
NumberPairAttributesInfo numPairInfo = GetNumberPairInfo();
|
|
|
|
for (uint32_t i = 0; i < numPairInfo.mCount; i++) {
|
|
if (aName == numPairInfo.mInfos[i].mName) {
|
|
MaybeSerializeAttrBeforeRemoval(aName, aNotify);
|
|
numPairInfo.Reset(i);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check if this is an integer attribute going away
|
|
IntegerAttributesInfo intInfo = GetIntegerInfo();
|
|
|
|
for (uint32_t i = 0; i < intInfo.mCount; i++) {
|
|
if (aName == intInfo.mInfos[i].mName) {
|
|
intInfo.Reset(i);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check if this is an integer pair attribute going away
|
|
IntegerPairAttributesInfo intPairInfo = GetIntegerPairInfo();
|
|
|
|
for (uint32_t i = 0; i < intPairInfo.mCount; i++) {
|
|
if (aName == intPairInfo.mInfos[i].mName) {
|
|
MaybeSerializeAttrBeforeRemoval(aName, aNotify);
|
|
intPairInfo.Reset(i);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check if this is a boolean attribute going away
|
|
BooleanAttributesInfo boolInfo = GetBooleanInfo();
|
|
|
|
for (uint32_t i = 0; i < boolInfo.mCount; i++) {
|
|
if (aName == boolInfo.mInfos[i].mName) {
|
|
boolInfo.Reset(i);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check if this is an enum attribute going away
|
|
EnumAttributesInfo enumInfo = GetEnumInfo();
|
|
|
|
for (uint32_t i = 0; i < enumInfo.mCount; i++) {
|
|
if (aName == enumInfo.mInfos[i].mName) {
|
|
enumInfo.Reset(i);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check if this is an orient attribute going away
|
|
if (aName == nsGkAtoms::orient) {
|
|
SVGAnimatedOrient* orient = GetAnimatedOrient();
|
|
if (orient) {
|
|
MaybeSerializeAttrBeforeRemoval(aName, aNotify);
|
|
orient->Init();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check if this is a viewBox attribute going away
|
|
if (aName == nsGkAtoms::viewBox) {
|
|
SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
|
|
if (viewBox) {
|
|
MaybeSerializeAttrBeforeRemoval(aName, aNotify);
|
|
viewBox->Init();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check if this is a preserveAspectRatio attribute going away
|
|
if (aName == nsGkAtoms::preserveAspectRatio) {
|
|
SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
|
|
GetAnimatedPreserveAspectRatio();
|
|
if (preserveAspectRatio) {
|
|
MaybeSerializeAttrBeforeRemoval(aName, aNotify);
|
|
preserveAspectRatio->Init();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check if this is a transform list attribute going away
|
|
if (GetTransformListAttrName() == aName) {
|
|
SVGAnimatedTransformList* transformList = GetAnimatedTransformList();
|
|
if (transformList) {
|
|
MaybeSerializeAttrBeforeRemoval(aName, aNotify);
|
|
transformList->ClearBaseValue();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check for conditional processing attributes
|
|
nsCOMPtr<SVGTests> tests = do_QueryObject(this);
|
|
if (tests && tests->IsConditionalProcessingAttribute(aName)) {
|
|
MaybeSerializeAttrBeforeRemoval(aName, aNotify);
|
|
tests->UnsetAttr(aName);
|
|
return;
|
|
}
|
|
|
|
// Check if this is a string list attribute going away
|
|
StringListAttributesInfo stringListInfo = GetStringListInfo();
|
|
|
|
for (uint32_t i = 0; i < stringListInfo.mCount; i++) {
|
|
if (aName == stringListInfo.mInfos[i].mName) {
|
|
MaybeSerializeAttrBeforeRemoval(aName, aNotify);
|
|
stringListInfo.Reset(i);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (aName == nsGkAtoms::_class) {
|
|
mClassAttribute.Init();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check if this is a string attribute going away
|
|
StringAttributesInfo stringInfo = GetStringInfo();
|
|
|
|
for (uint32_t i = 0; i < stringInfo.mCount; i++) {
|
|
if (aNamespaceID == stringInfo.mInfos[i].mNamespaceID &&
|
|
aName == stringInfo.mInfos[i].mName) {
|
|
stringInfo.Reset(i);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
nsresult SVGElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
|
|
const nsAttrValueOrString* aValue,
|
|
bool aNotify) {
|
|
if (!aValue) {
|
|
UnsetAttrInternal(aNamespaceID, aName, aNotify);
|
|
}
|
|
return SVGElementBase::BeforeSetAttr(aNamespaceID, aName, aValue, aNotify);
|
|
}
|
|
|
|
nsChangeHint SVGElement::GetAttributeChangeHint(const nsAtom* aAttribute,
|
|
int32_t aModType) const {
|
|
nsChangeHint retval =
|
|
SVGElementBase::GetAttributeChangeHint(aAttribute, aModType);
|
|
|
|
nsCOMPtr<SVGTests> tests = do_QueryObject(const_cast<SVGElement*>(this));
|
|
if (tests && tests->IsConditionalProcessingAttribute(aAttribute)) {
|
|
// It would be nice to only reconstruct the frame if the value returned by
|
|
// SVGTests::PassesConditionalProcessingTests has changed, but we don't
|
|
// know that
|
|
retval |= nsChangeHint_ReconstructFrame;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
bool SVGElement::IsNodeOfType(uint32_t aFlags) const { return false; }
|
|
|
|
void SVGElement::NodeInfoChanged(Document* aOldDoc) {
|
|
SVGElementBase::NodeInfoChanged(aOldDoc);
|
|
aOldDoc->UnscheduleSVGForPresAttrEvaluation(this);
|
|
mContentDeclarationBlock = nullptr;
|
|
OwnerDoc()->ScheduleSVGForPresAttrEvaluation(this);
|
|
}
|
|
|
|
NS_IMETHODIMP_(bool)
|
|
SVGElement::IsAttributeMapped(const nsAtom* name) const {
|
|
if (name == nsGkAtoms::lang) {
|
|
return true;
|
|
}
|
|
return SVGElementBase::IsAttributeMapped(name);
|
|
}
|
|
|
|
// PresentationAttributes-FillStroke
|
|
/* static */
|
|
const Element::MappedAttributeEntry SVGElement::sFillStrokeMap[] = {
|
|
{nsGkAtoms::fill},
|
|
{nsGkAtoms::fill_opacity},
|
|
{nsGkAtoms::fill_rule},
|
|
{nsGkAtoms::paint_order},
|
|
{nsGkAtoms::stroke},
|
|
{nsGkAtoms::stroke_dasharray},
|
|
{nsGkAtoms::stroke_dashoffset},
|
|
{nsGkAtoms::stroke_linecap},
|
|
{nsGkAtoms::stroke_linejoin},
|
|
{nsGkAtoms::stroke_miterlimit},
|
|
{nsGkAtoms::stroke_opacity},
|
|
{nsGkAtoms::stroke_width},
|
|
{nsGkAtoms::vector_effect},
|
|
{nullptr}};
|
|
|
|
// PresentationAttributes-Graphics
|
|
/* static */
|
|
const Element::MappedAttributeEntry SVGElement::sGraphicsMap[] = {
|
|
{nsGkAtoms::clip_path},
|
|
{nsGkAtoms::clip_rule},
|
|
{nsGkAtoms::colorInterpolation},
|
|
{nsGkAtoms::cursor},
|
|
{nsGkAtoms::display},
|
|
{nsGkAtoms::filter},
|
|
{nsGkAtoms::image_rendering},
|
|
{nsGkAtoms::mask},
|
|
{nsGkAtoms::opacity},
|
|
{nsGkAtoms::pointer_events},
|
|
{nsGkAtoms::shape_rendering},
|
|
{nsGkAtoms::text_rendering},
|
|
{nsGkAtoms::transform_origin},
|
|
{nsGkAtoms::visibility},
|
|
{nullptr}};
|
|
|
|
// PresentationAttributes-TextContentElements
|
|
/* static */
|
|
const Element::MappedAttributeEntry SVGElement::sTextContentElementsMap[] = {
|
|
// Properties that we don't support are commented out.
|
|
// { nsGkAtoms::alignment_baseline },
|
|
// { nsGkAtoms::baseline_shift },
|
|
{nsGkAtoms::direction},
|
|
{nsGkAtoms::dominant_baseline},
|
|
{nsGkAtoms::letter_spacing},
|
|
{nsGkAtoms::text_anchor},
|
|
{nsGkAtoms::text_decoration},
|
|
{nsGkAtoms::unicode_bidi},
|
|
{nsGkAtoms::word_spacing},
|
|
{nsGkAtoms::writing_mode},
|
|
{nullptr}};
|
|
|
|
// PresentationAttributes-FontSpecification
|
|
/* static */
|
|
const Element::MappedAttributeEntry SVGElement::sFontSpecificationMap[] = {
|
|
{nsGkAtoms::font_family}, {nsGkAtoms::font_size},
|
|
{nsGkAtoms::font_size_adjust}, {nsGkAtoms::font_stretch},
|
|
{nsGkAtoms::font_style}, {nsGkAtoms::font_variant},
|
|
{nsGkAtoms::fontWeight}, {nullptr}};
|
|
|
|
// PresentationAttributes-GradientStop
|
|
/* static */
|
|
const Element::MappedAttributeEntry SVGElement::sGradientStopMap[] = {
|
|
{nsGkAtoms::stop_color}, {nsGkAtoms::stop_opacity}, {nullptr}};
|
|
|
|
// PresentationAttributes-Viewports
|
|
/* static */
|
|
const Element::MappedAttributeEntry SVGElement::sViewportsMap[] = {
|
|
{nsGkAtoms::overflow}, {nsGkAtoms::clip}, {nullptr}};
|
|
|
|
// PresentationAttributes-Makers
|
|
/* static */
|
|
const Element::MappedAttributeEntry SVGElement::sMarkersMap[] = {
|
|
{nsGkAtoms::marker_end},
|
|
{nsGkAtoms::marker_mid},
|
|
{nsGkAtoms::marker_start},
|
|
{nullptr}};
|
|
|
|
// PresentationAttributes-Color
|
|
/* static */
|
|
const Element::MappedAttributeEntry SVGElement::sColorMap[] = {
|
|
{nsGkAtoms::color}, {nullptr}};
|
|
|
|
// PresentationAttributes-Filters
|
|
/* static */
|
|
const Element::MappedAttributeEntry SVGElement::sFiltersMap[] = {
|
|
{nsGkAtoms::colorInterpolationFilters}, {nullptr}};
|
|
|
|
// PresentationAttributes-feFlood
|
|
/* static */
|
|
const Element::MappedAttributeEntry SVGElement::sFEFloodMap[] = {
|
|
{nsGkAtoms::flood_color}, {nsGkAtoms::flood_opacity}, {nullptr}};
|
|
|
|
// PresentationAttributes-LightingEffects
|
|
/* static */
|
|
const Element::MappedAttributeEntry SVGElement::sLightingEffectsMap[] = {
|
|
{nsGkAtoms::lighting_color}, {nullptr}};
|
|
|
|
// PresentationAttributes-mask
|
|
/* static */
|
|
const Element::MappedAttributeEntry SVGElement::sMaskMap[] = {
|
|
{nsGkAtoms::mask_type}, {nullptr}};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Element methods
|
|
|
|
// forwarded to Element implementations
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
SVGSVGElement* SVGElement::GetOwnerSVGElement() {
|
|
nsIContent* ancestor = GetFlattenedTreeParent();
|
|
|
|
while (ancestor && ancestor->IsSVGElement()) {
|
|
if (ancestor->IsSVGElement(nsGkAtoms::foreignObject)) {
|
|
return nullptr;
|
|
}
|
|
if (ancestor->IsSVGElement(nsGkAtoms::svg)) {
|
|
return static_cast<SVGSVGElement*>(ancestor);
|
|
}
|
|
ancestor = ancestor->GetFlattenedTreeParent();
|
|
}
|
|
|
|
// we don't have an ancestor <svg> element...
|
|
return nullptr;
|
|
}
|
|
|
|
SVGElement* SVGElement::GetViewportElement() {
|
|
return SVGContentUtils::GetNearestViewportElement(this);
|
|
}
|
|
|
|
already_AddRefed<DOMSVGAnimatedString> SVGElement::ClassName() {
|
|
return mClassAttribute.ToDOMAnimatedString(this);
|
|
}
|
|
|
|
/* static */
|
|
bool SVGElement::UpdateDeclarationBlockFromLength(
|
|
DeclarationBlock& aBlock, nsCSSPropertyID aPropId,
|
|
const SVGAnimatedLength& aLength, ValToUse aValToUse) {
|
|
aBlock.AssertMutable();
|
|
|
|
float value;
|
|
if (aValToUse == ValToUse::Anim) {
|
|
value = aLength.GetAnimValInSpecifiedUnits();
|
|
} else {
|
|
MOZ_ASSERT(aValToUse == ValToUse::Base);
|
|
value = aLength.GetBaseValInSpecifiedUnits();
|
|
}
|
|
|
|
// SVG parser doesn't check non-negativity of some parsed value,
|
|
// we should not pass those to CSS side.
|
|
if (value < 0 &&
|
|
SVGGeometryProperty::IsNonNegativeGeometryProperty(aPropId)) {
|
|
return false;
|
|
}
|
|
|
|
nsCSSUnit cssUnit = SVGGeometryProperty::SpecifiedUnitTypeToCSSUnit(
|
|
aLength.GetSpecifiedUnitType());
|
|
|
|
if (cssUnit == eCSSUnit_Percent) {
|
|
Servo_DeclarationBlock_SetPercentValue(aBlock.Raw(), aPropId,
|
|
value / 100.f);
|
|
} else {
|
|
Servo_DeclarationBlock_SetLengthValue(aBlock.Raw(), aPropId, value,
|
|
cssUnit);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* static */
|
|
bool SVGElement::UpdateDeclarationBlockFromPath(
|
|
DeclarationBlock& aBlock, const SVGAnimatedPathSegList& aPath,
|
|
ValToUse aValToUse) {
|
|
aBlock.AssertMutable();
|
|
|
|
const SVGPathData& pathData =
|
|
aValToUse == ValToUse::Anim ? aPath.GetAnimValue() : aPath.GetBaseValue();
|
|
|
|
// SVGPathData::mData is fallible but rust binding accepts nsTArray only, so
|
|
// we need to point to one or the other. Fortunately, fallible and infallible
|
|
// array types can be implicitly converted provided they are const.
|
|
//
|
|
// FIXME: here we just convert the data structure from cpp verion into rust
|
|
// version. We don't do any normalization for the path data from d attribute.
|
|
// Based on the current discussion of https://github.com/w3c/svgwg/issues/321,
|
|
// we may have to convert the relative commands into absolute commands.
|
|
// The normalization should be fixed in Bug 1489392. Besides, Bug 1714238
|
|
// will use the same data structure, so we may simplify this more.
|
|
const nsTArray<float>& asInFallibleArray = pathData.RawData();
|
|
Servo_DeclarationBlock_SetPathValue(aBlock.Raw(), eCSSProperty_d,
|
|
&asInFallibleArray);
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// Helper class: MappedAttrParser, for parsing values of mapped attributes
|
|
|
|
namespace {
|
|
|
|
class MOZ_STACK_CLASS MappedAttrParser {
|
|
public:
|
|
MappedAttrParser(css::Loader* aLoader, nsIURI* aBaseURI,
|
|
SVGElement* aElement);
|
|
~MappedAttrParser();
|
|
|
|
// Parses a mapped attribute value.
|
|
void ParseMappedAttrValue(nsAtom* aMappedAttrName,
|
|
const nsAString& aMappedAttrValue);
|
|
|
|
void TellStyleAlreadyParsedResult(nsAtom const* aAtom,
|
|
SVGAnimatedLength const& aLength);
|
|
void TellStyleAlreadyParsedResult(const SVGAnimatedPathSegList& aPath);
|
|
|
|
// If we've parsed any values for mapped attributes, this method returns the
|
|
// already_AddRefed css::Declaration that incorporates the parsed
|
|
// values. Otherwise, this method returns null.
|
|
already_AddRefed<DeclarationBlock> GetDeclarationBlock();
|
|
|
|
private:
|
|
// MEMBER DATA
|
|
// -----------
|
|
css::Loader* mLoader;
|
|
|
|
nsCOMPtr<nsIURI> mBaseURI;
|
|
|
|
// Declaration for storing parsed values (lazily initialized)
|
|
RefPtr<DeclarationBlock> mDecl;
|
|
|
|
// For reporting use counters
|
|
SVGElement* mElement;
|
|
};
|
|
|
|
MappedAttrParser::MappedAttrParser(css::Loader* aLoader, nsIURI* aBaseURI,
|
|
SVGElement* aElement)
|
|
: mLoader(aLoader), mBaseURI(aBaseURI), mElement(aElement) {}
|
|
|
|
MappedAttrParser::~MappedAttrParser() {
|
|
MOZ_ASSERT(!mDecl,
|
|
"If mDecl was initialized, it should have been returned via "
|
|
"GetDeclarationBlock (and had its pointer cleared)");
|
|
}
|
|
|
|
void MappedAttrParser::ParseMappedAttrValue(nsAtom* aMappedAttrName,
|
|
const nsAString& aMappedAttrValue) {
|
|
if (!mDecl) {
|
|
mDecl = new DeclarationBlock();
|
|
}
|
|
|
|
// Get the nsCSSPropertyID ID for our mapped attribute.
|
|
nsCSSPropertyID propertyID =
|
|
nsCSSProps::LookupProperty(nsAtomCString(aMappedAttrName));
|
|
if (propertyID != eCSSProperty_UNKNOWN) {
|
|
bool changed = false; // outparam for ParseProperty.
|
|
NS_ConvertUTF16toUTF8 value(aMappedAttrValue);
|
|
|
|
// FIXME (bug 1343964): Figure out a better solution for sending the base
|
|
// uri to servo
|
|
nsCOMPtr<nsIReferrerInfo> referrerInfo =
|
|
ReferrerInfo::CreateForSVGResources(mElement->OwnerDoc());
|
|
|
|
auto data = MakeRefPtr<URLExtraData>(mBaseURI, referrerInfo,
|
|
mElement->NodePrincipal());
|
|
changed = Servo_DeclarationBlock_SetPropertyById(
|
|
mDecl->Raw(), propertyID, &value, false, data,
|
|
ParsingMode::AllowUnitlessLength,
|
|
mElement->OwnerDoc()->GetCompatibilityMode(), mLoader,
|
|
CSSRule_Binding::STYLE_RULE, {});
|
|
|
|
// TODO(emilio): If we want to record these from CSSOM more generally, we
|
|
// can pass the document use counters down the FFI call. For now manually
|
|
// count them.
|
|
if (changed && StaticPrefs::layout_css_use_counters_enabled()) {
|
|
UseCounter useCounter = nsCSSProps::UseCounterFor(propertyID);
|
|
MOZ_ASSERT(useCounter != eUseCounter_UNKNOWN);
|
|
mElement->OwnerDoc()->SetUseCounter(useCounter);
|
|
}
|
|
return;
|
|
}
|
|
MOZ_ASSERT(aMappedAttrName == nsGkAtoms::lang,
|
|
"Only 'lang' should be unrecognized!");
|
|
// CSS parser doesn't know about 'lang', so we need to handle it specially.
|
|
if (aMappedAttrName == nsGkAtoms::lang) {
|
|
propertyID = eCSSProperty__x_lang;
|
|
RefPtr<nsAtom> atom = NS_Atomize(aMappedAttrValue);
|
|
Servo_DeclarationBlock_SetIdentStringValue(mDecl->Raw(), propertyID, atom);
|
|
}
|
|
}
|
|
|
|
void MappedAttrParser::TellStyleAlreadyParsedResult(
|
|
nsAtom const* aAtom, SVGAnimatedLength const& aLength) {
|
|
if (!mDecl) {
|
|
mDecl = new DeclarationBlock();
|
|
}
|
|
nsCSSPropertyID propertyID = nsCSSProps::LookupProperty(nsAtomCString(aAtom));
|
|
SVGElement::UpdateDeclarationBlockFromLength(*mDecl, propertyID, aLength,
|
|
SVGElement::ValToUse::Base);
|
|
}
|
|
|
|
void MappedAttrParser::TellStyleAlreadyParsedResult(
|
|
const SVGAnimatedPathSegList& aPath) {
|
|
if (!mDecl) {
|
|
mDecl = new DeclarationBlock();
|
|
}
|
|
SVGElement::UpdateDeclarationBlockFromPath(*mDecl, aPath,
|
|
SVGElement::ValToUse::Base);
|
|
}
|
|
|
|
already_AddRefed<DeclarationBlock> MappedAttrParser::GetDeclarationBlock() {
|
|
return mDecl.forget();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
//----------------------------------------------------------------------
|
|
// Implementation Helpers:
|
|
|
|
void SVGElement::UpdateContentDeclarationBlock() {
|
|
NS_ASSERTION(!mContentDeclarationBlock,
|
|
"we already have a content declaration block");
|
|
|
|
uint32_t attrCount = mAttrs.AttrCount();
|
|
if (!attrCount) {
|
|
// nothing to do
|
|
return;
|
|
}
|
|
|
|
Document* doc = OwnerDoc();
|
|
MappedAttrParser mappedAttrParser(doc->CSSLoader(), GetBaseURI(), this);
|
|
|
|
bool lengthAffectsStyle =
|
|
SVGGeometryProperty::ElementMapsLengthsToStyle(this);
|
|
|
|
for (uint32_t i = 0; i < attrCount; ++i) {
|
|
const nsAttrName* attrName = mAttrs.AttrNameAt(i);
|
|
if (!attrName->IsAtom() || !IsAttributeMapped(attrName->Atom())) continue;
|
|
|
|
if (attrName->NamespaceID() != kNameSpaceID_None &&
|
|
!attrName->Equals(nsGkAtoms::lang, kNameSpaceID_XML)) {
|
|
continue;
|
|
}
|
|
|
|
if (attrName->Equals(nsGkAtoms::lang, kNameSpaceID_None) &&
|
|
HasAttr(kNameSpaceID_XML, nsGkAtoms::lang)) {
|
|
continue; // xml:lang has precedence
|
|
}
|
|
|
|
if (lengthAffectsStyle) {
|
|
auto const* length = GetAnimatedLength(attrName->Atom());
|
|
|
|
if (length && length->HasBaseVal()) {
|
|
// This is an element with geometry property set via SVG attribute,
|
|
// and the attribute is already successfully parsed. We want to go
|
|
// through the optimized path to tell the style system the result
|
|
// directly, rather than let it parse the same thing again.
|
|
mappedAttrParser.TellStyleAlreadyParsedResult(attrName->Atom(),
|
|
*length);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (attrName->Equals(nsGkAtoms::d, kNameSpaceID_None)) {
|
|
const auto* path = GetAnimPathSegList();
|
|
// Note: Only SVGPathElement has d attribute.
|
|
MOZ_ASSERT(
|
|
path,
|
|
"SVGPathElement should have the non-null SVGAnimatedPathSegList");
|
|
// The attribute should have been already successfully parsed.
|
|
// We want to go through the optimized path to tell the style system
|
|
// the result directly, rather than let it parse the same thing again.
|
|
mappedAttrParser.TellStyleAlreadyParsedResult(*path);
|
|
// Some other notes:
|
|
// The syntax of CSS d property is different from SVG d attribute.
|
|
// 1. CSS d proeprty accepts: none | path(<quoted string>);
|
|
// 2. SVG d attribtue accepts: none | <string>
|
|
// So we cannot use css parser to parse the SVG d attribute directly.
|
|
// Besides, |mAttrs.AttrAt(i)| removes the quotes already, so the svg path
|
|
// in |mAttrs.AttrAt(i)| would be something like `M0,0L1,1z` without the
|
|
// quotes. So css tokenizer cannot recognize this as a quoted string, and
|
|
// so svg_path::SVGPathData::parse() doesn't work for this. Fortunately,
|
|
// we still can rely on the parsed result from
|
|
// SVGElement::ParseAttribute() for d attribute.
|
|
continue;
|
|
}
|
|
|
|
nsAutoString value;
|
|
mAttrs.AttrAt(i)->ToString(value);
|
|
mappedAttrParser.ParseMappedAttrValue(attrName->Atom(), value);
|
|
}
|
|
mContentDeclarationBlock = mappedAttrParser.GetDeclarationBlock();
|
|
}
|
|
|
|
const DeclarationBlock* SVGElement::GetContentDeclarationBlock() const {
|
|
return mContentDeclarationBlock;
|
|
}
|
|
|
|
/**
|
|
* Helper methods for the type-specific WillChangeXXX methods.
|
|
*
|
|
* This method sends out appropriate pre-change notifications so that selector
|
|
* restyles (e.g. due to changes that cause |elem[attr="val"]| to start/stop
|
|
* matching) work, and it returns an nsAttrValue that _may_ contain the
|
|
* attribute's pre-change value.
|
|
*
|
|
* The nsAttrValue returned by this method depends on whether there are
|
|
* mutation event listeners listening for changes to this element's attributes.
|
|
* If not, then the object returned is empty. If there are, then the
|
|
* nsAttrValue returned contains a serialized copy of the attribute's value
|
|
* prior to the change, and this object should be passed to the corresponding
|
|
* DidChangeXXX method call (assuming a WillChangeXXX call is required for the
|
|
* SVG type - see comment below). This is necessary so that the 'prevValue'
|
|
* property of the mutation event that is dispatched will correctly contain the
|
|
* old value.
|
|
*
|
|
* The reason we need to serialize the old value if there are mutation
|
|
* event listeners is because the underlying nsAttrValue for the attribute
|
|
* points directly to a parsed representation of the attribute (e.g. an
|
|
* SVGAnimatedLengthList*) that is a member of the SVG element. That object
|
|
* will have changed by the time DidChangeXXX has been called, so without the
|
|
* serialization of the old attribute value that we provide, DidChangeXXX
|
|
* would have no way to get the old value to pass to SetAttrAndNotify.
|
|
*
|
|
* We only return the old value when there are mutation event listeners because
|
|
* it's not needed otherwise, and because it's expensive to serialize the old
|
|
* value. This is especially true for list type attributes, which may be built
|
|
* up via the SVG DOM resulting in a large number of Will/DidModifyXXX calls
|
|
* before the script finally finishes setting the attribute.
|
|
*
|
|
* Note that unlike using SetParsedAttr, using Will/DidChangeXXX does NOT check
|
|
* and filter out redundant changes. Before calling WillChangeXXX, the caller
|
|
* should check whether the new and old values are actually the same, and skip
|
|
* calling Will/DidChangeXXX if they are.
|
|
*
|
|
* Also note that not all SVG types use this scheme. For types that can be
|
|
* represented by an nsAttrValue without pointing back to an SVG object (e.g.
|
|
* enums, booleans, integers) we can simply use SetParsedAttr which will do all
|
|
* of the above for us. For such types there is no matching WillChangeXXX
|
|
* method, only DidChangeXXX which calls SetParsedAttr.
|
|
*/
|
|
nsAttrValue SVGElement::WillChangeValue(
|
|
nsAtom* aName, const mozAutoDocUpdate& aProofOfUpdate) {
|
|
// We need an empty attr value:
|
|
// a) to pass to BeforeSetAttr when GetParsedAttr returns nullptr
|
|
// b) to store the old value in the case we have mutation listeners
|
|
//
|
|
// We can use the same value for both purposes, because if GetParsedAttr
|
|
// returns non-null its return value is what will get passed to BeforeSetAttr,
|
|
// not matter what our mutation listener situation is.
|
|
//
|
|
// Also, we should be careful to always return this value to benefit from
|
|
// return value optimization.
|
|
nsAttrValue emptyOrOldAttrValue;
|
|
const nsAttrValue* attrValue = GetParsedAttr(aName);
|
|
|
|
// We only need to set the old value if we have listeners since otherwise it
|
|
// isn't used.
|
|
if (attrValue && nsContentUtils::HasMutationListeners(
|
|
this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this)) {
|
|
emptyOrOldAttrValue.SetToSerialized(*attrValue);
|
|
}
|
|
|
|
uint8_t modType =
|
|
attrValue ? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION)
|
|
: static_cast<uint8_t>(MutationEvent_Binding::ADDITION);
|
|
MutationObservers::NotifyAttributeWillChange(this, kNameSpaceID_None, aName,
|
|
modType);
|
|
|
|
// This is not strictly correct--the attribute value parameter for
|
|
// BeforeSetAttr should reflect the value that *will* be set but that implies
|
|
// allocating, e.g. an extra SVGAnimatedLength, and isn't necessary at the
|
|
// moment since no SVG elements overload BeforeSetAttr. For now we just pass
|
|
// the current value.
|
|
nsAttrValueOrString attrStringOrValue(attrValue ? *attrValue
|
|
: emptyOrOldAttrValue);
|
|
DebugOnly<nsresult> rv = BeforeSetAttr(
|
|
kNameSpaceID_None, aName, &attrStringOrValue, kNotifyDocumentObservers);
|
|
// SVG elements aren't expected to overload BeforeSetAttr in such a way that
|
|
// it may fail. So long as this is the case we don't need to check and pass on
|
|
// the return value which simplifies the calling code significantly.
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv), "Unexpected failure from BeforeSetAttr");
|
|
|
|
return emptyOrOldAttrValue;
|
|
}
|
|
|
|
/**
|
|
* Helper methods for the type-specific DidChangeXXX methods.
|
|
*
|
|
* aEmptyOrOldValue will normally be the object returned from the corresponding
|
|
* WillChangeXXX call. This is because:
|
|
* a) WillChangeXXX will ensure the object is set when we have mutation
|
|
* listeners, and
|
|
* b) WillChangeXXX will ensure the object represents a serialized version of
|
|
* the old attribute value so that the value doesn't change when the
|
|
* underlying SVG type is updated.
|
|
*
|
|
* aNewValue is replaced with the old value.
|
|
*/
|
|
void SVGElement::DidChangeValue(nsAtom* aName,
|
|
const nsAttrValue& aEmptyOrOldValue,
|
|
nsAttrValue& aNewValue,
|
|
const mozAutoDocUpdate& aProofOfUpdate) {
|
|
bool hasListeners = nsContentUtils::HasMutationListeners(
|
|
this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this);
|
|
uint8_t modType =
|
|
HasAttr(kNameSpaceID_None, aName)
|
|
? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION)
|
|
: static_cast<uint8_t>(MutationEvent_Binding::ADDITION);
|
|
|
|
// XXX Really, the fourth argument to SetAttrAndNotify should be null if
|
|
// aEmptyOrOldValue does not represent the actual previous value of the
|
|
// attribute, but currently SVG elements do not even use the old attribute
|
|
// value in |AfterSetAttr|, so this should be ok.
|
|
SetAttrAndNotify(kNameSpaceID_None, aName, nullptr, &aEmptyOrOldValue,
|
|
aNewValue, nullptr, modType, hasListeners,
|
|
kNotifyDocumentObservers, kCallAfterSetAttr,
|
|
GetComposedDoc(), aProofOfUpdate);
|
|
}
|
|
|
|
void SVGElement::MaybeSerializeAttrBeforeRemoval(nsAtom* aName, bool aNotify) {
|
|
if (!aNotify || !nsContentUtils::HasMutationListeners(
|
|
this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this)) {
|
|
return;
|
|
}
|
|
|
|
const nsAttrValue* attrValue = mAttrs.GetAttr(aName);
|
|
if (!attrValue) return;
|
|
|
|
nsAutoString serializedValue;
|
|
attrValue->ToString(serializedValue);
|
|
nsAttrValue oldAttrValue(serializedValue);
|
|
bool oldValueSet;
|
|
mAttrs.SetAndSwapAttr(aName, oldAttrValue, &oldValueSet);
|
|
}
|
|
|
|
nsAtom* SVGElement::GetEventNameForAttr(nsAtom* aAttr) {
|
|
if (IsSVGElement(nsGkAtoms::svg)) {
|
|
if (aAttr == nsGkAtoms::onload) return nsGkAtoms::onSVGLoad;
|
|
if (aAttr == nsGkAtoms::onscroll) return nsGkAtoms::onSVGScroll;
|
|
}
|
|
if (aAttr == nsGkAtoms::onbegin) return nsGkAtoms::onbeginEvent;
|
|
if (aAttr == nsGkAtoms::onrepeat) return nsGkAtoms::onrepeatEvent;
|
|
if (aAttr == nsGkAtoms::onend) return nsGkAtoms::onendEvent;
|
|
|
|
return SVGElementBase::GetEventNameForAttr(aAttr);
|
|
}
|
|
|
|
SVGViewportElement* SVGElement::GetCtx() const {
|
|
return SVGContentUtils::GetNearestViewportElement(this);
|
|
}
|
|
|
|
/* virtual */
|
|
gfxMatrix SVGElement::PrependLocalTransformsTo(const gfxMatrix& aMatrix,
|
|
SVGTransformTypes aWhich) const {
|
|
return aMatrix;
|
|
}
|
|
|
|
SVGElement::LengthAttributesInfo SVGElement::GetLengthInfo() {
|
|
return LengthAttributesInfo(nullptr, nullptr, 0);
|
|
}
|
|
|
|
void SVGElement::SetLength(nsAtom* aName, const SVGAnimatedLength& aLength) {
|
|
LengthAttributesInfo lengthInfo = GetLengthInfo();
|
|
|
|
for (uint32_t i = 0; i < lengthInfo.mCount; i++) {
|
|
if (aName == lengthInfo.mInfos[i].mName) {
|
|
lengthInfo.mValues[i] = aLength;
|
|
DidAnimateLength(i);
|
|
return;
|
|
}
|
|
}
|
|
MOZ_ASSERT(false, "no length found to set");
|
|
}
|
|
|
|
nsAttrValue SVGElement::WillChangeLength(
|
|
uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
|
|
return WillChangeValue(GetLengthInfo().mInfos[aAttrEnum].mName,
|
|
aProofOfUpdate);
|
|
}
|
|
|
|
void SVGElement::DidChangeLength(uint8_t aAttrEnum,
|
|
const nsAttrValue& aEmptyOrOldValue,
|
|
const mozAutoDocUpdate& aProofOfUpdate) {
|
|
LengthAttributesInfo info = GetLengthInfo();
|
|
|
|
NS_ASSERTION(info.mCount > 0,
|
|
"DidChangeLength on element with no length attribs");
|
|
NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
|
|
|
|
nsAttrValue newValue;
|
|
newValue.SetTo(info.mValues[aAttrEnum], nullptr);
|
|
|
|
DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
|
|
aProofOfUpdate);
|
|
}
|
|
|
|
void SVGElement::DidAnimateLength(uint8_t aAttrEnum) {
|
|
// We need to do this here. Normally the SMIL restyle would also cause us to
|
|
// do this from DidSetComputedStyle, but we don't have that guarantee if our
|
|
// frame gets reconstructed.
|
|
ClearAnyCachedPath();
|
|
|
|
if (SVGGeometryProperty::ElementMapsLengthsToStyle(this)) {
|
|
nsCSSPropertyID propId =
|
|
SVGGeometryProperty::AttrEnumToCSSPropId(this, aAttrEnum);
|
|
|
|
// We don't map use element width/height currently. We can remove this
|
|
// test when we do.
|
|
if (propId != eCSSProperty_UNKNOWN) {
|
|
SMILOverrideStyle()->SetSMILValue(propId,
|
|
GetLengthInfo().mValues[aAttrEnum]);
|
|
return;
|
|
}
|
|
}
|
|
|
|
nsIFrame* frame = GetPrimaryFrame();
|
|
|
|
if (frame) {
|
|
LengthAttributesInfo info = GetLengthInfo();
|
|
frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
|
|
MutationEvent_Binding::SMIL);
|
|
}
|
|
}
|
|
|
|
SVGAnimatedLength* SVGElement::GetAnimatedLength(uint8_t aAttrEnum) {
|
|
LengthAttributesInfo info = GetLengthInfo();
|
|
if (aAttrEnum < info.mCount) {
|
|
return &info.mValues[aAttrEnum];
|
|
}
|
|
MOZ_ASSERT_UNREACHABLE("Bad attrEnum");
|
|
return nullptr;
|
|
}
|
|
|
|
SVGAnimatedLength* SVGElement::GetAnimatedLength(const nsAtom* aAttrName) {
|
|
LengthAttributesInfo lengthInfo = GetLengthInfo();
|
|
|
|
for (uint32_t i = 0; i < lengthInfo.mCount; i++) {
|
|
if (aAttrName == lengthInfo.mInfos[i].mName) {
|
|
return &lengthInfo.mValues[i];
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void SVGElement::GetAnimatedLengthValues(float* aFirst, ...) {
|
|
LengthAttributesInfo info = GetLengthInfo();
|
|
|
|
NS_ASSERTION(info.mCount > 0,
|
|
"GetAnimatedLengthValues on element with no length attribs");
|
|
|
|
SVGViewportElement* ctx = nullptr;
|
|
|
|
float* f = aFirst;
|
|
uint32_t i = 0;
|
|
|
|
va_list args;
|
|
va_start(args, aFirst);
|
|
|
|
while (f && i < info.mCount) {
|
|
uint8_t type = info.mValues[i].GetSpecifiedUnitType();
|
|
if (!ctx) {
|
|
if (type != SVGLength_Binding::SVG_LENGTHTYPE_NUMBER &&
|
|
type != SVGLength_Binding::SVG_LENGTHTYPE_PX)
|
|
ctx = GetCtx();
|
|
}
|
|
if (type == SVGLength_Binding::SVG_LENGTHTYPE_EMS ||
|
|
type == SVGLength_Binding::SVG_LENGTHTYPE_EXS)
|
|
*f = info.mValues[i++].GetAnimValue(this);
|
|
else
|
|
*f = info.mValues[i++].GetAnimValue(ctx);
|
|
f = va_arg(args, float*);
|
|
}
|
|
|
|
va_end(args);
|
|
}
|
|
|
|
SVGElement::LengthListAttributesInfo SVGElement::GetLengthListInfo() {
|
|
return LengthListAttributesInfo(nullptr, nullptr, 0);
|
|
}
|
|
|
|
nsAttrValue SVGElement::WillChangeLengthList(
|
|
uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
|
|
return WillChangeValue(GetLengthListInfo().mInfos[aAttrEnum].mName,
|
|
aProofOfUpdate);
|
|
}
|
|
|
|
void SVGElement::DidChangeLengthList(uint8_t aAttrEnum,
|
|
const nsAttrValue& aEmptyOrOldValue,
|
|
const mozAutoDocUpdate& aProofOfUpdate) {
|
|
LengthListAttributesInfo info = GetLengthListInfo();
|
|
|
|
NS_ASSERTION(info.mCount > 0,
|
|
"DidChangeLengthList on element with no length list attribs");
|
|
NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
|
|
|
|
nsAttrValue newValue;
|
|
newValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr);
|
|
|
|
DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
|
|
aProofOfUpdate);
|
|
}
|
|
|
|
void SVGElement::DidAnimateLengthList(uint8_t aAttrEnum) {
|
|
nsIFrame* frame = GetPrimaryFrame();
|
|
|
|
if (frame) {
|
|
LengthListAttributesInfo info = GetLengthListInfo();
|
|
frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
|
|
MutationEvent_Binding::SMIL);
|
|
}
|
|
}
|
|
|
|
void SVGElement::GetAnimatedLengthListValues(SVGUserUnitList* aFirst, ...) {
|
|
LengthListAttributesInfo info = GetLengthListInfo();
|
|
|
|
NS_ASSERTION(
|
|
info.mCount > 0,
|
|
"GetAnimatedLengthListValues on element with no length list attribs");
|
|
|
|
SVGUserUnitList* list = aFirst;
|
|
uint32_t i = 0;
|
|
|
|
va_list args;
|
|
va_start(args, aFirst);
|
|
|
|
while (list && i < info.mCount) {
|
|
list->Init(&(info.mValues[i].GetAnimValue()), this, info.mInfos[i].mAxis);
|
|
++i;
|
|
list = va_arg(args, SVGUserUnitList*);
|
|
}
|
|
|
|
va_end(args);
|
|
}
|
|
|
|
SVGAnimatedLengthList* SVGElement::GetAnimatedLengthList(uint8_t aAttrEnum) {
|
|
LengthListAttributesInfo info = GetLengthListInfo();
|
|
if (aAttrEnum < info.mCount) {
|
|
return &(info.mValues[aAttrEnum]);
|
|
}
|
|
MOZ_ASSERT_UNREACHABLE("Bad attrEnum");
|
|
return nullptr;
|
|
}
|
|
|
|
SVGElement::NumberListAttributesInfo SVGElement::GetNumberListInfo() {
|
|
return NumberListAttributesInfo(nullptr, nullptr, 0);
|
|
}
|
|
|
|
nsAttrValue SVGElement::WillChangeNumberList(
|
|
uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
|
|
return WillChangeValue(GetNumberListInfo().mInfos[aAttrEnum].mName,
|
|
aProofOfUpdate);
|
|
}
|
|
|
|
void SVGElement::DidChangeNumberList(uint8_t aAttrEnum,
|
|
const nsAttrValue& aEmptyOrOldValue,
|
|
const mozAutoDocUpdate& aProofOfUpdate) {
|
|
NumberListAttributesInfo info = GetNumberListInfo();
|
|
|
|
MOZ_ASSERT(info.mCount > 0,
|
|
"DidChangeNumberList on element with no number list attribs");
|
|
MOZ_ASSERT(aAttrEnum < info.mCount, "aAttrEnum out of range");
|
|
|
|
nsAttrValue newValue;
|
|
newValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr);
|
|
|
|
DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
|
|
aProofOfUpdate);
|
|
}
|
|
|
|
void SVGElement::DidAnimateNumberList(uint8_t aAttrEnum) {
|
|
nsIFrame* frame = GetPrimaryFrame();
|
|
|
|
if (frame) {
|
|
NumberListAttributesInfo info = GetNumberListInfo();
|
|
MOZ_ASSERT(aAttrEnum < info.mCount, "aAttrEnum out of range");
|
|
|
|
frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
|
|
MutationEvent_Binding::SMIL);
|
|
}
|
|
}
|
|
|
|
SVGAnimatedNumberList* SVGElement::GetAnimatedNumberList(uint8_t aAttrEnum) {
|
|
NumberListAttributesInfo info = GetNumberListInfo();
|
|
if (aAttrEnum < info.mCount) {
|
|
return &(info.mValues[aAttrEnum]);
|
|
}
|
|
MOZ_ASSERT(false, "Bad attrEnum");
|
|
return nullptr;
|
|
}
|
|
|
|
SVGAnimatedNumberList* SVGElement::GetAnimatedNumberList(nsAtom* aAttrName) {
|
|
NumberListAttributesInfo info = GetNumberListInfo();
|
|
for (uint32_t i = 0; i < info.mCount; i++) {
|
|
if (aAttrName == info.mInfos[i].mName) {
|
|
return &info.mValues[i];
|
|
}
|
|
}
|
|
MOZ_ASSERT(false, "Bad caller");
|
|
return nullptr;
|
|
}
|
|
|
|
nsAttrValue SVGElement::WillChangePointList(
|
|
const mozAutoDocUpdate& aProofOfUpdate) {
|
|
MOZ_ASSERT(GetPointListAttrName(), "Changing non-existent point list?");
|
|
return WillChangeValue(GetPointListAttrName(), aProofOfUpdate);
|
|
}
|
|
|
|
void SVGElement::DidChangePointList(const nsAttrValue& aEmptyOrOldValue,
|
|
const mozAutoDocUpdate& aProofOfUpdate) {
|
|
MOZ_ASSERT(GetPointListAttrName(), "Changing non-existent point list?");
|
|
|
|
nsAttrValue newValue;
|
|
newValue.SetTo(GetAnimatedPointList()->GetBaseValue(), nullptr);
|
|
|
|
DidChangeValue(GetPointListAttrName(), aEmptyOrOldValue, newValue,
|
|
aProofOfUpdate);
|
|
}
|
|
|
|
void SVGElement::DidAnimatePointList() {
|
|
MOZ_ASSERT(GetPointListAttrName(), "Animating non-existent path data?");
|
|
|
|
ClearAnyCachedPath();
|
|
|
|
nsIFrame* frame = GetPrimaryFrame();
|
|
|
|
if (frame) {
|
|
frame->AttributeChanged(kNameSpaceID_None, GetPointListAttrName(),
|
|
MutationEvent_Binding::SMIL);
|
|
}
|
|
}
|
|
|
|
nsAttrValue SVGElement::WillChangePathSegList(
|
|
const mozAutoDocUpdate& aProofOfUpdate) {
|
|
MOZ_ASSERT(GetPathDataAttrName(), "Changing non-existent path seg list?");
|
|
return WillChangeValue(GetPathDataAttrName(), aProofOfUpdate);
|
|
}
|
|
|
|
void SVGElement::DidChangePathSegList(const nsAttrValue& aEmptyOrOldValue,
|
|
const mozAutoDocUpdate& aProofOfUpdate) {
|
|
MOZ_ASSERT(GetPathDataAttrName(), "Changing non-existent path seg list?");
|
|
|
|
nsAttrValue newValue;
|
|
newValue.SetTo(GetAnimPathSegList()->GetBaseValue(), nullptr);
|
|
|
|
DidChangeValue(GetPathDataAttrName(), aEmptyOrOldValue, newValue,
|
|
aProofOfUpdate);
|
|
}
|
|
|
|
void SVGElement::DidAnimatePathSegList() {
|
|
nsStaticAtom* name = GetPathDataAttrName();
|
|
MOZ_ASSERT(name, "Animating non-existent path data?");
|
|
|
|
ClearAnyCachedPath();
|
|
|
|
// Notify style we have to update the d property because of SMIL animation.
|
|
if (StaticPrefs::layout_css_d_property_enabled() && name == nsGkAtoms::d) {
|
|
SMILOverrideStyle()->SetSMILValue(nsCSSPropertyID::eCSSProperty_d,
|
|
*GetAnimPathSegList());
|
|
return;
|
|
}
|
|
|
|
if (nsIFrame* frame = GetPrimaryFrame()) {
|
|
frame->AttributeChanged(kNameSpaceID_None, name,
|
|
MutationEvent_Binding::SMIL);
|
|
}
|
|
}
|
|
|
|
SVGElement::NumberAttributesInfo SVGElement::GetNumberInfo() {
|
|
return NumberAttributesInfo(nullptr, nullptr, 0);
|
|
}
|
|
|
|
void SVGElement::DidChangeNumber(uint8_t aAttrEnum) {
|
|
NumberAttributesInfo info = GetNumberInfo();
|
|
|
|
NS_ASSERTION(info.mCount > 0,
|
|
"DidChangeNumber on element with no number attribs");
|
|
NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
|
|
|
|
nsAttrValue attrValue;
|
|
attrValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr);
|
|
|
|
SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
|
|
attrValue, true);
|
|
}
|
|
|
|
void SVGElement::DidAnimateNumber(uint8_t aAttrEnum) {
|
|
nsIFrame* frame = GetPrimaryFrame();
|
|
|
|
if (frame) {
|
|
NumberAttributesInfo info = GetNumberInfo();
|
|
frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
|
|
MutationEvent_Binding::SMIL);
|
|
}
|
|
}
|
|
|
|
void SVGElement::GetAnimatedNumberValues(float* aFirst, ...) {
|
|
NumberAttributesInfo info = GetNumberInfo();
|
|
|
|
NS_ASSERTION(info.mCount > 0,
|
|
"GetAnimatedNumberValues on element with no number attribs");
|
|
|
|
float* f = aFirst;
|
|
uint32_t i = 0;
|
|
|
|
va_list args;
|
|
va_start(args, aFirst);
|
|
|
|
while (f && i < info.mCount) {
|
|
*f = info.mValues[i++].GetAnimValue();
|
|
f = va_arg(args, float*);
|
|
}
|
|
va_end(args);
|
|
}
|
|
|
|
SVGElement::NumberPairAttributesInfo SVGElement::GetNumberPairInfo() {
|
|
return NumberPairAttributesInfo(nullptr, nullptr, 0);
|
|
}
|
|
|
|
nsAttrValue SVGElement::WillChangeNumberPair(uint8_t aAttrEnum) {
|
|
mozAutoDocUpdate updateBatch(GetComposedDoc(), kDontNotifyDocumentObservers);
|
|
return WillChangeValue(GetNumberPairInfo().mInfos[aAttrEnum].mName,
|
|
updateBatch);
|
|
}
|
|
|
|
void SVGElement::DidChangeNumberPair(uint8_t aAttrEnum,
|
|
const nsAttrValue& aEmptyOrOldValue) {
|
|
NumberPairAttributesInfo info = GetNumberPairInfo();
|
|
|
|
NS_ASSERTION(info.mCount > 0,
|
|
"DidChangePairNumber on element with no number pair attribs");
|
|
NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
|
|
|
|
nsAttrValue newValue;
|
|
newValue.SetTo(info.mValues[aAttrEnum], nullptr);
|
|
|
|
mozAutoDocUpdate updateBatch(GetComposedDoc(), kNotifyDocumentObservers);
|
|
DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
|
|
updateBatch);
|
|
}
|
|
|
|
void SVGElement::DidAnimateNumberPair(uint8_t aAttrEnum) {
|
|
nsIFrame* frame = GetPrimaryFrame();
|
|
|
|
if (frame) {
|
|
NumberPairAttributesInfo info = GetNumberPairInfo();
|
|
frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
|
|
MutationEvent_Binding::SMIL);
|
|
}
|
|
}
|
|
|
|
SVGElement::IntegerAttributesInfo SVGElement::GetIntegerInfo() {
|
|
return IntegerAttributesInfo(nullptr, nullptr, 0);
|
|
}
|
|
|
|
void SVGElement::DidChangeInteger(uint8_t aAttrEnum) {
|
|
IntegerAttributesInfo info = GetIntegerInfo();
|
|
NS_ASSERTION(info.mCount > 0,
|
|
"DidChangeInteger on element with no integer attribs");
|
|
NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
|
|
|
|
nsAttrValue attrValue;
|
|
attrValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr);
|
|
|
|
SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
|
|
attrValue, true);
|
|
}
|
|
|
|
void SVGElement::DidAnimateInteger(uint8_t aAttrEnum) {
|
|
nsIFrame* frame = GetPrimaryFrame();
|
|
|
|
if (frame) {
|
|
IntegerAttributesInfo info = GetIntegerInfo();
|
|
frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
|
|
MutationEvent_Binding::SMIL);
|
|
}
|
|
}
|
|
|
|
void SVGElement::GetAnimatedIntegerValues(int32_t* aFirst, ...) {
|
|
IntegerAttributesInfo info = GetIntegerInfo();
|
|
|
|
NS_ASSERTION(info.mCount > 0,
|
|
"GetAnimatedIntegerValues on element with no integer attribs");
|
|
|
|
int32_t* n = aFirst;
|
|
uint32_t i = 0;
|
|
|
|
va_list args;
|
|
va_start(args, aFirst);
|
|
|
|
while (n && i < info.mCount) {
|
|
*n = info.mValues[i++].GetAnimValue();
|
|
n = va_arg(args, int32_t*);
|
|
}
|
|
va_end(args);
|
|
}
|
|
|
|
SVGElement::IntegerPairAttributesInfo SVGElement::GetIntegerPairInfo() {
|
|
return IntegerPairAttributesInfo(nullptr, nullptr, 0);
|
|
}
|
|
|
|
nsAttrValue SVGElement::WillChangeIntegerPair(
|
|
uint8_t aAttrEnum, const mozAutoDocUpdate& aProofOfUpdate) {
|
|
return WillChangeValue(GetIntegerPairInfo().mInfos[aAttrEnum].mName,
|
|
aProofOfUpdate);
|
|
}
|
|
|
|
void SVGElement::DidChangeIntegerPair(uint8_t aAttrEnum,
|
|
const nsAttrValue& aEmptyOrOldValue,
|
|
const mozAutoDocUpdate& aProofOfUpdate) {
|
|
IntegerPairAttributesInfo info = GetIntegerPairInfo();
|
|
|
|
NS_ASSERTION(info.mCount > 0,
|
|
"DidChangeIntegerPair on element with no integer pair attribs");
|
|
NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
|
|
|
|
nsAttrValue newValue;
|
|
newValue.SetTo(info.mValues[aAttrEnum], nullptr);
|
|
|
|
DidChangeValue(info.mInfos[aAttrEnum].mName, aEmptyOrOldValue, newValue,
|
|
aProofOfUpdate);
|
|
}
|
|
|
|
void SVGElement::DidAnimateIntegerPair(uint8_t aAttrEnum) {
|
|
nsIFrame* frame = GetPrimaryFrame();
|
|
|
|
if (frame) {
|
|
IntegerPairAttributesInfo info = GetIntegerPairInfo();
|
|
frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
|
|
MutationEvent_Binding::SMIL);
|
|
}
|
|
}
|
|
|
|
SVGElement::BooleanAttributesInfo SVGElement::GetBooleanInfo() {
|
|
return BooleanAttributesInfo(nullptr, nullptr, 0);
|
|
}
|
|
|
|
void SVGElement::DidChangeBoolean(uint8_t aAttrEnum) {
|
|
BooleanAttributesInfo info = GetBooleanInfo();
|
|
|
|
NS_ASSERTION(info.mCount > 0,
|
|
"DidChangeBoolean on element with no boolean attribs");
|
|
NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
|
|
|
|
nsAttrValue attrValue(info.mValues[aAttrEnum].GetBaseValueAtom());
|
|
SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
|
|
attrValue, true);
|
|
}
|
|
|
|
void SVGElement::DidAnimateBoolean(uint8_t aAttrEnum) {
|
|
nsIFrame* frame = GetPrimaryFrame();
|
|
|
|
if (frame) {
|
|
BooleanAttributesInfo info = GetBooleanInfo();
|
|
frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
|
|
MutationEvent_Binding::SMIL);
|
|
}
|
|
}
|
|
|
|
SVGElement::EnumAttributesInfo SVGElement::GetEnumInfo() {
|
|
return EnumAttributesInfo(nullptr, nullptr, 0);
|
|
}
|
|
|
|
void SVGElement::DidChangeEnum(uint8_t aAttrEnum) {
|
|
EnumAttributesInfo info = GetEnumInfo();
|
|
|
|
NS_ASSERTION(info.mCount > 0,
|
|
"DidChangeEnum on element with no enum attribs");
|
|
NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
|
|
|
|
nsAttrValue attrValue(info.mValues[aAttrEnum].GetBaseValueAtom(this));
|
|
SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
|
|
attrValue, true);
|
|
}
|
|
|
|
void SVGElement::DidAnimateEnum(uint8_t aAttrEnum) {
|
|
nsIFrame* frame = GetPrimaryFrame();
|
|
|
|
if (frame) {
|
|
EnumAttributesInfo info = GetEnumInfo();
|
|
frame->AttributeChanged(kNameSpaceID_None, info.mInfos[aAttrEnum].mName,
|
|
MutationEvent_Binding::SMIL);
|
|
}
|
|
}
|
|
|
|
SVGAnimatedOrient* SVGElement::GetAnimatedOrient() { return nullptr; }
|
|
|
|
nsAttrValue SVGElement::WillChangeOrient(
|
|
const mozAutoDocUpdate& aProofOfUpdate) {
|
|
return WillChangeValue(nsGkAtoms::orient, aProofOfUpdate);
|
|
}
|
|
|
|
void SVGElement::DidChangeOrient(const nsAttrValue& aEmptyOrOldValue,
|
|
const mozAutoDocUpdate& aProofOfUpdate) {
|
|
SVGAnimatedOrient* orient = GetAnimatedOrient();
|
|
|
|
NS_ASSERTION(orient, "DidChangeOrient on element with no orient attrib");
|
|
|
|
nsAttrValue newValue;
|
|
newValue.SetTo(*orient, nullptr);
|
|
|
|
DidChangeValue(nsGkAtoms::orient, aEmptyOrOldValue, newValue, aProofOfUpdate);
|
|
}
|
|
|
|
void SVGElement::DidAnimateOrient() {
|
|
nsIFrame* frame = GetPrimaryFrame();
|
|
|
|
if (frame) {
|
|
frame->AttributeChanged(kNameSpaceID_None, nsGkAtoms::orient,
|
|
MutationEvent_Binding::SMIL);
|
|
}
|
|
}
|
|
|
|
SVGAnimatedViewBox* SVGElement::GetAnimatedViewBox() { return nullptr; }
|
|
|
|
nsAttrValue SVGElement::WillChangeViewBox(
|
|
const mozAutoDocUpdate& aProofOfUpdate) {
|
|
return WillChangeValue(nsGkAtoms::viewBox, aProofOfUpdate);
|
|
}
|
|
|
|
void SVGElement::DidChangeViewBox(const nsAttrValue& aEmptyOrOldValue,
|
|
const mozAutoDocUpdate& aProofOfUpdate) {
|
|
SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
|
|
|
|
NS_ASSERTION(viewBox, "DidChangeViewBox on element with no viewBox attrib");
|
|
|
|
nsAttrValue newValue;
|
|
newValue.SetTo(*viewBox, nullptr);
|
|
|
|
DidChangeValue(nsGkAtoms::viewBox, aEmptyOrOldValue, newValue,
|
|
aProofOfUpdate);
|
|
}
|
|
|
|
void SVGElement::DidAnimateViewBox() {
|
|
nsIFrame* frame = GetPrimaryFrame();
|
|
|
|
if (frame) {
|
|
frame->AttributeChanged(kNameSpaceID_None, nsGkAtoms::viewBox,
|
|
MutationEvent_Binding::SMIL);
|
|
}
|
|
}
|
|
|
|
SVGAnimatedPreserveAspectRatio* SVGElement::GetAnimatedPreserveAspectRatio() {
|
|
return nullptr;
|
|
}
|
|
|
|
nsAttrValue SVGElement::WillChangePreserveAspectRatio(
|
|
const mozAutoDocUpdate& aProofOfUpdate) {
|
|
return WillChangeValue(nsGkAtoms::preserveAspectRatio, aProofOfUpdate);
|
|
}
|
|
|
|
void SVGElement::DidChangePreserveAspectRatio(
|
|
const nsAttrValue& aEmptyOrOldValue,
|
|
const mozAutoDocUpdate& aProofOfUpdate) {
|
|
SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
|
|
GetAnimatedPreserveAspectRatio();
|
|
|
|
NS_ASSERTION(preserveAspectRatio,
|
|
"DidChangePreserveAspectRatio on element with no "
|
|
"preserveAspectRatio attrib");
|
|
|
|
nsAttrValue newValue;
|
|
newValue.SetTo(*preserveAspectRatio, nullptr);
|
|
|
|
DidChangeValue(nsGkAtoms::preserveAspectRatio, aEmptyOrOldValue, newValue,
|
|
aProofOfUpdate);
|
|
}
|
|
|
|
void SVGElement::DidAnimatePreserveAspectRatio() {
|
|
nsIFrame* frame = GetPrimaryFrame();
|
|
|
|
if (frame) {
|
|
frame->AttributeChanged(kNameSpaceID_None, nsGkAtoms::preserveAspectRatio,
|
|
MutationEvent_Binding::SMIL);
|
|
}
|
|
}
|
|
|
|
nsAttrValue SVGElement::WillChangeTransformList(
|
|
const mozAutoDocUpdate& aProofOfUpdate) {
|
|
return WillChangeValue(GetTransformListAttrName(), aProofOfUpdate);
|
|
}
|
|
|
|
void SVGElement::DidChangeTransformList(
|
|
const nsAttrValue& aEmptyOrOldValue,
|
|
const mozAutoDocUpdate& aProofOfUpdate) {
|
|
MOZ_ASSERT(GetTransformListAttrName(),
|
|
"Changing non-existent transform list?");
|
|
|
|
// The transform attribute is being set, so we must ensure that the
|
|
// SVGAnimatedTransformList is/has been allocated:
|
|
nsAttrValue newValue;
|
|
newValue.SetTo(GetAnimatedTransformList(DO_ALLOCATE)->GetBaseValue(),
|
|
nullptr);
|
|
|
|
DidChangeValue(GetTransformListAttrName(), aEmptyOrOldValue, newValue,
|
|
aProofOfUpdate);
|
|
}
|
|
|
|
void SVGElement::DidAnimateTransformList(int32_t aModType) {
|
|
MOZ_ASSERT(GetTransformListAttrName(),
|
|
"Animating non-existent transform data?");
|
|
|
|
nsIFrame* frame = GetPrimaryFrame();
|
|
|
|
if (frame) {
|
|
nsAtom* transformAttr = GetTransformListAttrName();
|
|
frame->AttributeChanged(kNameSpaceID_None, transformAttr, aModType);
|
|
// When script changes the 'transform' attribute, Element::SetAttrAndNotify
|
|
// will call MutationObservers::NotifyAttributeChanged, under which
|
|
// SVGTransformableElement::GetAttributeChangeHint will be called and an
|
|
// appropriate change event posted to update our frame's overflow rects.
|
|
// The SetAttrAndNotify doesn't happen for transform changes caused by
|
|
// 'animateTransform' though (and sending out the mutation events that
|
|
// MutationObservers::NotifyAttributeChanged dispatches would be
|
|
// inappropriate anyway), so we need to post the change event ourself.
|
|
nsChangeHint changeHint = GetAttributeChangeHint(transformAttr, aModType);
|
|
if (changeHint) {
|
|
nsLayoutUtils::PostRestyleEvent(this, RestyleHint{0}, changeHint);
|
|
}
|
|
}
|
|
}
|
|
|
|
SVGElement::StringAttributesInfo SVGElement::GetStringInfo() {
|
|
return StringAttributesInfo(nullptr, nullptr, 0);
|
|
}
|
|
|
|
void SVGElement::GetStringBaseValue(uint8_t aAttrEnum,
|
|
nsAString& aResult) const {
|
|
SVGElement::StringAttributesInfo info =
|
|
const_cast<SVGElement*>(this)->GetStringInfo();
|
|
|
|
NS_ASSERTION(info.mCount > 0,
|
|
"GetBaseValue on element with no string attribs");
|
|
|
|
NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
|
|
|
|
GetAttr(info.mInfos[aAttrEnum].mNamespaceID, info.mInfos[aAttrEnum].mName,
|
|
aResult);
|
|
}
|
|
|
|
void SVGElement::SetStringBaseValue(uint8_t aAttrEnum,
|
|
const nsAString& aValue) {
|
|
SVGElement::StringAttributesInfo info = GetStringInfo();
|
|
|
|
NS_ASSERTION(info.mCount > 0,
|
|
"SetBaseValue on element with no string attribs");
|
|
|
|
NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
|
|
|
|
SetAttr(info.mInfos[aAttrEnum].mNamespaceID, info.mInfos[aAttrEnum].mName,
|
|
aValue, true);
|
|
}
|
|
|
|
void SVGElement::DidAnimateString(uint8_t aAttrEnum) {
|
|
nsIFrame* frame = GetPrimaryFrame();
|
|
|
|
if (frame) {
|
|
StringAttributesInfo info = GetStringInfo();
|
|
frame->AttributeChanged(info.mInfos[aAttrEnum].mNamespaceID,
|
|
info.mInfos[aAttrEnum].mName,
|
|
MutationEvent_Binding::SMIL);
|
|
}
|
|
}
|
|
|
|
SVGElement::StringListAttributesInfo SVGElement::GetStringListInfo() {
|
|
return StringListAttributesInfo(nullptr, nullptr, 0);
|
|
}
|
|
|
|
nsAttrValue SVGElement::WillChangeStringList(
|
|
bool aIsConditionalProcessingAttribute, uint8_t aAttrEnum,
|
|
const mozAutoDocUpdate& aProofOfUpdate) {
|
|
nsStaticAtom* name;
|
|
if (aIsConditionalProcessingAttribute) {
|
|
nsCOMPtr<SVGTests> tests(do_QueryInterface(this));
|
|
name = tests->GetAttrName(aAttrEnum);
|
|
} else {
|
|
name = GetStringListInfo().mInfos[aAttrEnum].mName;
|
|
}
|
|
return WillChangeValue(name, aProofOfUpdate);
|
|
}
|
|
|
|
void SVGElement::DidChangeStringList(bool aIsConditionalProcessingAttribute,
|
|
uint8_t aAttrEnum,
|
|
const nsAttrValue& aEmptyOrOldValue,
|
|
const mozAutoDocUpdate& aProofOfUpdate) {
|
|
nsStaticAtom* name;
|
|
nsAttrValue newValue;
|
|
nsCOMPtr<SVGTests> tests;
|
|
|
|
if (aIsConditionalProcessingAttribute) {
|
|
tests = do_QueryObject(this);
|
|
name = tests->GetAttrName(aAttrEnum);
|
|
tests->GetAttrValue(aAttrEnum, newValue);
|
|
} else {
|
|
StringListAttributesInfo info = GetStringListInfo();
|
|
|
|
NS_ASSERTION(info.mCount > 0,
|
|
"DidChangeStringList on element with no string list attribs");
|
|
NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range");
|
|
|
|
name = info.mInfos[aAttrEnum].mName;
|
|
newValue.SetTo(info.mValues[aAttrEnum], nullptr);
|
|
}
|
|
|
|
DidChangeValue(name, aEmptyOrOldValue, newValue, aProofOfUpdate);
|
|
|
|
if (aIsConditionalProcessingAttribute) {
|
|
tests->MaybeInvalidate();
|
|
}
|
|
}
|
|
|
|
nsresult SVGElement::ReportAttributeParseFailure(Document* aDocument,
|
|
nsAtom* aAttribute,
|
|
const nsAString& aValue) {
|
|
AutoTArray<nsString, 2> strings;
|
|
strings.AppendElement(nsDependentAtomString(aAttribute));
|
|
strings.AppendElement(aValue);
|
|
return SVGContentUtils::ReportToConsole(aDocument, "AttributeParseWarning",
|
|
strings);
|
|
}
|
|
|
|
void SVGElement::RecompileScriptEventListeners() {
|
|
int32_t i, count = mAttrs.AttrCount();
|
|
for (i = 0; i < count; ++i) {
|
|
const nsAttrName* name = mAttrs.AttrNameAt(i);
|
|
|
|
// Eventlistenener-attributes are always in the null namespace
|
|
if (!name->IsAtom()) {
|
|
continue;
|
|
}
|
|
|
|
nsAtom* attr = name->Atom();
|
|
if (!IsEventAttributeName(attr)) {
|
|
continue;
|
|
}
|
|
|
|
nsAutoString value;
|
|
GetAttr(attr, value);
|
|
SetEventHandler(GetEventNameForAttr(attr), value, true);
|
|
}
|
|
}
|
|
|
|
UniquePtr<SMILAttr> SVGElement::GetAnimatedAttr(int32_t aNamespaceID,
|
|
nsAtom* aName) {
|
|
if (aNamespaceID == kNameSpaceID_None) {
|
|
// Transforms:
|
|
if (GetTransformListAttrName() == aName) {
|
|
// The transform attribute is being animated, so we must ensure that the
|
|
// SVGAnimatedTransformList is/has been allocated:
|
|
return GetAnimatedTransformList(DO_ALLOCATE)->ToSMILAttr(this);
|
|
}
|
|
|
|
// Motion (fake 'attribute' for animateMotion)
|
|
if (aName == nsGkAtoms::mozAnimateMotionDummyAttr) {
|
|
return MakeUnique<SVGMotionSMILAttr>(this);
|
|
}
|
|
|
|
// Lengths:
|
|
LengthAttributesInfo info = GetLengthInfo();
|
|
for (uint32_t i = 0; i < info.mCount; i++) {
|
|
if (aName == info.mInfos[i].mName) {
|
|
return info.mValues[i].ToSMILAttr(this);
|
|
}
|
|
}
|
|
|
|
// Numbers:
|
|
{
|
|
NumberAttributesInfo info = GetNumberInfo();
|
|
for (uint32_t i = 0; i < info.mCount; i++) {
|
|
if (aName == info.mInfos[i].mName) {
|
|
return info.mValues[i].ToSMILAttr(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Number Pairs:
|
|
{
|
|
NumberPairAttributesInfo info = GetNumberPairInfo();
|
|
for (uint32_t i = 0; i < info.mCount; i++) {
|
|
if (aName == info.mInfos[i].mName) {
|
|
return info.mValues[i].ToSMILAttr(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Integers:
|
|
{
|
|
IntegerAttributesInfo info = GetIntegerInfo();
|
|
for (uint32_t i = 0; i < info.mCount; i++) {
|
|
if (aName == info.mInfos[i].mName) {
|
|
return info.mValues[i].ToSMILAttr(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Integer Pairs:
|
|
{
|
|
IntegerPairAttributesInfo info = GetIntegerPairInfo();
|
|
for (uint32_t i = 0; i < info.mCount; i++) {
|
|
if (aName == info.mInfos[i].mName) {
|
|
return info.mValues[i].ToSMILAttr(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Enumerations:
|
|
{
|
|
EnumAttributesInfo info = GetEnumInfo();
|
|
for (uint32_t i = 0; i < info.mCount; i++) {
|
|
if (aName == info.mInfos[i].mName) {
|
|
return info.mValues[i].ToSMILAttr(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Booleans:
|
|
{
|
|
BooleanAttributesInfo info = GetBooleanInfo();
|
|
for (uint32_t i = 0; i < info.mCount; i++) {
|
|
if (aName == info.mInfos[i].mName) {
|
|
return info.mValues[i].ToSMILAttr(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
// orient:
|
|
if (aName == nsGkAtoms::orient) {
|
|
SVGAnimatedOrient* orient = GetAnimatedOrient();
|
|
return orient ? orient->ToSMILAttr(this) : nullptr;
|
|
}
|
|
|
|
// viewBox:
|
|
if (aName == nsGkAtoms::viewBox) {
|
|
SVGAnimatedViewBox* viewBox = GetAnimatedViewBox();
|
|
return viewBox ? viewBox->ToSMILAttr(this) : nullptr;
|
|
}
|
|
|
|
// preserveAspectRatio:
|
|
if (aName == nsGkAtoms::preserveAspectRatio) {
|
|
SVGAnimatedPreserveAspectRatio* preserveAspectRatio =
|
|
GetAnimatedPreserveAspectRatio();
|
|
return preserveAspectRatio ? preserveAspectRatio->ToSMILAttr(this)
|
|
: nullptr;
|
|
}
|
|
|
|
// NumberLists:
|
|
{
|
|
NumberListAttributesInfo info = GetNumberListInfo();
|
|
for (uint32_t i = 0; i < info.mCount; i++) {
|
|
if (aName == info.mInfos[i].mName) {
|
|
MOZ_ASSERT(i <= UCHAR_MAX, "Too many attributes");
|
|
return info.mValues[i].ToSMILAttr(this, uint8_t(i));
|
|
}
|
|
}
|
|
}
|
|
|
|
// LengthLists:
|
|
{
|
|
LengthListAttributesInfo info = GetLengthListInfo();
|
|
for (uint32_t i = 0; i < info.mCount; i++) {
|
|
if (aName == info.mInfos[i].mName) {
|
|
MOZ_ASSERT(i <= UCHAR_MAX, "Too many attributes");
|
|
return info.mValues[i].ToSMILAttr(this, uint8_t(i),
|
|
info.mInfos[i].mAxis,
|
|
info.mInfos[i].mCouldZeroPadList);
|
|
}
|
|
}
|
|
}
|
|
|
|
// PointLists:
|
|
{
|
|
if (GetPointListAttrName() == aName) {
|
|
SVGAnimatedPointList* pointList = GetAnimatedPointList();
|
|
if (pointList) {
|
|
return pointList->ToSMILAttr(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
// PathSegLists:
|
|
{
|
|
if (GetPathDataAttrName() == aName) {
|
|
SVGAnimatedPathSegList* segList = GetAnimPathSegList();
|
|
if (segList) {
|
|
return segList->ToSMILAttr(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (aName == nsGkAtoms::_class) {
|
|
return mClassAttribute.ToSMILAttr(this);
|
|
}
|
|
}
|
|
|
|
// Strings
|
|
{
|
|
StringAttributesInfo info = GetStringInfo();
|
|
for (uint32_t i = 0; i < info.mCount; i++) {
|
|
if (aNamespaceID == info.mInfos[i].mNamespaceID &&
|
|
aName == info.mInfos[i].mName) {
|
|
return info.mValues[i].ToSMILAttr(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void SVGElement::AnimationNeedsResample() {
|
|
Document* doc = GetComposedDoc();
|
|
if (doc && doc->HasAnimationController()) {
|
|
doc->GetAnimationController()->SetResampleNeeded();
|
|
}
|
|
}
|
|
|
|
void SVGElement::FlushAnimations() {
|
|
Document* doc = GetComposedDoc();
|
|
if (doc && doc->HasAnimationController()) {
|
|
doc->GetAnimationController()->FlushResampleRequests();
|
|
}
|
|
}
|
|
|
|
void SVGElement::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
|
|
size_t* aNodeSize) const {
|
|
Element::AddSizeOfExcludingThis(aSizes, aNodeSize);
|
|
|
|
// These are owned by the element and not referenced from the stylesheets.
|
|
// They're referenced from the rule tree, but the rule nodes don't measure
|
|
// their style source (since they're non-owning), so unconditionally reporting
|
|
// them even though it's a refcounted object is ok.
|
|
if (mContentDeclarationBlock) {
|
|
aSizes.mLayoutSvgMappedDeclarations +=
|
|
mContentDeclarationBlock->SizeofIncludingThis(
|
|
aSizes.mState.mMallocSizeOf);
|
|
}
|
|
}
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|