[SCM] WebKit Debian packaging branch, webkit-1.1, updated. upstream/1.1.20-204-g221d8e8

ggaren at apple.com ggaren at apple.com
Wed Feb 10 22:15:39 UTC 2010


The following commit has been merged in the webkit-1.1 branch:
commit 4ab7b0ba7cc17067b0f7ac9f1adcff10491599a5
Author: ggaren at apple.com <ggaren at apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Date:   Fri Feb 5 03:39:09 2010 +0000

    WebCore: REGRESSION (r52082): Missing event handlers on JQuery demo page (33383)
    https://bugs.webkit.org/show_bug.cgi?id=33383
    <rdar://problem/7559449>
    
    Reviewed by Alexey Proskuryakov and Darin Adler.
    
    There were two bugs here:
    
    1. A stale wrapper would invalidate a node's event listeners, even if
    the node had a fresh wrapper keeping it alive.
    
    The fix for this is for an event listener to keep a WeakGCPtr back-pointer
    to the wrapper it expects to mark it. The wrapper destructor checks this
    back-pointer, and only invalidates the event listener in the case of a match.
    
    2. Conversely, a stale wrapper would not invalidate a node's event
    listeners soon enough, if its destructor didn't have a chance to run
    before an event fired on the node. (This can only happen in cases where
    we've made some other error and failed to mark a wrapper that was circuitously
    observable in the DOM. But we know we have edge case bugs like this, and
    we don't want them to be crashes.)
    
    The fix for this is to check the wrapper back-pointer before firing the
    event listener. As long as the the wrapper back-pointer is not null,
    it's safe to fire the listener.
    
    * ForwardingHeaders/runtime/WeakGCPtr.h: Added. Appease build gods.
    
    * bindings/js/JSAbstractWorkerCustom.cpp:
    (WebCore::JSAbstractWorker::addEventListener):
    (WebCore::JSAbstractWorker::removeEventListener):
    * bindings/js/JSDOMApplicationCacheCustom.cpp:
    (WebCore::JSDOMApplicationCache::addEventListener):
    (WebCore::JSDOMApplicationCache::removeEventListener):
    * bindings/js/JSDOMWindowCustom.cpp:
    (WebCore::JSDOMWindow::markChildren):
    (WebCore::JSDOMWindow::addEventListener):
    (WebCore::JSDOMWindow::removeEventListener): Updated to pass a wrapper
    to the JSEventListener constructor.
    
    * bindings/js/JSEventListener.cpp:
    (WebCore::JSEventListener::JSEventListener):
    (WebCore::JSEventListener::initializeJSFunction):
    (WebCore::JSEventListener::invalidateJSFunction):
    * bindings/js/JSEventListener.h:
    (WebCore::JSEventListener::create):
    (WebCore::JSEventListener::isolatedWorld):
    (WebCore::JSEventListener::wrapper):
    (WebCore::JSEventListener::setWrapper):
    (WebCore::JSEventListener::jsFunction):
    (WebCore::createJSAttributeEventListener): Implemented the back-pointer
    described above. Refactored the jsFunction() accessor to return 0 if
    the wrapper back-pointer is 0.
    
    * bindings/js/JSEventSourceCustom.cpp:
    (WebCore::JSEventSource::addEventListener):
    (WebCore::JSEventSource::removeEventListener):
    * bindings/js/JSLazyEventListener.cpp:
    (WebCore::JSLazyEventListener::JSLazyEventListener):
    (WebCore::JSLazyEventListener::initializeJSFunction):
    * bindings/js/JSLazyEventListener.h:
    (WebCore::JSLazyEventListener::create):
    * bindings/js/JSMessagePortCustom.cpp:
    (WebCore::JSMessagePort::markChildren):
    (WebCore::JSMessagePort::addEventListener):
    (WebCore::JSMessagePort::removeEventListener):
    * bindings/js/JSNodeCustom.cpp:
    (WebCore::JSNode::addEventListener):
    (WebCore::JSNode::removeEventListener):
    (WebCore::JSNode::markChildren):
    * bindings/js/JSSVGElementInstanceCustom.cpp:
    (WebCore::JSSVGElementInstance::addEventListener):
    (WebCore::JSSVGElementInstance::removeEventListener):
    * bindings/js/JSWebSocketCustom.cpp:
    (WebCore::JSWebSocket::addEventListener):
    (WebCore::JSWebSocket::removeEventListener):
    * bindings/js/JSWorkerContextCustom.cpp:
    (WebCore::JSWorkerContext::markChildren):
    (WebCore::JSWorkerContext::addEventListener):
    (WebCore::JSWorkerContext::removeEventListener):
    * bindings/js/JSXMLHttpRequestCustom.cpp:
    (WebCore::JSXMLHttpRequest::markChildren):
    (WebCore::JSXMLHttpRequest::addEventListener):
    (WebCore::JSXMLHttpRequest::removeEventListener):
    * bindings/js/JSXMLHttpRequestUploadCustom.cpp:
    (WebCore::JSXMLHttpRequestUpload::markChildren):
    (WebCore::JSXMLHttpRequestUpload::addEventListener):
    (WebCore::JSXMLHttpRequestUpload::removeEventListener): Updated to pass a wrapper
    to the JSEventListener constructor.
    
    
    * bindings/js/ScriptEventListener.cpp:
    (WebCore::createAttributeEventListener): Updated to pass a wrapper
    to the JSEventListener constructor.
    (WebCore::getEventListenerHandlerBody): Updated for the fact that jsFunction()
    is no longer a virtual accessor on the EventHandler base class.
    
    * bindings/scripts/CodeGeneratorJS.pm: Updated for the fact that jsFunction()
    is no longer a virtual accessor on the EventHandler base class. Added a "JS"
    to invalidateEventListeners and markEventListeners to clarify that these
    actions are for JS event listeners only. Added a wrapper parameter to
    invalidateEventListeners for the back-pointer check explained above.
    
    * dom/EventListener.h:
    (WebCore::EventListener::invalidateJSFunction): ditto
    
    * dom/EventTarget.h:
    (WebCore::EventTarget::markJSEventListeners):
    (WebCore::EventTarget::invalidateJSEventListeners): ditto
    
    LayoutTests: REGRESSION (r52082): Missing event handlers on JQuery demo page (33383)
    https://bugs.webkit.org/show_bug.cgi?id=33383
    <rdar://problem/7559449>
    
    Reviewed by Alexey Proskuryakov and Darin Adler.
    
    * fast/events/bogus-event-listener-invalidation-expected.txt: Added.
    * fast/events/bogus-event-listener-invalidation.html: Added.
    
    
    
    git-svn-id: http://svn.webkit.org/repository/webkit/trunk@54400 268f45cc-cd09-0410-ab3c-d52691b4dbfc

diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index e2dd3fa..e64b446 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,14 @@
+2010-01-27  Geoffrey Garen  <ggaren at apple.com>
+
+        Reviewed by Alexey Proskuryakov and Darin Adler.
+
+        REGRESSION (r52082): Missing event handlers on JQuery demo page (33383)
+        https://bugs.webkit.org/show_bug.cgi?id=33383
+        <rdar://problem/7559449>
+
+        * fast/events/bogus-event-listener-invalidation-expected.txt: Added.
+        * fast/events/bogus-event-listener-invalidation.html: Added.
+
 2010-02-04  Tony Chang  <tony at chromium.org>
 
         Reviewed by Eric Seidel.
diff --git a/LayoutTests/fast/events/bogus-event-listener-invalidation-expected.txt b/LayoutTests/fast/events/bogus-event-listener-invalidation-expected.txt
new file mode 100644
index 0000000..0ed17c9
--- /dev/null
+++ b/LayoutTests/fast/events/bogus-event-listener-invalidation-expected.txt
@@ -0,0 +1,4 @@
+This page tests for a bogus invalidation of a node's event listeners. If the test passes, you'll see a PASS message below.
+
+PASS: div.onclick should be function () { return "click"; } and is.
+
diff --git a/LayoutTests/fast/events/bogus-event-listener-invalidation.html b/LayoutTests/fast/events/bogus-event-listener-invalidation.html
new file mode 100644
index 0000000..c505b3f
--- /dev/null
+++ b/LayoutTests/fast/events/bogus-event-listener-invalidation.html
@@ -0,0 +1,65 @@
+<p>This page tests for a bogus invalidation of a node's event listeners. If the
+test passes, you'll see a PASS message below.</p>
+<pre id="console"></pre>
+
+<div id="div"></div>
+
+<script>
+function $(id)
+{
+    return document.getElementById(id);
+}
+
+function log(s)
+{
+    $('console').appendChild(document.createTextNode(s + '\n'));
+}
+
+function shouldBe(aDescription, a, b)
+{
+    if (a === b) {
+        log("PASS: " + aDescription + " should be " + b + " and is.");
+        return;
+    }
+    log("FAIL: " + aDescription + " should be " + b + " but instead is " + a);
+}
+
+function allocate() {
+    for (var i = 0; i < 5000; ++i)
+        new Object;
+}
+
+(function () {
+    if (window.layoutTestController) {
+        layoutTestController.dumpAsText();
+        layoutTestController.waitUntilDone();
+    }
+    
+    var listener = function () { return "click"; };
+    
+    // Fill the heap with garbage...
+    var a = []
+    for (var i = 0; i < 5000; ++i)
+        a[a.length] = new Object;
+
+    // ...followed by a DOM node wrapper.
+    $("div");
+    a = null;
+    
+    // Move the allocation pointer up to the DOM node wrapper.
+    allocate();
+
+    // Make a new DOM node wrapper, and add an event listener.
+    $("div").onclick = listener;
+    
+    // Collect the original DOM node wrapper.
+    for (var i = 0; i < 3; ++i)
+        allocate();
+
+    // See if the listener is still registered.
+    shouldBe("div.onclick", div.onclick, listener);
+
+    if (window.layoutTestController)
+        layoutTestController.notifyDone();
+})();
+</script>
diff --git a/WebCore/ChangeLog b/WebCore/ChangeLog
index 5c07192..26bd56c 100644
--- a/WebCore/ChangeLog
+++ b/WebCore/ChangeLog
@@ -1,3 +1,115 @@
+2010-02-04  Geoffrey Garen  <ggaren at apple.com>
+
+        Reviewed by Alexey Proskuryakov and Darin Adler.
+
+        REGRESSION (r52082): Missing event handlers on JQuery demo page (33383)
+        https://bugs.webkit.org/show_bug.cgi?id=33383
+        <rdar://problem/7559449>
+        
+        There were two bugs here:
+        
+        1. A stale wrapper would invalidate a node's event listeners, even if
+        the node had a fresh wrapper keeping it alive.
+        
+        The fix for this is for an event listener to keep a WeakGCPtr back-pointer
+        to the wrapper it expects to mark it. The wrapper destructor checks this
+        back-pointer, and only invalidates the event listener in the case of a match.
+
+        2. Conversely, a stale wrapper would not invalidate a node's event
+        listeners soon enough, if its destructor didn't have a chance to run
+        before an event fired on the node. (This can only happen in cases where
+        we've made some other error and failed to mark a wrapper that was circuitously
+        observable in the DOM. But we know we have edge case bugs like this, and
+        we don't want them to be crashes.)
+        
+        The fix for this is to check the wrapper back-pointer before firing the
+        event listener. As long as the the wrapper back-pointer is not null,
+        it's safe to fire the listener. 
+
+        * ForwardingHeaders/runtime/WeakGCPtr.h: Added. Appease build gods.
+
+        * bindings/js/JSAbstractWorkerCustom.cpp:
+        (WebCore::JSAbstractWorker::addEventListener):
+        (WebCore::JSAbstractWorker::removeEventListener):
+        * bindings/js/JSDOMApplicationCacheCustom.cpp:
+        (WebCore::JSDOMApplicationCache::addEventListener):
+        (WebCore::JSDOMApplicationCache::removeEventListener):
+        * bindings/js/JSDOMWindowCustom.cpp:
+        (WebCore::JSDOMWindow::markChildren):
+        (WebCore::JSDOMWindow::addEventListener):
+        (WebCore::JSDOMWindow::removeEventListener): Updated to pass a wrapper
+        to the JSEventListener constructor.
+
+        * bindings/js/JSEventListener.cpp:
+        (WebCore::JSEventListener::JSEventListener):
+        (WebCore::JSEventListener::initializeJSFunction):
+        (WebCore::JSEventListener::invalidateJSFunction):
+        * bindings/js/JSEventListener.h:
+        (WebCore::JSEventListener::create):
+        (WebCore::JSEventListener::isolatedWorld):
+        (WebCore::JSEventListener::wrapper):
+        (WebCore::JSEventListener::setWrapper):
+        (WebCore::JSEventListener::jsFunction):
+        (WebCore::createJSAttributeEventListener): Implemented the back-pointer
+        described above. Refactored the jsFunction() accessor to return 0 if
+        the wrapper back-pointer is 0.
+
+        * bindings/js/JSEventSourceCustom.cpp:
+        (WebCore::JSEventSource::addEventListener):
+        (WebCore::JSEventSource::removeEventListener):
+        * bindings/js/JSLazyEventListener.cpp:
+        (WebCore::JSLazyEventListener::JSLazyEventListener):
+        (WebCore::JSLazyEventListener::initializeJSFunction):
+        * bindings/js/JSLazyEventListener.h:
+        (WebCore::JSLazyEventListener::create):
+        * bindings/js/JSMessagePortCustom.cpp:
+        (WebCore::JSMessagePort::markChildren):
+        (WebCore::JSMessagePort::addEventListener):
+        (WebCore::JSMessagePort::removeEventListener):
+        * bindings/js/JSNodeCustom.cpp:
+        (WebCore::JSNode::addEventListener):
+        (WebCore::JSNode::removeEventListener):
+        (WebCore::JSNode::markChildren):
+        * bindings/js/JSSVGElementInstanceCustom.cpp:
+        (WebCore::JSSVGElementInstance::addEventListener):
+        (WebCore::JSSVGElementInstance::removeEventListener):
+        * bindings/js/JSWebSocketCustom.cpp:
+        (WebCore::JSWebSocket::addEventListener):
+        (WebCore::JSWebSocket::removeEventListener):
+        * bindings/js/JSWorkerContextCustom.cpp:
+        (WebCore::JSWorkerContext::markChildren):
+        (WebCore::JSWorkerContext::addEventListener):
+        (WebCore::JSWorkerContext::removeEventListener):
+        * bindings/js/JSXMLHttpRequestCustom.cpp:
+        (WebCore::JSXMLHttpRequest::markChildren):
+        (WebCore::JSXMLHttpRequest::addEventListener):
+        (WebCore::JSXMLHttpRequest::removeEventListener):
+        * bindings/js/JSXMLHttpRequestUploadCustom.cpp:
+        (WebCore::JSXMLHttpRequestUpload::markChildren):
+        (WebCore::JSXMLHttpRequestUpload::addEventListener):
+        (WebCore::JSXMLHttpRequestUpload::removeEventListener): Updated to pass a wrapper
+        to the JSEventListener constructor.
+
+
+        * bindings/js/ScriptEventListener.cpp:
+        (WebCore::createAttributeEventListener): Updated to pass a wrapper
+        to the JSEventListener constructor.
+        (WebCore::getEventListenerHandlerBody): Updated for the fact that jsFunction()
+        is no longer a virtual accessor on the EventHandler base class.
+
+        * bindings/scripts/CodeGeneratorJS.pm: Updated for the fact that jsFunction()
+        is no longer a virtual accessor on the EventHandler base class. Added a "JS"
+        to invalidateEventListeners and markEventListeners to clarify that these
+        actions are for JS event listeners only. Added a wrapper parameter to
+        invalidateEventListeners for the back-pointer check explained above.
+
+        * dom/EventListener.h:
+        (WebCore::EventListener::invalidateJSFunction): ditto
+
+        * dom/EventTarget.h:
+        (WebCore::EventTarget::markJSEventListeners):
+        (WebCore::EventTarget::invalidateJSEventListeners): ditto
+
 2010-02-04  Tony Chang  <tony at chromium.org>
 
         Reviewed by Eric Seidel.
diff --git a/WebCore/bindings/js/JSAbstractWorkerCustom.cpp b/WebCore/bindings/js/JSAbstractWorkerCustom.cpp
index 6eca7bd..61fcf98 100644
--- a/WebCore/bindings/js/JSAbstractWorkerCustom.cpp
+++ b/WebCore/bindings/js/JSAbstractWorkerCustom.cpp
@@ -50,7 +50,7 @@ JSValue JSAbstractWorker::addEventListener(ExecState* exec, const ArgList& args)
     if (!listener.isObject())
         return jsUndefined();
 
-    impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false, currentWorld(exec)), args.at(2).toBoolean(exec));
+    impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)), args.at(2).toBoolean(exec));
     return jsUndefined();
 }
 
