don't discard iframe children.
[kdelibs.git] / khtml / svg / SVGUseElement.cpp
blobc6db99db55f3431304e03214c6197594fa886f69
1 /*
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.
23 #include "config.h"
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
32 #if ENABLE(SVG)
33 #include "SVGUseElement.h"
35 #include "CSSStyleSelector.h"
36 /*#include "CString.h"*/
37 #include "Document.h"
38 /*#include "Event.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"
45 #include "SVGNames.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>
54 namespace WebCore {
56 SVGUseElement::SVGUseElement(const QualifiedName& tagName, Document* doc)
57 : SVGStyledTransformableElement(tagName, doc)
58 , SVGTests()
59 , SVGLangSpace()
60 , SVGExternalResourcesRequired()
61 , SVGURIReference()
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.
86 return 0;
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");
103 } else {
104 if (SVGTests::parseMappedAttribute(attr))
105 return;
106 if (SVGLangSpace::parseMappedAttribute(attr))
107 return;
108 if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
109 return;
110 if (SVGURIReference::parseMappedAttribute(attr))
111 return;
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);
133 if (!attached())
134 return;
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*/);
156 if (!attached())
157 return;
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())
172 return;
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);
180 if (ch == Detach) {
181 ASSERT(m_shadowTreeRootElement->attached());
182 m_shadowTreeRootElement->detach();
183 attachShadowTree();
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());*/
189 return;
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();
203 ASSERT(element);
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)
211 text += " ";
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());
216 depth++;
218 for (SVGElementInstance* instance = targetInstance->firstChild(); instance; instance = instance->nextSibling())
219 dumpInstanceTree(depth, text, instance);
221 depth--;
223 #endif
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))
230 return true;
231 #endif
232 #if ENABLE(SVG_ANIMATION)
233 if (SVGSMILElement::isSMILElement(element))
234 return true;
235 #endif
237 return false;
240 static bool subtreeContainsDisallowedElement(Node* start)
242 if (isDisallowedElement(start))
243 return true;
245 for (Node* cur = start->firstChild(); cur; cur = cur->nextSibling()) {
246 if (subtreeContainsDisallowedElement(cur))
247 return true;
250 return false;
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);
262 return;
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();
268 while (parent) {
269 if (parent->isShadowNode())
270 return;
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;
284 return;
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.
305 if (foundProblem) {
306 m_targetElementInstance = 0;
307 m_shadowTreeRootElement = 0;
308 return;
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());
341 #endif
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
349 String text;
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());
354 #endif
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);
363 ASSERT(ec == 0);
365 fprintf(stderr, "Dumping <use> shadow tree markup:\n%s\n", markup.latin1().data());
366 #endif
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".
370 attachShadowTree();
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.
383 attachShadowTree();
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>");
415 else
416 return static_cast<SVGStyledTransformableElement*>(n)->toClipPath();
419 return Path();
422 void SVGUseElement::buildInstanceTree(SVGElement* target, SVGElementInstance* targetInstance, bool& foundProblem)
424 ASSERT(target);
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))
441 continue;
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);
473 if (!target)
474 return;
476 // Cycle detection first!
477 foundProblem = (target == this);
479 // Shortcut for self-references
480 if (foundProblem)
481 return;
483 SVGElementInstance* instance = targetInstance->parentNode();
484 while (instance) {
485 SVGElement* element = instance->correspondingElement();
487 if (element->getIDAttribute() == id) {
488 foundProblem = true;
489 return;
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());
519 ExceptionCode ec;
520 Node* node = subtree->firstChild();
521 while (node) {
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);
526 node = next;
527 } else
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))
536 return;
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());
551 ASSERT(newChildPtr);
553 /*ExceptionCode*//*khtml*/int ec = 0;
554 m_shadowTreeRootElement->appendChild(newChild.releaseRef(), ec);
555 ASSERT(ec == 0);
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.
582 if (target) {
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);
597 } else {
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);
613 ASSERT(ec == 0);
614 return;
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());
630 ASSERT(newChildPtr);
632 cloneParent->appendChild(newChild.release(), ec);
633 ASSERT(ec == 0);
635 // Replace <use> with referenced content.
636 ASSERT(use->parentNode());
637 use->parentNode()->replaceChild(cloneParent.release(), use, ec);
638 ASSERT(ec == 0);
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());
646 return;
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);
681 ASSERT(ec == 0);
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);
695 ASSERT(ec == 0);
697 // Immediately stop here, and restart expanding.
698 expandSymbolElementsInShadowTree(m_shadowTreeRootElement.get());
699 return;
702 for (RefPtr<Node> child = element->firstChild(); child; child = child->nextSibling())
703 expandSymbolElementsInShadowTree(child.get());
706 #endif
708 void SVGUseElement::attachShadowTree()
710 if (!m_shadowTreeRootElement || m_shadowTreeRootElement->attached() || /*khtml !document()->shouldCreateRenderers() ||*/ !attached() || !renderer())
711 return;
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())
730 child->attach();
734 void SVGUseElement::associateInstancesWithShadowTreeElements(Node* target, SVGElementInstance* targetInstance)
736 if (!target || !targetInstance)
737 return;
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);*/
745 #else
746 /*ASSERT(target->nodeName() == SVGNames::gTag || target->nodeName() == SVGNames::useTag);*/
747 #endif
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);
752 #endif
753 } else
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
781 ASSERT(element);
782 ASSERT(instance);
783 ASSERT(instance->shadowTreeElement());
785 if (element == instance->shadowTreeElement())
786 return instance;
788 for (SVGElementInstance* current = instance->firstChild(); current; current = current->nextSibling()) {
789 SVGElementInstance* search = instanceForShadowTreeElement(element, current);
790 if (search)
791 return search;
794 return 0;
797 void SVGUseElement::transferUseAttributesToReplacedElement(SVGElement* from, SVGElement* to) const
799 // Implement me: khtml
800 /*ASSERT(from);
801 ASSERT(to);
803 to->attributes()->setAttributes(*from->attributes());
805 ExceptionCode ec = 0;
807 to->removeAttribute(SVGNames::xAttr, ec);
808 ASSERT(ec == 0);
810 to->removeAttribute(SVGNames::yAttr, ec);
811 ASSERT(ec == 0);
813 to->removeAttribute(SVGNames::widthAttr, ec);
814 ASSERT(ec == 0);
816 to->removeAttribute(SVGNames::heightAttr, ec);
817 ASSERT(ec == 0);
819 to->removeAttribute(XLinkNames::hrefAttr, ec);
820 ASSERT(ec == 0);*/
825 #endif // ENABLE(SVG)