2 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
4 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
5 * Copyright (C) 2011 Torch Mobile (Beijing) Co. Ltd. All rights reserved.
6 * Copyright (C) 2012 University of Szeged
7 * Copyright (C) 2012 Renata Hodovan <reni@webkit.org>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB. If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
27 #include "core/svg/SVGUseElement.h"
29 #include "bindings/core/v8/ExceptionStatePlaceholder.h"
30 #include "core/SVGNames.h"
31 #include "core/XLinkNames.h"
32 #include "core/dom/Document.h"
33 #include "core/dom/ElementTraversal.h"
34 #include "core/events/Event.h"
35 #include "core/dom/shadow/ElementShadow.h"
36 #include "core/dom/shadow/ShadowRoot.h"
37 #include "core/fetch/FetchRequest.h"
38 #include "core/fetch/ResourceFetcher.h"
39 #include "core/layout/svg/LayoutSVGTransformableContainer.h"
40 #include "core/svg/SVGGElement.h"
41 #include "core/svg/SVGLengthContext.h"
42 #include "core/svg/SVGSVGElement.h"
43 #include "core/xml/parser/XMLDocumentParser.h"
47 static SVGUseEventSender
& svgUseLoadEventSender()
49 DEFINE_STATIC_LOCAL(SVGUseEventSender
, sharedLoadEventSender
, (EventTypeNames::load
));
50 return sharedLoadEventSender
;
53 inline SVGUseElement::SVGUseElement(Document
& document
)
54 : SVGGraphicsElement(SVGNames::useTag
, document
)
55 , SVGURIReference(this)
56 , m_x(SVGAnimatedLength::create(this, SVGNames::xAttr
, SVGLength::create(SVGLengthMode::Width
), AllowNegativeLengths
))
57 , m_y(SVGAnimatedLength::create(this, SVGNames::yAttr
, SVGLength::create(SVGLengthMode::Height
), AllowNegativeLengths
))
58 , m_width(SVGAnimatedLength::create(this, SVGNames::widthAttr
, SVGLength::create(SVGLengthMode::Width
), ForbidNegativeLengths
))
59 , m_height(SVGAnimatedLength::create(this, SVGNames::heightAttr
, SVGLength::create(SVGLengthMode::Height
), ForbidNegativeLengths
))
60 , m_haveFiredLoadEvent(false)
61 , m_needsShadowTreeRecreation(false)
63 ASSERT(hasCustomStyleCallbacks());
65 addToPropertyMap(m_x
);
66 addToPropertyMap(m_y
);
67 addToPropertyMap(m_width
);
68 addToPropertyMap(m_height
);
71 PassRefPtrWillBeRawPtr
<SVGUseElement
> SVGUseElement::create(Document
& document
)
73 // Always build a user agent #shadow-root for SVGUseElement.
74 RefPtrWillBeRawPtr
<SVGUseElement
> use
= adoptRefWillBeNoop(new SVGUseElement(document
));
75 use
->ensureUserAgentShadowRoot();
79 SVGUseElement::~SVGUseElement()
81 setDocumentResource(0);
84 cancelShadowTreeRecreation();
86 svgUseLoadEventSender().cancelEvent(this);
89 DEFINE_TRACE(SVGUseElement
)
93 visitor
->trace(m_width
);
94 visitor
->trace(m_height
);
95 visitor
->trace(m_targetElementInstance
);
96 SVGGraphicsElement::trace(visitor
);
97 SVGURIReference::trace(visitor
);
101 static inline bool isWellFormedDocument(Document
* document
)
103 if (document
->isXMLDocument())
104 return static_cast<XMLDocumentParser
*>(document
->parser())->wellFormed();
109 Node::InsertionNotificationRequest
SVGUseElement::insertedInto(ContainerNode
* rootParent
)
111 // This functions exists to assure assumptions made in the code regarding SVGElementInstance creation/destruction are satisfied.
112 SVGGraphicsElement::insertedInto(rootParent
);
113 if (!rootParent
->inDocument())
114 return InsertionDone
;
115 ASSERT(!m_targetElementInstance
|| !isWellFormedDocument(&document()));
116 ASSERT(!hasPendingResources() || !isWellFormedDocument(&document()));
117 invalidateShadowTree();
118 return InsertionDone
;
121 void SVGUseElement::removedFrom(ContainerNode
* rootParent
)
123 SVGGraphicsElement::removedFrom(rootParent
);
124 if (rootParent
->inDocument()) {
126 cancelShadowTreeRecreation();
130 TreeScope
* SVGUseElement::referencedScope() const
132 if (isStructurallyExternal())
133 return externalDocument();
137 Document
* SVGUseElement::externalDocument() const
139 if (m_resource
&& m_resource
->isLoaded()) {
140 // Gracefully handle error condition.
141 if (m_resource
->errorOccurred())
143 ASSERT(m_resource
->document());
144 return m_resource
->document();
149 void transferUseWidthAndHeightIfNeeded(const SVGUseElement
& use
, SVGElement
* shadowElement
, const SVGElement
& originalElement
)
151 DEFINE_STATIC_LOCAL(const AtomicString
, hundredPercentString
, ("100%", AtomicString::ConstructFromLiteral
));
152 ASSERT(shadowElement
);
153 if (isSVGSymbolElement(*shadowElement
)) {
154 // Spec (<use> on <symbol>): This generated 'svg' will always have explicit values for attributes width and height.
155 // If attributes width and/or height are provided on the 'use' element, then these attributes
156 // will be transferred to the generated 'svg'. If attributes width and/or height are not specified,
157 // the generated 'svg' element will use values of 100% for these attributes.
158 shadowElement
->setAttribute(SVGNames::widthAttr
, use
.width()->isSpecified() ? AtomicString(use
.width()->currentValue()->valueAsString()) : hundredPercentString
);
159 shadowElement
->setAttribute(SVGNames::heightAttr
, use
.height()->isSpecified() ? AtomicString(use
.height()->currentValue()->valueAsString()) : hundredPercentString
);
160 } else if (isSVGSVGElement(*shadowElement
)) {
161 // Spec (<use> on <svg>): If attributes width and/or height are provided on the 'use' element, then these
162 // values will override the corresponding attributes on the 'svg' in the generated tree.
163 if (use
.width()->isSpecified())
164 shadowElement
->setAttribute(SVGNames::widthAttr
, AtomicString(use
.width()->currentValue()->valueAsString()));
166 shadowElement
->setAttribute(SVGNames::widthAttr
, originalElement
.getAttribute(SVGNames::widthAttr
));
167 if (use
.height()->isSpecified())
168 shadowElement
->setAttribute(SVGNames::heightAttr
, AtomicString(use
.height()->currentValue()->valueAsString()));
170 shadowElement
->setAttribute(SVGNames::heightAttr
, originalElement
.getAttribute(SVGNames::heightAttr
));
174 bool SVGUseElement::isPresentationAttribute(const QualifiedName
& attrName
) const
176 if (attrName
== SVGNames::xAttr
|| attrName
== SVGNames::yAttr
)
178 return SVGGraphicsElement::isPresentationAttribute(attrName
);
181 bool SVGUseElement::isPresentationAttributeWithSVGDOM(const QualifiedName
& attrName
) const
183 if (attrName
== SVGNames::xAttr
|| attrName
== SVGNames::yAttr
)
185 return SVGGraphicsElement::isPresentationAttributeWithSVGDOM(attrName
);
188 void SVGUseElement::collectStyleForPresentationAttribute(const QualifiedName
& name
, const AtomicString
& value
, MutableStylePropertySet
* style
)
190 RefPtrWillBeRawPtr
<SVGAnimatedPropertyBase
> property
= propertyFromAttribute(name
);
192 addSVGLengthPropertyToPresentationAttributeStyle(style
, CSSPropertyX
, *m_x
->currentValue());
193 else if (property
== m_y
)
194 addSVGLengthPropertyToPresentationAttributeStyle(style
, CSSPropertyY
, *m_y
->currentValue());
196 SVGGraphicsElement::collectStyleForPresentationAttribute(name
, value
, style
);
199 void SVGUseElement::svgAttributeChanged(const QualifiedName
& attrName
)
201 if (attrName
== SVGNames::xAttr
202 || attrName
== SVGNames::yAttr
203 || attrName
== SVGNames::widthAttr
204 || attrName
== SVGNames::heightAttr
) {
205 SVGElement::InvalidationGuard
invalidationGuard(this);
207 if (attrName
== SVGNames::xAttr
208 || attrName
== SVGNames::yAttr
) {
209 invalidateSVGPresentationAttributeStyle();
210 setNeedsStyleRecalc(LocalStyleChange
,
211 StyleChangeReasonForTracing::fromAttribute(attrName
));
214 updateRelativeLengthsInformation();
215 if (m_targetElementInstance
) {
216 ASSERT(m_targetElementInstance
->correspondingElement());
217 transferUseWidthAndHeightIfNeeded(*this, m_targetElementInstance
.get(), *m_targetElementInstance
->correspondingElement());
220 LayoutObject
* object
= this->layoutObject();
222 markForLayoutAndParentResourceInvalidation(object
);
226 if (SVGURIReference::isKnownAttribute(attrName
)) {
227 SVGElement::InvalidationGuard
invalidationGuard(this);
228 if (isStructurallyExternal()) {
229 KURL url
= document().completeURL(hrefString());
230 if (url
.hasFragmentIdentifier()) {
231 FetchRequest
request(ResourceRequest(url
), localName());
232 setDocumentResource(DocumentResource::fetchSVGDocument(request
, document().fetcher()));
235 setDocumentResource(0);
238 invalidateShadowTree();
243 SVGGraphicsElement::svgAttributeChanged(attrName
);
246 static bool isDisallowedElement(Node
* node
)
248 // Spec: "Any 'svg', 'symbol', 'g', graphics element or other 'use' is potentially a template object that can be re-used
249 // (i.e., "instanced") in the SVG document via a 'use' element."
250 // "Graphics Element" is defined as 'circle', 'ellipse', 'image', 'line', 'path', 'polygon', 'polyline', 'rect', 'text'
251 // Excluded are anything that is used by reference or that only make sense to appear once in a document.
252 // We must also allow the shadow roots of other use elements.
253 if (node
->isShadowRoot() || node
->isTextNode())
256 if (!node
->isSVGElement())
259 Element
* element
= toElement(node
);
261 DEFINE_STATIC_LOCAL(HashSet
<QualifiedName
>, allowedElementTags
, ());
262 if (allowedElementTags
.isEmpty()) {
263 allowedElementTags
.add(SVGNames::aTag
);
264 allowedElementTags
.add(SVGNames::circleTag
);
265 allowedElementTags
.add(SVGNames::descTag
);
266 allowedElementTags
.add(SVGNames::ellipseTag
);
267 allowedElementTags
.add(SVGNames::gTag
);
268 allowedElementTags
.add(SVGNames::imageTag
);
269 allowedElementTags
.add(SVGNames::lineTag
);
270 allowedElementTags
.add(SVGNames::metadataTag
);
271 allowedElementTags
.add(SVGNames::pathTag
);
272 allowedElementTags
.add(SVGNames::polygonTag
);
273 allowedElementTags
.add(SVGNames::polylineTag
);
274 allowedElementTags
.add(SVGNames::rectTag
);
275 allowedElementTags
.add(SVGNames::svgTag
);
276 allowedElementTags
.add(SVGNames::switchTag
);
277 allowedElementTags
.add(SVGNames::symbolTag
);
278 allowedElementTags
.add(SVGNames::textTag
);
279 allowedElementTags
.add(SVGNames::textPathTag
);
280 allowedElementTags
.add(SVGNames::titleTag
);
281 allowedElementTags
.add(SVGNames::tspanTag
);
282 allowedElementTags
.add(SVGNames::useTag
);
284 return !allowedElementTags
.contains
<SVGAttributeHashTranslator
>(element
->tagQName());
287 static bool subtreeContainsDisallowedElement(Node
* start
)
289 if (isDisallowedElement(start
))
292 for (Node
* cur
= start
->firstChild(); cur
; cur
= cur
->nextSibling()) {
293 if (subtreeContainsDisallowedElement(cur
))
300 void SVGUseElement::scheduleShadowTreeRecreation()
302 if (inUseShadowTree())
304 m_needsShadowTreeRecreation
= true;
305 document().scheduleUseShadowTreeUpdate(*this);
308 void SVGUseElement::cancelShadowTreeRecreation()
310 m_needsShadowTreeRecreation
= false;
311 document().unscheduleUseShadowTreeUpdate(*this);
314 void SVGUseElement::clearInstanceRoot()
316 if (m_targetElementInstance
)
317 m_targetElementInstance
= nullptr;
320 void SVGUseElement::clearShadowTree()
324 // FIXME: We should try to optimize this, to at least allow partial reclones.
325 if (ShadowRoot
* shadowTreeRootElement
= userAgentShadowRoot())
326 shadowTreeRootElement
->removeChildren(OmitSubtreeModifiedEvent
);
328 removeAllOutgoingReferences();
331 void SVGUseElement::buildPendingResource()
333 if (inUseShadowTree())
336 cancelShadowTreeRecreation();
337 if (!referencedScope() || !inDocument())
341 Element
* target
= SVGURIReference::targetElementFromIRIString(hrefString(), treeScope(), &id
, externalDocument());
342 if (!target
|| !target
->inDocument()) {
343 // If we can't find the target of an external element, just give up.
344 // We can't observe if the target somewhen enters the external document, nor should we do it.
345 if (externalDocument())
350 referencedScope()->document().accessSVGExtensions().addPendingResource(id
, this);
351 ASSERT(hasPendingResources());
355 if (target
->isSVGElement()) {
356 buildShadowAndInstanceTree(toSVGElement(target
));
357 invalidateDependentShadowTrees();
360 ASSERT(!m_needsShadowTreeRecreation
);
363 static PassRefPtrWillBeRawPtr
<Node
> cloneNodeAndAssociate(Node
& toClone
)
365 RefPtrWillBeRawPtr
<Node
> clone
= toClone
.cloneNode(false);
366 if (!clone
->isSVGElement())
367 return clone
.release();
369 SVGElement
& svgElement
= toSVGElement(toClone
);
370 ASSERT(!svgElement
.correspondingElement());
371 toSVGElement(clone
.get())->setCorrespondingElement(&svgElement
);
372 if (EventTargetData
* data
= toClone
.eventTargetData())
373 data
->eventListenerMap
.copyEventListenersNotCreatedFromMarkupToTarget(clone
.get());
374 TrackExceptionState exceptionState
;
375 for (Node
* node
= toClone
.firstChild(); node
&& !exceptionState
.hadException(); node
= node
->nextSibling())
376 clone
->appendChild(cloneNodeAndAssociate(*node
), exceptionState
);
377 return clone
.release();
380 void SVGUseElement::buildShadowAndInstanceTree(SVGElement
* target
)
382 ASSERT(!m_targetElementInstance
);
383 ASSERT(!m_needsShadowTreeRecreation
);
385 // <use> creates a "user agent" shadow root. Do not build the shadow/instance tree for <use>
386 // elements living in a user agent shadow tree because they will get expanded in a second
387 // pass -- see expandUseElementsInShadowTree().
388 if (inUseShadowTree())
391 // Do not allow self-referencing.
392 // 'target' may be null, if it's a non SVG namespaced element.
393 if (!target
|| target
== this || isDisallowedElement(target
))
396 // Set up root SVG element in shadow tree.
397 RefPtrWillBeRawPtr
<Element
> newChild
= target
->cloneElementWithoutChildren();
398 m_targetElementInstance
= toSVGElement(newChild
.get());
399 ShadowRoot
* shadowTreeRootElement
= userAgentShadowRoot();
400 shadowTreeRootElement
->appendChild(newChild
.release());
402 // Clone the target subtree into the shadow tree, not handling <use> and <symbol> yet.
404 // SVG specification does not say a word about <use> & cycles. My view on this is: just ignore it!
405 // Non-appearing <use> content is easier to debug, then half-appearing content.
406 if (!buildShadowTree(target
, m_targetElementInstance
.get(), false)) {
411 if (instanceTreeIsLoading(m_targetElementInstance
.get()))
414 // Assure shadow tree building was successfull
415 ASSERT(m_targetElementInstance
);
416 ASSERT(m_targetElementInstance
->correspondingUseElement() == this);
417 ASSERT(m_targetElementInstance
->correspondingElement() == target
);
419 // Expand all <use> elements in the shadow tree.
420 // Expand means: replace the actual <use> element by what it references.
421 if (!expandUseElementsInShadowTree(m_targetElementInstance
.get())) {
426 // Expand all <symbol> elements in the shadow tree.
427 // Expand means: replace the actual <symbol> element by the <svg> element.
428 expandSymbolElementsInShadowTree(toSVGElement(shadowTreeRootElement
->firstChild()));
430 m_targetElementInstance
= toSVGElement(shadowTreeRootElement
->firstChild());
431 transferUseWidthAndHeightIfNeeded(*this, m_targetElementInstance
.get(), *m_targetElementInstance
->correspondingElement());
433 ASSERT(m_targetElementInstance
->parentNode() == shadowTreeRootElement
);
435 // Update relative length information.
436 updateRelativeLengthsInformation();
439 LayoutObject
* SVGUseElement::createLayoutObject(const ComputedStyle
&)
441 return new LayoutSVGTransformableContainer(this);
444 static bool isDirectReference(const SVGElement
& element
)
446 return isSVGPathElement(element
)
447 || isSVGRectElement(element
)
448 || isSVGCircleElement(element
)
449 || isSVGEllipseElement(element
)
450 || isSVGPolygonElement(element
)
451 || isSVGPolylineElement(element
)
452 || isSVGTextElement(element
);
455 void SVGUseElement::toClipPath(Path
& path
) const
457 ASSERT(path
.isEmpty());
459 const SVGGraphicsElement
* element
= targetGraphicsElementForClipping();
464 if (element
->isSVGGeometryElement()) {
465 toSVGGeometryElement(*element
).toClipPath(path
);
466 // FIXME: Avoid manual resolution of x/y here. Its potentially harmful.
467 SVGLengthContext
lengthContext(this);
468 path
.translate(FloatSize(m_x
->currentValue()->value(lengthContext
), m_y
->currentValue()->value(lengthContext
)));
469 path
.transform(calculateAnimatedLocalTransform());
473 SVGGraphicsElement
* SVGUseElement::targetGraphicsElementForClipping() const
475 Node
* n
= userAgentShadowRoot()->firstChild();
476 if (!n
|| !n
->isSVGElement())
479 SVGElement
& element
= toSVGElement(*n
);
481 if (!element
.isSVGGraphicsElement())
484 // Spec: "If a <use> element is a child of a clipPath element, it must directly
485 // reference <path>, <text> or basic shapes elements. Indirect references are an
486 // error and the clipPath element must be ignored."
487 // http://dev.w3.org/fxtf/css-masking-1/#the-clip-path
488 if (!isDirectReference(element
)) {
489 // Spec: Indirect references are an error (14.3.5)
490 document().accessSVGExtensions().reportError("Not allowed to use indirect reference in <clip-path>");
494 return &toSVGGraphicsElement(element
);
497 bool SVGUseElement::buildShadowTree(SVGElement
* target
, SVGElement
* targetInstance
, bool foundUse
)
500 ASSERT(targetInstance
);
502 // Spec: If the referenced object is itself a 'use', or if there are 'use' subelements within the referenced
503 // object, the instance tree will contain recursive expansion of the indirect references to form a complete tree.
504 if (isSVGUseElement(*target
)) {
505 // We only need to track first degree <use> dependencies. Indirect references are handled
506 // as the invalidation bubbles up the dependency chain.
507 if (!foundUse
&& !isStructurallyExternal()) {
508 addReferenceTo(target
);
511 } else if (isDisallowedElement(target
)) {
515 targetInstance
->setCorrespondingElement(target
);
516 if (EventTargetData
* data
= target
->eventTargetData())
517 data
->eventListenerMap
.copyEventListenersNotCreatedFromMarkupToTarget(targetInstance
);
519 for (Node
* child
= target
->firstChild(); child
; child
= child
->nextSibling()) {
520 // Skip any disallowed element.
521 if (isDisallowedElement(child
))
524 RefPtrWillBeRawPtr
<Node
> newChild
= child
->cloneNode(false);
525 targetInstance
->appendChild(newChild
.get());
526 if (newChild
->isSVGElement()) {
527 // Enter recursion, appending new instance tree nodes to the "instance" object.
528 if (!buildShadowTree(toSVGElement(child
), toSVGElement(newChild
), foundUse
))
535 bool SVGUseElement::hasCycleUseReferencing(SVGUseElement
* use
, ContainerNode
* targetInstance
, SVGElement
*& newTarget
)
537 ASSERT(referencedScope());
538 Element
* targetElement
= SVGURIReference::targetElementFromIRIString(use
->hrefString(), *referencedScope());
540 if (targetElement
&& targetElement
->isSVGElement())
541 newTarget
= toSVGElement(targetElement
);
546 // Shortcut for self-references
547 if (newTarget
== this)
550 AtomicString targetId
= newTarget
->getIdAttribute();
551 ContainerNode
* instance
= targetInstance
->parentNode();
552 while (instance
&& instance
->isSVGElement()) {
553 SVGElement
* element
= toSVGElement(instance
);
554 if (element
->hasID() && element
->getIdAttribute() == targetId
&& element
->document() == newTarget
->document())
557 instance
= instance
->parentNode();
562 static inline void removeDisallowedElementsFromSubtree(Element
& subtree
)
564 ASSERT(!subtree
.inDocument());
565 Element
* element
= ElementTraversal::firstWithin(subtree
);
567 if (isDisallowedElement(element
)) {
568 Element
* next
= ElementTraversal::nextSkippingChildren(*element
, &subtree
);
569 // The subtree is not in document so this won't generate events that could mutate the tree.
570 element
->parentNode()->removeChild(element
);
573 element
= ElementTraversal::next(*element
, &subtree
);
578 bool SVGUseElement::expandUseElementsInShadowTree(SVGElement
* element
)
581 // Why expand the <use> elements in the shadow tree here, and not just
582 // do this directly in buildShadowTree, if we encounter a <use> element?
584 // Short answer: Because we may miss to expand some elements. For example, if a <symbol>
585 // contains <use> tags, we'd miss them. So once we're done with setting up the
586 // actual shadow tree (after the special case modification for svg/symbol) we have
587 // to walk it completely and expand all <use> elements.
588 if (isSVGUseElement(*element
)) {
589 SVGUseElement
* use
= toSVGUseElement(element
);
590 ASSERT(!use
->resourceIsStillLoading());
592 SVGElement
* target
= 0;
593 if (hasCycleUseReferencing(toSVGUseElement(use
->correspondingElement()), use
, target
))
596 if (target
&& isDisallowedElement(target
))
598 // Don't ASSERT(target) here, it may be "pending", too.
599 // Setup sub-shadow tree root node
600 RefPtrWillBeRawPtr
<SVGGElement
> cloneParent
= SVGGElement::create(referencedScope()->document());
601 cloneParent
->setCorrespondingElement(use
->correspondingElement());
603 // Move already cloned elements to the new <g> element
604 for (Node
* child
= use
->firstChild(); child
; ) {
605 Node
* nextChild
= child
->nextSibling();
606 cloneParent
->appendChild(child
);
610 // Spec: In the generated content, the 'use' will be replaced by 'g', where all attributes from the
611 // 'use' element except for x, y, width, height and xlink:href are transferred to the generated 'g' element.
612 transferUseAttributesToReplacedElement(use
, cloneParent
.get());
615 RefPtrWillBeRawPtr
<Node
> newChild
= cloneNodeAndAssociate(*target
);
616 ASSERT(newChild
->isSVGElement());
617 transferUseWidthAndHeightIfNeeded(*use
, toSVGElement(newChild
.get()), *target
);
618 cloneParent
->appendChild(newChild
.release());
621 // We don't walk the target tree element-by-element, and clone each element,
622 // but instead use cloneElementWithChildren(). This is an optimization for the common
623 // case where <use> doesn't contain disallowed elements (ie. <foreignObject>).
624 // Though if there are disallowed elements in the subtree, we have to remove them.
625 // For instance: <use> on <g> containing <foreignObject> (indirect case).
626 if (subtreeContainsDisallowedElement(cloneParent
.get()))
627 removeDisallowedElementsFromSubtree(*cloneParent
);
629 RefPtrWillBeRawPtr
<SVGElement
> replacingElement(cloneParent
.get());
631 // Replace <use> with referenced content.
632 ASSERT(use
->parentNode());
633 use
->parentNode()->replaceChild(cloneParent
.release(), use
);
635 // Expand the siblings because the *element* is replaced and we will
636 // lose the sibling chain when we are back from recursion.
637 element
= replacingElement
.get();
638 for (RefPtrWillBeRawPtr
<SVGElement
> sibling
= Traversal
<SVGElement
>::nextSibling(*element
); sibling
; sibling
= Traversal
<SVGElement
>::nextSibling(*sibling
)) {
639 if (!expandUseElementsInShadowTree(sibling
.get()))
644 for (RefPtrWillBeRawPtr
<SVGElement
> child
= Traversal
<SVGElement
>::firstChild(*element
); child
; child
= Traversal
<SVGElement
>::nextSibling(*child
)) {
645 if (!expandUseElementsInShadowTree(child
.get()))
651 void SVGUseElement::expandSymbolElementsInShadowTree(SVGElement
* element
)
654 if (isSVGSymbolElement(*element
)) {
655 // Spec: The referenced 'symbol' and its contents are deep-cloned into the generated tree,
656 // with the exception that the 'symbol' is replaced by an 'svg'. This generated 'svg' will
657 // always have explicit values for attributes width and height. If attributes width and/or
658 // height are provided on the 'use' element, then these attributes will be transferred to
659 // the generated 'svg'. If attributes width and/or height are not specified, the generated
660 // 'svg' element will use values of 100% for these attributes.
661 ASSERT(referencedScope());
662 RefPtrWillBeRawPtr
<SVGSVGElement
> svgElement
= SVGSVGElement::create(referencedScope()->document());
663 // Transfer all data (attributes, etc.) from <symbol> to the new <svg> element.
664 svgElement
->cloneDataFromElement(*element
);
665 svgElement
->setCorrespondingElement(element
->correspondingElement());
667 // Move already cloned elements to the new <svg> element
668 for (Node
* child
= element
->firstChild(); child
; ) {
669 Node
* nextChild
= child
->nextSibling();
670 svgElement
->appendChild(child
);
674 // We don't walk the target tree element-by-element, and clone each element,
675 // but instead use cloneNode(deep=true). This is an optimization for the common
676 // case where <use> doesn't contain disallowed elements (ie. <foreignObject>).
677 // Though if there are disallowed elements in the subtree, we have to remove them.
678 // For instance: <use> on <g> containing <foreignObject> (indirect case).
679 if (subtreeContainsDisallowedElement(svgElement
.get()))
680 removeDisallowedElementsFromSubtree(*svgElement
);
682 RefPtrWillBeRawPtr
<SVGElement
> replacingElement(svgElement
.get());
684 // Replace <symbol> with <svg>.
685 ASSERT(element
->parentNode());
686 element
->parentNode()->replaceChild(svgElement
.release(), element
);
688 // Expand the siblings because the *element* is replaced and we will
689 // lose the sibling chain when we are back from recursion.
690 element
= replacingElement
.get();
693 for (RefPtrWillBeRawPtr
<SVGElement
> child
= Traversal
<SVGElement
>::firstChild(*element
); child
; child
= Traversal
<SVGElement
>::nextSibling(*child
))
694 expandSymbolElementsInShadowTree(child
.get());
697 void SVGUseElement::invalidateShadowTree()
699 if (!inActiveDocument() || m_needsShadowTreeRecreation
)
702 scheduleShadowTreeRecreation();
703 invalidateDependentShadowTrees();
706 void SVGUseElement::invalidateDependentShadowTrees()
708 // Recursively invalidate dependent <use> shadow trees
709 const WillBeHeapHashSet
<RawPtrWillBeWeakMember
<SVGElement
>>& instances
= instancesForElement();
710 for (SVGElement
* instance
: instances
) {
711 if (SVGUseElement
* element
= instance
->correspondingUseElement()) {
712 ASSERT(element
->inDocument());
713 element
->invalidateShadowTree();
718 void SVGUseElement::transferUseAttributesToReplacedElement(SVGElement
* from
, SVGElement
* to
) const
723 to
->cloneDataFromElement(*from
);
725 to
->removeAttribute(SVGNames::xAttr
);
726 to
->removeAttribute(SVGNames::yAttr
);
727 to
->removeAttribute(SVGNames::widthAttr
);
728 to
->removeAttribute(SVGNames::heightAttr
);
729 to
->removeAttribute(XLinkNames::hrefAttr
);
732 bool SVGUseElement::selfHasRelativeLengths() const
734 if (m_x
->currentValue()->isRelative()
735 || m_y
->currentValue()->isRelative()
736 || m_width
->currentValue()->isRelative()
737 || m_height
->currentValue()->isRelative())
740 if (!m_targetElementInstance
)
743 return m_targetElementInstance
->hasRelativeLengths();
746 FloatRect
SVGUseElement::getBBox()
748 document().updateLayoutIgnorePendingStylesheets();
753 LayoutSVGTransformableContainer
& transformableContainer
= toLayoutSVGTransformableContainer(*layoutObject());
754 // Don't apply the additional translation if the oBB is invalid.
755 if (!transformableContainer
.isObjectBoundingBoxValid())
758 // TODO(fs): Preferably this would just use objectBoundingBox() (and hence
759 // don't need to override SVGGraphicsElement::getBBox at all) and be
760 // correct without additional work. That will not work out ATM without
761 // additional quirks. The problem stems from including the additional
762 // translation directly on the LayoutObject corresponding to the
764 FloatRect bbox
= transformableContainer
.objectBoundingBox();
765 bbox
.move(transformableContainer
.additionalTranslation());
769 void SVGUseElement::dispatchPendingEvent(SVGUseEventSender
* eventSender
)
771 ASSERT_UNUSED(eventSender
, eventSender
== &svgUseLoadEventSender());
772 ASSERT(isStructurallyExternal() && m_haveFiredLoadEvent
);
773 dispatchEvent(Event::create(EventTypeNames::load
));
776 void SVGUseElement::notifyFinished(Resource
* resource
)
781 invalidateShadowTree();
782 if (resource
->errorOccurred())
783 dispatchEvent(Event::create(EventTypeNames::error
));
784 else if (!resource
->wasCanceled()) {
785 if (m_haveFiredLoadEvent
)
787 if (!isStructurallyExternal())
789 ASSERT(!m_haveFiredLoadEvent
);
790 m_haveFiredLoadEvent
= true;
791 svgUseLoadEventSender().dispatchEventSoon(this);
795 bool SVGUseElement::resourceIsStillLoading()
797 if (m_resource
&& m_resource
->isLoading())
802 bool SVGUseElement::instanceTreeIsLoading(SVGElement
* targetInstance
)
804 for (SVGElement
* element
= Traversal
<SVGElement
>::firstChild(*targetInstance
); element
; element
= Traversal
<SVGElement
>::nextSibling(*element
)) {
805 if (SVGUseElement
* use
= element
->correspondingUseElement()) {
806 if (use
->resourceIsStillLoading())
809 if (element
->hasChildren() && instanceTreeIsLoading(element
))
815 void SVGUseElement::setDocumentResource(ResourcePtr
<DocumentResource
> resource
)
817 if (m_resource
== resource
)
821 m_resource
->removeClient(this);
823 m_resource
= resource
;
825 m_resource
->addClient(this);