@@ -60,7 +60,7 @@ JSValue JSAbstractWorker::removeEventListener(ExecState* exec, const ArgList& ar
     if (!listener.isObject())
         return jsUndefined();
 
-    impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
+    impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
     return jsUndefined();
 }
 
diff --git a/WebCore/bindings/js/JSDOMApplicationCacheCustom.cpp b/WebCore/bindings/js/JSDOMApplicationCacheCustom.cpp
index 91ee51a..5637087 100644
--- a/WebCore/bindings/js/JSDOMApplicationCacheCustom.cpp
+++ b/WebCore/bindings/js/JSDOMApplicationCacheCustom.cpp
@@ -91,7 +91,7 @@ JSValue JSDOMApplicationCache::addEventListener(ExecState* exec, const ArgList&
     if (!listener.isObject())
         return jsUndefined();
 
-    impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false, currentWorld(exec)), args.at(2).toBoolean(exec));
+    impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)), args.at(2).toBoolean(exec));
     return jsUndefined();
 }
 
@@ -101,7 +101,7 @@ JSValue JSDOMApplicationCache::removeEventListener(ExecState* exec, const ArgLis
     if (!listener.isObject())
         return jsUndefined();
 
-    impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
+    impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
     return jsUndefined();
 }
 
