24#include "wtf/Platform.h"
35#include "css/cssstyleselector.h"
40#include "RenderSVGTransformableContainer.h"
52#include <wtf/OwnPtr.h>
56SVGUseElement::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)
69SVGUseElement::~SVGUseElement()
73ANIMATED_PROPERTY_DEFINITIONS(SVGUseElement, SVGLength, Length, length,
X, x, SVGNames::xAttr, m_x)
74ANIMATED_PROPERTY_DEFINITIONS(SVGUseElement, SVGLength, Length, length, Y, y, SVGNames::yAttr, m_y)
75ANIMATED_PROPERTY_DEFINITIONS(SVGUseElement, SVGLength, Length, length, Width, width, SVGNames::widthAttr, m_width)
76ANIMATED_PROPERTY_DEFINITIONS(SVGUseElement, SVGLength, Length, length, Height, height, SVGNames::heightAttr, m_height)
78SVGElementInstance* SVGUseElement::instanceRoot()
const
80 return m_targetElementInstance.get();
83SVGElementInstance* SVGUseElement::animatedInstanceRoot()
const
89void 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);
116void SVGUseElement::insertedIntoDocument()
118 SVGElement::insertedIntoDocument();
119 buildPendingResource();
122void SVGUseElement::removedFromDocument()
124 m_targetElementInstance = 0;
125 m_shadowTreeRootElement = 0;
126 SVGElement::removedFromDocument();
129void 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)) {
145 buildPendingResource();
147 if (m_shadowTreeRootElement)
148 m_shadowTreeRootElement->setChanged();
152void SVGUseElement::childrenChanged(
bool changedByParser,
Node* beforeChange,
Node* afterChange,
int childCountDelta)
154 Q_UNUSED(changedByParser);
155 Q_UNUSED(beforeChange);
156 Q_UNUSED(afterChange);
157 Q_UNUSED(childCountDelta);
158 SVGElement::childrenChanged();
163 buildPendingResource();
165 if (m_shadowTreeRootElement)
166 m_shadowTreeRootElement->setChanged();
169void SVGUseElement::recalcStyle(StyleChange change)
171 SVGStyledElement::recalcStyle(change);
175 if (!m_shadowTreeRootElement || !m_shadowTreeRootElement->attached())
181 if (change >= Inherit || m_shadowTreeRootElement->changed()) {
182 RenderStyle* newStyle = document()->styleSelector()->styleForElement(m_shadowTreeRootElement.get());
184 StyleChange ch = m_shadowTreeRootElement->diff((m_shadowTreeRootElement->renderer() ? m_shadowTreeRootElement->renderer()->style() : 0), newStyle);
186 ASSERT(m_shadowTreeRootElement->attached());
187 m_shadowTreeRootElement->detach();
191 m_shadowTreeRootElement->setChanged(
false);
192 m_shadowTreeRootElement->setHasChangedChild(
false);
201 m_shadowTreeRootElement->recalcStyle(change);
204#ifdef DUMP_INSTANCE_TREE
205void dumpInstanceTree(
unsigned int& depth, String& text, SVGElementInstance* targetInstance)
207 SVGElement* element = targetInstance->correspondingElement();
210 String elementId = element->getIDAttribute();
211 String elementNodeName = element->nodeName();
212 String parentNodeName = element->parentNode() ? element->parentNode()->nodeName() :
"null";
213 String firstChildNodeName = element->firstChild() ? element->firstChild()->nodeName() :
"null";
215 for (
unsigned int i = 0; i < depth; ++i)
218 text += String::format(
"SVGElementInstance (parentNode=%s, firstChild=%s, correspondingElement=%s, id=%s)\n",
219 parentNodeName.latin1().data(), firstChildNodeName.latin1().data(), elementNodeName.latin1().data(), elementId.latin1().data());
223 for (SVGElementInstance* instance = targetInstance->firstChild(); instance; instance = instance->nextSibling())
224 dumpInstanceTree(depth, text, instance);
230static bool isDisallowedElement(
Node* element)
233#if ENABLE(SVG_FOREIGN_OBJECT)
235 if (element->hasTagName(SVGNames::foreignObjectTag))
238#if ENABLE(SVG_ANIMATION)
239 if (SVGSMILElement::isSMILElement(element))
246static bool subtreeContainsDisallowedElement(
Node* start)
248 if (isDisallowedElement(start))
252 if (subtreeContainsDisallowedElement(cur))
259void SVGUseElement::buildPendingResource()
261 String id = SVGURIReference::getTarget(href());
262 Element* targetElement = document()->getElementById(
id);
264 if (!targetElement) {
267 document()->accessSVGExtensions()->addPendingResource(
id,
this);
273 Node* parent = parentNode();
275 if (parent->isShadowNode())
281 SVGElement* target = 0;
282 if (targetElement && targetElement->isSVGElement())
283 target =
static_cast<SVGElement*
>(targetElement);
287 if (!target || target ==
this) {
288 m_targetElementInstance = 0;
289 m_shadowTreeRootElement = 0;
303 m_targetElementInstance =
new SVGElementInstance(
this, target);
306 bool foundProblem =
false;
307 buildInstanceTree(target, m_targetElementInstance.get(), foundProblem);
312 m_targetElementInstance = 0;
313 m_shadowTreeRootElement = 0;
318 ASSERT(m_targetElementInstance);
319 ASSERT(m_targetElementInstance->correspondingUseElement() ==
this);
322 m_shadowTreeRootElement =
new SVGGElement(SVGNames::gTag, document());
323 m_shadowTreeRootElement->setInDocument();
324 m_shadowTreeRootElement->setShadowParentNode(
this);
329 if (x().value() != 0.0 || y().value() != 0.0) {
330 String transformString = String::format(
"translate(%f, %f)", x().value(), y().value());
331 m_shadowTreeRootElement->setAttribute(SVGNames::transformAttr, transformString);
336 buildShadowTree(target, m_targetElementInstance.get());
338#if ENABLE(SVG) && ENABLE(SVG_USE)
341 expandUseElementsInShadowTree(m_shadowTreeRootElement.get());
345 expandSymbolElementsInShadowTree(m_shadowTreeRootElement.get());
351 associateInstancesWithShadowTreeElements(m_shadowTreeRootElement->firstChild(), m_targetElementInstance.get());
354#ifdef DUMP_INSTANCE_TREE
356 unsigned int depth = 0;
358 dumpInstanceTree(depth, text, m_targetElementInstance.get());
359 fprintf(stderr,
"\nDumping <use> instance tree:\n%s\n", text.latin1().data());
363#ifdef DUMP_SHADOW_TREE
366 PassRefPtr<XMLSerializer> serializer = XMLSerializer::create();
368 String markup = serializer->serializeToString(m_shadowTreeRootElement.get(), ec);
371 fprintf(stderr,
"Dumping <use> shadow tree markup:\n%s\n", markup.latin1().data());
379RenderObject* SVGUseElement::createRenderer(RenderArena* arena, RenderStyle*)
381 return new (arena) RenderSVGTransformableContainer(
this);
384void SVGUseElement::attach()
386 SVGStyledTransformableElement::attach();
392void SVGUseElement::detach()
394 if (m_shadowTreeRootElement)
395 m_shadowTreeRootElement->detach();
397 SVGStyledTransformableElement::detach();
400static bool isDirectReference(
Node* n)
402 return n->hasTagName(SVGNames::pathTag) ||
403 n->hasTagName(SVGNames::rectTag) ||
404 n->hasTagName(SVGNames::circleTag) ||
405 n->hasTagName(SVGNames::ellipseTag) ||
406 n->hasTagName(SVGNames::polygonTag) ||
407 n->hasTagName(SVGNames::polylineTag) ||
408 n->hasTagName(SVGNames::textTag);
411Path SVGUseElement::toClipPath()
const
413 if (!m_shadowTreeRootElement)
414 const_cast<SVGUseElement*
>(
this)->buildPendingResource();
416 if (!m_shadowTreeRootElement)
420 if (n->isSVGElement() &&
static_cast<SVGElement*
>(n)->isStyledTransformable()) {
421 if (!isDirectReference(n))
423 document()->accessSVGExtensions()->reportError(
"Not allowed to use indirect reference in <clip-path>");
425 return static_cast<SVGStyledTransformableElement*
>(n)->toClipPath();
431void SVGUseElement::buildInstanceTree(SVGElement* target, SVGElementInstance* targetInstance,
bool& foundProblem)
434 ASSERT(targetInstance);
444 SVGElement* element = 0;
445 if (node->isSVGElement())
446 element =
static_cast<SVGElement*
>(node);
449 if (!element || isDisallowedElement(element))
453 SVGElementInstance* instancePtr =
new SVGElementInstance(
this, element);
455 RefPtr<SVGElementInstance> instance = instancePtr;
456 targetInstance->appendChild(instance.release());
459 if (element->hasChildNodes())
460 buildInstanceTree(element, instancePtr, foundProblem);
464 if (element->hasTagName(SVGNames::useTag))
465 handleDeepUseReferencing(element, instancePtr, foundProblem);
470 if (target->hasTagName(SVGNames::useTag))
471 handleDeepUseReferencing(target, targetInstance, foundProblem);
474void SVGUseElement::handleDeepUseReferencing(SVGElement* use, SVGElementInstance* targetInstance,
bool& foundProblem)
476 String id = SVGURIReference::getTarget(use->href());
477 Element* targetElement = document()->getElementById(
id);
478 SVGElement* target = 0;
479 if (targetElement && targetElement->isSVGElement())
480 target =
static_cast<SVGElement*
>(targetElement);
486 foundProblem = (target ==
this);
492 SVGElementInstance* instance = targetInstance->parentNode();
494 SVGElement* element = instance->correspondingElement();
496 if (element->getIDAttribute() ==
id) {
501 instance = instance->parentNode();
505 SVGElementInstance* newInstance =
new SVGElementInstance(
this, target);
506 targetInstance->appendChild(newInstance);
509 buildInstanceTree(target, newInstance, foundProblem);
512void SVGUseElement::alterShadowTreeForSVGTag(SVGElement* target)
514 String widthString = String::number(width().value());
515 String heightString = String::number(height().value());
517 if (hasAttribute(SVGNames::widthAttr))
518 target->setAttribute(SVGNames::widthAttr, widthString);
520 if (hasAttribute(SVGNames::heightAttr))
521 target->setAttribute(SVGNames::heightAttr, heightString);
524void SVGUseElement::removeDisallowedElementsFromSubtree(
Node* subtree)
542void SVGUseElement::buildShadowTree(SVGElement* target, SVGElementInstance* targetInstance)
545 if (isDisallowedElement(target))
548 PassRefPtr<NodeImpl> newChild = targetInstance->correspondingElement()->cloneNode(
true);
555 if (subtreeContainsDisallowedElement(newChild.get()))
556 removeDisallowedElementsFromSubtree(newChild.get());
558 SVGElement* newChildPtr = 0;
559 if (newChild->isSVGElement())
560 newChildPtr =
static_cast<SVGElement*
>(newChild.get());
564 m_shadowTreeRootElement->appendChild(newChild.releaseRef(), ec);
568 if (target->hasTagName(SVGNames::svgTag))
569 alterShadowTreeForSVGTag(newChildPtr);
572#if ENABLE(SVG) && ENABLE(SVG_USE)
573void SVGUseElement::expandUseElementsInShadowTree(
Node* element)
582 if (element->hasTagName(SVGNames::useTag)) {
583 SVGUseElement* use =
static_cast<SVGUseElement*
>(element);
585 String id = SVGURIReference::getTarget(use->href());
586 Element* targetElement = document()->getElementById(
id);
587 SVGElement* target = 0;
588 if (targetElement && targetElement->isSVGElement())
589 target =
static_cast<SVGElement*
>(targetElement);
594 RefPtr<SVGElement> cloneParent =
new SVGGElement(SVGNames::gTag, document());
598 transferUseAttributesToReplacedElement(use, cloneParent.get());
603 if (use->x().value() != 0.0 || use->y().value() != 0.0) {
604 if (!cloneParent->hasAttribute(SVGNames::transformAttr)) {
605 String transformString = String::format(
"translate(%f, %f)", use->x().value(), use->y().value());
606 cloneParent->setAttribute(SVGNames::transformAttr, transformString);
608 String transformString = String::format(
" translate(%f, %f)", use->x().value(), use->y().value());
609 const AtomicString& transformAttribute = cloneParent->getAttribute(SVGNames::transformAttr);
610 cloneParent->setAttribute(SVGNames::transformAttr, transformAttribute + transformString);
617 if (isDisallowedElement(target)) {
621 ASSERT(use->parentNode());
622 use->parentNode()->replaceChild(cloneParent.release(), use, ec);
627 RefPtr<Node> newChild = target->cloneNode(
true);
634 if (subtreeContainsDisallowedElement(newChild.get()))
635 removeDisallowedElementsFromSubtree(newChild.get());
637 SVGElement* newChildPtr = 0;
638 if (newChild->isSVGElement())
639 newChildPtr =
static_cast<SVGElement*
>(newChild.get());
642 cloneParent->appendChild(newChild.release(), ec);
646 ASSERT(use->parentNode());
647 use->parentNode()->replaceChild(cloneParent.release(), use, ec);
651 if (target->hasTagName(SVGNames::svgTag))
652 alterShadowTreeForSVGTag(newChildPtr);
655 expandUseElementsInShadowTree(m_shadowTreeRootElement.get());
661 expandUseElementsInShadowTree(child.get());
664void SVGUseElement::expandSymbolElementsInShadowTree(
Node* element)
666 if (element->hasTagName(SVGNames::symbolTag)) {
673 RefPtr<SVGSVGElement> svgElement =
new SVGSVGElement(SVGNames::svgTag, document());
676 svgElement->attributes()->setAttributes(*element->
attributes());
679 String widthString = String::number(width().value());
680 String heightString = String::number(height().value());
682 svgElement->setAttribute(SVGNames::widthAttr, hasAttribute(SVGNames::widthAttr) ? widthString :
"100%");
683 svgElement->setAttribute(SVGNames::heightAttr, hasAttribute(SVGNames::heightAttr) ? heightString :
"100%");
689 RefPtr<Node> newChild = child->cloneNode(
true);
690 svgElement->appendChild(newChild.release(), ec);
699 if (subtreeContainsDisallowedElement(svgElement.get()))
700 removeDisallowedElementsFromSubtree(svgElement.get());
708 expandSymbolElementsInShadowTree(m_shadowTreeRootElement.get());
713 expandSymbolElementsInShadowTree(child.get());
718void SVGUseElement::attachShadowTree()
720 if (!m_shadowTreeRootElement || m_shadowTreeRootElement->attached() || !attached() || !renderer())
724 if (renderer()->childAllowed() && childShouldCreateRenderer(m_shadowTreeRootElement.get())) {
725 RenderStyle* style = m_shadowTreeRootElement->styleForRenderer(renderer());
728 if (m_shadowTreeRootElement->rendererIsNeeded(style)) {
729 m_shadowTreeRootElement->setRenderer(m_shadowTreeRootElement->createRenderer(document()->renderArena(), style));
730 if (RenderObject* shadowRenderer = m_shadowTreeRootElement->renderer()) {
731 shadowRenderer->setStyle(style);
732 renderer()->addChild(shadowRenderer, m_shadowTreeRootElement->nextRenderer());
733 m_shadowTreeRootElement->setAttached();
745void SVGUseElement::associateInstancesWithShadowTreeElements(
Node* target, SVGElementInstance* targetInstance)
747 if (!target || !targetInstance)
750 SVGElement* originalElement = targetInstance->correspondingElement();
752 if (originalElement->hasTagName(SVGNames::useTag)) {
753#if ENABLE(SVG) && ENABLE(SVG_USE)
759 }
else if (originalElement->hasTagName(SVGNames::symbolTag)) {
761#if ENABLE(SVG) && ENABLE(SVG_USE) && ENABLE(SVG_FOREIGN_OBJECT)
762 ASSERT(target->
nodeName() == SVGNames::svgTag);
765 ASSERT(target->
nodeName() == originalElement->nodeName());
767 SVGElement* element = 0;
768 if (target->isSVGElement())
769 element =
static_cast<SVGElement*
>(target);
771 ASSERT(!targetInstance->shadowTreeElement());
772 targetInstance->setShadowTreeElement(element);
775 for (SVGElementInstance* instance = targetInstance->firstChild(); node && instance; instance = instance->nextSibling()) {
777 while (node && !node->isSVGElement())
780 associateInstancesWithShadowTreeElements(node, instance);
785SVGElementInstance* SVGUseElement::instanceForShadowTreeElement(
Node* element)
const
787 return instanceForShadowTreeElement(element, m_targetElementInstance.get());
790SVGElementInstance* SVGUseElement::instanceForShadowTreeElement(
Node* element, SVGElementInstance* instance)
const
794 ASSERT(instance->shadowTreeElement());
796 if (element == instance->shadowTreeElement())
799 for (SVGElementInstance* current = instance->firstChild(); current; current = current->nextSibling()) {
800 SVGElementInstance* search = instanceForShadowTreeElement(element, current);
808void SVGUseElement::transferUseAttributesToReplacedElement(SVGElement* from, SVGElement* to)
const
Node nextSibling() const
The node immediately following this node.
Node replaceChild(const Node &newChild, const Node &oldChild)
Replaces the child node oldChild with newChild in the list of children, and returns the oldChild node...
The Node interface is the primary datatype for the entire Document Object Model.
Node nextSibling() const
The node immediately following this node.
Node parentNode() const
The parent of this node.
Node firstChild() const
The first child of this node.
NamedNodeMap attributes() const
A NamedNodeMap containing the attributes of this node (if it is an Element ) or null otherwise.
DOMString nodeName() const
The name of this node, depending on its type; see the table above.
unsigned short ExceptionCode