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