diff --git a/WebCore/bindings/js/JSDOMWindowCustom.cpp b/WebCore/bindings/js/JSDOMWindowCustom.cpp
index f7be2c5..9e7856b 100644
--- a/WebCore/bindings/js/JSDOMWindowCustom.cpp
+++ b/WebCore/bindings/js/JSDOMWindowCustom.cpp
@@ -97,7 +97,7 @@ void JSDOMWindow::markChildren(MarkStack& markStack)
 {
     Base::markChildren(markStack);
 
-    impl()->markEventListeners(markStack);
+    impl()->markJSEventListeners(markStack);
 
     JSGlobalData& globalData = *Heap::heap(this)->globalData();
 
@@ -997,7 +997,7 @@ JSValue JSDOMWindow::addEventListener(ExecState* exec, const ArgList& args)
     if (!listener.isObject())
         return jsUndefined();
 
-    impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false, currentWorld(exec)), args.at(2).toBoolean(exec));
+    impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)), args.at(2).toBoolean(exec));
     return jsUndefined();
 }
 
@@ -1011,7 +1011,7 @@ JSValue JSDOMWindow::removeEventListener(ExecState* exec, const ArgList& args)
     if (!listener.isObject())
         return jsUndefined();
 
-    impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
+    impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
     return jsUndefined();
 }
 
