tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SVGUseElement.cpp (22264B)


      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/SVGUseElement.h"
      8 
      9 #include "SVGGeometryProperty.h"
     10 #include "mozilla/ErrorResult.h"
     11 #include "mozilla/SVGObserverUtils.h"
     12 #include "mozilla/SVGUseFrame.h"
     13 #include "mozilla/ScopeExit.h"
     14 #include "mozilla/StaticPrefs_svg.h"
     15 #include "mozilla/URLExtraData.h"
     16 #include "mozilla/dom/Document.h"
     17 #include "mozilla/dom/ReferrerInfo.h"
     18 #include "mozilla/dom/SVGGraphicsElement.h"
     19 #include "mozilla/dom/SVGLengthBinding.h"
     20 #include "mozilla/dom/SVGSVGElement.h"
     21 #include "mozilla/dom/SVGSwitchElement.h"
     22 #include "mozilla/dom/SVGSymbolElement.h"
     23 #include "mozilla/dom/SVGUseElementBinding.h"
     24 #include "mozilla/dom/ShadowIncludingTreeIterator.h"
     25 #include "nsContentUtils.h"
     26 #include "nsGkAtoms.h"
     27 #include "nsIReferrerInfo.h"
     28 #include "nsIURI.h"
     29 
     30 NS_IMPL_NS_NEW_SVG_ELEMENT(Use)
     31 
     32 namespace mozilla::dom {
     33 
     34 JSObject* SVGUseElement::WrapNode(JSContext* aCx,
     35                                  JS::Handle<JSObject*> aGivenProto) {
     36  return SVGUseElement_Binding::Wrap(aCx, this, aGivenProto);
     37 }
     38 
     39 ////////////////////////////////////////////////////////////////////////
     40 // implementation
     41 
     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},
     51 };
     52 
     53 SVGElement::StringInfo SVGUseElement::sStringInfo[2] = {
     54    {nsGkAtoms::href, kNameSpaceID_None, true},
     55    {nsGkAtoms::href, kNameSpaceID_XLink, true}};
     56 
     57 //----------------------------------------------------------------------
     58 // nsISupports methods
     59 
     60 NS_IMPL_CYCLE_COLLECTION_CLASS(SVGUseElement)
     61 
     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
     72 
     73 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(SVGUseElement, SVGUseElementBase,
     74                                             nsIMutationObserver)
     75 
     76 //----------------------------------------------------------------------
     77 // Implementation
     78 
     79 SVGUseElement::SVGUseElement(
     80    already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
     81    : SVGUseElementBase(std::move(aNodeInfo)), mReferencedElementTracker(this) {
     82  SetEnabledCallbacks(kCharacterDataChanged | kAttributeChanged |
     83                      kContentAppended | kContentInserted |
     84                      kContentWillBeRemoved | kNodeWillBeDestroyed);
     85 }
     86 
     87 SVGUseElement::~SVGUseElement() {
     88  UnlinkSource();
     89  MOZ_DIAGNOSTIC_ASSERT(!OwnerDoc()->SVGUseElementNeedsShadowTreeUpdate(*this),
     90                        "Dying without unbinding?");
     91 }
     92 
     93 namespace SVGT = SVGGeometryProperty::Tags;
     94 
     95 //----------------------------------------------------------------------
     96 // nsINode methods
     97 
     98 void SVGUseElement::ProcessAttributeChange(int32_t aNamespaceID,
     99                                           nsAtom* aAttribute) {
    100  if (OwnerDoc()->CloningForSVGUse()) {
    101    return;
    102  }
    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);
    109      }
    110 
    111      if (auto* frame = GetFrame()) {
    112        frame->DimensionAttributeChanged(hadValidDimensions, isUsed);
    113      }
    114    }
    115  }
    116 
    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();
    123    }
    124    UnlinkSource();
    125    TriggerReclone();
    126  }
    127 }
    128 
    129 void SVGUseElement::DidAnimateAttribute(int32_t aNameSpaceID,
    130                                        nsAtom* aAttribute) {
    131  ProcessAttributeChange(aNameSpaceID, aAttribute);
    132 }
    133 
    134 void SVGUseElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aAttribute,
    135                                 const nsAttrValue* aValue,
    136                                 const nsAttrValue* aOldValue,
    137                                 nsIPrincipal* aSubjectPrincipal,
    138                                 bool aNotify) {
    139  ProcessAttributeChange(aNamespaceID, aAttribute);
    140  return SVGUseElementBase::AfterSetAttr(aNamespaceID, aAttribute, aValue,
    141                                         aOldValue, aSubjectPrincipal, aNotify);
    142 }
    143 
    144 nsresult SVGUseElement::Clone(dom::NodeInfo* aNodeInfo,
    145                              nsINode** aResult) const {
    146  *aResult = nullptr;
    147  SVGUseElement* it =
    148      new (aNodeInfo->NodeInfoManager()) SVGUseElement(do_AddRef(aNodeInfo));
    149 
    150  nsCOMPtr<nsINode> kungFuDeathGrip(it);
    151  nsresult rv1 = it->Init();
    152  nsresult rv2 = const_cast<SVGUseElement*>(this)->CopyInnerTo(it);
    153 
    154  if (aNodeInfo->GetDocument()->CloningForSVGUse()) {
    155    // SVGUseElement specific portion - record who we cloned from
    156    it->mOriginal = const_cast<SVGUseElement*>(this);
    157  }
    158 
    159  if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) {
    160    kungFuDeathGrip.swap(*aResult);
    161  }
    162 
    163  return NS_FAILED(rv1) ? rv1 : rv2;
    164 }
    165 
    166 nsresult SVGUseElement::BindToTree(BindContext& aContext, nsINode& aParent) {
    167  nsresult rv = SVGUseElementBase::BindToTree(aContext, aParent);
    168  NS_ENSURE_SUCCESS(rv, rv);
    169 
    170  TriggerReclone();
    171  return NS_OK;
    172 }
    173 
    174 void SVGUseElement::UnbindFromTree(UnbindContext& aContext) {
    175  SVGUseElementBase::UnbindFromTree(aContext);
    176  OwnerDoc()->UnscheduleSVGUseElementShadowTreeUpdate(*this);
    177 }
    178 
    179 already_AddRefed<DOMSVGAnimatedString> SVGUseElement::Href() {
    180  return mStringAttributes[HREF].IsExplicitlySet() ||
    181                 !mStringAttributes[XLINK_HREF].IsExplicitlySet()
    182             ? mStringAttributes[HREF].ToDOMAnimatedString(this)
    183             : mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this);
    184 }
    185 
    186 //----------------------------------------------------------------------
    187 
    188 already_AddRefed<DOMSVGAnimatedLength> SVGUseElement::X() {
    189  return mLengthAttributes[ATTR_X].ToDOMAnimatedLength(this);
    190 }
    191 
    192 already_AddRefed<DOMSVGAnimatedLength> SVGUseElement::Y() {
    193  return mLengthAttributes[ATTR_Y].ToDOMAnimatedLength(this);
    194 }
    195 
    196 already_AddRefed<DOMSVGAnimatedLength> SVGUseElement::Width() {
    197  return mLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this);
    198 }
    199 
    200 already_AddRefed<DOMSVGAnimatedLength> SVGUseElement::Height() {
    201  return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this);
    202 }
    203 
    204 //----------------------------------------------------------------------
    205 // nsIMutationObserver methods
    206 
    207 void SVGUseElement::CharacterDataChanged(nsIContent* aContent,
    208                                         const CharacterDataChangeInfo&) {
    209  if (nsContentUtils::IsInSameAnonymousTree(mReferencedElementTracker.get(),
    210                                            aContent)) {
    211    TriggerReclone();
    212  }
    213 }
    214 
    215 void SVGUseElement::AttributeChanged(Element* aElement, int32_t aNamespaceID,
    216                                     nsAtom* aAttribute, AttrModType,
    217                                     const nsAttrValue* aOldValue) {
    218  if (nsContentUtils::IsInSameAnonymousTree(mReferencedElementTracker.get(),
    219                                            aElement)) {
    220    TriggerReclone();
    221  }
    222 }
    223 
    224 void SVGUseElement::ContentAppended(nsIContent* aFirstNewContent,
    225                                    const ContentAppendInfo&) {
    226  // FIXME(emilio, bug 1442336): Why does this check the parent but
    227  // ContentInserted the child?
    228  if (nsContentUtils::IsInSameAnonymousTree(mReferencedElementTracker.get(),
    229                                            aFirstNewContent->GetParent())) {
    230    TriggerReclone();
    231  }
    232 }
    233 
    234 void SVGUseElement::ContentInserted(nsIContent* aChild,
    235                                    const ContentInsertInfo&) {
    236  // FIXME(emilio, bug 1442336): Why does this check the child but
    237  // ContentAppended the parent?
    238  if (nsContentUtils::IsInSameAnonymousTree(mReferencedElementTracker.get(),
    239                                            aChild)) {
    240    TriggerReclone();
    241  }
    242 }
    243 
    244 void SVGUseElement::ContentWillBeRemoved(nsIContent* aChild,
    245                                         const ContentRemoveInfo&) {
    246  if (nsContentUtils::IsInSameAnonymousTree(mReferencedElementTracker.get(),
    247                                            aChild)) {
    248    TriggerReclone();
    249  }
    250 }
    251 
    252 void SVGUseElement::NodeWillBeDestroyed(nsINode* aNode) {
    253  nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
    254  UnlinkSource();
    255 }
    256 
    257 // Returns whether this node could ever be displayed.
    258 static bool NodeCouldBeRendered(const nsINode& aNode) {
    259  if (const auto* symbol = SVGSymbolElement::FromNode(aNode)) {
    260    return symbol->CouldBeRendered();
    261  }
    262  if (auto* svgSwitch =
    263          SVGSwitchElement::FromNodeOrNull(aNode.GetParentNode())) {
    264    if (&aNode != svgSwitch->GetActiveChild()) {
    265      return false;
    266    }
    267  } else if (const auto* svgGraphics = SVGGraphicsElement::FromNode(aNode)) {
    268    if (!svgGraphics->PassesConditionalProcessingTests()) {
    269      return false;
    270    }
    271  }
    272  return true;
    273 }
    274 
    275 // <svg:use> can be used (no pun intended) to trivially cause an explosion of
    276 // clones that could potentially DoS the browser. We have a configurable limit
    277 // to control this.
    278 static bool IsTooMuchRecursion(uint32_t aCount) {
    279  switch (StaticPrefs::svg_use_element_recursive_clone_limit_enabled()) {
    280    case 0:
    281      return false;
    282    case 1:
    283      break;
    284    default:
    285      if (!XRE_IsParentProcess()) {
    286        return false;
    287      }
    288      break;
    289  }
    290  return aCount >= StaticPrefs::svg_use_element_recursive_clone_limit();
    291 }
    292 
    293 // Circular loop detection, plus detection of whether this shadow tree is
    294 // rendered at all.
    295 auto SVGUseElement::ScanAncestors(const Element& aTarget) const -> ScanResult {
    296  uint32_t count = 0;
    297  return ScanAncestorsInternal(aTarget, count);
    298 }
    299 
    300 auto SVGUseElement::ScanAncestorsInternal(const Element& aTarget,
    301                                          uint32_t& aCount) const
    302    -> ScanResult {
    303  if (&aTarget == this) {
    304    return ScanResult::CyclicReference;
    305  }
    306  if (mOriginal) {
    307    if (IsTooMuchRecursion(++aCount)) {
    308      return ScanResult::TooDeep;
    309    }
    310    auto result = mOriginal->ScanAncestorsInternal(aTarget, aCount);
    311    switch (result) {
    312      case ScanResult::TooDeep:
    313      case ScanResult::CyclicReference:
    314        return result;
    315      case ScanResult::Ok:
    316      case ScanResult::Invisible:
    317        break;
    318    }
    319  }
    320 
    321  auto result = ScanResult::Ok;
    322  for (nsINode* parent = GetParentOrShadowHostNode(); parent;
    323       parent = parent->GetParentOrShadowHostNode()) {
    324    if (parent == &aTarget) {
    325      return ScanResult::CyclicReference;
    326    }
    327    if (auto* use = SVGUseElement::FromNode(*parent)) {
    328      if (IsTooMuchRecursion(++aCount)) {
    329        return ScanResult::TooDeep;
    330      }
    331      if (mOriginal && use->mOriginal == mOriginal) {
    332        return ScanResult::CyclicReference;
    333      }
    334    }
    335    // Do we have other similar cases we can optimize out easily?
    336    if (!NodeCouldBeRendered(*parent)) {
    337      // NOTE(emilio): We can't just return here. If we're cyclic, we need to
    338      // know.
    339      result = ScanResult::Invisible;
    340    }
    341  }
    342  return result;
    343 }
    344 
    345 //----------------------------------------------------------------------
    346 
    347 static bool IsForbiddenUseNode(const nsINode& aNode) {
    348  if (!aNode.IsElement()) {
    349    return false;
    350  }
    351  const auto* svg = SVGElement::FromNode(aNode);
    352  return !svg || !svg->IsSVGGraphicsElement();
    353 }
    354 
    355 static void CollectForbiddenNodes(Element& aRoot,
    356                                  nsTArray<RefPtr<nsINode>>& aNodes) {
    357  auto iter = dom::ShadowIncludingTreeIterator(aRoot);
    358  while (iter) {
    359    nsINode* node = *iter;
    360    if (IsForbiddenUseNode(*node)) {
    361      aNodes.AppendElement(node);
    362      iter.SkipChildren();
    363      continue;
    364    }
    365    ++iter;
    366  }
    367 }
    368 
    369 // SVG1 restricted <use> trees to SVGGraphicsElements.
    370 // https://www.w3.org/TR/SVG11/struct.html#UseElement:
    371 //
    372 //    Any ‘svg’, ‘symbol’, ‘g’, graphics element or other ‘use’ is potentially a
    373 //    template object that can be re-used (i.e., "instanced") in the SVG
    374 //    document via a ‘use’ element. The ‘use’ element references another element
    375 //    and indicates that the graphical contents of that element is
    376 //    included/drawn at that given point in the document.
    377 //
    378 // SVG2 doesn't have that same restriction.
    379 // https://www.w3.org/TR/SVG2/struct.html#UseShadowTree:
    380 //
    381 //    Previous versions of SVG restricted the contents of the shadow tree to SVG
    382 //    graphics elements. This specification allows any valid SVG document
    383 //    subtree to be cloned. Cloning non-graphical content, however, will not
    384 //    usually have any visible effect.
    385 //
    386 // But it's pretty ambiguous as to what the behavior should be for some
    387 // elements, because <script> is inert, but <iframe> is not, see:
    388 // https://github.com/w3c/svgwg/issues/876
    389 //
    390 // So, fairly confusing, all-in-all.
    391 static void RemoveForbiddenNodes(Element& aRoot, bool aIsCrossDocument) {
    392  switch (StaticPrefs::svg_use_element_graphics_element_restrictions()) {
    393    case 0:
    394      return;
    395    case 1:
    396      if (!aIsCrossDocument) {
    397        return;
    398      }
    399      break;
    400    default:
    401      break;
    402  }
    403 
    404  AutoTArray<RefPtr<nsINode>, 10> unsafeNodes;
    405  CollectForbiddenNodes(aRoot, unsafeNodes);
    406  for (auto& unsafeNode : unsafeNodes) {
    407    unsafeNode->Remove();
    408  }
    409 }
    410 
    411 void SVGUseElement::UpdateShadowTree() {
    412  MOZ_ASSERT(IsInComposedDoc());
    413 
    414  if (mReferencedElementTracker.get()) {
    415    mReferencedElementTracker.get()->RemoveMutationObserver(this);
    416  }
    417 
    418  LookupHref();
    419 
    420  RefPtr<ShadowRoot> shadow = GetShadowRoot();
    421  if (!shadow) {
    422    shadow = AttachShadowWithoutNameChecks(ShadowRootMode::Closed);
    423  }
    424  MOZ_ASSERT(shadow);
    425 
    426  auto* targetElement =
    427      SVGGraphicsElement::FromNodeOrNull(mReferencedElementTracker.get());
    428  RefPtr<Element> newElement;
    429 
    430  auto UpdateShadowTree = mozilla::MakeScopeExit([&]() {
    431    if (nsIContent* firstChild = shadow->GetFirstChild()) {
    432      MOZ_ASSERT(!firstChild->GetNextSibling());
    433      shadow->RemoveChildNode(firstChild, /* aNotify = */ true);
    434    }
    435 
    436    if (newElement) {
    437      shadow->AppendChildTo(newElement, /* aNotify = */ true, IgnoreErrors());
    438    }
    439  });
    440 
    441  // make sure target is valid type for <use>
    442  if (!targetElement) {
    443    return;
    444  }
    445 
    446  if (ScanAncestors(*targetElement) != ScanResult::Ok) {
    447    return;
    448  }
    449 
    450  nsCOMPtr<nsIURI> baseURI = targetElement->GetBaseURI();
    451  if (!baseURI) {
    452    return;
    453  }
    454 
    455  {
    456    const bool isCrossDocument = targetElement->OwnerDoc() != OwnerDoc();
    457 
    458    nsNodeInfoManager* nodeInfoManager =
    459        isCrossDocument ? OwnerDoc()->NodeInfoManager() : nullptr;
    460 
    461    nsCOMPtr<nsINode> newNode =
    462        targetElement->Clone(true, nodeInfoManager, IgnoreErrors());
    463    if (!newNode) {
    464      return;
    465    }
    466 
    467    MOZ_ASSERT(newNode->IsElement());
    468    newElement = newNode.forget().downcast<Element>();
    469    RemoveForbiddenNodes(*newElement, isCrossDocument);
    470  }
    471 
    472  if (newElement->IsAnyOfSVGElements(nsGkAtoms::svg, nsGkAtoms::symbol)) {
    473    auto* newSVGElement = static_cast<SVGElement*>(newElement.get());
    474    if (mLengthAttributes[ATTR_WIDTH].IsExplicitlySet())
    475      newSVGElement->SetLength(nsGkAtoms::width, mLengthAttributes[ATTR_WIDTH]);
    476    if (mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet())
    477      newSVGElement->SetLength(nsGkAtoms::height,
    478                               mLengthAttributes[ATTR_HEIGHT]);
    479  }
    480 
    481  // Bug 1415044 the specs do not say which referrer information we should use.
    482  // This may change if there's any spec comes out.
    483  auto referrerInfo = MakeRefPtr<ReferrerInfo>(*this);
    484  mContentURLData = new URLExtraData(baseURI.forget(), referrerInfo.forget(),
    485                                     do_AddRef(NodePrincipal()));
    486 
    487  targetElement->AddMutationObserver(this);
    488 }
    489 
    490 Document* SVGUseElement::GetSourceDocument() const {
    491  nsIContent* targetElement = mReferencedElementTracker.get();
    492  return targetElement ? targetElement->OwnerDoc() : nullptr;
    493 }
    494 
    495 nsIURI* SVGUseElement::GetSourceDocURI() const {
    496  if (auto* doc = GetSourceDocument()) {
    497    return doc->GetDocumentURI();
    498  }
    499  return nullptr;
    500 }
    501 
    502 const Encoding* SVGUseElement::GetSourceDocCharacterSet() const {
    503  if (auto* doc = GetSourceDocument()) {
    504    return doc->GetDocumentCharacterSet();
    505  }
    506  return nullptr;
    507 }
    508 
    509 static nsINode* GetClonedChild(const SVGUseElement& aUseElement) {
    510  const ShadowRoot* shadow = aUseElement.GetShadowRoot();
    511  return shadow ? shadow->GetFirstChild() : nullptr;
    512 }
    513 
    514 bool SVGUseElement::OurWidthAndHeightAreUsed() const {
    515  nsINode* clonedChild = GetClonedChild(*this);
    516  return clonedChild &&
    517         clonedChild->IsAnyOfSVGElements(nsGkAtoms::svg, nsGkAtoms::symbol);
    518 }
    519 
    520 //----------------------------------------------------------------------
    521 // implementation helpers
    522 
    523 void SVGUseElement::SyncWidthOrHeight(nsAtom* aName) {
    524  NS_ASSERTION(aName == nsGkAtoms::width || aName == nsGkAtoms::height,
    525               "The clue is in the function name");
    526  NS_ASSERTION(OurWidthAndHeightAreUsed(), "Don't call this");
    527 
    528  if (!OurWidthAndHeightAreUsed()) {
    529    return;
    530  }
    531 
    532  auto* target = SVGElement::FromNode(GetClonedChild(*this));
    533  uint32_t index =
    534      sLengthInfo[ATTR_WIDTH].mName == aName ? ATTR_WIDTH : ATTR_HEIGHT;
    535 
    536  if (mLengthAttributes[index].IsExplicitlySet()) {
    537    target->SetLength(aName, mLengthAttributes[index]);
    538    return;
    539  }
    540  if (target->IsSVGElement(nsGkAtoms::svg)) {
    541    // Our width/height attribute is now no longer explicitly set, so we
    542    // need to revert the clone's width/height to the width/height of the
    543    // content that's being cloned.
    544    TriggerReclone();
    545    return;
    546  }
    547  // Our width/height attribute is now no longer explicitly set, so we
    548  // need to set the value to 100%
    549  SVGAnimatedLength length;
    550  length.Init(SVGContentUtils::XY, 0xff, 100,
    551              SVGLength_Binding::SVG_LENGTHTYPE_PERCENTAGE);
    552  target->SetLength(aName, length);
    553 }
    554 
    555 void SVGUseElement::LookupHref() {
    556  nsAutoString href;
    557  if (mStringAttributes[HREF].IsExplicitlySet()) {
    558    mStringAttributes[HREF].GetAnimValue(href, this);
    559  } else {
    560    mStringAttributes[XLINK_HREF].GetAnimValue(href, this);
    561  }
    562 
    563  if (href.IsEmpty()) {
    564    return;
    565  }
    566 
    567  Element* treeToWatch = mOriginal ? mOriginal.get() : this;
    568  if (nsContentUtils::IsLocalRefURL(href)) {
    569    mReferencedElementTracker.ResetToLocalFragmentID(*treeToWatch, href);
    570    return;
    571  }
    572 
    573  nsCOMPtr<nsIURI> baseURI = treeToWatch->GetBaseURI();
    574  nsCOMPtr<nsIURI> targetURI;
    575  nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
    576                                            GetComposedDoc(), baseURI);
    577  if (!targetURI) {
    578    return;
    579  }
    580 
    581  // Don't allow <use href="data:...">. Using "#ref" inside a data: document is
    582  // handled above.
    583  if (targetURI->SchemeIs("data")) {
    584    return;
    585  }
    586 
    587  nsIReferrerInfo* referrer =
    588      OwnerDoc()->ReferrerInfoForInternalCSSAndSVGResources();
    589  mReferencedElementTracker.ResetToURIWithFragmentID(*treeToWatch, targetURI,
    590                                                     referrer);
    591 }
    592 
    593 void SVGUseElement::TriggerReclone() {
    594  if (Document* doc = GetComposedDoc()) {
    595    doc->ScheduleSVGUseElementShadowTreeUpdate(*this);
    596  }
    597 }
    598 
    599 void SVGUseElement::UnlinkSource() {
    600  if (mReferencedElementTracker.get()) {
    601    mReferencedElementTracker.get()->RemoveMutationObserver(this);
    602  }
    603  mReferencedElementTracker.Unlink();
    604 }
    605 
    606 //----------------------------------------------------------------------
    607 // SVGElement methods
    608 
    609 /* virtual */
    610 gfxMatrix SVGUseElement::ChildToUserSpaceTransform() const {
    611  float x, y;
    612  if (!SVGGeometryProperty::ResolveAll<SVGT::X, SVGT::Y>(this, &x, &y)) {
    613    const_cast<SVGUseElement*>(this)->GetAnimatedLengthValues(&x, &y, nullptr);
    614  }
    615  return gfxMatrix::Translation(x, y);
    616 }
    617 
    618 /* virtual */
    619 bool SVGUseElement::HasValidDimensions() const {
    620  if (!OurWidthAndHeightAreUsed()) {
    621    return true;
    622  }
    623 
    624  return (!mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() ||
    625          mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0) &&
    626         (!mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() ||
    627          mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0);
    628 }
    629 
    630 SVGElement::LengthAttributesInfo SVGUseElement::GetLengthInfo() {
    631  return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
    632                              std::size(sLengthInfo));
    633 }
    634 
    635 SVGElement::StringAttributesInfo SVGUseElement::GetStringInfo() {
    636  return StringAttributesInfo(mStringAttributes, sStringInfo,
    637                              std::size(sStringInfo));
    638 }
    639 
    640 SVGUseFrame* SVGUseElement::GetFrame() const {
    641  nsIFrame* frame = GetPrimaryFrame();
    642  // We might be a plain SVGContainerFrame if we didn't pass the conditional
    643  // processing checks.
    644  if (!frame || !frame->IsSVGUseFrame()) {
    645    MOZ_ASSERT_IF(frame, frame->Type() == LayoutFrameType::None);
    646    return nullptr;
    647  }
    648  return static_cast<SVGUseFrame*>(frame);
    649 }
    650 
    651 //----------------------------------------------------------------------
    652 // nsIContent methods
    653 
    654 NS_IMETHODIMP_(bool)
    655 SVGUseElement::IsAttributeMapped(const nsAtom* name) const {
    656  return name == nsGkAtoms::x || name == nsGkAtoms::y ||
    657         SVGUseElementBase::IsAttributeMapped(name);
    658 }
    659 
    660 NonCustomCSSPropertyId SVGUseElement::GetCSSPropertyIdForAttrEnum(
    661    uint8_t aAttrEnum) {
    662  switch (aAttrEnum) {
    663    case ATTR_X:
    664      return eCSSProperty_x;
    665    case ATTR_Y:
    666      return eCSSProperty_y;
    667    default:
    668      // Currently we don't map width or height to style
    669      return eCSSProperty_UNKNOWN;
    670  }
    671 }
    672 
    673 }  // namespace mozilla::dom