Title: [124681] trunk
Revision
124681
Author
[email protected]
Date
2012-08-03 18:23:58 -0700 (Fri, 03 Aug 2012)

Log Message

[SVG] Tref target event listener cleanup
https://bugs.webkit.org/show_bug.cgi?id=93004

Reviewed by Abhishek Arya.

Source/WebCore:

Currently SVGTRefElement allocates event listeners dynamically as it attaches to its
targets. Synchronizing the lifetime of the target listener vs. the tref element is
error prone, as various events can stack and trigger nested handlers.

In order to reduce complexity and address a couple of outstanding issues, this patch
changes the way event listeners are allocated: only one target listener is created
for the lifetime of the SVGTRefElement, and gets reused if the target element changes.

Test: svg/custom/tref-nested-events-crash.svg

* dom/EventListener.h:
Added new <tref> target event listener type.
* svg/SVGTRefElement.cpp:
(WebCore):
(WebCore::SVGTRefTargetEventListener::create):
(WebCore::SVGTRefTargetEventListener::cast):
(SVGTRefTargetEventListener):
(WebCore::SVGTRefTargetEventListener::isAttached):
(WebCore::SVGTRefTargetEventListener::SVGTRefTargetEventListener):
(WebCore::SVGTRefTargetEventListener::attach):
(WebCore::SVGTRefTargetEventListener::detach):
(WebCore::SVGTRefTargetEventListener::operator==):
(WebCore::SVGTRefTargetEventListener::handleEvent):
No need to check m_trefElement anymore - the listener is allocated for the whole element
lifetime, detached when the element is removed and deallocated when the element is
destroyed.
(WebCore::SVGTRefElement::SVGTRefElement):
Allocate one target listener per element, at construction time.
(WebCore::SVGTRefElement::~SVGTRefElement):
Detach the listener if necessary.
(WebCore::SVGTRefElement::detachTarget):
Check whether the element is still in document after updating the text (may have been
removed by event handlers).
(WebCore::SVGTRefElement::buildPendingResource):
Attach the event listener before updating the text content to avoid racing with event
handlers (which can remove the element).
(WebCore::SVGTRefElement::removedFrom):
* svg/SVGTRefElement.h:
(WebCore):
(SVGTRefElement):

LayoutTests:

* svg/custom/tref-nested-events-crash-expected.txt: Added.
* svg/custom/tref-nested-events-crash.svg: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (124680 => 124681)


--- trunk/LayoutTests/ChangeLog	2012-08-04 01:13:22 UTC (rev 124680)
+++ trunk/LayoutTests/ChangeLog	2012-08-04 01:23:58 UTC (rev 124681)
@@ -1,3 +1,13 @@
+2012-08-03  Florin Malita  <[email protected]>
+
+        [SVG] Tref target event listener cleanup
+        https://bugs.webkit.org/show_bug.cgi?id=93004
+
+        Reviewed by Abhishek Arya.
+
+        * svg/custom/tref-nested-events-crash-expected.txt: Added.
+        * svg/custom/tref-nested-events-crash.svg: Added.
+
 2012-08-01  Ryosuke Niwa  <[email protected]>
 
         Use testRunner instead of layoutTestController in fast/scrolling through fast/xsl tests

Added: trunk/LayoutTests/svg/custom/tref-nested-events-crash-expected.txt (0 => 124681)


--- trunk/LayoutTests/svg/custom/tref-nested-events-crash-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/svg/custom/tref-nested-events-crash-expected.txt	2012-08-04 01:23:58 UTC (rev 124681)
@@ -0,0 +1 @@
+PASS: did not crash.

Added: trunk/LayoutTests/svg/custom/tref-nested-events-crash.svg (0 => 124681)


--- trunk/LayoutTests/svg/custom/tref-nested-events-crash.svg	                        (rev 0)
+++ trunk/LayoutTests/svg/custom/tref-nested-events-crash.svg	2012-08-04 01:23:58 UTC (rev 124681)
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg _onload_="CFcrash()" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+  <!-- Test for https://bugs.webkit.org/show_bug.cgi?id=93004 -->
+  <g id="test-body-content">
+    <defs>
+        <text id="hello">
+            <tspan id="tspan">##</tspan>
+        </text>
+    </defs>
+
+    <text>
+        <tref id="tref" xlink:href=""
+    </text>
+  </g>
+
+  <text>PASS: did not crash.</text>
+  <script>
+    if (window.testRunner) {
+        testRunner.waitUntilDone();
+        testRunner.dumpAsText();
+    }
+
+    var test_body_content = document.getElementById("test-body-content");
+    var tspan = document.getElementById("tspan");
+
+    function CFcrash() {
+        test_body_content.addEventListener("DOMCharacterDataModified", function () { 
+            try { test_body_content.parentNode.removeChild(test_body_content); } catch (e) {}
+
+            if (window.testRunner)
+                setTimeout('testRunner.notifyDone()', 0);
+        }, false); 
+        document.adoptNode(tspan); 
+    }
+  </script>
+</svg>