diff --git a/WebCore/bindings/js/JSEventListener.cpp b/WebCore/bindings/js/JSEventListener.cpp
index d3b5fe1..93e059f 100644
--- a/WebCore/bindings/js/JSEventListener.cpp
+++ b/WebCore/bindings/js/JSEventListener.cpp
@@ -31,9 +31,10 @@ using namespace JSC;
 
 namespace WebCore {
 
-JSEventListener::JSEventListener(JSObject* function, bool isAttribute, DOMWrapperWorld* isolatedWorld)
+JSEventListener::JSEventListener(JSObject* function, JSObject* wrapper, bool isAttribute, DOMWrapperWorld* isolatedWorld)
     : EventListener(JSEventListenerType)
     , m_jsFunction(function)
+    , m_wrapper(wrapper)
     , m_isAttribute(isAttribute)
     , m_isolatedWorld(isolatedWorld)
 {
@@ -43,9 +44,10 @@ JSEventListener::~JSEventListener()
 {
 }
 
-JSObject* JSEventListener::jsFunction(ScriptExecutionContext*) const
+JSObject* JSEventListener::initializeJSFunction(ScriptExecutionContext*) const
 {
-    return m_jsFunction;
+    ASSERT_NOT_REACHED();
+    return 0;
 }
 
 void JSEventListener::markJSFunction(MarkStack& markStack)
@@ -54,6 +56,15 @@ void JSEventListener::markJSFunction(MarkStack& markStack)
         markStack.append(m_jsFunction);
 }
 
+void JSEventListener::invalidateJSFunction(JSC::JSObject* wrapper)
+{
+    // Since m_wrapper is a WeakGCPtr, it pretends to be null, and therefore !=
+    // to wrapper, when it's awaiting destruction. So, we check for == wrapper
+    // or == null.
+    if (!m_wrapper || m_wrapper == wrapper)
+        m_wrapper = 0;
+}
+
 void JSEventListener::handleEvent(ScriptExecutionContext* scriptExecutionContext, Event* event)
 {
     ASSERT(scriptExecutionContext);
diff --git a/WebCore/bindings/js/JSEventListener.h b/WebCore/bindings/js/JSEventListener.h
index 9ba09c6..9612f98 100644
--- a/WebCore/bindings/js/JSEventListener.h
+++ b/WebCore/bindings/js/JSEventListener.h
@@ -22,7 +22,7 @@
 
 #include "EventListener.h"
 #include "JSDOMWindow.h"
-#include <runtime/Protect.h>
+#include <runtime/WeakGCPtr.h>
 
 namespace WebCore {
 
@@ -30,9 +30,9 @@ namespace WebCore {
 
     class JSEventListener : public EventListener {
     public:
-        static PassRefPtr<JSEventListener> create(JSC::JSObject* listener, bool isAttribute, DOMWrapperWorld* isolatedWorld)
+        static PassRefPtr<JSEventListener> create(JSC::JSObject* listener, JSC::JSObject* wrapper, bool isAttribute, DOMWrapperWorld* isolatedWorld)
         {
-            return adoptRef(new JSEventListener(listener, isAttribute, isolatedWorld));
+            return adoptRef(new JSEventListener(listener, wrapper, isAttribute, isolatedWorld));
         }
 
         static const JSEventListener* cast(const EventListener* listener)
@@ -49,30 +49,56 @@ namespace WebCore {
         // Returns true if this event listener was created for an event handler attribute, like "onload" or "onclick".
         bool isAttribute() const { return m_isAttribute; }
 
-        virtual JSC::JSObject* jsFunction(ScriptExecutionContext*) const;
+        JSC::JSObject* jsFunction(ScriptExecutionContext*) const;
+        DOMWrapperWorld* isolatedWorld() const { return m_isolatedWorld.get(); }
+
+        JSC::JSObject* wrapper() const { return m_wrapper.get(); }
+        void setWrapper(JSC::JSObject* wrapper) const { m_wrapper = wrapper; }
 
     private:
+        virtual JSC::JSObject* initializeJSFunction(ScriptExecutionContext*) const;
         virtual void markJSFunction(JSC::MarkStack&);
+        virtual void invalidateJSFunction(JSC::JSObject*);
         virtual void handleEvent(ScriptExecutionContext*, Event*);
         virtual bool reportError(ScriptExecutionContext*, const String& message, const String& url, int lineNumber);
         virtual bool virtualisAttribute() const;
-        void clearJSFunctionInline();
 
     protected:
-        JSEventListener(JSC::JSObject* function, bool isAttribute, DOMWrapperWorld* isolatedWorld);
+        JSEventListener(JSC::JSObject* function, JSC::JSObject* wrapper, bool isAttribute, DOMWrapperWorld* isolatedWorld);
 
+    private:
         mutable JSC::JSObject* m_jsFunction;
+        mutable JSC::WeakGCPtr<JSC::JSObject> m_wrapper;
+
         bool m_isAttribute;
         RefPtr<DOMWrapperWorld> m_isolatedWorld;
     };
 
+    inline JSC::JSObject* JSEventListener::jsFunction(ScriptExecutionContext* scriptExecutionContext) const
+    {
+        if (!m_jsFunction)
+            m_jsFunction = initializeJSFunction(scriptExecutionContext);
+
+        // Verify that we have a valid wrapper protecting our function from
+        // garbage collection.
+        ASSERT(m_wrapper || !m_jsFunction);
+        if (!m_wrapper)
+            return 0;
+
+        // Try to verify that m_jsFunction wasn't recycled. (Not exact, since an
+        // event listener can be almost anything, but this makes test-writing easier).
+        ASSERT(!m_jsFunction || static_cast<JSC::JSCell*>(m_jsFunction)->isObject());
+
+        return m_jsFunction;
+    }
+
     // Creates a JS EventListener for an "onXXX" event attribute.
-    inline PassRefPtr<JSEventListener> createJSAttributeEventListener(JSC::ExecState* exec, JSC::JSValue listener)
+    inline PassRefPtr<JSEventListener> createJSAttributeEventListener(JSC::ExecState* exec, JSC::JSValue listener, JSC::JSObject* wrapper)
     {
         if (!listener.isObject())
             return 0;
 
-        return JSEventListener::create(asObject(listener), true, currentWorld(exec));
+        return JSEventListener::create(asObject(listener), wrapper, true, currentWorld(exec));
     }
 
 
diff --git a/WebCore/bindings/js/JSEventSourceCustom.cpp b/WebCore/bindings/js/JSEventSourceCustom.cpp
index 8f0dfb1..dab3285 100644
--- a/WebCore/bindings/js/JSEventSourceCustom.cpp
+++ b/WebCore/bindings/js/JSEventSourceCustom.cpp
@@ -49,7 +49,7 @@ JSValue JSEventSource::addEventListener(ExecState* exec, const ArgList& args)
     if (!listener.isObject())
         return jsUndefined();
 
-    impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
+    impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
     return jsUndefined();
 }
 
@@ -59,7 +59,7 @@ JSValue JSEventSource::removeEventListener(ExecState* exec, const ArgList& args)
     if (!listener.isObject())
         return jsUndefined();
 
-    impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
+    impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
     return jsUndefined();
 }
 
diff --git a/WebCore/bindings/js/JSLazyEventListener.cpp b/WebCore/bindings/js/JSLazyEventListener.cpp
index b707991..4fbdaa6 100644
--- a/WebCore/bindings/js/JSLazyEventListener.cpp
+++ b/WebCore/bindings/js/JSLazyEventListener.cpp
@@ -35,12 +35,11 @@ namespace WebCore {
 static WTF::RefCountedLeakCounter eventListenerCounter("JSLazyEventListener");
 #endif
 
-JSLazyEventListener::JSLazyEventListener(const String& functionName, const String& eventParameterName, const String& code, Node* node, const String& sourceURL, int lineNumber, DOMWrapperWorld* isolatedWorld)
-    : JSEventListener(0, true, isolatedWorld)
+JSLazyEventListener::JSLazyEventListener(const String& functionName, const String& eventParameterName, const String& code, Node* node, const String& sourceURL, int lineNumber, JSObject* wrapper, DOMWrapperWorld* isolatedWorld)
+    : JSEventListener(0, wrapper, true, isolatedWorld)
     , m_functionName(functionName)
     , m_eventParameterName(eventParameterName)
     , m_code(code)
-    , m_parsed(false)
     , m_sourceURL(sourceURL)
     , m_lineNumber(lineNumber)
     , m_originalNode(node)
@@ -48,8 +47,8 @@ JSLazyEventListener::JSLazyEventListener(const String& functionName, const Strin
     // We don't retain the original node because we assume it
     // will stay alive as long as this handler object is around
     // and we need to avoid a reference cycle. If JS transfers
-    // this handler to another node, parseCode will be called and
-    // then originalNode is no longer needed.
+    // this handler to another node, initializeJSFunction will
+    // be called and then originalNode is no longer needed.
 
     // A JSLazyEventListener can be created with a line number of zero when it is created with
     // a setAttribute call from JavaScript, so make the line number 1 in that case.
@@ -68,79 +67,62 @@ JSLazyEventListener::~JSLazyEventListener()
 #endif
 }
 
-JSObject* JSLazyEventListener::jsFunction(ScriptExecutionContext* executionContext) const
-{
-    parseCode(executionContext);
-    return m_jsFunction;
-}
-
-void JSLazyEventListener::parseCode(ScriptExecutionContext* executionContext) const
+JSObject* JSLazyEventListener::initializeJSFunction(ScriptExecutionContext* executionContext) const
 {
     ASSERT(executionContext);
     ASSERT(executionContext->isDocument());
     if (!executionContext)
-        return;
-
-    if (m_parsed)
-        return;
+        return 0;
 
     Frame* frame = static_cast<Document*>(executionContext)->frame();
     if (!frame)
-        return;
+        return 0;
 
     ScriptController* scriptController = frame->script();
     if (!scriptController->canExecuteScripts())
-        return;
+        return 0;
 
-    JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(executionContext, m_isolatedWorld.get());
+    JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(executionContext, isolatedWorld());
     if (!globalObject)
-        return;
-
-    // Ensure that 'node' has a JavaScript wrapper to mark the event listener we're creating.
-    if (m_originalNode) {
-        JSLock lock(SilenceAssertionsOnly);
-        // FIXME: Should pass the global object associated with the node
-        toJS(globalObject->globalExec(), globalObject, m_originalNode);
-    }
+        return 0;
 
     if (executionContext->isDocument()) {
         JSDOMWindow* window = static_cast<JSDOMWindow*>(globalObject);
         Frame* frame = window->impl()->frame();
         if (!frame)
-            return;
+            return 0;
         // FIXME: Is this check needed for non-Document contexts?
         ScriptController* script = frame->script();
         if (!script->canExecuteScripts() || script->isPaused())
-            return;
+            return 0;
     }
 
-    m_parsed = true;
-
     ExecState* exec = globalObject->globalExec();
 
     MarkedArgumentBuffer args;
     args.append(jsNontrivialString(exec, m_eventParameterName));
     args.append(jsString(exec, m_code));
 
-    m_jsFunction = constructFunction(exec, args, Identifier(exec, m_functionName), m_sourceURL, m_lineNumber); // FIXME: is globalExec ok?
-
-    JSFunction* listenerAsFunction = static_cast<JSFunction*>(m_jsFunction);
-
+    JSObject* jsFunction = constructFunction(exec, args, Identifier(exec, m_functionName), m_sourceURL, m_lineNumber); // FIXME: is globalExec ok?
     if (exec->hadException()) {
         exec->clearException();
+        return 0;
+    }
+
+    JSFunction* listenerAsFunction = static_cast<JSFunction*>(jsFunction);
+    if (m_originalNode) {
+        if (!wrapper()) {
+            // Ensure that 'node' has a JavaScript wrapper to mark the event listener we're creating.
+            JSLock lock(SilenceAssertionsOnly);
+            // FIXME: Should pass the global object associated with the node
+            setWrapper(asObject(toJS(globalObject->globalExec(), globalObject, m_originalNode)));
+        }
 
-        // failed to parse, so let's just make this listener a no-op
-        m_jsFunction = 0;
-    } else if (m_originalNode) {
         // Add the event's home element to the scope
         // (and the document, and the form - see JSHTMLElement::eventHandlerScope)
         ScopeChain scope = listenerAsFunction->scope();
-
-        JSValue thisObj = toJS(exec, globalObject, m_originalNode);
-        if (thisObj.isObject()) {
-            static_cast<JSNode*>(asObject(thisObj))->pushEventHandlerScope(exec, scope);
-            listenerAsFunction->setScope(scope);
-        }
+        static_cast<JSNode*>(wrapper())->pushEventHandlerScope(exec, scope);
+        listenerAsFunction->setScope(scope);
     }
 
     // Since we only parse once, there's no need to keep data used for parsing around anymore.
@@ -148,6 +130,7 @@ void JSLazyEventListener::parseCode(ScriptExecutionContext* executionContext) co
     m_code = String();
     m_eventParameterName = String();
     m_sourceURL = String();
+    return jsFunction;
 }
 
 } // namespace WebCore
diff --git a/WebCore/bindings/js/JSLazyEventListener.h b/WebCore/bindings/js/JSLazyEventListener.h
index ba26ef6..1b00b75 100644
--- a/WebCore/bindings/js/JSLazyEventListener.h
+++ b/WebCore/bindings/js/JSLazyEventListener.h
@@ -29,24 +29,21 @@ namespace WebCore {
 
     class JSLazyEventListener : public JSEventListener {
     public:
-        static PassRefPtr<JSLazyEventListener> create(const String& functionName, const String& eventParameterName, const String& code, Node* node, const String& sourceURL, int lineNumber, DOMWrapperWorld* isolatedWorld)
+        static PassRefPtr<JSLazyEventListener> create(const String& functionName, const String& eventParameterName, const String& code, Node* node, const String& sourceURL, int lineNumber, JSC::JSObject* wrapper, DOMWrapperWorld* isolatedWorld)
         {
-            return adoptRef(new JSLazyEventListener(functionName, eventParameterName, code, node, sourceURL, lineNumber, isolatedWorld));
+            return adoptRef(new JSLazyEventListener(functionName, eventParameterName, code, node, sourceURL, lineNumber, wrapper, isolatedWorld));
         }
         virtual ~JSLazyEventListener();
 
     private:
-        JSLazyEventListener(const String& functionName, const String& eventParameterName, const String& code, Node*, const String& sourceURL, int lineNumber, DOMWrapperWorld* isolatedWorld);
+        JSLazyEventListener(const String& functionName, const String& eventParameterName, const String& code, Node*, const String& sourceURL, int lineNumber, JSC::JSObject* wrapper, DOMWrapperWorld* isolatedWorld);
 
-        virtual JSC::JSObject* jsFunction(ScriptExecutionContext*) const;
+        virtual JSC::JSObject* initializeJSFunction(ScriptExecutionContext*) const;
         virtual bool wasCreatedFromMarkup() const { return true; }
 
-        void parseCode(ScriptExecutionContext*) const;
-
         mutable String m_functionName;
         mutable String m_eventParameterName;
         mutable String m_code;
-        mutable bool m_parsed;
         mutable String m_sourceURL;
         int m_lineNumber;
         Node* m_originalNode;
diff --git a/WebCore/bindings/js/JSMessagePortCustom.cpp b/WebCore/bindings/js/JSMessagePortCustom.cpp
index 2c09620..2ee8125 100644
--- a/WebCore/bindings/js/JSMessagePortCustom.cpp
+++ b/WebCore/bindings/js/JSMessagePortCustom.cpp
@@ -49,7 +49,7 @@ void JSMessagePort::markChildren(MarkStack& markStack)
     if (MessagePort* entangledPort = m_impl->locallyEntangledPort())
         markDOMObjectWrapper(markStack, *Heap::heap(this)->globalData(), entangledPort);
 
-    m_impl->markEventListeners(markStack);
+    m_impl->markJSEventListeners(markStack);
 }
 
 JSValue JSMessagePort::addEventListener(ExecState* exec, const ArgList& args)
@@ -58,7 +58,7 @@ JSValue JSMessagePort::addEventListener(ExecState* exec, const ArgList& args)
     if (!listener.isObject())
         return jsUndefined();
 
-    impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
+    impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
     return jsUndefined();
 }
 
@@ -68,7 +68,7 @@ JSValue JSMessagePort::removeEventListener(ExecState* exec, const ArgList& args)
     if (!listener.isObject())
         return jsUndefined();
 
-    impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
+    impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
     return jsUndefined();
 }
 
diff --git a/WebCore/bindings/js/JSNodeCustom.cpp b/WebCore/bindings/js/JSNodeCustom.cpp
index 737430e..46a30a4 100644
--- a/WebCore/bindings/js/JSNodeCustom.cpp
+++ b/WebCore/bindings/js/JSNodeCustom.cpp
@@ -114,7 +114,7 @@ JSValue JSNode::addEventListener(ExecState* exec, const ArgList& args)
     if (!listener.isObject())
         return jsUndefined();
 
-    impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false, currentWorld(exec)), args.at(2).toBoolean(exec));
+    impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)), args.at(2).toBoolean(exec));
     return jsUndefined();
 }
 
