SVGElement.cpp (77473B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/dom/SVGElement.h" 8 9 #include <stdarg.h> 10 11 #include "SVGAnimatedBoolean.h" 12 #include "SVGAnimatedEnumeration.h" 13 #include "SVGAnimatedInteger.h" 14 #include "SVGAnimatedIntegerPair.h" 15 #include "SVGAnimatedLength.h" 16 #include "SVGAnimatedLengthList.h" 17 #include "SVGAnimatedNumber.h" 18 #include "SVGAnimatedNumberList.h" 19 #include "SVGAnimatedNumberPair.h" 20 #include "SVGAnimatedOrient.h" 21 #include "SVGAnimatedPathSegList.h" 22 #include "SVGAnimatedPointList.h" 23 #include "SVGAnimatedString.h" 24 #include "SVGAnimatedTransformList.h" 25 #include "SVGAnimatedViewBox.h" 26 #include "SVGGeometryProperty.h" 27 #include "SVGMotionSMILAttr.h" 28 #include "mozAutoDocUpdate.h" 29 #include "mozilla/AlreadyAddRefed.h" 30 #include "mozilla/DeclarationBlock.h" 31 #include "mozilla/EventListenerManager.h" 32 #include "mozilla/PresShell.h" 33 #include "mozilla/RestyleManager.h" 34 #include "mozilla/SMILAnimationController.h" 35 #include "mozilla/SVGContentUtils.h" 36 #include "mozilla/SVGObserverUtils.h" 37 #include "mozilla/StaticPrefs_layout.h" 38 #include "mozilla/dom/BindContext.h" 39 #include "mozilla/dom/CSSRuleBinding.h" 40 #include "mozilla/dom/Document.h" 41 #include "mozilla/dom/Element.h" 42 #include "mozilla/dom/MutationObservers.h" 43 #include "mozilla/dom/SVGElementBinding.h" 44 #include "mozilla/dom/SVGGeometryElement.h" 45 #include "mozilla/dom/SVGLengthBinding.h" 46 #include "mozilla/dom/SVGSVGElement.h" 47 #include "mozilla/dom/SVGTests.h" 48 #include "mozilla/dom/SVGUnitTypesBinding.h" 49 #include "nsAttrValueOrString.h" 50 #include "nsCSSProps.h" 51 #include "nsCSSValue.h" 52 #include "nsContentUtils.h" 53 #include "nsDOMCSSAttrDeclaration.h" 54 #include "nsError.h" 55 #include "nsGkAtoms.h" 56 #include "nsICSSDeclaration.h" 57 #include "nsIContentInlines.h" 58 #include "nsIFrame.h" 59 #include "nsIMutationObserver.h" 60 #include "nsLayoutUtils.h" 61 #include "nsQueryObject.h" 62 63 // This is needed to ensure correct handling of calls to the 64 // vararg-list methods in this file: 65 // SVGElement::GetAnimated{Length,Number,Integer}Values 66 // See bug 547964 for details: 67 static_assert(sizeof(void*) == sizeof(nullptr), 68 "nullptr should be the correct size"); 69 70 nsresult NS_NewSVGElement( 71 mozilla::dom::Element** aResult, 72 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) { 73 RefPtr<mozilla::dom::NodeInfo> nodeInfo(aNodeInfo); 74 auto* nim = nodeInfo->NodeInfoManager(); 75 RefPtr<mozilla::dom::SVGElement> it = 76 new (nim) mozilla::dom::SVGElement(nodeInfo.forget()); 77 nsresult rv = it->Init(); 78 79 if (NS_FAILED(rv)) { 80 return rv; 81 } 82 83 it.forget(aResult); 84 return rv; 85 } 86 87 namespace mozilla::dom { 88 using namespace SVGUnitTypes_Binding; 89 90 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGElement) 91 92 // Use the CC variant of this, even though this class does not define 93 // a new CC participant, to make QIing to the CC interfaces faster. 94 NS_IMPL_QUERY_INTERFACE_CYCLE_COLLECTION_INHERITED(SVGElement, SVGElementBase, 95 SVGElement) 96 97 SVGEnumMapping SVGElement::sSVGUnitTypesMap[] = { 98 {nsGkAtoms::userSpaceOnUse, SVG_UNIT_TYPE_USERSPACEONUSE}, 99 {nsGkAtoms::objectBoundingBox, SVG_UNIT_TYPE_OBJECTBOUNDINGBOX}, 100 {nullptr, 0}}; 101 102 SVGElement::SVGElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) 103 : SVGElementBase(std::move(aNodeInfo)) {} 104 105 SVGElement::~SVGElement() = default; 106 107 JSObject* SVGElement::WrapNode(JSContext* aCx, 108 JS::Handle<JSObject*> aGivenProto) { 109 return SVGElement_Binding::Wrap(aCx, this, aGivenProto); 110 } 111 112 template <typename Value, typename Info> 113 void SVGElement::AttributesInfo<Value, Info>::ResetAll() { 114 for (uint32_t i = 0; i < mCount; ++i) { 115 Reset(i); 116 } 117 } 118 119 template <typename Value, typename Info> 120 void SVGElement::AttributesInfo<Value, Info>::CopyAllFrom( 121 const AttributesInfo& aOther) { 122 MOZ_DIAGNOSTIC_ASSERT(mCount == aOther.mCount, 123 "Should only be called on clones"); 124 for (uint32_t i = 0; i < mCount; ++i) { 125 mValues[i] = aOther.mValues[i]; 126 } 127 } 128 129 template <> 130 void SVGElement::LengthAttributesInfo::Reset(uint8_t aAttrEnum) { 131 mValues[aAttrEnum].Init(mInfos[aAttrEnum].mCtxType, aAttrEnum, 132 mInfos[aAttrEnum].mDefaultValue, 133 mInfos[aAttrEnum].mDefaultUnitType); 134 } 135 136 template <> 137 void SVGElement::LengthListAttributesInfo::Reset(uint8_t aAttrEnum) { 138 mValues[aAttrEnum].ClearBaseValue(aAttrEnum); 139 // caller notifies 140 } 141 142 template <> 143 void SVGElement::NumberListAttributesInfo::Reset(uint8_t aAttrEnum) { 144 MOZ_ASSERT(aAttrEnum < mCount, "Bad attr enum"); 145 mValues[aAttrEnum].ClearBaseValue(aAttrEnum); 146 // caller notifies 147 } 148 149 template <> 150 void SVGElement::NumberAttributesInfo::Reset(uint8_t aAttrEnum) { 151 mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue); 152 } 153 154 template <> 155 void SVGElement::NumberPairAttributesInfo::Reset(uint8_t aAttrEnum) { 156 mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue1, 157 mInfos[aAttrEnum].mDefaultValue2); 158 } 159 160 template <> 161 void SVGElement::IntegerAttributesInfo::Reset(uint8_t aAttrEnum) { 162 mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue); 163 } 164 165 template <> 166 void SVGElement::IntegerPairAttributesInfo::Reset(uint8_t aAttrEnum) { 167 mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue1, 168 mInfos[aAttrEnum].mDefaultValue2); 169 } 170 171 template <> 172 void SVGElement::BooleanAttributesInfo::Reset(uint8_t aAttrEnum) { 173 mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue); 174 } 175 176 template <> 177 void SVGElement::StringListAttributesInfo::Reset(uint8_t aAttrEnum) { 178 mValues[aAttrEnum].Clear(); 179 // caller notifies 180 } 181 182 template <> 183 void SVGElement::EnumAttributesInfo::Reset(uint8_t aAttrEnum) { 184 mValues[aAttrEnum].Init(aAttrEnum, mInfos[aAttrEnum].mDefaultValue); 185 } 186 187 template <> 188 void SVGElement::StringAttributesInfo::Reset(uint8_t aAttrEnum) { 189 mValues[aAttrEnum].Init(aAttrEnum); 190 } 191 192 nsresult SVGElement::CopyInnerTo(mozilla::dom::Element* aDest) { 193 nsresult rv = Element::CopyInnerTo(aDest); 194 NS_ENSURE_SUCCESS(rv, rv); 195 196 auto* dest = static_cast<SVGElement*>(aDest); 197 198 // cloning a node must retain its internal nonce slot 199 if (auto* nonce = static_cast<nsString*>(GetProperty(nsGkAtoms::nonce))) { 200 dest->SetNonce(*nonce); 201 } 202 203 // If our destination is a print document, copy all the relevant length values 204 // etc so that they match the state of the original node. 205 if (aDest->OwnerDoc()->IsStaticDocument() || 206 aDest->OwnerDoc()->CloningForSVGUse()) { 207 LengthAttributesInfo lengthInfo = GetLengthInfo(); 208 dest->GetLengthInfo().CopyAllFrom(lengthInfo); 209 if (SVGGeometryProperty::ElementMapsLengthsToStyle(this)) { 210 for (uint32_t i = 0; i < lengthInfo.mCount; i++) { 211 NonCustomCSSPropertyId propId = 212 SVGGeometryProperty::AttrEnumToCSSPropId(this, i); 213 214 // We don't map use element width/height currently. We can remove this 215 // test when we do. 216 if (propId != eCSSProperty_UNKNOWN && 217 lengthInfo.mValues[i].IsAnimated()) { 218 dest->SMILOverrideStyle()->SetSMILValue(propId, 219 lengthInfo.mValues[i]); 220 } 221 } 222 } 223 dest->GetNumberInfo().CopyAllFrom(GetNumberInfo()); 224 dest->GetNumberPairInfo().CopyAllFrom(GetNumberPairInfo()); 225 dest->GetIntegerInfo().CopyAllFrom(GetIntegerInfo()); 226 dest->GetIntegerPairInfo().CopyAllFrom(GetIntegerPairInfo()); 227 dest->GetBooleanInfo().CopyAllFrom(GetBooleanInfo()); 228 if (const auto* orient = GetAnimatedOrient()) { 229 *dest->GetAnimatedOrient() = *orient; 230 } 231 if (const auto* viewBox = GetAnimatedViewBox()) { 232 *dest->GetAnimatedViewBox() = *viewBox; 233 } 234 if (const auto* preserveAspectRatio = GetAnimatedPreserveAspectRatio()) { 235 *dest->GetAnimatedPreserveAspectRatio() = *preserveAspectRatio; 236 } 237 dest->GetEnumInfo().CopyAllFrom(GetEnumInfo()); 238 dest->GetStringInfo().CopyAllFrom(GetStringInfo()); 239 dest->GetLengthListInfo().CopyAllFrom(GetLengthListInfo()); 240 dest->GetNumberListInfo().CopyAllFrom(GetNumberListInfo()); 241 if (const auto* pointList = GetAnimatedPointList()) { 242 *dest->GetAnimatedPointList() = *pointList; 243 } 244 if (const auto* pathSegList = GetAnimPathSegList()) { 245 *dest->GetAnimPathSegList() = *pathSegList; 246 if (pathSegList->IsAnimating()) { 247 dest->SMILOverrideStyle()->SetSMILValue(eCSSProperty_d, *pathSegList); 248 } 249 } 250 if (const auto* transformList = GetAnimatedTransformList()) { 251 *dest->GetAnimatedTransformList(DO_ALLOCATE) = *transformList; 252 } 253 if (const auto* animateMotionTransform = GetAnimateMotionTransform()) { 254 dest->SetAnimateMotionTransform(animateMotionTransform); 255 } 256 if (const auto* smilOverrideStyleDecoration = 257 GetSMILOverrideStyleDeclaration()) { 258 RefPtr<DeclarationBlock> declClone = smilOverrideStyleDecoration->Clone(); 259 declClone->SetDirty(); 260 dest->SetSMILOverrideStyleDeclaration(*declClone); 261 } 262 } 263 264 return NS_OK; 265 } 266 267 //---------------------------------------------------------------------- 268 // SVGElement methods 269 270 void SVGElement::DidAnimateClass() { 271 // For Servo, snapshot the element before we change it. 272 PresShell* presShell = OwnerDoc()->GetPresShell(); 273 if (presShell) { 274 if (nsPresContext* presContext = presShell->GetPresContext()) { 275 presContext->RestyleManager()->ClassAttributeWillBeChangedBySMIL(this); 276 } 277 } 278 279 nsAutoString src; 280 mClassAttribute.GetAnimValue(src, this); 281 if (!mClassAnimAttr) { 282 mClassAnimAttr = MakeUnique<nsAttrValue>(); 283 } 284 mClassAnimAttr->ParseAtomArray(src); 285 286 // Update bloom filter after mutating mClassAnimAttr. 287 UpdateSubtreeBloomFilterForClass(mClassAnimAttr.get()); 288 UpdateSubtreeBloomFilterForAttribute(nsGkAtoms::_class); 289 PropagateBloomFilterToParents(); 290 291 // FIXME(emilio): This re-selector-matches, but we do the snapshot stuff right 292 // above... Is this needed anymore? 293 if (presShell) { 294 presShell->RestyleForAnimation(this, RestyleHint::RESTYLE_SELF); 295 } 296 DidAnimateAttribute(kNameSpaceID_None, nsGkAtoms::_class); 297 } 298 299 nsresult SVGElement::Init() { 300 // Set up length attributes - can't do this in the constructor 301 // because we can't do a virtual call at that point 302 303 GetLengthInfo().ResetAll(); 304 GetNumberInfo().ResetAll(); 305 GetNumberPairInfo().ResetAll(); 306 GetIntegerInfo().ResetAll(); 307 GetIntegerPairInfo().ResetAll(); 308 GetBooleanInfo().ResetAll(); 309 GetEnumInfo().ResetAll(); 310 311 if (SVGAnimatedOrient* orient = GetAnimatedOrient()) { 312 orient->Init(); 313 } 314 315 if (SVGAnimatedViewBox* viewBox = GetAnimatedViewBox()) { 316 viewBox->Init(); 317 } 318 319 if (SVGAnimatedPreserveAspectRatio* preserveAspectRatio = 320 GetAnimatedPreserveAspectRatio()) { 321 preserveAspectRatio->Init(); 322 } 323 324 GetLengthListInfo().ResetAll(); 325 GetNumberListInfo().ResetAll(); 326 327 // No need to reset SVGPointList since the default value is always the same 328 // (an empty list). 329 330 // No need to reset SVGPathData since the default value is always the same 331 // (an empty list). 332 333 GetStringInfo().ResetAll(); 334 return NS_OK; 335 } 336 337 //---------------------------------------------------------------------- 338 // Implementation 339 340 //---------------------------------------------------------------------- 341 // nsIContent methods 342 343 nsresult SVGElement::BindToTree(BindContext& aContext, nsINode& aParent) { 344 nsresult rv = SVGElementBase::BindToTree(aContext, aParent); 345 NS_ENSURE_SUCCESS(rv, rv); 346 347 // Hide any nonce from the DOM, but keep the internal value of the 348 // nonce by copying and resetting the internal nonce value. 349 if (!aContext.IsMove() && HasFlag(NODE_HAS_NONCE_AND_HEADER_CSP) && 350 IsInComposedDoc() && OwnerDoc()->GetBrowsingContext()) { 351 nsContentUtils::AddScriptRunner(NS_NewRunnableFunction( 352 "SVGElement::ResetNonce::Runnable", 353 [self = RefPtr<SVGElement>(this)]() { 354 nsAutoString nonce; 355 self->GetNonce(nonce); 356 self->SetAttr(kNameSpaceID_None, nsGkAtoms::nonce, u""_ns, true); 357 self->SetNonce(nonce); 358 })); 359 } 360 361 return NS_OK; 362 } 363 364 void SVGElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName, 365 const nsAttrValue* aValue, 366 const nsAttrValue* aOldValue, 367 nsIPrincipal* aSubjectPrincipal, bool aNotify) { 368 if (IsEventAttributeName(aName) && aValue) { 369 MOZ_ASSERT(aValue->Type() == nsAttrValue::eString || 370 aValue->Type() == nsAttrValue::eAtom, 371 "Expected string or atom value for script body"); 372 SetEventHandler(GetEventNameForAttr(aName), 373 nsAttrValueOrString(aValue).String()); 374 } 375 376 // The nonce will be copied over to an internal slot and cleared from the 377 // Element within BindToTree to avoid CSS Selector nonce exfiltration if 378 // the CSP list contains a header-delivered CSP. 379 if (nsGkAtoms::nonce == aName && kNameSpaceID_None == aNamespaceID) { 380 if (aValue) { 381 SetNonce(nsAttrValueOrString(aValue).String()); 382 if (OwnerDoc()->GetHasCSPDeliveredThroughHeader()) { 383 SetFlags(NODE_HAS_NONCE_AND_HEADER_CSP); 384 } 385 } else { 386 RemoveNonce(); 387 } 388 } 389 390 return SVGElementBase::AfterSetAttr(aNamespaceID, aName, aValue, aOldValue, 391 aSubjectPrincipal, aNotify); 392 } 393 394 bool SVGElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute, 395 const nsAString& aValue, 396 nsIPrincipal* aMaybeScriptedPrincipal, 397 nsAttrValue& aResult) { 398 nsresult rv = NS_OK; 399 bool foundMatch = false; 400 bool didSetResult = false; 401 402 if (aNamespaceID == kNameSpaceID_None) { 403 // Check for SVGAnimatedLength attribute 404 LengthAttributesInfo lengthInfo = GetLengthInfo(); 405 406 uint32_t i; 407 for (i = 0; i < lengthInfo.mCount; i++) { 408 if (aAttribute == lengthInfo.mInfos[i].mName) { 409 rv = lengthInfo.mValues[i].SetBaseValueString(aValue, this, false); 410 if (NS_FAILED(rv)) { 411 lengthInfo.Reset(i); 412 } else { 413 aResult.SetTo(lengthInfo.mValues[i], &aValue); 414 didSetResult = true; 415 } 416 foundMatch = true; 417 break; 418 } 419 } 420 421 if (!foundMatch) { 422 // Check for SVGAnimatedLengthList attribute 423 LengthListAttributesInfo lengthListInfo = GetLengthListInfo(); 424 for (i = 0; i < lengthListInfo.mCount; i++) { 425 if (aAttribute == lengthListInfo.mInfos[i].mName) { 426 rv = lengthListInfo.mValues[i].SetBaseValueString(aValue); 427 if (NS_FAILED(rv)) { 428 lengthListInfo.Reset(i); 429 } else { 430 aResult.SetTo(lengthListInfo.mValues[i].GetBaseValue(), &aValue); 431 didSetResult = true; 432 } 433 foundMatch = true; 434 break; 435 } 436 } 437 } 438 439 if (!foundMatch) { 440 // Check for SVGAnimatedNumberList attribute 441 NumberListAttributesInfo numberListInfo = GetNumberListInfo(); 442 for (i = 0; i < numberListInfo.mCount; i++) { 443 if (aAttribute == numberListInfo.mInfos[i].mName) { 444 rv = numberListInfo.mValues[i].SetBaseValueString(aValue); 445 if (NS_FAILED(rv)) { 446 numberListInfo.Reset(i); 447 } else { 448 aResult.SetTo(numberListInfo.mValues[i].GetBaseValue(), &aValue); 449 didSetResult = true; 450 } 451 foundMatch = true; 452 break; 453 } 454 } 455 } 456 457 if (!foundMatch) { 458 // Check for SVGAnimatedPointList attribute 459 if (GetPointListAttrName() == aAttribute) { 460 if (SVGAnimatedPointList* pointList = GetAnimatedPointList()) { 461 pointList->SetBaseValueString(aValue); 462 // The spec says we parse everything up to the failure, so we DON'T 463 // need to check the result of SetBaseValueString or call 464 // pointList->ClearBaseValue() if it fails 465 aResult.SetTo(pointList->GetBaseValue(), &aValue); 466 didSetResult = true; 467 foundMatch = true; 468 } 469 } 470 } 471 472 if (!foundMatch) { 473 // Check for SVGAnimatedPathSegList attribute 474 if (GetPathDataAttrName() == aAttribute) { 475 if (SVGAnimatedPathSegList* segList = GetAnimPathSegList()) { 476 segList->SetBaseValueString(aValue); 477 // The spec says we parse everything up to the failure, so we DON'T 478 // need to check the result of SetBaseValueString or call 479 // segList->ClearBaseValue() if it fails 480 aResult.SetTo(segList->GetBaseValue(), &aValue); 481 didSetResult = true; 482 foundMatch = true; 483 } 484 } 485 } 486 487 if (!foundMatch) { 488 // Check for SVGAnimatedNumber attribute 489 NumberAttributesInfo numberInfo = GetNumberInfo(); 490 for (i = 0; i < numberInfo.mCount; i++) { 491 if (aAttribute == numberInfo.mInfos[i].mName) { 492 rv = numberInfo.mValues[i].SetBaseValueString(aValue, this); 493 if (NS_FAILED(rv)) { 494 numberInfo.Reset(i); 495 } else { 496 aResult.SetTo(numberInfo.mValues[i].GetBaseValue(), &aValue); 497 didSetResult = true; 498 } 499 foundMatch = true; 500 break; 501 } 502 } 503 } 504 505 if (!foundMatch) { 506 // Check for SVGAnimatedNumberPair attribute 507 NumberPairAttributesInfo numberPairInfo = GetNumberPairInfo(); 508 for (i = 0; i < numberPairInfo.mCount; i++) { 509 if (aAttribute == numberPairInfo.mInfos[i].mName) { 510 rv = numberPairInfo.mValues[i].SetBaseValueString(aValue, this); 511 if (NS_FAILED(rv)) { 512 numberPairInfo.Reset(i); 513 } else { 514 aResult.SetTo(numberPairInfo.mValues[i], &aValue); 515 didSetResult = true; 516 } 517 foundMatch = true; 518 break; 519 } 520 } 521 } 522 523 if (!foundMatch) { 524 // Check for SVGAnimatedInteger attribute 525 IntegerAttributesInfo integerInfo = GetIntegerInfo(); 526 for (i = 0; i < integerInfo.mCount; i++) { 527 if (aAttribute == integerInfo.mInfos[i].mName) { 528 rv = integerInfo.mValues[i].SetBaseValueString(aValue, this); 529 if (NS_FAILED(rv)) { 530 integerInfo.Reset(i); 531 } else { 532 aResult.SetTo(integerInfo.mValues[i].GetBaseValue(), &aValue); 533 didSetResult = true; 534 } 535 foundMatch = true; 536 break; 537 } 538 } 539 } 540 541 if (!foundMatch) { 542 // Check for SVGAnimatedIntegerPair attribute 543 IntegerPairAttributesInfo integerPairInfo = GetIntegerPairInfo(); 544 for (i = 0; i < integerPairInfo.mCount; i++) { 545 if (aAttribute == integerPairInfo.mInfos[i].mName) { 546 rv = integerPairInfo.mValues[i].SetBaseValueString(aValue, this); 547 if (NS_FAILED(rv)) { 548 integerPairInfo.Reset(i); 549 } else { 550 aResult.SetTo(integerPairInfo.mValues[i], &aValue); 551 didSetResult = true; 552 } 553 foundMatch = true; 554 break; 555 } 556 } 557 } 558 559 if (!foundMatch) { 560 // Check for SVGAnimatedBoolean attribute 561 BooleanAttributesInfo booleanInfo = GetBooleanInfo(); 562 for (i = 0; i < booleanInfo.mCount; i++) { 563 if (aAttribute == booleanInfo.mInfos[i].mName) { 564 nsAtom* valAtom = NS_GetStaticAtom(aValue); 565 rv = valAtom ? booleanInfo.mValues[i].SetBaseValueAtom(valAtom, this) 566 : NS_ERROR_DOM_SYNTAX_ERR; 567 if (NS_FAILED(rv)) { 568 booleanInfo.Reset(i); 569 } else { 570 aResult.SetTo(valAtom); 571 didSetResult = true; 572 } 573 foundMatch = true; 574 break; 575 } 576 } 577 } 578 579 if (!foundMatch) { 580 // Check for SVGAnimatedEnumeration attribute 581 EnumAttributesInfo enumInfo = GetEnumInfo(); 582 for (i = 0; i < enumInfo.mCount; i++) { 583 if (aAttribute == enumInfo.mInfos[i].mName) { 584 RefPtr<nsAtom> valAtom = NS_Atomize(aValue); 585 if (!enumInfo.mValues[i].SetBaseValueAtom(valAtom, this)) { 586 // Exact error value does not matter; we just need to mark the 587 // parse as failed. 588 rv = NS_ERROR_FAILURE; 589 enumInfo.Reset(i); 590 } else { 591 aResult.SetTo(valAtom); 592 didSetResult = true; 593 } 594 foundMatch = true; 595 break; 596 } 597 } 598 } 599 600 if (!foundMatch) { 601 // Check for conditional processing attributes 602 nsCOMPtr<SVGTests> tests = do_QueryObject(this); 603 if (tests && tests->ParseConditionalProcessingAttribute( 604 aAttribute, aValue, aResult)) { 605 foundMatch = true; 606 } 607 } 608 609 if (!foundMatch) { 610 // Check for StringList attribute 611 StringListAttributesInfo stringListInfo = GetStringListInfo(); 612 for (i = 0; i < stringListInfo.mCount; i++) { 613 if (aAttribute == stringListInfo.mInfos[i].mName) { 614 rv = stringListInfo.mValues[i].SetValue(aValue); 615 if (NS_FAILED(rv)) { 616 stringListInfo.Reset(i); 617 } else { 618 aResult.SetTo(stringListInfo.mValues[i], &aValue); 619 didSetResult = true; 620 } 621 foundMatch = true; 622 break; 623 } 624 } 625 } 626 627 if (!foundMatch) { 628 // Check for orient attribute 629 if (aAttribute == nsGkAtoms::orient) { 630 SVGAnimatedOrient* orient = GetAnimatedOrient(); 631 if (orient) { 632 rv = orient->SetBaseValueString(aValue, this, false); 633 if (NS_FAILED(rv)) { 634 orient->Init(); 635 } else { 636 aResult.SetTo(*orient, &aValue); 637 didSetResult = true; 638 } 639 foundMatch = true; 640 } 641 // Check for viewBox attribute 642 } else if (aAttribute == nsGkAtoms::viewBox) { 643 SVGAnimatedViewBox* viewBox = GetAnimatedViewBox(); 644 if (viewBox) { 645 rv = viewBox->SetBaseValueString(aValue, this, false); 646 if (NS_FAILED(rv)) { 647 viewBox->Init(); 648 } else { 649 aResult.SetTo(*viewBox, &aValue); 650 didSetResult = true; 651 } 652 foundMatch = true; 653 } 654 // Check for preserveAspectRatio attribute 655 } else if (aAttribute == nsGkAtoms::preserveAspectRatio) { 656 SVGAnimatedPreserveAspectRatio* preserveAspectRatio = 657 GetAnimatedPreserveAspectRatio(); 658 if (preserveAspectRatio) { 659 rv = preserveAspectRatio->SetBaseValueString(aValue, this, false); 660 if (NS_FAILED(rv)) { 661 preserveAspectRatio->Init(); 662 } else { 663 aResult.SetTo(*preserveAspectRatio, &aValue); 664 didSetResult = true; 665 } 666 foundMatch = true; 667 } 668 // Check for SVGAnimatedTransformList attribute 669 } else if (GetTransformListAttrName() == aAttribute) { 670 // The transform attribute is being set, so we must ensure that the 671 // SVGAnimatedTransformList is/has been allocated: 672 SVGAnimatedTransformList* transformList = 673 GetAnimatedTransformList(DO_ALLOCATE); 674 rv = transformList->SetBaseValueString(aValue, this); 675 if (NS_FAILED(rv)) { 676 transformList->ClearBaseValue(); 677 } else { 678 aResult.SetTo(transformList->GetBaseValue(), &aValue); 679 didSetResult = true; 680 } 681 foundMatch = true; 682 } else if (aAttribute == nsGkAtoms::tabindex) { 683 didSetResult = aResult.ParseIntValue(aValue); 684 foundMatch = true; 685 } 686 } 687 688 if (aAttribute == nsGkAtoms::_class) { 689 mClassAttribute.SetBaseValue(aValue, this, false); 690 aResult.ParseAtomArray(aValue); 691 return true; 692 } 693 694 if (aAttribute == nsGkAtoms::rel) { 695 aResult.ParseAtomArray(aValue); 696 return true; 697 } 698 } 699 700 if (!foundMatch) { 701 // Check for SVGAnimatedString attribute 702 StringAttributesInfo stringInfo = GetStringInfo(); 703 for (uint32_t i = 0; i < stringInfo.mCount; i++) { 704 if (aNamespaceID == stringInfo.mInfos[i].mNamespaceID && 705 aAttribute == stringInfo.mInfos[i].mName) { 706 stringInfo.mValues[i].SetBaseValue(aValue, this, false); 707 foundMatch = true; 708 break; 709 } 710 } 711 } 712 713 if (foundMatch) { 714 if (NS_FAILED(rv)) { 715 ReportAttributeParseFailure(OwnerDoc(), aAttribute, aValue); 716 return false; 717 } 718 if (!didSetResult) { 719 aResult.SetTo(aValue); 720 } 721 return true; 722 } 723 724 return SVGElementBase::ParseAttribute(aNamespaceID, aAttribute, aValue, 725 aMaybeScriptedPrincipal, aResult); 726 } 727 728 void SVGElement::UnsetAttrInternal(int32_t aNamespaceID, nsAtom* aName, 729 bool aNotify) { 730 // XXXbz there's a bunch of redundancy here with AfterSetAttr. 731 // Maybe consolidate? 732 733 if (aNamespaceID == kNameSpaceID_None) { 734 if (IsEventAttributeName(aName)) { 735 EventListenerManager* manager = GetExistingListenerManager(); 736 if (manager) { 737 nsAtom* eventName = GetEventNameForAttr(aName); 738 manager->RemoveEventHandler(eventName); 739 } 740 return; 741 } 742 743 // Check if this is a length attribute going away 744 LengthAttributesInfo lenInfo = GetLengthInfo(); 745 746 for (uint32_t i = 0; i < lenInfo.mCount; i++) { 747 if (aName == lenInfo.mInfos[i].mName) { 748 lenInfo.Reset(i); 749 return; 750 } 751 } 752 753 // Check if this is a length list attribute going away 754 LengthListAttributesInfo lengthListInfo = GetLengthListInfo(); 755 756 for (uint32_t i = 0; i < lengthListInfo.mCount; i++) { 757 if (aName == lengthListInfo.mInfos[i].mName) { 758 lengthListInfo.Reset(i); 759 return; 760 } 761 } 762 763 // Check if this is a number list attribute going away 764 NumberListAttributesInfo numberListInfo = GetNumberListInfo(); 765 766 for (uint32_t i = 0; i < numberListInfo.mCount; i++) { 767 if (aName == numberListInfo.mInfos[i].mName) { 768 numberListInfo.Reset(i); 769 return; 770 } 771 } 772 773 // Check if this is a point list attribute going away 774 if (GetPointListAttrName() == aName) { 775 SVGAnimatedPointList* pointList = GetAnimatedPointList(); 776 if (pointList) { 777 pointList->ClearBaseValue(); 778 return; 779 } 780 } 781 782 // Check if this is a path segment list attribute going away 783 if (GetPathDataAttrName() == aName) { 784 SVGAnimatedPathSegList* segList = GetAnimPathSegList(); 785 if (segList) { 786 segList->ClearBaseValue(); 787 return; 788 } 789 } 790 791 // Check if this is a number attribute going away 792 NumberAttributesInfo numInfo = GetNumberInfo(); 793 794 for (uint32_t i = 0; i < numInfo.mCount; i++) { 795 if (aName == numInfo.mInfos[i].mName) { 796 numInfo.Reset(i); 797 return; 798 } 799 } 800 801 // Check if this is a number pair attribute going away 802 NumberPairAttributesInfo numPairInfo = GetNumberPairInfo(); 803 804 for (uint32_t i = 0; i < numPairInfo.mCount; i++) { 805 if (aName == numPairInfo.mInfos[i].mName) { 806 numPairInfo.Reset(i); 807 return; 808 } 809 } 810 811 // Check if this is an integer attribute going away 812 IntegerAttributesInfo intInfo = GetIntegerInfo(); 813 814 for (uint32_t i = 0; i < intInfo.mCount; i++) { 815 if (aName == intInfo.mInfos[i].mName) { 816 intInfo.Reset(i); 817 return; 818 } 819 } 820 821 // Check if this is an integer pair attribute going away 822 IntegerPairAttributesInfo intPairInfo = GetIntegerPairInfo(); 823 824 for (uint32_t i = 0; i < intPairInfo.mCount; i++) { 825 if (aName == intPairInfo.mInfos[i].mName) { 826 intPairInfo.Reset(i); 827 return; 828 } 829 } 830 831 // Check if this is a boolean attribute going away 832 BooleanAttributesInfo boolInfo = GetBooleanInfo(); 833 834 for (uint32_t i = 0; i < boolInfo.mCount; i++) { 835 if (aName == boolInfo.mInfos[i].mName) { 836 boolInfo.Reset(i); 837 return; 838 } 839 } 840 841 // Check if this is an enum attribute going away 842 EnumAttributesInfo enumInfo = GetEnumInfo(); 843 844 for (uint32_t i = 0; i < enumInfo.mCount; i++) { 845 if (aName == enumInfo.mInfos[i].mName) { 846 enumInfo.Reset(i); 847 return; 848 } 849 } 850 851 // Check if this is an orient attribute going away 852 if (aName == nsGkAtoms::orient) { 853 SVGAnimatedOrient* orient = GetAnimatedOrient(); 854 if (orient) { 855 orient->Init(); 856 return; 857 } 858 } 859 860 // Check if this is a viewBox attribute going away 861 if (aName == nsGkAtoms::viewBox) { 862 SVGAnimatedViewBox* viewBox = GetAnimatedViewBox(); 863 if (viewBox) { 864 viewBox->Init(); 865 return; 866 } 867 } 868 869 // Check if this is a preserveAspectRatio attribute going away 870 if (aName == nsGkAtoms::preserveAspectRatio) { 871 SVGAnimatedPreserveAspectRatio* preserveAspectRatio = 872 GetAnimatedPreserveAspectRatio(); 873 if (preserveAspectRatio) { 874 preserveAspectRatio->Init(); 875 return; 876 } 877 } 878 879 // Check if this is a transform list attribute going away 880 if (GetTransformListAttrName() == aName) { 881 SVGAnimatedTransformList* transformList = GetAnimatedTransformList(); 882 if (transformList) { 883 transformList->ClearBaseValue(); 884 return; 885 } 886 } 887 888 // Check for conditional processing attributes 889 nsCOMPtr<SVGTests> tests = do_QueryObject(this); 890 if (tests && tests->IsConditionalProcessingAttribute(aName)) { 891 tests->UnsetAttr(aName); 892 return; 893 } 894 895 // Check if this is a string list attribute going away 896 StringListAttributesInfo stringListInfo = GetStringListInfo(); 897 898 for (uint32_t i = 0; i < stringListInfo.mCount; i++) { 899 if (aName == stringListInfo.mInfos[i].mName) { 900 stringListInfo.Reset(i); 901 return; 902 } 903 } 904 905 if (aName == nsGkAtoms::_class) { 906 mClassAttribute.Init(); 907 return; 908 } 909 } 910 911 // Check if this is a string attribute going away 912 StringAttributesInfo stringInfo = GetStringInfo(); 913 914 for (uint32_t i = 0; i < stringInfo.mCount; i++) { 915 if (aNamespaceID == stringInfo.mInfos[i].mNamespaceID && 916 aName == stringInfo.mInfos[i].mName) { 917 stringInfo.Reset(i); 918 return; 919 } 920 } 921 } 922 923 void SVGElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName, 924 const nsAttrValue* aValue, bool aNotify) { 925 if (!aValue) { 926 UnsetAttrInternal(aNamespaceID, aName, aNotify); 927 } 928 return SVGElementBase::BeforeSetAttr(aNamespaceID, aName, aValue, aNotify); 929 } 930 931 nsChangeHint SVGElement::GetAttributeChangeHint(const nsAtom* aAttribute, 932 AttrModType aModType) const { 933 nsChangeHint retval = 934 SVGElementBase::GetAttributeChangeHint(aAttribute, aModType); 935 936 nsCOMPtr<SVGTests> tests = do_QueryObject(const_cast<SVGElement*>(this)); 937 if (tests && tests->IsConditionalProcessingAttribute(aAttribute)) { 938 // It would be nice to only reconstruct the frame if the value returned by 939 // SVGTests::PassesConditionalProcessingTests has changed, but we don't 940 // know that 941 retval |= nsChangeHint_ReconstructFrame; 942 } 943 return retval; 944 } 945 946 void SVGElement::NodeInfoChanged(Document* aOldDoc) { 947 SVGElementBase::NodeInfoChanged(aOldDoc); 948 } 949 950 NS_IMETHODIMP_(bool) 951 SVGElement::IsAttributeMapped(const nsAtom* name) const { 952 if (name == nsGkAtoms::lang) { 953 return true; 954 } 955 956 if (IsSVGAnimationElement()) { 957 return SVGElementBase::IsAttributeMapped(name); 958 } 959 960 static const MappedAttributeEntry attributes[] = { 961 // Properties that we don't support are commented out. 962 // { nsGkAtoms::alignment_baseline }, 963 // { nsGkAtoms::baseline_shift }, 964 {nsGkAtoms::clip}, 965 {nsGkAtoms::clip_path}, 966 {nsGkAtoms::clip_rule}, 967 {nsGkAtoms::color}, 968 {nsGkAtoms::color_interpolation}, 969 {nsGkAtoms::color_interpolation_filters}, 970 {nsGkAtoms::cursor}, 971 {nsGkAtoms::direction}, 972 {nsGkAtoms::display}, 973 {nsGkAtoms::dominant_baseline}, 974 {nsGkAtoms::fill}, 975 {nsGkAtoms::fill_opacity}, 976 {nsGkAtoms::fill_rule}, 977 {nsGkAtoms::filter}, 978 {nsGkAtoms::flood_color}, 979 {nsGkAtoms::flood_opacity}, 980 {nsGkAtoms::font_family}, 981 {nsGkAtoms::font_size}, 982 {nsGkAtoms::font_size_adjust}, 983 {nsGkAtoms::font_stretch}, 984 {nsGkAtoms::font_style}, 985 {nsGkAtoms::font_variant}, 986 {nsGkAtoms::font_weight}, 987 {nsGkAtoms::image_rendering}, 988 {nsGkAtoms::letter_spacing}, 989 {nsGkAtoms::lighting_color}, 990 {nsGkAtoms::marker_end}, 991 {nsGkAtoms::marker_mid}, 992 {nsGkAtoms::marker_start}, 993 {nsGkAtoms::mask}, 994 {nsGkAtoms::mask_type}, 995 {nsGkAtoms::opacity}, 996 {nsGkAtoms::overflow}, 997 {nsGkAtoms::paint_order}, 998 {nsGkAtoms::pointer_events}, 999 {nsGkAtoms::shape_rendering}, 1000 {nsGkAtoms::stop_color}, 1001 {nsGkAtoms::stop_opacity}, 1002 {nsGkAtoms::stroke}, 1003 {nsGkAtoms::stroke_dasharray}, 1004 {nsGkAtoms::stroke_dashoffset}, 1005 {nsGkAtoms::stroke_linecap}, 1006 {nsGkAtoms::stroke_linejoin}, 1007 {nsGkAtoms::stroke_miterlimit}, 1008 {nsGkAtoms::stroke_opacity}, 1009 {nsGkAtoms::stroke_width}, 1010 {nsGkAtoms::text_anchor}, 1011 {nsGkAtoms::text_decoration}, 1012 {nsGkAtoms::text_rendering}, 1013 {nsGkAtoms::transform_origin}, 1014 {nsGkAtoms::unicode_bidi}, 1015 {nsGkAtoms::vector_effect}, 1016 {nsGkAtoms::visibility}, 1017 {nsGkAtoms::white_space}, 1018 {nsGkAtoms::word_spacing}, 1019 {nsGkAtoms::writing_mode}, 1020 {nullptr}}; 1021 1022 static const MappedAttributeEntry* const map[] = {attributes}; 1023 1024 return FindAttributeDependence(name, map) || 1025 SVGElementBase::IsAttributeMapped(name); 1026 } 1027 1028 //---------------------------------------------------------------------- 1029 // Element methods 1030 1031 // forwarded to Element implementations 1032 1033 //---------------------------------------------------------------------- 1034 1035 SVGSVGElement* SVGElement::GetOwnerSVGElement() { 1036 nsIContent* ancestor = GetFlattenedTreeParent(); 1037 1038 while (ancestor && ancestor->IsSVGElement()) { 1039 if (ancestor->IsSVGElement(nsGkAtoms::foreignObject)) { 1040 return nullptr; 1041 } 1042 if (auto* svg = SVGSVGElement::FromNode(ancestor)) { 1043 return svg; 1044 } 1045 ancestor = ancestor->GetFlattenedTreeParent(); 1046 } 1047 1048 // we don't have an ancestor <svg> element... 1049 return nullptr; 1050 } 1051 1052 SVGElement* SVGElement::GetViewportElement() { 1053 return SVGContentUtils::GetNearestViewportElement(this); 1054 } 1055 1056 already_AddRefed<DOMSVGAnimatedString> SVGElement::ClassName() { 1057 return mClassAttribute.ToDOMAnimatedString(this); 1058 } 1059 1060 /* static */ 1061 bool SVGElement::UpdateDeclarationBlockFromLength( 1062 const StyleLockedDeclarationBlock& aBlock, NonCustomCSSPropertyId aPropId, 1063 const SVGAnimatedLength& aLength, ValToUse aValToUse) { 1064 float value; 1065 uint8_t units; 1066 if (aValToUse == ValToUse::Anim) { 1067 value = aLength.GetAnimValInSpecifiedUnits(); 1068 units = aLength.GetAnimUnitType(); 1069 } else { 1070 MOZ_ASSERT(aValToUse == ValToUse::Base); 1071 value = aLength.GetBaseValInSpecifiedUnits(); 1072 units = aLength.GetBaseUnitType(); 1073 } 1074 1075 // SVG parser doesn't check non-negativity of some parsed value, we should not 1076 // pass those to CSS side. 1077 if (value < 0 && 1078 SVGGeometryProperty::IsNonNegativeGeometryProperty(aPropId)) { 1079 return false; 1080 } 1081 1082 nsCSSUnit cssUnit = SVGLength::SpecifiedUnitTypeToCSSUnit(units); 1083 1084 if (cssUnit == eCSSUnit_Percent) { 1085 Servo_DeclarationBlock_SetPercentValue(&aBlock, aPropId, value / 100.f); 1086 } else { 1087 Servo_DeclarationBlock_SetLengthValue(&aBlock, aPropId, value, cssUnit); 1088 } 1089 1090 return true; 1091 } 1092 1093 /* static */ 1094 bool SVGElement::UpdateDeclarationBlockFromPath( 1095 const StyleLockedDeclarationBlock& aBlock, 1096 const SVGAnimatedPathSegList& aPath, ValToUse aValToUse) { 1097 const SVGPathData& pathData = 1098 aValToUse == ValToUse::Anim ? aPath.GetAnimValue() : aPath.GetBaseValue(); 1099 1100 // Based on the current discussion of https://github.com/w3c/svgwg/issues/321, 1101 // we may have to convert the relative commands into absolute commands. 1102 // The normalization should be fixed in Bug 1489392. 1103 Servo_DeclarationBlock_SetPathValue(&aBlock, eCSSProperty_d, 1104 &pathData.RawData()); 1105 return true; 1106 } 1107 1108 template <typename Float> 1109 static StyleTransformOperation MatrixToTransformOperation( 1110 const gfx::BaseMatrix<Float>& aMatrix) { 1111 return StyleTransformOperation::Matrix(StyleGenericMatrix<float>{ 1112 .a = float(aMatrix._11), 1113 .b = float(aMatrix._12), 1114 .c = float(aMatrix._21), 1115 .d = float(aMatrix._22), 1116 .e = float(aMatrix._31), 1117 .f = float(aMatrix._32), 1118 }); 1119 } 1120 1121 static void SVGTransformToCSS(const SVGTransform& aTransform, 1122 nsTArray<StyleTransformOperation>& aOut) { 1123 switch (aTransform.Type()) { 1124 case dom::SVGTransform_Binding::SVG_TRANSFORM_SCALE: { 1125 const auto& m = aTransform.GetMatrix(); 1126 aOut.AppendElement(StyleTransformOperation::Scale(m._11, m._22)); 1127 return; 1128 } 1129 case dom::SVGTransform_Binding::SVG_TRANSFORM_TRANSLATE: { 1130 auto p = aTransform.GetMatrix().GetTranslation(); 1131 aOut.AppendElement(StyleTransformOperation::Translate( 1132 LengthPercentage::FromPixels(CSSCoord(p.x)), 1133 LengthPercentage::FromPixels(CSSCoord(p.y)))); 1134 return; 1135 } 1136 case dom::SVGTransform_Binding::SVG_TRANSFORM_ROTATE: { 1137 float cx, cy; 1138 aTransform.GetRotationOrigin(cx, cy); 1139 const StyleAngle angle{aTransform.Angle()}; 1140 const bool hasOrigin = cx != 0.0f || cy != 0.0f; 1141 if (hasOrigin) { 1142 aOut.AppendElement(StyleTransformOperation::Translate( 1143 LengthPercentage::FromPixels(cx), 1144 LengthPercentage::FromPixels(cy))); 1145 } 1146 aOut.AppendElement(StyleTransformOperation::Rotate(angle)); 1147 if (hasOrigin) { 1148 aOut.AppendElement(StyleTransformOperation::Translate( 1149 LengthPercentage::FromPixels(-cx), 1150 LengthPercentage::FromPixels(-cy))); 1151 } 1152 return; 1153 } 1154 case dom::SVGTransform_Binding::SVG_TRANSFORM_SKEWX: 1155 aOut.AppendElement(StyleTransformOperation::SkewX({aTransform.Angle()})); 1156 return; 1157 case dom::SVGTransform_Binding::SVG_TRANSFORM_SKEWY: 1158 aOut.AppendElement(StyleTransformOperation::SkewY({aTransform.Angle()})); 1159 return; 1160 case dom::SVGTransform_Binding::SVG_TRANSFORM_MATRIX: { 1161 aOut.AppendElement(MatrixToTransformOperation(aTransform.GetMatrix())); 1162 return; 1163 } 1164 case dom::SVGTransform_Binding::SVG_TRANSFORM_UNKNOWN: 1165 default: 1166 MOZ_CRASH("Bad SVGTransform?"); 1167 } 1168 } 1169 1170 /* static */ 1171 bool SVGElement::UpdateDeclarationBlockFromTransform( 1172 const StyleLockedDeclarationBlock& aBlock, 1173 const SVGAnimatedTransformList* aTransform, 1174 const gfx::Matrix* aAnimateMotionTransform, ValToUse aValToUse) { 1175 MOZ_ASSERT(aTransform || aAnimateMotionTransform); 1176 AutoTArray<StyleTransformOperation, 5> operations; 1177 if (aAnimateMotionTransform) { 1178 operations.AppendElement( 1179 MatrixToTransformOperation(*aAnimateMotionTransform)); 1180 } 1181 if (aTransform) { 1182 const SVGTransformList& transforms = aValToUse == ValToUse::Anim 1183 ? aTransform->GetAnimValue() 1184 : aTransform->GetBaseValue(); 1185 // TODO: Maybe make SVGTransform use StyleTransformOperation directly? 1186 for (size_t i = 0, len = transforms.Length(); i < len; ++i) { 1187 SVGTransformToCSS(transforms[i], operations); 1188 } 1189 } 1190 Servo_DeclarationBlock_SetTransform(&aBlock, eCSSProperty_transform, 1191 &operations); 1192 return true; 1193 } 1194 1195 //------------------------------------------------------------------------ 1196 // Helper class: MappedAttrParser, for parsing values of mapped attributes 1197 1198 namespace { 1199 1200 class MOZ_STACK_CLASS MappedAttrParser { 1201 public: 1202 explicit MappedAttrParser(SVGElement& aElement, 1203 StyleLockedDeclarationBlock* aDecl) 1204 : mElement(aElement), mDecl(aDecl) { 1205 if (mDecl) { 1206 Servo_DeclarationBlock_Clear(mDecl); 1207 } 1208 } 1209 ~MappedAttrParser() { 1210 MOZ_ASSERT(!mDecl, 1211 "If mDecl was initialized, it should have been returned via " 1212 "TakeDeclarationBlock (and have its pointer cleared)"); 1213 }; 1214 1215 // Parses a mapped attribute value. 1216 void ParseMappedAttrValue(nsAtom* aMappedAttrName, 1217 const nsAString& aMappedAttrValue); 1218 1219 void TellStyleAlreadyParsedResult(nsAtom const* aAtom, 1220 SVGAnimatedLength const& aLength); 1221 void TellStyleAlreadyParsedResult(const SVGAnimatedPathSegList&); 1222 void TellStyleAlreadyParsedResult(const SVGAnimatedTransformList&); 1223 1224 // If we've parsed any values for mapped attributes, this method returns the 1225 // already_AddRefed declaration block that incorporates the parsed values. 1226 // Otherwise, this method returns null. 1227 already_AddRefed<StyleLockedDeclarationBlock> TakeDeclarationBlock() { 1228 return mDecl.forget(); 1229 } 1230 1231 StyleLockedDeclarationBlock& EnsureDeclarationBlock() { 1232 if (!mDecl) { 1233 mDecl = Servo_DeclarationBlock_CreateEmpty().Consume(); 1234 } 1235 return *mDecl; 1236 } 1237 1238 URLExtraData& EnsureExtraData() { 1239 if (!mExtraData) { 1240 mExtraData = mElement.GetURLDataForStyleAttr(); 1241 } 1242 return *mExtraData; 1243 } 1244 1245 private: 1246 // For reporting use counters 1247 SVGElement& mElement; 1248 1249 // Declaration for storing parsed values (lazily initialized). 1250 RefPtr<StyleLockedDeclarationBlock> mDecl; 1251 1252 // URL data for parsing stuff. Also lazy. 1253 RefPtr<URLExtraData> mExtraData; 1254 }; 1255 1256 void MappedAttrParser::ParseMappedAttrValue(nsAtom* aMappedAttrName, 1257 const nsAString& aMappedAttrValue) { 1258 // Get the NonCustomCSSPropertyId id for our mapped attribute. 1259 NonCustomCSSPropertyId propertyId = 1260 nsCSSProps::LookupProperty(nsAutoAtomCString(aMappedAttrName)); 1261 if (propertyId != eCSSProperty_UNKNOWN) { 1262 bool changed = false; // outparam for ParseProperty. 1263 NS_ConvertUTF16toUTF8 value(aMappedAttrValue); 1264 1265 auto* doc = mElement.OwnerDoc(); 1266 changed = Servo_DeclarationBlock_SetPropertyById( 1267 &EnsureDeclarationBlock(), propertyId, &value, false, 1268 &EnsureExtraData(), StyleParsingMode::ALLOW_UNITLESS_LENGTH, 1269 doc->GetCompatibilityMode(), &doc->EnsureCSSLoader(), 1270 StyleCssRuleType::Style, {}); 1271 1272 // TODO(emilio): If we want to record these from CSSOM more generally, we 1273 // can pass the document use counters down the FFI call. For now manually 1274 // count them. 1275 if (changed && StaticPrefs::layout_css_use_counters_enabled()) { 1276 UseCounter useCounter = nsCSSProps::UseCounterFor(propertyId); 1277 MOZ_ASSERT(useCounter != eUseCounter_UNKNOWN); 1278 doc->SetUseCounter(useCounter); 1279 } 1280 return; 1281 } 1282 MOZ_ASSERT(aMappedAttrName == nsGkAtoms::lang, 1283 "Only 'lang' should be unrecognized!"); 1284 // CSS parser doesn't know about 'lang', so we need to handle it specially. 1285 if (aMappedAttrName == nsGkAtoms::lang) { 1286 propertyId = eCSSProperty__x_lang; 1287 RefPtr<nsAtom> atom = NS_Atomize(aMappedAttrValue); 1288 Servo_DeclarationBlock_SetIdentStringValue(&EnsureDeclarationBlock(), 1289 propertyId, atom); 1290 } 1291 } 1292 1293 void MappedAttrParser::TellStyleAlreadyParsedResult( 1294 nsAtom const* aAtom, SVGAnimatedLength const& aLength) { 1295 NonCustomCSSPropertyId propertyID = 1296 nsCSSProps::LookupProperty(nsAutoAtomCString(aAtom)); 1297 SVGElement::UpdateDeclarationBlockFromLength(EnsureDeclarationBlock(), 1298 propertyID, aLength, 1299 SVGElement::ValToUse::Base); 1300 } 1301 1302 void MappedAttrParser::TellStyleAlreadyParsedResult( 1303 const SVGAnimatedPathSegList& aPath) { 1304 SVGElement::UpdateDeclarationBlockFromPath(EnsureDeclarationBlock(), aPath, 1305 SVGElement::ValToUse::Base); 1306 } 1307 1308 void MappedAttrParser::TellStyleAlreadyParsedResult( 1309 const SVGAnimatedTransformList& aTransform) { 1310 SVGElement::UpdateDeclarationBlockFromTransform(EnsureDeclarationBlock(), 1311 &aTransform, nullptr, 1312 SVGElement::ValToUse::Base); 1313 } 1314 1315 } // namespace 1316 1317 //---------------------------------------------------------------------- 1318 // Implementation Helpers: 1319 1320 void SVGElement::UpdateMappedDeclarationBlock() { 1321 MOZ_ASSERT(IsPendingMappedAttributeEvaluation()); 1322 MappedAttrParser mappedAttrParser(*this, mAttrs.GetMappedDeclarationBlock()); 1323 1324 const bool lengthAffectsStyle = 1325 SVGGeometryProperty::ElementMapsLengthsToStyle(this); 1326 bool sawTransform = false; 1327 uint32_t i = 0; 1328 while (BorrowedAttrInfo info = GetAttrInfoAt(i++)) { 1329 const nsAttrName* attrName = info.mName; 1330 if (!attrName->IsAtom()) { 1331 continue; 1332 } 1333 1334 nsAtom* nameAtom = attrName->Atom(); 1335 if (!IsAttributeMapped(nameAtom)) { 1336 continue; 1337 } 1338 1339 if (nameAtom == nsGkAtoms::lang && 1340 HasAttr(kNameSpaceID_XML, nsGkAtoms::lang)) { 1341 // xml:lang has precedence, and will get set via Gecko_GetXMLLangValue(). 1342 continue; 1343 } 1344 1345 if (lengthAffectsStyle) { 1346 auto const* length = GetAnimatedLength(nameAtom); 1347 1348 if (length && length->HasBaseVal()) { 1349 // This is an element with geometry property set via SVG attribute, 1350 // and the attribute is already successfully parsed. We want to go 1351 // through the optimized path to tell the style system the result 1352 // directly, rather than let it parse the same thing again. 1353 mappedAttrParser.TellStyleAlreadyParsedResult(nameAtom, *length); 1354 continue; 1355 } 1356 } 1357 1358 if (nameAtom == nsGkAtoms::transform || 1359 nameAtom == nsGkAtoms::patternTransform || 1360 nameAtom == nsGkAtoms::gradientTransform) { 1361 sawTransform = true; 1362 const auto* transform = GetAnimatedTransformList(); 1363 MOZ_ASSERT(GetTransformListAttrName() == nameAtom); 1364 MOZ_ASSERT(transform); 1365 // We want to go through the optimized path to tell the style system the 1366 // result directly, rather than let it parse the same thing again. 1367 mappedAttrParser.TellStyleAlreadyParsedResult(*transform); 1368 continue; 1369 } 1370 1371 if (nameAtom == nsGkAtoms::d) { 1372 const auto* path = GetAnimPathSegList(); 1373 // Note: Only SVGPathElement has d attribute. 1374 MOZ_ASSERT( 1375 path, 1376 "SVGPathElement should have the non-null SVGAnimatedPathSegList"); 1377 // The attribute should have been already successfully parsed. 1378 // We want to go through the optimized path to tell the style system 1379 // the result directly, rather than let it parse the same thing again. 1380 mappedAttrParser.TellStyleAlreadyParsedResult(*path); 1381 // Some other notes: 1382 // The syntax of CSS d property is different from SVG d attribute. 1383 // 1. CSS d proeprty accepts: none | path(<quoted string>); 1384 // 2. SVG d attribtue accepts: none | <string> 1385 // So we cannot use css parser to parse the SVG d attribute directly. 1386 // Besides, |mAttrs.AttrAt(i)| removes the quotes already, so the svg path 1387 // in |mAttrs.AttrAt(i)| would be something like `M0,0L1,1z` without the 1388 // quotes. So css tokenizer cannot recognize this as a quoted string, and 1389 // so svg_path::SVGPathData::parse() doesn't work for this. Fortunately, 1390 // we still can rely on the parsed result from 1391 // SVGElement::ParseAttribute() for d attribute. 1392 continue; 1393 } 1394 1395 nsAutoString value; 1396 info.mValue->ToString(value); 1397 mappedAttrParser.ParseMappedAttrValue(nameAtom, value); 1398 } 1399 1400 // We need to map the SVG view's transform if we haven't mapped it already. 1401 if (NodeInfo()->NameAtom() == nsGkAtoms::svg && !sawTransform) { 1402 if (const auto* transform = GetAnimatedTransformList()) { 1403 mappedAttrParser.TellStyleAlreadyParsedResult(*transform); 1404 } 1405 } 1406 1407 mAttrs.SetMappedDeclarationBlock(mappedAttrParser.TakeDeclarationBlock()); 1408 } 1409 1410 /** 1411 * Helper methods for the type-specific WillChangeXXX methods. 1412 * 1413 * This method sends out appropriate pre-change notifications so that selector 1414 * restyles (e.g. due to changes that cause |elem[attr="val"]| to start/stop 1415 * matching) work. 1416 * 1417 * The nsAttrValue returned by this method depends on whether there are 1418 * mutation event listeners listening for changes to this element's attributes. 1419 * If not, then the object returned is empty. If there are, then the 1420 * nsAttrValue returned contains a serialized copy of the attribute's value 1421 * prior to the change, and this object should be passed to the corresponding 1422 * DidChangeXXX method call (assuming a WillChangeXXX call is required for the 1423 * SVG type - see comment below). This is necessary so that the 'prevValue' 1424 * property of the mutation event that is dispatched will correctly contain the 1425 * old value. 1426 * 1427 * The reason we don't handle the old value is, it's expensive to serialize the 1428 * old value. This is especially true for list type attributes, which may be 1429 * built up via the SVG DOM resulting in a large number of Will/DidModifyXXX 1430 * calls before the script finally finishes setting the attribute. 1431 * 1432 * Note that unlike using SetParsedAttr, using Will/DidChangeXXX does NOT check 1433 * and filter out redundant changes. Before calling WillChangeXXX, the caller 1434 * should check whether the new and old values are actually the same, and skip 1435 * calling Will/DidChangeXXX if they are. 1436 * 1437 * Also note that not all SVG types use this scheme. For types that can be 1438 * represented by an nsAttrValue without pointing back to an SVG object (e.g. 1439 * enums, booleans, integers) we can simply use SetParsedAttr which will do all 1440 * of the above for us. For such types there is no matching WillChangeXXX 1441 * method, only DidChangeXXX which calls SetParsedAttr. 1442 */ 1443 void SVGElement::WillChangeValue(nsAtom* aName, 1444 const mozAutoDocUpdate& aProofOfUpdate) { 1445 const nsAttrValue* attrValue = GetParsedAttr(aName); 1446 const AttrModType modType = 1447 attrValue ? AttrModType::Modification : AttrModType::Addition; 1448 MutationObservers::NotifyAttributeWillChange(this, kNameSpaceID_None, aName, 1449 modType); 1450 1451 // This is not strictly correct--the attribute value parameter for 1452 // BeforeSetAttr should reflect the value that *will* be set but that implies 1453 // allocating, e.g. an extra SVGAnimatedLength, and isn't necessary at the 1454 // moment since no SVG elements overload BeforeSetAttr. For now we just pass 1455 // the current value. 1456 const nsAttrValue emptyAttrValue; 1457 const nsAttrValue* value = attrValue ? attrValue : &emptyAttrValue; 1458 BeforeSetAttr(kNameSpaceID_None, aName, value, kNotifyDocumentObservers); 1459 } 1460 1461 /** 1462 * Helper methods for the type-specific DidChangeXXX methods. 1463 * 1464 * aNewValue is replaced with the old value. 1465 */ 1466 void SVGElement::DidChangeValue(nsAtom* aName, nsAttrValue& aNewValue, 1467 const mozAutoDocUpdate& aProofOfUpdate) { 1468 // XXX Really, the fourth argument to SetAttrAndNotify should be null if 1469 // emptyValue does not represent the actual previous value of the attribute, 1470 // but currently SVG elements do not even use the old attribute value in 1471 // |AfterSetAttr|, so this should be ok. 1472 const AttrModType modType = 1473 aName ? AttrModType::Modification : AttrModType::Addition; 1474 const nsAttrValue emptyValue; 1475 SetAttrAndNotify(kNameSpaceID_None, aName, nullptr, &emptyValue, aNewValue, 1476 nullptr, modType, kNotifyDocumentObservers, 1477 kCallAfterSetAttr, GetComposedDoc(), aProofOfUpdate); 1478 } 1479 1480 nsAtom* SVGElement::GetEventNameForAttr(nsAtom* aAttr) { 1481 if (IsSVGElement(nsGkAtoms::svg)) { 1482 if (aAttr == nsGkAtoms::onload) return nsGkAtoms::onSVGLoad; 1483 if (aAttr == nsGkAtoms::onscroll) return nsGkAtoms::onSVGScroll; 1484 } 1485 if (aAttr == nsGkAtoms::onbegin) return nsGkAtoms::onbeginEvent; 1486 if (aAttr == nsGkAtoms::onrepeat) return nsGkAtoms::onrepeatEvent; 1487 if (aAttr == nsGkAtoms::onend) return nsGkAtoms::onendEvent; 1488 1489 return SVGElementBase::GetEventNameForAttr(aAttr); 1490 } 1491 1492 SVGViewportElement* SVGElement::GetCtx() const { 1493 return SVGContentUtils::GetNearestViewportElement(this); 1494 } 1495 1496 /* virtual */ 1497 gfxMatrix SVGElement::ChildToUserSpaceTransform() const { return {}; } 1498 1499 SVGElement::LengthAttributesInfo SVGElement::GetLengthInfo() { 1500 return LengthAttributesInfo(nullptr, nullptr, 0); 1501 } 1502 1503 void SVGElement::SetLength(nsAtom* aName, const SVGAnimatedLength& aLength) { 1504 LengthAttributesInfo lengthInfo = GetLengthInfo(); 1505 1506 for (uint32_t i = 0; i < lengthInfo.mCount; i++) { 1507 if (aName == lengthInfo.mInfos[i].mName) { 1508 lengthInfo.mValues[i] = aLength; 1509 DidAnimateLength(i); 1510 return; 1511 } 1512 } 1513 MOZ_ASSERT(false, "no length found to set"); 1514 } 1515 1516 void SVGElement::WillChangeLength(uint8_t aAttrEnum, 1517 const mozAutoDocUpdate& aProofOfUpdate) { 1518 WillChangeValue(GetLengthInfo().mInfos[aAttrEnum].mName, aProofOfUpdate); 1519 } 1520 1521 void SVGElement::DidChangeLength(uint8_t aAttrEnum, 1522 const mozAutoDocUpdate& aProofOfUpdate) { 1523 LengthAttributesInfo info = GetLengthInfo(); 1524 1525 NS_ASSERTION(info.mCount > 0, 1526 "DidChangeLength on element with no length attribs"); 1527 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range"); 1528 1529 nsAttrValue newValue; 1530 newValue.SetTo(info.mValues[aAttrEnum], nullptr); 1531 1532 DidChangeValue(info.mInfos[aAttrEnum].mName, newValue, aProofOfUpdate); 1533 } 1534 1535 void SVGElement::DidAnimateLength(uint8_t aAttrEnum) { 1536 // We need to do this here. Normally the SMIL restyle would also cause us to 1537 // do this from DidSetComputedStyle, but we don't have that guarantee if our 1538 // frame gets reconstructed. 1539 ClearAnyCachedPath(); 1540 1541 if (SVGGeometryProperty::ElementMapsLengthsToStyle(this)) { 1542 NonCustomCSSPropertyId propId = 1543 SVGGeometryProperty::AttrEnumToCSSPropId(this, aAttrEnum); 1544 1545 // We don't map use element width/height currently. We can remove this 1546 // test when we do. 1547 if (propId != eCSSProperty_UNKNOWN) { 1548 auto lengthInfo = GetLengthInfo(); 1549 if (lengthInfo.mValues[aAttrEnum].IsAnimated()) { 1550 SMILOverrideStyle()->SetSMILValue(propId, 1551 lengthInfo.mValues[aAttrEnum]); 1552 } else { 1553 SMILOverrideStyle()->ClearSMILValue(propId); 1554 } 1555 } 1556 } 1557 1558 auto info = GetLengthInfo(); 1559 DidAnimateAttribute(kNameSpaceID_None, info.mInfos[aAttrEnum].mName); 1560 } 1561 1562 SVGAnimatedLength* SVGElement::GetAnimatedLength(uint8_t aAttrEnum) { 1563 LengthAttributesInfo info = GetLengthInfo(); 1564 if (aAttrEnum < info.mCount) { 1565 return &info.mValues[aAttrEnum]; 1566 } 1567 MOZ_ASSERT_UNREACHABLE("Bad attrEnum"); 1568 return nullptr; 1569 } 1570 1571 SVGAnimatedLength* SVGElement::GetAnimatedLength(const nsAtom* aAttrName) { 1572 LengthAttributesInfo lengthInfo = GetLengthInfo(); 1573 1574 for (uint32_t i = 0; i < lengthInfo.mCount; i++) { 1575 if (aAttrName == lengthInfo.mInfos[i].mName) { 1576 return &lengthInfo.mValues[i]; 1577 } 1578 } 1579 return nullptr; 1580 } 1581 1582 void SVGElement::GetAnimatedLengthValues(float* aFirst, ...) { 1583 LengthAttributesInfo info = GetLengthInfo(); 1584 1585 NS_ASSERTION(info.mCount > 0, 1586 "GetAnimatedLengthValues on element with no length attribs"); 1587 1588 SVGElementMetrics metrics(this); 1589 1590 float* f = aFirst; 1591 uint32_t i = 0; 1592 1593 va_list args; 1594 va_start(args, aFirst); 1595 1596 while (f && i < info.mCount) { 1597 *f = info.mValues[i++].GetAnimValueWithZoom(metrics); 1598 f = va_arg(args, float*); 1599 } 1600 1601 va_end(args); 1602 } 1603 1604 SVGElement::LengthListAttributesInfo SVGElement::GetLengthListInfo() { 1605 return LengthListAttributesInfo(nullptr, nullptr, 0); 1606 } 1607 1608 void SVGElement::WillChangeLengthList(uint8_t aAttrEnum, 1609 const mozAutoDocUpdate& aProofOfUpdate) { 1610 WillChangeValue(GetLengthListInfo().mInfos[aAttrEnum].mName, aProofOfUpdate); 1611 } 1612 1613 void SVGElement::DidChangeLengthList(uint8_t aAttrEnum, 1614 const mozAutoDocUpdate& aProofOfUpdate) { 1615 LengthListAttributesInfo info = GetLengthListInfo(); 1616 1617 NS_ASSERTION(info.mCount > 0, 1618 "DidChangeLengthList on element with no length list attribs"); 1619 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range"); 1620 1621 nsAttrValue newValue; 1622 newValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr); 1623 1624 DidChangeValue(info.mInfos[aAttrEnum].mName, newValue, aProofOfUpdate); 1625 } 1626 1627 void SVGElement::GetAnimatedLengthListValues(SVGUserUnitList* aFirst, ...) { 1628 LengthListAttributesInfo info = GetLengthListInfo(); 1629 1630 NS_ASSERTION( 1631 info.mCount > 0, 1632 "GetAnimatedLengthListValues on element with no length list attribs"); 1633 1634 SVGUserUnitList* list = aFirst; 1635 uint32_t i = 0; 1636 1637 va_list args; 1638 va_start(args, aFirst); 1639 1640 while (list && i < info.mCount) { 1641 list->Init(&(info.mValues[i].GetAnimValue()), this, info.mInfos[i].mAxis); 1642 ++i; 1643 list = va_arg(args, SVGUserUnitList*); 1644 } 1645 1646 va_end(args); 1647 } 1648 1649 SVGAnimatedLengthList* SVGElement::GetAnimatedLengthList(uint8_t aAttrEnum) { 1650 LengthListAttributesInfo info = GetLengthListInfo(); 1651 if (aAttrEnum < info.mCount) { 1652 return &(info.mValues[aAttrEnum]); 1653 } 1654 MOZ_ASSERT_UNREACHABLE("Bad attrEnum"); 1655 return nullptr; 1656 } 1657 1658 SVGElement::NumberListAttributesInfo SVGElement::GetNumberListInfo() { 1659 return NumberListAttributesInfo(nullptr, nullptr, 0); 1660 } 1661 1662 void SVGElement::WillChangeNumberList(uint8_t aAttrEnum, 1663 const mozAutoDocUpdate& aProofOfUpdate) { 1664 WillChangeValue(GetNumberListInfo().mInfos[aAttrEnum].mName, aProofOfUpdate); 1665 } 1666 1667 void SVGElement::DidChangeNumberList(uint8_t aAttrEnum, 1668 const mozAutoDocUpdate& aProofOfUpdate) { 1669 NumberListAttributesInfo info = GetNumberListInfo(); 1670 1671 MOZ_ASSERT(info.mCount > 0, 1672 "DidChangeNumberList on element with no number list attribs"); 1673 MOZ_ASSERT(aAttrEnum < info.mCount, "aAttrEnum out of range"); 1674 1675 nsAttrValue newValue; 1676 newValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr); 1677 1678 DidChangeValue(info.mInfos[aAttrEnum].mName, newValue, aProofOfUpdate); 1679 } 1680 1681 SVGAnimatedNumberList* SVGElement::GetAnimatedNumberList(uint8_t aAttrEnum) { 1682 NumberListAttributesInfo info = GetNumberListInfo(); 1683 if (aAttrEnum < info.mCount) { 1684 return &(info.mValues[aAttrEnum]); 1685 } 1686 MOZ_ASSERT(false, "Bad attrEnum"); 1687 return nullptr; 1688 } 1689 1690 SVGAnimatedNumberList* SVGElement::GetAnimatedNumberList(nsAtom* aAttrName) { 1691 NumberListAttributesInfo info = GetNumberListInfo(); 1692 for (uint32_t i = 0; i < info.mCount; i++) { 1693 if (aAttrName == info.mInfos[i].mName) { 1694 return &info.mValues[i]; 1695 } 1696 } 1697 MOZ_ASSERT(false, "Bad caller"); 1698 return nullptr; 1699 } 1700 1701 void SVGElement::WillChangePointList(const mozAutoDocUpdate& aProofOfUpdate) { 1702 MOZ_ASSERT(GetPointListAttrName(), "Changing non-existent point list?"); 1703 WillChangeValue(GetPointListAttrName(), aProofOfUpdate); 1704 } 1705 1706 void SVGElement::DidChangePointList(const mozAutoDocUpdate& aProofOfUpdate) { 1707 MOZ_ASSERT(GetPointListAttrName(), "Changing non-existent point list?"); 1708 1709 nsAttrValue newValue; 1710 newValue.SetTo(GetAnimatedPointList()->GetBaseValue(), nullptr); 1711 1712 DidChangeValue(GetPointListAttrName(), newValue, aProofOfUpdate); 1713 } 1714 1715 void SVGElement::DidAnimatePointList() { 1716 MOZ_ASSERT(GetPointListAttrName(), "Animating non-existent path data?"); 1717 1718 ClearAnyCachedPath(); 1719 1720 DidAnimateAttribute(kNameSpaceID_None, GetPointListAttrName()); 1721 } 1722 1723 void SVGElement::WillChangePathSegList(const mozAutoDocUpdate& aProofOfUpdate) { 1724 MOZ_ASSERT(GetPathDataAttrName(), "Changing non-existent path seg list?"); 1725 WillChangeValue(GetPathDataAttrName(), aProofOfUpdate); 1726 } 1727 1728 void SVGElement::DidChangePathSegList(const mozAutoDocUpdate& aProofOfUpdate) { 1729 MOZ_ASSERT(GetPathDataAttrName(), "Changing non-existent path seg list?"); 1730 1731 nsAttrValue newValue; 1732 newValue.SetTo(GetAnimPathSegList()->GetBaseValue(), nullptr); 1733 1734 DidChangeValue(GetPathDataAttrName(), newValue, aProofOfUpdate); 1735 } 1736 1737 void SVGElement::DidAnimatePathSegList() { 1738 nsStaticAtom* name = GetPathDataAttrName(); 1739 MOZ_ASSERT(name, "Animating non-existent path data?"); 1740 1741 ClearAnyCachedPath(); 1742 1743 // Notify style we have to update the d property because of SMIL animation. 1744 if (name == nsGkAtoms::d) { 1745 auto* animPathSegList = GetAnimPathSegList(); 1746 if (animPathSegList->IsAnimating()) { 1747 SMILOverrideStyle()->SetSMILValue(eCSSProperty_d, *animPathSegList); 1748 } else { 1749 SMILOverrideStyle()->ClearSMILValue(eCSSProperty_d); 1750 } 1751 } 1752 1753 DidAnimateAttribute(kNameSpaceID_None, name); 1754 } 1755 1756 SVGElement::NumberAttributesInfo SVGElement::GetNumberInfo() { 1757 return NumberAttributesInfo(nullptr, nullptr, 0); 1758 } 1759 1760 void SVGElement::DidChangeNumber(uint8_t aAttrEnum) { 1761 NumberAttributesInfo info = GetNumberInfo(); 1762 1763 NS_ASSERTION(info.mCount > 0, 1764 "DidChangeNumber on element with no number attribs"); 1765 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range"); 1766 1767 nsAttrValue attrValue; 1768 attrValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr); 1769 1770 SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr, 1771 attrValue, true); 1772 } 1773 1774 void SVGElement::GetAnimatedNumberValues(float* aFirst, ...) { 1775 NumberAttributesInfo info = GetNumberInfo(); 1776 1777 NS_ASSERTION(info.mCount > 0, 1778 "GetAnimatedNumberValues on element with no number attribs"); 1779 1780 float* f = aFirst; 1781 uint32_t i = 0; 1782 1783 va_list args; 1784 va_start(args, aFirst); 1785 1786 while (f && i < info.mCount) { 1787 *f = info.mValues[i++].GetAnimValue(); 1788 f = va_arg(args, float*); 1789 } 1790 va_end(args); 1791 } 1792 1793 SVGElement::NumberPairAttributesInfo SVGElement::GetNumberPairInfo() { 1794 return NumberPairAttributesInfo(nullptr, nullptr, 0); 1795 } 1796 1797 void SVGElement::WillChangeNumberPair(uint8_t aAttrEnum) { 1798 mozAutoDocUpdate updateBatch(GetComposedDoc(), kDontNotifyDocumentObservers); 1799 WillChangeValue(GetNumberPairInfo().mInfos[aAttrEnum].mName, updateBatch); 1800 } 1801 1802 void SVGElement::DidChangeNumberPair(uint8_t aAttrEnum) { 1803 NumberPairAttributesInfo info = GetNumberPairInfo(); 1804 1805 NS_ASSERTION(info.mCount > 0, 1806 "DidChangePairNumber on element with no number pair attribs"); 1807 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range"); 1808 1809 nsAttrValue newValue; 1810 newValue.SetTo(info.mValues[aAttrEnum], nullptr); 1811 1812 mozAutoDocUpdate updateBatch(GetComposedDoc(), kNotifyDocumentObservers); 1813 DidChangeValue(info.mInfos[aAttrEnum].mName, newValue, updateBatch); 1814 } 1815 1816 SVGElement::IntegerAttributesInfo SVGElement::GetIntegerInfo() { 1817 return IntegerAttributesInfo(nullptr, nullptr, 0); 1818 } 1819 1820 void SVGElement::DidChangeInteger(uint8_t aAttrEnum) { 1821 IntegerAttributesInfo info = GetIntegerInfo(); 1822 NS_ASSERTION(info.mCount > 0, 1823 "DidChangeInteger on element with no integer attribs"); 1824 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range"); 1825 1826 nsAttrValue attrValue; 1827 attrValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr); 1828 1829 SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr, 1830 attrValue, true); 1831 } 1832 1833 void SVGElement::GetAnimatedIntegerValues(int32_t* aFirst, ...) { 1834 IntegerAttributesInfo info = GetIntegerInfo(); 1835 1836 NS_ASSERTION(info.mCount > 0, 1837 "GetAnimatedIntegerValues on element with no integer attribs"); 1838 1839 int32_t* n = aFirst; 1840 uint32_t i = 0; 1841 1842 va_list args; 1843 va_start(args, aFirst); 1844 1845 while (n && i < info.mCount) { 1846 *n = info.mValues[i++].GetAnimValue(); 1847 n = va_arg(args, int32_t*); 1848 } 1849 va_end(args); 1850 } 1851 1852 SVGElement::IntegerPairAttributesInfo SVGElement::GetIntegerPairInfo() { 1853 return IntegerPairAttributesInfo(nullptr, nullptr, 0); 1854 } 1855 1856 void SVGElement::WillChangeIntegerPair(uint8_t aAttrEnum, 1857 const mozAutoDocUpdate& aProofOfUpdate) { 1858 WillChangeValue(GetIntegerPairInfo().mInfos[aAttrEnum].mName, aProofOfUpdate); 1859 } 1860 1861 void SVGElement::DidChangeIntegerPair(uint8_t aAttrEnum, 1862 const mozAutoDocUpdate& aProofOfUpdate) { 1863 IntegerPairAttributesInfo info = GetIntegerPairInfo(); 1864 1865 NS_ASSERTION(info.mCount > 0, 1866 "DidChangeIntegerPair on element with no integer pair attribs"); 1867 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range"); 1868 1869 nsAttrValue newValue; 1870 newValue.SetTo(info.mValues[aAttrEnum], nullptr); 1871 1872 DidChangeValue(info.mInfos[aAttrEnum].mName, newValue, aProofOfUpdate); 1873 } 1874 1875 SVGElement::BooleanAttributesInfo SVGElement::GetBooleanInfo() { 1876 return BooleanAttributesInfo(nullptr, nullptr, 0); 1877 } 1878 1879 void SVGElement::DidChangeBoolean(uint8_t aAttrEnum) { 1880 BooleanAttributesInfo info = GetBooleanInfo(); 1881 1882 NS_ASSERTION(info.mCount > 0, 1883 "DidChangeBoolean on element with no boolean attribs"); 1884 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range"); 1885 1886 nsAttrValue attrValue(info.mValues[aAttrEnum].GetBaseValueAtom()); 1887 SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr, 1888 attrValue, true); 1889 } 1890 1891 SVGElement::EnumAttributesInfo SVGElement::GetEnumInfo() { 1892 return EnumAttributesInfo(nullptr, nullptr, 0); 1893 } 1894 1895 void SVGElement::DidChangeEnum(uint8_t aAttrEnum) { 1896 EnumAttributesInfo info = GetEnumInfo(); 1897 1898 NS_ASSERTION(info.mCount > 0, 1899 "DidChangeEnum on element with no enum attribs"); 1900 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range"); 1901 1902 nsAttrValue attrValue(info.mValues[aAttrEnum].GetBaseValueAtom(this)); 1903 SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr, 1904 attrValue, true); 1905 } 1906 1907 SVGAnimatedOrient* SVGElement::GetAnimatedOrient() { return nullptr; } 1908 1909 void SVGElement::WillChangeOrient(const mozAutoDocUpdate& aProofOfUpdate) { 1910 WillChangeValue(nsGkAtoms::orient, aProofOfUpdate); 1911 } 1912 1913 void SVGElement::DidChangeOrient(const mozAutoDocUpdate& aProofOfUpdate) { 1914 SVGAnimatedOrient* orient = GetAnimatedOrient(); 1915 1916 NS_ASSERTION(orient, "DidChangeOrient on element with no orient attrib"); 1917 1918 nsAttrValue newValue; 1919 newValue.SetTo(*orient, nullptr); 1920 1921 DidChangeValue(nsGkAtoms::orient, newValue, aProofOfUpdate); 1922 } 1923 1924 SVGAnimatedViewBox* SVGElement::GetAnimatedViewBox() { return nullptr; } 1925 1926 void SVGElement::WillChangeViewBox(const mozAutoDocUpdate& aProofOfUpdate) { 1927 WillChangeValue(nsGkAtoms::viewBox, aProofOfUpdate); 1928 } 1929 1930 void SVGElement::DidChangeViewBox(const mozAutoDocUpdate& aProofOfUpdate) { 1931 SVGAnimatedViewBox* viewBox = GetAnimatedViewBox(); 1932 1933 NS_ASSERTION(viewBox, "DidChangeViewBox on element with no viewBox attrib"); 1934 1935 nsAttrValue newValue; 1936 newValue.SetTo(*viewBox, nullptr); 1937 1938 DidChangeValue(nsGkAtoms::viewBox, newValue, aProofOfUpdate); 1939 } 1940 1941 SVGAnimatedPreserveAspectRatio* SVGElement::GetAnimatedPreserveAspectRatio() { 1942 return nullptr; 1943 } 1944 1945 void SVGElement::WillChangePreserveAspectRatio( 1946 const mozAutoDocUpdate& aProofOfUpdate) { 1947 WillChangeValue(nsGkAtoms::preserveAspectRatio, aProofOfUpdate); 1948 } 1949 1950 void SVGElement::DidChangePreserveAspectRatio( 1951 const mozAutoDocUpdate& aProofOfUpdate) { 1952 SVGAnimatedPreserveAspectRatio* preserveAspectRatio = 1953 GetAnimatedPreserveAspectRatio(); 1954 1955 NS_ASSERTION(preserveAspectRatio, 1956 "DidChangePreserveAspectRatio on element with no " 1957 "preserveAspectRatio attrib"); 1958 1959 nsAttrValue newValue; 1960 newValue.SetTo(*preserveAspectRatio, nullptr); 1961 1962 DidChangeValue(nsGkAtoms::preserveAspectRatio, newValue, aProofOfUpdate); 1963 } 1964 1965 void SVGElement::WillChangeTransformList( 1966 const mozAutoDocUpdate& aProofOfUpdate) { 1967 WillChangeValue(GetTransformListAttrName(), aProofOfUpdate); 1968 } 1969 1970 void SVGElement::DidChangeTransformList( 1971 const mozAutoDocUpdate& aProofOfUpdate) { 1972 MOZ_ASSERT(GetTransformListAttrName(), 1973 "Changing non-existent transform list?"); 1974 1975 // The transform attribute is being set, so we must ensure that the 1976 // SVGAnimatedTransformList is/has been allocated: 1977 nsAttrValue newValue; 1978 newValue.SetTo(GetAnimatedTransformList(DO_ALLOCATE)->GetBaseValue(), 1979 nullptr); 1980 1981 DidChangeValue(GetTransformListAttrName(), newValue, aProofOfUpdate); 1982 } 1983 1984 void SVGElement::DidAnimateTransformList() { 1985 MOZ_ASSERT(GetTransformListAttrName(), 1986 "Animating non-existent transform data?"); 1987 const auto* animTransformList = GetAnimatedTransformList(); 1988 const auto* animateMotion = GetAnimateMotionTransform(); 1989 if (animateMotion || 1990 (animTransformList && animTransformList->IsAnimating())) { 1991 SMILOverrideStyle()->SetSMILValue(eCSSProperty_transform, animTransformList, 1992 animateMotion); 1993 } else { 1994 SMILOverrideStyle()->ClearSMILValue(eCSSProperty_transform); 1995 } 1996 } 1997 1998 SVGElement::StringAttributesInfo SVGElement::GetStringInfo() { 1999 return StringAttributesInfo(nullptr, nullptr, 0); 2000 } 2001 2002 void SVGElement::GetStringBaseValue(uint8_t aAttrEnum, 2003 nsAString& aResult) const { 2004 SVGElement::StringAttributesInfo info = 2005 const_cast<SVGElement*>(this)->GetStringInfo(); 2006 2007 NS_ASSERTION(info.mCount > 0, 2008 "GetBaseValue on element with no string attribs"); 2009 2010 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range"); 2011 2012 GetAttr(info.mInfos[aAttrEnum].mNamespaceID, info.mInfos[aAttrEnum].mName, 2013 aResult); 2014 } 2015 2016 void SVGElement::SetStringBaseValue(uint8_t aAttrEnum, 2017 const nsAString& aValue) { 2018 SVGElement::StringAttributesInfo info = GetStringInfo(); 2019 2020 NS_ASSERTION(info.mCount > 0, 2021 "SetBaseValue on element with no string attribs"); 2022 2023 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range"); 2024 2025 SetAttr(info.mInfos[aAttrEnum].mNamespaceID, info.mInfos[aAttrEnum].mName, 2026 aValue, true); 2027 } 2028 2029 SVGElement::StringListAttributesInfo SVGElement::GetStringListInfo() { 2030 return StringListAttributesInfo(nullptr, nullptr, 0); 2031 } 2032 2033 void SVGElement::WillChangeStringList(bool aIsConditionalProcessingAttribute, 2034 uint8_t aAttrEnum, 2035 const mozAutoDocUpdate& aProofOfUpdate) { 2036 nsStaticAtom* name; 2037 if (aIsConditionalProcessingAttribute) { 2038 nsCOMPtr<SVGTests> tests(do_QueryInterface(this)); 2039 name = tests->GetAttrName(aAttrEnum); 2040 } else { 2041 name = GetStringListInfo().mInfos[aAttrEnum].mName; 2042 } 2043 WillChangeValue(name, aProofOfUpdate); 2044 } 2045 2046 void SVGElement::DidChangeStringList(bool aIsConditionalProcessingAttribute, 2047 uint8_t aAttrEnum, 2048 const mozAutoDocUpdate& aProofOfUpdate) { 2049 nsStaticAtom* name; 2050 nsAttrValue newValue; 2051 nsCOMPtr<SVGTests> tests; 2052 2053 if (aIsConditionalProcessingAttribute) { 2054 tests = do_QueryObject(this); 2055 name = tests->GetAttrName(aAttrEnum); 2056 tests->GetAttrValue(aAttrEnum, newValue); 2057 } else { 2058 StringListAttributesInfo info = GetStringListInfo(); 2059 2060 NS_ASSERTION(info.mCount > 0, 2061 "DidChangeStringList on element with no string list attribs"); 2062 NS_ASSERTION(aAttrEnum < info.mCount, "aAttrEnum out of range"); 2063 2064 name = info.mInfos[aAttrEnum].mName; 2065 newValue.SetTo(info.mValues[aAttrEnum], nullptr); 2066 } 2067 2068 DidChangeValue(name, newValue, aProofOfUpdate); 2069 2070 if (aIsConditionalProcessingAttribute) { 2071 tests->MaybeInvalidate(); 2072 } 2073 } 2074 2075 void SVGElement::DidAnimateAttribute(int32_t aNameSpaceID, nsAtom* aAttribute) { 2076 if (auto* frame = GetPrimaryFrame()) { 2077 frame->AttributeChanged(aNameSpaceID, aAttribute, 2078 AttrModType::Modification); 2079 SVGObserverUtils::InvalidateRenderingObservers(frame); 2080 return; 2081 } 2082 SVGObserverUtils::InvalidateDirectRenderingObservers(this); 2083 } 2084 2085 nsresult SVGElement::ReportAttributeParseFailure(Document* aDocument, 2086 nsAtom* aAttribute, 2087 const nsAString& aValue) { 2088 AutoTArray<nsString, 2> strings; 2089 strings.AppendElement(nsDependentAtomString(aAttribute)); 2090 strings.AppendElement(aValue); 2091 return SVGContentUtils::ReportToConsole(aDocument, "AttributeParseWarning", 2092 strings); 2093 } 2094 2095 UniquePtr<SMILAttr> SVGElement::GetAnimatedAttr(int32_t aNamespaceID, 2096 nsAtom* aName) { 2097 if (aNamespaceID == kNameSpaceID_None) { 2098 // Transforms: 2099 if (GetTransformListAttrName() == aName) { 2100 // The transform attribute is being animated, so we must ensure that the 2101 // SVGAnimatedTransformList is/has been allocated: 2102 return GetAnimatedTransformList(DO_ALLOCATE)->ToSMILAttr(this); 2103 } 2104 2105 // Motion (fake 'attribute' for animateMotion) 2106 if (aName == nsGkAtoms::mozAnimateMotionDummyAttr) { 2107 return MakeUnique<SVGMotionSMILAttr>(this); 2108 } 2109 2110 // Lengths: 2111 LengthAttributesInfo info = GetLengthInfo(); 2112 for (uint32_t i = 0; i < info.mCount; i++) { 2113 if (aName == info.mInfos[i].mName) { 2114 return info.mValues[i].ToSMILAttr(this); 2115 } 2116 } 2117 2118 // Numbers: 2119 { 2120 NumberAttributesInfo info = GetNumberInfo(); 2121 for (uint32_t i = 0; i < info.mCount; i++) { 2122 if (aName == info.mInfos[i].mName) { 2123 return info.mValues[i].ToSMILAttr(this); 2124 } 2125 } 2126 } 2127 2128 // Number Pairs: 2129 { 2130 NumberPairAttributesInfo info = GetNumberPairInfo(); 2131 for (uint32_t i = 0; i < info.mCount; i++) { 2132 if (aName == info.mInfos[i].mName) { 2133 return info.mValues[i].ToSMILAttr(this); 2134 } 2135 } 2136 } 2137 2138 // Integers: 2139 { 2140 IntegerAttributesInfo info = GetIntegerInfo(); 2141 for (uint32_t i = 0; i < info.mCount; i++) { 2142 if (aName == info.mInfos[i].mName) { 2143 return info.mValues[i].ToSMILAttr(this); 2144 } 2145 } 2146 } 2147 2148 // Integer Pairs: 2149 { 2150 IntegerPairAttributesInfo info = GetIntegerPairInfo(); 2151 for (uint32_t i = 0; i < info.mCount; i++) { 2152 if (aName == info.mInfos[i].mName) { 2153 return info.mValues[i].ToSMILAttr(this); 2154 } 2155 } 2156 } 2157 2158 // Enumerations: 2159 { 2160 EnumAttributesInfo info = GetEnumInfo(); 2161 for (uint32_t i = 0; i < info.mCount; i++) { 2162 if (aName == info.mInfos[i].mName) { 2163 return info.mValues[i].ToSMILAttr(this); 2164 } 2165 } 2166 } 2167 2168 // Booleans: 2169 { 2170 BooleanAttributesInfo info = GetBooleanInfo(); 2171 for (uint32_t i = 0; i < info.mCount; i++) { 2172 if (aName == info.mInfos[i].mName) { 2173 return info.mValues[i].ToSMILAttr(this); 2174 } 2175 } 2176 } 2177 2178 // orient: 2179 if (aName == nsGkAtoms::orient) { 2180 SVGAnimatedOrient* orient = GetAnimatedOrient(); 2181 return orient ? orient->ToSMILAttr(this) : nullptr; 2182 } 2183 2184 // viewBox: 2185 if (aName == nsGkAtoms::viewBox) { 2186 SVGAnimatedViewBox* viewBox = GetAnimatedViewBox(); 2187 return viewBox ? viewBox->ToSMILAttr(this) : nullptr; 2188 } 2189 2190 // preserveAspectRatio: 2191 if (aName == nsGkAtoms::preserveAspectRatio) { 2192 SVGAnimatedPreserveAspectRatio* preserveAspectRatio = 2193 GetAnimatedPreserveAspectRatio(); 2194 return preserveAspectRatio ? preserveAspectRatio->ToSMILAttr(this) 2195 : nullptr; 2196 } 2197 2198 // NumberLists: 2199 { 2200 NumberListAttributesInfo info = GetNumberListInfo(); 2201 for (uint32_t i = 0; i < info.mCount; i++) { 2202 if (aName == info.mInfos[i].mName) { 2203 MOZ_ASSERT(i <= UCHAR_MAX, "Too many attributes"); 2204 return info.mValues[i].ToSMILAttr(this, uint8_t(i)); 2205 } 2206 } 2207 } 2208 2209 // LengthLists: 2210 { 2211 LengthListAttributesInfo info = GetLengthListInfo(); 2212 for (uint32_t i = 0; i < info.mCount; i++) { 2213 if (aName == info.mInfos[i].mName) { 2214 MOZ_ASSERT(i <= UCHAR_MAX, "Too many attributes"); 2215 return info.mValues[i].ToSMILAttr(this, uint8_t(i), 2216 info.mInfos[i].mAxis, 2217 info.mInfos[i].mCouldZeroPadList); 2218 } 2219 } 2220 } 2221 2222 // PointLists: 2223 { 2224 if (GetPointListAttrName() == aName) { 2225 SVGAnimatedPointList* pointList = GetAnimatedPointList(); 2226 if (pointList) { 2227 return pointList->ToSMILAttr(this); 2228 } 2229 } 2230 } 2231 2232 // PathSegLists: 2233 { 2234 if (GetPathDataAttrName() == aName) { 2235 SVGAnimatedPathSegList* segList = GetAnimPathSegList(); 2236 if (segList) { 2237 return segList->ToSMILAttr(this); 2238 } 2239 } 2240 } 2241 2242 if (aName == nsGkAtoms::_class) { 2243 return mClassAttribute.ToSMILAttr(this); 2244 } 2245 } 2246 2247 // Strings 2248 { 2249 StringAttributesInfo info = GetStringInfo(); 2250 for (uint32_t i = 0; i < info.mCount; i++) { 2251 if (aNamespaceID == info.mInfos[i].mNamespaceID && 2252 aName == info.mInfos[i].mName) { 2253 return info.mValues[i].ToSMILAttr(this); 2254 } 2255 } 2256 } 2257 2258 return nullptr; 2259 } 2260 2261 void SVGElement::AnimationNeedsResample() { 2262 Document* doc = GetComposedDoc(); 2263 if (doc && doc->HasAnimationController()) { 2264 doc->GetAnimationController()->SetResampleNeeded(); 2265 } 2266 } 2267 2268 void SVGElement::FlushAnimations() { 2269 Document* doc = GetComposedDoc(); 2270 if (doc && doc->HasAnimationController()) { 2271 doc->GetAnimationController()->FlushResampleRequests(); 2272 } 2273 } 2274 2275 void SVGElement::AddSizeOfExcludingThis(nsWindowSizes& aSizes, 2276 size_t* aNodeSize) const { 2277 Element::AddSizeOfExcludingThis(aSizes, aNodeSize); 2278 } 2279 2280 } // namespace mozilla::dom