Modified: trunk/Source/WebCore/ChangeLog (124680 => 124681)


--- trunk/Source/WebCore/ChangeLog	2012-08-04 01:13:22 UTC (rev 124680)
+++ trunk/Source/WebCore/ChangeLog	2012-08-04 01:23:58 UTC (rev 124681)
@@ -1,3 +1,51 @@
+2012-08-03  Florin Malita  <[email protected]>
+
+        [SVG] Tref target event listener cleanup
+        https://bugs.webkit.org/show_bug.cgi?id=93004
+
+        Reviewed by Abhishek Arya.
+
+        Currently SVGTRefElement allocates event listeners dynamically as it attaches to its
+        targets. Synchronizing the lifetime of the target listener vs. the tref element is
+        error prone, as various events can stack and trigger nested handlers.
+
+        In order to reduce complexity and address a couple of outstanding issues, this patch
+        changes the way event listeners are allocated: only one target listener is created
+        for the lifetime of the SVGTRefElement, and gets reused if the target element changes.
+
+        Test: svg/custom/tref-nested-events-crash.svg
+
+        * dom/EventListener.h:
+        Added new <tref> target event listener type.
+        * svg/SVGTRefElement.cpp:
+        (WebCore):
+        (WebCore::SVGTRefTargetEventListener::create):
+        (WebCore::SVGTRefTargetEventListener::cast):
+        (SVGTRefTargetEventListener):
+        (WebCore::SVGTRefTargetEventListener::isAttached):
+        (WebCore::SVGTRefTargetEventListener::SVGTRefTargetEventListener):
+        (WebCore::SVGTRefTargetEventListener::attach):
+        (WebCore::SVGTRefTargetEventListener::detach):
+        (WebCore::SVGTRefTargetEventListener::operator==):
+        (WebCore::SVGTRefTargetEventListener::handleEvent):
+        No need to check m_trefElement anymore - the listener is allocated for the whole element
+        lifetime, detached when the element is removed and deallocated when the element is
+        destroyed.
+        (WebCore::SVGTRefElement::SVGTRefElement):
+        Allocate one target listener per element, at construction time.
+        (WebCore::SVGTRefElement::~SVGTRefElement):
+        Detach the listener if necessary.
+        (WebCore::SVGTRefElement::detachTarget):
+        Check whether the element is still in document after updating the text (may have been
+        removed by event handlers).
+        (WebCore::SVGTRefElement::buildPendingResource):
+        Attach the event listener before updating the text content to avoid racing with event
+        handlers (which can remove the element).
+        (WebCore::SVGTRefElement::removedFrom):
+        * svg/SVGTRefElement.h:
+        (WebCore):
+        (SVGTRefElement):
+
 2012-08-03  Michael Saboff  <[email protected]>
 
         Convert HTML parser to handle 8-bit resources without converting to UChar*

Modified: trunk/Source/WebCore/dom/EventListener.h (124680 => 124681)


--- trunk/Source/WebCore/dom/EventListener.h	2012-08-04 01:13:22 UTC (rev 124680)
+++ trunk/Source/WebCore/dom/EventListener.h	2012-08-04 01:23:58 UTC (rev 124681)
@@ -42,7 +42,8 @@
             CPPEventListenerType,
             ConditionEventListenerType,
             GObjectEventListenerType,
-            NativeEventListenerType
+            NativeEventListenerType,
+            SVGTRefTargetEventListenerType
         };
 
         virtual ~EventListener() { }

Modified: trunk/Source/WebCore/svg/SVGTRefElement.cpp (124680 => 124681)


--- trunk/Source/WebCore/svg/SVGTRefElement.cpp	2012-08-04 01:13:22 UTC (rev 124680)
+++ trunk/Source/WebCore/svg/SVGTRefElement.cpp	2012-08-04 01:23:58 UTC (rev 124681)
@@ -49,13 +49,6 @@
     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGTextPositioningElement)
 END_REGISTER_ANIMATED_PROPERTIES
 
-inline SVGTRefElement::SVGTRefElement(const QualifiedName& tagName, Document* document)
-    : SVGTextPositioningElement(tagName, document)
-{
-    ASSERT(hasTagName(SVGNames::trefTag));
-    registerAnimatedPropertiesForSVGTRefElement();
-}
-
 PassRefPtr<SVGTRefElement> SVGTRefElement::create(const QualifiedName& tagName, Document* document)
 {
     RefPtr<SVGTRefElement> element = adoptRef(new SVGTRefElement(tagName, document));
@@ -63,59 +56,83 @@
     return element.release();
 }
 
