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 "mozilla/ArrayUtils.h"
10 #include "mozilla/ErrorResult.h"
11 #include "mozilla/dom/SVGLengthBinding.h"
12 #include "mozilla/dom/SVGUseElementBinding.h"
13 #include "nsGkAtoms.h"
14 #include "mozilla/dom/SVGSVGElement.h"
15 #include "mozilla/dom/Document.h"
16 #include "mozilla/dom/Element.h"
17 #include "nsContentUtils.h"
18 #include "nsIURI.h"
19 #include "mozilla/URLExtraData.h"
20 #include "SVGObserverUtils.h"
21 #include "nsSVGUseFrame.h"
22 
23 NS_IMPL_NS_NEW_SVG_ELEMENT(Use)
24 
25 namespace mozilla {
26 namespace dom {
27 
WrapNode(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)28 JSObject* SVGUseElement::WrapNode(JSContext* aCx,
29                                   JS::Handle<JSObject*> aGivenProto) {
30   return SVGUseElement_Binding::Wrap(aCx, this, aGivenProto);
31 }
32 
33 ////////////////////////////////////////////////////////////////////////
34 // implementation
35 
36 SVGElement::LengthInfo SVGUseElement::sLengthInfo[4] = {
37     {nsGkAtoms::x, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER,
38      SVGContentUtils::X},
39     {nsGkAtoms::y, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER,
40      SVGContentUtils::Y},
41     {nsGkAtoms::width, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER,
42      SVGContentUtils::X},
43     {nsGkAtoms::height, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER,
44      SVGContentUtils::Y},
45 };
46 
47 SVGElement::StringInfo SVGUseElement::sStringInfo[2] = {
48     {nsGkAtoms::href, kNameSpaceID_None, true},
49     {nsGkAtoms::href, kNameSpaceID_XLink, true}};
50 
51 //----------------------------------------------------------------------
52 // nsISupports methods
53 
54 NS_IMPL_CYCLE_COLLECTION_CLASS(SVGUseElement)
55 
56 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SVGUseElement,
57                                                 SVGUseElementBase)
58   nsAutoScriptBlocker scriptBlocker;
59   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOriginal)
60   tmp->UnlinkSource();
61 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
62 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SVGUseElement,
63                                                   SVGUseElementBase)
64   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginal)
65   tmp->mReferencedElementTracker.Traverse(&cb);
66 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
67 
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(SVGUseElement,SVGUseElementBase,nsIMutationObserver)68 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(SVGUseElement, SVGUseElementBase,
69                                              nsIMutationObserver)
70 
71 //----------------------------------------------------------------------
72 // Implementation
73 
74 SVGUseElement::SVGUseElement(
75     already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
76     : SVGUseElementBase(std::move(aNodeInfo)),
77       mReferencedElementTracker(this) {}
78 
~SVGUseElement()79 SVGUseElement::~SVGUseElement() {
80   UnlinkSource();
81   MOZ_DIAGNOSTIC_ASSERT(!OwnerDoc()->SVGUseElementNeedsShadowTreeUpdate(*this),
82                         "Dying without unbinding?");
83 }
84 
85 //----------------------------------------------------------------------
86 // nsINode methods
87 
ProcessAttributeChange(int32_t aNamespaceID,nsAtom * aAttribute)88 void SVGUseElement::ProcessAttributeChange(int32_t aNamespaceID,
89                                            nsAtom* aAttribute) {
90   if (aNamespaceID == kNameSpaceID_None) {
91     if (aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y) {
92       if (auto* frame = GetFrame()) {
93         frame->PositionAttributeChanged();
94       }
95     } else if (aAttribute == nsGkAtoms::width ||
96                aAttribute == nsGkAtoms::height) {
97       const bool hadValidDimensions = HasValidDimensions();
98       const bool isUsed = OurWidthAndHeightAreUsed();
99       if (isUsed) {
100         SyncWidthOrHeight(aAttribute);
101       }
102 
103       if (auto* frame = GetFrame()) {
104         frame->DimensionAttributeChanged(hadValidDimensions, isUsed);
105       }
106     }
107   }
108 
109   if ((aNamespaceID == kNameSpaceID_XLink ||
110        aNamespaceID == kNameSpaceID_None) &&
111       aAttribute == nsGkAtoms::href) {
112     // We're changing our nature, clear out the clone information.
113     if (auto* frame = GetFrame()) {
114       frame->HrefChanged();
115     }
116     mOriginal = nullptr;
117     UnlinkSource();
118     TriggerReclone();
119   }
120 }
121 
AfterSetAttr(int32_t aNamespaceID,nsAtom * aAttribute,const nsAttrValue * aValue,const nsAttrValue * aOldValue,nsIPrincipal * aSubjectPrincipal,bool aNotify)122 nsresult SVGUseElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aAttribute,
123                                      const nsAttrValue* aValue,
124                                      const nsAttrValue* aOldValue,
125                                      nsIPrincipal* aSubjectPrincipal,
126                                      bool aNotify) {
127   ProcessAttributeChange(aNamespaceID, aAttribute);
128   return SVGUseElementBase::AfterSetAttr(aNamespaceID, aAttribute, aValue,
129                                          aOldValue, aSubjectPrincipal, aNotify);
130 }
131 
Clone(dom::NodeInfo * aNodeInfo,nsINode ** aResult) const132 nsresult SVGUseElement::Clone(dom::NodeInfo* aNodeInfo,
133                               nsINode** aResult) const {
134   *aResult = nullptr;
135   SVGUseElement* it =
136       new (aNodeInfo->NodeInfoManager()) SVGUseElement(do_AddRef(aNodeInfo));
137 
138   nsCOMPtr<nsINode> kungFuDeathGrip(it);
139   nsresult rv1 = it->Init();
140   nsresult rv2 = const_cast<SVGUseElement*>(this)->CopyInnerTo(it);
141 
142   // SVGUseElement specific portion - record who we cloned from
143   it->mOriginal = const_cast<SVGUseElement*>(this);
144 
145   if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) {
146     kungFuDeathGrip.swap(*aResult);
147   }
148 
149   return NS_FAILED(rv1) ? rv1 : rv2;
150 }
151 
BindToTree(BindContext & aContext,nsINode & aParent)152 nsresult SVGUseElement::BindToTree(BindContext& aContext, nsINode& aParent) {
153   nsresult rv = SVGUseElementBase::BindToTree(aContext, aParent);
154   NS_ENSURE_SUCCESS(rv, rv);
155 
156   TriggerReclone();
157   return NS_OK;
158 }
159 
UnbindFromTree(bool aNullParent)160 void SVGUseElement::UnbindFromTree(bool aNullParent) {
161   SVGUseElementBase::UnbindFromTree(aNullParent);
162   OwnerDoc()->UnscheduleSVGUseElementShadowTreeUpdate(*this);
163 }
164 
Href()165 already_AddRefed<DOMSVGAnimatedString> SVGUseElement::Href() {
166   return mStringAttributes[HREF].IsExplicitlySet()
167              ? mStringAttributes[HREF].ToDOMAnimatedString(this)
168              : mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this);
169 }
170 
171 //----------------------------------------------------------------------
172 
X()173 already_AddRefed<DOMSVGAnimatedLength> SVGUseElement::X() {
174   return mLengthAttributes[ATTR_X].ToDOMAnimatedLength(this);
175 }
176 
Y()177 already_AddRefed<DOMSVGAnimatedLength> SVGUseElement::Y() {
178   return mLengthAttributes[ATTR_Y].ToDOMAnimatedLength(this);
179 }
180 
Width()181 already_AddRefed<DOMSVGAnimatedLength> SVGUseElement::Width() {
182   return mLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this);
183 }
184 
Height()185 already_AddRefed<DOMSVGAnimatedLength> SVGUseElement::Height() {
186   return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this);
187 }
188 
189 //----------------------------------------------------------------------
190 // nsIMutationObserver methods
191 
CharacterDataChanged(nsIContent * aContent,const CharacterDataChangeInfo &)192 void SVGUseElement::CharacterDataChanged(nsIContent* aContent,
193                                          const CharacterDataChangeInfo&) {
194   if (nsContentUtils::IsInSameAnonymousTree(mReferencedElementTracker.get(),
195                                             aContent)) {
196     TriggerReclone();
197   }
198 }
199 
AttributeChanged(Element * aElement,int32_t aNamespaceID,nsAtom * aAttribute,int32_t aModType,const nsAttrValue * aOldValue)200 void SVGUseElement::AttributeChanged(Element* aElement, int32_t aNamespaceID,
201                                      nsAtom* aAttribute, int32_t aModType,
202                                      const nsAttrValue* aOldValue) {
203   if (nsContentUtils::IsInSameAnonymousTree(mReferencedElementTracker.get(),
204                                             aElement)) {
205     TriggerReclone();
206   }
207 }
208 
ContentAppended(nsIContent * aFirstNewContent)209 void SVGUseElement::ContentAppended(nsIContent* aFirstNewContent) {
210   // FIXME(emilio, bug 1442336): Why does this check the parent but
211   // ContentInserted the child?
212   if (nsContentUtils::IsInSameAnonymousTree(mReferencedElementTracker.get(),
213                                             aFirstNewContent->GetParent())) {
214     TriggerReclone();
215   }
216 }
217 
ContentInserted(nsIContent * aChild)218 void SVGUseElement::ContentInserted(nsIContent* aChild) {
219   // FIXME(emilio, bug 1442336): Why does this check the child but
220   // ContentAppended the parent?
221   if (nsContentUtils::IsInSameAnonymousTree(mReferencedElementTracker.get(),
222                                             aChild)) {
223     TriggerReclone();
224   }
225 }
226 
ContentRemoved(nsIContent * aChild,nsIContent * aPreviousSibling)227 void SVGUseElement::ContentRemoved(nsIContent* aChild,
228                                    nsIContent* aPreviousSibling) {
229   if (nsContentUtils::IsInSameAnonymousTree(mReferencedElementTracker.get(),
230                                             aChild)) {
231     TriggerReclone();
232   }
233 }
234 
NodeWillBeDestroyed(const nsINode * aNode)235 void SVGUseElement::NodeWillBeDestroyed(const nsINode* aNode) {
236   nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
237   UnlinkSource();
238 }
239 
240 // Returns whether this node could ever be displayed.
NodeCouldBeRendered(const nsINode & aNode)241 static bool NodeCouldBeRendered(const nsINode& aNode) {
242   if (aNode.IsSVGElement(nsGkAtoms::symbol)) {
243     // Only <symbol> elements in the root of a <svg:use> shadow tree are
244     // displayed.
245     auto* shadowRoot = ShadowRoot::FromNodeOrNull(aNode.GetParentNode());
246     return shadowRoot && shadowRoot->Host()->IsSVGElement(nsGkAtoms::use);
247   }
248   // TODO: Do we have other cases we can optimize out easily?
249   return true;
250 }
251 
252 // Circular loop detection, plus detection of whether this shadow tree is
253 // rendered at all.
ScanAncestors(const Element & aTarget) const254 auto SVGUseElement::ScanAncestors(const Element& aTarget) const -> ScanResult {
255   if (&aTarget == this) {
256     return ScanResult::CyclicReference;
257   }
258   if (mOriginal &&
259       mOriginal->ScanAncestors(aTarget) == ScanResult::CyclicReference) {
260     return ScanResult::CyclicReference;
261   }
262   auto result = ScanResult::Ok;
263   for (nsINode* parent = GetParentOrShadowHostNode(); parent;
264        parent = parent->GetParentOrShadowHostNode()) {
265     if (parent == &aTarget) {
266       return ScanResult::CyclicReference;
267     }
268     if (auto* use = SVGUseElement::FromNode(*parent)) {
269       if (mOriginal && use->mOriginal == mOriginal) {
270         return ScanResult::CyclicReference;
271       }
272     }
273     // Do we have other similar cases we can optimize out easily?
274     if (!NodeCouldBeRendered(*parent)) {
275       // NOTE(emilio): We can't just return here. If we're cyclic, we need to
276       // know.
277       result = ScanResult::Invisible;
278     }
279   }
280   return result;
281 }
282 
283 //----------------------------------------------------------------------
284 
UpdateShadowTree()285 void SVGUseElement::UpdateShadowTree() {
286   MOZ_ASSERT(IsInComposedDoc());
287 
288   if (mReferencedElementTracker.get()) {
289     mReferencedElementTracker.get()->RemoveMutationObserver(this);
290   }
291 
292   LookupHref();
293 
294   RefPtr<ShadowRoot> shadow = GetShadowRoot();
295   if (!shadow) {
296     shadow = AttachShadowWithoutNameChecks(ShadowRootMode::Closed);
297   }
298   MOZ_ASSERT(shadow);
299 
300   Element* targetElement = mReferencedElementTracker.get();
301   RefPtr<Element> newElement;
302 
303   auto UpdateShadowTree = mozilla::MakeScopeExit([&]() {
304     nsIContent* firstChild = shadow->GetFirstChild();
305     if (firstChild) {
306       MOZ_ASSERT(!firstChild->GetNextSibling());
307       shadow->RemoveChildNode(firstChild, /* aNotify = */ true);
308     }
309 
310     if (newElement) {
311       shadow->AppendChildTo(newElement, /* aNotify = */ true);
312     }
313   });
314 
315   // make sure target is valid type for <use>
316   // QIable nsSVGGraphicsElement would eliminate enumerating all elements
317   if (!targetElement ||
318       !targetElement->IsAnyOfSVGElements(
319           nsGkAtoms::svg, nsGkAtoms::symbol, nsGkAtoms::g, nsGkAtoms::path,
320           nsGkAtoms::text, nsGkAtoms::rect, nsGkAtoms::circle,
321           nsGkAtoms::ellipse, nsGkAtoms::line, nsGkAtoms::polyline,
322           nsGkAtoms::polygon, nsGkAtoms::image, nsGkAtoms::use)) {
323     return;
324   }
325 
326   if (ScanAncestors(*targetElement) != ScanResult::Ok) {
327     return;
328   }
329 
330   nsCOMPtr<nsIURI> baseURI = targetElement->GetBaseURI();
331   if (!baseURI) {
332     return;
333   }
334 
335   {
336     nsNodeInfoManager* nodeInfoManager = targetElement->OwnerDoc() == OwnerDoc()
337                                              ? nullptr
338                                              : OwnerDoc()->NodeInfoManager();
339 
340     nsCOMPtr<nsINode> newNode =
341         targetElement->Clone(true, nodeInfoManager, IgnoreErrors());
342     if (!newNode) {
343       return;
344     }
345 
346     MOZ_ASSERT(newNode->IsElement());
347     newElement = newNode.forget().downcast<Element>();
348   }
349 
350   if (newElement->IsAnyOfSVGElements(nsGkAtoms::svg, nsGkAtoms::symbol)) {
351     auto* newSVGElement = static_cast<SVGElement*>(newElement.get());
352     if (mLengthAttributes[ATTR_WIDTH].IsExplicitlySet())
353       newSVGElement->SetLength(nsGkAtoms::width, mLengthAttributes[ATTR_WIDTH]);
354     if (mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet())
355       newSVGElement->SetLength(nsGkAtoms::height,
356                                mLengthAttributes[ATTR_HEIGHT]);
357   }
358 
359   // Bug 1415044 the specs do not say which referrer information we should use.
360   // This may change if there's any spec comes out.
361   auto referrerInfo = MakeRefPtr<ReferrerInfo>(*this);
362   mContentURLData = new URLExtraData(baseURI.forget(), referrerInfo.forget(),
363                                      do_AddRef(NodePrincipal()));
364 
365   targetElement->AddMutationObserver(this);
366 }
367 
GetSourceDocURI()368 nsIURI* SVGUseElement::GetSourceDocURI() {
369   nsIContent* targetElement = mReferencedElementTracker.get();
370   if (!targetElement) {
371     return nullptr;
372   }
373 
374   return targetElement->OwnerDoc()->GetDocumentURI();
375 }
376 
GetClonedChild(const SVGUseElement & aUseElement)377 static nsINode* GetClonedChild(const SVGUseElement& aUseElement) {
378   const ShadowRoot* shadow = aUseElement.GetShadowRoot();
379   return shadow ? shadow->GetFirstChild() : nullptr;
380 }
381 
OurWidthAndHeightAreUsed() const382 bool SVGUseElement::OurWidthAndHeightAreUsed() const {
383   nsINode* clonedChild = GetClonedChild(*this);
384   return clonedChild &&
385          clonedChild->IsAnyOfSVGElements(nsGkAtoms::svg, nsGkAtoms::symbol);
386 }
387 
388 //----------------------------------------------------------------------
389 // implementation helpers
390 
SyncWidthOrHeight(nsAtom * aName)391 void SVGUseElement::SyncWidthOrHeight(nsAtom* aName) {
392   NS_ASSERTION(aName == nsGkAtoms::width || aName == nsGkAtoms::height,
393                "The clue is in the function name");
394   NS_ASSERTION(OurWidthAndHeightAreUsed(), "Don't call this");
395 
396   if (!OurWidthAndHeightAreUsed()) {
397     return;
398   }
399 
400   auto* target = SVGElement::FromNode(GetClonedChild(*this));
401   uint32_t index =
402       sLengthInfo[ATTR_WIDTH].mName == aName ? ATTR_WIDTH : ATTR_HEIGHT;
403 
404   if (mLengthAttributes[index].IsExplicitlySet()) {
405     target->SetLength(aName, mLengthAttributes[index]);
406     return;
407   }
408   if (target->IsSVGElement(nsGkAtoms::svg)) {
409     // Our width/height attribute is now no longer explicitly set, so we
410     // need to revert the clone's width/height to the width/height of the
411     // content that's being cloned.
412     TriggerReclone();
413     return;
414   }
415   // Our width/height attribute is now no longer explicitly set, so we
416   // need to set the value to 100%
417   SVGAnimatedLength length;
418   length.Init(SVGContentUtils::XY, 0xff, 100,
419               SVGLength_Binding::SVG_LENGTHTYPE_PERCENTAGE);
420   target->SetLength(aName, length);
421 }
422 
LookupHref()423 void SVGUseElement::LookupHref() {
424   nsAutoString href;
425   if (mStringAttributes[HREF].IsExplicitlySet()) {
426     mStringAttributes[HREF].GetAnimValue(href, this);
427   } else {
428     mStringAttributes[XLINK_HREF].GetAnimValue(href, this);
429   }
430 
431   if (href.IsEmpty()) {
432     return;
433   }
434 
435   nsCOMPtr<nsIURI> originURI =
436       mOriginal ? mOriginal->GetBaseURI() : GetBaseURI();
437   nsCOMPtr<nsIURI> baseURI =
438       nsContentUtils::IsLocalRefURL(href)
439           ? SVGObserverUtils::GetBaseURLForLocalRef(this, originURI)
440           : originURI;
441 
442   nsCOMPtr<nsIURI> targetURI;
443   nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
444                                             GetComposedDoc(), baseURI);
445   nsCOMPtr<nsIReferrerInfo> referrerInfo =
446       ReferrerInfo::CreateForSVGResources(OwnerDoc());
447 
448   mReferencedElementTracker.ResetToURIFragmentID(this, targetURI, referrerInfo);
449 }
450 
TriggerReclone()451 void SVGUseElement::TriggerReclone() {
452   if (Document* doc = GetComposedDoc()) {
453     doc->ScheduleSVGUseElementShadowTreeUpdate(*this);
454   }
455 }
456 
UnlinkSource()457 void SVGUseElement::UnlinkSource() {
458   if (mReferencedElementTracker.get()) {
459     mReferencedElementTracker.get()->RemoveMutationObserver(this);
460   }
461   mReferencedElementTracker.Unlink();
462 }
463 
464 //----------------------------------------------------------------------
465 // SVGElement methods
466 
467 /* virtual */
PrependLocalTransformsTo(const gfxMatrix & aMatrix,SVGTransformTypes aWhich) const468 gfxMatrix SVGUseElement::PrependLocalTransformsTo(
469     const gfxMatrix& aMatrix, SVGTransformTypes aWhich) const {
470   // 'transform' attribute:
471   gfxMatrix userToParent;
472 
473   if (aWhich == eUserSpaceToParent || aWhich == eAllTransforms) {
474     userToParent = GetUserToParentTransform(mAnimateMotionTransform.get(),
475                                             mTransforms.get());
476     if (aWhich == eUserSpaceToParent) {
477       return userToParent * aMatrix;
478     }
479   }
480 
481   // our 'x' and 'y' attributes:
482   float x, y;
483   const_cast<SVGUseElement*>(this)->GetAnimatedLengthValues(&x, &y, nullptr);
484 
485   gfxMatrix childToUser = gfxMatrix::Translation(x, y);
486 
487   if (aWhich == eAllTransforms) {
488     return childToUser * userToParent * aMatrix;
489   }
490 
491   MOZ_ASSERT(aWhich == eChildToUserSpace, "Unknown TransformTypes");
492 
493   // The following may look broken because pre-multiplying our eChildToUserSpace
494   // transform with another matrix without including our eUserSpaceToParent
495   // transform between the two wouldn't make sense.  We don't expect that to
496   // ever happen though.  We get here either when the identity matrix has been
497   // passed because our caller just wants our eChildToUserSpace transform, or
498   // when our eUserSpaceToParent transform has already been multiplied into the
499   // matrix that our caller passes (such as when we're called from PaintSVG).
500   return childToUser * aMatrix;
501 }
502 
503 /* virtual */
HasValidDimensions() const504 bool SVGUseElement::HasValidDimensions() const {
505   return (!mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() ||
506           mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0) &&
507          (!mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() ||
508           mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0);
509 }
510 
GetLengthInfo()511 SVGElement::LengthAttributesInfo SVGUseElement::GetLengthInfo() {
512   return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
513                               ArrayLength(sLengthInfo));
514 }
515 
GetStringInfo()516 SVGElement::StringAttributesInfo SVGUseElement::GetStringInfo() {
517   return StringAttributesInfo(mStringAttributes, sStringInfo,
518                               ArrayLength(sStringInfo));
519 }
520 
GetFrame() const521 nsSVGUseFrame* SVGUseElement::GetFrame() const {
522   nsIFrame* frame = GetPrimaryFrame();
523   // We might be a plain nsSVGContainerFrame if we didn't pass the conditional
524   // processing checks.
525   if (!frame || !frame->IsSVGUseFrame()) {
526     MOZ_ASSERT_IF(frame, frame->Type() == LayoutFrameType::None);
527     return nullptr;
528   }
529   return static_cast<nsSVGUseFrame*>(frame);
530 }
531 
532 //----------------------------------------------------------------------
533 // nsIContent methods
534 
NS_IMETHODIMP_(bool)535 NS_IMETHODIMP_(bool)
536 SVGUseElement::IsAttributeMapped(const nsAtom* name) const {
537   static const MappedAttributeEntry* const map[] = {sFEFloodMap,
538                                                     sFiltersMap,
539                                                     sFontSpecificationMap,
540                                                     sGradientStopMap,
541                                                     sLightingEffectsMap,
542                                                     sMarkersMap,
543                                                     sTextContentElementsMap,
544                                                     sViewportsMap};
545 
546   return FindAttributeDependence(name, map) ||
547          SVGUseElementBase::IsAttributeMapped(name);
548 }
549 
550 }  // namespace dom
551 }  // namespace mozilla
552