/* -*- 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/ArrayUtils.h" #include "mozilla/DebugOnly.h" #include "mozilla/Unused.h" #include "nsSVGElement.h" #include "mozilla/dom/SVGSVGElement.h" #include "mozilla/dom/SVGTests.h" #include "nsContentUtils.h" #include "nsICSSDeclaration.h" #include "nsIDocument.h" #include "nsIDOMMutationEvent.h" #include "nsSVGPathGeometryElement.h" #include "mozilla/InternalMutationEvent.h" #include "nsError.h" #include "nsIPresShell.h" #include "nsGkAtoms.h" #include "mozilla/css/StyleRule.h" #include "nsRuleWalker.h" #include "mozilla/css/Declaration.h" #include "nsCSSProps.h" #include "nsCSSParser.h" #include "mozilla/EventListenerManager.h" #include "nsLayoutUtils.h" #include "nsSVGAnimatedTransformList.h" #include "nsSVGLength2.h" #include "nsSVGNumber2.h" #include "nsSVGNumberPair.h" #include "nsSVGInteger.h" #include "nsSVGIntegerPair.h" #include "nsSVGAngle.h" #include "nsSVGBoolean.h" #include "nsSVGEnum.h" #include "nsSVGViewBox.h" #include "nsSVGString.h" #include "mozilla/dom/SVGAnimatedEnumeration.h" #include "SVGAnimatedNumberList.h" #include "SVGAnimatedLengthList.h" #include "SVGAnimatedPointList.h" #include "SVGAnimatedPathSegList.h" #include "SVGContentUtils.h" #include "nsIFrame.h" #include "nsQueryObject.h" #include <stdarg.h> #include "nsSMILMappedAttribute.h" #include "SVGMotionSMILAttr.h" #include "nsAttrValueOrString.h" #include "nsSMILAnimationController.h" #include "mozilla/dom/SVGElementBinding.h" #include "mozilla/Unused.h" #include "mozilla/RestyleManagerHandle.h" #include "mozilla/RestyleManagerHandleInlines.h" using namespace mozilla; using namespace mozilla::dom; // This is needed to ensure correct handling of calls to the // vararg-list methods in this file: // nsSVGElement::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(Element **aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) { RefPtr<nsSVGElement> it = new nsSVGElement(aNodeInfo); nsresult rv = it->Init(); if (NS_FAILED(rv)) { return rv; } it.forget(aResult); return rv; } NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGElement) nsSVGEnumMapping nsSVGElement::sSVGUnitTypesMap[] = { {&nsGkAtoms::userSpaceOnUse, SVG_UNIT_TYPE_USERSPACEONUSE}, {&nsGkAtoms::objectBoundingBox, SVG_UNIT_TYPE_OBJECTBOUNDINGBOX}, {nullptr, 0} }; nsSVGElement::nsSVGElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) : nsSVGElementBase(aNodeInfo) { } JSObject* nsSVGElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) { return SVGElementBinding::Wrap(aCx, this, aGivenProto); } //---------------------------------------------------------------------- NS_IMETHODIMP nsSVGElement::GetSVGClassName(nsISupports** aClassName) { *aClassName = ClassName().take(); return NS_OK; } NS_IMETHODIMP nsSVGElement::GetStyle(nsIDOMCSSStyleDeclaration** aStyle) { NS_ADDREF(*aStyle = Style()); return NS_OK; } //---------------------------------------------------------------------- // nsSVGElement methods void nsSVGElement::DidAnimateClass() { nsAutoString src; mClassAttribute.GetAnimValue(src, this); if (!mClassAnimAttr) { mClassAnimAttr = new nsAttrValue(); } mClassAnimAttr->ParseAtomArray(src); nsIPresShell* shell = OwnerDoc()->GetShell(); if (shell) { shell->RestyleForAnimation(this, eRestyle_Self); } } nsresult nsSVGElement::Init() { // Set up length attributes - can't do this in the constructor // because we can't do a virtual call at that point LengthAttributesInfo lengthInfo = GetLengthInfo(); uint32_t i; for (i = 0; i < lengthInfo.mLengthCount; i++) { lengthInfo.Reset(i); } NumberAttributesInfo numberInfo = GetNumberInfo(); for (i = 0; i < numberInfo.mNumberCount; i++) { numberInfo.Reset(i); } NumberPairAttributesInfo numberPairInfo = GetNumberPairInfo(); for (i = 0; i < numberPairInfo.mNumberPairCount; i++) { numberPairInfo.Reset(i); } IntegerAttributesInfo integerInfo = GetIntegerInfo(); for (i = 0; i < integerInfo.mIntegerCount; i++) { integerInfo.Reset(i); } IntegerPairAttributesInfo integerPairInfo = GetIntegerPairInfo(); for (i = 0; i < integerPairInfo.mIntegerPairCount; i++) { integerPairInfo.Reset(i); } AngleAttributesInfo angleInfo = GetAngleInfo(); for (i = 0; i < angleInfo.mAngleCount; i++) { angleInfo.Reset(i); } BooleanAttributesInfo booleanInfo = GetBooleanInfo(); for (i = 0; i < booleanInfo.mBooleanCount; i++) { booleanInfo.Reset(i); } EnumAttributesInfo enumInfo = GetEnumInfo(); for (i = 0; i < enumInfo.mEnumCount; i++) { enumInfo.Reset(i); } nsSVGViewBox *viewBox = GetViewBox(); if (viewBox) { viewBox->Init(); } SVGAnimatedPreserveAspectRatio *preserveAspectRatio = GetPreserveAspectRatio(); if (preserveAspectRatio) { preserveAspectRatio->Init(); } LengthListAttributesInfo lengthListInfo = GetLengthListInfo(); for (i = 0; i < lengthListInfo.mLengthListCount; i++) { lengthListInfo.Reset(i); } NumberListAttributesInfo numberListInfo = GetNumberListInfo(); for (i = 0; i < numberListInfo.mNumberListCount; i++) { numberListInfo.Reset(i); } // 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). StringAttributesInfo stringInfo = GetStringInfo(); for (i = 0; i < stringInfo.mStringCount; i++) { stringInfo.Reset(i); } return NS_OK; } //---------------------------------------------------------------------- // nsISupports methods NS_IMPL_ISUPPORTS_INHERITED(nsSVGElement, nsSVGElementBase, nsIDOMNode, nsIDOMElement, nsIDOMSVGElement) //---------------------------------------------------------------------- // Implementation //---------------------------------------------------------------------- // nsIContent methods nsresult nsSVGElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsIContent* aBindingParent, bool aCompileEventHandlers) { nsresult rv = nsSVGElementBase::BindToTree(aDocument, aParent, aBindingParent, aCompileEventHandlers); NS_ENSURE_SUCCESS(rv, rv); if (!MayHaveStyle()) { return NS_OK; } const nsAttrValue* oldVal = mAttrsAndChildren.GetAttr(nsGkAtoms::style); if (oldVal && oldVal->Type() == nsAttrValue::eCSSDeclaration) { // we need to force a reparse because the baseURI of the document // may have changed, and in particular because we may be clones of // XBL anonymous content now being bound to the document we should // render in and due to the hacky way in which we implement the // interaction of XBL and SVG resources. Once we have a sane // ownerDocument on XBL anonymous content, this can all go away. nsAttrValue attrValue; nsAutoString stringValue; oldVal->ToString(stringValue); // Force in data doc, since we already have a style rule ParseStyleAttribute(stringValue, attrValue, true); // Don't bother going through SetInlineStyleDeclaration; we don't // want to fire off mutation events or document notifications anyway rv = mAttrsAndChildren.SetAndSwapAttr(nsGkAtoms::style, attrValue); NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; } nsresult nsSVGElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, const nsAttrValue* aValue, 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 // mAttrsAndChildren->mMappedAttributes, meaning it's shared between // elements), the pointer will dangle. See bug 724680. MOZ_ASSERT(!mAttrsAndChildren.HasMappedAttrs(), "Unexpected use of nsMappedAttributes within SVG"); // If this is an svg presentation attribute we need to map it into // the content stylerule. // XXX For some reason incremental mapping doesn't work, so for now // just delete the style rule and lazily reconstruct it in // GetContentStyleRule() if (aNamespaceID == kNameSpaceID_None && IsAttributeMapped(aName)) { mContentStyleRule = nullptr; } if (IsEventAttributeName(aName) && aValue) { MOZ_ASSERT(aValue->Type() == nsAttrValue::eString, "Expected string value for script body"); nsresult rv = SetEventHandler(GetEventNameForAttr(aName), aValue->GetStringValue()); NS_ENSURE_SUCCESS(rv, rv); } return nsSVGElementBase::AfterSetAttr(aNamespaceID, aName, aValue, aNotify); } bool nsSVGElement::ParseAttribute(int32_t aNamespaceID, nsIAtom* aAttribute, const nsAString& aValue, nsAttrValue& aResult) { nsresult rv = NS_OK; bool foundMatch = false; bool didSetResult = false; if (aNamespaceID == kNameSpaceID_None) { // Check for nsSVGLength2 attribute LengthAttributesInfo lengthInfo = GetLengthInfo(); uint32_t i; for (i = 0; i < lengthInfo.mLengthCount; i++) { if (aAttribute == *lengthInfo.mLengthInfo[i].mName) { rv = lengthInfo.mLengths[i].SetBaseValueString(aValue, this, false); if (NS_FAILED(rv)) { lengthInfo.Reset(i); } else { aResult.SetTo(lengthInfo.mLengths[i], &aValue); didSetResult = true; } foundMatch = true; break; } } if (!foundMatch) { // Check for SVGAnimatedLengthList attribute LengthListAttributesInfo lengthListInfo = GetLengthListInfo(); for (i = 0; i < lengthListInfo.mLengthListCount; i++) { if (aAttribute == *lengthListInfo.mLengthListInfo[i].mName) { rv = lengthListInfo.mLengthLists[i].SetBaseValueString(aValue); if (NS_FAILED(rv)) { lengthListInfo.Reset(i); } else { aResult.SetTo(lengthListInfo.mLengthLists[i].GetBaseValue(), &aValue); didSetResult = true; } foundMatch = true; break; } } } if (!foundMatch) { // Check for SVGAnimatedNumberList attribute NumberListAttributesInfo numberListInfo = GetNumberListInfo(); for (i = 0; i < numberListInfo.mNumberListCount; i++) { if (aAttribute == *numberListInfo.mNumberListInfo[i].mName) { rv = numberListInfo.mNumberLists[i].SetBaseValueString(aValue); if (NS_FAILED(rv)) { numberListInfo.Reset(i); } else { aResult.SetTo(numberListInfo.mNumberLists[i].GetBaseValue(), &aValue); didSetResult = true; } foundMatch = true; break; } } } if (!foundMatch) { // Check for SVGAnimatedPointList attribute if (GetPointListAttrName() == aAttribute) { SVGAnimatedPointList* pointList = GetAnimatedPointList(); if (pointList) { 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) { SVGAnimatedPathSegList* segList = GetAnimPathSegList(); if (segList) { 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 nsSVGNumber2 attribute NumberAttributesInfo numberInfo = GetNumberInfo(); for (i = 0; i < numberInfo.mNumberCount; i++) { if (aAttribute == *numberInfo.mNumberInfo[i].mName) { rv = numberInfo.mNumbers[i].SetBaseValueString(aValue, this); if (NS_FAILED(rv)) { numberInfo.Reset(i); } else { aResult.SetTo(numberInfo.mNumbers[i].GetBaseValue(), &aValue); didSetResult = true; } foundMatch = true; break; } } } if (!foundMatch) { // Check for nsSVGNumberPair attribute NumberPairAttributesInfo numberPairInfo = GetNumberPairInfo(); for (i = 0; i < numberPairInfo.mNumberPairCount; i++) { if (aAttribute == *numberPairInfo.mNumberPairInfo[i].mName) { rv = numberPairInfo.mNumberPairs[i].SetBaseValueString(aValue, this); if (NS_FAILED(rv)) { numberPairInfo.Reset(i); } else { aResult.SetTo(numberPairInfo.mNumberPairs[i], &aValue); didSetResult = true; } foundMatch = true; break; } } } if (!foundMatch) { // Check for nsSVGInteger attribute IntegerAttributesInfo integerInfo = GetIntegerInfo(); for (i = 0; i < integerInfo.mIntegerCount; i++) { if (aAttribute == *integerInfo.mIntegerInfo[i].mName) { rv = integerInfo.mIntegers[i].SetBaseValueString(aValue, this); if (NS_FAILED(rv)) { integerInfo.Reset(i); } else { aResult.SetTo(integerInfo.mIntegers[i].GetBaseValue(), &aValue); didSetResult = true; } foundMatch = true; break; } } } if (!foundMatch) { // Check for nsSVGIntegerPair attribute IntegerPairAttributesInfo integerPairInfo = GetIntegerPairInfo(); for (i = 0; i < integerPairInfo.mIntegerPairCount; i++) { if (aAttribute == *integerPairInfo.mIntegerPairInfo[i].mName) { rv = integerPairInfo.mIntegerPairs[i].SetBaseValueString(aValue, this); if (NS_FAILED(rv)) { integerPairInfo.Reset(i); } else { aResult.SetTo(integerPairInfo.mIntegerPairs[i], &aValue); didSetResult = true; } foundMatch = true; break; } } } if (!foundMatch) { // Check for nsSVGAngle attribute AngleAttributesInfo angleInfo = GetAngleInfo(); for (i = 0; i < angleInfo.mAngleCount; i++) { if (aAttribute == *angleInfo.mAngleInfo[i].mName) { rv = angleInfo.mAngles[i].SetBaseValueString(aValue, this, false); if (NS_FAILED(rv)) { angleInfo.Reset(i); } else { aResult.SetTo(angleInfo.mAngles[i], &aValue); didSetResult = true; } foundMatch = true; break; } } } if (!foundMatch) { // Check for nsSVGBoolean attribute BooleanAttributesInfo booleanInfo = GetBooleanInfo(); for (i = 0; i < booleanInfo.mBooleanCount; i++) { if (aAttribute == *booleanInfo.mBooleanInfo[i].mName) { nsIAtom *valAtom = NS_GetStaticAtom(aValue); rv = valAtom ? booleanInfo.mBooleans[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 nsSVGEnum attribute EnumAttributesInfo enumInfo = GetEnumInfo(); for (i = 0; i < enumInfo.mEnumCount; i++) { if (aAttribute == *enumInfo.mEnumInfo[i].mName) { nsCOMPtr<nsIAtom> valAtom = NS_Atomize(aValue); rv = enumInfo.mEnums[i].SetBaseValueAtom(valAtom, this); if (NS_FAILED(rv)) { 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.mStringListCount; i++) { if (aAttribute == *stringListInfo.mStringListInfo[i].mName) { rv = stringListInfo.mStringLists[i].SetValue(aValue); if (NS_FAILED(rv)) { stringListInfo.Reset(i); } else { aResult.SetTo(stringListInfo.mStringLists[i], &aValue); didSetResult = true; } foundMatch = true; break; } } } if (!foundMatch) { // Check for nsSVGViewBox attribute if (aAttribute == nsGkAtoms::viewBox) { nsSVGViewBox* viewBox = GetViewBox(); 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 SVGAnimatedPreserveAspectRatio attribute } else if (aAttribute == nsGkAtoms::preserveAspectRatio) { SVGAnimatedPreserveAspectRatio *preserveAspectRatio = GetPreserveAspectRatio(); 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 // nsSVGAnimatedTransformList is/has been allocated: nsSVGAnimatedTransformList *transformList = GetAnimatedTransformList(DO_ALLOCATE); rv = transformList->SetBaseValueString(aValue); 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 (!foundMatch) { // Check for nsSVGString attribute StringAttributesInfo stringInfo = GetStringInfo(); for (uint32_t i = 0; i < stringInfo.mStringCount; i++) { if (aNamespaceID == stringInfo.mStringInfo[i].mNamespaceID && aAttribute == *stringInfo.mStringInfo[i].mName) { stringInfo.mStrings[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 nsSVGElementBase::ParseAttribute(aNamespaceID, aAttribute, aValue, aResult); } void nsSVGElement::UnsetAttrInternal(int32_t aNamespaceID, nsIAtom* 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 rule to force an update if (IsAttributeMapped(aName)) mContentStyleRule = nullptr; if (IsEventAttributeName(aName)) { EventListenerManager* manager = GetExistingListenerManager(); if (manager) { nsIAtom* eventName = GetEventNameForAttr(aName); manager->RemoveEventHandler(eventName, EmptyString()); } return; } // Check if this is a length attribute going away LengthAttributesInfo lenInfo = GetLengthInfo(); for (uint32_t i = 0; i < lenInfo.mLengthCount; i++) { if (aName == *lenInfo.mLengthInfo[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.mLengthListCount; i++) { if (aName == *lengthListInfo.mLengthListInfo[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.mNumberListCount; i++) { if (aName == *numberListInfo.mNumberListInfo[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.mNumberCount; i++) { if (aName == *numInfo.mNumberInfo[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.mNumberPairCount; i++) { if (aName == *numPairInfo.mNumberPairInfo[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.mIntegerCount; i++) { if (aName == *intInfo.mIntegerInfo[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.mIntegerPairCount; i++) { if (aName == *intPairInfo.mIntegerPairInfo[i].mName) { MaybeSerializeAttrBeforeRemoval(aName, aNotify); intPairInfo.Reset(i); return; } } // Check if this is an angle attribute going away AngleAttributesInfo angleInfo = GetAngleInfo(); for (uint32_t i = 0; i < angleInfo.mAngleCount; i++) { if (aName == *angleInfo.mAngleInfo[i].mName) { MaybeSerializeAttrBeforeRemoval(aName, aNotify); angleInfo.Reset(i); return; } } // Check if this is a boolean attribute going away BooleanAttributesInfo boolInfo = GetBooleanInfo(); for (uint32_t i = 0; i < boolInfo.mBooleanCount; i++) { if (aName == *boolInfo.mBooleanInfo[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.mEnumCount; i++) { if (aName == *enumInfo.mEnumInfo[i].mName) { enumInfo.Reset(i); return; } } // Check if this is a nsViewBox attribute going away if (aName == nsGkAtoms::viewBox) { nsSVGViewBox* viewBox = GetViewBox(); if (viewBox) { MaybeSerializeAttrBeforeRemoval(aName, aNotify); viewBox->Init(); return; } } // Check if this is a preserveAspectRatio attribute going away if (aName == nsGkAtoms::preserveAspectRatio) { SVGAnimatedPreserveAspectRatio *preserveAspectRatio = GetPreserveAspectRatio(); if (preserveAspectRatio) { MaybeSerializeAttrBeforeRemoval(aName, aNotify); preserveAspectRatio->Init(); return; } } // Check if this is a transform list attribute going away if (GetTransformListAttrName() == aName) { nsSVGAnimatedTransformList *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.mStringListCount; i++) { if (aName == *stringListInfo.mStringListInfo[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.mStringCount; i++) { if (aNamespaceID == stringInfo.mStringInfo[i].mNamespaceID && aName == *stringInfo.mStringInfo[i].mName) { stringInfo.Reset(i); return; } } } nsresult nsSVGElement::UnsetAttr(int32_t aNamespaceID, nsIAtom* aName, bool aNotify) { UnsetAttrInternal(aNamespaceID, aName, aNotify); return nsSVGElementBase::UnsetAttr(aNamespaceID, aName, aNotify); } nsChangeHint nsSVGElement::GetAttributeChangeHint(const nsIAtom* aAttribute, int32_t aModType) const { nsChangeHint retval = nsSVGElementBase::GetAttributeChangeHint(aAttribute, aModType); nsCOMPtr<SVGTests> tests = do_QueryObject(const_cast<nsSVGElement*>(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 nsSVGElement::IsNodeOfType(uint32_t aFlags) const { return !(aFlags & ~eCONTENT); } NS_IMETHODIMP nsSVGElement::WalkContentStyleRules(nsRuleWalker* aRuleWalker) { #ifdef DEBUG // printf("nsSVGElement(%p)::WalkContentStyleRules()\n", this); #endif if (!mContentStyleRule) UpdateContentStyleRule(); if (mContentStyleRule) { css::Declaration* declaration = mContentStyleRule->GetDeclaration(); declaration->SetImmutable(); aRuleWalker->Forward(declaration); } return NS_OK; } void nsSVGElement::WalkAnimatedContentStyleRules(nsRuleWalker* aRuleWalker) { // Update & walk the animated content style rule, to include style from // animated mapped attributes. But first, get nsPresContext to check // whether this is a "no-animation restyle". (This should match the check // in nsHTMLCSSStyleSheet::RulesMatching(), where we determine whether to // apply the SMILOverrideStyle.) RestyleManagerHandle restyleManager = aRuleWalker->PresContext()->RestyleManager(); MOZ_ASSERT(restyleManager->IsGecko(), "stylo: Servo-backed style system should not be calling " "WalkAnimatedContentStyleRules"); if (!restyleManager->AsGecko()->SkipAnimationRules()) { // update/walk the animated content style rule. css::StyleRule* animContentStyleRule = GetAnimatedContentStyleRule(); if (!animContentStyleRule) { UpdateAnimatedContentStyleRule(); animContentStyleRule = GetAnimatedContentStyleRule(); } if (animContentStyleRule) { css::Declaration* declaration = animContentStyleRule->GetDeclaration(); declaration->SetImmutable(); aRuleWalker->Forward(declaration); } } } NS_IMETHODIMP_(bool) nsSVGElement::IsAttributeMapped(const nsIAtom* name) const { if (name == nsGkAtoms::lang) { return true; } return nsSVGElementBase::IsAttributeMapped(name); } // PresentationAttributes-FillStroke /* static */ const Element::MappedAttributeEntry nsSVGElement::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 nsSVGElement::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::visibility }, { nullptr } }; // PresentationAttributes-TextContentElements /* static */ const Element::MappedAttributeEntry nsSVGElement::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 nsSVGElement::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 nsSVGElement::sGradientStopMap[] = { { &nsGkAtoms::stop_color }, { &nsGkAtoms::stop_opacity }, { nullptr } }; // PresentationAttributes-Viewports /* static */ const Element::MappedAttributeEntry nsSVGElement::sViewportsMap[] = { { &nsGkAtoms::overflow }, { &nsGkAtoms::clip }, { nullptr } }; // PresentationAttributes-Makers /* static */ const Element::MappedAttributeEntry nsSVGElement::sMarkersMap[] = { { &nsGkAtoms::marker_end }, { &nsGkAtoms::marker_mid }, { &nsGkAtoms::marker_start }, { nullptr } }; // PresentationAttributes-Color /* static */ const Element::MappedAttributeEntry nsSVGElement::sColorMap[] = { { &nsGkAtoms::color }, { nullptr } }; // PresentationAttributes-Filters /* static */ const Element::MappedAttributeEntry nsSVGElement::sFiltersMap[] = { { &nsGkAtoms::colorInterpolationFilters }, { nullptr } }; // PresentationAttributes-feFlood /* static */ const Element::MappedAttributeEntry nsSVGElement::sFEFloodMap[] = { { &nsGkAtoms::flood_color }, { &nsGkAtoms::flood_opacity }, { nullptr } }; // PresentationAttributes-LightingEffects /* static */ const Element::MappedAttributeEntry nsSVGElement::sLightingEffectsMap[] = { { &nsGkAtoms::lighting_color }, { nullptr } }; // PresentationAttributes-mask /* static */ const Element::MappedAttributeEntry nsSVGElement::sMaskMap[] = { { &nsGkAtoms::mask_type }, { nullptr } }; //---------------------------------------------------------------------- // nsIDOMElement methods // forwarded to Element implementations //---------------------------------------------------------------------- // nsIDOMSVGElement methods NS_IMETHODIMP nsSVGElement::GetOwnerSVGElement(nsIDOMSVGElement * *aOwnerSVGElement) { NS_IF_ADDREF(*aOwnerSVGElement = GetOwnerSVGElement()); return NS_OK; } SVGSVGElement* nsSVGElement::GetOwnerSVGElement() { return GetCtx(); // this may return nullptr } NS_IMETHODIMP nsSVGElement::GetViewportElement(nsIDOMSVGElement * *aViewportElement) { nsSVGElement* elem = GetViewportElement(); NS_ADDREF(*aViewportElement = elem); return NS_OK; } nsSVGElement* nsSVGElement::GetViewportElement() { return SVGContentUtils::GetNearestViewportElement(this); } already_AddRefed<SVGAnimatedString> nsSVGElement::ClassName() { return mClassAttribute.ToDOMAnimatedString(this); } bool nsSVGElement::IsFocusableInternal(int32_t* aTabIndex, bool) { int32_t index = TabIndex(); if (index == -1) { return false; } *aTabIndex = index; return true; } //------------------------------------------------------------------------ // Helper class: MappedAttrParser, for parsing values of mapped attributes namespace { class MOZ_STACK_CLASS MappedAttrParser { public: MappedAttrParser(css::Loader* aLoader, nsIURI* aDocURI, already_AddRefed<nsIURI> aBaseURI, nsSVGElement* aElement); ~MappedAttrParser(); // Parses a mapped attribute value. void ParseMappedAttrValue(nsIAtom* aMappedAttrName, const nsAString& aMappedAttrValue); // If we've parsed any values for mapped attributes, this method returns // a new already_AddRefed css::StyleRule that incorporates the parsed // values. Otherwise, this method returns null. already_AddRefed<css::StyleRule> CreateStyleRule(); private: // MEMBER DATA // ----------- nsCSSParser mParser; // Arguments for nsCSSParser::ParseProperty nsIURI* mDocURI; nsCOMPtr<nsIURI> mBaseURI; // Declaration for storing parsed values (lazily initialized) css::Declaration* mDecl; // For reporting use counters nsSVGElement* mElement; }; MappedAttrParser::MappedAttrParser(css::Loader* aLoader, nsIURI* aDocURI, already_AddRefed<nsIURI> aBaseURI, nsSVGElement* aElement) : mParser(aLoader), mDocURI(aDocURI), mBaseURI(aBaseURI), mDecl(nullptr), mElement(aElement) { } MappedAttrParser::~MappedAttrParser() { MOZ_ASSERT(!mDecl, "If mDecl was initialized, it should have been converted " "into a style rule (and had its pointer cleared)"); } void MappedAttrParser::ParseMappedAttrValue(nsIAtom* aMappedAttrName, const nsAString& aMappedAttrValue) { if (!mDecl) { mDecl = new css::Declaration(); mDecl->InitializeEmpty(); } // Get the nsCSSPropertyID ID for our mapped attribute. nsCSSPropertyID propertyID = nsCSSProps::LookupProperty(nsDependentAtomString(aMappedAttrName), CSSEnabledState::eForAllContent); if (propertyID != eCSSProperty_UNKNOWN) { bool changed = false; // outparam for ParseProperty. mParser.ParseProperty(propertyID, aMappedAttrValue, mDocURI, mBaseURI, mElement->NodePrincipal(), mDecl, &changed, false, true); if (changed) { // The normal reporting of use counters by the nsCSSParser won't happen // since it doesn't have a sheet. if (nsCSSProps::IsShorthand(propertyID)) { CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(subprop, propertyID, CSSEnabledState::eForAllContent) { UseCounter useCounter = nsCSSProps::UseCounterFor(*subprop); if (useCounter != eUseCounter_UNKNOWN) { mElement->OwnerDoc()->SetDocumentAndPageUseCounter(useCounter); } } } else { UseCounter useCounter = nsCSSProps::UseCounterFor(propertyID); if (useCounter != eUseCounter_UNKNOWN) { mElement->OwnerDoc()->SetDocumentAndPageUseCounter(useCounter); } } } return; } MOZ_ASSERT(aMappedAttrName == nsGkAtoms::lang, "Only 'lang' should be unrecognized!"); // nsCSSParser doesn't know about 'lang', so we need to handle it specially. if (aMappedAttrName == nsGkAtoms::lang) { propertyID = eCSSProperty__x_lang; nsCSSExpandedDataBlock block; mDecl->ExpandTo(&block); nsCSSValue cssValue(PromiseFlatString(aMappedAttrValue), eCSSUnit_Ident); block.AddLonghandProperty(propertyID, cssValue); mDecl->ValueAppended(propertyID); mDecl->CompressFrom(&block); } } already_AddRefed<css::StyleRule> MappedAttrParser::CreateStyleRule() { if (!mDecl) { return nullptr; // No mapped attributes were parsed } RefPtr<css::StyleRule> rule = new css::StyleRule(nullptr, mDecl, 0, 0); mDecl = nullptr; // We no longer own the declaration -- drop our pointer to it return rule.forget(); } } // namespace //---------------------------------------------------------------------- // Implementation Helpers: void nsSVGElement::UpdateContentStyleRule() { NS_ASSERTION(!mContentStyleRule, "we already have a content style rule"); uint32_t attrCount = mAttrsAndChildren.AttrCount(); if (!attrCount) { // nothing to do return; } nsIDocument* doc = OwnerDoc(); MappedAttrParser mappedAttrParser(doc->CSSLoader(), doc->GetDocumentURI(), GetBaseURI(), this); for (uint32_t i = 0; i < attrCount; ++i) { const nsAttrName* attrName = mAttrsAndChildren.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 (IsSVGElement(nsGkAtoms::svg)) { // Special case: we don't want <svg> 'width'/'height' mapped into style // if the attribute value isn't a valid <length> according to SVG (which // only supports a subset of the CSS <length> values). We don't enforce // this by checking the attribute value in SVGSVGElement:: // IsAttributeMapped since we don't want that method to depend on the // value of the attribute that is being checked. Rather we just prevent // the actual mapping here, as necessary. if (attrName->Atom() == nsGkAtoms::width && !GetAnimatedLength(nsGkAtoms::width)->HasBaseVal()) { continue; } if (attrName->Atom() == nsGkAtoms::height && !GetAnimatedLength(nsGkAtoms::height)->HasBaseVal()) { continue; } } nsAutoString value; mAttrsAndChildren.AttrAt(i)->ToString(value); mappedAttrParser.ParseMappedAttrValue(attrName->Atom(), value); } mContentStyleRule = mappedAttrParser.CreateStyleRule(); } static void ParseMappedAttrAnimValueCallback(void* aObject, nsIAtom* aPropertyName, void* aPropertyValue, void* aData) { MOZ_ASSERT(aPropertyName != SMIL_MAPPED_ATTR_STYLERULE_ATOM, "animated content style rule should have been removed " "from properties table already (we're rebuilding it now)"); MappedAttrParser* mappedAttrParser = static_cast<MappedAttrParser*>(aData); MOZ_ASSERT(mappedAttrParser, "parser should be non-null"); nsStringBuffer* animValBuf = static_cast<nsStringBuffer*>(aPropertyValue); MOZ_ASSERT(animValBuf, "animated value should be non-null"); nsString animValStr; nsContentUtils::PopulateStringFromStringBuffer(animValBuf, animValStr); mappedAttrParser->ParseMappedAttrValue(aPropertyName, animValStr); } // Callback for freeing animated content style rule, in property table. static void ReleaseStyleRule(void* aObject, /* unused */ nsIAtom* aPropertyName, void* aPropertyValue, void* aData /* unused */) { MOZ_ASSERT(aPropertyName == SMIL_MAPPED_ATTR_STYLERULE_ATOM, "unexpected property name, for animated content style rule"); css::StyleRule* styleRule = static_cast<css::StyleRule*>(aPropertyValue); MOZ_ASSERT(styleRule, "unexpected null style rule"); styleRule->Release(); } void nsSVGElement::UpdateAnimatedContentStyleRule() { MOZ_ASSERT(!GetAnimatedContentStyleRule(), "Animated content style rule already set"); nsIDocument* doc = OwnerDoc(); if (!doc) { NS_ERROR("SVG element without owner document"); return; } MappedAttrParser mappedAttrParser(doc->CSSLoader(), doc->GetDocumentURI(), GetBaseURI(), this); doc->PropertyTable(SMIL_MAPPED_ATTR_ANIMVAL)-> Enumerate(this, ParseMappedAttrAnimValueCallback, &mappedAttrParser); RefPtr<css::StyleRule> animContentStyleRule(mappedAttrParser.CreateStyleRule()); if (animContentStyleRule) { #ifdef DEBUG nsresult rv = #endif SetProperty(SMIL_MAPPED_ATTR_ANIMVAL, SMIL_MAPPED_ATTR_STYLERULE_ATOM, animContentStyleRule.get(), ReleaseStyleRule); Unused << animContentStyleRule.forget(); MOZ_ASSERT(rv == NS_OK, "SetProperty failed (or overwrote something)"); } } css::StyleRule* nsSVGElement::GetAnimatedContentStyleRule() { return static_cast<css::StyleRule*>(GetProperty(SMIL_MAPPED_ATTR_ANIMVAL, SMIL_MAPPED_ATTR_STYLERULE_ATOM, nullptr)); } /** * 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 nsSVGElement::WillChangeValue(nsIAtom* aName) { // 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 since (a) happens before (b). // Also, we should be careful to always return this value to benefit from // return value optimization. nsAttrValue emptyOrOldAttrValue; const nsAttrValue* attrValue = GetParsedAttr(aName); // 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 nsSVGLength2, 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"); // 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>(nsIDOMMutationEvent::MODIFICATION) : static_cast<uint8_t>(nsIDOMMutationEvent::ADDITION); nsNodeUtils::AttributeWillChange(this, kNameSpaceID_None, aName, modType, nullptr); 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 nsSVGElement::DidChangeValue(nsIAtom* aName, const nsAttrValue& aEmptyOrOldValue, nsAttrValue& aNewValue) { bool hasListeners = nsContentUtils::HasMutationListeners(this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this); uint8_t modType = HasAttr(kNameSpaceID_None, aName) ? static_cast<uint8_t>(nsIDOMMutationEvent::MODIFICATION) : static_cast<uint8_t>(nsIDOMMutationEvent::ADDITION); SetAttrAndNotify(kNameSpaceID_None, aName, nullptr, aEmptyOrOldValue, aNewValue, modType, hasListeners, kNotifyDocumentObservers, kCallAfterSetAttr); } void nsSVGElement::MaybeSerializeAttrBeforeRemoval(nsIAtom* aName, bool aNotify) { if (!aNotify || !nsContentUtils::HasMutationListeners(this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this)) { return; } const nsAttrValue* attrValue = mAttrsAndChildren.GetAttr(aName); if (!attrValue) return; nsAutoString serializedValue; attrValue->ToString(serializedValue); nsAttrValue oldAttrValue(serializedValue); mAttrsAndChildren.SetAndSwapAttr(aName, oldAttrValue); } /* static */ nsIAtom* nsSVGElement::GetEventNameForAttr(nsIAtom* aAttr) { if (aAttr == nsGkAtoms::onload) return nsGkAtoms::onSVGLoad; if (aAttr == nsGkAtoms::onunload) return nsGkAtoms::onSVGUnload; if (aAttr == nsGkAtoms::onresize) return nsGkAtoms::onSVGResize; if (aAttr == nsGkAtoms::onscroll) return nsGkAtoms::onSVGScroll; if (aAttr == nsGkAtoms::onzoom) return nsGkAtoms::onSVGZoom; if (aAttr == nsGkAtoms::onbegin) return nsGkAtoms::onbeginEvent; if (aAttr == nsGkAtoms::onrepeat) return nsGkAtoms::onrepeatEvent; if (aAttr == nsGkAtoms::onend) return nsGkAtoms::onendEvent; return aAttr; } SVGSVGElement * nsSVGElement::GetCtx() const { 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; } /* virtual */ gfxMatrix nsSVGElement::PrependLocalTransformsTo( const gfxMatrix &aMatrix, SVGTransformTypes aWhich) const { return aMatrix; } nsSVGElement::LengthAttributesInfo nsSVGElement::GetLengthInfo() { return LengthAttributesInfo(nullptr, nullptr, 0); } void nsSVGElement::LengthAttributesInfo::Reset(uint8_t aAttrEnum) { mLengths[aAttrEnum].Init(mLengthInfo[aAttrEnum].mCtxType, aAttrEnum, mLengthInfo[aAttrEnum].mDefaultValue, mLengthInfo[aAttrEnum].mDefaultUnitType); } void nsSVGElement::SetLength(nsIAtom* aName, const nsSVGLength2 &aLength) { LengthAttributesInfo lengthInfo = GetLengthInfo(); for (uint32_t i = 0; i < lengthInfo.mLengthCount; i++) { if (aName == *lengthInfo.mLengthInfo[i].mName) { lengthInfo.mLengths[i] = aLength; DidAnimateLength(i); return; } } MOZ_ASSERT(false, "no length found to set"); } nsAttrValue nsSVGElement::WillChangeLength(uint8_t aAttrEnum) { return WillChangeValue(*GetLengthInfo().mLengthInfo[aAttrEnum].mName); } void nsSVGElement::DidChangeLength(uint8_t aAttrEnum, const nsAttrValue& aEmptyOrOldValue) { LengthAttributesInfo info = GetLengthInfo(); NS_ASSERTION(info.mLengthCount > 0, "DidChangeLength on element with no length attribs"); NS_ASSERTION(aAttrEnum < info.mLengthCount, "aAttrEnum out of range"); nsAttrValue newValue; newValue.SetTo(info.mLengths[aAttrEnum], nullptr); DidChangeValue(*info.mLengthInfo[aAttrEnum].mName, aEmptyOrOldValue, newValue); } void nsSVGElement::DidAnimateLength(uint8_t aAttrEnum) { ClearAnyCachedPath(); nsIFrame* frame = GetPrimaryFrame(); if (frame) { LengthAttributesInfo info = GetLengthInfo(); frame->AttributeChanged(kNameSpaceID_None, *info.mLengthInfo[aAttrEnum].mName, nsIDOMMutationEvent::MODIFICATION); } } nsSVGLength2* nsSVGElement::GetAnimatedLength(const nsIAtom *aAttrName) { LengthAttributesInfo lengthInfo = GetLengthInfo(); for (uint32_t i = 0; i < lengthInfo.mLengthCount; i++) { if (aAttrName == *lengthInfo.mLengthInfo[i].mName) { return &lengthInfo.mLengths[i]; } } MOZ_ASSERT(false, "no matching length found"); return nullptr; } void nsSVGElement::GetAnimatedLengthValues(float *aFirst, ...) { LengthAttributesInfo info = GetLengthInfo(); NS_ASSERTION(info.mLengthCount > 0, "GetAnimatedLengthValues on element with no length attribs"); SVGSVGElement *ctx = nullptr; float *f = aFirst; uint32_t i = 0; va_list args; va_start(args, aFirst); while (f && i < info.mLengthCount) { uint8_t type = info.mLengths[i].GetSpecifiedUnitType(); if (!ctx) { if (type != nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER && type != nsIDOMSVGLength::SVG_LENGTHTYPE_PX) ctx = GetCtx(); } if (type == nsIDOMSVGLength::SVG_LENGTHTYPE_EMS || type == nsIDOMSVGLength::SVG_LENGTHTYPE_EXS) *f = info.mLengths[i++].GetAnimValue(this); else *f = info.mLengths[i++].GetAnimValue(ctx); f = va_arg(args, float*); } va_end(args); } nsSVGElement::LengthListAttributesInfo nsSVGElement::GetLengthListInfo() { return LengthListAttributesInfo(nullptr, nullptr, 0); } void nsSVGElement::LengthListAttributesInfo::Reset(uint8_t aAttrEnum) { mLengthLists[aAttrEnum].ClearBaseValue(aAttrEnum); // caller notifies } nsAttrValue nsSVGElement::WillChangeLengthList(uint8_t aAttrEnum) { return WillChangeValue(*GetLengthListInfo().mLengthListInfo[aAttrEnum].mName); } void nsSVGElement::DidChangeLengthList(uint8_t aAttrEnum, const nsAttrValue& aEmptyOrOldValue) { LengthListAttributesInfo info = GetLengthListInfo(); NS_ASSERTION(info.mLengthListCount > 0, "DidChangeLengthList on element with no length list attribs"); NS_ASSERTION(aAttrEnum < info.mLengthListCount, "aAttrEnum out of range"); nsAttrValue newValue; newValue.SetTo(info.mLengthLists[aAttrEnum].GetBaseValue(), nullptr); DidChangeValue(*info.mLengthListInfo[aAttrEnum].mName, aEmptyOrOldValue, newValue); } void nsSVGElement::DidAnimateLengthList(uint8_t aAttrEnum) { nsIFrame* frame = GetPrimaryFrame(); if (frame) { LengthListAttributesInfo info = GetLengthListInfo(); frame->AttributeChanged(kNameSpaceID_None, *info.mLengthListInfo[aAttrEnum].mName, nsIDOMMutationEvent::MODIFICATION); } } void nsSVGElement::GetAnimatedLengthListValues(SVGUserUnitList *aFirst, ...) { LengthListAttributesInfo info = GetLengthListInfo(); NS_ASSERTION(info.mLengthListCount > 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.mLengthListCount) { list->Init(&(info.mLengthLists[i].GetAnimValue()), this, info.mLengthListInfo[i].mAxis); ++i; list = va_arg(args, SVGUserUnitList*); } va_end(args); } SVGAnimatedLengthList* nsSVGElement::GetAnimatedLengthList(uint8_t aAttrEnum) { LengthListAttributesInfo info = GetLengthListInfo(); if (aAttrEnum < info.mLengthListCount) { return &(info.mLengthLists[aAttrEnum]); } NS_NOTREACHED("Bad attrEnum"); return nullptr; } nsSVGElement::NumberListAttributesInfo nsSVGElement::GetNumberListInfo() { return NumberListAttributesInfo(nullptr, nullptr, 0); } void nsSVGElement::NumberListAttributesInfo::Reset(uint8_t aAttrEnum) { MOZ_ASSERT(aAttrEnum < mNumberListCount, "Bad attr enum"); mNumberLists[aAttrEnum].ClearBaseValue(aAttrEnum); // caller notifies } nsAttrValue nsSVGElement::WillChangeNumberList(uint8_t aAttrEnum) { return WillChangeValue(*GetNumberListInfo().mNumberListInfo[aAttrEnum].mName); } void nsSVGElement::DidChangeNumberList(uint8_t aAttrEnum, const nsAttrValue& aEmptyOrOldValue) { NumberListAttributesInfo info = GetNumberListInfo(); MOZ_ASSERT(info.mNumberListCount > 0, "DidChangeNumberList on element with no number list attribs"); MOZ_ASSERT(aAttrEnum < info.mNumberListCount, "aAttrEnum out of range"); nsAttrValue newValue; newValue.SetTo(info.mNumberLists[aAttrEnum].GetBaseValue(), nullptr); DidChangeValue(*info.mNumberListInfo[aAttrEnum].mName, aEmptyOrOldValue, newValue); } void nsSVGElement::DidAnimateNumberList(uint8_t aAttrEnum) { nsIFrame* frame = GetPrimaryFrame(); if (frame) { NumberListAttributesInfo info = GetNumberListInfo(); MOZ_ASSERT(aAttrEnum < info.mNumberListCount, "aAttrEnum out of range"); frame->AttributeChanged(kNameSpaceID_None, *info.mNumberListInfo[aAttrEnum].mName, nsIDOMMutationEvent::MODIFICATION); } } SVGAnimatedNumberList* nsSVGElement::GetAnimatedNumberList(uint8_t aAttrEnum) { NumberListAttributesInfo info = GetNumberListInfo(); if (aAttrEnum < info.mNumberListCount) { return &(info.mNumberLists[aAttrEnum]); } MOZ_ASSERT(false, "Bad attrEnum"); return nullptr; } SVGAnimatedNumberList* nsSVGElement::GetAnimatedNumberList(nsIAtom *aAttrName) { NumberListAttributesInfo info = GetNumberListInfo(); for (uint32_t i = 0; i < info.mNumberListCount; i++) { if (aAttrName == *info.mNumberListInfo[i].mName) { return &info.mNumberLists[i]; } } MOZ_ASSERT(false, "Bad caller"); return nullptr; } nsAttrValue nsSVGElement::WillChangePointList() { MOZ_ASSERT(GetPointListAttrName(), "Changing non-existent point list?"); return WillChangeValue(GetPointListAttrName()); } void nsSVGElement::DidChangePointList(const nsAttrValue& aEmptyOrOldValue) { MOZ_ASSERT(GetPointListAttrName(), "Changing non-existent point list?"); nsAttrValue newValue; newValue.SetTo(GetAnimatedPointList()->GetBaseValue(), nullptr); DidChangeValue(GetPointListAttrName(), aEmptyOrOldValue, newValue); } void nsSVGElement::DidAnimatePointList() { MOZ_ASSERT(GetPointListAttrName(), "Animating non-existent path data?"); ClearAnyCachedPath(); nsIFrame* frame = GetPrimaryFrame(); if (frame) { frame->AttributeChanged(kNameSpaceID_None, GetPointListAttrName(), nsIDOMMutationEvent::MODIFICATION); } } nsAttrValue nsSVGElement::WillChangePathSegList() { MOZ_ASSERT(GetPathDataAttrName(), "Changing non-existent path seg list?"); return WillChangeValue(GetPathDataAttrName()); } void nsSVGElement::DidChangePathSegList(const nsAttrValue& aEmptyOrOldValue) { MOZ_ASSERT(GetPathDataAttrName(), "Changing non-existent path seg list?"); nsAttrValue newValue; newValue.SetTo(GetAnimPathSegList()->GetBaseValue(), nullptr); DidChangeValue(GetPathDataAttrName(), aEmptyOrOldValue, newValue); } void nsSVGElement::DidAnimatePathSegList() { MOZ_ASSERT(GetPathDataAttrName(), "Animating non-existent path data?"); ClearAnyCachedPath(); nsIFrame* frame = GetPrimaryFrame(); if (frame) { frame->AttributeChanged(kNameSpaceID_None, GetPathDataAttrName(), nsIDOMMutationEvent::MODIFICATION); } } nsSVGElement::NumberAttributesInfo nsSVGElement::GetNumberInfo() { return NumberAttributesInfo(nullptr, nullptr, 0); } void nsSVGElement::NumberAttributesInfo::Reset(uint8_t aAttrEnum) { mNumbers[aAttrEnum].Init(aAttrEnum, mNumberInfo[aAttrEnum].mDefaultValue); } void nsSVGElement::DidChangeNumber(uint8_t aAttrEnum) { NumberAttributesInfo info = GetNumberInfo(); NS_ASSERTION(info.mNumberCount > 0, "DidChangeNumber on element with no number attribs"); NS_ASSERTION(aAttrEnum < info.mNumberCount, "aAttrEnum out of range"); nsAttrValue attrValue; attrValue.SetTo(info.mNumbers[aAttrEnum].GetBaseValue(), nullptr); SetParsedAttr(kNameSpaceID_None, *info.mNumberInfo[aAttrEnum].mName, nullptr, attrValue, true); } void nsSVGElement::DidAnimateNumber(uint8_t aAttrEnum) { nsIFrame* frame = GetPrimaryFrame(); if (frame) { NumberAttributesInfo info = GetNumberInfo(); frame->AttributeChanged(kNameSpaceID_None, *info.mNumberInfo[aAttrEnum].mName, nsIDOMMutationEvent::MODIFICATION); } } void nsSVGElement::GetAnimatedNumberValues(float *aFirst, ...) { NumberAttributesInfo info = GetNumberInfo(); NS_ASSERTION(info.mNumberCount > 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.mNumberCount) { *f = info.mNumbers[i++].GetAnimValue(); f = va_arg(args, float*); } va_end(args); } nsSVGElement::NumberPairAttributesInfo nsSVGElement::GetNumberPairInfo() { return NumberPairAttributesInfo(nullptr, nullptr, 0); } void nsSVGElement::NumberPairAttributesInfo::Reset(uint8_t aAttrEnum) { mNumberPairs[aAttrEnum].Init(aAttrEnum, mNumberPairInfo[aAttrEnum].mDefaultValue1, mNumberPairInfo[aAttrEnum].mDefaultValue2); } nsAttrValue nsSVGElement::WillChangeNumberPair(uint8_t aAttrEnum) { return WillChangeValue(*GetNumberPairInfo().mNumberPairInfo[aAttrEnum].mName); } void nsSVGElement::DidChangeNumberPair(uint8_t aAttrEnum, const nsAttrValue& aEmptyOrOldValue) { NumberPairAttributesInfo info = GetNumberPairInfo(); NS_ASSERTION(info.mNumberPairCount > 0, "DidChangePairNumber on element with no number pair attribs"); NS_ASSERTION(aAttrEnum < info.mNumberPairCount, "aAttrEnum out of range"); nsAttrValue newValue; newValue.SetTo(info.mNumberPairs[aAttrEnum], nullptr); DidChangeValue(*info.mNumberPairInfo[aAttrEnum].mName, aEmptyOrOldValue, newValue); } void nsSVGElement::DidAnimateNumberPair(uint8_t aAttrEnum) { nsIFrame* frame = GetPrimaryFrame(); if (frame) { NumberPairAttributesInfo info = GetNumberPairInfo(); frame->AttributeChanged(kNameSpaceID_None, *info.mNumberPairInfo[aAttrEnum].mName, nsIDOMMutationEvent::MODIFICATION); } } nsSVGElement::IntegerAttributesInfo nsSVGElement::GetIntegerInfo() { return IntegerAttributesInfo(nullptr, nullptr, 0); } void nsSVGElement::IntegerAttributesInfo::Reset(uint8_t aAttrEnum) { mIntegers[aAttrEnum].Init(aAttrEnum, mIntegerInfo[aAttrEnum].mDefaultValue); } void nsSVGElement::DidChangeInteger(uint8_t aAttrEnum) { IntegerAttributesInfo info = GetIntegerInfo(); NS_ASSERTION(info.mIntegerCount > 0, "DidChangeInteger on element with no integer attribs"); NS_ASSERTION(aAttrEnum < info.mIntegerCount, "aAttrEnum out of range"); nsAttrValue attrValue; attrValue.SetTo(info.mIntegers[aAttrEnum].GetBaseValue(), nullptr); SetParsedAttr(kNameSpaceID_None, *info.mIntegerInfo[aAttrEnum].mName, nullptr, attrValue, true); } void nsSVGElement::DidAnimateInteger(uint8_t aAttrEnum) { nsIFrame* frame = GetPrimaryFrame(); if (frame) { IntegerAttributesInfo info = GetIntegerInfo(); frame->AttributeChanged(kNameSpaceID_None, *info.mIntegerInfo[aAttrEnum].mName, nsIDOMMutationEvent::MODIFICATION); } } void nsSVGElement::GetAnimatedIntegerValues(int32_t *aFirst, ...) { IntegerAttributesInfo info = GetIntegerInfo(); NS_ASSERTION(info.mIntegerCount > 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.mIntegerCount) { *n = info.mIntegers[i++].GetAnimValue(); n = va_arg(args, int32_t*); } va_end(args); } nsSVGElement::IntegerPairAttributesInfo nsSVGElement::GetIntegerPairInfo() { return IntegerPairAttributesInfo(nullptr, nullptr, 0); } void nsSVGElement::IntegerPairAttributesInfo::Reset(uint8_t aAttrEnum) { mIntegerPairs[aAttrEnum].Init(aAttrEnum, mIntegerPairInfo[aAttrEnum].mDefaultValue1, mIntegerPairInfo[aAttrEnum].mDefaultValue2); } nsAttrValue nsSVGElement::WillChangeIntegerPair(uint8_t aAttrEnum) { return WillChangeValue( *GetIntegerPairInfo().mIntegerPairInfo[aAttrEnum].mName); } void nsSVGElement::DidChangeIntegerPair(uint8_t aAttrEnum, const nsAttrValue& aEmptyOrOldValue) { IntegerPairAttributesInfo info = GetIntegerPairInfo(); NS_ASSERTION(info.mIntegerPairCount > 0, "DidChangeIntegerPair on element with no integer pair attribs"); NS_ASSERTION(aAttrEnum < info.mIntegerPairCount, "aAttrEnum out of range"); nsAttrValue newValue; newValue.SetTo(info.mIntegerPairs[aAttrEnum], nullptr); DidChangeValue(*info.mIntegerPairInfo[aAttrEnum].mName, aEmptyOrOldValue, newValue); } void nsSVGElement::DidAnimateIntegerPair(uint8_t aAttrEnum) { nsIFrame* frame = GetPrimaryFrame(); if (frame) { IntegerPairAttributesInfo info = GetIntegerPairInfo(); frame->AttributeChanged(kNameSpaceID_None, *info.mIntegerPairInfo[aAttrEnum].mName, nsIDOMMutationEvent::MODIFICATION); } } nsSVGElement::AngleAttributesInfo nsSVGElement::GetAngleInfo() { return AngleAttributesInfo(nullptr, nullptr, 0); } void nsSVGElement::AngleAttributesInfo::Reset(uint8_t aAttrEnum) { mAngles[aAttrEnum].Init(aAttrEnum, mAngleInfo[aAttrEnum].mDefaultValue, mAngleInfo[aAttrEnum].mDefaultUnitType); } nsAttrValue nsSVGElement::WillChangeAngle(uint8_t aAttrEnum) { return WillChangeValue(*GetAngleInfo().mAngleInfo[aAttrEnum].mName); } void nsSVGElement::DidChangeAngle(uint8_t aAttrEnum, const nsAttrValue& aEmptyOrOldValue) { AngleAttributesInfo info = GetAngleInfo(); NS_ASSERTION(info.mAngleCount > 0, "DidChangeAngle on element with no angle attribs"); NS_ASSERTION(aAttrEnum < info.mAngleCount, "aAttrEnum out of range"); nsAttrValue newValue; newValue.SetTo(info.mAngles[aAttrEnum], nullptr); DidChangeValue(*info.mAngleInfo[aAttrEnum].mName, aEmptyOrOldValue, newValue); } void nsSVGElement::DidAnimateAngle(uint8_t aAttrEnum) { nsIFrame* frame = GetPrimaryFrame(); if (frame) { AngleAttributesInfo info = GetAngleInfo(); frame->AttributeChanged(kNameSpaceID_None, *info.mAngleInfo[aAttrEnum].mName, nsIDOMMutationEvent::MODIFICATION); } } nsSVGElement::BooleanAttributesInfo nsSVGElement::GetBooleanInfo() { return BooleanAttributesInfo(nullptr, nullptr, 0); } void nsSVGElement::BooleanAttributesInfo::Reset(uint8_t aAttrEnum) { mBooleans[aAttrEnum].Init(aAttrEnum, mBooleanInfo[aAttrEnum].mDefaultValue); } void nsSVGElement::DidChangeBoolean(uint8_t aAttrEnum) { BooleanAttributesInfo info = GetBooleanInfo(); NS_ASSERTION(info.mBooleanCount > 0, "DidChangeBoolean on element with no boolean attribs"); NS_ASSERTION(aAttrEnum < info.mBooleanCount, "aAttrEnum out of range"); nsAttrValue attrValue(info.mBooleans[aAttrEnum].GetBaseValueAtom()); SetParsedAttr(kNameSpaceID_None, *info.mBooleanInfo[aAttrEnum].mName, nullptr, attrValue, true); } void nsSVGElement::DidAnimateBoolean(uint8_t aAttrEnum) { nsIFrame* frame = GetPrimaryFrame(); if (frame) { BooleanAttributesInfo info = GetBooleanInfo(); frame->AttributeChanged(kNameSpaceID_None, *info.mBooleanInfo[aAttrEnum].mName, nsIDOMMutationEvent::MODIFICATION); } } nsSVGElement::EnumAttributesInfo nsSVGElement::GetEnumInfo() { return EnumAttributesInfo(nullptr, nullptr, 0); } void nsSVGElement::EnumAttributesInfo::Reset(uint8_t aAttrEnum) { mEnums[aAttrEnum].Init(aAttrEnum, mEnumInfo[aAttrEnum].mDefaultValue); } void nsSVGElement::DidChangeEnum(uint8_t aAttrEnum) { EnumAttributesInfo info = GetEnumInfo(); NS_ASSERTION(info.mEnumCount > 0, "DidChangeEnum on element with no enum attribs"); NS_ASSERTION(aAttrEnum < info.mEnumCount, "aAttrEnum out of range"); nsAttrValue attrValue(info.mEnums[aAttrEnum].GetBaseValueAtom(this)); SetParsedAttr(kNameSpaceID_None, *info.mEnumInfo[aAttrEnum].mName, nullptr, attrValue, true); } void nsSVGElement::DidAnimateEnum(uint8_t aAttrEnum) { nsIFrame* frame = GetPrimaryFrame(); if (frame) { EnumAttributesInfo info = GetEnumInfo(); frame->AttributeChanged(kNameSpaceID_None, *info.mEnumInfo[aAttrEnum].mName, nsIDOMMutationEvent::MODIFICATION); } } nsSVGViewBox * nsSVGElement::GetViewBox() { return nullptr; } nsAttrValue nsSVGElement::WillChangeViewBox() { return WillChangeValue(nsGkAtoms::viewBox); } void nsSVGElement::DidChangeViewBox(const nsAttrValue& aEmptyOrOldValue) { nsSVGViewBox *viewBox = GetViewBox(); NS_ASSERTION(viewBox, "DidChangeViewBox on element with no viewBox attrib"); nsAttrValue newValue; newValue.SetTo(*viewBox, nullptr); DidChangeValue(nsGkAtoms::viewBox, aEmptyOrOldValue, newValue); } void nsSVGElement::DidAnimateViewBox() { nsIFrame* frame = GetPrimaryFrame(); if (frame) { frame->AttributeChanged(kNameSpaceID_None, nsGkAtoms::viewBox, nsIDOMMutationEvent::MODIFICATION); } } SVGAnimatedPreserveAspectRatio * nsSVGElement::GetPreserveAspectRatio() { return nullptr; } nsAttrValue nsSVGElement::WillChangePreserveAspectRatio() { return WillChangeValue(nsGkAtoms::preserveAspectRatio); } void nsSVGElement::DidChangePreserveAspectRatio(const nsAttrValue& aEmptyOrOldValue) { SVGAnimatedPreserveAspectRatio *preserveAspectRatio = GetPreserveAspectRatio(); NS_ASSERTION(preserveAspectRatio, "DidChangePreserveAspectRatio on element with no " "preserveAspectRatio attrib"); nsAttrValue newValue; newValue.SetTo(*preserveAspectRatio, nullptr); DidChangeValue(nsGkAtoms::preserveAspectRatio, aEmptyOrOldValue, newValue); } void nsSVGElement::DidAnimatePreserveAspectRatio() { nsIFrame* frame = GetPrimaryFrame(); if (frame) { frame->AttributeChanged(kNameSpaceID_None, nsGkAtoms::preserveAspectRatio, nsIDOMMutationEvent::MODIFICATION); } } nsAttrValue nsSVGElement::WillChangeTransformList() { return WillChangeValue(GetTransformListAttrName()); } void nsSVGElement::DidChangeTransformList(const nsAttrValue& aEmptyOrOldValue) { 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); } void nsSVGElement::DidAnimateTransformList(int32_t aModType) { MOZ_ASSERT(GetTransformListAttrName(), "Animating non-existent transform data?"); nsIFrame* frame = GetPrimaryFrame(); if (frame) { nsIAtom *transformAttr = GetTransformListAttrName(); frame->AttributeChanged(kNameSpaceID_None, transformAttr, aModType); // When script changes the 'transform' attribute, Element::SetAttrAndNotify // will call nsNodeUtills::AttributeChanged, 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 // nsNodeUtills::AttributeChanged dispatches would be inappropriate // anyway), so we need to post the change event ourself. nsChangeHint changeHint = GetAttributeChangeHint(transformAttr, aModType); if (changeHint) { nsLayoutUtils::PostRestyleEvent(this, nsRestyleHint(0), changeHint); } } } nsSVGElement::StringAttributesInfo nsSVGElement::GetStringInfo() { return StringAttributesInfo(nullptr, nullptr, 0); } void nsSVGElement::StringAttributesInfo::Reset(uint8_t aAttrEnum) { mStrings[aAttrEnum].Init(aAttrEnum); } void nsSVGElement::GetStringBaseValue(uint8_t aAttrEnum, nsAString& aResult) const { nsSVGElement::StringAttributesInfo info = const_cast<nsSVGElement*>(this)->GetStringInfo(); NS_ASSERTION(info.mStringCount > 0, "GetBaseValue on element with no string attribs"); NS_ASSERTION(aAttrEnum < info.mStringCount, "aAttrEnum out of range"); GetAttr(info.mStringInfo[aAttrEnum].mNamespaceID, *info.mStringInfo[aAttrEnum].mName, aResult); } void nsSVGElement::SetStringBaseValue(uint8_t aAttrEnum, const nsAString& aValue) { nsSVGElement::StringAttributesInfo info = GetStringInfo(); NS_ASSERTION(info.mStringCount > 0, "SetBaseValue on element with no string attribs"); NS_ASSERTION(aAttrEnum < info.mStringCount, "aAttrEnum out of range"); SetAttr(info.mStringInfo[aAttrEnum].mNamespaceID, *info.mStringInfo[aAttrEnum].mName, aValue, true); } void nsSVGElement::DidAnimateString(uint8_t aAttrEnum) { nsIFrame* frame = GetPrimaryFrame(); if (frame) { StringAttributesInfo info = GetStringInfo(); frame->AttributeChanged(info.mStringInfo[aAttrEnum].mNamespaceID, *info.mStringInfo[aAttrEnum].mName, nsIDOMMutationEvent::MODIFICATION); } } nsSVGElement::StringListAttributesInfo nsSVGElement::GetStringListInfo() { return StringListAttributesInfo(nullptr, nullptr, 0); } nsAttrValue nsSVGElement::WillChangeStringList(bool aIsConditionalProcessingAttribute, uint8_t aAttrEnum) { nsIAtom* name; if (aIsConditionalProcessingAttribute) { nsCOMPtr<SVGTests> tests(do_QueryInterface(static_cast<nsIDOMSVGElement*>(this))); name = tests->GetAttrName(aAttrEnum); } else { name = *GetStringListInfo().mStringListInfo[aAttrEnum].mName; } return WillChangeValue(name); } void nsSVGElement::DidChangeStringList(bool aIsConditionalProcessingAttribute, uint8_t aAttrEnum, const nsAttrValue& aEmptyOrOldValue) { nsIAtom* 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.mStringListCount > 0, "DidChangeStringList on element with no string list attribs"); NS_ASSERTION(aAttrEnum < info.mStringListCount, "aAttrEnum out of range"); name = *info.mStringListInfo[aAttrEnum].mName; newValue.SetTo(info.mStringLists[aAttrEnum], nullptr); } DidChangeValue(name, aEmptyOrOldValue, newValue); if (aIsConditionalProcessingAttribute) { tests->MaybeInvalidate(); } } void nsSVGElement::StringListAttributesInfo::Reset(uint8_t aAttrEnum) { mStringLists[aAttrEnum].Clear(); // caller notifies } nsresult nsSVGElement::ReportAttributeParseFailure(nsIDocument* aDocument, nsIAtom* aAttribute, const nsAString& aValue) { const nsAFlatString& attributeValue = PromiseFlatString(aValue); const char16_t *strings[] = { aAttribute->GetUTF16String(), attributeValue.get() }; return SVGContentUtils::ReportToConsole(aDocument, "AttributeParseWarning", strings, ArrayLength(strings)); } void nsSVGElement::RecompileScriptEventListeners() { int32_t i, count = mAttrsAndChildren.AttrCount(); for (i = 0; i < count; ++i) { const nsAttrName *name = mAttrsAndChildren.AttrNameAt(i); // Eventlistenener-attributes are always in the null namespace if (!name->IsAtom()) { continue; } nsIAtom *attr = name->Atom(); if (!IsEventAttributeName(attr)) { continue; } nsAutoString value; GetAttr(kNameSpaceID_None, attr, value); SetEventHandler(GetEventNameForAttr(attr), value, true); } } nsISMILAttr* nsSVGElement::GetAnimatedAttr(int32_t aNamespaceID, nsIAtom* aName) { if (aNamespaceID == kNameSpaceID_None) { // We check mapped-into-style attributes first so that animations // targeting width/height on outer-<svg> don't appear to be ignored // because we returned a nsISMILAttr for the corresponding // SVGAnimatedLength. // Mapped attributes: if (IsAttributeMapped(aName)) { nsCSSPropertyID prop = nsCSSProps::LookupProperty(nsDependentAtomString(aName), CSSEnabledState::eForAllContent); // Check IsPropertyAnimatable to avoid attributes that... // - map to explicitly unanimatable properties (e.g. 'direction') // - map to unsupported attributes (e.g. 'glyph-orientation-horizontal') if (nsSMILCSSProperty::IsPropertyAnimatable(prop)) { return new nsSMILMappedAttribute(prop, this); } } // 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 new SVGMotionSMILAttr(this); } // Lengths: LengthAttributesInfo info = GetLengthInfo(); for (uint32_t i = 0; i < info.mLengthCount; i++) { if (aName == *info.mLengthInfo[i].mName) { return info.mLengths[i].ToSMILAttr(this); } } // Numbers: { NumberAttributesInfo info = GetNumberInfo(); for (uint32_t i = 0; i < info.mNumberCount; i++) { if (aName == *info.mNumberInfo[i].mName) { return info.mNumbers[i].ToSMILAttr(this); } } } // Number Pairs: { NumberPairAttributesInfo info = GetNumberPairInfo(); for (uint32_t i = 0; i < info.mNumberPairCount; i++) { if (aName == *info.mNumberPairInfo[i].mName) { return info.mNumberPairs[i].ToSMILAttr(this); } } } // Integers: { IntegerAttributesInfo info = GetIntegerInfo(); for (uint32_t i = 0; i < info.mIntegerCount; i++) { if (aName == *info.mIntegerInfo[i].mName) { return info.mIntegers[i].ToSMILAttr(this); } } } // Integer Pairs: { IntegerPairAttributesInfo info = GetIntegerPairInfo(); for (uint32_t i = 0; i < info.mIntegerPairCount; i++) { if (aName == *info.mIntegerPairInfo[i].mName) { return info.mIntegerPairs[i].ToSMILAttr(this); } } } // Enumerations: { EnumAttributesInfo info = GetEnumInfo(); for (uint32_t i = 0; i < info.mEnumCount; i++) { if (aName == *info.mEnumInfo[i].mName) { return info.mEnums[i].ToSMILAttr(this); } } } // Booleans: { BooleanAttributesInfo info = GetBooleanInfo(); for (uint32_t i = 0; i < info.mBooleanCount; i++) { if (aName == *info.mBooleanInfo[i].mName) { return info.mBooleans[i].ToSMILAttr(this); } } } // Angles: { AngleAttributesInfo info = GetAngleInfo(); for (uint32_t i = 0; i < info.mAngleCount; i++) { if (aName == *info.mAngleInfo[i].mName) { return info.mAngles[i].ToSMILAttr(this); } } } // viewBox: if (aName == nsGkAtoms::viewBox) { nsSVGViewBox *viewBox = GetViewBox(); return viewBox ? viewBox->ToSMILAttr(this) : nullptr; } // preserveAspectRatio: if (aName == nsGkAtoms::preserveAspectRatio) { SVGAnimatedPreserveAspectRatio *preserveAspectRatio = GetPreserveAspectRatio(); return preserveAspectRatio ? preserveAspectRatio->ToSMILAttr(this) : nullptr; } // NumberLists: { NumberListAttributesInfo info = GetNumberListInfo(); for (uint32_t i = 0; i < info.mNumberListCount; i++) { if (aName == *info.mNumberListInfo[i].mName) { MOZ_ASSERT(i <= UCHAR_MAX, "Too many attributes"); return info.mNumberLists[i].ToSMILAttr(this, uint8_t(i)); } } } // LengthLists: { LengthListAttributesInfo info = GetLengthListInfo(); for (uint32_t i = 0; i < info.mLengthListCount; i++) { if (aName == *info.mLengthListInfo[i].mName) { MOZ_ASSERT(i <= UCHAR_MAX, "Too many attributes"); return info.mLengthLists[i].ToSMILAttr(this, uint8_t(i), info.mLengthListInfo[i].mAxis, info.mLengthListInfo[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.mStringCount; i++) { if (aNamespaceID == info.mStringInfo[i].mNamespaceID && aName == *info.mStringInfo[i].mName) { return info.mStrings[i].ToSMILAttr(this); } } } return nullptr; } void nsSVGElement::AnimationNeedsResample() { nsIDocument* doc = GetComposedDoc(); if (doc && doc->HasAnimationController()) { doc->GetAnimationController()->SetResampleNeeded(); } } void nsSVGElement::FlushAnimations() { nsIDocument* doc = GetComposedDoc(); if (doc && doc->HasAnimationController()) { doc->GetAnimationController()->FlushResampleRequests(); } }