1/*
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>
8 *
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.
13 *
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.
18 *
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.
23 */
24
25#include "config.h"
26
27#include "core/svg/SVGUseElement.h"
28
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/RenderSVGResource.h"
39#include "core/rendering/svg/RenderSVGTransformableContainer.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"
44
45namespace blink {
46
47inline SVGUseElement::SVGUseElement(Document& document)
48    : SVGGraphicsElement(SVGNames::useTag, document)
49    , SVGURIReference(this)
50    , m_x(SVGAnimatedLength::create(this, SVGNames::xAttr, SVGLength::create(LengthModeWidth), AllowNegativeLengths))
51    , m_y(SVGAnimatedLength::create(this, SVGNames::yAttr, SVGLength::create(LengthModeHeight), AllowNegativeLengths))
52    , m_width(SVGAnimatedLength::create(this, SVGNames::widthAttr, SVGLength::create(LengthModeWidth), ForbidNegativeLengths))
53    , m_height(SVGAnimatedLength::create(this, SVGNames::heightAttr, SVGLength::create(LengthModeHeight), ForbidNegativeLengths))
54    , m_haveFiredLoadEvent(false)
55    , m_needsShadowTreeRecreation(false)
56    , m_svgLoadEventTimer(this, &SVGElement::svgLoadEventTimerFired)
57{
58    ASSERT(hasCustomStyleCallbacks());
59
60    addToPropertyMap(m_x);
61    addToPropertyMap(m_y);
62    addToPropertyMap(m_width);
63    addToPropertyMap(m_height);
64}
65
66PassRefPtrWillBeRawPtr<SVGUseElement> SVGUseElement::create(Document& document)
67{
68    // Always build a user agent #shadow-root for SVGUseElement.
69    RefPtrWillBeRawPtr<SVGUseElement> use = adoptRefWillBeNoop(new SVGUseElement(document));
70    use->ensureUserAgentShadowRoot();
71    return use.release();
72}
73
74SVGUseElement::~SVGUseElement()
75{
76    setDocumentResource(0);
77#if !ENABLE(OILPAN)
78    clearResourceReferences();
79#endif
80}
81
82bool SVGUseElement::isSupportedAttribute(const QualifiedName& attrName)
83{
84    DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
85    if (supportedAttributes.isEmpty()) {
86        SVGURIReference::addSupportedAttributes(supportedAttributes);
87        supportedAttributes.add(SVGNames::xAttr);
88        supportedAttributes.add(SVGNames::yAttr);
89        supportedAttributes.add(SVGNames::widthAttr);
90        supportedAttributes.add(SVGNames::heightAttr);
91    }
92    return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
93}
94
95void SVGUseElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
96{
97    parseAttributeNew(name, value);
98}
99
100#if ENABLE(ASSERT)
101static inline bool isWellFormedDocument(Document* document)
102{
103    if (document->isXMLDocument())
104        return static_cast<XMLDocumentParser*>(document->parser())->wellFormed();
105    return true;
106}
107#endif
108
109Node::InsertionNotificationRequest SVGUseElement::insertedInto(ContainerNode* rootParent)
110{
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    if (!isStructurallyExternal())
119        sendSVGLoadEventIfPossibleAsynchronously();
120    return InsertionDone;
121}
122
123void SVGUseElement::removedFrom(ContainerNode* rootParent)
124{
125    SVGGraphicsElement::removedFrom(rootParent);
126    if (rootParent->inDocument())
127        clearResourceReferences();
128}
129
130TreeScope* SVGUseElement::referencedScope() const
131{
132    if (!isExternalURIReference(hrefString(), document()))
133        return &treeScope();
134    return externalDocument();
135}
136
137Document* SVGUseElement::externalDocument() const
138{
139    if (m_resource && m_resource->isLoaded()) {
140        // Gracefully handle error condition.
141        if (m_resource->errorOccurred())
142            return 0;
143        ASSERT(m_resource->document());
144        return m_resource->document();
145    }
146    return 0;
147}
148
149void transferUseWidthAndHeightIfNeeded(const SVGUseElement& use, SVGElement* shadowElement, const SVGElement& originalElement)
150{
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()));
165        else
166            shadowElement->setAttribute(SVGNames::widthAttr, originalElement.getAttribute(SVGNames::widthAttr));
167        if (use.height()->isSpecified())
168            shadowElement->setAttribute(SVGNames::heightAttr, AtomicString(use.height()->currentValue()->valueAsString()));
169        else
170            shadowElement->setAttribute(SVGNames::heightAttr, originalElement.getAttribute(SVGNames::heightAttr));
171    }
172}
173
174void SVGUseElement::svgAttributeChanged(const QualifiedName& attrName)
175{
176    if (!isSupportedAttribute(attrName)) {
177        SVGGraphicsElement::svgAttributeChanged(attrName);
178        return;
179    }
180
181    SVGElement::InvalidationGuard invalidationGuard(this);
182
183    RenderObject* renderer = this->renderer();
184    if (attrName == SVGNames::xAttr
185        || attrName == SVGNames::yAttr
186        || attrName == SVGNames::widthAttr
187        || attrName == SVGNames::heightAttr) {
188        updateRelativeLengthsInformation();
189        if (m_targetElementInstance) {
190            ASSERT(m_targetElementInstance->correspondingElement());
191            transferUseWidthAndHeightIfNeeded(*this, m_targetElementInstance.get(), *m_targetElementInstance->correspondingElement());
192        }
193        if (renderer)
194            RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
195        return;
196    }
197
198    if (SVGURIReference::isKnownAttribute(attrName)) {
199        bool isExternalReference = isExternalURIReference(hrefString(), document());
200        if (isExternalReference) {
201            KURL url = document().completeURL(hrefString());
202            if (url.hasFragmentIdentifier()) {
203                FetchRequest request(ResourceRequest(url), localName());
204                setDocumentResource(document().fetcher()->fetchSVGDocument(request));
205            }
206        } else {
207            setDocumentResource(0);
208        }
209
210        invalidateShadowTree();
211
212        return;
213    }
214
215    if (!renderer)
216        return;
217
218    ASSERT_NOT_REACHED();
219}
220
221static bool isDisallowedElement(Node* node)
222{
223    // Spec: "Any 'svg', 'symbol', 'g', graphics element or other 'use' is potentially a template object that can be re-used
224    // (i.e., "instanced") in the SVG document via a 'use' element."
225    // "Graphics Element" is defined as 'circle', 'ellipse', 'image', 'line', 'path', 'polygon', 'polyline', 'rect', 'text'
226    // Excluded are anything that is used by reference or that only make sense to appear once in a document.
227    // We must also allow the shadow roots of other use elements.
228    if (node->isShadowRoot() || node->isTextNode())
229        return false;
230
231    if (!node->isSVGElement())
232        return true;
233
234    Element* element = toElement(node);
235
236    DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, allowedElementTags, ());
237    if (allowedElementTags.isEmpty()) {
238        allowedElementTags.add(SVGNames::aTag);
239        allowedElementTags.add(SVGNames::circleTag);
240        allowedElementTags.add(SVGNames::descTag);
241        allowedElementTags.add(SVGNames::ellipseTag);
242        allowedElementTags.add(SVGNames::gTag);
243        allowedElementTags.add(SVGNames::imageTag);
244        allowedElementTags.add(SVGNames::lineTag);
245        allowedElementTags.add(SVGNames::metadataTag);
246        allowedElementTags.add(SVGNames::pathTag);
247        allowedElementTags.add(SVGNames::polygonTag);
248        allowedElementTags.add(SVGNames::polylineTag);
249        allowedElementTags.add(SVGNames::rectTag);
250        allowedElementTags.add(SVGNames::svgTag);
251        allowedElementTags.add(SVGNames::switchTag);
252        allowedElementTags.add(SVGNames::symbolTag);
253        allowedElementTags.add(SVGNames::textTag);
254        allowedElementTags.add(SVGNames::textPathTag);
255        allowedElementTags.add(SVGNames::titleTag);
256        allowedElementTags.add(SVGNames::tspanTag);
257        allowedElementTags.add(SVGNames::useTag);
258    }
259    return !allowedElementTags.contains<SVGAttributeHashTranslator>(element->tagQName());
260}
261
262static bool subtreeContainsDisallowedElement(Node* start)
263{
264    if (isDisallowedElement(start))
265        return true;
266
267    for (Node* cur = start->firstChild(); cur; cur = cur->nextSibling()) {
268        if (subtreeContainsDisallowedElement(cur))
269            return true;
270    }
271
272    return false;
273}
274
275void SVGUseElement::scheduleShadowTreeRecreation()
276{
277    if (!referencedScope() || inUseShadowTree())
278        return;
279    m_needsShadowTreeRecreation = true;
280    document().scheduleUseShadowTreeUpdate(*this);
281}
282
283void SVGUseElement::clearResourceReferences()
284{
285    if (m_targetElementInstance)
286        m_targetElementInstance = nullptr;
287
288    // FIXME: We should try to optimize this, to at least allow partial reclones.
289    if (ShadowRoot* shadowTreeRootElement = userAgentShadowRoot())
290        shadowTreeRootElement->removeChildren();
291
292    m_needsShadowTreeRecreation = false;
293    document().unscheduleUseShadowTreeUpdate(*this);
294
295    removeAllOutgoingReferences();
296}
297
298void SVGUseElement::buildPendingResource()
299{
300    if (!referencedScope() || inUseShadowTree())
301        return;
302    clearResourceReferences();
303    if (!inDocument())
304        return;
305
306    AtomicString id;
307    Element* target = SVGURIReference::targetElementFromIRIString(hrefString(), treeScope(), &id, externalDocument());
308    if (!target || !target->inDocument()) {
309        // If we can't find the target of an external element, just give up.
310        // We can't observe if the target somewhen enters the external document, nor should we do it.
311        if (externalDocument())
312            return;
313        if (id.isEmpty())
314            return;
315
316        referencedScope()->document().accessSVGExtensions().addPendingResource(id, this);
317        ASSERT(hasPendingResources());
318        return;
319    }
320
321    if (target->isSVGElement()) {
322        buildShadowAndInstanceTree(toSVGElement(target));
323        invalidateDependentShadowTrees();
324    }
325
326    ASSERT(!m_needsShadowTreeRecreation);
327}
328
329static PassRefPtrWillBeRawPtr<Node> cloneNodeAndAssociate(Node& toClone)
330{
331    RefPtrWillBeRawPtr<Node> clone = toClone.cloneNode(false);
332    if (!clone->isSVGElement())
333        return clone.release();
334
335    SVGElement& svgElement = toSVGElement(toClone);
336    ASSERT(!svgElement.correspondingElement());
337    toSVGElement(clone.get())->setCorrespondingElement(&svgElement);
338    if (EventTargetData* data = toClone.eventTargetData())
339        data->eventListenerMap.copyEventListenersNotCreatedFromMarkupToTarget(clone.get());
340    TrackExceptionState exceptionState;
341    for (Node* node = toClone.firstChild(); node && !exceptionState.hadException(); node = node->nextSibling())
342        clone->appendChild(cloneNodeAndAssociate(*node), exceptionState);
343    return clone.release();
344}
345
346void SVGUseElement::buildShadowAndInstanceTree(SVGElement* target)
347{
348    ASSERT(!m_targetElementInstance);
349
350    // <use> creates a "user agent" shadow root. Do not build the shadow/instance tree for <use>
351    // elements living in a user agent shadow tree because they will get expanded in a second
352    // pass -- see expandUseElementsInShadowTree().
353    if (inUseShadowTree())
354        return;
355
356    // Do not allow self-referencing.
357    // 'target' may be null, if it's a non SVG namespaced element.
358    if (!target || target == this)
359        return;
360
361    // Set up root SVG element in shadow tree.
362    RefPtrWillBeRawPtr<Element> newChild = target->cloneElementWithoutChildren();
363    m_targetElementInstance = toSVGElement(newChild.get());
364    ShadowRoot* shadowTreeRootElement = userAgentShadowRoot();
365    shadowTreeRootElement->appendChild(newChild.release());
366
367    // Clone the target subtree into the shadow tree, not handling <use> and <symbol> yet.
368
369    // SVG specification does not say a word about <use> & cycles. My view on this is: just ignore it!
370    // Non-appearing <use> content is easier to debug, then half-appearing content.
371    if (!buildShadowTree(target, m_targetElementInstance.get(), false)) {
372        clearResourceReferences();
373        return;
374    }
375
376    if (instanceTreeIsLoading(m_targetElementInstance.get()))
377        return;
378
379    // Assure shadow tree building was successfull
380    ASSERT(m_targetElementInstance);
381    ASSERT(m_targetElementInstance->correspondingUseElement() == this);
382    ASSERT(m_targetElementInstance->correspondingElement() == target);
383
384    // Expand all <use> elements in the shadow tree.
385    // Expand means: replace the actual <use> element by what it references.
386    if (!expandUseElementsInShadowTree(m_targetElementInstance.get())) {
387        clearResourceReferences();
388        return;
389    }
390
391    // Expand all <symbol> elements in the shadow tree.
392    // Expand means: replace the actual <symbol> element by the <svg> element.
393    expandSymbolElementsInShadowTree(toSVGElement(shadowTreeRootElement->firstChild()));
394
395    m_targetElementInstance = toSVGElement(shadowTreeRootElement->firstChild());
396    transferUseWidthAndHeightIfNeeded(*this, m_targetElementInstance.get(), *m_targetElementInstance->correspondingElement());
397
398    ASSERT(m_targetElementInstance->parentNode() == shadowTreeRootElement);
399
400    // Update relative length information.
401    updateRelativeLengthsInformation();
402}
403
404RenderObject* SVGUseElement::createRenderer(RenderStyle*)
405{
406    return new RenderSVGTransformableContainer(this);
407}
408
409static bool isDirectReference(const SVGElement& element)
410{
411    return isSVGPathElement(element)
412        || isSVGRectElement(element)
413        || isSVGCircleElement(element)
414        || isSVGEllipseElement(element)
415        || isSVGPolygonElement(element)
416        || isSVGPolylineElement(element)
417        || isSVGTextElement(element);
418}
419
420void SVGUseElement::toClipPath(Path& path)
421{
422    ASSERT(path.isEmpty());
423
424    Node* n = userAgentShadowRoot()->firstChild();
425    if (!n || !n->isSVGElement())
426        return;
427    SVGElement& element = toSVGElement(*n);
428
429    if (element.isSVGGraphicsElement()) {
430        if (!isDirectReference(element)) {
431            // Spec: Indirect references are an error (14.3.5)
432            document().accessSVGExtensions().reportError("Not allowed to use indirect reference in <clip-path>");
433        } else {
434            toSVGGraphicsElement(element).toClipPath(path);
435            // FIXME: Avoid manual resolution of x/y here. Its potentially harmful.
436            SVGLengthContext lengthContext(this);
437            path.translate(FloatSize(m_x->currentValue()->value(lengthContext), m_y->currentValue()->value(lengthContext)));
438            path.transform(animatedLocalTransform());
439        }
440    }
441}
442
443RenderObject* SVGUseElement::rendererClipChild() const
444{
445    if (Node* n = userAgentShadowRoot()->firstChild()) {
446        if (n->isSVGElement() && isDirectReference(toSVGElement(*n)))
447            return n->renderer();
448    }
449
450    return 0;
451}
452
453bool SVGUseElement::buildShadowTree(SVGElement* target, SVGElement* targetInstance, bool foundUse)
454{
455    ASSERT(target);
456    ASSERT(targetInstance);
457
458    // Spec: If the referenced object is itself a 'use', or if there are 'use' subelements within the referenced
459    // object, the instance tree will contain recursive expansion of the indirect references to form a complete tree.
460    if (isSVGUseElement(*target)) {
461        // We only need to track first degree <use> dependencies. Indirect references are handled
462        // as the invalidation bubbles up the dependency chain.
463        if (!foundUse && !isStructurallyExternal()) {
464            addReferenceTo(target);
465            foundUse = true;
466        }
467    } else if (isDisallowedElement(target)) {
468        return false;
469    }
470
471    targetInstance->setCorrespondingElement(target);
472    if (EventTargetData* data = target->eventTargetData())
473        data->eventListenerMap.copyEventListenersNotCreatedFromMarkupToTarget(targetInstance);
474
475    for (Node* child = target->firstChild(); child; child = child->nextSibling()) {
476        // Skip any disallowed element.
477        if (isDisallowedElement(child))
478            continue;
479
480        RefPtrWillBeRawPtr<Node> newChild = child->cloneNode(false);
481        targetInstance->appendChild(newChild.get());
482        if (newChild->isSVGElement()) {
483            // Enter recursion, appending new instance tree nodes to the "instance" object.
484            if (!buildShadowTree(toSVGElement(child), toSVGElement(newChild), foundUse))
485                return false;
486        }
487    }
488    return true;
489}
490
491bool SVGUseElement::hasCycleUseReferencing(SVGUseElement* use, ContainerNode* targetInstance, SVGElement*& newTarget)
492{
493    ASSERT(referencedScope());
494    Element* targetElement = SVGURIReference::targetElementFromIRIString(use->hrefString(), *referencedScope());
495    newTarget = 0;
496    if (targetElement && targetElement->isSVGElement())
497        newTarget = toSVGElement(targetElement);
498
499    if (!newTarget)
500        return false;
501
502    // Shortcut for self-references
503    if (newTarget == this)
504        return true;
505
506    AtomicString targetId = newTarget->getIdAttribute();
507    ContainerNode* instance = targetInstance->parentNode();
508    while (instance && instance->isSVGElement()) {
509        SVGElement* element = toSVGElement(instance);
510        if (element->hasID() && element->getIdAttribute() == targetId && element->document() == newTarget->document())
511            return true;
512
513        instance = instance->parentNode();
514    }
515    return false;
516}
517
518static inline void removeDisallowedElementsFromSubtree(Element& subtree)
519{
520    ASSERT(!subtree.inDocument());
521    Element* element = ElementTraversal::firstWithin(subtree);
522    while (element) {
523        if (isDisallowedElement(element)) {
524            Element* next = ElementTraversal::nextSkippingChildren(*element, &subtree);
525            // The subtree is not in document so this won't generate events that could mutate the tree.
526            element->parentNode()->removeChild(element);
527            element = next;
528        } else {
529            element = ElementTraversal::next(*element, &subtree);
530        }
531    }
532}
533
534bool SVGUseElement::expandUseElementsInShadowTree(SVGElement* element)
535{
536    ASSERT(element);
537    // Why expand the <use> elements in the shadow tree here, and not just
538    // do this directly in buildShadowTree, if we encounter a <use> element?
539    //
540    // Short answer: Because we may miss to expand some elements. For example, if a <symbol>
541    // contains <use> tags, we'd miss them. So once we're done with setting up the
542    // actual shadow tree (after the special case modification for svg/symbol) we have
543    // to walk it completely and expand all <use> elements.
544    if (isSVGUseElement(*element)) {
545        SVGUseElement* use = toSVGUseElement(element);
546        ASSERT(!use->resourceIsStillLoading());
547
548        SVGElement* target = 0;
549        if (hasCycleUseReferencing(toSVGUseElement(use->correspondingElement()), use, target))
550            return false;
551
552        if (target && isDisallowedElement(target))
553            return false;
554        // Don't ASSERT(target) here, it may be "pending", too.
555        // Setup sub-shadow tree root node
556        RefPtrWillBeRawPtr<SVGGElement> cloneParent = SVGGElement::create(referencedScope()->document());
557        cloneParent->setCorrespondingElement(use->correspondingElement());
558
559        // Move already cloned elements to the new <g> element
560        for (Node* child = use->firstChild(); child; ) {
561            Node* nextChild = child->nextSibling();
562            cloneParent->appendChild(child);
563            child = nextChild;
564        }
565
566        // Spec: In the generated content, the 'use' will be replaced by 'g', where all attributes from the
567        // 'use' element except for x, y, width, height and xlink:href are transferred to the generated 'g' element.
568        transferUseAttributesToReplacedElement(use, cloneParent.get());
569
570        if (target) {
571            RefPtrWillBeRawPtr<Node> newChild = cloneNodeAndAssociate(*target);
572            ASSERT(newChild->isSVGElement());
573            transferUseWidthAndHeightIfNeeded(*use, toSVGElement(newChild.get()), *target);
574            cloneParent->appendChild(newChild.release());
575        }
576
577        // We don't walk the target tree element-by-element, and clone each element,
578        // but instead use cloneElementWithChildren(). This is an optimization for the common
579        // case where <use> doesn't contain disallowed elements (ie. <foreignObject>).
580        // Though if there are disallowed elements in the subtree, we have to remove them.
581        // For instance: <use> on <g> containing <foreignObject> (indirect case).
582        if (subtreeContainsDisallowedElement(cloneParent.get()))
583            removeDisallowedElementsFromSubtree(*cloneParent);
584
585        RefPtrWillBeRawPtr<SVGElement> replacingElement(cloneParent.get());
586
587        // Replace <use> with referenced content.
588        ASSERT(use->parentNode());
589        use->parentNode()->replaceChild(cloneParent.release(), use);
590
591        // Expand the siblings because the *element* is replaced and we will
592        // lose the sibling chain when we are back from recursion.
593        element = replacingElement.get();
594        for (RefPtrWillBeRawPtr<SVGElement> sibling = Traversal<SVGElement>::nextSibling(*element); sibling; sibling = Traversal<SVGElement>::nextSibling(*sibling)) {
595            if (!expandUseElementsInShadowTree(sibling.get()))
596                return false;
597        }
598    }
599
600    for (RefPtrWillBeRawPtr<SVGElement> child = Traversal<SVGElement>::firstChild(*element); child; child = Traversal<SVGElement>::nextSibling(*child)) {
601        if (!expandUseElementsInShadowTree(child.get()))
602            return false;
603    }
604    return true;
605}
606
607void SVGUseElement::expandSymbolElementsInShadowTree(SVGElement* element)
608{
609    ASSERT(element);
610    if (isSVGSymbolElement(*element)) {
611        // Spec: The referenced 'symbol' and its contents are deep-cloned into the generated tree,
612        // with the exception that the 'symbol' is replaced by an 'svg'. This generated 'svg' will
613        // always have explicit values for attributes width and height. If attributes width and/or
614        // height are provided on the 'use' element, then these attributes will be transferred to
615        // the generated 'svg'. If attributes width and/or height are not specified, the generated
616        // 'svg' element will use values of 100% for these attributes.
617        ASSERT(referencedScope());
618        RefPtrWillBeRawPtr<SVGSVGElement> svgElement = SVGSVGElement::create(referencedScope()->document());
619        // Transfer all data (attributes, etc.) from <symbol> to the new <svg> element.
620        svgElement->cloneDataFromElement(*element);
621        svgElement->setCorrespondingElement(element->correspondingElement());
622
623        // Move already cloned elements to the new <svg> element
624        for (Node* child = element->firstChild(); child; ) {
625            Node* nextChild = child->nextSibling();
626            svgElement->appendChild(child);
627            child = nextChild;
628        }
629
630        // We don't walk the target tree element-by-element, and clone each element,
631        // but instead use cloneNode(deep=true). This is an optimization for the common
632        // case where <use> doesn't contain disallowed elements (ie. <foreignObject>).
633        // Though if there are disallowed elements in the subtree, we have to remove them.
634        // For instance: <use> on <g> containing <foreignObject> (indirect case).
635        if (subtreeContainsDisallowedElement(svgElement.get()))
636            removeDisallowedElementsFromSubtree(*svgElement);
637
638        RefPtrWillBeRawPtr<SVGElement> replacingElement(svgElement.get());
639
640        // Replace <symbol> with <svg>.
641        ASSERT(element->parentNode());
642        element->parentNode()->replaceChild(svgElement.release(), element);
643
644        // Expand the siblings because the *element* is replaced and we will
645        // lose the sibling chain when we are back from recursion.
646        element = replacingElement.get();
647    }
648
649    for (RefPtrWillBeRawPtr<SVGElement> child = Traversal<SVGElement>::firstChild(*element); child; child = Traversal<SVGElement>::nextSibling(*child))
650        expandSymbolElementsInShadowTree(child.get());
651}
652
653void SVGUseElement::invalidateShadowTree()
654{
655    if (!inActiveDocument() || m_needsShadowTreeRecreation)
656        return;
657    scheduleShadowTreeRecreation();
658    invalidateDependentShadowTrees();
659}
660
661void SVGUseElement::invalidateDependentShadowTrees()
662{
663    // Recursively invalidate dependent <use> shadow trees
664    const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >& instances = instancesForElement();
665    const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator end = instances.end();
666    for (WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator it = instances.begin(); it != end; ++it) {
667        if (SVGUseElement* element = (*it)->correspondingUseElement()) {
668            ASSERT(element->inDocument());
669            element->invalidateShadowTree();
670        }
671    }
672}
673
674void SVGUseElement::transferUseAttributesToReplacedElement(SVGElement* from, SVGElement* to) const
675{
676    ASSERT(from);
677    ASSERT(to);
678
679    to->cloneDataFromElement(*from);
680
681    to->removeAttribute(SVGNames::xAttr);
682    to->removeAttribute(SVGNames::yAttr);
683    to->removeAttribute(SVGNames::widthAttr);
684    to->removeAttribute(SVGNames::heightAttr);
685    to->removeAttribute(XLinkNames::hrefAttr);
686}
687
688bool SVGUseElement::selfHasRelativeLengths() const
689{
690    if (m_x->currentValue()->isRelative()
691        || m_y->currentValue()->isRelative()
692        || m_width->currentValue()->isRelative()
693        || m_height->currentValue()->isRelative())
694        return true;
695
696    if (!m_targetElementInstance)
697        return false;
698
699    return m_targetElementInstance->hasRelativeLengths();
700}
701
702void SVGUseElement::notifyFinished(Resource* resource)
703{
704    if (!inDocument())
705        return;
706
707    invalidateShadowTree();
708    if (resource->errorOccurred())
709        dispatchEvent(Event::create(EventTypeNames::error));
710    else if (!resource->wasCanceled()) {
711        if (m_haveFiredLoadEvent)
712            return;
713        if (!isStructurallyExternal())
714            return;
715        ASSERT(!m_haveFiredLoadEvent);
716        m_haveFiredLoadEvent = true;
717        sendSVGLoadEventIfPossibleAsynchronously();
718    }
719}
720
721bool SVGUseElement::resourceIsStillLoading()
722{
723    if (m_resource && m_resource->isLoading())
724        return true;
725    return false;
726}
727
728bool SVGUseElement::instanceTreeIsLoading(SVGElement* targetInstance)
729{
730    for (SVGElement* element = Traversal<SVGElement>::firstChild(*targetInstance); element; element = Traversal<SVGElement>::nextSibling(*element)) {
731        if (SVGUseElement* use = element->correspondingUseElement()) {
732            if (use->resourceIsStillLoading())
733                return true;
734        }
735        if (element->hasChildren() && instanceTreeIsLoading(element))
736            return true;
737    }
738    return false;
739}
740
741void SVGUseElement::setDocumentResource(ResourcePtr<DocumentResource> resource)
742{
743    if (m_resource == resource)
744        return;
745
746    if (m_resource)
747        m_resource->removeClient(this);
748
749    m_resource = resource;
750    if (m_resource)
751        m_resource->addClient(this);
752}
753
754void SVGUseElement::trace(Visitor* visitor)
755{
756    visitor->trace(m_targetElementInstance);
757    SVGGraphicsElement::trace(visitor);
758}
759
760}
761