2011-05-01 Nikolas Zimmermann <nzimmermann@rim.com>
authorzimmermann@webkit.org <zimmermann@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 1 May 2011 14:38:44 +0000 (14:38 +0000)
committerzimmermann@webkit.org <zimmermann@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 1 May 2011 14:38:44 +0000 (14:38 +0000)
        Reviewed by Dirk Schulze.

        LEAK: SVGElement leaks when detaching it in a pending resource state
        https://bugs.webkit.org/show_bug.cgi?id=59072

        Add testcase that used to leak, the leaks bot will assure they won't in future.

        * svg/custom/pending-resource-leak-2-expected.txt: Added.
        * svg/custom/pending-resource-leak-2.svg: Added.
        * svg/custom/pending-resource-leak-3-expected.txt: Added.
        * svg/custom/pending-resource-leak-3.svg: Added.
        * svg/custom/pending-resource-leak-expected.txt: Added.
        * svg/custom/pending-resource-leak.svg: Added.

2011-05-01  Nikolas Zimmermann  <nzimmermann@rim.com>

        Reviewed by Dirk Schulze.

        LEAK: SVGElement leaks when detaching it in a pending resource state
        https://bugs.webkit.org/show_bug.cgi?id=59072

        Make the pending resources set non-refcounted again. We made it refcounted a while ago
        to fix a security bug, as we had dangling pointers in the set in SVGDocumentExtensions.
        Fix the underlying problem, by removing all pending resources referencing to a particular
        SVGElement, upon its destruction or upon removing it from the document.

        Example: <rect fill="url(#foo)" id="rect">
        When we try to render the rect, the foo paint server can't be found and thus "foo" will be
        added to the pending resource set, with "rect" as client. When "foo" appears, it would remove
        itself from the pending resource set, and a ref count to the "rect" would be released.
        If "foo" never appears, SVGDocumentExtensions still holds a ref to the <rect>, thus keeping
        it and the associated document alive.

        Tests: svg/custom/pending-resource-leak-2.svg
               svg/custom/pending-resource-leak-3.svg
               svg/custom/pending-resource-leak.svg

        These tests cover several scenarios where we used to leak. Should fix several SVG*Element leaks on the bots.
        I manually tested reloading above testcases dozens of times, before the leak count was incremented by 2 nodes on every reload, that's gone now.

        * rendering/svg/RenderSVGResourceContainer.cpp:
        (WebCore::RenderSVGResourceContainer::registerResource):
        * rendering/svg/RenderSVGShadowTreeRootContainer.cpp:
        (WebCore::RenderSVGShadowTreeRootContainer::updateFromElement):
        * rendering/svg/SVGResources.cpp:
        (WebCore::registerPendingResource):
        * svg/SVGDocumentExtensions.cpp:
        (WebCore::SVGDocumentExtensions::addPendingResource):
        (WebCore::SVGDocumentExtensions::hasPendingResources):
        (WebCore::SVGDocumentExtensions::removeElementFromPendingResources):
        (WebCore::SVGDocumentExtensions::removePendingResource):
        * svg/SVGDocumentExtensions.h:
        * svg/SVGElement.cpp:
        * svg/SVGElement.h:
        * svg/SVGElementRareData.h:
        (WebCore::SVGElementRareData::SVGElementRareData):
        (WebCore::SVGElementRareData::hasPendingResources):
        (WebCore::SVGElementRareData::setHasPendingResources):
        * svg/SVGStyledElement.cpp:
        (WebCore::SVGStyledElement::~SVGStyledElement):
        (WebCore::SVGStyledElement::insertedIntoDocument):
        (WebCore::SVGStyledElement::removedFromDocument):
        (WebCore::SVGStyledElement::hasPendingResources):
        (WebCore::SVGStyledElement::setHasPendingResources):
        * svg/SVGStyledElement.h:
        (WebCore::SVGStyledElement::needsPendingResourceHandling):
        (WebCore::SVGStyledElement::buildPendingResource):
        * svg/SVGUseElement.cpp:
        (WebCore::SVGUseElement::SVGUseElement):
        (WebCore::SVGUseElement::insertedIntoDocument):
        (WebCore::SVGUseElement::svgAttributeChanged):
        (WebCore::SVGUseElement::buildPendingResource):
        * svg/SVGUseElement.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@85413 268f45cc-cd09-0410-ab3c-d52691b4dbfc

