2 Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
5 This file is part of the KDE project
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.
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.
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.
24 #include "wtf/Platform.h"
26 // Dump SVGElementInstance object tree - useful to debug instanceRoot problems
27 // #define DUMP_INSTANCE_TREE
29 // Dump the deep-expanded shadow tree (where the renderes are built from)
30 // #define DUMP_SHADOW_TREE
33 #include "SVGUseElement.h"
35 #include "CSSStyleSelector.h"
36 /*#include "CString.h"*/
39 #include "HTMLNames.h"*/
40 #include "RenderSVGTransformableContainer.h"
41 #include "SVGElementInstance.h"
42 #include "SVGElementInstanceList.h"
43 #include "SVGGElement.h"
44 #include "SVGLength.h"
46 #include "SVGPreserveAspectRatio.h"
47 /*#include "SVGSMILElement.h"*/
48 #include "SVGSVGElement.h"
49 #include "SVGSymbolElement.h"
50 #include "XLinkNames.h"
51 /*#include "XMLSerializer.h"*/
52 #include <wtf/OwnPtr.h>
56 SVGUseElement::SVGUseElement(const QualifiedName
& tagName
, Document
* doc
)
57 : SVGStyledTransformableElement(tagName
, doc
)
60 , SVGExternalResourcesRequired()
62 , m_x(this, LengthModeWidth
)
63 , m_y(this, LengthModeHeight
)
64 , m_width(this, LengthModeWidth
)
65 , m_height(this, LengthModeHeight
)
69 SVGUseElement::~SVGUseElement()
73 ANIMATED_PROPERTY_DEFINITIONS(SVGUseElement
, SVGLength
, Length
, length
, X
, x
, SVGNames::xAttr
, m_x
)
74 ANIMATED_PROPERTY_DEFINITIONS(SVGUseElement
, SVGLength
, Length
, length
, Y
, y
, SVGNames::yAttr
, m_y
)
75 ANIMATED_PROPERTY_DEFINITIONS(SVGUseElement
, SVGLength
, Length
, length
, Width
, width
, SVGNames::widthAttr
, m_width
)
76 ANIMATED_PROPERTY_DEFINITIONS(SVGUseElement
, SVGLength
, Length
, length
, Height
, height
, SVGNames::heightAttr
, m_height
)
78 SVGElementInstance
* SVGUseElement::instanceRoot() const
80 return m_targetElementInstance
.get();
83 SVGElementInstance
* SVGUseElement::animatedInstanceRoot() const
85 // FIXME: Implement me.
89 void SVGUseElement::parseMappedAttribute(MappedAttribute
* attr
)
91 if (attr
->name() == SVGNames::xAttr
)
92 setXBaseValue(SVGLength(this, LengthModeWidth
, attr
->value()));
93 else if (attr
->name() == SVGNames::yAttr
)
94 setYBaseValue(SVGLength(this, LengthModeHeight
, attr
->value()));
95 else if (attr
->name() == SVGNames::widthAttr
) {
96 setWidthBaseValue(SVGLength(this, LengthModeWidth
, attr
->value()));
97 if (width().value() < 0.0)
98 document()->accessSVGExtensions()->reportError("A negative value for use attribute <width> is not allowed");
99 } else if (attr
->name() == SVGNames::heightAttr
) {
100 setHeightBaseValue(SVGLength(this, LengthModeHeight
, attr
->value()));
101 if (height().value() < 0.0)
102 document()->accessSVGExtensions()->reportError("A negative value for use attribute <height> is not allowed");
104 if (SVGTests::parseMappedAttribute(attr
))
106 if (SVGLangSpace::parseMappedAttribute(attr
))
108 if (SVGExternalResourcesRequired::parseMappedAttribute(attr
))
110 if (SVGURIReference::parseMappedAttribute(attr
))
112 SVGStyledTransformableElement::parseMappedAttribute(attr
);
116 void SVGUseElement::insertedIntoDocument()
118 SVGElement::insertedIntoDocument();
119 buildPendingResource();
122 void SVGUseElement::removedFromDocument()
124 m_targetElementInstance
= 0;
125 m_shadowTreeRootElement
= 0;
126 SVGElement::removedFromDocument();
129 void SVGUseElement::svgAttributeChanged(const QualifiedName
& attrName
)
131 SVGStyledTransformableElement::svgAttributeChanged(attrName
);
136 if (attrName
== SVGNames::xAttr
|| attrName
== SVGNames::yAttr
||
137 attrName
== SVGNames::widthAttr
|| attrName
== SVGNames::heightAttr
||
138 SVGTests::isKnownAttribute(attrName
) ||
139 SVGLangSpace::isKnownAttribute(attrName
) ||
140 SVGExternalResourcesRequired::isKnownAttribute(attrName
) ||
141 SVGURIReference::isKnownAttribute(attrName
) ||
142 SVGStyledTransformableElement::isKnownAttribute(attrName
)) {
143 // TODO: Now that we're aware of the attribute name, we can finally optimize
144 // updating <use> attributes - to not reclone every time.
145 buildPendingResource();
147 if (m_shadowTreeRootElement
)
148 m_shadowTreeRootElement
->setChanged();
152 void SVGUseElement::childrenChanged(bool changedByParser
, Node
* beforeChange
, Node
* afterChange
, int childCountDelta
)
154 SVGElement::childrenChanged(/*changedByParser, beforeChange, afterChange, childCountDelta*/);
159 buildPendingResource();
161 if (m_shadowTreeRootElement
)
162 m_shadowTreeRootElement
->setChanged();
165 void SVGUseElement::recalcStyle(StyleChange change
)
167 SVGStyledElement::recalcStyle(change
);
169 // The shadow tree root element is NOT a direct child element of us.
170 // So we have to take care it receives style updates, manually.
171 if (!m_shadowTreeRootElement
|| !m_shadowTreeRootElement
->attached())
174 // Mimic Element::recalcStyle(). The main difference is that we don't call attach() on the
175 // shadow tree root element, but call attachShadowTree() here. Calling attach() will crash
176 // as the shadow tree root element has no (direct) parent node. Yes, shadow trees are tricky.
177 if (change
>= Inherit
|| m_shadowTreeRootElement
->changed()) {
178 RenderStyle
* newStyle
= document()->styleSelector()->styleForElement(m_shadowTreeRootElement
.get());
179 StyleChange ch
= m_shadowTreeRootElement
->diff((m_shadowTreeRootElement
->renderer() ? m_shadowTreeRootElement
->renderer()->style() : 0)/*renderStyle()*/, newStyle
);
181 ASSERT(m_shadowTreeRootElement
->attached());
182 m_shadowTreeRootElement
->detach();
185 // attach recalulates the style for all children. No need to do it twice.
186 m_shadowTreeRootElement
->setChanged(/*NoStyleChange*/);
187 m_shadowTreeRootElement
->setHasChangedChild(false);
188 /*newStyle->deref(document()->renderArena());*/
192 /*newStyle->deref(document()->renderArena());*/
195 // Only change==Detach needs special treatment, for anything else recalcStyle() works.
196 m_shadowTreeRootElement
->recalcStyle(change
);
199 #ifdef DUMP_INSTANCE_TREE
200 void dumpInstanceTree(unsigned int& depth
, String
& text
, SVGElementInstance
* targetInstance
)
202 SVGElement
* element
= targetInstance
->correspondingElement();
205 String elementId
= element
->getIDAttribute();
206 String elementNodeName
= element
->nodeName();
207 String parentNodeName
= element
->parentNode() ? element
->parentNode()->nodeName() : "null";
208 String firstChildNodeName
= element
->firstChild() ? element
->firstChild()->nodeName() : "null";
210 for (unsigned int i
= 0; i
< depth
; ++i
)
213 text
+= String::format("SVGElementInstance (parentNode=%s, firstChild=%s, correspondingElement=%s, id=%s)\n",
214 parentNodeName
.latin1().data(), firstChildNodeName
.latin1().data(), elementNodeName
.latin1().data(), elementId
.latin1().data());
218 for (SVGElementInstance
* instance
= targetInstance
->firstChild(); instance
; instance
= instance
->nextSibling())
219 dumpInstanceTree(depth
, text
, instance
);
225 static bool isDisallowedElement(Node
* element
)
227 #if ENABLE(SVG_FOREIGN_OBJECT)
228 // <foreignObject> should never be contained in a <use> tree. Too dangerous side effects possible.
229 if (element
->hasTagName(SVGNames::foreignObjectTag
))
232 #if ENABLE(SVG_ANIMATION)
233 if (SVGSMILElement::isSMILElement(element
))
240 static bool subtreeContainsDisallowedElement(Node
* start
)
242 if (isDisallowedElement(start
))
245 for (Node
* cur
= start
->firstChild(); cur
; cur
= cur
->nextSibling()) {
246 if (subtreeContainsDisallowedElement(cur
))
253 void SVGUseElement::buildPendingResource()
255 String id
= SVGURIReference::getTarget(href());
256 Element
* targetElement
= document()->getElementById(id
);
258 if (!targetElement
) {
259 // TODO: We want to deregister as pending resource, if our href() changed!
260 // TODO: Move to svgAttributeChanged, once we're fixing use & the new dynamic update concept.
261 document()->accessSVGExtensions()->addPendingResource(id
, this);
265 // Do not build the shadow/instance tree for <use> elements living in a shadow tree.
266 // The will be expanded soon anyway - see expandUseElementsInShadowTree().
267 Node
* parent
= parentNode();
269 if (parent
->isShadowNode())
272 parent
= parent
->parentNode();
275 SVGElement
* target
= 0;
276 if (targetElement
&& targetElement
->isSVGElement())
277 target
= static_cast<SVGElement
*>(targetElement
);
279 // Do not allow self-referencing.
280 // 'target' may be null, if it's a non SVG namespaced element.
281 if (!target
|| target
== this) {
282 m_targetElementInstance
= 0;
283 m_shadowTreeRootElement
= 0;
287 // Why a separated instance/shadow tree? SVG demands it:
288 // The instance tree is accesable from JavaScript, and has to
289 // expose a 1:1 copy of the referenced tree, whereas internally we need
290 // to alter the tree for correct "use-on-symbol", "use-on-svg" support.
292 // Build instance tree. Create root SVGElementInstance object for the first sub-tree node.
294 // Spec: If the 'use' element references a simple graphics element such as a 'rect', then there is only a
295 // single SVGElementInstance object, and the correspondingElement attribute on this SVGElementInstance object
296 // is the SVGRectElement that corresponds to the referenced 'rect' element.
297 m_targetElementInstance
= new SVGElementInstance(this, target
);
299 // Eventually enter recursion to build SVGElementInstance objects for the sub-tree children
300 bool foundProblem
= false;
301 buildInstanceTree(target
, m_targetElementInstance
.get(), foundProblem
);
303 // SVG specification does not say a word about <use> & cycles. My view on this is: just ignore it!
304 // Non-appearing <use> content is easier to debug, then half-appearing content.
306 m_targetElementInstance
= 0;
307 m_shadowTreeRootElement
= 0;
311 // Assure instance tree building was successful
312 ASSERT(m_targetElementInstance
);
313 ASSERT(m_targetElementInstance
->correspondingUseElement() == this);
315 // Setup shadow tree root node
316 m_shadowTreeRootElement
= new SVGGElement(SVGNames::gTag
, document());
317 m_shadowTreeRootElement
->setInDocument();
318 m_shadowTreeRootElement
->setShadowParentNode(this);
320 // Spec: An additional transformation translate(x,y) is appended to the end
321 // (i.e., right-side) of the transform attribute on the generated 'g', where x
322 // and y represent the values of the x and y attributes on the 'use' element.
323 if (x().value() != 0.0 || y().value() != 0.0) {
324 String transformString
= String::format("translate(%f, %f)", x().value(), y().value());
325 m_shadowTreeRootElement
->setAttribute(SVGNames::transformAttr
, transformString
);
328 // Build shadow tree from instance tree
329 // This also handles the special cases: <use> on <symbol>, <use> on <svg>.
330 buildShadowTree(target
, m_targetElementInstance
.get());
332 #if ENABLE(SVG) && ENABLE(SVG_USE)
333 // Expand all <use> elements in the shadow tree.
334 // Expand means: replace the actual <use> element by what it references.
335 expandUseElementsInShadowTree(m_shadowTreeRootElement
.get());
337 // Expand all <symbol> elements in the shadow tree.
338 // Expand means: replace the actual <symbol> element by the <svg> element.
339 expandSymbolElementsInShadowTree(m_shadowTreeRootElement
.get());
343 // Now that the shadow tree is completely expanded, we can associate
344 // shadow tree elements <-> instances in the instance tree.
345 associateInstancesWithShadowTreeElements(m_shadowTreeRootElement
->firstChild(), m_targetElementInstance
.get());
347 // Eventually dump instance tree
348 #ifdef DUMP_INSTANCE_TREE
350 unsigned int depth
= 0;
352 dumpInstanceTree(depth
, text
, m_targetElementInstance
.get());
353 fprintf(stderr
, "\nDumping <use> instance tree:\n%s\n", text
.latin1().data());
356 // Eventually dump shadow tree
357 #ifdef DUMP_SHADOW_TREE
358 ExceptionCode ec
= 0;
360 PassRefPtr
<XMLSerializer
> serializer
= XMLSerializer::create();
362 String markup
= serializer
->serializeToString(m_shadowTreeRootElement
.get(), ec
);
365 fprintf(stderr
, "Dumping <use> shadow tree markup:\n%s\n", markup
.latin1().data());
368 // The DOM side is setup properly. Now we have to attach the root shadow
369 // tree element manually - using attach() won't work for "shadow nodes".
373 RenderObject
* SVGUseElement::createRenderer(RenderArena
* arena
, RenderStyle
*)
375 return new (arena
) RenderSVGTransformableContainer(this);
378 void SVGUseElement::attach()
380 SVGStyledTransformableElement::attach();
382 // If we're a pending resource, this doesn't have any effect.
386 void SVGUseElement::detach()
388 SVGStyledTransformableElement::detach();
390 if (m_shadowTreeRootElement
)
391 m_shadowTreeRootElement
->detach();
394 static bool isDirectReference(Node
* n
)
396 return n
->hasTagName(SVGNames::pathTag
) ||
397 n
->hasTagName(SVGNames::rectTag
) ||
398 n
->hasTagName(SVGNames::circleTag
) ||
399 n
->hasTagName(SVGNames::ellipseTag
) ||
400 n
->hasTagName(SVGNames::polygonTag
) ||
401 n
->hasTagName(SVGNames::polylineTag
) ||
402 n
->hasTagName(SVGNames::textTag
);
405 Path
SVGUseElement::toClipPath() const
407 if (!m_shadowTreeRootElement
)
408 const_cast<SVGUseElement
*>(this)->buildPendingResource();
410 Node
* n
= m_shadowTreeRootElement
->firstChild();
411 if (n
->isSVGElement() && static_cast<SVGElement
*>(n
)->isStyledTransformable()) {
412 if (!isDirectReference(n
))
413 // Spec: Indirect references are an error (14.3.5)
414 document()->accessSVGExtensions()->reportError("Not allowed to use indirect reference in <clip-path>");
416 return static_cast<SVGStyledTransformableElement
*>(n
)->toClipPath();
422 void SVGUseElement::buildInstanceTree(SVGElement
* target
, SVGElementInstance
* targetInstance
, bool& foundProblem
)
425 ASSERT(targetInstance
);
427 // A general description from the SVG spec, describing what buildInstanceTree() actually does.
429 // Spec: If the 'use' element references a 'g' which contains two 'rect' elements, then the instance tree
430 // contains three SVGElementInstance objects, a root SVGElementInstance object whose correspondingElement
431 // is the SVGGElement object for the 'g', and then two child SVGElementInstance objects, each of which has
432 // its correspondingElement that is an SVGRectElement object.
434 for (Node
* node
= target
->firstChild(); node
; node
= node
->nextSibling()) {
435 SVGElement
* element
= 0;
436 if (node
->isSVGElement())
437 element
= static_cast<SVGElement
*>(node
);
439 // Skip any non-svg nodes or any disallowed element.
440 if (!element
|| isDisallowedElement(element
))
443 // Create SVGElementInstance object, for both container/non-container nodes.
444 SVGElementInstance
* instancePtr
= new SVGElementInstance(this, element
);
446 RefPtr
<SVGElementInstance
> instance
= instancePtr
;
447 targetInstance
->appendChild(instance
.release());
449 // Enter recursion, appending new instance tree nodes to the "instance" object.
450 if (element
->hasChildNodes())
451 buildInstanceTree(element
, instancePtr
, foundProblem
);
453 // Spec: If the referenced object is itself a 'use', or if there are 'use' subelements within the referenced
454 // object, the instance tree will contain recursive expansion of the indirect references to form a complete tree.
455 if (element
->hasTagName(SVGNames::useTag
))
456 handleDeepUseReferencing(element
, instancePtr
, foundProblem
);
459 // Spec: If the referenced object is itself a 'use', or if there are 'use' subelements within the referenced
460 // object, the instance tree will contain recursive expansion of the indirect references to form a complete tree.
461 if (target
->hasTagName(SVGNames::useTag
))
462 handleDeepUseReferencing(target
, targetInstance
, foundProblem
);
465 void SVGUseElement::handleDeepUseReferencing(SVGElement
* use
, SVGElementInstance
* targetInstance
, bool& foundProblem
)
467 String id
= SVGURIReference::getTarget(use
->href());
468 Element
* targetElement
= document()->getElementById(id
);
469 SVGElement
* target
= 0;
470 if (targetElement
&& targetElement
->isSVGElement())
471 target
= static_cast<SVGElement
*>(targetElement
);
476 // Cycle detection first!
477 foundProblem
= (target
== this);
479 // Shortcut for self-references
483 SVGElementInstance
* instance
= targetInstance
->parentNode();
485 SVGElement
* element
= instance
->correspondingElement();
487 if (element
->getIDAttribute() == id
) {
492 instance
= instance
->parentNode();
495 // Create an instance object, even if we're dealing with a cycle
496 SVGElementInstance
* newInstance
= new SVGElementInstance(this, target
);
497 targetInstance
->appendChild(newInstance
);
499 // Eventually enter recursion to build SVGElementInstance objects for the sub-tree children
500 buildInstanceTree(target
, newInstance
, foundProblem
);
503 void SVGUseElement::alterShadowTreeForSVGTag(SVGElement
* target
)
505 String widthString
= String::number(width().value());
506 String heightString
= String::number(height().value());
508 if (hasAttribute(SVGNames::widthAttr
))
509 target
->setAttribute(SVGNames::widthAttr
, widthString
);
511 if (hasAttribute(SVGNames::heightAttr
))
512 target
->setAttribute(SVGNames::heightAttr
, heightString
);
515 void SVGUseElement::removeDisallowedElementsFromSubtree(Node
* subtree
)
517 // Implement me: khtml, NodeImpl::traverseNextSibling
518 /*ASSERT(!subtree->inDocument());
520 Node* node = subtree->firstChild();
522 if (isDisallowedElement(node)) {
523 Node* next = node->traverseNextSibling(subtree);
524 // The subtree is not in document so this won't generate events that could mutate the tree.
525 node->parent()->removeChild(node, ec);
528 node = node->traverseNextNode(subtree);
532 void SVGUseElement::buildShadowTree(SVGElement
* target
, SVGElementInstance
* targetInstance
)
534 // For instance <use> on <foreignObject> (direct case).
535 if (isDisallowedElement(target
))
538 PassRefPtr
<NodeImpl
> newChild
= targetInstance
->correspondingElement()->cloneNode(true);
540 // We don't walk the target tree element-by-element, and clone each element,
541 // but instead use cloneNode(deep=true). This is an optimization for the common
542 // case where <use> doesn't contain disallowed elements (ie. <foreignObject>).
543 // Though if there are disallowed elements in the subtree, we have to remove them.
544 // For instance: <use> on <g> containing <foreignObject> (indirect case).
545 if (subtreeContainsDisallowedElement(newChild
.get()))
546 removeDisallowedElementsFromSubtree(newChild
.get());
548 SVGElement
* newChildPtr
= 0;
549 if (newChild
->isSVGElement())
550 newChildPtr
= static_cast<SVGElement
*>(newChild
.get());
553 /*ExceptionCode*//*khtml*/int ec
= 0;
554 m_shadowTreeRootElement
->appendChild(newChild
.releaseRef(), ec
);
557 // Handle use referencing <svg> special case
558 if (target
->hasTagName(SVGNames::svgTag
))
559 alterShadowTreeForSVGTag(newChildPtr
);
562 #if ENABLE(SVG) && ENABLE(SVG_USE)
563 void SVGUseElement::expandUseElementsInShadowTree(Node
* element
)
565 // Why expand the <use> elements in the shadow tree here, and not just
566 // do this directly in buildShadowTree, if we encounter a <use> element?
568 // Short answer: Because we may miss to expand some elements. Ie. if a <symbol>
569 // contains <use> tags, we'd miss them. So once we're done with settin' up the
570 // actual shadow tree (after the special case modification for svg/symbol) we have
571 // to walk it completely and expand all <use> elements.
572 if (element
->hasTagName(SVGNames::useTag
)) {
573 SVGUseElement
* use
= static_cast<SVGUseElement
*>(element
);
575 String id
= SVGURIReference::getTarget(use
->href());
576 Element
* targetElement
= document()->getElementById(id
);
577 SVGElement
* target
= 0;
578 if (targetElement
&& targetElement
->isSVGElement())
579 target
= static_cast<SVGElement
*>(targetElement
);
581 // Don't ASSERT(target) here, it may be "pending", too.
583 // Setup sub-shadow tree root node
584 RefPtr
<SVGElement
> cloneParent
= new SVGGElement(SVGNames::gTag
, document());
586 // Spec: In the generated content, the 'use' will be replaced by 'g', where all attributes from the
587 // 'use' element except for x, y, width, height and xlink:href are transferred to the generated 'g' element.
588 transferUseAttributesToReplacedElement(use
, cloneParent
.get());
590 // Spec: An additional transformation translate(x,y) is appended to the end
591 // (i.e., right-side) of the transform attribute on the generated 'g', where x
592 // and y represent the values of the x and y attributes on the 'use' element.
593 if (use
->x().value() != 0.0 || use
->y().value() != 0.0) {
594 if (!cloneParent
->hasAttribute(SVGNames::transformAttr
)) {
595 String transformString
= String::format("translate(%f, %f)", use
->x().value(), use
->y().value());
596 cloneParent
->setAttribute(SVGNames::transformAttr
, transformString
);
598 String transformString
= String::format(" translate(%f, %f)", use
->x().value(), use
->y().value());
599 const AtomicString
& transformAttribute
= cloneParent
->getAttribute(SVGNames::transformAttr
);
600 cloneParent
->setAttribute(SVGNames::transformAttr
, transformAttribute
+ transformString
);
604 ExceptionCode ec
= 0;
606 // For instance <use> on <foreignObject> (direct case).
607 if (isDisallowedElement(target
)) {
608 // We still have to setup the <use> replacment (<g>). Otherwhise
609 // associateInstancesWithShadowTreeElements() makes wrong assumptions.
610 // Replace <use> with referenced content.
611 ASSERT(use
->parentNode());
612 use
->parentNode()->replaceChild(cloneParent
.release(), use
, ec
);
617 RefPtr
<Node
> newChild
= target
->cloneNode(true);
619 // We don't walk the target tree element-by-element, and clone each element,
620 // but instead use cloneNode(deep=true). This is an optimization for the common
621 // case where <use> doesn't contain disallowed elements (ie. <foreignObject>).
622 // Though if there are disallowed elements in the subtree, we have to remove them.
623 // For instance: <use> on <g> containing <foreignObject> (indirect case).
624 if (subtreeContainsDisallowedElement(newChild
.get()))
625 removeDisallowedElementsFromSubtree(newChild
.get());
627 SVGElement
* newChildPtr
= 0;
628 if (newChild
->isSVGElement())
629 newChildPtr
= static_cast<SVGElement
*>(newChild
.get());
632 cloneParent
->appendChild(newChild
.release(), ec
);
635 // Replace <use> with referenced content.
636 ASSERT(use
->parentNode());
637 use
->parentNode()->replaceChild(cloneParent
.release(), use
, ec
);
640 // Handle use referencing <svg> special case
641 if (target
->hasTagName(SVGNames::svgTag
))
642 alterShadowTreeForSVGTag(newChildPtr
);
644 // Immediately stop here, and restart expanding.
645 expandUseElementsInShadowTree(m_shadowTreeRootElement
.get());
650 for (RefPtr
<Node
> child
= element
->firstChild(); child
; child
= child
->nextSibling())
651 expandUseElementsInShadowTree(child
.get());
654 void SVGUseElement::expandSymbolElementsInShadowTree(Node
* element
)
656 if (element
->hasTagName(SVGNames::symbolTag
)) {
657 // Spec: The referenced 'symbol' and its contents are deep-cloned into the generated tree,
658 // with the exception that the 'symbol' is replaced by an 'svg'. This generated 'svg' will
659 // always have explicit values for attributes width and height. If attributes width and/or
660 // height are provided on the 'use' element, then these attributes will be transferred to
661 // the generated 'svg'. If attributes width and/or height are not specified, the generated
662 // 'svg' element will use values of 100% for these attributes.
663 RefPtr
<SVGSVGElement
> svgElement
= new SVGSVGElement(SVGNames::svgTag
, document());
665 // Transfer all attributes from <symbol> to the new <svg> element
666 svgElement
->attributes()->setAttributes(*element
->attributes());
668 // Explicitly re-set width/height values
669 String widthString
= String::number(width().value());
670 String heightString
= String::number(height().value());
672 svgElement
->setAttribute(SVGNames::widthAttr
, hasAttribute(SVGNames::widthAttr
) ? widthString
: "100%");
673 svgElement
->setAttribute(SVGNames::heightAttr
, hasAttribute(SVGNames::heightAttr
) ? heightString
: "100%");
675 ExceptionCode ec
= 0;
677 // Only clone symbol children, and add them to the new <svg> element
678 for (Node
* child
= element
->firstChild(); child
; child
= child
->nextSibling()) {
679 RefPtr
<Node
> newChild
= child
->cloneNode(true);
680 svgElement
->appendChild(newChild
.release(), ec
);
684 // We don't walk the target tree element-by-element, and clone each element,
685 // but instead use cloneNode(deep=true). This is an optimization for the common
686 // case where <use> doesn't contain disallowed elements (ie. <foreignObject>).
687 // Though if there are disallowed elements in the subtree, we have to remove them.
688 // For instance: <use> on <g> containing <foreignObject> (indirect case).
689 if (subtreeContainsDisallowedElement(svgElement
.get()))
690 removeDisallowedElementsFromSubtree(svgElement
.get());
692 // Replace <symbol> with <svg>.
693 ASSERT(element
->parentNode());
694 element
->parentNode()->replaceChild(svgElement
.release(), element
, ec
);
697 // Immediately stop here, and restart expanding.
698 expandSymbolElementsInShadowTree(m_shadowTreeRootElement
.get());
702 for (RefPtr
<Node
> child
= element
->firstChild(); child
; child
= child
->nextSibling())
703 expandSymbolElementsInShadowTree(child
.get());
708 void SVGUseElement::attachShadowTree()
710 if (!m_shadowTreeRootElement
|| m_shadowTreeRootElement
->attached() || /*khtml !document()->shouldCreateRenderers() ||*/ !attached() || !renderer())
713 // Inspired by RenderTextControl::createSubtreeIfNeeded().
714 if (renderer()->childAllowed()/*canHaveChildren()*/ && childShouldCreateRenderer(m_shadowTreeRootElement
.get())) {
715 RenderStyle
* style
= m_shadowTreeRootElement
->styleForRenderer(renderer());
717 if (m_shadowTreeRootElement
->rendererIsNeeded(style
)) {
718 m_shadowTreeRootElement
->setRenderer(m_shadowTreeRootElement
->createRenderer(document()->renderArena(), style
));
719 if (RenderObject
* shadowRenderer
= m_shadowTreeRootElement
->renderer()) {
720 shadowRenderer
->setStyle(style
);
721 renderer()->addChild(shadowRenderer
, m_shadowTreeRootElement
->nextRenderer());
722 m_shadowTreeRootElement
->setAttached();
726 /*style->deref(document()->renderArena());*/
728 // This will take care of attaching all shadow tree child nodes.
729 for (Node
* child
= m_shadowTreeRootElement
->firstChild(); child
; child
= child
->nextSibling())
734 void SVGUseElement::associateInstancesWithShadowTreeElements(Node
* target
, SVGElementInstance
* targetInstance
)
736 if (!target
|| !targetInstance
)
739 SVGElement
* originalElement
= targetInstance
->correspondingElement();
741 if (originalElement
->hasTagName(SVGNames::useTag
)) {
742 #if ENABLE(SVG) && ENABLE(SVG_USE)
743 // <use> gets replaced by <g>
744 /*ASSERT(target->nodeName() == SVGNames::gTag);*/
746 /*ASSERT(target->nodeName() == SVGNames::gTag || target->nodeName() == SVGNames::useTag);*/
748 } else if (originalElement
->hasTagName(SVGNames::symbolTag
)) {
749 // <symbol> gets replaced by <svg>
750 #if ENABLE(SVG) && ENABLE(SVG_USE) && ENABLE(SVG_FOREIGN_OBJECT)
751 ASSERT(target
->nodeName() == SVGNames::svgTag
);
754 ASSERT(target
->nodeName() == originalElement
->nodeName());
756 SVGElement
* element
= 0;
757 if (target
->isSVGElement())
758 element
= static_cast<SVGElement
*>(target
);
760 ASSERT(!targetInstance
->shadowTreeElement());
761 targetInstance
->setShadowTreeElement(element
);
763 Node
* node
= target
->firstChild();
764 for (SVGElementInstance
* instance
= targetInstance
->firstChild(); node
&& instance
; instance
= instance
->nextSibling()) {
765 // Skip any non-svg elements in shadow tree
766 while (node
&& !node
->isSVGElement())
767 node
= node
->nextSibling();
769 associateInstancesWithShadowTreeElements(node
, instance
);
770 node
= node
->nextSibling();
774 SVGElementInstance
* SVGUseElement::instanceForShadowTreeElement(Node
* element
) const
776 return instanceForShadowTreeElement(element
, m_targetElementInstance
.get());
779 SVGElementInstance
* SVGUseElement::instanceForShadowTreeElement(Node
* element
, SVGElementInstance
* instance
) const
783 ASSERT(instance
->shadowTreeElement());
785 if (element
== instance
->shadowTreeElement())
788 for (SVGElementInstance
* current
= instance
->firstChild(); current
; current
= current
->nextSibling()) {
789 SVGElementInstance
* search
= instanceForShadowTreeElement(element
, current
);
797 void SVGUseElement::transferUseAttributesToReplacedElement(SVGElement
* from
, SVGElement
* to
) const
799 // Implement me: khtml
803 to->attributes()->setAttributes(*from->attributes());
805 ExceptionCode ec = 0;
807 to->removeAttribute(SVGNames::xAttr, ec);
810 to->removeAttribute(SVGNames::yAttr, ec);
813 to->removeAttribute(SVGNames::widthAttr, ec);
816 to->removeAttribute(SVGNames::heightAttr, ec);
819 to->removeAttribute(XLinkNames::hrefAttr, ec);
825 #endif // ENABLE(SVG)