@@ -124,7 +124,7 @@ JSValue JSNode::removeEventListener(ExecState* exec, const ArgList& args)
     if (!listener.isObject())
         return jsUndefined();
 
-    impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
+    impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
     return jsUndefined();
 }
 
@@ -137,7 +137,7 @@ void JSNode::markChildren(MarkStack& markStack)
     Base::markChildren(markStack);
 
     Node* node = m_impl.get();
-    node->markEventListeners(markStack);
+    node->markJSEventListeners(markStack);
 
     // Nodes in the document are kept alive by JSDocument::mark, so, if we're in
     // the document, we need to mark the document, but we don't need to explicitly
diff --git a/WebCore/bindings/js/JSSVGElementInstanceCustom.cpp b/WebCore/bindings/js/JSSVGElementInstanceCustom.cpp
index 5f26df3..b3bded5 100644
--- a/WebCore/bindings/js/JSSVGElementInstanceCustom.cpp
+++ b/WebCore/bindings/js/JSSVGElementInstanceCustom.cpp
@@ -52,7 +52,7 @@ JSValue JSSVGElementInstance::addEventListener(ExecState* exec, const ArgList& a
     if (!listener.isObject())
         return jsUndefined();
 
-    impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false, currentWorld(exec)), args.at(2).toBoolean(exec));
+    impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)), args.at(2).toBoolean(exec));
     return jsUndefined();
 }
 