20 files changed:
LayoutTests/ChangeLog
LayoutTests/svg/custom/pending-resource-leak-2-expected.txt [new file with mode: 0644]
LayoutTests/svg/custom/pending-resource-leak-2.svg [new file with mode: 0644]
LayoutTests/svg/custom/pending-resource-leak-3-expected.txt [new file with mode: 0644]
LayoutTests/svg/custom/pending-resource-leak-3.svg [new file with mode: 0644]
LayoutTests/svg/custom/pending-resource-leak-expected.txt [new file with mode: 0644]
LayoutTests/svg/custom/pending-resource-leak.svg [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/rendering/svg/RenderSVGResourceContainer.cpp
Source/WebCore/rendering/svg/RenderSVGShadowTreeRootContainer.cpp
Source/WebCore/rendering/svg/SVGResources.cpp
Source/WebCore/svg/SVGDocumentExtensions.cpp
Source/WebCore/svg/SVGDocumentExtensions.h
Source/WebCore/svg/SVGElement.cpp
Source/WebCore/svg/SVGElement.h
Source/WebCore/svg/SVGElementRareData.h
Source/WebCore/svg/SVGStyledElement.cpp
Source/WebCore/svg/SVGStyledElement.h
Source/WebCore/svg/SVGUseElement.cpp
Source/WebCore/svg/SVGUseElement.h

index 459927b37785e89ff38ab0a8b5322ffe57a6ec4f..f82ca12958ea680cf02fa767f2d5a8177322f33d 100644 (file)
@@ -1,3 +1,19 @@
+2011-05-01  Nikolas Zimmermann  <nzimmermann@rim.com>
+
+        Reviewed by Dirk Schulze.
+
+        LEAK: SVGElement leaks when detaching it in a pending resource state
+        https://bugs.webkit.org/show_bug.cgi?id=59072
+
+        Add testcase that used to leak, the leaks bot will assure they won't in future.
+
+        * svg/custom/pending-resource-leak-2-expected.txt: Added.
+        * svg/custom/pending-resource-leak-2.svg: Added.
+        * svg/custom/pending-resource-leak-3-expected.txt: Added.
+        * svg/custom/pending-resource-leak-3.svg: Added.
+        * svg/custom/pending-resource-leak-expected.txt: Added.
+        * svg/custom/pending-resource-leak.svg: Added.
+
 2011-05-01  Dan Bernstein  <mitz@apple.com>
 
         Reviewed by Anders Carlsson.
diff --git a/LayoutTests/svg/custom/pending-resource-leak-2-expected.txt b/LayoutTests/svg/custom/pending-resource-leak-2-expected.txt
new file mode 100644 (file)
index 0000000..a665ac8
--- /dev/null
@@ -0,0 +1,2 @@
+This should not leak
+
diff --git a/LayoutTests/svg/custom/pending-resource-leak-2.svg b/LayoutTests/svg/custom/pending-resource-leak-2.svg
new file mode 100644 (file)
index 0000000..cf779c2
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg">
+    <script>
+        if (window.layoutTestController) {
+            layoutTestController.dumpAsText();
+            layoutTestController.waitUntilDone();
+        }
+
+        setTimeout(function() {
+            document.getElementById("rect").setAttribute("fill",  "green");
+            if (window.layoutTestController)
+                layoutTestController.notifyDone();
+        }, 0);
+    </script>
+    <text x="0" y="70">This should not leak</text>
+    <rect width="50" height="50" fill="url(#foo)" id="rect"/>
+</svg>
+
diff --git a/LayoutTests/svg/custom/pending-resource-leak-3-expected.txt b/LayoutTests/svg/custom/pending-resource-leak-3-expected.txt
new file mode 100644 (file)
index 0000000..a665ac8
--- /dev/null
@@ -0,0 +1,2 @@
+This should not leak
+
diff --git a/LayoutTests/svg/custom/pending-resource-leak-3.svg b/LayoutTests/svg/custom/pending-resource-leak-3.svg
new file mode 100644 (file)
index 0000000..c911b2f
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg">
+    <script>
+        if (window.layoutTestController) {
+            layoutTestController.dumpAsText();
+            layoutTestController.waitUntilDone();
+        }
+
+        setTimeout(function() {
+            document.getElementById("rect").setAttribute("fill",  "url(#bar)");
+            if (window.layoutTestController)
+                layoutTestController.notifyDone();
+        }, 0);
+    </script>
+    <text x="0" y="70">This should not leak</text>
+    <rect width="50" height="50" fill="url(#foo)" id="rect"/>
+</svg>
+
diff --git a/LayoutTests/svg/custom/pending-resource-leak-expected.txt b/LayoutTests/svg/custom/pending-resource-leak-expected.txt
new file mode 100644 (file)
index 0000000..a665ac8
--- /dev/null
@@ -0,0 +1,2 @@
+This should not leak
+
diff --git a/LayoutTests/svg/custom/pending-resource-leak.svg b/LayoutTests/svg/custom/pending-resource-leak.svg
new file mode 100644 (file)
index 0000000..e784b9d
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg">
+    <script>
+        if (window.layoutTestController)
+            layoutTestController.dumpAsText();
+    </script>
+    <text x="0" y="70">This should not leak</text>
+    <rect width="50" height="50" fill="url(#foo)" id="rect"/>
+</svg>
+
index 2a866be11230b263958c1117ff87b674a65a3aa3..94022997062bcdfd292c2cc03b20398a68240fbe 100644 (file)
@@ -1,3 +1,63 @@
+2011-05-01  Nikolas Zimmermann  <nzimmermann@rim.com>
+
+        Reviewed by Dirk Schulze.
+
+        LEAK: SVGElement leaks when detaching it in a pending resource state
+        https://bugs.webkit.org/show_bug.cgi?id=59072
+
+        Make the pending resources set non-refcounted again. We made it refcounted a while ago
+        to fix a security bug, as we had dangling pointers in the set in SVGDocumentExtensions.
+        Fix the underlying problem, by removing all pending resources referencing to a particular
+        SVGElement, upon its destruction or upon removing it from the document.
+
+        Example: <rect fill="url(#foo)" id="rect">
+        When we try to render the rect, the foo paint server can't be found and thus "foo" will be
+        added to the pending resource set, with "rect" as client. When "foo" appears, it would remove
+        itself from the pending resource set, and a ref count to the "rect" would be released.
+        If "foo" never appears, SVGDocumentExtensions still holds a ref to the <rect>, thus keeping
+        it and the associated document alive.
+
+        Tests: svg/custom/pending-resource-leak-2.svg
+               svg/custom/pending-resource-leak-3.svg
+               svg/custom/pending-resource-leak.svg
+
+        These tests cover several scenarios where we used to leak. Should fix several SVG*Element leaks on the bots.
+        I manually tested reloading above testcases dozens of times, before the leak count was incremented by 2 nodes on every reload, that's gone now.
+
+        * rendering/svg/RenderSVGResourceContainer.cpp:
+        (WebCore::RenderSVGResourceContainer::registerResource):
+        * rendering/svg/RenderSVGShadowTreeRootContainer.cpp:
+        (WebCore::RenderSVGShadowTreeRootContainer::updateFromElement):
+        * rendering/svg/SVGResources.cpp:
+        (WebCore::registerPendingResource):
+        * svg/SVGDocumentExtensions.cpp:
+        (WebCore::SVGDocumentExtensions::addPendingResource):
+        (WebCore::SVGDocumentExtensions::hasPendingResources):
+        (WebCore::SVGDocumentExtensions::removeElementFromPendingResources):
+        (WebCore::SVGDocumentExtensions::removePendingResource):
+        * svg/SVGDocumentExtensions.h:
+        * svg/SVGElement.cpp:
+        * svg/SVGElement.h:
+        * svg/SVGElementRareData.h:
+        (WebCore::SVGElementRareData::SVGElementRareData):
+        (WebCore::SVGElementRareData::hasPendingResources):
+        (WebCore::SVGElementRareData::setHasPendingResources):
+        * svg/SVGStyledElement.cpp:
+        (WebCore::SVGStyledElement::~SVGStyledElement):
+        (WebCore::SVGStyledElement::insertedIntoDocument):
+        (WebCore::SVGStyledElement::removedFromDocument):
+        (WebCore::SVGStyledElement::hasPendingResources):
+        (WebCore::SVGStyledElement::setHasPendingResources):
+        * svg/SVGStyledElement.h:
+        (WebCore::SVGStyledElement::needsPendingResourceHandling):
+        (WebCore::SVGStyledElement::buildPendingResource):
+        * svg/SVGUseElement.cpp:
+        (WebCore::SVGUseElement::SVGUseElement):
+        (WebCore::SVGUseElement::insertedIntoDocument):
+        (WebCore::SVGUseElement::svgAttributeChanged):
+        (WebCore::SVGUseElement::buildPendingResource):
+        * svg/SVGUseElement.h:
+
 2011-05-01  Rafael Brandao  <rafael.lobo@openbossa.org>
 
         Reviewed by Csaba Osztrogon√°c.
index 0215e8eddddc7e15b70983585d60fdc943b938a5..79e8e1e2f7dcb98d90a869d27683b1eb5073b6f8 100644 (file)
@@ -154,7 +154,7 @@ void RenderSVGResourceContainer::removeClient(RenderObject* client)
 void RenderSVGResourceContainer::registerResource()
 {
     SVGDocumentExtensions* extensions = svgExtensionsFromNode(node());
-    if (!extensions->isPendingResource(m_id)) {
+    if (!extensions->hasPendingResources(m_id)) {
         extensions->addResource(m_id, this);
         return;
     }
@@ -167,6 +167,8 @@ void RenderSVGResourceContainer::registerResource()
     // Update cached resources of pending clients.
     const SVGDocumentExtensions::SVGPendingElements::const_iterator end = clients->end();
     for (SVGDocumentExtensions::SVGPendingElements::const_iterator it = clients->begin(); it != end; ++it) {
+        ASSERT((*it)->hasPendingResources());
+        (*it)->setHasPendingResources(false);
         RenderObject* renderer = (*it)->renderer();
         if (!renderer)
             continue;
index 2fa884a7220fa009ca2e6363714335d10735fed6..7cc89e983c68ed1c6cff978e45c9c2d61f9b0859 100644 (file)
@@ -73,7 +73,7 @@ void RenderSVGShadowTreeRootContainer::updateFromElement()
     // Only rebuild the shadow tree, if we a) never had a tree or b) we were specifically asked to do so
     // If the use element is a pending resource, and a) or b) is true, do nothing, and wait for the use
     // element to be asked to buildPendingResource(), this will call us again, with m_recreateTrue=true.
-    if ((shouldRecreateTree || !hadExistingTree) && !useElement->isPendingResource()) {
+    if ((shouldRecreateTree || !hadExistingTree) && !useElement->hasPendingResources()) {
         useElement->buildShadowAndInstanceTree(m_shadowRoot.get());
 
         // Attach shadow root element
index 2704443a2c014d54b4afb81a427bb23dd902b184..f21fcbf9f35b1919e529530301ac1465398673c3 100644 (file)
@@ -171,9 +171,7 @@ static inline RenderSVGResourceContainer* paintingResourceFromSVGPaint(Document*
 static inline void registerPendingResource(SVGDocumentExtensions* extensions, const AtomicString& id, SVGElement* element)
 {
     ASSERT(element);
-    if (!element->isStyled())
-        return;
-
+    ASSERT(element->isStyled());
     extensions->addPendingResource(id, static_cast<SVGStyledElement*>(element));
 }
 
index abe68ebe371424ccf2c5f69d2067dae5e26241ae..d74f64666bd4357e1089b7f6493f22f3e429c4ed 100644 (file)
@@ -211,24 +211,26 @@ void SVGDocumentExtensions::reportError(const String& message)
     reportMessage(m_document, ErrorMessageLevel, "Error: " + message);
 }
 
-void SVGDocumentExtensions::addPendingResource(const AtomicString& id, PassRefPtr<SVGStyledElement> obj)
+void SVGDocumentExtensions::addPendingResource(const AtomicString& id, SVGStyledElement* element)
 {
-    ASSERT(obj);
+    ASSERT(element);
 
     if (id.isEmpty())
         return;
 
     if (m_pendingResources.contains(id))
-        m_pendingResources.get(id)->add(obj);
+        m_pendingResources.get(id)->add(element);
     else {
         SVGPendingElements* set = new SVGPendingElements;
-        set->add(obj);
+        set->add(element);
 
         m_pendingResources.add(id, set);
     }
+
+    element->setHasPendingResources(true);
 }
 
-bool SVGDocumentExtensions::isPendingResource(const AtomicString& id) const
+bool SVGDocumentExtensions::hasPendingResources(const AtomicString& id) const
 {
     if (id.isEmpty())
         return false;
@@ -236,7 +238,36 @@ bool SVGDocumentExtensions::isPendingResource(const AtomicString& id) const
     return m_pendingResources.contains(id);
 }
 
-PassOwnPtr<HashSet<RefPtr<SVGStyledElement> > > SVGDocumentExtensions::removePendingResource(const AtomicString& id)
+void SVGDocumentExtensions::removeElementFromPendingResources(SVGStyledElement* element)
+{
+    ASSERT(element);
+
+    if (m_pendingResources.isEmpty() || !element->hasPendingResources())
+        return;
+
+    element->setHasPendingResources(false);
+
+    Vector<AtomicString> toBeRemoved;
+    HashMap<AtomicString, SVGPendingElements*>::iterator end = m_pendingResources.end();
+    for (HashMap<AtomicString, SVGPendingElements*>::iterator it = m_pendingResources.begin(); it != end; ++it) {
+        SVGPendingElements* elements = it->second;
+        ASSERT(elements);
+        ASSERT(!elements->isEmpty());
+
+        elements->remove(element);
+        if (elements->isEmpty())
+            toBeRemoved.append(it->first);
+    }
+
+    if (toBeRemoved.isEmpty())
+        return;
+
+    Vector<AtomicString>::iterator endVector = toBeRemoved.end();
+    for (Vector<AtomicString>::iterator it = toBeRemoved.begin(); it != endVector; ++it)
+        m_pendingResources.remove(*it);
+}
+
+PassOwnPtr<SVGDocumentExtensions::SVGPendingElements> SVGDocumentExtensions::removePendingResource(const AtomicString& id)
 {
     ASSERT(m_pendingResources.contains(id));
 
index 4e28b0da8607ba72bebabf59ea0e26453eff82ea..a66b066c3947d82402ede3c4b9860ef76b0f7b3f 100644 (file)
@@ -42,7 +42,7 @@ class SVGSVGElement;
 class SVGDocumentExtensions {
     WTF_MAKE_NONCOPYABLE(SVGDocumentExtensions); WTF_MAKE_FAST_ALLOCATED;
 public:
-    typedef HashSet<RefPtr<SVGStyledElement> > SVGPendingElements;
+    typedef HashSet<SVGStyledElement*> SVGPendingElements;
     SVGDocumentExtensions(Document*);
     ~SVGDocumentExtensions();
     
@@ -79,8 +79,9 @@ public:
     // This HashMap contains a list of pending resources. Pending resources, are such
     // which are referenced by any object in the SVG document, but do NOT exist yet.
     // For instance, dynamically build gradients / patterns / clippers...
-    void addPendingResource(const AtomicString& id, PassRefPtr<SVGStyledElement>);
-    bool isPendingResource(const AtomicString& id) const;
+    void addPendingResource(const AtomicString& id, SVGStyledElement*);
+    bool hasPendingResources(const AtomicString& id) const;
+    void removeElementFromPendingResources(SVGStyledElement*);
     PassOwnPtr<SVGPendingElements> removePendingResource(const AtomicString& id);
 };
 
index 044f439bbd4e67659a695e7d0e299065c5874cf7..1e56e257203b04d3eb14169448cfb311bc197cd1 100644 (file)
@@ -338,27 +338,6 @@ bool SVGElement::childShouldCreateRenderer(Node* child) const
     return false;
 }
 
-void SVGElement::insertedIntoDocument()
-{
-    StyledElement::insertedIntoDocument();
-
-    if (!needsPendingResourceHandling())
-        return;
-
-    SVGDocumentExtensions* extensions = document()->accessSVGExtensions();
-    String resourceId = getIdAttribute();
-    if (!extensions->isPendingResource(resourceId))
-        return;
-    
-    OwnPtr<SVGDocumentExtensions::SVGPendingElements> clients(extensions->removePendingResource(resourceId));
-    if (clients->isEmpty())
-        return;
-
-    const SVGDocumentExtensions::SVGPendingElements::const_iterator end = clients->end();
-    for (SVGDocumentExtensions::SVGPendingElements::const_iterator it = clients->begin(); it != end; ++it)
-        (*it)->buildPendingResource();
-}
-
 void SVGElement::attributeChanged(Attribute* attr, bool preserveDecls)
 {
     ASSERT(attr);
index 380fa0e8a9291b94a94b94cbbfcf3571e3bde079..5e5abf8de537390ba8e56ac3aa80140e160041e1 100644 (file)
@@ -115,7 +115,6 @@ protected:
     virtual void parseMappedAttribute(Attribute*);
 
     virtual void finishParsingChildren();
-    virtual void insertedIntoDocument();
     virtual void attributeChanged(Attribute*, bool preserveDecls = false);
     virtual bool childShouldCreateRenderer(Node*) const;
     
@@ -131,9 +130,6 @@ private:
 
     virtual bool isSupported(StringImpl* feature, StringImpl* version) const;
 
-    virtual bool needsPendingResourceHandling() const { return true; }
-    virtual void buildPendingResource() { }
-
     void mapInstanceToElement(SVGElementInstance*);
     void removeInstanceMapping(SVGElementInstance*);
 
index 3318dee6efc34f6ac7d16b27993b93be58b4787a..50bf23531c19c4aa17d4c16dd58cdd7982bbdcc3 100644 (file)
@@ -38,6 +38,7 @@ public:
         : m_cursorElement(0)
         , m_cursorImageValue(0)
         , m_instancesUpdatesBlocked(false)
+        , m_hasPendingResources(false)
     {
     }
 
@@ -60,6 +61,9 @@ public:
     bool instanceUpdatesBlocked() const { return m_instancesUpdatesBlocked; }
     void setInstanceUpdatesBlocked(bool value) { m_instancesUpdatesBlocked = value; }
 
+    bool hasPendingResources() const { return m_hasPendingResources; }
+    void setHasPendingResources(bool value) { m_hasPendingResources = value; }
+
     SVGCursorElement* cursorElement() const { return m_cursorElement; }
     void setCursorElement(SVGCursorElement* cursorElement) { m_cursorElement = cursorElement; }
 
@@ -71,6 +75,7 @@ private:
     SVGCursorElement* m_cursorElement;
     CSSCursorImageValue* m_cursorImageValue;
     bool m_instancesUpdatesBlocked : 1;
+    bool m_hasPendingResources : 1;
 };
 
 }
index 79e1633e01f7eca1eadbe98e00461dfed67c191b..c1a644905acada267fa9e61a7e20d41e417c7864 100644 (file)
@@ -66,6 +66,10 @@ SVGStyledElement::SVGStyledElement(const QualifiedName& tagName, Document* docum
 
 SVGStyledElement::~SVGStyledElement()
 {
+    if (needsPendingResourceHandling() && hasPendingResources() && document())
+        document()->accessSVGExtensions()->removeElementFromPendingResources(this);
+
+    ASSERT(!hasPendingResources());
 }
 
 String SVGStyledElement::title() const
@@ -359,12 +363,37 @@ void SVGStyledElement::insertedIntoDocument()
 {
     SVGElement::insertedIntoDocument();
     updateRelativeLengthsInformation();
+
+    Document* document = this->document();
+    if (!needsPendingResourceHandling() || !document)
+        return;
+
+    SVGDocumentExtensions* extensions = document->accessSVGExtensions();
+    String resourceId = getIdAttribute();
+    if (!extensions->hasPendingResources(resourceId))
+        return;
+
+    OwnPtr<SVGDocumentExtensions::SVGPendingElements> clients(extensions->removePendingResource(resourceId));
+    ASSERT(!clients->isEmpty());
+
+    const SVGDocumentExtensions::SVGPendingElements::const_iterator end = clients->end();
+    for (SVGDocumentExtensions::SVGPendingElements::const_iterator it = clients->begin(); it != end; ++it) {
+        ASSERT((*it)->hasPendingResources());
+        (*it)->buildPendingResource();
+        (*it)->setHasPendingResources(false);
+    }
 }
 
 void SVGStyledElement::removedFromDocument()
 {
     updateRelativeLengthsInformation(false, this);
     SVGElement::removedFromDocument();
+
+    Document* document = this->document();
+    if (!needsPendingResourceHandling() || !document)
+        return;
+
+    document->accessSVGExtensions()->removeElementFromPendingResources(this);
 }
 
 void SVGStyledElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
@@ -419,6 +448,16 @@ void SVGStyledElement::setInstanceUpdatesBlocked(bool value)
         rareSVGData()->setInstanceUpdatesBlocked(value);
 }
 
+bool SVGStyledElement::hasPendingResources() const
+{
+    return hasRareSVGData() && rareSVGData()->hasPendingResources();
+}
+
+void SVGStyledElement::setHasPendingResources(bool value)
+{
+    ensureRareSVGData()->setHasPendingResources(value);
+}
+
 AffineTransform SVGStyledElement::localCoordinateSpaceTransform(SVGLocatable::CTMScope) const
 {
     // To be overriden by SVGStyledLocatableElement/SVGStyledTransformableElement (or as special case SVGTextElement)
index b6c0b96a8ef33346d1d402a580f3b4c92cef7d95..f48513c251a84f262c4bbd82eabc8fbccb575760 100644 (file)
@@ -52,12 +52,16 @@ public:
     bool instanceUpdatesBlocked() const;
     void setInstanceUpdatesBlocked(bool);
 
+    bool hasPendingResources() const;
+    void setHasPendingResources(bool);
+
     AnimatedAttributeType animatedPropertyTypeForCSSProperty(const QualifiedName&);
     static bool isAnimatableCSSProperty(const QualifiedName&);
 
     virtual AffineTransform localCoordinateSpaceTransform(SVGLocatable::CTMScope) const;
 
     virtual CSSStyleDeclaration* style() { return StyledElement::style(); }
+    virtual bool needsPendingResourceHandling() const { return true; }
 
 protected: 
     SVGStyledElement(const QualifiedName&, Document*);
@@ -81,6 +85,7 @@ protected:
     void updateRelativeLengthsInformation(bool hasRelativeLengths, SVGStyledElement*);
 
     virtual bool selfHasRelativeLengths() const { return false; }
+    virtual void buildPendingResource() { }
 
 private:
     virtual bool isStyled() const { return true; }
index e50ae85b71a7c6f5bf628b24d90f39afa703554c..e23fa622cf355b40f1bb0e50b277f41e323c65bf 100644 (file)
@@ -72,7 +72,6 @@ inline SVGUseElement::SVGUseElement(const QualifiedName& tagName, Document* docu
     , m_width(LengthModeWidth)
     , m_height(LengthModeHeight)
     , m_updatesBlocked(false)
-    , m_isPendingResource(false)
     , m_needsShadowTreeRecreation(false)
 {
 }
@@ -139,7 +138,7 @@ void SVGUseElement::insertedIntoDocument()
     // This functions exists to assure assumptions made in the code regarding SVGElementInstance creation/destruction are satisfied.
     SVGStyledTransformableElement::insertedIntoDocument();
     ASSERT(!m_targetElementInstance || !isWellFormedDocument(document()));
-    ASSERT(!m_isPendingResource || !isWellFormedDocument(document()));
+    ASSERT(!hasPendingResources() || !isWellFormedDocument(document()));
 }
 
 void SVGUseElement::removedFromDocument()
@@ -166,10 +165,18 @@ void SVGUseElement::svgAttributeChanged(const QualifiedName& attrName)
         return;
 
     if (SVGURIReference::isKnownAttribute(attrName)) {
-        if (m_isPendingResource) {
-            document()->accessSVGExtensions()->removePendingResource(m_resourceId);
+        if (hasPendingResources()) {
+            OwnPtr<SVGDocumentExtensions::SVGPendingElements> clients(document()->accessSVGExtensions()->removePendingResource(m_resourceId));
+            ASSERT(!clients->isEmpty());
+
+            const SVGDocumentExtensions::SVGPendingElements::const_iterator end = clients->end();
+            for (SVGDocumentExtensions::SVGPendingElements::const_iterator it = clients->begin(); it != end; ++it) {
+                ASSERT((*it)->hasPendingResources());
+                (*it)->setHasPendingResources(false);
+            }
+
             m_resourceId = String();
-            m_isPendingResource = false;
+            setHasPendingResources(false);
         }
 
         invalidateShadowTree();
@@ -470,18 +477,19 @@ void SVGUseElement::buildPendingResource()
     ASSERT(!m_targetElementInstance);
 
     if (!targetElement) {
-        if (m_isPendingResource || id.isEmpty())
+        if (hasPendingResources() || id.isEmpty())
             return;
 
-        m_isPendingResource = true;
         m_resourceId = id;
+        ASSERT(!hasPendingResources());
         document()->accessSVGExtensions()->addPendingResource(id, this);
+        ASSERT(hasPendingResources());
         return;
     }
 
-    if (m_isPendingResource) {
+
+    if (hasPendingResources()) {
         ASSERT(!m_targetElementInstance);
-        m_isPendingResource = false;    
         m_resourceId = String();
         invalidateShadowTree();
     }
index 30569b5e3484828d273bea77b9b5cf05416b8a14..4adfb961b327361f789db74816a2af6c291f7d0c 100644 (file)
@@ -77,7 +77,6 @@ private:
     void setUpdatesBlocked(bool blocked) { m_updatesBlocked = blocked; }
 
     friend class RenderSVGShadowTreeRootContainer;
-    bool isPendingResource() const { return m_isPendingResource; }
     void buildShadowAndInstanceTree(SVGShadowTreeRootElement*);
     void detachInstance();
 
@@ -118,7 +117,6 @@ private:
     DECLARE_ANIMATED_BOOLEAN(ExternalResourcesRequired, externalResourcesRequired)
 
     bool m_updatesBlocked;
-    bool m_isPendingResource;
     bool m_needsShadowTreeRecreation;
     String m_resourceId;
     RefPtr<SVGElementInstance> m_targetElementInstance;