mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
d5bb664bcf
Note that this means that when we start transitions, we post restyles that are processed during the current restyling operation, rather than in a later phase. This depends on patch 11, which makes the transition manager skip style changes that it posts while starting transitions, to ensure that this doesn't lead to an infinite loop. This also depends on patch 16, which only consumes restyle data for the primary frame, to ensure that the animation restyles posted are processed properly. It also depends on patch 14, which makes us retain data on finished transitions, to avoid triggering extra transitions on descendants when both an ancestor and a descendant transition an inherited property, and the descendant does so faster. This fixes a known failure in layout/style/test/test_animations.html and test_animations_omta.html (as visible in the patch). I believe this is because this patch changes us to compute keyframe values for animations on top of a style context *with* animation data rather than one without, which means what we're computing them on top of changes each time. (The purpose of patch 3 was to avoid this in the case where avoiding it matters, i.e., implicit 0% and 100% keyframes.)
2716 lines
81 KiB
C++
2716 lines
81 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* 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 <stdarg.h>
|
|
#include "nsSMILMappedAttribute.h"
|
|
#include "SVGMotionSMILAttr.h"
|
|
#include "nsAttrValueOrString.h"
|
|
#include "nsSMILAnimationController.h"
|
|
#include "mozilla/dom/SVGElementBinding.h"
|
|
#include "mozilla/unused.h"
|
|
#include "RestyleManager.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)
|
|
{
|
|
nsRefPtr<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)
|
|
{
|
|
return SVGElementBinding::Wrap(aCx, this);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
/* readonly attribute SVGAnimatedString className; */
|
|
NS_IMETHODIMP
|
|
nsSVGElement::GetSVGClassName(nsISupports** aClassName)
|
|
{
|
|
*aClassName = ClassName().take();
|
|
return NS_OK;
|
|
}
|
|
|
|
/* readonly attribute nsIDOMCSSStyleDeclaration style; */
|
|
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::eCSSStyleRule) {
|
|
// 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 SetInlineStyleRule, we don't want to fire off
|
|
// mutation events or document notifications anyway
|
|
rv = mAttrsAndChildren.SetAndTakeAttr(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 = do_GetAtom(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;
|
|
}
|
|
}
|
|
|
|
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
|
|
NS_UpdateHint(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) {
|
|
mContentStyleRule->RuleMatched();
|
|
aRuleWalker->Forward(mContentStyleRule);
|
|
}
|
|
|
|
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.)
|
|
RestyleManager* restyleManager = aRuleWalker->PresContext()->RestyleManager();
|
|
if (!restyleManager->SkipAnimationRules()) {
|
|
// update/walk the animated content style rule.
|
|
css::StyleRule* animContentStyleRule = GetAnimatedContentStyleRule();
|
|
if (!animContentStyleRule) {
|
|
UpdateAnimatedContentStyleRule();
|
|
animContentStyleRule = GetAnimatedContentStyleRule();
|
|
}
|
|
if (animContentStyleRule) {
|
|
animContentStyleRule->RuleMatched();
|
|
aRuleWalker->Forward(animContentStyleRule);
|
|
}
|
|
}
|
|
}
|
|
|
|
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::glyph_orientation_horizontal },
|
|
// { &nsGkAtoms::glyph_orientation_vertical },
|
|
// { &nsGkAtoms::kerning },
|
|
{ &nsGkAtoms::letter_spacing },
|
|
{ &nsGkAtoms::text_anchor },
|
|
{ &nsGkAtoms::text_decoration },
|
|
{ &nsGkAtoms::unicode_bidi },
|
|
{ &nsGkAtoms::word_spacing },
|
|
{ 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
|
|
|
|
/* readonly attribute nsIDOMSVGSVGElement ownerSVGElement; */
|
|
NS_IMETHODIMP
|
|
nsSVGElement::GetOwnerSVGElement(nsIDOMSVGElement * *aOwnerSVGElement)
|
|
{
|
|
NS_IF_ADDREF(*aOwnerSVGElement = GetOwnerSVGElement());
|
|
return NS_OK;
|
|
}
|
|
|
|
SVGSVGElement*
|
|
nsSVGElement::GetOwnerSVGElement()
|
|
{
|
|
return GetCtx(); // this may return nullptr
|
|
}
|
|
|
|
/* readonly attribute nsIDOMSVGElement viewportElement; */
|
|
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);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// 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,
|
|
nsIPrincipal* aNodePrincipal);
|
|
~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;
|
|
nsIPrincipal* mNodePrincipal;
|
|
|
|
// Declaration for storing parsed values (lazily initialized)
|
|
css::Declaration* mDecl;
|
|
};
|
|
|
|
MappedAttrParser::MappedAttrParser(css::Loader* aLoader,
|
|
nsIURI* aDocURI,
|
|
already_AddRefed<nsIURI> aBaseURI,
|
|
nsIPrincipal* aNodePrincipal)
|
|
: mParser(aLoader), mDocURI(aDocURI), mBaseURI(aBaseURI),
|
|
mNodePrincipal(aNodePrincipal), mDecl(nullptr)
|
|
{
|
|
}
|
|
|
|
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 nsCSSProperty ID for our mapped attribute.
|
|
nsCSSProperty propertyID =
|
|
nsCSSProps::LookupProperty(nsDependentAtomString(aMappedAttrName),
|
|
nsCSSProps::eEnabledForAllContent);
|
|
if (propertyID != eCSSProperty_UNKNOWN) {
|
|
bool changed; // outparam for ParseProperty. (ignored)
|
|
mParser.ParseProperty(propertyID, aMappedAttrValue, mDocURI, mBaseURI,
|
|
mNodePrincipal, mDecl, &changed, false, true);
|
|
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
|
|
}
|
|
|
|
nsRefPtr<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();
|
|
}
|
|
|
|
} // anonymous 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(), NodePrincipal());
|
|
|
|
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 (Tag() == 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(), NodePrincipal());
|
|
doc->PropertyTable(SMIL_MAPPED_ATTR_ANIMVAL)->
|
|
Enumerate(this, ParseMappedAttrAnimValueCallback, &mappedAttrParser);
|
|
|
|
nsRefPtr<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);
|
|
|
|
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.
|
|
*/
|
|
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.SetAndTakeAttr(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->IsSVG()) {
|
|
nsIAtom* tag = ancestor->Tag();
|
|
if (tag == nsGkAtoms::foreignObject) {
|
|
return nullptr;
|
|
}
|
|
if (tag == 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,
|
|
TransformTypes 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)) {
|
|
nsCSSProperty prop =
|
|
nsCSSProps::LookupProperty(nsDependentAtomString(aName),
|
|
nsCSSProps::eEnabledForAllContent);
|
|
// 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();
|
|
}
|
|
}
|