@@ -62,7 +62,7 @@ JSValue JSSVGElementInstance::removeEventListener(ExecState* exec, const ArgList
     if (!listener.isObject())
         return jsUndefined();
 
-    impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
+    impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
     return jsUndefined();
 }
 
diff --git a/WebCore/bindings/js/JSWebSocketCustom.cpp b/WebCore/bindings/js/JSWebSocketCustom.cpp
index 238b041..d610f01 100644
--- a/WebCore/bindings/js/JSWebSocketCustom.cpp
+++ b/WebCore/bindings/js/JSWebSocketCustom.cpp
@@ -65,7 +65,7 @@ JSValue JSWebSocket::addEventListener(ExecState* exec, const ArgList& args)
     if (!listener.isObject())
         return jsUndefined();
 
-    impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false, currentWorld(exec)), args.at(2).toBoolean(exec));
+    impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)), args.at(2).toBoolean(exec));
     return jsUndefined();
 }
 
@@ -75,7 +75,7 @@ JSValue JSWebSocket::removeEventListener(ExecState* exec, const ArgList& args)
     if (!listener.isObject())
         return jsUndefined();
 
-    impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
+    impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
     return jsUndefined();
 }
 
diff --git a/WebCore/bindings/js/JSWorkerContextCustom.cpp b/WebCore/bindings/js/JSWorkerContextCustom.cpp
index ab28674..d6c8dbd 100644
--- a/WebCore/bindings/js/JSWorkerContextCustom.cpp
+++ b/WebCore/bindings/js/JSWorkerContextCustom.cpp
@@ -60,7 +60,7 @@ void JSWorkerContext::markChildren(MarkStack& markStack)
     markDOMObjectWrapper(markStack, globalData, impl()->optionalLocation());
     markDOMObjectWrapper(markStack, globalData, impl()->optionalNavigator());
 
