Bug 1867190 - Initialise the PHC allocate delay later r=glandium
[gecko.git] / dom / svg / SVGUseElement.cpp
blob5e671fc802acb3ee093c817c87cb6a48a5f1f09a
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/. */
7 #include "mozilla/dom/SVGUseElement.h"
9 #include "mozilla/ArrayUtils.h"
10 #include "mozilla/ErrorResult.h"
11 #include "mozilla/ScopeExit.h"
12 #include "mozilla/StaticPrefs_svg.h"
13 #include "mozilla/SVGObserverUtils.h"
14 #include "mozilla/SVGUseFrame.h"
15 #include "mozilla/URLExtraData.h"
16 #include "mozilla/dom/Document.h"
17 #include "mozilla/dom/ReferrerInfo.h"
18 #include "mozilla/dom/ShadowIncludingTreeIterator.h"
19 #include "mozilla/dom/SVGGraphicsElement.h"
20 #include "mozilla/dom/SVGLengthBinding.h"
21 #include "mozilla/dom/SVGSVGElement.h"
22 #include "mozilla/dom/SVGSymbolElement.h"
23 #include "mozilla/dom/SVGUseElementBinding.h"
24 #include "nsGkAtoms.h"
25 #include "nsContentUtils.h"
26 #include "nsIReferrerInfo.h"
27 #include "nsIURI.h"
28 #include "SVGGeometryProperty.h"
30 NS_IMPL_NS_NEW_SVG_ELEMENT(Use)
32 namespace mozilla::dom {
34 JSObject* SVGUseElement::WrapNode(JSContext* aCx,
35 JS::Handle<JSObject*> aGivenProto) {
36 return SVGUseElement_Binding::Wrap(aCx, this, aGivenProto);
39 ////////////////////////////////////////////////////////////////////////
40 // implementation
42 SVGElement::LengthInfo SVGUseElement::sLengthInfo[4] = {
43 {nsGkAtoms::x, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER,
44 SVGContentUtils::X},
45 {nsGkAtoms::y, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER,
46 SVGContentUtils::Y},
47 {nsGkAtoms::width, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER,
48 SVGContentUtils::X},
49 {nsGkAtoms::height, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER,
50 SVGContentUtils::Y},
53 SVGElement::StringInfo SVGUseElement::sStringInfo[2] = {
54 {nsGkAtoms::href, kNameSpaceID_None, true},
55 {nsGkAtoms::href, kNameSpaceID_XLink, true}};
57 //----------------------------------------------------------------------
58 // nsISupports methods
60 NS_IMPL_CYCLE_COLLECTION_CLASS(SVGUseElement)
62 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SVGUseElement,
63 SVGUseElementBase)
64 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOriginal)
65 tmp->UnlinkSource();
66 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
67 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SVGUseElement,
68 SVGUseElementBase)
69 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginal)
70 tmp->mReferencedElementTracker.Traverse(&cb);
71 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
73 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(SVGUseElement, SVGUseElementBase,
74 nsIMutationObserver)
76 //----------------------------------------------------------------------
77 // Implementation
79 SVGUseElement::SVGUseElement(
80 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
81 : SVGUseElementBase(std::move(aNodeInfo)), mReferencedElementTracker(this) {
82 SetEnabledCallbacks(kCharacterDataChanged | kAttributeChanged |
83 kContentAppended | kContentInserted | kContentRemoved |
84 kNodeWillBeDestroyed);
87 SVGUseElement::~SVGUseElement() {
88 UnlinkSource();
89 MOZ_DIAGNOSTIC_ASSERT(!OwnerDoc()->SVGUseElementNeedsShadowTreeUpdate(*this),
90 "Dying without unbinding?");
93 namespace SVGT = SVGGeometryProperty::Tags;
95 //----------------------------------------------------------------------
96 // nsINode methods
98 void SVGUseElement::ProcessAttributeChange(int32_t aNamespaceID,
99 nsAtom* aAttribute) {
100 if (OwnerDoc()->CloningForSVGUse()) {
101 return;
103 if (aNamespaceID == kNameSpaceID_None) {
104 if (aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height) {
105 const bool hadValidDimensions = HasValidDimensions();
106 const bool isUsed = OurWidthAndHeightAreUsed();
107 if (isUsed) {
108 SyncWidthOrHeight(aAttribute);
111 if (auto* frame = GetFrame()) {
112 frame->DimensionAttributeChanged(hadValidDimensions, isUsed);
117 if ((aNamespaceID == kNameSpaceID_XLink ||
118 aNamespaceID == kNameSpaceID_None) &&
119 aAttribute == nsGkAtoms::href) {
120 // We're changing our nature, clear out the clone information.
121 if (auto* frame = GetFrame()) {
122 frame->HrefChanged();
124 UnlinkSource();
125 TriggerReclone();
129 void SVGUseElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aAttribute,
130 const nsAttrValue* aValue,
131 const nsAttrValue* aOldValue,
132 nsIPrincipal* aSubjectPrincipal,
133 bool aNotify) {
134 ProcessAttributeChange(aNamespaceID, aAttribute);
135 return SVGUseElementBase::AfterSetAttr(aNamespaceID, aAttribute, aValue,
136 aOldValue, aSubjectPrincipal, aNotify);
139 nsresult SVGUseElement::Clone(dom::NodeInfo* aNodeInfo,
140 nsINode** aResult) const {
141 *aResult = nullptr;
142 SVGUseElement* it =
143 new (aNodeInfo->NodeInfoManager()) SVGUseElement(do_AddRef(aNodeInfo));
145 nsCOMPtr<nsINode> kungFuDeathGrip(it);
146 nsresult rv1 = it->Init();
147 nsresult rv2 = const_cast<SVGUseElement*>(this)->CopyInnerTo(it);
149 if (aNodeInfo->GetDocument()->CloningForSVGUse()) {
150 // SVGUseElement specific portion - record who we cloned from
151 it->mOriginal = const_cast<SVGUseElement*>(this);
154 if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) {
155 kungFuDeathGrip.swap(*aResult);
158 return NS_FAILED(rv1) ? rv1 : rv2;
161 nsresult SVGUseElement::BindToTree(BindContext& aContext, nsINode& aParent) {
162 nsresult rv = SVGUseElementBase::BindToTree(aContext, aParent);
163 NS_ENSURE_SUCCESS(rv, rv);
165 TriggerReclone();
166 return NS_OK;
169 void SVGUseElement::UnbindFromTree(bool aNullParent) {
170 SVGUseElementBase::UnbindFromTree(aNullParent);
171 OwnerDoc()->UnscheduleSVGUseElementShadowTreeUpdate(*this);
174 already_AddRefed<DOMSVGAnimatedString> SVGUseElement::Href() {
175 return mStringAttributes[HREF].IsExplicitlySet()
176 ? mStringAttributes[HREF].ToDOMAnimatedString(this)
177 : mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this);
180 //----------------------------------------------------------------------
182 already_AddRefed<DOMSVGAnimatedLength> SVGUseElement::X() {
183 return mLengthAttributes[ATTR_X].ToDOMAnimatedLength(this);
186 already_AddRefed<DOMSVGAnimatedLength> SVGUseElement::Y() {
187 return mLengthAttributes[ATTR_Y].ToDOMAnimatedLength(this);
190 already_AddRefed<DOMSVGAnimatedLength> SVGUseElement::Width() {
191 return mLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this);
194 already_AddRefed<DOMSVGAnimatedLength> SVGUseElement::Height() {
195 return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this);
198 //----------------------------------------------------------------------
199 // nsIMutationObserver methods
201 void SVGUseElement::CharacterDataChanged(nsIContent* aContent,
202 const CharacterDataChangeInfo&) {
203 if (nsContentUtils::IsInSameAnonymousTree(mReferencedElementTracker.get(),
204 aContent)) {
205 TriggerReclone();
209 void SVGUseElement::AttributeChanged(Element* aElement, int32_t aNamespaceID,
210 nsAtom* aAttribute, int32_t aModType,
211 const nsAttrValue* aOldValue) {
212 if (nsContentUtils::IsInSameAnonymousTree(mReferencedElementTracker.get(),
213 aElement)) {
214 TriggerReclone();
218 void SVGUseElement::ContentAppended(nsIContent* aFirstNewContent) {
219 // FIXME(emilio, bug 1442336): Why does this check the parent but
220 // ContentInserted the child?
221 if (nsContentUtils::IsInSameAnonymousTree(mReferencedElementTracker.get(),
222 aFirstNewContent->GetParent())) {
223 TriggerReclone();
227 void SVGUseElement::ContentInserted(nsIContent* aChild) {
228 // FIXME(emilio, bug 1442336): Why does this check the child but
229 // ContentAppended the parent?
230 if (nsContentUtils::IsInSameAnonymousTree(mReferencedElementTracker.get(),
231 aChild)) {
232 TriggerReclone();
236 void SVGUseElement::ContentRemoved(nsIContent* aChild,
237 nsIContent* aPreviousSibling) {
238 if (nsContentUtils::IsInSameAnonymousTree(mReferencedElementTracker.get(),
239 aChild)) {
240 TriggerReclone();
244 void SVGUseElement::NodeWillBeDestroyed(nsINode* aNode) {
245 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
246 UnlinkSource();
249 // Returns whether this node could ever be displayed.
250 static bool NodeCouldBeRendered(const nsINode& aNode) {
251 if (const auto* symbol = SVGSymbolElement::FromNode(aNode)) {
252 return symbol->CouldBeRendered();
254 // TODO: Do we have other cases we can optimize out easily?
255 return true;
258 // <svg:use> can be used (no pun intended) to trivially cause an explosion of
259 // clones that could potentially DoS the browser. We have a configurable limit
260 // to control this.
261 static bool IsTooMuchRecursion(uint32_t aCount) {
262 switch (StaticPrefs::svg_use_element_recursive_clone_limit_enabled()) {
263 case 0:
264 return false;
265 case 1:
266 break;
267 default:
268 if (!XRE_IsParentProcess()) {
269 return false;
271 break;
273 return aCount >= StaticPrefs::svg_use_element_recursive_clone_limit();
276 // Circular loop detection, plus detection of whether this shadow tree is
277 // rendered at all.
278 auto SVGUseElement::ScanAncestors(const Element& aTarget) const -> ScanResult {
279 uint32_t count = 0;
280 return ScanAncestorsInternal(aTarget, count);
283 auto SVGUseElement::ScanAncestorsInternal(const Element& aTarget,
284 uint32_t& aCount) const
285 -> ScanResult {
286 if (&aTarget == this) {
287 return ScanResult::CyclicReference;
289 if (mOriginal) {
290 if (IsTooMuchRecursion(++aCount)) {
291 return ScanResult::TooDeep;
293 auto result = mOriginal->ScanAncestorsInternal(aTarget, aCount);
294 switch (result) {
295 case ScanResult::TooDeep:
296 case ScanResult::CyclicReference:
297 return result;
298 case ScanResult::Ok:
299 case ScanResult::Invisible:
300 break;
304 auto result = ScanResult::Ok;
305 for (nsINode* parent = GetParentOrShadowHostNode(); parent;
306 parent = parent->GetParentOrShadowHostNode()) {
307 if (parent == &aTarget) {
308 return ScanResult::CyclicReference;
310 if (auto* use = SVGUseElement::FromNode(*parent)) {
311 if (IsTooMuchRecursion(++aCount)) {
312 return ScanResult::TooDeep;
314 if (mOriginal && use->mOriginal == mOriginal) {
315 return ScanResult::CyclicReference;
318 // Do we have other similar cases we can optimize out easily?
319 if (!NodeCouldBeRendered(*parent)) {
320 // NOTE(emilio): We can't just return here. If we're cyclic, we need to
321 // know.
322 result = ScanResult::Invisible;
325 return result;
328 //----------------------------------------------------------------------
330 static bool IsForbiddenUseNode(const nsINode& aNode) {
331 if (!aNode.IsElement()) {
332 return false;
334 const auto* svg = SVGElement::FromNode(aNode);
335 return !svg || !svg->IsSVGGraphicsElement();
338 static void CollectForbiddenNodes(Element& aRoot,
339 nsTArray<RefPtr<nsINode>>& aNodes) {
340 auto iter = dom::ShadowIncludingTreeIterator(aRoot);
341 while (iter) {
342 nsINode* node = *iter;
343 if (IsForbiddenUseNode(*node)) {
344 aNodes.AppendElement(node);
345 iter.SkipChildren();
346 continue;
348 ++iter;
352 // SVG1 restricted <use> trees to SVGGraphicsElements.
353 // https://www.w3.org/TR/SVG11/struct.html#UseElement:
355 // Any ‘svg’, ‘symbol’, ‘g’, graphics element or other ‘use’ is potentially a
356 // template object that can be re-used (i.e., "instanced") in the SVG
357 // document via a ‘use’ element. The ‘use’ element references another element
358 // and indicates that the graphical contents of that element is
359 // included/drawn at that given point in the document.
361 // SVG2 doesn't have that same restriction.
362 // https://www.w3.org/TR/SVG2/struct.html#UseShadowTree:
364 // Previous versions of SVG restricted the contents of the shadow tree to SVG
365 // graphics elements. This specification allows any valid SVG document
366 // subtree to be cloned. Cloning non-graphical content, however, will not
367 // usually have any visible effect.
369 // But it's pretty ambiguous as to what the behavior should be for some
370 // elements, because <script> is inert, but <iframe> is not, see:
371 // https://github.com/w3c/svgwg/issues/876
373 // So, fairly confusing, all-in-all.
374 static void RemoveForbiddenNodes(Element& aRoot, bool aIsCrossDocument) {
375 switch (StaticPrefs::svg_use_element_graphics_element_restrictions()) {
376 case 0:
377 return;
378 case 1:
379 if (!aIsCrossDocument) {
380 return;
382 break;
383 default:
384 break;
387 AutoTArray<RefPtr<nsINode>, 10> unsafeNodes;
388 CollectForbiddenNodes(aRoot, unsafeNodes);
389 for (auto& unsafeNode : unsafeNodes) {
390 unsafeNode->Remove();
394 void SVGUseElement::UpdateShadowTree() {
395 MOZ_ASSERT(IsInComposedDoc());
397 if (mReferencedElementTracker.get()) {
398 mReferencedElementTracker.get()->RemoveMutationObserver(this);
401 LookupHref();
403 RefPtr<ShadowRoot> shadow = GetShadowRoot();
404 if (!shadow) {
405 shadow = AttachShadowWithoutNameChecks(ShadowRootMode::Closed);
407 MOZ_ASSERT(shadow);
409 auto* targetElement =
410 SVGGraphicsElement::FromNodeOrNull(mReferencedElementTracker.get());
411 RefPtr<Element> newElement;
413 auto UpdateShadowTree = mozilla::MakeScopeExit([&]() {
414 if (nsIContent* firstChild = shadow->GetFirstChild()) {
415 MOZ_ASSERT(!firstChild->GetNextSibling());
416 shadow->RemoveChildNode(firstChild, /* aNotify = */ true);
419 if (newElement) {
420 shadow->AppendChildTo(newElement, /* aNotify = */ true, IgnoreErrors());
424 // make sure target is valid type for <use>
425 if (!targetElement) {
426 return;
429 if (ScanAncestors(*targetElement) != ScanResult::Ok) {
430 return;
433 nsCOMPtr<nsIURI> baseURI = targetElement->GetBaseURI();
434 if (!baseURI) {
435 return;
439 const bool isCrossDocument = targetElement->OwnerDoc() != OwnerDoc();
441 nsNodeInfoManager* nodeInfoManager =
442 isCrossDocument ? OwnerDoc()->NodeInfoManager() : nullptr;
444 nsCOMPtr<nsINode> newNode =
445 targetElement->Clone(true, nodeInfoManager, IgnoreErrors());
446 if (!newNode) {
447 return;
450 MOZ_ASSERT(newNode->IsElement());
451 newElement = newNode.forget().downcast<Element>();
452 RemoveForbiddenNodes(*newElement, isCrossDocument);
455 if (newElement->IsAnyOfSVGElements(nsGkAtoms::svg, nsGkAtoms::symbol)) {
456 auto* newSVGElement = static_cast<SVGElement*>(newElement.get());
457 if (mLengthAttributes[ATTR_WIDTH].IsExplicitlySet())
458 newSVGElement->SetLength(nsGkAtoms::width, mLengthAttributes[ATTR_WIDTH]);
459 if (mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet())
460 newSVGElement->SetLength(nsGkAtoms::height,
461 mLengthAttributes[ATTR_HEIGHT]);
464 // Bug 1415044 the specs do not say which referrer information we should use.
465 // This may change if there's any spec comes out.
466 auto referrerInfo = MakeRefPtr<ReferrerInfo>(*this);
467 mContentURLData = new URLExtraData(baseURI.forget(), referrerInfo.forget(),
468 do_AddRef(NodePrincipal()));
470 targetElement->AddMutationObserver(this);
473 nsIURI* SVGUseElement::GetSourceDocURI() {
474 nsIContent* targetElement = mReferencedElementTracker.get();
475 if (!targetElement) {
476 return nullptr;
479 return targetElement->OwnerDoc()->GetDocumentURI();
482 const Encoding* SVGUseElement::GetSourceDocCharacterSet() {
483 nsIContent* targetElement = mReferencedElementTracker.get();
484 if (!targetElement) {
485 return nullptr;
488 return targetElement->OwnerDoc()->GetDocumentCharacterSet();
491 static nsINode* GetClonedChild(const SVGUseElement& aUseElement) {
492 const ShadowRoot* shadow = aUseElement.GetShadowRoot();
493 return shadow ? shadow->GetFirstChild() : nullptr;
496 bool SVGUseElement::OurWidthAndHeightAreUsed() const {
497 nsINode* clonedChild = GetClonedChild(*this);
498 return clonedChild &&
499 clonedChild->IsAnyOfSVGElements(nsGkAtoms::svg, nsGkAtoms::symbol);
502 //----------------------------------------------------------------------
503 // implementation helpers
505 void SVGUseElement::SyncWidthOrHeight(nsAtom* aName) {
506 NS_ASSERTION(aName == nsGkAtoms::width || aName == nsGkAtoms::height,
507 "The clue is in the function name");
508 NS_ASSERTION(OurWidthAndHeightAreUsed(), "Don't call this");
510 if (!OurWidthAndHeightAreUsed()) {
511 return;
514 auto* target = SVGElement::FromNode(GetClonedChild(*this));
515 uint32_t index =
516 sLengthInfo[ATTR_WIDTH].mName == aName ? ATTR_WIDTH : ATTR_HEIGHT;
518 if (mLengthAttributes[index].IsExplicitlySet()) {
519 target->SetLength(aName, mLengthAttributes[index]);
520 return;
522 if (target->IsSVGElement(nsGkAtoms::svg)) {
523 // Our width/height attribute is now no longer explicitly set, so we
524 // need to revert the clone's width/height to the width/height of the
525 // content that's being cloned.
526 TriggerReclone();
527 return;
529 // Our width/height attribute is now no longer explicitly set, so we
530 // need to set the value to 100%
531 SVGAnimatedLength length;
532 length.Init(SVGContentUtils::XY, 0xff, 100,
533 SVGLength_Binding::SVG_LENGTHTYPE_PERCENTAGE);
534 target->SetLength(aName, length);
537 void SVGUseElement::LookupHref() {
538 nsAutoString href;
539 if (mStringAttributes[HREF].IsExplicitlySet()) {
540 mStringAttributes[HREF].GetAnimValue(href, this);
541 } else {
542 mStringAttributes[XLINK_HREF].GetAnimValue(href, this);
545 if (href.IsEmpty()) {
546 return;
549 Element* treeToWatch = mOriginal ? mOriginal.get() : this;
550 if (nsContentUtils::IsLocalRefURL(href)) {
551 mReferencedElementTracker.ResetWithLocalRef(*treeToWatch, href);
552 return;
555 nsCOMPtr<nsIURI> baseURI = treeToWatch->GetBaseURI();
556 nsCOMPtr<nsIURI> targetURI;
557 nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
558 GetComposedDoc(), baseURI);
559 if (!targetURI) {
560 return;
563 // Don't allow <use href="data:...">. Using "#ref" inside a data: document is
564 // handled above.
565 if (targetURI->SchemeIs("data") &&
566 !StaticPrefs::svg_use_element_data_url_href_allowed()) {
567 return;
570 nsIReferrerInfo* referrer =
571 OwnerDoc()->ReferrerInfoForInternalCSSAndSVGResources();
572 mReferencedElementTracker.ResetToURIFragmentID(treeToWatch, targetURI,
573 referrer);
576 void SVGUseElement::TriggerReclone() {
577 if (Document* doc = GetComposedDoc()) {
578 doc->ScheduleSVGUseElementShadowTreeUpdate(*this);
582 void SVGUseElement::UnlinkSource() {
583 if (mReferencedElementTracker.get()) {
584 mReferencedElementTracker.get()->RemoveMutationObserver(this);
586 mReferencedElementTracker.Unlink();
589 //----------------------------------------------------------------------
590 // SVGElement methods
592 /* virtual */
593 gfxMatrix SVGUseElement::PrependLocalTransformsTo(
594 const gfxMatrix& aMatrix, SVGTransformTypes aWhich) const {
595 // 'transform' attribute:
596 gfxMatrix userToParent;
598 if (aWhich == eUserSpaceToParent || aWhich == eAllTransforms) {
599 userToParent = GetUserToParentTransform(mAnimateMotionTransform.get(),
600 mTransforms.get());
601 if (aWhich == eUserSpaceToParent) {
602 return userToParent * aMatrix;
606 // our 'x' and 'y' attributes:
607 float x, y;
608 if (!SVGGeometryProperty::ResolveAll<SVGT::X, SVGT::Y>(this, &x, &y)) {
609 const_cast<SVGUseElement*>(this)->GetAnimatedLengthValues(&x, &y, nullptr);
612 gfxMatrix childToUser = gfxMatrix::Translation(x, y);
614 if (aWhich == eAllTransforms) {
615 return childToUser * userToParent * aMatrix;
618 MOZ_ASSERT(aWhich == eChildToUserSpace, "Unknown TransformTypes");
620 // The following may look broken because pre-multiplying our eChildToUserSpace
621 // transform with another matrix without including our eUserSpaceToParent
622 // transform between the two wouldn't make sense. We don't expect that to
623 // ever happen though. We get here either when the identity matrix has been
624 // passed because our caller just wants our eChildToUserSpace transform, or
625 // when our eUserSpaceToParent transform has already been multiplied into the
626 // matrix that our caller passes (such as when we're called from PaintSVG).
627 return childToUser * aMatrix;
630 /* virtual */
631 bool SVGUseElement::HasValidDimensions() const {
632 if (!OurWidthAndHeightAreUsed()) {
633 return true;
636 return (!mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() ||
637 mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0) &&
638 (!mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() ||
639 mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0);
642 SVGElement::LengthAttributesInfo SVGUseElement::GetLengthInfo() {
643 return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
644 ArrayLength(sLengthInfo));
647 SVGElement::StringAttributesInfo SVGUseElement::GetStringInfo() {
648 return StringAttributesInfo(mStringAttributes, sStringInfo,
649 ArrayLength(sStringInfo));
652 SVGUseFrame* SVGUseElement::GetFrame() const {
653 nsIFrame* frame = GetPrimaryFrame();
654 // We might be a plain SVGContainerFrame if we didn't pass the conditional
655 // processing checks.
656 if (!frame || !frame->IsSVGUseFrame()) {
657 MOZ_ASSERT_IF(frame, frame->Type() == LayoutFrameType::None);
658 return nullptr;
660 return static_cast<SVGUseFrame*>(frame);
663 //----------------------------------------------------------------------
664 // nsIContent methods
666 NS_IMETHODIMP_(bool)
667 SVGUseElement::IsAttributeMapped(const nsAtom* name) const {
668 return name == nsGkAtoms::x || name == nsGkAtoms::y ||
669 SVGUseElementBase::IsAttributeMapped(name);
672 nsCSSPropertyID SVGUseElement::GetCSSPropertyIdForAttrEnum(uint8_t aAttrEnum) {
673 switch (aAttrEnum) {
674 case ATTR_X:
675 return eCSSProperty_x;
676 case ATTR_Y:
677 return eCSSProperty_y;
678 default:
679 // Currently we don't map width or height to style
680 return eCSSProperty_UNKNOWN;
684 } // namespace mozilla::dom