summaryrefslogtreecommitdiffstats
path: root/chromium/third_party/WebKit/Source/core/svg/SVGUseElement.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/WebKit/Source/core/svg/SVGUseElement.cpp')
-rw-r--r--chromium/third_party/WebKit/Source/core/svg/SVGUseElement.cpp708
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);
+}
+
}