-    impl()->markEventListeners(markStack);
+    impl()->markJSEventListeners(markStack);
 }
 
 bool JSWorkerContext::getOwnPropertySlotDelegate(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
@@ -127,7 +127,7 @@ JSValue JSWorkerContext::addEventListener(ExecState* exec, const ArgList& args)
     if (!listener.isObject())
         return jsUndefined();
 
-    impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false, currentWorld(exec)), args.at(2).toBoolean(exec));
+    impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)), args.at(2).toBoolean(exec));
     return jsUndefined();
 }
 
@@ -137,7 +137,7 @@ JSValue JSWorkerContext::removeEventListener(ExecState* exec, const ArgList& arg
     if (!listener.isObject())
         return jsUndefined();
 
-    impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
+    impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
     return jsUndefined();
 }
 
diff --git a/WebCore/bindings/js/JSXMLHttpRequestCustom.cpp b/WebCore/bindings/js/JSXMLHttpRequestCustom.cpp
index d35d53a..e20b6d9 100644
--- a/WebCore/bindings/js/JSXMLHttpRequestCustom.cpp
+++ b/WebCore/bindings/js/JSXMLHttpRequestCustom.cpp
@@ -56,7 +56,7 @@ void JSXMLHttpRequest::markChildren(MarkStack& markStack)
     if (XMLHttpRequestUpload* upload = m_impl->optionalUpload())
         markDOMObjectWrapper(markStack, *Heap::heap(this)->globalData(), upload);
 
-    m_impl->markEventListeners(markStack);
+    m_impl->markJSEventListeners(markStack);
 }
 
 // Custom functions
@@ -153,7 +153,7 @@ JSValue JSXMLHttpRequest::addEventListener(ExecState* exec, const ArgList& args)
     if (!listener.isObject())
         return jsUndefined();
 
-    impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false, currentWorld(exec)), args.at(2).toBoolean(exec));
+    impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)), args.at(2).toBoolean(exec));
     return jsUndefined();
 }
 
@@ -163,7 +163,7 @@ JSValue JSXMLHttpRequest::removeEventListener(ExecState* exec, const ArgList& ar
     if (!listener.isObject())
         return jsUndefined();
 
-    impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
+    impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
     return jsUndefined();
 }
 
diff --git a/WebCore/bindings/js/JSXMLHttpRequestUploadCustom.cpp b/WebCore/bindings/js/JSXMLHttpRequestUploadCustom.cpp
index fa7cfec..857c12d 100644
--- a/WebCore/bindings/js/JSXMLHttpRequestUploadCustom.cpp
+++ b/WebCore/bindings/js/JSXMLHttpRequestUploadCustom.cpp
@@ -48,7 +48,7 @@ void JSXMLHttpRequestUpload::markChildren(MarkStack& markStack)
     if (XMLHttpRequest* xmlHttpRequest = m_impl->associatedXMLHttpRequest())
         markDOMObjectWrapper(markStack, *Heap::heap(this)->globalData(), xmlHttpRequest);
 
-    m_impl->markEventListeners(markStack);
+    m_impl->markJSEventListeners(markStack);
 }
 
 JSValue JSXMLHttpRequestUpload::addEventListener(ExecState* exec, const ArgList& args)
@@ -57,7 +57,7 @@ JSValue JSXMLHttpRequestUpload::addEventListener(ExecState* exec, const ArgList&
     if (!listener.isObject())
         return jsUndefined();
 
-    impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false, currentWorld(exec)), args.at(2).toBoolean(exec));
+    impl()->addEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)), args.at(2).toBoolean(exec));
     return jsUndefined();
 }
 
@@ -67,7 +67,7 @@ JSValue JSXMLHttpRequestUpload::removeEventListener(ExecState* exec, const ArgLi
     if (!listener.isObject())
         return jsUndefined();
 
-    impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
+    impl()->removeEventListener(args.at(0).toString(exec), JSEventListener::create(asObject(listener), this, false, currentWorld(exec)).get(), args.at(2).toBoolean(exec));
     return jsUndefined();
 }
 
diff --git a/WebCore/bindings/js/ScriptEventListener.cpp b/WebCore/bindings/js/ScriptEventListener.cpp
index 4325dc3..fd45546 100644
--- a/WebCore/bindings/js/ScriptEventListener.cpp
+++ b/WebCore/bindings/js/ScriptEventListener.cpp
@@ -37,6 +37,7 @@
 #include "JSNode.h"
 #include "Frame.h"
 #include "XSSAuditor.h"
+#include <runtime/JSLock.h>
 
 using namespace JSC;
 
@@ -58,6 +59,7 @@ PassRefPtr<JSLazyEventListener> createAttributeEventListener(Node* node, Attribu
 
     int lineNumber = 1;
     String sourceURL;
+    JSObject* wrapper = 0;
     
     // FIXME: We should be able to provide accurate source information for frameless documents, too (e.g. for importing nodes from XMLHttpRequest.responseXML).
     if (Frame* frame = node->document()->frame()) {
@@ -72,9 +74,13 @@ PassRefPtr<JSLazyEventListener> createAttributeEventListener(Node* node, Attribu
 
         lineNumber = scriptController->eventHandlerLineNumber();
         sourceURL = node->document()->url().string();
+
+        JSC::JSLock lock(SilenceAssertionsOnly);
+        JSDOMGlobalObject* globalObject = toJSDOMGlobalObject(node->document(), mainThreadNormalWorld());
+        wrapper = asObject(toJS(globalObject->globalExec(), globalObject, node));
     }
 
-    return JSLazyEventListener::create(attr->localName().string(), eventParameterName(node->isSVGElement()), attr->value(), node, sourceURL, lineNumber, mainThreadNormalWorld());
+    return JSLazyEventListener::create(attr->localName().string(), eventParameterName(node->isSVGElement()), attr->value(), node, sourceURL, lineNumber, wrapper, mainThreadNormalWorld());
 }
 
 PassRefPtr<JSLazyEventListener> createAttributeEventListener(Frame* frame, Attribute* attr)
@@ -100,15 +106,19 @@ PassRefPtr<JSLazyEventListener> createAttributeEventListener(Frame* frame, Attri
 
     lineNumber = scriptController->eventHandlerLineNumber();
     sourceURL = frame->document()->url().string();
-    return JSLazyEventListener::create(attr->localName().string(), eventParameterName(frame->document()->isSVGDocument()), attr->value(), 0, sourceURL, lineNumber, mainThreadNormalWorld());
+    JSObject* wrapper = toJSDOMWindow(frame, mainThreadNormalWorld());
+    return JSLazyEventListener::create(attr->localName().string(), eventParameterName(frame->document()->isSVGDocument()), attr->value(), 0, sourceURL, lineNumber, wrapper, mainThreadNormalWorld());
 }
 
 String getEventListenerHandlerBody(ScriptExecutionContext* context, ScriptState* scriptState, EventListener* eventListener)
 {
-    JSC::JSObject* functionObject = eventListener->jsFunction(context);
-    if (!functionObject)
+    const JSEventListener* jsListener = JSEventListener::cast(eventListener);
+    if (!jsListener)
+        return "";
+    JSC::JSObject* jsFunction = jsListener->jsFunction(context);
+    if (!jsFunction)
         return "";
-    return functionObject->toString(scriptState);
+    return jsFunction->toString(scriptState);
 }
 
 } // namespace WebCore
