diff options
Diffstat (limited to 'chromium/third_party/WebKit/Source/core/svg/SVGUseElement.cpp')
-rw-r--r-- | chromium/third_party/WebKit/Source/core/svg/SVGUseElement.cpp | 708 |
1 files changed, 243 insertions, 465 deletions
diff --git a/chromium/third_party/WebKit/Source/core/svg/SVGUseElement.cpp b/chromium/third_party/WebKit/Source/core/svg/SVGUseElement.cpp index 80d36bf97f3..a45d3228474 100644 --- a/chromium/third_party/WebKit/Source/core/svg/SVGUseElement.cpp +++ b/chromium/third_party/WebKit/Source/core/svg/SVGUseElement.cpp @@ -26,8 +26,8 @@ #include "core/svg/SVGUseElement.h" -#include "XLinkNames.h" #include "bindings/v8/ExceptionStatePlaceholder.h" +#include "core/XLinkNames.h" #include "core/dom/Document.h" #include "core/dom/ElementTraversal.h" #include "core/events/Event.h" @@ -37,58 +37,37 @@ #include "core/fetch/ResourceFetcher.h" #include "core/rendering/svg/RenderSVGResource.h" #include "core/rendering/svg/RenderSVGTransformableContainer.h" -#include "core/svg/SVGElementInstance.h" #include "core/svg/SVGGElement.h" #include "core/svg/SVGLengthContext.h" #include "core/svg/SVGSVGElement.h" #include "core/xml/parser/XMLDocumentParser.h" -// Dump SVGElementInstance object tree - useful to debug instanceRoot problems -// #define DUMP_INSTANCE_TREE - -// Dump the deep-expanded shadow tree (where the renderers are built from) -// #define DUMP_SHADOW_TREE - namespace WebCore { -// Animated property definitions -DEFINE_ANIMATED_LENGTH(SVGUseElement, SVGNames::xAttr, X, x) -DEFINE_ANIMATED_LENGTH(SVGUseElement, SVGNames::yAttr, Y, y) -DEFINE_ANIMATED_LENGTH(SVGUseElement, SVGNames::widthAttr, Width, width) -DEFINE_ANIMATED_LENGTH(SVGUseElement, SVGNames::heightAttr, Height, height) -DEFINE_ANIMATED_STRING(SVGUseElement, XLinkNames::hrefAttr, Href, href) -DEFINE_ANIMATED_BOOLEAN(SVGUseElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired) - -BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGUseElement) - REGISTER_LOCAL_ANIMATED_PROPERTY(x) - REGISTER_LOCAL_ANIMATED_PROPERTY(y) - REGISTER_LOCAL_ANIMATED_PROPERTY(width) - REGISTER_LOCAL_ANIMATED_PROPERTY(height) - REGISTER_LOCAL_ANIMATED_PROPERTY(href) - REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired) - REGISTER_PARENT_ANIMATED_PROPERTIES(SVGGraphicsElement) -END_REGISTER_ANIMATED_PROPERTIES - -inline SVGUseElement::SVGUseElement(Document& document, bool wasInsertedByParser) +inline SVGUseElement::SVGUseElement(Document& document) : SVGGraphicsElement(SVGNames::useTag, document) - , m_x(LengthModeWidth) - , m_y(LengthModeHeight) - , m_width(LengthModeWidth) - , m_height(LengthModeHeight) - , m_wasInsertedByParser(wasInsertedByParser) + , SVGURIReference(this) + , m_x(SVGAnimatedLength::create(this, SVGNames::xAttr, SVGLength::create(LengthModeWidth), AllowNegativeLengths)) + , m_y(SVGAnimatedLength::create(this, SVGNames::yAttr, SVGLength::create(LengthModeHeight), AllowNegativeLengths)) + , m_width(SVGAnimatedLength::create(this, SVGNames::widthAttr, SVGLength::create(LengthModeWidth), ForbidNegativeLengths)) + , m_height(SVGAnimatedLength::create(this, SVGNames::heightAttr, SVGLength::create(LengthModeHeight), ForbidNegativeLengths)) , m_haveFiredLoadEvent(false) , m_needsShadowTreeRecreation(false) , m_svgLoadEventTimer(this, &SVGElement::svgLoadEventTimerFired) { ASSERT(hasCustomStyleCallbacks()); ScriptWrappable::init(this); - registerAnimatedPropertiesForSVGUseElement(); + + addToPropertyMap(m_x); + addToPropertyMap(m_y); + addToPropertyMap(m_width); + addToPropertyMap(m_height); } -PassRefPtr<SVGUseElement> SVGUseElement::create(Document& document, bool wasInsertedByParser) +PassRefPtrWillBeRawPtr<SVGUseElement> SVGUseElement::create(Document& document) { - // Always build a #shadow-root for SVGUseElement. - RefPtr<SVGUseElement> use = adoptRef(new SVGUseElement(document, wasInsertedByParser)); + // Always build a user agent #shadow-root for SVGUseElement. + RefPtrWillBeRawPtr<SVGUseElement> use = adoptRefWillBeNoop(new SVGUseElement(document)); use->ensureUserAgentShadowRoot(); return use.release(); } @@ -96,33 +75,15 @@ PassRefPtr<SVGUseElement> SVGUseElement::create(Document& document, bool wasInse SVGUseElement::~SVGUseElement() { setDocumentResource(0); - +#if !ENABLE(OILPAN) clearResourceReferences(); -} - -SVGElementInstance* SVGUseElement::instanceRoot() -{ - // 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().updateStyleIfNeeded(); - - return m_targetElementInstance.get(); -} - -SVGElementInstance* SVGUseElement::animatedInstanceRoot() const -{ - // FIXME: Implement me. - return 0; +#endif } bool SVGUseElement::isSupportedAttribute(const QualifiedName& attrName) { DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ()); if (supportedAttributes.isEmpty()) { - SVGExternalResourcesRequired::addSupportedAttributes(supportedAttributes); SVGURIReference::addSupportedAttributes(supportedAttributes); supportedAttributes.add(SVGNames::xAttr); supportedAttributes.add(SVGNames::yAttr); @@ -136,28 +97,28 @@ void SVGUseElement::parseAttribute(const QualifiedName& name, const AtomicString { SVGParsingError parseError = NoError; - if (!isSupportedAttribute(name)) + if (!isSupportedAttribute(name)) { SVGGraphicsElement::parseAttribute(name, value); - else if (name == SVGNames::xAttr) - setXBaseValue(SVGLength::construct(LengthModeWidth, value, parseError)); - else if (name == SVGNames::yAttr) - setYBaseValue(SVGLength::construct(LengthModeHeight, value, parseError)); - else if (name == SVGNames::widthAttr) - setWidthBaseValue(SVGLength::construct(LengthModeWidth, value, parseError, ForbidNegativeLengths)); - else if (name == SVGNames::heightAttr) - setHeightBaseValue(SVGLength::construct(LengthModeHeight, value, parseError, ForbidNegativeLengths)); - else if (SVGExternalResourcesRequired::parseAttribute(name, value) - || SVGURIReference::parseAttribute(name, value)) { - } else + } else if (name == SVGNames::xAttr) { + m_x->setBaseValueAsString(value, parseError); + } else if (name == SVGNames::yAttr) { + m_y->setBaseValueAsString(value, parseError); + } else if (name == SVGNames::widthAttr) { + m_width->setBaseValueAsString(value, parseError); + } else if (name == SVGNames::heightAttr) { + m_height->setBaseValueAsString(value, parseError); + } else if (SVGURIReference::parseAttribute(name, value, parseError)) { + } else { ASSERT_NOT_REACHED(); + } reportAttributeParsingError(parseError, name, value); } -#if !ASSERT_DISABLED +#if ASSERT_ENABLED static inline bool isWellFormedDocument(Document* document) { - if (document->isSVGDocument() || document->isXHTMLDocument()) + if (document->isXMLDocument()) return static_cast<XMLDocumentParser*>(document->parser())->wellFormed(); return true; } @@ -171,9 +132,9 @@ Node::InsertionNotificationRequest SVGUseElement::insertedInto(ContainerNode* ro return InsertionDone; ASSERT(!m_targetElementInstance || !isWellFormedDocument(&document())); ASSERT(!hasPendingResources() || !isWellFormedDocument(&document())); - if (!m_wasInsertedByParser) - buildPendingResource(); - SVGExternalResourcesRequired::insertedIntoDocument(this); + invalidateShadowTree(); + if (!isStructurallyExternal()) + sendSVGLoadEventIfPossibleAsynchronously(); return InsertionDone; } @@ -184,10 +145,10 @@ void SVGUseElement::removedFrom(ContainerNode* rootParent) clearResourceReferences(); } -Document* SVGUseElement::referencedDocument() const +TreeScope* SVGUseElement::referencedScope() const { - if (!isExternalURIReference(hrefCurrentValue(), document())) - return &document(); + if (!isExternalURIReference(hrefString(), document())) + return &treeScope(); return externalDocument(); } @@ -203,6 +164,30 @@ Document* SVGUseElement::externalDocument() const return 0; } +void transferUseWidthAndHeightIfNeeded(const SVGUseElement& use, SVGElement* shadowElement, const SVGElement& originalElement) +{ + ASSERT(shadowElement); + if (isSVGSymbolElement(*shadowElement)) { + // 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. + shadowElement->setAttribute(SVGNames::widthAttr, use.width()->isSpecified() ? AtomicString(use.width()->currentValue()->valueAsString()) : "100%"); + shadowElement->setAttribute(SVGNames::heightAttr, use.height()->isSpecified() ? AtomicString(use.height()->currentValue()->valueAsString()) : "100%"); + } else if (isSVGSVGElement(*shadowElement)) { + // 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 (use.width()->isSpecified()) + shadowElement->setAttribute(SVGNames::widthAttr, AtomicString(use.width()->currentValue()->valueAsString())); + else + shadowElement->setAttribute(SVGNames::widthAttr, originalElement.getAttribute(SVGNames::widthAttr)); + if (use.height()->isSpecified()) + shadowElement->setAttribute(SVGNames::heightAttr, AtomicString(use.height()->currentValue()->valueAsString())); + else + shadowElement->setAttribute(SVGNames::heightAttr, originalElement.getAttribute(SVGNames::heightAttr)); + } +} + void SVGUseElement::svgAttributeChanged(const QualifiedName& attrName) { if (!isSupportedAttribute(attrName)) { @@ -210,7 +195,7 @@ void SVGUseElement::svgAttributeChanged(const QualifiedName& attrName) return; } - SVGElementInstance::InvalidationGuard invalidationGuard(this); + SVGElement::InvalidationGuard invalidationGuard(this); RenderObject* renderer = this->renderer(); if (attrName == SVGNames::xAttr @@ -218,18 +203,19 @@ void SVGUseElement::svgAttributeChanged(const QualifiedName& attrName) || attrName == SVGNames::widthAttr || attrName == SVGNames::heightAttr) { updateRelativeLengthsInformation(); + if (m_targetElementInstance) { + ASSERT(m_targetElementInstance->correspondingElement()); + transferUseWidthAndHeightIfNeeded(*this, m_targetElementInstance.get(), *m_targetElementInstance->correspondingElement()); + } if (renderer) RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); return; } - if (SVGExternalResourcesRequired::handleAttributeChange(this, attrName)) - return; - if (SVGURIReference::isKnownAttribute(attrName)) { - bool isExternalReference = isExternalURIReference(hrefCurrentValue(), document()); + bool isExternalReference = isExternalURIReference(hrefString(), document()); if (isExternalReference) { - KURL url = document().completeURL(hrefCurrentValue()); + KURL url = document().completeURL(hrefString()); if (url.hasFragmentIdentifier()) { FetchRequest request(ResourceRequest(url.string()), localName()); setDocumentResource(document().fetcher()->fetchSVGDocument(request)); @@ -238,8 +224,7 @@ void SVGUseElement::svgAttributeChanged(const QualifiedName& attrName) setDocumentResource(0); } - if (!m_wasInsertedByParser) - buildPendingResource(); + invalidateShadowTree(); return; } @@ -247,81 +232,9 @@ void SVGUseElement::svgAttributeChanged(const QualifiedName& attrName) if (!renderer) return; - if (SVGExternalResourcesRequired::isKnownAttribute(attrName)) { - invalidateShadowTree(); - return; - } - ASSERT_NOT_REACHED(); } -void SVGUseElement::attach(const AttachContext& context) -{ - if (m_needsShadowTreeRecreation) - buildPendingResource(); - SVGGraphicsElement::attach(context); -} - -void SVGUseElement::willRecalcStyle(StyleRecalcChange) -{ - if (!m_wasInsertedByParser && m_needsShadowTreeRecreation && renderer() && needsStyleRecalc()) - buildPendingResource(); -} - -#ifdef DUMP_INSTANCE_TREE -static void dumpInstanceTree(unsigned int& depth, String& text, SVGElementInstance* targetInstance) -{ - SVGElement* element = targetInstance->correspondingElement(); - ASSERT(element); - - if (element->hasTagName(SVGNames::useTag)) { - if (toSVGUseElement(element)->resourceIsStillLoading()) - return; - } - - SVGElement* shadowTreeElement = targetInstance->shadowTreeElement(); - ASSERT(shadowTreeElement); - - SVGUseElement* directUseElement = targetInstance->directUseElement(); - String directUseElementName = directUseElement ? directUseElement->nodeName() : "null"; - - 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 (%p), firstChild=%s (%p), correspondingElement=%s (%p), directUseElement=%s (%p), shadowTreeElement=%s (%p), id=%s)\n", - targetInstance, parentNodeName.latin1().data(), element->parentNode(), firstChildNodeName.latin1().data(), element->firstChild(), - elementNodeName.latin1().data(), element, directUseElementName.latin1().data(), directUseElement, shadowTreeElementNodeName.latin1().data(), shadowTreeElement, elementId.latin1().data()); - - for (unsigned int i = 0; i < depth; ++i) - text += " "; - - const HashSet<SVGElementInstance*>& elementInstances = element->instancesForElement(); - text += "Corresponding element is associated with " + String::number(elementInstances.size()) + " instance(s):\n"; - - 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 += " "; - - text += String::format(" -> SVGElementInstance this=%p, (refCount: %i, shadowTreeElement in document? %i)\n", - *it, (*it)->refCount(), (*it)->shadowTreeElement()->inDocument()); - } - - ++depth; - - for (SVGElementInstance* instance = targetInstance->firstChild(); instance; instance = instance->nextSibling()) - dumpInstanceTree(depth, text, instance); - - --depth; -} -#endif - static bool isDisallowedElement(Node* node) { // Spec: "Any 'svg', 'symbol', 'g', graphics element or other 'use' is potentially a template object that can be re-used @@ -376,32 +289,39 @@ static bool subtreeContainsDisallowedElement(Node* start) return false; } +void SVGUseElement::scheduleShadowTreeRecreation() +{ + if (!referencedScope() || inUseShadowTree()) + return; + m_needsShadowTreeRecreation = true; + document().scheduleUseShadowTreeUpdate(*this); +} + void SVGUseElement::clearResourceReferences() { + if (m_targetElementInstance) + m_targetElementInstance = nullptr; + // FIXME: We should try to optimize this, to at least allow partial reclones. if (ShadowRoot* shadowTreeRootElement = userAgentShadowRoot()) shadowTreeRootElement->removeChildren(); - if (m_targetElementInstance) { - m_targetElementInstance->detach(); - m_targetElementInstance = 0; - } - m_needsShadowTreeRecreation = false; + document().unscheduleUseShadowTreeUpdate(*this); - document().accessSVGExtensions()->removeAllTargetReferencesForElement(this); + document().accessSVGExtensions().removeAllTargetReferencesForElement(this); } void SVGUseElement::buildPendingResource() { - if (!referencedDocument() || isInShadowTree()) + if (!referencedScope() || inUseShadowTree()) return; clearResourceReferences(); if (!inDocument()) return; - String id; - Element* target = SVGURIReference::targetElementFromIRIString(hrefCurrentValue(), document(), &id, externalDocument()); + AtomicString id; + Element* target = SVGURIReference::targetElementFromIRIString(hrefString(), treeScope(), &id, externalDocument()); if (!target || !target->inDocument()) { // If we can't find the target of an external element, just give up. // We can't observe if the target somewhen enters the external document, nor should we do it. @@ -410,7 +330,7 @@ void SVGUseElement::buildPendingResource() if (id.isEmpty()) return; - referencedDocument()->accessSVGExtensions()->addPendingResource(id, this); + referencedScope()->document().accessSVGExtensions().addPendingResource(id, this); ASSERT(hasPendingResources()); return; } @@ -423,13 +343,31 @@ void SVGUseElement::buildPendingResource() ASSERT(!m_needsShadowTreeRecreation); } +static PassRefPtrWillBeRawPtr<Node> cloneNodeAndAssociate(Node& toClone) +{ + RefPtrWillBeRawPtr<Node> clone = toClone.cloneNode(false); + if (!clone->isSVGElement()) + return clone.release(); + + SVGElement& svgElement = toSVGElement(toClone); + ASSERT(!svgElement.correspondingElement()); + toSVGElement(clone.get())->setCorrespondingElement(&svgElement); + if (EventTargetData* data = toClone.eventTargetData()) + data->eventListenerMap.copyEventListenersNotCreatedFromMarkupToTarget(clone.get()); + TrackExceptionState exceptionState; + for (Node* node = toClone.firstChild(); node && !exceptionState.hadException(); node = node->nextSibling()) + clone->appendChild(cloneNodeAndAssociate(*node), exceptionState); + return clone.release(); +} + void SVGUseElement::buildShadowAndInstanceTree(SVGElement* target) { ASSERT(!m_targetElementInstance); - // Do not build the shadow/instance tree for <use> elements living in a shadow tree. - // The will be expanded soon anyway - see expandUseElementsInShadowTree(). - if (isInShadowTree()) + // <use> creates a "user agent" shadow root. Do not build the shadow/instance tree for <use> + // elements living in a user agent shadow tree because they will get expanded in a second + // pass -- see expandUseElementsInShadowTree(). + if (inUseShadowTree()) return; // Do not allow self-referencing. @@ -437,89 +375,47 @@ void SVGUseElement::buildShadowAndInstanceTree(SVGElement* target) if (!target || target == this) return; - // Why a seperated instance/shadow tree? SVG demands it: - // The instance tree is accesable from JavaScript, and has to - // expose a 1:1 copy of the referenced tree, whereas internally we need - // to alter the tree for correct "use-on-symbol", "use-on-svg" support. - - // Build instance tree. Create root SVGElementInstance object for the first sub-tree node. - // - // Spec: If the 'use' element references a simple graphics element such as a 'rect', then there is only a - // single SVGElementInstance object, and the correspondingElement attribute on this SVGElementInstance object - // is the SVGRectElement that corresponds to the referenced 'rect' element. - m_targetElementInstance = SVGElementInstance::create(this, this, target); + // Set up root SVG element in shadow tree. + RefPtrWillBeRawPtr<Element> newChild = target->cloneElementWithoutChildren(); + m_targetElementInstance = toSVGElement(newChild.get()); + ShadowRoot* shadowTreeRootElement = userAgentShadowRoot(); + shadowTreeRootElement->appendChild(newChild.release()); - // Eventually enter recursion to build SVGElementInstance objects for the sub-tree children - bool foundProblem = false; - buildInstanceTree(target, m_targetElementInstance.get(), foundProblem, false); - - if (instanceTreeIsLoading(m_targetElementInstance.get())) - return; + // Clone the target subtree into the shadow tree, not handling <use> and <symbol> yet. // SVG specification does not say a word about <use> & cycles. My view on this is: just ignore it! // Non-appearing <use> content is easier to debug, then half-appearing content. - if (foundProblem) { + if (!buildShadowTree(target, m_targetElementInstance.get(), false)) { clearResourceReferences(); return; } - // Assure instance tree building was successfull + if (instanceTreeIsLoading(m_targetElementInstance.get())) + return; + + // Assure shadow tree building was successfull ASSERT(m_targetElementInstance); - ASSERT(!m_targetElementInstance->shadowTreeElement()); ASSERT(m_targetElementInstance->correspondingUseElement() == this); - ASSERT(m_targetElementInstance->directUseElement() == this); ASSERT(m_targetElementInstance->correspondingElement() == target); - ShadowRoot* shadowTreeRootElement = userAgentShadowRoot(); - ASSERT(shadowTreeRootElement); - - // Build shadow tree from instance tree - // This also handles the special cases: <use> on <symbol>, <use> on <svg>. - buildShadowTree(target, m_targetElementInstance.get()); - // Expand all <use> elements in the shadow tree. // Expand means: replace the actual <use> element by what it references. - expandUseElementsInShadowTree(shadowTreeRootElement); - - // Expand all <symbol> elements in the shadow tree. - // Expand means: replace the actual <symbol> element by the <svg> element. - expandSymbolElementsInShadowTree(shadowTreeRootElement); - - // Now that the shadow tree is completly expanded, we can associate - // shadow tree elements <-> instances in the instance tree. - associateInstancesWithShadowTreeElements(shadowTreeRootElement->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()) { + if (!expandUseElementsInShadowTree(m_targetElementInstance.get())) { clearResourceReferences(); return; } - ASSERT(m_targetElementInstance->shadowTreeElement()->parentNode() == shadowTreeRootElement); + // Expand all <symbol> elements in the shadow tree. + // Expand means: replace the actual <symbol> element by the <svg> element. + expandSymbolElementsInShadowTree(toSVGElement(shadowTreeRootElement->firstChild())); + + m_targetElementInstance = toSVGElement(shadowTreeRootElement->firstChild()); + transferUseWidthAndHeightIfNeeded(*this, m_targetElementInstance.get(), *m_targetElementInstance->correspondingElement()); - // Transfer event listeners assigned to the referenced element to our shadow tree elements. - transferEventListenersToShadowTree(m_targetElementInstance.get()); + ASSERT(m_targetElementInstance->parentNode() == shadowTreeRootElement); // Update relative length information. updateRelativeLengthsInformation(); - - // Eventually dump instance tree -#ifdef DUMP_INSTANCE_TREE - String text; - unsigned int depth = 0; - - dumpInstanceTree(depth, text, m_targetElementInstance.get()); - fprintf(stderr, "\nDumping <use> instance tree:\n%s\n", text.latin1().data()); -#endif - - // Eventually dump shadow tree -#ifdef DUMP_SHADOW_TREE - RefPtr<XMLSerializer> serializer = XMLSerializer::create(); - String markup = serializer->serializeToString(shadowTreeRootElement, ASSERT_NO_EXCEPTION); - fprintf(stderr, "Dumping <use> shadow tree markup:\n%s\n", markup.latin1().data()); -#endif } RenderObject* SVGUseElement::createRenderer(RenderStyle*) @@ -527,34 +423,34 @@ RenderObject* SVGUseElement::createRenderer(RenderStyle*) return new RenderSVGTransformableContainer(this); } -static bool isDirectReference(const Node* node) +static bool isDirectReference(const Node& node) { - return node->hasTagName(SVGNames::pathTag) - || node->hasTagName(SVGNames::rectTag) - || node->hasTagName(SVGNames::circleTag) - || node->hasTagName(SVGNames::ellipseTag) - || node->hasTagName(SVGNames::polygonTag) - || node->hasTagName(SVGNames::polylineTag) - || node->hasTagName(SVGNames::textTag); + return isSVGPathElement(node) + || isSVGRectElement(node) + || isSVGCircleElement(node) + || isSVGEllipseElement(node) + || isSVGPolygonElement(node) + || isSVGPolylineElement(node) + || isSVGTextElement(node); } void SVGUseElement::toClipPath(Path& path) { ASSERT(path.isEmpty()); - Node* n = m_targetElementInstance ? m_targetElementInstance->shadowTreeElement() : 0; + Node* n = userAgentShadowRoot()->firstChild(); if (!n) return; if (n->isSVGElement() && toSVGElement(n)->isSVGGraphicsElement()) { - if (!isDirectReference(n)) { + if (!isDirectReference(*n)) { // Spec: Indirect references are an error (14.3.5) - document().accessSVGExtensions()->reportError("Not allowed to use indirect reference in <clip-path>"); + document().accessSVGExtensions().reportError("Not allowed to use indirect reference in <clip-path>"); } else { toSVGGraphicsElement(n)->toClipPath(path); // FIXME: Avoid manual resolution of x/y here. Its potentially harmful. SVGLengthContext lengthContext(this); - path.translate(FloatSize(xCurrentValue().value(lengthContext), yCurrentValue().value(lengthContext))); + path.translate(FloatSize(m_x->currentValue()->value(lengthContext), m_y->currentValue()->value(lengthContext))); path.transform(animatedLocalTransform()); } } @@ -562,81 +458,56 @@ void SVGUseElement::toClipPath(Path& path) RenderObject* SVGUseElement::rendererClipChild() const { - Node* n = m_targetElementInstance ? m_targetElementInstance->shadowTreeElement() : 0; - if (!n) - return 0; - - if (n->isSVGElement() && isDirectReference(n)) - return toSVGElement(n)->renderer(); + if (Node* n = userAgentShadowRoot()->firstChild()) { + if (n->isSVGElement() && isDirectReference(*n)) + return toSVGElement(n)->renderer(); + } return 0; } -void SVGUseElement::buildInstanceTree(SVGElement* target, SVGElementInstance* targetInstance, bool& foundProblem, bool foundUse) +bool SVGUseElement::buildShadowTree(SVGElement* target, SVGElement* targetInstance, bool foundUse) { ASSERT(target); ASSERT(targetInstance); // Spec: If the referenced object is itself a 'use', or if there are 'use' subelements within the referenced // object, the instance tree will contain recursive expansion of the indirect references to form a complete tree. - bool targetHasUseTag = target->hasTagName(SVGNames::useTag); - SVGElement* newTarget = 0; - if (targetHasUseTag) { - foundProblem = hasCycleUseReferencing(toSVGUseElement(target), targetInstance, newTarget); - if (foundProblem) - return; - + if (isSVGUseElement(*target)) { // We only need to track first degree <use> dependencies. Indirect references are handled // as the invalidation bubbles up the dependency chain. if (!foundUse) { - document().accessSVGExtensions()->addElementReferencingTarget(this, target); + document().accessSVGExtensions().addElementReferencingTarget(this, target); foundUse = true; } } else if (isDisallowedElement(target)) { - foundProblem = true; - return; + return false; } - // A general description from the SVG spec, describing what buildInstanceTree() actually does. - // - // Spec: If the 'use' element references a 'g' which contains two 'rect' elements, then the instance tree - // contains three SVGElementInstance objects, a root SVGElementInstance object whose correspondingElement - // is the SVGGElement object for the 'g', and then two child SVGElementInstance objects, each of which has - // its correspondingElement that is an SVGRectElement object. - - for (Node* node = target->firstChild(); node; node = node->nextSibling()) { - SVGElement* element = 0; - if (node->isSVGElement()) - element = toSVGElement(node); - - // Skip any non-svg nodes or any disallowed element. - if (!element || isDisallowedElement(element)) - continue; + targetInstance->setCorrespondingElement(target); + if (EventTargetData* data = target->eventTargetData()) + data->eventListenerMap.copyEventListenersNotCreatedFromMarkupToTarget(targetInstance); - // Create SVGElementInstance object, for both container/non-container nodes. - RefPtr<SVGElementInstance> instance = SVGElementInstance::create(this, 0, element); - SVGElementInstance* instancePtr = instance.get(); - targetInstance->appendChild(instance.release()); + for (Node* child = target->firstChild(); child; child = child->nextSibling()) { + // Skip any disallowed element. + if (isDisallowedElement(child)) + continue; - // Enter recursion, appending new instance tree nodes to the "instance" object. - buildInstanceTree(element, instancePtr, foundProblem, foundUse); - if (foundProblem) - return; + RefPtrWillBeRawPtr<Node> newChild = child->cloneNode(false); + targetInstance->appendChild(newChild.get()); + if (newChild->isSVGElement()) { + // Enter recursion, appending new instance tree nodes to the "instance" object. + if (!buildShadowTree(toSVGElement(child), toSVGElement(newChild), foundUse)) + return false; + } } - - if (!targetHasUseTag || !newTarget) - return; - - RefPtr<SVGElementInstance> newInstance = SVGElementInstance::create(this, toSVGUseElement(target), newTarget); - SVGElementInstance* newInstancePtr = newInstance.get(); - targetInstance->appendChild(newInstance.release()); - buildInstanceTree(newTarget, newInstancePtr, foundProblem, foundUse); + return true; } -bool SVGUseElement::hasCycleUseReferencing(SVGUseElement* use, SVGElementInstance* targetInstance, SVGElement*& newTarget) +bool SVGUseElement::hasCycleUseReferencing(SVGUseElement* use, ContainerNode* targetInstance, SVGElement*& newTarget) { - ASSERT(referencedDocument()); - Element* targetElement = SVGURIReference::targetElementFromIRIString(use->hrefCurrentValue(), *referencedDocument()); + ASSERT(referencedScope()); + Element* targetElement = SVGURIReference::targetElementFromIRIString(use->hrefString(), *referencedScope()); newTarget = 0; if (targetElement && targetElement->isSVGElement()) newTarget = toSVGElement(targetElement); @@ -649,10 +520,9 @@ bool SVGUseElement::hasCycleUseReferencing(SVGUseElement* use, SVGElementInstanc return true; AtomicString targetId = newTarget->getIdAttribute(); - SVGElementInstance* instance = targetInstance->parentNode(); - while (instance) { - SVGElement* element = instance->correspondingElement(); - + ContainerNode* instance = targetInstance->parentNode(); + while (instance && instance->isSVGElement()) { + SVGElement* element = toSVGElement(instance); if (element->hasID() && element->getIdAttribute() == targetId && element->document() == newTarget->document()) return true; @@ -677,56 +547,46 @@ static inline void removeDisallowedElementsFromSubtree(Element& subtree) } } -void SVGUseElement::buildShadowTree(SVGElement* target, SVGElementInstance* targetInstance) -{ - // For instance <use> on <foreignObject> (direct case). - if (isDisallowedElement(target)) - return; - - RefPtr<Element> newChild = targetInstance->correspondingElement()->cloneElementWithChildren(); - - // We don't walk the target tree element-by-element, and clone each element, - // but instead use cloneElementWithChildren(). This is an optimization for the common - // case where <use> doesn't contain disallowed elements (ie. <foreignObject>). - // Though if there are disallowed elements in the subtree, we have to remove them. - // For instance: <use> on <g> containing <foreignObject> (indirect case). - if (subtreeContainsDisallowedElement(newChild.get())) - removeDisallowedElementsFromSubtree(*newChild); - - userAgentShadowRoot()->appendChild(newChild.release()); -} - -void SVGUseElement::expandUseElementsInShadowTree(Node* element) +bool SVGUseElement::expandUseElementsInShadowTree(SVGElement* element) { + ASSERT(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? // - // Short answer: Because we may miss to expand some elements. Ie. if a <symbol> - // contains <use> tags, we'd miss them. So once we're done with settin' up the + // Short answer: Because we may miss to expand some elements. For example, if a <symbol> + // contains <use> tags, we'd miss them. So once we're done with setting up the // actual shadow tree (after the special case modification for svg/symbol) we have // to walk it completely and expand all <use> elements. - if (element->hasTagName(SVGNames::useTag)) { + if (isSVGUseElement(*element)) { SVGUseElement* use = toSVGUseElement(element); ASSERT(!use->resourceIsStillLoading()); - ASSERT(referencedDocument()); - Element* targetElement = SVGURIReference::targetElementFromIRIString(use->hrefCurrentValue(), *referencedDocument()); SVGElement* target = 0; - if (targetElement && targetElement->isSVGElement()) - target = toSVGElement(targetElement); + if (hasCycleUseReferencing(toSVGUseElement(use->correspondingElement()), use, target)) + return false; + if (target && isDisallowedElement(target)) + return false; // Don't ASSERT(target) here, it may be "pending", too. // Setup sub-shadow tree root node - RefPtr<SVGGElement> cloneParent = SVGGElement::create(*referencedDocument()); - use->cloneChildNodes(cloneParent.get()); + RefPtrWillBeRawPtr<SVGGElement> cloneParent = SVGGElement::create(referencedScope()->document()); + cloneParent->setCorrespondingElement(use->correspondingElement()); + + // Move already cloned elements to the new <g> element + for (Node* child = use->firstChild(); child; ) { + Node* nextChild = child->nextSibling(); + cloneParent->appendChild(child); + child = nextChild; + } // 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()); - if (target && !isDisallowedElement(target)) { - RefPtr<Element> newChild = target->cloneElementWithChildren(); + if (target) { + RefPtrWillBeRawPtr<Node> newChild = cloneNodeAndAssociate(*target); ASSERT(newChild->isSVGElement()); + transferUseWidthAndHeightIfNeeded(*use, toSVGElement(newChild.get()), *target); cloneParent->appendChild(newChild.release()); } @@ -738,7 +598,7 @@ void SVGUseElement::expandUseElementsInShadowTree(Node* element) if (subtreeContainsDisallowedElement(cloneParent.get())) removeDisallowedElementsFromSubtree(*cloneParent); - RefPtr<Node> replacingElement(cloneParent.get()); + RefPtrWillBeRawPtr<SVGElement> replacingElement(cloneParent.get()); // Replace <use> with referenced content. ASSERT(use->parentNode()); @@ -747,33 +607,40 @@ void SVGUseElement::expandUseElementsInShadowTree(Node* element) // Expand the siblings because the *element* is replaced and we will // lose the sibling chain when we are back from recursion. element = replacingElement.get(); - for (RefPtr<Node> sibling = element->nextSibling(); sibling; sibling = sibling->nextSibling()) - expandUseElementsInShadowTree(sibling.get()); + for (RefPtrWillBeRawPtr<SVGElement> sibling = Traversal<SVGElement>::nextSibling(*element); sibling; sibling = Traversal<SVGElement>::nextSibling(*sibling)) { + if (!expandUseElementsInShadowTree(sibling.get())) + return false; + } } - for (RefPtr<Node> child = element->firstChild(); child; child = child->nextSibling()) - expandUseElementsInShadowTree(child.get()); + for (RefPtrWillBeRawPtr<SVGElement> child = Traversal<SVGElement>::firstChild(*element); child; child = Traversal<SVGElement>::nextSibling(*child)) { + if (!expandUseElementsInShadowTree(child.get())) + return false; + } + return true; } -void SVGUseElement::expandSymbolElementsInShadowTree(Node* element) +void SVGUseElement::expandSymbolElementsInShadowTree(SVGElement* element) { - if (element->hasTagName(SVGNames::symbolTag)) { + ASSERT(element); + if (isSVGSymbolElement(*element)) { // Spec: The referenced 'symbol' and its contents are deep-cloned into the generated tree, // with the exception that the 'symbol' is replaced by an 'svg'. 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. - ASSERT(referencedDocument()); - RefPtr<SVGSVGElement> svgElement = SVGSVGElement::create(*referencedDocument()); - + ASSERT(referencedScope()); + RefPtrWillBeRawPtr<SVGSVGElement> svgElement = SVGSVGElement::create(referencedScope()->document()); // Transfer all data (attributes, etc.) from <symbol> to the new <svg> element. - svgElement->cloneDataFromElement(*toElement(element)); - - // Only clone symbol children, and add them to the new <svg> element - for (Node* child = element->firstChild(); child; child = child->nextSibling()) { - RefPtr<Node> newChild = child->cloneNode(true); - svgElement->appendChild(newChild.release()); + svgElement->cloneDataFromElement(*element); + svgElement->setCorrespondingElement(element->correspondingElement()); + + // Move already cloned elements to the new <svg> element + for (Node* child = element->firstChild(); child; ) { + Node* nextChild = child->nextSibling(); + svgElement->appendChild(child); + child = nextChild; } // We don't walk the target tree element-by-element, and clone each element, @@ -784,123 +651,35 @@ void SVGUseElement::expandSymbolElementsInShadowTree(Node* element) if (subtreeContainsDisallowedElement(svgElement.get())) removeDisallowedElementsFromSubtree(*svgElement); - RefPtr<Node> replacingElement(svgElement.get()); + RefPtrWillBeRawPtr<SVGElement> replacingElement(svgElement.get()); // Replace <symbol> with <svg>. + ASSERT(element->parentNode()); element->parentNode()->replaceChild(svgElement.release(), element); // Expand the siblings because the *element* is replaced and we will // lose the sibling chain when we are back from recursion. element = replacingElement.get(); - for (RefPtr<Node> sibling = element->nextSibling(); sibling; sibling = sibling->nextSibling()) - expandSymbolElementsInShadowTree(sibling.get()); } - for (RefPtr<Node> child = element->firstChild(); child; child = child->nextSibling()) + for (RefPtrWillBeRawPtr<SVGElement> child = Traversal<SVGElement>::firstChild(*element); child; child = Traversal<SVGElement>::nextSibling(*child)) expandSymbolElementsInShadowTree(child.get()); } -void SVGUseElement::transferEventListenersToShadowTree(SVGElementInstance* target) -{ - if (!target) - return; - - SVGElement* originalElement = target->correspondingElement(); - ASSERT(originalElement); - - if (SVGElement* shadowTreeElement = target->shadowTreeElement()) { - if (EventTargetData* data = originalElement->eventTargetData()) - data->eventListenerMap.copyEventListenersNotCreatedFromMarkupToTarget(shadowTreeElement); - } - - for (SVGElementInstance* instance = target->firstChild(); instance; instance = instance->nextSibling()) - transferEventListenersToShadowTree(instance); -} - -void SVGUseElement::associateInstancesWithShadowTreeElements(Node* target, SVGElementInstance* targetInstance) -{ - if (!target || !targetInstance) - return; - - SVGElement* originalElement = targetInstance->correspondingElement(); - - if (originalElement->hasTagName(SVGNames::useTag)) { - // <use> gets replaced by <g> - ASSERT(target->nodeName() == SVGNames::gTag); - } else if (originalElement->hasTagName(SVGNames::symbolTag)) { - // <symbol> gets replaced by <svg> - ASSERT(target->nodeName() == SVGNames::svgTag); - } else - ASSERT(target->nodeName() == originalElement->nodeName()); - - SVGElement* element = 0; - if (target->isSVGElement()) - element = toSVGElement(target); - - ASSERT(!targetInstance->shadowTreeElement()); - targetInstance->setShadowTreeElement(element); - element->setCorrespondingElement(originalElement); - - Node* node = target->firstChild(); - for (SVGElementInstance* instance = targetInstance->firstChild(); node && instance; instance = instance->nextSibling()) { - // Skip any non-svg elements in shadow tree - while (node && !node->isSVGElement()) - node = node->nextSibling(); - - if (!node) - break; - - associateInstancesWithShadowTreeElements(node, instance); - node = node->nextSibling(); - } -} - -SVGElementInstance* SVGUseElement::instanceForShadowTreeElement(Node* element) const -{ - if (!m_targetElementInstance) { - ASSERT(!inDocument()); - return 0; - } - - return instanceForShadowTreeElement(element, m_targetElementInstance.get()); -} - -SVGElementInstance* SVGUseElement::instanceForShadowTreeElement(Node* element, SVGElementInstance* instance) const -{ - ASSERT(element); - ASSERT(instance); - - // We're dispatching a mutation event during shadow tree construction - // this instance hasn't yet been associated to a shadowTree element. - if (!instance->shadowTreeElement()) - return 0; - - if (element == instance->shadowTreeElement()) - return instance; - - for (SVGElementInstance* current = instance->firstChild(); current; current = current->nextSibling()) { - if (SVGElementInstance* search = instanceForShadowTreeElement(element, current)) - return search; - } - - return 0; -} - void SVGUseElement::invalidateShadowTree() { if (!inActiveDocument() || m_needsShadowTreeRecreation) return; - m_needsShadowTreeRecreation = true; - setNeedsStyleRecalc(); + scheduleShadowTreeRecreation(); invalidateDependentShadowTrees(); } void SVGUseElement::invalidateDependentShadowTrees() { // Recursively invalidate dependent <use> shadow trees - const HashSet<SVGElementInstance*>& instances = instancesForElement(); - const HashSet<SVGElementInstance*>::const_iterator end = instances.end(); - for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) { + const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >& instances = instancesForElement(); + const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator end = instances.end(); + for (WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator it = instances.begin(); it != end; ++it) { if (SVGUseElement* element = (*it)->correspondingUseElement()) { ASSERT(element->inDocument()); element->invalidateShadowTree(); @@ -924,20 +703,16 @@ void SVGUseElement::transferUseAttributesToReplacedElement(SVGElement* from, SVG bool SVGUseElement::selfHasRelativeLengths() const { - if (xCurrentValue().isRelative() - || yCurrentValue().isRelative() - || widthCurrentValue().isRelative() - || heightCurrentValue().isRelative()) + if (m_x->currentValue()->isRelative() + || m_y->currentValue()->isRelative() + || m_width->currentValue()->isRelative() + || m_height->currentValue()->isRelative()) return true; if (!m_targetElementInstance) return false; - SVGElement* element = m_targetElementInstance->correspondingElement(); - if (!element) - return false; - - return element->hasRelativeLengths(); + return m_targetElementInstance->hasRelativeLengths(); } void SVGUseElement::notifyFinished(Resource* resource) @@ -948,8 +723,15 @@ void SVGUseElement::notifyFinished(Resource* resource) invalidateShadowTree(); if (resource->errorOccurred()) dispatchEvent(Event::create(EventTypeNames::error)); - else if (!resource->wasCanceled()) - SVGExternalResourcesRequired::dispatchLoadEvent(this); + else if (!resource->wasCanceled()) { + if (m_haveFiredLoadEvent) + return; + if (!isStructurallyExternal()) + return; + ASSERT(!m_haveFiredLoadEvent); + m_haveFiredLoadEvent = true; + sendSVGLoadEventIfPossibleAsynchronously(); + } } bool SVGUseElement::resourceIsStillLoading() @@ -959,29 +741,19 @@ bool SVGUseElement::resourceIsStillLoading() return false; } -bool SVGUseElement::instanceTreeIsLoading(SVGElementInstance* targetElementInstance) +bool SVGUseElement::instanceTreeIsLoading(SVGElement* targetInstance) { - for (SVGElementInstance* instance = targetElementInstance->firstChild(); instance; instance = instance->nextSibling()) { - if (SVGUseElement* use = instance->correspondingUseElement()) { + for (SVGElement* element = Traversal<SVGElement>::firstChild(*targetInstance); element; element = Traversal<SVGElement>::nextSibling(*element)) { + if (SVGUseElement* use = element->correspondingUseElement()) { if (use->resourceIsStillLoading()) return true; } - if (instance->hasChildNodes()) - instanceTreeIsLoading(instance); + if (element->hasChildren() && instanceTreeIsLoading(element)) + return true; } return false; } -void SVGUseElement::finishParsingChildren() -{ - SVGGraphicsElement::finishParsingChildren(); - SVGExternalResourcesRequired::finishParsingChildren(); - if (m_wasInsertedByParser) { - buildPendingResource(); - m_wasInsertedByParser = false; - } -} - void SVGUseElement::setDocumentResource(ResourcePtr<DocumentResource> resource) { if (m_resource == resource) @@ -995,4 +767,10 @@ void SVGUseElement::setDocumentResource(ResourcePtr<DocumentResource> resource) m_resource->addClient(this); } +void SVGUseElement::trace(Visitor* visitor) +{ + visitor->trace(m_targetElementInstance); + SVGGraphicsElement::trace(visitor); +} + } |