diff options
Diffstat (limited to 'WebCore/svg/SVGUseElement.cpp')
-rw-r--r-- | WebCore/svg/SVGUseElement.cpp | 511 |
1 files changed, 301 insertions, 210 deletions
diff --git a/WebCore/svg/SVGUseElement.cpp b/WebCore/svg/SVGUseElement.cpp index a566992..2ce1bd1 100644 --- a/WebCore/svg/SVGUseElement.cpp +++ b/WebCore/svg/SVGUseElement.cpp @@ -1,7 +1,7 @@ /* Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org> 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org> - Copyright (C) Research In Motion Limited 2009. All rights reserved. + Copyright (C) Research In Motion Limited 2009-2010. All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -33,12 +33,13 @@ #include "MappedAttribute.h" #include "NodeRenderStyle.h" #include "RegisteredEventListener.h" -#include "RenderSVGTransformableContainer.h" +#include "RenderSVGShadowTreeRootContainer.h" #include "SVGElementInstance.h" #include "SVGElementInstanceList.h" #include "SVGGElement.h" #include "SVGLength.h" #include "SVGPreserveAspectRatio.h" +#include "SVGShadowTreeElements.h" #include "SVGSMILElement.h" #include "SVGSVGElement.h" #include "SVGSymbolElement.h" @@ -59,12 +60,12 @@ SVGUseElement::SVGUseElement(const QualifiedName& tagName, Document* doc) , SVGLangSpace() , SVGExternalResourcesRequired() , SVGURIReference() - , m_x(this, SVGNames::xAttr, LengthModeWidth) - , m_y(this, SVGNames::yAttr, LengthModeHeight) - , m_width(this, SVGNames::widthAttr, LengthModeWidth) - , m_height(this, SVGNames::heightAttr, LengthModeHeight) - , m_href(this, XLinkNames::hrefAttr) - , m_externalResourcesRequired(this, SVGNames::externalResourcesRequiredAttr, false) + , m_x(LengthModeWidth) + , m_y(LengthModeHeight) + , m_width(LengthModeWidth) + , m_height(LengthModeHeight) + , m_isPendingResource(false) + , m_needsShadowTreeRecreation(false) { } @@ -74,6 +75,13 @@ SVGUseElement::~SVGUseElement() SVGElementInstance* SVGUseElement::instanceRoot() const { + // If there is no element instance tree, force immediate SVGElementInstance tree + // creation by asking the document to invoke our recalcStyle function - as we can't + // wait for the lazy creation to happen if e.g. JS wants to access the instanceRoot + // object right after creating the element on-the-fly + if (!m_targetElementInstance) + document()->updateLayoutIgnorePendingStylesheets(); + return m_targetElementInstance.get(); } @@ -112,14 +120,15 @@ void SVGUseElement::parseMappedAttribute(MappedAttribute* attr) void SVGUseElement::insertedIntoDocument() { + // This functions exists to assure assumptions made in the code regarding SVGElementInstance creation/destruction are satisfied. SVGElement::insertedIntoDocument(); - buildPendingResource(); + ASSERT(!m_targetElementInstance); + ASSERT(!m_isPendingResource); } void SVGUseElement::removedFromDocument() { m_targetElementInstance = 0; - m_shadowTreeRootElement = 0; SVGElement::removedFromDocument(); } @@ -127,87 +136,198 @@ void SVGUseElement::svgAttributeChanged(const QualifiedName& attrName) { SVGStyledTransformableElement::svgAttributeChanged(attrName); - if (!attached()) + if (!renderer()) return; - if (attrName == SVGNames::xAttr || attrName == SVGNames::yAttr || - attrName == SVGNames::widthAttr || attrName == SVGNames::heightAttr || - SVGTests::isKnownAttribute(attrName) || - SVGLangSpace::isKnownAttribute(attrName) || - SVGExternalResourcesRequired::isKnownAttribute(attrName) || - SVGURIReference::isKnownAttribute(attrName) || - SVGStyledTransformableElement::isKnownAttribute(attrName)) { - buildPendingResource(); - - if (m_shadowTreeRootElement) - m_shadowTreeRootElement->setNeedsStyleRecalc(); + if (SVGURIReference::isKnownAttribute(attrName)) { + if (m_isPendingResource) { + document()->accessSVGExtensions()->removePendingResource(m_resourceId); + m_resourceId = String(); + m_isPendingResource = false; + } + + invalidateShadowTree(); + return; + } + + if (attrName == SVGNames::xAttr || attrName == SVGNames::yAttr) { + updateContainerOffsets(); + return; + } + + if (attrName == SVGNames::widthAttr || attrName == SVGNames::heightAttr) { + updateContainerSizes(); + return; + } + + // Be very careful here, if svgAttributeChanged() has been called because a SVG CSS property changed, we do NOT want to reclone the tree! + if (SVGStyledElement::isKnownAttribute(attrName)) { + setNeedsStyleRecalc(); + return; + } + + if (SVGTests::isKnownAttribute(attrName) + || SVGLangSpace::isKnownAttribute(attrName) + || SVGExternalResourcesRequired::isKnownAttribute(attrName) + || SVGStyledTransformableElement::isKnownAttribute(attrName)) { + invalidateShadowTree(); } } -void SVGUseElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) +void SVGUseElement::synchronizeProperty(const QualifiedName& attrName) { - SVGElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); + SVGStyledTransformableElement::synchronizeProperty(attrName); - if (!attached()) + if (attrName == anyQName()) { + synchronizeX(); + synchronizeY(); + synchronizeWidth(); + synchronizeHeight(); + synchronizeExternalResourcesRequired(); + synchronizeHref(); return; + } - buildPendingResource(); + if (attrName == SVGNames::xAttr) + synchronizeX(); + else if (attrName == SVGNames::yAttr) + synchronizeY(); + else if (attrName == SVGNames::widthAttr) + synchronizeWidth(); + else if (attrName == SVGNames::heightAttr) + synchronizeHeight(); + else if (SVGExternalResourcesRequired::isKnownAttribute(attrName)) + synchronizeExternalResourcesRequired(); + else if (SVGURIReference::isKnownAttribute(attrName)) + synchronizeHref(); +} - if (m_shadowTreeRootElement) - m_shadowTreeRootElement->setNeedsStyleRecalc(); +static void updateContainerSize(SVGUseElement* useElement, SVGElementInstance* targetInstance) +{ + // Depth-first used to write the method in early exit style, no particular other reason. + for (SVGElementInstance* instance = targetInstance->firstChild(); instance; instance = instance->nextSibling()) + updateContainerSize(useElement, instance); + + SVGElement* correspondingElement = targetInstance->correspondingElement(); + ASSERT(correspondingElement); + + bool isSymbolTag = correspondingElement->hasTagName(SVGNames::symbolTag); + if (!correspondingElement->hasTagName(SVGNames::svgTag) && !isSymbolTag) + return; + + SVGElement* shadowTreeElement = targetInstance->shadowTreeElement(); + ASSERT(shadowTreeElement); + ASSERT(shadowTreeElement->hasTagName(SVGNames::svgTag)); + + // Spec (<use> on <symbol>): This generated 'svg' will always have explicit values for attributes width and height. + // If attributes width and/or height are provided on the 'use' element, then these attributes + // will be transferred to the generated 'svg'. If attributes width and/or height are not specified, + // the generated 'svg' element will use values of 100% for these attributes. + + // Spec (<use> on <svg>): If attributes width and/or height are provided on the 'use' element, then these + // values will override the corresponding attributes on the 'svg' in the generated tree. + + if (useElement->hasAttribute(SVGNames::widthAttr)) + shadowTreeElement->setAttribute(SVGNames::widthAttr, useElement->getAttribute(SVGNames::widthAttr)); + else if (isSymbolTag && shadowTreeElement->hasAttribute(SVGNames::widthAttr)) + shadowTreeElement->setAttribute(SVGNames::widthAttr, "100%"); + + if (useElement->hasAttribute(SVGNames::heightAttr)) + shadowTreeElement->setAttribute(SVGNames::heightAttr, useElement->getAttribute(SVGNames::heightAttr)); + else if (isSymbolTag && shadowTreeElement->hasAttribute(SVGNames::heightAttr)) + shadowTreeElement->setAttribute(SVGNames::heightAttr, "100%"); +} + +void SVGUseElement::updateContainerSizes() +{ + if (!m_targetElementInstance) + return; + + // Update whole subtree, scanning for shadow container elements, that correspond to <svg>/<symbol> tags + updateContainerSize(this, m_targetElementInstance.get()); + + if (renderer()) + renderer()->setNeedsLayout(true); } - -static bool shadowTreeContainsChangedNodes(SVGElementInstance* target) + +static void updateContainerOffset(SVGElementInstance* targetInstance) { - if (!target) // when use is referencing an non-existing element, there will be no Instance tree built - return false; + // Depth-first used to write the method in early exit style, no particular other reason. + for (SVGElementInstance* instance = targetInstance->firstChild(); instance; instance = instance->nextSibling()) + updateContainerOffset(instance); - if (target->needsUpdate()) - return true; + SVGElement* correspondingElement = targetInstance->correspondingElement(); + ASSERT(correspondingElement); - for (SVGElementInstance* instance = target->firstChild(); instance; instance = instance->nextSibling()) - if (shadowTreeContainsChangedNodes(instance)) - return true; + if (!correspondingElement->hasTagName(SVGNames::useTag)) + return; - return false; + SVGElement* shadowTreeElement = targetInstance->shadowTreeElement(); + ASSERT(shadowTreeElement); + ASSERT(shadowTreeElement->hasTagName(SVGNames::gTag)); + + if (!static_cast<SVGGElement*>(shadowTreeElement)->isShadowTreeContainerElement()) + return; + + // Spec: An additional transformation translate(x,y) is appended to the end + // (i.e., right-side) of the transform attribute on the generated 'g', where x + // and y represent the values of the x and y attributes on the 'use' element. + SVGUseElement* useElement = static_cast<SVGUseElement*>(correspondingElement); + SVGShadowTreeContainerElement* containerElement = static_cast<SVGShadowTreeContainerElement*>(shadowTreeElement); + containerElement->setContainerOffset(useElement->x(), useElement->y()); } -void SVGUseElement::recalcStyle(StyleChange change) +void SVGUseElement::updateContainerOffsets() { - if (attached() && needsStyleRecalc() && shadowTreeContainsChangedNodes(m_targetElementInstance.get())) { - buildPendingResource(); + if (!m_targetElementInstance) + return; + + // Update root container offset (not reachable through instance tree) + SVGElement* shadowRoot = m_targetElementInstance->shadowTreeElement(); + ASSERT(shadowRoot); - if (m_shadowTreeRootElement) - m_shadowTreeRootElement->setNeedsStyleRecalc(); + Node* parentNode = shadowRoot->parentNode(); + ASSERT(parentNode); + ASSERT(parentNode->isSVGElement()); + ASSERT(parentNode->hasTagName(SVGNames::gTag)); + ASSERT(static_cast<SVGGElement*>(parentNode)->isShadowTreeContainerElement()); + + SVGShadowTreeContainerElement* containerElement = static_cast<SVGShadowTreeContainerElement*>(parentNode); + containerElement->setContainerOffset(x(), y()); + + // Update whole subtree, scanning for shadow container elements, marking a cloned use subtree + updateContainerOffset(m_targetElementInstance.get()); + + if (renderer()) + renderer()->setNeedsLayout(true); +} + +void SVGUseElement::recalcStyle(StyleChange change) +{ + // Eventually mark shadow root element needing style recalc + if (needsStyleRecalc() && m_targetElementInstance) { + if (SVGElement* shadowRoot = m_targetElementInstance->shadowTreeElement()) + shadowRoot->setNeedsStyleRecalc(); } - SVGStyledElement::recalcStyle(change); + SVGStyledTransformableElement::recalcStyle(change); + + bool needsStyleUpdate = !m_needsShadowTreeRecreation; + if (m_needsShadowTreeRecreation) { + static_cast<RenderSVGShadowTreeRootContainer*>(renderer())->markShadowTreeForRecreation(); + m_needsShadowTreeRecreation = false; + } - // The shadow tree root element is NOT a direct child element of us. - // So we have to take care it receives style updates, manually. - if (!m_shadowTreeRootElement || !m_shadowTreeRootElement->attached()) + RenderSVGShadowTreeRootContainer* shadowRoot = static_cast<RenderSVGShadowTreeRootContainer*>(renderer()); + if (!shadowRoot) return; - // Mimic Element::recalcStyle(). The main difference is that we don't call attach() on the - // shadow tree root element, but call attachShadowTree() here. Calling attach() will crash - // as the shadow tree root element has no (direct) parent node. Yes, shadow trees are tricky. - if (change >= Inherit || m_shadowTreeRootElement->needsStyleRecalc()) { - RefPtr<RenderStyle> newStyle = document()->styleSelector()->styleForElement(m_shadowTreeRootElement.get()); - StyleChange ch = Node::diff(m_shadowTreeRootElement->renderStyle(), newStyle.get()); - if (ch == Detach) { - ASSERT(m_shadowTreeRootElement->attached()); - m_shadowTreeRootElement->detach(); - attachShadowTree(); - - // attach recalulates the style for all children. No need to do it twice. - m_shadowTreeRootElement->setNeedsStyleRecalc(NoStyleChange); - m_shadowTreeRootElement->setChildNeedsStyleRecalc(false); - return; - } - } + shadowRoot->updateFromElement(); - // Only change==Detach needs special treatment, for anything else recalcStyle() works. - m_shadowTreeRootElement->recalcStyle(change); + if (!needsStyleUpdate) + return; + + shadowRoot->updateStyle(change); } #ifdef DUMP_INSTANCE_TREE @@ -216,26 +336,30 @@ void dumpInstanceTree(unsigned int& depth, String& text, SVGElementInstance* tar SVGElement* element = targetInstance->correspondingElement(); ASSERT(element); + SVGElement* shadowTreeElement = targetInstance->shadowTreeElement(); + ASSERT(shadowTreeElement); + String elementId = element->getIDAttribute(); String elementNodeName = element->nodeName(); + String shadowTreeElementNodeName = shadowTreeElement->nodeName(); String parentNodeName = element->parentNode() ? element->parentNode()->nodeName() : "null"; String firstChildNodeName = element->firstChild() ? element->firstChild()->nodeName() : "null"; for (unsigned int i = 0; i < depth; ++i) text += " "; - text += String::format("SVGElementInstance this=%p, (parentNode=%s, firstChild=%s, correspondingElement=%s (%p), shadowTreeElement=%p, id=%s)\n", - targetInstance, parentNodeName.latin1().data(), firstChildNodeName.latin1().data(), elementNodeName.latin1().data(), - element, targetInstance->shadowTreeElement(), elementId.latin1().data()); + text += String::format("SVGElementInstance this=%p, (parentNode=%s (%p), firstChild=%s (%p), correspondingElement=%s (%p), shadowTreeElement=%s (%p), id=%s)\n", + targetInstance, parentNodeName.latin1().data(), element->parentNode(), firstChildNodeName.latin1().data(), element->firstChild(), + elementNodeName.latin1().data(), element, shadowTreeElementNodeName.latin1().data(), shadowTreeElement, elementId.latin1().data()); for (unsigned int i = 0; i < depth; ++i) text += " "; - HashSet<SVGElementInstance*> elementInstances = element->instancesForElement(); + const HashSet<SVGElementInstance*>& elementInstances = element->instancesForElement(); text += String::format("Corresponding element is associated with %i instance(s):\n", elementInstances.size()); - HashSet<SVGElementInstance*>::iterator end = elementInstances.end(); - for (HashSet<SVGElementInstance*>::iterator it = elementInstances.begin(); it != end; ++it) { + const HashSet<SVGElementInstance*>::const_iterator end = elementInstances.end(); + for (HashSet<SVGElementInstance*>::const_iterator it = elementInstances.begin(); it != end; ++it) { for (unsigned int i = 0; i < depth; ++i) text += " "; @@ -282,16 +406,36 @@ static bool subtreeContainsDisallowedElement(Node* start) void SVGUseElement::buildPendingResource() { + // If we're called the first time (during shadow tree root creation from RenderSVGShadowTreeRootContainer) + // we either determine that our target is available or not - then we add ourselves to the pending resource list + // Once the pending resource appears, it will call buildPendingResource(), so we're called a second time. String id = SVGURIReference::getTarget(href()); Element* targetElement = document()->getElementById(id); + ASSERT(!m_targetElementInstance); if (!targetElement) { - // TODO: We want to deregister as pending resource, if our href() changed! - // TODO: Move to svgAttributeChanged, once we're fixing use & the new dynamic update concept. + if (m_isPendingResource) + return; + + m_isPendingResource = true; + m_resourceId = id; document()->accessSVGExtensions()->addPendingResource(id, this); return; } + if (m_isPendingResource) { + ASSERT(!m_targetElementInstance); + m_isPendingResource = false; + invalidateShadowTree(); + } +} + +void SVGUseElement::buildShadowAndInstanceTree(SVGShadowTreeRootElement* shadowRoot) +{ + String id = SVGURIReference::getTarget(href()); + Element* targetElement = document()->getElementById(id); + ASSERT(targetElement); + // Do not build the shadow/instance tree for <use> elements living in a shadow tree. // The will be expanded soon anyway - see expandUseElementsInShadowTree(). Node* parent = parentNode(); @@ -306,17 +450,13 @@ void SVGUseElement::buildPendingResource() if (targetElement && targetElement->isSVGElement()) target = static_cast<SVGElement*>(targetElement); - if (m_targetElementInstance) { - m_targetElementInstance->forgetWrapper(); + if (m_targetElementInstance) m_targetElementInstance = 0; - } // Do not allow self-referencing. // 'target' may be null, if it's a non SVG namespaced element. - if (!target || target == this) { - m_shadowTreeRootElement = 0; + if (!target || target == this) return; - } // Why a seperated instance/shadow tree? SVG demands it: // The instance tree is accesable from JavaScript, and has to @@ -338,45 +478,44 @@ void SVGUseElement::buildPendingResource() // Non-appearing <use> content is easier to debug, then half-appearing content. if (foundProblem) { m_targetElementInstance = 0; - m_shadowTreeRootElement = 0; return; } // Assure instance tree building was successfull ASSERT(m_targetElementInstance); + ASSERT(!m_targetElementInstance->shadowTreeElement()); ASSERT(m_targetElementInstance->correspondingUseElement() == this); - - // Setup shadow tree root node - m_shadowTreeRootElement = new SVGGElement(SVGNames::gTag, document()); - m_shadowTreeRootElement->setInDocument(); - m_shadowTreeRootElement->setShadowParentNode(this); - - // Spec: An additional transformation translate(x,y) is appended to the end - // (i.e., right-side) of the transform attribute on the generated 'g', where x - // and y represent the values of the x and y attributes on the 'use' element. - if (x().value(this) != 0.0 || y().value(this) != 0.0) { - String transformString = String::format("translate(%f, %f)", x().value(this), y().value(this)); - m_shadowTreeRootElement->setAttribute(SVGNames::transformAttr, transformString); - } + ASSERT(m_targetElementInstance->correspondingElement() == target); // Build shadow tree from instance tree // This also handles the special cases: <use> on <symbol>, <use> on <svg>. - buildShadowTree(target, m_targetElementInstance.get()); + buildShadowTree(shadowRoot, target, m_targetElementInstance.get()); #if ENABLE(SVG) && ENABLE(SVG_USE) // Expand all <use> elements in the shadow tree. // Expand means: replace the actual <use> element by what it references. - expandUseElementsInShadowTree(m_shadowTreeRootElement.get()); + expandUseElementsInShadowTree(shadowRoot, shadowRoot); // Expand all <symbol> elements in the shadow tree. // Expand means: replace the actual <symbol> element by the <svg> element. - expandSymbolElementsInShadowTree(m_shadowTreeRootElement.get()); - + expandSymbolElementsInShadowTree(shadowRoot, shadowRoot); #endif // Now that the shadow tree is completly expanded, we can associate // shadow tree elements <-> instances in the instance tree. - associateInstancesWithShadowTreeElements(m_shadowTreeRootElement->firstChild(), m_targetElementInstance.get()); + associateInstancesWithShadowTreeElements(shadowRoot->firstChild(), m_targetElementInstance.get()); + + // If no shadow tree element is present, this means that the reference root + // element was removed, as it is disallowed (ie. <use> on <foreignObject>) + // Do NOT leave an inconsistent instance tree around, instead destruct it. + if (!m_targetElementInstance->shadowTreeElement()) { + shadowRoot->removeAllChildren(); + m_targetElementInstance = 0; + return; + } + + // Consistency checks - this is assumed in updateContainerOffset(). + ASSERT(m_targetElementInstance->shadowTreeElement()->parentNode() == shadowRoot); // Eventually dump instance tree #ifdef DUMP_INSTANCE_TREE @@ -393,8 +532,8 @@ void SVGUseElement::buildPendingResource() PassRefPtr<XMLSerializer> serializer = XMLSerializer::create(); - String markup = serializer->serializeToString(m_shadowTreeRootElement.get(), ec); - ASSERT(ec == 0); + String markup = serializer->serializeToString(shadowRoot, ec); + ASSERT(!ec); fprintf(stderr, "Dumping <use> shadow tree markup:\n%s\n", markup.latin1().data()); #endif @@ -402,30 +541,34 @@ void SVGUseElement::buildPendingResource() // Transfer event listeners assigned to the referenced element to our shadow tree elements. transferEventListenersToShadowTree(m_targetElementInstance.get()); - // The DOM side is setup properly. Now we have to attach the root shadow - // tree element manually - using attach() won't work for "shadow nodes". - attachShadowTree(); + // Update container offset/size + updateContainerOffsets(); + updateContainerSizes(); } RenderObject* SVGUseElement::createRenderer(RenderArena* arena, RenderStyle*) { - return new (arena) RenderSVGTransformableContainer(this); + return new (arena) RenderSVGShadowTreeRootContainer(this); +} + +static void updateFromElementCallback(Node* node) +{ + if (RenderObject* renderer = node->renderer()) + renderer->updateFromElement(); } void SVGUseElement::attach() { SVGStyledTransformableElement::attach(); - // If we're a pending resource, this doesn't have any effect. - attachShadowTree(); + if (renderer()) + queuePostAttachCallback(updateFromElementCallback, this); } void SVGUseElement::detach() { SVGStyledTransformableElement::detach(); - - if (m_shadowTreeRootElement) - m_shadowTreeRootElement->detach(); + m_targetElementInstance = 0; } static bool isDirectReference(Node* n) @@ -441,13 +584,10 @@ static bool isDirectReference(Node* n) Path SVGUseElement::toClipPath() const { - if (!m_shadowTreeRootElement) - const_cast<SVGUseElement*>(this)->buildPendingResource(); - - if (!m_shadowTreeRootElement) + Node* n = m_targetElementInstance ? m_targetElementInstance->shadowTreeElement() : 0; + if (!n) return Path(); - Node* n = m_shadowTreeRootElement->firstChild(); if (n->isSVGElement() && static_cast<SVGElement*>(n)->isStyledTransformable()) { if (!isDirectReference(n)) // Spec: Indirect references are an error (14.3.5) @@ -481,11 +621,12 @@ void SVGUseElement::buildInstanceTree(SVGElement* target, SVGElementInstance* ta continue; // Create SVGElementInstance object, for both container/non-container nodes. - RefPtr<SVGElementInstance> instancePtr = SVGElementInstance::create(this, element); - targetInstance->appendChild(instancePtr.get()); + RefPtr<SVGElementInstance> instance = SVGElementInstance::create(this, element); + SVGElementInstance* instancePtr = instance.get(); + targetInstance->appendChild(instance.release()); // Enter recursion, appending new instance tree nodes to the "instance" object. - buildInstanceTree(element, instancePtr.get(), foundProblem); + buildInstanceTree(element, instancePtr, foundProblem); } // Spec: If the referenced object is itself a 'use', or if there are 'use' subelements within the referenced @@ -526,22 +667,11 @@ void SVGUseElement::handleDeepUseReferencing(SVGUseElement* use, SVGElementInsta // Create an instance object, even if we're dealing with a cycle RefPtr<SVGElementInstance> newInstance = SVGElementInstance::create(this, target); - targetInstance->appendChild(newInstance); + SVGElementInstance* newInstancePtr = newInstance.get(); + targetInstance->appendChild(newInstance.release()); // Eventually enter recursion to build SVGElementInstance objects for the sub-tree children - buildInstanceTree(target, newInstance.get(), foundProblem); -} - -void SVGUseElement::alterShadowTreeForSVGTag(SVGElement* target) -{ - String widthString = String::number(width().value(this)); - String heightString = String::number(height().value(this)); - - if (hasAttribute(SVGNames::widthAttr)) - target->setAttribute(SVGNames::widthAttr, widthString); - - if (hasAttribute(SVGNames::heightAttr)) - target->setAttribute(SVGNames::heightAttr, heightString); + buildInstanceTree(target, newInstancePtr, foundProblem); } void SVGUseElement::removeDisallowedElementsFromSubtree(Node* subtree) @@ -560,7 +690,7 @@ void SVGUseElement::removeDisallowedElementsFromSubtree(Node* subtree) } } -void SVGUseElement::buildShadowTree(SVGElement* target, SVGElementInstance* targetInstance) +void SVGUseElement::buildShadowTree(SVGShadowTreeRootElement* shadowRoot, SVGElement* target, SVGElementInstance* targetInstance) { // For instance <use> on <foreignObject> (direct case). if (isDisallowedElement(target)) @@ -582,16 +712,12 @@ void SVGUseElement::buildShadowTree(SVGElement* target, SVGElementInstance* targ ASSERT(newChildPtr); ExceptionCode ec = 0; - m_shadowTreeRootElement->appendChild(newChild.release(), ec); - ASSERT(ec == 0); - - // Handle use referencing <svg> special case - if (target->hasTagName(SVGNames::svgTag)) - alterShadowTreeForSVGTag(newChildPtr); + shadowRoot->appendChild(newChild.release(), ec); + ASSERT(!ec); } #if ENABLE(SVG) && ENABLE(SVG_USE) -void SVGUseElement::expandUseElementsInShadowTree(Node* element) +void SVGUseElement::expandUseElementsInShadowTree(SVGShadowTreeRootElement* shadowRoot, Node* element) { // Why expand the <use> elements in the shadow tree here, and not just // do this directly in buildShadowTree, if we encounter a <use> element? @@ -612,28 +738,14 @@ void SVGUseElement::expandUseElementsInShadowTree(Node* element) // Don't ASSERT(target) here, it may be "pending", too. if (target) { // Setup sub-shadow tree root node - RefPtr<SVGElement> cloneParent = new SVGGElement(SVGNames::gTag, document()); + RefPtr<SVGShadowTreeContainerElement> cloneParent = new SVGShadowTreeContainerElement(document()); // Spec: In the generated content, the 'use' will be replaced by 'g', where all attributes from the // 'use' element except for x, y, width, height and xlink:href are transferred to the generated 'g' element. transferUseAttributesToReplacedElement(use, cloneParent.get()); - // Spec: An additional transformation translate(x,y) is appended to the end - // (i.e., right-side) of the transform attribute on the generated 'g', where x - // and y represent the values of the x and y attributes on the 'use' element. - if (use->x().value(this) != 0.0 || use->y().value(this) != 0.0) { - if (!cloneParent->hasAttribute(SVGNames::transformAttr)) { - String transformString = String::format("translate(%f, %f)", use->x().value(this), use->y().value(this)); - cloneParent->setAttribute(SVGNames::transformAttr, transformString); - } else { - String transformString = String::format(" translate(%f, %f)", use->x().value(this), use->y().value(this)); - const AtomicString& transformAttribute = cloneParent->getAttribute(SVGNames::transformAttr); - cloneParent->setAttribute(SVGNames::transformAttr, transformAttribute + transformString); - } - } - ExceptionCode ec = 0; - + // For instance <use> on <foreignObject> (direct case). if (isDisallowedElement(target)) { // We still have to setup the <use> replacment (<g>). Otherwhise @@ -641,7 +753,7 @@ void SVGUseElement::expandUseElementsInShadowTree(Node* element) // Replace <use> with referenced content. ASSERT(use->parentNode()); use->parentNode()->replaceChild(cloneParent.release(), use, ec); - ASSERT(ec == 0); + ASSERT(!ec); return; } @@ -661,28 +773,24 @@ void SVGUseElement::expandUseElementsInShadowTree(Node* element) ASSERT(newChildPtr); cloneParent->appendChild(newChild.release(), ec); - ASSERT(ec == 0); + ASSERT(!ec); // Replace <use> with referenced content. ASSERT(use->parentNode()); use->parentNode()->replaceChild(cloneParent.release(), use, ec); - ASSERT(ec == 0); - - // Handle use referencing <svg> special case - if (target->hasTagName(SVGNames::svgTag)) - alterShadowTreeForSVGTag(newChildPtr); + ASSERT(!ec); // Immediately stop here, and restart expanding. - expandUseElementsInShadowTree(m_shadowTreeRootElement.get()); + expandUseElementsInShadowTree(shadowRoot, shadowRoot); return; } } for (RefPtr<Node> child = element->firstChild(); child; child = child->nextSibling()) - expandUseElementsInShadowTree(child.get()); + expandUseElementsInShadowTree(shadowRoot, child.get()); } -void SVGUseElement::expandSymbolElementsInShadowTree(Node* element) +void SVGUseElement::expandSymbolElementsInShadowTree(SVGShadowTreeRootElement* shadowRoot, Node* element) { if (element->hasTagName(SVGNames::symbolTag)) { // Spec: The referenced 'symbol' and its contents are deep-cloned into the generated tree, @@ -696,20 +804,12 @@ void SVGUseElement::expandSymbolElementsInShadowTree(Node* element) // Transfer all attributes from <symbol> to the new <svg> element svgElement->attributes()->setAttributes(*element->attributes()); - // Explicitly re-set width/height values - String widthString = String::number(width().value(this)); - String heightString = String::number(height().value(this)); - - svgElement->setAttribute(SVGNames::widthAttr, hasAttribute(SVGNames::widthAttr) ? widthString : "100%"); - svgElement->setAttribute(SVGNames::heightAttr, hasAttribute(SVGNames::heightAttr) ? heightString : "100%"); - - ExceptionCode ec = 0; - // Only clone symbol children, and add them to the new <svg> element + ExceptionCode ec = 0; for (Node* child = element->firstChild(); child; child = child->nextSibling()) { RefPtr<Node> newChild = child->cloneNode(true); svgElement->appendChild(newChild.release(), ec); - ASSERT(ec == 0); + ASSERT(!ec); } // We don't walk the target tree element-by-element, and clone each element, @@ -723,43 +823,19 @@ void SVGUseElement::expandSymbolElementsInShadowTree(Node* element) // Replace <symbol> with <svg>. ASSERT(element->parentNode()); element->parentNode()->replaceChild(svgElement.release(), element, ec); - ASSERT(ec == 0); + ASSERT(!ec); // Immediately stop here, and restart expanding. - expandSymbolElementsInShadowTree(m_shadowTreeRootElement.get()); + expandSymbolElementsInShadowTree(shadowRoot, shadowRoot); return; } for (RefPtr<Node> child = element->firstChild(); child; child = child->nextSibling()) - expandSymbolElementsInShadowTree(child.get()); + expandSymbolElementsInShadowTree(shadowRoot, child.get()); } #endif - -void SVGUseElement::attachShadowTree() -{ - if (!m_shadowTreeRootElement || m_shadowTreeRootElement->attached() || !document()->shouldCreateRenderers() || !attached() || !renderer()) - return; - // Inspired by RenderTextControl::createSubtreeIfNeeded(). - if (renderer()->canHaveChildren() && childShouldCreateRenderer(m_shadowTreeRootElement.get())) { - RefPtr<RenderStyle> style = m_shadowTreeRootElement->styleForRenderer(); - - if (m_shadowTreeRootElement->rendererIsNeeded(style.get())) { - m_shadowTreeRootElement->setRenderer(m_shadowTreeRootElement->createRenderer(document()->renderArena(), style.get())); - if (RenderObject* shadowRenderer = m_shadowTreeRootElement->renderer()) { - shadowRenderer->setStyle(style.release()); - renderer()->addChild(shadowRenderer, m_shadowTreeRootElement->nextRenderer()); - m_shadowTreeRootElement->setAttached(); - } - } - - // This will take care of attaching all shadow tree child nodes. - for (Node* child = m_shadowTreeRootElement->firstChild(); child; child = child->nextSibling()) - child->attach(); - } -} - void SVGUseElement::transferEventListenersToShadowTree(SVGElementInstance* target) { if (!target) @@ -830,6 +906,11 @@ void SVGUseElement::associateInstancesWithShadowTreeElements(Node* target, SVGEl SVGElementInstance* SVGUseElement::instanceForShadowTreeElement(Node* element) const { + if (!m_targetElementInstance) { + ASSERT(!inDocument()); + return 0; + } + return instanceForShadowTreeElement(element, m_targetElementInstance.get()); } @@ -847,14 +928,19 @@ SVGElementInstance* SVGUseElement::instanceForShadowTreeElement(Node* element, S return instance; for (SVGElementInstance* current = instance->firstChild(); current; current = current->nextSibling()) { - SVGElementInstance* search = instanceForShadowTreeElement(element, current); - if (search) + if (SVGElementInstance* search = instanceForShadowTreeElement(element, current)) return search; } return 0; } +void SVGUseElement::invalidateShadowTree() +{ + m_needsShadowTreeRecreation = true; + setNeedsStyleRecalc(); +} + void SVGUseElement::transferUseAttributesToReplacedElement(SVGElement* from, SVGElement* to) const { ASSERT(from); @@ -865,19 +951,24 @@ void SVGUseElement::transferUseAttributesToReplacedElement(SVGElement* from, SVG ExceptionCode ec = 0; to->removeAttribute(SVGNames::xAttr, ec); - ASSERT(ec == 0); + ASSERT(!ec); to->removeAttribute(SVGNames::yAttr, ec); - ASSERT(ec == 0); + ASSERT(!ec); to->removeAttribute(SVGNames::widthAttr, ec); - ASSERT(ec == 0); + ASSERT(!ec); to->removeAttribute(SVGNames::heightAttr, ec); - ASSERT(ec == 0); + ASSERT(!ec); to->removeAttribute(XLinkNames::hrefAttr, ec); - ASSERT(ec == 0); + ASSERT(!ec); +} + +bool SVGUseElement::hasRelativeValues() const +{ + return x().isRelative() || y().isRelative() || width().isRelative() || height().isRelative(); } } |