diff --git a/WebCore/bindings/scripts/CodeGeneratorJS.pm b/WebCore/bindings/scripts/CodeGeneratorJS.pm
index c774a57..cb60b3c 100644
--- a/WebCore/bindings/scripts/CodeGeneratorJS.pm
+++ b/WebCore/bindings/scripts/CodeGeneratorJS.pm
@@ -1198,7 +1198,7 @@ sub GenerateImplementation
 
         if ($eventTarget) {
             $implIncludes{"RegisteredEventListener.h"} = 1;
-            push(@implContent, "    impl()->invalidateEventListeners();\n");
+            push(@implContent, "    impl()->invalidateJSEventListeners(this);\n");
         }
 
         if (!$dataNode->extendedAttributes->{"ExtendsDOMGlobalObject"}) {
@@ -1218,7 +1218,7 @@ sub GenerateImplementation
         push(@implContent, "void ${className}::markChildren(MarkStack& markStack)\n");
         push(@implContent, "{\n");
         push(@implContent, "    Base::markChildren(markStack);\n");
-        push(@implContent, "    impl()->markEventListeners(markStack);\n");
+        push(@implContent, "    impl()->markJSEventListeners(markStack);\n");
         push(@implContent, "}\n\n");
     }
 
@@ -1318,12 +1318,14 @@ sub GenerateImplementation
                     push(@implContent, "    UNUSED_PARAM(exec);\n");
                     push(@implContent, "    $implClassName* imp = static_cast<$implClassName*>(castedThis->impl());\n");
                     push(@implContent, "    if (EventListener* listener = imp->$implGetterFunctionName()) {\n");
+                    push(@implContent, "        if (const JSEventListener* jsListener = JSEventListener::cast(listener)) {\n");
                     if ($implClassName eq "Document" || $implClassName eq "WorkerContext" || $implClassName eq "SharedWorkerContext" || $implClassName eq "DedicatedWorkerContext") {
-                        push(@implContent, "        if (JSObject* jsFunction = listener->jsFunction(imp))\n");
+                        push(@implContent, "            if (JSObject* jsFunction = jsListener->jsFunction(imp))\n");
                     } else {
-                        push(@implContent, "        if (JSObject* jsFunction = listener->jsFunction(imp->scriptExecutionContext()))\n");
+                        push(@implContent, "            if (JSObject* jsFunction = jsListener->jsFunction(imp->scriptExecutionContext()))\n");
                     }
-                    push(@implContent, "            return jsFunction;\n");
+                    push(@implContent, "                return jsFunction;\n");
+                    push(@implContent, "        }\n");
                     push(@implContent, "    }\n");
                     push(@implContent, "    return jsNull();\n");
                 } elsif ($attribute->signature->type =~ /Constructor$/) {
@@ -1477,7 +1479,7 @@ sub GenerateImplementation
                             $implIncludes{"JSEventListener.h"} = 1;
                             push(@implContent, "    UNUSED_PARAM(exec);\n");
                             push(@implContent, "    $implClassName* imp = static_cast<$implClassName*>(static_cast<$className*>(thisObject)->impl());\n");
-                            push(@implContent, "    imp->set$implSetterFunctionName(createJSAttributeEventListener(exec, value));\n");
+                            push(@implContent, "    imp->set$implSetterFunctionName(createJSAttributeEventListener(exec, value, thisObject));\n");
                         } elsif ($attribute->signature->type =~ /Constructor$/) {
                             my $constructorType = $attribute->signature->type;
                             $constructorType =~ s/Constructor$//;
diff --git a/WebCore/dom/EventListener.h b/WebCore/dom/EventListener.h
index f834b31..8a35922 100644
--- a/WebCore/dom/EventListener.h
+++ b/WebCore/dom/EventListener.h
@@ -51,8 +51,8 @@ namespace WebCore {
         virtual bool wasCreatedFromMarkup() const { return false; }
 
 #if USE(JSC)
-        virtual JSC::JSObject* jsFunction(ScriptExecutionContext*) const { return 0; }
         virtual void markJSFunction(JSC::MarkStack&) { }
+        virtual void invalidateJSFunction(JSC::JSObject*) { }
 #endif
 
         bool isAttribute() const { return virtualisAttribute(); }
diff --git a/WebCore/dom/EventTarget.h b/WebCore/dom/EventTarget.h
index fa5ca09..ece0f34 100644
--- a/WebCore/dom/EventTarget.h
+++ b/WebCore/dom/EventTarget.h
@@ -140,8 +140,8 @@ namespace WebCore {
         bool isFiringEventListeners();
 
 #if USE(JSC)
-        void markEventListeners(JSC::MarkStack&);
-        void invalidateEventListeners();
+        void markJSEventListeners(JSC::MarkStack&);
+        void invalidateJSEventListeners(JSC::JSObject*);
 #endif
 
     protected:
@@ -185,7 +185,7 @@ namespace WebCore {
 #endif
 
 #if USE(JSC)
-    inline void EventTarget::markEventListeners(JSC::MarkStack& markStack)
+    inline void EventTarget::markJSEventListeners(JSC::MarkStack& markStack)
     {
         EventTargetData* d = eventTargetData();
         if (!d)
@@ -199,14 +199,18 @@ namespace WebCore {
         }
     }
 
-    inline void EventTarget::invalidateEventListeners()
+    inline void EventTarget::invalidateJSEventListeners(JSC::JSObject* wrapper)
     {
         EventTargetData* d = eventTargetData();
         if (!d)
             return;
 
-        deleteAllValues(d->eventListenerMap);
-        d->eventListenerMap.clear();
+        EventListenerMap::iterator end = d->eventListenerMap.end();
+        for (EventListenerMap::iterator it = d->eventListenerMap.begin(); it != end; ++it) {
+            EventListenerVector& entry = *it->second;
+            for (size_t i = 0; i < entry.size(); ++i)
+                entry[i].listener->invalidateJSFunction(wrapper);
+        }
     }
 #endif
 

-- 
WebKit Debian packaging



More information about the Pkg-webkit-commits mailing list