-class TargetListener : public EventListener {
+class SVGTRefTargetEventListener : public EventListener {
 public:
-    static PassRefPtr<TargetListener> create(SVGTRefElement* trefElement, String targetId)
+    static PassRefPtr<SVGTRefTargetEventListener> create(SVGTRefElement* trefElement)
     {
-        return adoptRef(new TargetListener(trefElement, targetId));
+        return adoptRef(new SVGTRefTargetEventListener(trefElement));
     }
 
-    static const TargetListener* cast(const EventListener* listener)
+    static const SVGTRefTargetEventListener* cast(const EventListener* listener)
     {
-        return listener->type() == CPPEventListenerType ? static_cast<const TargetListener*>(listener) : 0;
+        return listener->type() == SVGTRefTargetEventListenerType
+                ? static_cast<const SVGTRefTargetEventListener*>(listener) : 0;
     }
 
-    virtual bool operator==(const EventListener&);
+    void attach(Element* target, String& targetId);
+    void detach();
+    bool isAttached() const { return m_attached; }
 
-    void clear()
-    {
-        Element* target = m_trefElement->treeScope()->getElementById(m_targetId);
-        if (target) {
-            target->removeEventListener(eventNames().DOMSubtreeModifiedEvent, this, false);
-            target->removeEventListener(eventNames().DOMNodeRemovedFromDocumentEvent, this, false);
-        }
-        
-        m_trefElement = 0;
-        m_targetId = String();
-    }
-
 private:
-    TargetListener(SVGTRefElement* trefElement, String targetId)
-        : EventListener(CPPEventListenerType)
-        , m_trefElement(trefElement)
-        , m_targetId(targetId)
-    {
-    }
+    SVGTRefTargetEventListener(SVGTRefElement* trefElement);
 
-    virtual void handleEvent(ScriptExecutionContext*, Event*);
+    virtual void handleEvent(ScriptExecutionContext*, Event*) OVERRIDE;
+    virtual bool operator==(const EventListener&) OVERRIDE;
 
     SVGTRefElement* m_trefElement;
     String m_targetId;
+    bool m_attached;
 };
 
-bool TargetListener::operator==(const EventListener& listener)
+SVGTRefTargetEventListener::SVGTRefTargetEventListener(SVGTRefElement* trefElement)
+    : EventListener(SVGTRefTargetEventListenerType)
+    , m_trefElement(trefElement)
+    , m_attached(false)
 {
-    if (const TargetListener* subtreeModificationEventListener = TargetListener::cast(&listener))
-        return m_trefElement == subtreeModificationEventListener->m_trefElement;
+    ASSERT(m_trefElement);
+}
+
+void SVGTRefTargetEventListener::attach(Element* target, String& targetId)
+{
+    ASSERT(!isAttached());
+    ASSERT(target);
+    ASSERT(target->inDocument());
+    ASSERT(!targetId.isEmpty());
+
+    target->addEventListener(eventNames().DOMSubtreeModifiedEvent, this, false);
+    target->addEventListener(eventNames().DOMNodeRemovedFromDocumentEvent, this, false);
+    m_targetId = targetId;
+    m_attached = true;
+}
+
+void SVGTRefTargetEventListener::detach()
+{
+    if (!isAttached())
+        return;
+
+    if (Element* target = m_trefElement->treeScope()->getElementById(m_targetId)) {
+        target->removeEventListener(eventNames().DOMSubtreeModifiedEvent, this, false);
+        target->removeEventListener(eventNames().DOMNodeRemovedFromDocumentEvent, this, false);
+    }
+
+    m_targetId = emptyString();
+    m_attached = false;
+}
+
+bool SVGTRefTargetEventListener::operator==(const EventListener& listener)
+{
+    if (const SVGTRefTargetEventListener* targetListener = SVGTRefTargetEventListener::cast(&listener))
+        return m_trefElement == targetListener->m_trefElement;
     return false;
 }
 
-void TargetListener::handleEvent(ScriptExecutionContext*, Event* event)
+void SVGTRefTargetEventListener::handleEvent(ScriptExecutionContext*, Event* event)
 {
-    if (m_trefElement && event->type() == eventNames().DOMSubtreeModifiedEvent && m_trefElement != event->target())
+    ASSERT(isAttached());
+
+    if (event->type() == eventNames().DOMSubtreeModifiedEvent && m_trefElement != event->target())
         m_trefElement->updateReferencedText();
-
-    if (m_trefElement && event->type() == eventNames().DOMNodeRemovedFromDocumentEvent)
+    else if (event->type() == eventNames().DOMNodeRemovedFromDocumentEvent)
         m_trefElement->detachTarget();
 }
 
@@ -148,9 +165,17 @@
     }
 }
 
