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