Reviewed by Eric.
[WebKit-https.git] / WebCore / svg / SVGUseElement.cpp
1 /*
2     Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
3                   2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
4
5     This file is part of the KDE project
6
7     This library is free software; you can redistribute it and/or
8     modify it under the terms of the GNU Library General Public
9     License as published by the Free Software Foundation; either
10     version 2 of the License, or (at your option) any later version.
11
12     This library is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15     Library General Public License for more details.
16
17     You should have received a copy of the GNU Library General Public License
18     along with this library; see the file COPYING.LIB.  If not, write to
19     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20     Boston, MA 02110-1301, USA.
21 */
22
23 #include "config.h"
24
25 // Dump SVGElementInstance object tree - useful to debug instanceRoot problems
26 // #define DUMP_INSTANCE_TREE
27
28 // Dump the deep-expanded shadow tree (where the renderes are built from)
29 // #define DUMP_SHADOW_TREE
30
31 #if ENABLE(SVG)
32 #include "SVGUseElement.h"
33
34 #include "CSSStyleSelector.h"
35 #include "CString.h"
36 #include "Document.h"
37 #include "Event.h"
38 #include "HTMLNames.h"
39 #include "RenderSVGTransformableContainer.h"
40 #include "SVGElementInstance.h"
41 #include "SVGElementInstanceList.h"
42 #include "SVGGElement.h"
43 #include "SVGLength.h"
44 #include "SVGNames.h"
45 #include "SVGPreserveAspectRatio.h"
46 #include "SVGSVGElement.h"
47 #include "SVGSymbolElement.h"
48 #include "XLinkNames.h"
49 #include "XMLSerializer.h"
50 #include <wtf/OwnPtr.h>
51
52 namespace WebCore {
53
54 SVGUseElement::SVGUseElement(const QualifiedName& tagName, Document* doc)
55     : SVGStyledTransformableElement(tagName, doc)
56     , SVGTests()
57     , SVGLangSpace()
58     , SVGExternalResourcesRequired()
59     , SVGURIReference()
60     , m_x(this, LengthModeWidth)
61     , m_y(this, LengthModeHeight)
62     , m_width(this, LengthModeWidth)
63     , m_height(this, LengthModeHeight)
64 {
65 }
66
67 SVGUseElement::~SVGUseElement()
68 {
69 }
70
71 ANIMATED_PROPERTY_DEFINITIONS(SVGUseElement, SVGLength, Length, length, X, x, SVGNames::xAttr.localName(), m_x)
72 ANIMATED_PROPERTY_DEFINITIONS(SVGUseElement, SVGLength, Length, length, Y, y, SVGNames::yAttr.localName(), m_y)
73 ANIMATED_PROPERTY_DEFINITIONS(SVGUseElement, SVGLength, Length, length, Width, width, SVGNames::widthAttr.localName(), m_width)
74 ANIMATED_PROPERTY_DEFINITIONS(SVGUseElement, SVGLength, Length, length, Height, height, SVGNames::heightAttr.localName(), m_height)
75
76 SVGElementInstance* SVGUseElement::instanceRoot() const
77 {
78     return m_targetElementInstance.get();
79 }
80
81 SVGElementInstance* SVGUseElement::animatedInstanceRoot() const
82 {
83     // FIXME: Implement me.
84     return 0;
85 }
86  
87 void SVGUseElement::parseMappedAttribute(MappedAttribute* attr)
88 {
89     if (attr->name() == SVGNames::xAttr)
90         setXBaseValue(SVGLength(this, LengthModeWidth, attr->value()));
91     else if (attr->name() == SVGNames::yAttr)
92         setYBaseValue(SVGLength(this, LengthModeHeight, attr->value()));
93     else if (attr->name() == SVGNames::widthAttr) {
94         setWidthBaseValue(SVGLength(this, LengthModeWidth, attr->value()));
95         if (width().value() < 0.0)
96             document()->accessSVGExtensions()->reportError("A negative value for use attribute <width> is not allowed");
97     } else if (attr->name() == SVGNames::heightAttr) {
98         setHeightBaseValue(SVGLength(this, LengthModeHeight, attr->value()));
99         if (height().value() < 0.0)
100             document()->accessSVGExtensions()->reportError("A negative value for use attribute <height> is not allowed");
101     } else {
102         if (SVGTests::parseMappedAttribute(attr))
103             return;
104         if (SVGLangSpace::parseMappedAttribute(attr))
105             return;
106         if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
107             return;
108         if (SVGURIReference::parseMappedAttribute(attr))
109             return;
110         SVGStyledTransformableElement::parseMappedAttribute(attr);
111     }
112 }
113
114 void SVGUseElement::insertedIntoDocument()
115 {
116     SVGElement::insertedIntoDocument();
117
118     String id = SVGURIReference::getTarget(href());
119     Element* targetElement = document()->getElementById(id);
120     if (!targetElement) {
121         document()->accessSVGExtensions()->addPendingResource(id, this);
122         return;
123     }
124
125     buildPendingResource();
126 }
127
128 void SVGUseElement::removedFromDocument()
129 {
130     SVGElement::removedFromDocument();
131
132     m_targetElementInstance = 0;
133     m_shadowTreeRootElement = 0;
134 }
135
136 void SVGUseElement::attributeChanged(Attribute* attr, bool preserveDecls)
137 {
138     // Avoid calling SVGStyledElement::attributeChanged(), as it always calls notifyAttributeChange.
139     SVGElement::attributeChanged(attr, preserveDecls);
140
141     if (!attached())
142        return;
143
144     // Only update the tree if x/y/width/height or xlink:href changed.
145     if (attr->name() == SVGNames::xAttr || attr->name() == SVGNames::yAttr ||
146         attr->name() == SVGNames::widthAttr || attr->name() == SVGNames::heightAttr ||
147         attr->name().matches(XLinkNames::hrefAttr))
148         buildPendingResource();
149     else if (m_shadowTreeRootElement)
150         m_shadowTreeRootElement->setChanged();
151 }
152
153 void SVGUseElement::notifyAttributeChange() const
154 {
155     if (!attached())
156         return;
157
158     // NOTE: A lot of room for improvments here. This is too slow.
159     // It has to be done correctly, by implementing attributeChanged().
160     const_cast<SVGUseElement*>(this)->buildPendingResource();
161
162     if (m_shadowTreeRootElement)
163         m_shadowTreeRootElement->setChanged();
164 }
165
166 void SVGUseElement::recalcStyle(StyleChange change)
167 {
168     SVGStyledElement::recalcStyle(change);
169
170     // The shadow tree root element is NOT a direct child element of us.
171     // So we have to take care it receives style updates, manually.
172     if (!m_shadowTreeRootElement || !m_shadowTreeRootElement->attached())
173         return;
174
175     // Mimic Element::recalcStyle(). The main difference is that we don't call attach() on the
176     // shadow tree root element, but call attachShadowTree() here. Calling attach() will crash
177     // as the shadow tree root element has no (direct) parent node. Yes, shadow trees are tricky.
178     if (change >= Inherit || m_shadowTreeRootElement->changed()) {
179         RenderStyle* newStyle = document()->styleSelector()->styleForElement(m_shadowTreeRootElement.get());
180         StyleChange ch = m_shadowTreeRootElement->diff(m_shadowTreeRootElement->renderStyle(), newStyle);
181         if (ch == Detach) {
182             ASSERT(m_shadowTreeRootElement->attached());
183             m_shadowTreeRootElement->detach();
184             attachShadowTree();
185
186             // attach recalulates the style for all children. No need to do it twice.
187             m_shadowTreeRootElement->setChanged(NoStyleChange);
188             m_shadowTreeRootElement->setHasChangedChild(false);
189             newStyle->deref(document()->renderArena());
190             return;
191         }
192
193         newStyle->deref(document()->renderArena());
194     }
195
196     // Only change==Detach needs special treatment, for anything else recalcStyle() works.
197     m_shadowTreeRootElement->recalcStyle(change);
198 }
199
200 #ifdef DUMP_INSTANCE_TREE
201 void dumpInstanceTree(unsigned int& depth, String& text, SVGElementInstance* targetInstance)
202 {
203     SVGElement* element = targetInstance->correspondingElement();
204     ASSERT(element);
205
206     String elementId = element->getIDAttribute();
207     String elementNodeName = element->nodeName();
208     String parentNodeName = element->parentNode() ? element->parentNode()->nodeName() : "null";
209     String firstChildNodeName = element->firstChild() ? element->firstChild()->nodeName() : "null";
210
211     for (unsigned int i = 0; i < depth; ++i)
212         text += "  ";
213
214     text += String::format("SVGElementInstance (parentNode=%s, firstChild=%s, correspondingElement=%s, id=%s)\n",
215                            parentNodeName.latin1().data(), firstChildNodeName.latin1().data(), elementNodeName.latin1().data(), elementId.latin1().data());
216  
217     depth++;
218
219     for (SVGElementInstance* instance = targetInstance->firstChild(); instance; instance = instance->nextSibling())
220         dumpInstanceTree(depth, text, instance);
221
222     depth--;
223 }
224 #endif
225
226 static bool isDisallowedElement(Node* element)
227 {
228     // <foreignObject> should never be contained in a <use> tree. Too dangerous side effects possible.
229     if (element->hasTagName(SVGNames::foreignObjectTag))
230         return true;
231
232     return false;
233 }
234
235 static bool subtreeContainsDisallowedElement(Node* start)
236 {
237     if (isDisallowedElement(start))
238         return true;
239
240     for (Node* cur = start->firstChild(); cur; cur = cur->nextSibling()) {
241         if (subtreeContainsDisallowedElement(cur))
242             return true;
243     }
244
245     return false;
246 }
247
248 void SVGUseElement::buildPendingResource()
249 {
250     // Do not build the shadow/instance tree for <use> elements living in a shadow tree.
251     // The will be expanded soon anyway - see expandUseElementsInShadowTree().
252     Node* parent = parentNode();
253     while (parent) {
254         if (parent->isShadowNode())
255             return;
256
257         parent = parent->parentNode();
258     }
259
260     String id = SVGURIReference::getTarget(href());
261     Element* targetElement = document()->getElementById(id); 
262     SVGElement* target = 0;
263     if (targetElement && targetElement->isSVGElement())
264         target = static_cast<SVGElement*>(targetElement);
265
266     // Do not allow self-referencing.
267     // 'target' may be null, if it's a non SVG namespaced element.
268     if (!target || target == this) {
269         m_targetElementInstance = 0;
270         m_shadowTreeRootElement = 0;
271         return;
272     }
273
274     // Why a seperated instance/shadow tree? SVG demands it:
275     // The instance tree is accesable from JavaScript, and has to
276     // expose a 1:1 copy of the referenced tree, whereas internally we need
277     // to alter the tree for correct "use-on-symbol", "use-on-svg" support.  
278  
279     // Build instance tree. Create root SVGElementInstance object for the first sub-tree node.
280     //
281     // Spec: If the 'use' element references a simple graphics element such as a 'rect', then there is only a
282     // single SVGElementInstance object, and the correspondingElement attribute on this SVGElementInstance object
283     // is the SVGRectElement that corresponds to the referenced 'rect' element.
284     m_targetElementInstance = new SVGElementInstance(this, target);
285
286     // Eventually enter recursion to build SVGElementInstance objects for the sub-tree children
287     bool foundProblem = false;
288     buildInstanceTree(target, m_targetElementInstance.get(), foundProblem);
289
290     // SVG specification does not say a word about <use> & cycles. My view on this is: just ignore it!
291     // Non-appearing <use> content is easier to debug, then half-appearing content.
292     if (foundProblem) {
293         m_targetElementInstance = 0;
294         m_shadowTreeRootElement = 0;
295         return;
296     }
297
298     // Assure instance tree building was successfull
299     ASSERT(m_targetElementInstance);
300     ASSERT(m_targetElementInstance->correspondingUseElement() == this);
301
302     // Setup shadow tree root node
303     m_shadowTreeRootElement = new SVGGElement(SVGNames::gTag, document());
304     m_shadowTreeRootElement->setInDocument();
305     m_shadowTreeRootElement->setShadowParentNode(this);
306
307     // Spec: An additional transformation translate(x,y) is appended to the end
308     // (i.e., right-side) of the transform attribute on the generated 'g', where x
309     // and y represent the values of the x and y attributes on the 'use' element. 
310     if (x().value() != 0.0 || y().value() != 0.0) {
311         String transformString = String::format("translate(%f, %f)", x().value(), y().value());
312         m_shadowTreeRootElement->setAttribute(SVGNames::transformAttr, transformString);
313     }
314
315     // Build shadow tree from instance tree
316     // This also handles the special cases: <use> on <symbol>, <use> on <svg>.
317     buildShadowTree(target, m_targetElementInstance.get());
318
319 #if ENABLE(SVG) && ENABLE(SVG_USE)
320     // Expand all <use> elements in the shadow tree.
321     // Expand means: replace the actual <use> element by what it references.
322     expandUseElementsInShadowTree(m_shadowTreeRootElement.get());
323
324     // Expand all <symbol> elements in the shadow tree.
325     // Expand means: replace the actual <symbol> element by the <svg> element.
326     expandSymbolElementsInShadowTree(m_shadowTreeRootElement.get());
327
328 #endif
329
330     // Now that the shadow tree is completly expanded, we can associate
331     // shadow tree elements <-> instances in the instance tree.
332     associateInstancesWithShadowTreeElements(m_shadowTreeRootElement->firstChild(), m_targetElementInstance.get());
333
334     // Eventually dump instance tree
335 #ifdef DUMP_INSTANCE_TREE
336     String text;
337     unsigned int depth = 0;
338
339     dumpInstanceTree(depth, text, m_targetElementInstance.get());
340     fprintf(stderr, "\nDumping <use> instance tree:\n%s\n", text.latin1().data());
341 #endif
342
343     // Eventually dump shadow tree
344 #ifdef DUMP_SHADOW_TREE
345     ExceptionCode ec = 0;
346     OwnPtr<XMLSerializer> serializer(new XMLSerializer());
347
348     String markup = serializer->serializeToString(m_shadowTreeRootElement.get(), ec);
349     ASSERT(ec == 0);
350
351     fprintf(stderr, "Dumping <use> shadow tree markup:\n%s\n", markup.latin1().data());
352 #endif
353
354     // The DOM side is setup properly. Now we have to attach the root shadow
355     // tree element manually - using attach() won't work for "shadow nodes".
356     attachShadowTree();
357 }
358
359 RenderObject* SVGUseElement::createRenderer(RenderArena* arena, RenderStyle*)
360 {
361     return new (arena) RenderSVGTransformableContainer(this);
362 }
363
364 void SVGUseElement::attach()
365 {
366     SVGStyledTransformableElement::attach();
367
368     // If we're a pending resource, this doesn't have any effect.
369     attachShadowTree();
370 }
371
372 void SVGUseElement::detach()
373 {
374     SVGStyledTransformableElement::detach();
375
376     if (m_shadowTreeRootElement)
377         m_shadowTreeRootElement->detach();
378 }
379
380 static bool isDirectReference(Node* n)
381 {
382     return n->hasTagName(SVGNames::pathTag) ||
383            n->hasTagName(SVGNames::rectTag) ||
384            n->hasTagName(SVGNames::circleTag) ||
385            n->hasTagName(SVGNames::ellipseTag) ||
386            n->hasTagName(SVGNames::polygonTag) ||
387            n->hasTagName(SVGNames::polylineTag) ||
388            n->hasTagName(SVGNames::textTag);
389 }
390
391 Path SVGUseElement::toClipPath() const
392 {
393     if (!m_shadowTreeRootElement)
394         const_cast<SVGUseElement*>(this)->buildPendingResource();
395
396     Node* n = m_shadowTreeRootElement->firstChild();
397     if (n->isSVGElement() && static_cast<SVGElement*>(n)->isStyledTransformable()) {
398         if (!isDirectReference(n))
399             // Spec: Indirect references are an error (14.3.5)
400             document()->accessSVGExtensions()->reportError("Not allowed to use indirect reference in <clip-path>");
401         else
402             return static_cast<SVGStyledTransformableElement*>(n)->toClipPath();
403     }
404
405     return Path();
406 }
407
408 void SVGUseElement::buildInstanceTree(SVGElement* target, SVGElementInstance* targetInstance, bool& foundProblem)
409 {
410     ASSERT(target);
411     ASSERT(targetInstance);
412
413     // A general description from the SVG spec, describing what buildInstanceTree() actually does.
414     //
415     // Spec: If the 'use' element references a 'g' which contains two 'rect' elements, then the instance tree
416     // contains three SVGElementInstance objects, a root SVGElementInstance object whose correspondingElement
417     // is the SVGGElement object for the 'g', and then two child SVGElementInstance objects, each of which has
418     // its correspondingElement that is an SVGRectElement object.
419
420     for (Node* node = target->firstChild(); node; node = node->nextSibling()) {
421         SVGElement* element = 0;
422         if (node->isSVGElement())
423             element = static_cast<SVGElement*>(node);
424
425         // Skip any non-svg nodes or any disallowed element.
426         if (!element || isDisallowedElement(element))
427             continue;
428
429         // Create SVGElementInstance object, for both container/non-container nodes.
430         SVGElementInstance* instancePtr = new SVGElementInstance(this, element);
431
432         RefPtr<SVGElementInstance> instance = instancePtr;
433         targetInstance->appendChild(instance.release());
434
435         // Enter recursion, appending new instance tree nodes to the "instance" object.
436         if (element->hasChildNodes())
437             buildInstanceTree(element, instancePtr, foundProblem);
438
439         // Spec: If the referenced object is itself a 'use', or if there are 'use' subelements within the referenced
440         // object, the instance tree will contain recursive expansion of the indirect references to form a complete tree.
441         if (element->hasTagName(SVGNames::useTag))
442             handleDeepUseReferencing(element, instancePtr, foundProblem);
443     }
444
445     // Spec: If the referenced object is itself a 'use', or if there are 'use' subelements within the referenced
446     // object, the instance tree will contain recursive expansion of the indirect references to form a complete tree.
447     if (target->hasTagName(SVGNames::useTag))
448         handleDeepUseReferencing(target, targetInstance, foundProblem);
449 }
450
451 void SVGUseElement::handleDeepUseReferencing(SVGElement* use, SVGElementInstance* targetInstance, bool& foundProblem)
452 {
453     String id = SVGURIReference::getTarget(use->href());
454     Element* targetElement = document()->getElementById(id); 
455     SVGElement* target = 0;
456     if (targetElement && targetElement->isSVGElement())
457         target = static_cast<SVGElement*>(targetElement);
458
459     if (!target)
460         return;
461
462     // Cycle detection first!
463     foundProblem = (target == this);
464
465     // Shortcut for self-references
466     if (foundProblem)
467         return;
468
469     SVGElementInstance* instance = targetInstance->parentNode();
470     while (instance) {
471         SVGElement* element = instance->correspondingElement();
472
473         if (element->getIDAttribute() == id) {
474             foundProblem = true;
475             return;
476         }
477     
478         instance = instance->parentNode();
479     }
480
481     // Create an instance object, even if we're dealing with a cycle
482     SVGElementInstance* newInstance = new SVGElementInstance(this, target);
483     targetInstance->appendChild(newInstance);
484
485     // Eventually enter recursion to build SVGElementInstance objects for the sub-tree children
486     buildInstanceTree(target, newInstance, foundProblem);
487 }
488
489 void SVGUseElement::alterShadowTreeForSVGTag(SVGElement* target)
490 {
491     String widthString = String::number(width().value());
492     String heightString = String::number(height().value());
493
494     if (hasAttribute(SVGNames::widthAttr))
495         target->setAttribute(SVGNames::widthAttr, widthString);
496
497     if (hasAttribute(SVGNames::heightAttr))
498         target->setAttribute(SVGNames::heightAttr, heightString);
499 }
500
501 void SVGUseElement::removeDisallowedElementsFromSubtree(Node* element)
502 {
503     ExceptionCode ec = 0;
504
505     for (RefPtr<Node> child = element->firstChild(); child; child = child->nextSibling()) {
506         if (isDisallowedElement(child.get())) {
507             ASSERT(child->parent());
508             child->parent()->removeChild(child.get(), ec);
509             ASSERT(ec == 0);
510
511             continue;
512         }
513
514         if (child->hasChildNodes())
515             removeDisallowedElementsFromSubtree(child.get());
516     }
517 }
518
519 void SVGUseElement::buildShadowTree(SVGElement* target, SVGElementInstance* targetInstance)
520 {
521     // For instance <use> on <foreignObject> (direct case).
522     if (isDisallowedElement(target))
523         return;
524
525     RefPtr<Node> newChild = targetInstance->correspondingElement()->cloneNode(true);
526
527     // We don't walk the target tree element-by-element, and clone each element,
528     // but instead use cloneNode(deep=true). This is an optimization for the common
529     // case where <use> doesn't contain disallowed elements (ie. <foreignObject>).
530     // Though if there are disallowed elements in the subtree, we have to remove them.
531     // For instance: <use> on <g> containing <foreignObject> (indirect case).
532     if (subtreeContainsDisallowedElement(newChild.get()))
533         removeDisallowedElementsFromSubtree(newChild.get());
534
535     SVGElement* newChildPtr = 0;
536     if (newChild->isSVGElement())
537         newChildPtr = static_cast<SVGElement*>(newChild.get());
538     ASSERT(newChildPtr);
539
540     ExceptionCode ec = 0;
541     m_shadowTreeRootElement->appendChild(newChild.release(), ec);
542     ASSERT(ec == 0);
543
544     // Handle use referencing <svg> special case
545     if (target->hasTagName(SVGNames::svgTag))
546         alterShadowTreeForSVGTag(newChildPtr);
547 }
548
549 #if ENABLE(SVG) && ENABLE(SVG_USE)
550 void SVGUseElement::expandUseElementsInShadowTree(Node* element)
551 {
552     // Why expand the <use> elements in the shadow tree here, and not just
553     // do this directly in buildShadowTree, if we encounter a <use> element?
554     //
555     // Short answer: Because we may miss to expand some elements. Ie. if a <symbol>
556     // contains <use> tags, we'd miss them. So once we're done with settin' up the
557     // actual shadow tree (after the special case modification for svg/symbol) we have
558     // to walk it completely and expand all <use> elements.
559     if (element->hasTagName(SVGNames::useTag)) {
560         SVGUseElement* use = static_cast<SVGUseElement*>(element);
561
562         String id = SVGURIReference::getTarget(use->href());
563         Element* targetElement = document()->getElementById(id); 
564         SVGElement* target = 0;
565         if (targetElement && targetElement->isSVGElement())
566             target = static_cast<SVGElement*>(targetElement);
567
568         // Don't ASSERT(target) here, it may be "pending", too.
569         if (target) {
570             // Setup sub-shadow tree root node
571             RefPtr<SVGElement> cloneParent = new SVGGElement(SVGNames::gTag, document());
572
573             // Spec: In the generated content, the 'use' will be replaced by 'g', where all attributes from the
574             // 'use' element except for x, y, width, height and xlink:href are transferred to the generated 'g' element.
575             transferUseAttributesToReplacedElement(use, cloneParent.get());
576
577             // Spec: An additional transformation translate(x,y) is appended to the end
578             // (i.e., right-side) of the transform attribute on the generated 'g', where x
579             // and y represent the values of the x and y attributes on the 'use' element.
580             if (use->x().value() != 0.0 || use->y().value() != 0.0) {
581                 if (!cloneParent->hasAttribute(SVGNames::transformAttr)) {
582                     String transformString = String::format("translate(%f, %f)", use->x().value(), use->y().value());
583                     cloneParent->setAttribute(SVGNames::transformAttr, transformString);
584                 } else {
585                     String transformString = String::format(" translate(%f, %f)", use->x().value(), use->y().value());
586                     const AtomicString& transformAttribute = cloneParent->getAttribute(SVGNames::transformAttr);
587                     cloneParent->setAttribute(SVGNames::transformAttr, transformAttribute + transformString); 
588                 }
589             }
590
591             ExceptionCode ec = 0;
592  
593             // For instance <use> on <foreignObject> (direct case).
594             if (isDisallowedElement(target)) {
595                 // We still have to setup the <use> replacment (<g>). Otherwhise
596                 // associateInstancesWithShadowTreeElements() makes wrong assumptions.
597                 // Replace <use> with referenced content.
598                 ASSERT(use->parentNode()); 
599                 use->parentNode()->replaceChild(cloneParent.release(), use, ec);
600                 ASSERT(ec == 0);
601                 return;
602             }
603
604             RefPtr<Node> newChild = target->cloneNode(true);
605
606             // We don't walk the target tree element-by-element, and clone each element,
607             // but instead use cloneNode(deep=true). This is an optimization for the common
608             // case where <use> doesn't contain disallowed elements (ie. <foreignObject>).
609             // Though if there are disallowed elements in the subtree, we have to remove them.
610             // For instance: <use> on <g> containing <foreignObject> (indirect case).
611             if (subtreeContainsDisallowedElement(newChild.get()))
612                 removeDisallowedElementsFromSubtree(newChild.get());
613
614             SVGElement* newChildPtr = 0;
615             if (newChild->isSVGElement())
616                 newChildPtr = static_cast<SVGElement*>(newChild.get());
617             ASSERT(newChildPtr);
618
619             cloneParent->appendChild(newChild.release(), ec);
620             ASSERT(ec == 0);
621
622             // Replace <use> with referenced content.
623             ASSERT(use->parentNode()); 
624             use->parentNode()->replaceChild(cloneParent.release(), use, ec);
625             ASSERT(ec == 0);
626
627             // Handle use referencing <svg> special case
628             if (target->hasTagName(SVGNames::svgTag))
629                 alterShadowTreeForSVGTag(newChildPtr);
630
631             // Immediately stop here, and restart expanding.
632             expandUseElementsInShadowTree(m_shadowTreeRootElement.get());
633             return;
634         }
635     }
636
637     for (RefPtr<Node> child = element->firstChild(); child; child = child->nextSibling())
638         expandUseElementsInShadowTree(child.get());
639 }
640
641 void SVGUseElement::expandSymbolElementsInShadowTree(Node* element)
642 {
643     if (element->hasTagName(SVGNames::symbolTag)) {
644         // Spec: The referenced 'symbol' and its contents are deep-cloned into the generated tree,
645         // with the exception that the 'symbol' is replaced by an 'svg'. This generated 'svg' will
646         // always have explicit values for attributes width and height. If attributes width and/or
647         // height are provided on the 'use' element, then these attributes will be transferred to
648         // the generated 'svg'. If attributes width and/or height are not specified, the generated
649         // 'svg' element will use values of 100% for these attributes.
650         RefPtr<SVGSVGElement> svgElement = new SVGSVGElement(SVGNames::svgTag, document());
651
652         // Transfer all attributes from <symbol> to the new <svg> element
653         *svgElement->attributes() = *element->attributes();
654
655         // Explicitly re-set width/height values
656         String widthString = String::number(width().value());
657         String heightString = String::number(height().value()); 
658
659         svgElement->setAttribute(SVGNames::widthAttr, hasAttribute(SVGNames::widthAttr) ? widthString : "100%");
660         svgElement->setAttribute(SVGNames::heightAttr, hasAttribute(SVGNames::heightAttr) ? heightString : "100%");
661
662         ExceptionCode ec = 0;
663
664         // Only clone symbol children, and add them to the new <svg> element    
665         for (Node* child = element->firstChild(); child; child = child->nextSibling()) {
666             RefPtr<Node> newChild = child->cloneNode(true);
667             svgElement->appendChild(newChild.release(), ec);
668             ASSERT(ec == 0);
669         }
670     
671         // We don't walk the target tree element-by-element, and clone each element,
672         // but instead use cloneNode(deep=true). This is an optimization for the common
673         // case where <use> doesn't contain disallowed elements (ie. <foreignObject>).
674         // Though if there are disallowed elements in the subtree, we have to remove them.
675         // For instance: <use> on <g> containing <foreignObject> (indirect case).
676         if (subtreeContainsDisallowedElement(svgElement.get()))
677             removeDisallowedElementsFromSubtree(svgElement.get());
678
679         // Replace <symbol> with <svg>.
680         ASSERT(element->parentNode()); 
681         element->parentNode()->replaceChild(svgElement.release(), element, ec);
682         ASSERT(ec == 0);
683
684         // Immediately stop here, and restart expanding.
685         expandSymbolElementsInShadowTree(m_shadowTreeRootElement.get());
686         return;
687     }
688
689     for (RefPtr<Node> child = element->firstChild(); child; child = child->nextSibling())
690         expandSymbolElementsInShadowTree(child.get());
691 }
692
693 #endif
694     
695 void SVGUseElement::attachShadowTree()
696 {
697     if (!m_shadowTreeRootElement || m_shadowTreeRootElement->attached() || !document()->shouldCreateRenderers() || !attached() || !renderer())
698         return;
699
700     // Inspired by RenderTextControl::createSubtreeIfNeeded(). 
701     if (renderer()->canHaveChildren() && childShouldCreateRenderer(m_shadowTreeRootElement.get())) {
702         RenderStyle* style = m_shadowTreeRootElement->styleForRenderer(renderer());
703
704         if (m_shadowTreeRootElement->rendererIsNeeded(style)) {
705             m_shadowTreeRootElement->setRenderer(m_shadowTreeRootElement->createRenderer(document()->renderArena(), style));
706             if (RenderObject* shadowRenderer = m_shadowTreeRootElement->renderer()) {
707                 shadowRenderer->setStyle(style);
708                 renderer()->addChild(shadowRenderer, m_shadowTreeRootElement->nextRenderer());
709                 m_shadowTreeRootElement->setAttached();
710             }
711         }
712
713         style->deref(document()->renderArena());
714
715         // This will take care of attaching all shadow tree child nodes.
716         for (Node* child = m_shadowTreeRootElement->firstChild(); child; child = child->nextSibling())
717             child->attach();
718     }
719 }
720
721 void SVGUseElement::associateInstancesWithShadowTreeElements(Node* target, SVGElementInstance* targetInstance)
722 {
723     if (!target || !targetInstance)
724         return;
725
726     SVGElement* originalElement = targetInstance->correspondingElement();
727
728     if (originalElement->hasTagName(SVGNames::useTag)) {
729 #if ENABLE(SVG) && ENABLE(SVG_USE)
730         // <use> gets replaced by <g>
731         ASSERT(target->nodeName() == SVGNames::gTag);
732 #else 
733         ASSERT(target->nodeName() == SVGNames::gTag || target->nodeName() == SVGNames::useTag);
734 #endif
735     } else if (originalElement->hasTagName(SVGNames::symbolTag)) {
736         // <symbol> gets replaced by <svg>
737         ASSERT(target->nodeName() == SVGNames::svgTag);
738     } else
739         ASSERT(target->nodeName() == originalElement->nodeName());
740
741     SVGElement* element = 0;
742     if (target->isSVGElement())
743         element = static_cast<SVGElement*>(target);
744
745     ASSERT(!targetInstance->shadowTreeElement());
746     targetInstance->setShadowTreeElement(element);
747
748     Node* node = target->firstChild();
749     for (SVGElementInstance* instance = targetInstance->firstChild(); node && instance; instance = instance->nextSibling()) {
750         // Skip any non-svg elements in shadow tree
751         while (node && !node->isSVGElement())
752            node = node->nextSibling();
753
754         associateInstancesWithShadowTreeElements(node, instance);
755         node = node->nextSibling();
756     }
757 }
758
759 SVGElementInstance* SVGUseElement::instanceForShadowTreeElement(Node* element) const
760 {
761     return instanceForShadowTreeElement(element, m_targetElementInstance.get());
762 }
763
764 SVGElementInstance* SVGUseElement::instanceForShadowTreeElement(Node* element, SVGElementInstance* instance) const
765 {
766     ASSERT(element);
767     ASSERT(instance);
768     ASSERT(instance->shadowTreeElement());
769
770     if (element == instance->shadowTreeElement())
771         return instance;
772
773     for (SVGElementInstance* current = instance->firstChild(); current; current = current->nextSibling()) {
774         SVGElementInstance* search = instanceForShadowTreeElement(element, current);
775         if (search)
776             return search;
777     }
778
779     return 0;
780 }
781
782 void SVGUseElement::transferUseAttributesToReplacedElement(SVGElement* from, SVGElement* to) const
783 {
784     ASSERT(from);
785     ASSERT(to);
786
787     *to->attributes() = *from->attributes();
788
789     ExceptionCode ec = 0;
790
791     to->removeAttribute(SVGNames::xAttr, ec);
792     ASSERT(ec == 0);
793
794     to->removeAttribute(SVGNames::yAttr, ec);
795     ASSERT(ec == 0);
796
797     to->removeAttribute(SVGNames::widthAttr, ec);
798     ASSERT(ec == 0);
799
800     to->removeAttribute(SVGNames::heightAttr, ec);
801     ASSERT(ec == 0);
802
803     to->removeAttribute(XLinkNames::hrefAttr, ec);
804     ASSERT(ec == 0);
805 }
806
807 }
808
809 #endif // ENABLE(SVG)
810
811 // vim:ts=4:noet