+inline SVGTRefElement::SVGTRefElement(const QualifiedName& tagName, Document* document)
+    : SVGTextPositioningElement(tagName, document)
+    , m_targetListener(SVGTRefTargetEventListener::create(this))
+{
+    ASSERT(hasTagName(SVGNames::trefTag));
+    registerAnimatedPropertiesForSVGTRefElement();
+}
+
 SVGTRefElement::~SVGTRefElement()
 {
-    clearEventListener();
+    m_targetListener->detach();
 }
 
 void SVGTRefElement::createShadowSubtree()
@@ -175,7 +200,7 @@
 void SVGTRefElement::detachTarget()
 {
     // Remove active listeners and clear the text content.
-    clearEventListener();
+    m_targetListener->detach();
 
     String emptyContent;
     ExceptionCode ignore = 0;
@@ -185,7 +210,8 @@
     if (container)
         container->setTextContent(emptyContent, ignore);
 
-    ASSERT(inDocument());
+    if (!inDocument())
+        return;
 
     // Mark the referenced ID as pending.
     String id;
@@ -263,7 +289,7 @@
 void SVGTRefElement::buildPendingResource()
 {
     // Remove any existing event listener.
-    clearEventListener();
+    m_targetListener->detach();
 
     // If we're not yet in a document, this function will be called again from insertedInto().
     if (!inDocument())
@@ -280,17 +306,14 @@
         return;
     }
 
-    updateReferencedText();
-
     // Don't set up event listeners if this is a shadow tree node.
     // SVGUseElement::transferEventListenersToShadowTree() handles this task, and addEventListener()
     // expects every element instance to have an associated shadow tree element - which is not the
     // case when we land here from SVGUseElement::buildShadowTree().
-    if (!isInShadowTree()) {
-        m_eventListener = TargetListener::create(this, id);
-        target->addEventListener(eventNames().DOMSubtreeModifiedEvent, m_eventListener.get(), false);
-        target->addEventListener(eventNames().DOMNodeRemovedFromDocumentEvent, m_eventListener.get(), false);
-    }
+    if (!isInShadowTree())
+        m_targetListener->attach(target, id);
+
+    updateReferencedText();
 }
 
 Node::InsertionNotificationRequest SVGTRefElement::insertedInto(ContainerNode* rootParent)
@@ -305,17 +328,9 @@
 {
     SVGStyledElement::removedFrom(rootParent);
     if (rootParent->inDocument())
-        clearEventListener();
+        m_targetListener->detach();
 }
 
-void SVGTRefElement::clearEventListener()
-{
-    if (m_eventListener) {
-        m_eventListener->clear();
-        m_eventListener = 0;
-    }
 }
 
-}
-
 #endif // ENABLE(SVG)

Modified: trunk/Source/WebCore/svg/SVGTRefElement.h (124680 => 124681)


--- trunk/Source/WebCore/svg/SVGTRefElement.h	2012-08-04 01:13:22 UTC (rev 124680)
+++ trunk/Source/WebCore/svg/SVGTRefElement.h	2012-08-04 01:23:58 UTC (rev 124681)
@@ -27,7 +27,7 @@
 
 namespace WebCore {
 
-class TargetListener;
+class SVGTRefTargetEventListener;
 
 class SVGTRefElement : public SVGTextPositioningElement,
                        public SVGURIReference {
@@ -35,7 +35,7 @@
     static PassRefPtr<SVGTRefElement> create(const QualifiedName&, Document*);
 
 private:
-    friend class TargetListener;
+    friend class SVGTRefTargetEventListener;
 
     SVGTRefElement(const QualifiedName&, Document*);
     virtual ~SVGTRefElement();
@@ -53,8 +53,6 @@
     virtual InsertionNotificationRequest insertedInto(ContainerNode*) OVERRIDE;
     virtual void removedFrom(ContainerNode*) OVERRIDE;
 
-    void clearEventListener();
-
     void updateReferencedText();
 
     void detachTarget();
@@ -65,7 +63,7 @@
         DECLARE_ANIMATED_STRING(Href, href)
     END_DECLARE_ANIMATED_PROPERTIES
 
-    RefPtr<TargetListener> m_eventListener;
+    RefPtr<SVGTRefTargetEventListener> m_targetListener;
 };
 
 } // namespace WebCore
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to