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/XLinkNames.h"
31 #include "core/dom/Document.h"
32 #include "core/dom/ElementTraversal.h"
33 #include "core/events/Event.h"
34 #include "core/dom/shadow/ElementShadow.h"
35 #include "core/dom/shadow/ShadowRoot.h"
36 #include "core/fetch/FetchRequest.h"
37 #include "core/fetch/ResourceFetcher.h"
38 #include "core/rendering/svg/RenderSVGTransformableContainer.h"
39 #include "core/svg/SVGGElement.h"
40 #include "core/svg/SVGLengthContext.h"
41 #include "core/svg/SVGSVGElement.h"
42 #include "core/xml/parser/XMLDocumentParser.h"
46 inline SVGUseElement::SVGUseElement(Document& document)
47 : SVGGraphicsElement(SVGNames::useTag, document)
48 , SVGURIReference(this)
49 , m_x(SVGAnimatedLength::create(this, SVGNames::xAttr, SVGLength::create(LengthModeWidth), AllowNegativeLengths))
50 , m_y(SVGAnimatedLength::create(this, SVGNames::yAttr, SVGLength::create(LengthModeHeight), AllowNegativeLengths))
51 , m_width(SVGAnimatedLength::create(this, SVGNames::widthAttr, SVGLength::create(LengthModeWidth), ForbidNegativeLengths))
52 , m_height(SVGAnimatedLength::create(this, SVGNames::heightAttr, SVGLength::create(LengthModeHeight), ForbidNegativeLengths))
53 , m_haveFiredLoadEvent(false)
54 , m_needsShadowTreeRecreation(false)
55 , m_svgLoadEventTimer(this, &SVGElement::svgLoadEventTimerFired)
57 ASSERT(hasCustomStyleCallbacks());
59 addToPropertyMap(m_x);
60 addToPropertyMap(m_y);
61 addToPropertyMap(m_width);
62 addToPropertyMap(m_height);
65 PassRefPtrWillBeRawPtr<SVGUseElement> SVGUseElement::create(Document& document)
67 // Always build a user agent #shadow-root for SVGUseElement.
68 RefPtrWillBeRawPtr<SVGUseElement> use = adoptRefWillBeNoop(new SVGUseElement(document));
69 use->ensureUserAgentShadowRoot();
73 SVGUseElement::~SVGUseElement()
75 setDocumentResource(0);
77 clearResourceReferences();
81 bool SVGUseElement::isSupportedAttribute(const QualifiedName& attrName)
83 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
84 if (supportedAttributes.isEmpty()) {
85 SVGURIReference::addSupportedAttributes(supportedAttributes);
86 supportedAttributes.add(SVGNames::xAttr);
87 supportedAttributes.add(SVGNames::yAttr);
88 supportedAttributes.add(SVGNames::widthAttr);
89 supportedAttributes.add(SVGNames::heightAttr);
91 return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
94 void SVGUseElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
96 parseAttributeNew(name, value);
100 static inline bool isWellFormedDocument(Document* document)
102 if (document->isXMLDocument())
103 return static_cast<XMLDocumentParser*>(document->parser())->wellFormed();
108 Node::InsertionNotificationRequest SVGUseElement::insertedInto(ContainerNode* rootParent)
110 // This functions exists to assure assumptions made in the code regarding SVGElementInstance creation/destruction are satisfied.
111 SVGGraphicsElement::insertedInto(rootParent);
112 if (!rootParent->inDocument())
113 return InsertionDone;
114 ASSERT(!m_targetElementInstance || !isWellFormedDocument(&document()));
115 ASSERT(!hasPendingResources() || !isWellFormedDocument(&document()));
116 invalidateShadowTree();
117 if (!isStructurallyExternal())
118 sendSVGLoadEventIfPossibleAsynchronously();
119 return InsertionDone;
122 void SVGUseElement::removedFrom(ContainerNode* rootParent)
124 SVGGraphicsElement::removedFrom(rootParent);
125 if (rootParent->inDocument())
126 clearResourceReferences();
129 TreeScope* SVGUseElement::referencedScope() const
131 if (!isExternalURIReference(hrefString(), document()))
133 return externalDocument();
136 Document* SVGUseElement::externalDocument() const
138 if (m_resource && m_resource->isLoaded()) {
139 // Gracefully handle error condition.
140 if (m_resource->errorOccurred())
142 ASSERT(m_resource->document());
143 return m_resource->document();
148 void transferUseWidthAndHeightIfNeeded(const SVGUseElement& use, SVGElement* shadowElement, const SVGElement& originalElement)
150 DEFINE_STATIC_LOCAL(const AtomicString, hundredPercentString, ("100%", AtomicString::ConstructFromLiteral));
151 ASSERT(shadowElement);
152 if (isSVGSymbolElement(*shadowElement)) {
153 // Spec (<use> on <symbol>): This generated 'svg' will always have explicit values for attributes width and height.
154 // If attributes width and/or height are provided on the 'use' element, then these attributes
155 // will be transferred to the generated 'svg'. If attributes width and/or height are not specified,
156 // the generated 'svg' element will use values of 100% for these attributes.
157 shadowElement->setAttribute(SVGNames::widthAttr, use.width()->isSpecified() ? AtomicString(use.width()->currentValue()->valueAsString()) : hundredPercentString);
158 shadowElement->setAttribute(SVGNames::heightAttr, use.height()->isSpecified() ? AtomicString(use.height()->currentValue()->valueAsString()) : hundredPercentString);
159 } else if (isSVGSVGElement(*shadowElement)) {
160 // Spec (<use> on <svg>): If attributes width and/or height are provided on the 'use' element, then these
161 // values will override the corresponding attributes on the 'svg' in the generated tree.
162 if (use.width()->isSpecified())
163 shadowElement->setAttribute(SVGNames::widthAttr, AtomicString(use.width()->currentValue()->valueAsString()));
165 shadowElement->setAttribute(SVGNames::widthAttr, originalElement.getAttribute(SVGNames::widthAttr));
166 if (use.height()->isSpecified())
167 shadowElement->setAttribute(SVGNames::heightAttr, AtomicString(use.height()->currentValue()->valueAsString()));
169 shadowElement->setAttribute(SVGNames::heightAttr, originalElement.getAttribute(SVGNames::heightAttr));
173 void SVGUseElement::svgAttributeChanged(const QualifiedName& attrName)
175 if (!isSupportedAttribute(attrName)) {
176 SVGGraphicsElement::svgAttributeChanged(attrName);
180 SVGElement::InvalidationGuard invalidationGuard(this);
182 RenderObject* renderer = this->renderer();
183 if (attrName == SVGNames::xAttr
184 || attrName == SVGNames::yAttr
185 || attrName == SVGNames::widthAttr
186 || attrName == SVGNames::heightAttr) {
187 updateRelativeLengthsInformation();
188 if (m_targetElementInstance) {
189 ASSERT(m_targetElementInstance->correspondingElement());
190 transferUseWidthAndHeightIfNeeded(*this, m_targetElementInstance.get(), *m_targetElementInstance->correspondingElement());
193 markForLayoutAndParentResourceInvalidation(renderer);
197 if (SVGURIReference::isKnownAttribute(attrName)) {
198 bool isExternalReference = isExternalURIReference(hrefString(), document());
199 if (isExternalReference) {
200 KURL url = document().completeURL(hrefString());
201 if (url.hasFragmentIdentifier()) {
202 FetchRequest request(ResourceRequest(url), localName());
203 setDocumentResource(document().fetcher()->fetchSVGDocument(request));
206 setDocumentResource(0);
209 invalidateShadowTree();
217 ASSERT_NOT_REACHED();
220 static bool isDisallowedElement(Node* node)
222 // Spec: "Any 'svg', 'symbol', 'g', graphics element or other 'use' is potentially a template object that can be re-used
223 // (i.e., "instanced") in the SVG document via a 'use' element."
224 // "Graphics Element" is defined as 'circle', 'ellipse', 'image', 'line', 'path', 'polygon', 'polyline', 'rect', 'text'
225 // Excluded are anything that is used by reference or that only make sense to appear once in a document.
226 // We must also allow the shadow roots of other use elements.
227 if (node->isShadowRoot() || node->isTextNode())
230 if (!node->isSVGElement())
233 Element* element = toElement(node);
235 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, allowedElementTags, ());
236 if (allowedElementTags.isEmpty()) {
237 allowedElementTags.add(SVGNames::aTag);
238 allowedElementTags.add(SVGNames::circleTag);
239 allowedElementTags.add(SVGNames::descTag);
240 allowedElementTags.add(SVGNames::ellipseTag);
241 allowedElementTags.add(SVGNames::gTag);
242 allowedElementTags.add(SVGNames::imageTag);
243 allowedElementTags.add(SVGNames::lineTag);
244 allowedElementTags.add(SVGNames::metadataTag);
245 allowedElementTags.add(SVGNames::pathTag);
246 allowedElementTags.add(SVGNames::polygonTag);
247 allowedElementTags.add(SVGNames::polylineTag);
248 allowedElementTags.add(SVGNames::rectTag);
249 allowedElementTags.add(SVGNames::svgTag);
250 allowedElementTags.add(SVGNames::switchTag);
251 allowedElementTags.add(SVGNames::symbolTag);
252 allowedElementTags.add(SVGNames::textTag);
253 allowedElementTags.add(SVGNames::textPathTag);
254 allowedElementTags.add(SVGNames::titleTag);
255 allowedElementTags.add(SVGNames::tspanTag);
256 allowedElementTags.add(SVGNames::useTag);
258 return !allowedElementTags.contains<SVGAttributeHashTranslator>(element->tagQName());
261 static bool subtreeContainsDisallowedElement(Node* start)
263 if (isDisallowedElement(start))
266 for (Node* cur = start->firstChild(); cur; cur = cur->nextSibling()) {
267 if (subtreeContainsDisallowedElement(cur))
274 void SVGUseElement::scheduleShadowTreeRecreation()
276 if (!referencedScope() || inUseShadowTree())
278 m_needsShadowTreeRecreation = true;
279 document().scheduleUseShadowTreeUpdate(*this);
282 void SVGUseElement::clearResourceReferences()
284 if (m_targetElementInstance)
285 m_targetElementInstance = nullptr;
287 // FIXME: We should try to optimize this, to at least allow partial reclones.
288 if (ShadowRoot* shadowTreeRootElement = userAgentShadowRoot())
289 shadowTreeRootElement->removeChildren();
291 m_needsShadowTreeRecreation = false;
292 document().unscheduleUseShadowTreeUpdate(*this);
294 removeAllOutgoingReferences();
297 void SVGUseElement::buildPendingResource()
299 if (!referencedScope() || inUseShadowTree())
301 clearResourceReferences();
306 Element* target = SVGURIReference::targetElementFromIRIString(hrefString(), treeScope(), &id, externalDocument());
307 if (!target || !target->inDocument()) {
308 // If we can't find the target of an external element, just give up.
309 // We can't observe if the target somewhen enters the external document, nor should we do it.
310 if (externalDocument())
315 referencedScope()->document().accessSVGExtensions().addPendingResource(id, this);
316 ASSERT(hasPendingResources());
320 if (target->isSVGElement()) {
321 buildShadowAndInstanceTree(toSVGElement(target));
322 invalidateDependentShadowTrees();
325 ASSERT(!m_needsShadowTreeRecreation);
328 static PassRefPtrWillBeRawPtr<Node> cloneNodeAndAssociate(Node& toClone)
330 RefPtrWillBeRawPtr<Node> clone = toClone.cloneNode(false);
331 if (!clone->isSVGElement())
332 return clone.release();
334 SVGElement& svgElement = toSVGElement(toClone);
335 ASSERT(!svgElement.correspondingElement());
336 toSVGElement(clone.get())->setCorrespondingElement(&svgElement);
337 if (EventTargetData* data = toClone.eventTargetData())
338 data->eventListenerMap.copyEventListenersNotCreatedFromMarkupToTarget(clone.get());
339 TrackExceptionState exceptionState;
340 for (Node* node = toClone.firstChild(); node && !exceptionState.hadException(); node = node->nextSibling())
341 clone->appendChild(cloneNodeAndAssociate(*node), exceptionState);
342 return clone.release();
345 void SVGUseElement::buildShadowAndInstanceTree(SVGElement* target)
347 ASSERT(!m_targetElementInstance);
349 // <use> creates a "user agent" shadow root. Do not build the shadow/instance tree for <use>
350 // elements living in a user agent shadow tree because they will get expanded in a second
351 // pass -- see expandUseElementsInShadowTree().
352 if (inUseShadowTree())
355 // Do not allow self-referencing.
356 // 'target' may be null, if it's a non SVG namespaced element.
357 if (!target || target == this)
360 // Set up root SVG element in shadow tree.
361 RefPtrWillBeRawPtr<Element> newChild = target->cloneElementWithoutChildren();
362 m_targetElementInstance = toSVGElement(newChild.get());
363 ShadowRoot* shadowTreeRootElement = userAgentShadowRoot();
364 shadowTreeRootElement->appendChild(newChild.release());
366 // Clone the target subtree into the shadow tree, not handling <use> and <symbol> yet.
368 // SVG specification does not say a word about <use> & cycles. My view on this is: just ignore it!
369 // Non-appearing <use> content is easier to debug, then half-appearing content.
370 if (!buildShadowTree(target, m_targetElementInstance.get(), false)) {
371 clearResourceReferences();
375 if (instanceTreeIsLoading(m_targetElementInstance.get()))
378 // Assure shadow tree building was successfull
379 ASSERT(m_targetElementInstance);
380 ASSERT(m_targetElementInstance->correspondingUseElement() == this);
381 ASSERT(m_targetElementInstance->correspondingElement() == target);
383 // Expand all <use> elements in the shadow tree.
384 // Expand means: replace the actual <use> element by what it references.
385 if (!expandUseElementsInShadowTree(m_targetElementInstance.get())) {
386 clearResourceReferences();
390 // Expand all <symbol> elements in the shadow tree.
391 // Expand means: replace the actual <symbol> element by the <svg> element.
392 expandSymbolElementsInShadowTree(toSVGElement(shadowTreeRootElement->firstChild()));
394 m_targetElementInstance = toSVGElement(shadowTreeRootElement->firstChild());
395 transferUseWidthAndHeightIfNeeded(*this, m_targetElementInstance.get(), *m_targetElementInstance->correspondingElement());
397 ASSERT(m_targetElementInstance->parentNode() == shadowTreeRootElement);
399 // Update relative length information.
400 updateRelativeLengthsInformation();
403 RenderObject* SVGUseElement::createRenderer(RenderStyle*)
405 return new RenderSVGTransformableContainer(this);
408 static bool isDirectReference(const SVGElement& element)
410 return isSVGPathElement(element)
411 || isSVGRectElement(element)
412 || isSVGCircleElement(element)
413 || isSVGEllipseElement(element)
414 || isSVGPolygonElement(element)
415 || isSVGPolylineElement(element)
416 || isSVGTextElement(element);
419 void SVGUseElement::toClipPath(Path& path)
421 ASSERT(path.isEmpty());
423 Node* n = userAgentShadowRoot()->firstChild();
424 if (!n || !n->isSVGElement())
426 SVGElement& element = toSVGElement(*n);
428 if (element.isSVGGraphicsElement()) {
429 if (!isDirectReference(element)) {
430 // Spec: Indirect references are an error (14.3.5)
431 document().accessSVGExtensions().reportError("Not allowed to use indirect reference in <clip-path>");
433 toSVGGraphicsElement(element).toClipPath(path);
434 // FIXME: Avoid manual resolution of x/y here. Its potentially harmful.
435 SVGLengthContext lengthContext(this);
436 path.translate(FloatSize(m_x->currentValue()->value(lengthContext), m_y->currentValue()->value(lengthContext)));
437 path.transform(calculateAnimatedLocalTransform());
442 RenderObject* SVGUseElement::rendererClipChild() const
444 if (Node* n = userAgentShadowRoot()->firstChild()) {
445 if (n->isSVGElement() && isDirectReference(toSVGElement(*n)))
446 return n->renderer();
452 bool SVGUseElement::buildShadowTree(SVGElement* target, SVGElement* targetInstance, bool foundUse)
455 ASSERT(targetInstance);
457 // Spec: If the referenced object is itself a 'use', or if there are 'use' subelements within the referenced
458 // object, the instance tree will contain recursive expansion of the indirect references to form a complete tree.
459 if (isSVGUseElement(*target)) {
460 // We only need to track first degree <use> dependencies. Indirect references are handled
461 // as the invalidation bubbles up the dependency chain.
462 if (!foundUse && !isStructurallyExternal()) {
463 addReferenceTo(target);
466 } else if (isDisallowedElement(target)) {
470 targetInstance->setCorrespondingElement(target);
471 if (EventTargetData* data = target->eventTargetData())
472 data->eventListenerMap.copyEventListenersNotCreatedFromMarkupToTarget(targetInstance);
474 for (Node* child = target->firstChild(); child; child = child->nextSibling()) {
475 // Skip any disallowed element.
476 if (isDisallowedElement(child))
479 RefPtrWillBeRawPtr<Node> newChild = child->cloneNode(false);
480 targetInstance->appendChild(newChild.get());
481 if (newChild->isSVGElement()) {
482 // Enter recursion, appending new instance tree nodes to the "instance" object.
483 if (!buildShadowTree(toSVGElement(child), toSVGElement(newChild), foundUse))
490 bool SVGUseElement::hasCycleUseReferencing(SVGUseElement* use, ContainerNode* targetInstance, SVGElement*& newTarget)
492 ASSERT(referencedScope());
493 Element* targetElement = SVGURIReference::targetElementFromIRIString(use->hrefString(), *referencedScope());
495 if (targetElement && targetElement->isSVGElement())
496 newTarget = toSVGElement(targetElement);
501 // Shortcut for self-references
502 if (newTarget == this)
505 AtomicString targetId = newTarget->getIdAttribute();
506 ContainerNode* instance = targetInstance->parentNode();
507 while (instance && instance->isSVGElement()) {
508 SVGElement* element = toSVGElement(instance);
509 if (element->hasID() && element->getIdAttribute() == targetId && element->document() == newTarget->document())
512 instance = instance->parentNode();
517 static inline void removeDisallowedElementsFromSubtree(Element& subtree)
519 ASSERT(!subtree.inDocument());
520 Element* element = ElementTraversal::firstWithin(subtree);
522 if (isDisallowedElement(element)) {
523 Element* next = ElementTraversal::nextSkippingChildren(*element, &subtree);
524 // The subtree is not in document so this won't generate events that could mutate the tree.
525 element->parentNode()->removeChild(element);
528 element = ElementTraversal::next(*element, &subtree);
533 bool SVGUseElement::expandUseElementsInShadowTree(SVGElement* element)
536 // Why expand the <use> elements in the shadow tree here, and not just
537 // do this directly in buildShadowTree, if we encounter a <use> element?
539 // Short answer: Because we may miss to expand some elements. For example, if a <symbol>
540 // contains <use> tags, we'd miss them. So once we're done with setting up the
541 // actual shadow tree (after the special case modification for svg/symbol) we have
542 // to walk it completely and expand all <use> elements.
543 if (isSVGUseElement(*element)) {
544 SVGUseElement* use = toSVGUseElement(element);
545 ASSERT(!use->resourceIsStillLoading());
547 SVGElement* target = 0;
548 if (hasCycleUseReferencing(toSVGUseElement(use->correspondingElement()), use, target))
551 if (target && isDisallowedElement(target))
553 // Don't ASSERT(target) here, it may be "pending", too.
554 // Setup sub-shadow tree root node
555 RefPtrWillBeRawPtr<SVGGElement> cloneParent = SVGGElement::create(referencedScope()->document());
556 cloneParent->setCorrespondingElement(use->correspondingElement());
558 // Move already cloned elements to the new <g> element
559 for (Node* child = use->firstChild(); child; ) {
560 Node* nextChild = child->nextSibling();
561 cloneParent->appendChild(child);
565 // Spec: In the generated content, the 'use' will be replaced by 'g', where all attributes from the
566 // 'use' element except for x, y, width, height and xlink:href are transferred to the generated 'g' element.
567 transferUseAttributesToReplacedElement(use, cloneParent.get());
570 RefPtrWillBeRawPtr<Node> newChild = cloneNodeAndAssociate(*target);
571 ASSERT(newChild->isSVGElement());
572 transferUseWidthAndHeightIfNeeded(*use, toSVGElement(newChild.get()), *target);
573 cloneParent->appendChild(newChild.release());
576 // We don't walk the target tree element-by-element, and clone each element,
577 // but instead use cloneElementWithChildren(). This is an optimization for the common
578 // case where <use> doesn't contain disallowed elements (ie. <foreignObject>).
579 // Though if there are disallowed elements in the subtree, we have to remove them.
580 // For instance: <use> on <g> containing <foreignObject> (indirect case).
581 if (subtreeContainsDisallowedElement(cloneParent.get()))
582 removeDisallowedElementsFromSubtree(*cloneParent);
584 RefPtrWillBeRawPtr<SVGElement> replacingElement(cloneParent.get());
586 // Replace <use> with referenced content.
587 ASSERT(use->parentNode());
588 use->parentNode()->replaceChild(cloneParent.release(), use);
590 // Expand the siblings because the *element* is replaced and we will
591 // lose the sibling chain when we are back from recursion.
592 element = replacingElement.get();
593 for (RefPtrWillBeRawPtr<SVGElement> sibling = Traversal<SVGElement>::nextSibling(*element); sibling; sibling = Traversal<SVGElement>::nextSibling(*sibling)) {
594 if (!expandUseElementsInShadowTree(sibling.get()))
599 for (RefPtrWillBeRawPtr<SVGElement> child = Traversal<SVGElement>::firstChild(*element); child; child = Traversal<SVGElement>::nextSibling(*child)) {
600 if (!expandUseElementsInShadowTree(child.get()))
606 void SVGUseElement::expandSymbolElementsInShadowTree(SVGElement* element)
609 if (isSVGSymbolElement(*element)) {
610 // Spec: The referenced 'symbol' and its contents are deep-cloned into the generated tree,
611 // with the exception that the 'symbol' is replaced by an 'svg'. This generated 'svg' will
612 // always have explicit values for attributes width and height. If attributes width and/or
613 // height are provided on the 'use' element, then these attributes will be transferred to
614 // the generated 'svg'. If attributes width and/or height are not specified, the generated
615 // 'svg' element will use values of 100% for these attributes.
616 ASSERT(referencedScope());
617 RefPtrWillBeRawPtr<SVGSVGElement> svgElement = SVGSVGElement::create(referencedScope()->document());
618 // Transfer all data (attributes, etc.) from <symbol> to the new <svg> element.
619 svgElement->cloneDataFromElement(*element);
620 svgElement->setCorrespondingElement(element->correspondingElement());
622 // Move already cloned elements to the new <svg> element
623 for (Node* child = element->firstChild(); child; ) {
624 Node* nextChild = child->nextSibling();
625 svgElement->appendChild(child);
629 // We don't walk the target tree element-by-element, and clone each element,
630 // but instead use cloneNode(deep=true). This is an optimization for the common
631 // case where <use> doesn't contain disallowed elements (ie. <foreignObject>).
632 // Though if there are disallowed elements in the subtree, we have to remove them.
633 // For instance: <use> on <g> containing <foreignObject> (indirect case).
634 if (subtreeContainsDisallowedElement(svgElement.get()))
635 removeDisallowedElementsFromSubtree(*svgElement);
637 RefPtrWillBeRawPtr<SVGElement> replacingElement(svgElement.get());
639 // Replace <symbol> with <svg>.
640 ASSERT(element->parentNode());
641 element->parentNode()->replaceChild(svgElement.release(), element);
643 // Expand the siblings because the *element* is replaced and we will
644 // lose the sibling chain when we are back from recursion.
645 element = replacingElement.get();
648 for (RefPtrWillBeRawPtr<SVGElement> child = Traversal<SVGElement>::firstChild(*element); child; child = Traversal<SVGElement>::nextSibling(*child))
649 expandSymbolElementsInShadowTree(child.get());
652 void SVGUseElement::invalidateShadowTree()
654 if (!inActiveDocument() || m_needsShadowTreeRecreation)
656 scheduleShadowTreeRecreation();
657 invalidateDependentShadowTrees();
660 void SVGUseElement::invalidateDependentShadowTrees()
662 // Recursively invalidate dependent <use> shadow trees
663 const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >& instances = instancesForElement();
664 const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator end = instances.end();
665 for (WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator it = instances.begin(); it != end; ++it) {
666 if (SVGUseElement* element = (*it)->correspondingUseElement()) {
667 ASSERT(element->inDocument());
668 element->invalidateShadowTree();
673 void SVGUseElement::transferUseAttributesToReplacedElement(SVGElement* from, SVGElement* to) const
678 to->cloneDataFromElement(*from);
680 to->removeAttribute(SVGNames::xAttr);
681 to->removeAttribute(SVGNames::yAttr);
682 to->removeAttribute(SVGNames::widthAttr);
683 to->removeAttribute(SVGNames::heightAttr);
684 to->removeAttribute(XLinkNames::hrefAttr);
687 bool SVGUseElement::selfHasRelativeLengths() const
689 if (m_x->currentValue()->isRelative()
690 || m_y->currentValue()->isRelative()
691 || m_width->currentValue()->isRelative()
692 || m_height->currentValue()->isRelative())
695 if (!m_targetElementInstance)
698 return m_targetElementInstance->hasRelativeLengths();
701 void SVGUseElement::notifyFinished(Resource* resource)
706 invalidateShadowTree();
707 if (resource->errorOccurred())
708 dispatchEvent(Event::create(EventTypeNames::error));
709 else if (!resource->wasCanceled()) {
710 if (m_haveFiredLoadEvent)
712 if (!isStructurallyExternal())
714 ASSERT(!m_haveFiredLoadEvent);
715 m_haveFiredLoadEvent = true;
716 sendSVGLoadEventIfPossibleAsynchronously();
720 bool SVGUseElement::resourceIsStillLoading()
722 if (m_resource && m_resource->isLoading())
727 bool SVGUseElement::instanceTreeIsLoading(SVGElement* targetInstance)
729 for (SVGElement* element = Traversal<SVGElement>::firstChild(*targetInstance); element; element = Traversal<SVGElement>::nextSibling(*element)) {
730 if (SVGUseElement* use = element->correspondingUseElement()) {
731 if (use->resourceIsStillLoading())
734 if (element->hasChildren() && instanceTreeIsLoading(element))
740 void SVGUseElement::setDocumentResource(ResourcePtr<DocumentResource> resource)
742 if (m_resource == resource)
746 m_resource->removeClient(this);
748 m_resource = resource;
750 m_resource->addClient(this);
753 void SVGUseElement::trace(Visitor* visitor)
755 visitor->trace(m_targetElementInstance);
756 SVGGraphicsElement::trace(visitor);