Title: [287293] trunk/Source/WebCore
Revision
287293
Author
[email protected]
Date
2021-12-20 18:15:25 -0800 (Mon, 20 Dec 2021)

Log Message

Introduce a fast path for replacing an attribute event listener
https://bugs.webkit.org/show_bug.cgi?id=234441

Patch by Alexey Shvayka <[email protected]> on 2021-12-20
Reviewed by Chris Dumez.

This patch makes replacing attribute event listener (via EventHandler IDL attribute)
2.6x faster by avoiding creation of intermediate JSEventListener instance.

Reusing is safe even for JSErrorHandler listeners as they can be replaced only with
instances of the same class. Uninitialized JSLazyEventListener can also be "replaced"
if m_isInitialized if set, which makes it behave like a regular JSEventListener.
All this is caught by existing tests.

Additionaly, this change slightly (about 3% according to a microbenchmark) speeds up
lookup of attribute event listeners by removing virtual isAttribute() call and related
downcasts from the hot path. Also, inlines event handler's getters / setters,
and simplifies call forwarding.

Altogether, this patch improves Speedometer2/Inferno-TodoMVC score by 4%.

No new tests, no behavior change.

* bindings/js/JSErrorHandler.h:
(WebCore::createJSErrorHandler): Deleted.
* bindings/js/JSEventListener.cpp:
(WebCore::JSEventListener::create):
(WebCore::JSEventListener::replaceJSFunctionForAttributeListener):
(WebCore::eventHandlerAttribute):
(WebCore::createEventListenerForEventHandlerAttribute): Deleted.
(WebCore::setEventHandlerAttribute): Deleted.
(WebCore::windowEventHandlerAttribute): Deleted.
(WebCore::setWindowEventHandlerAttribute): Deleted.
* bindings/js/JSEventListener.h:

Although setWindowEventHandlerAttribute<JSErrorHandler> is currently unused, it's
templatized to accommodate a follow-up patch that will fix a web-compat issue.
This change carefully preserves current (slightly incorrect) `onerror` behavior.

While we don't care about performance of `onerror`, using templates improves uniformity
(aligns signatures of create() methods) and will simplify code generation in the follow-up.

(WebCore::setEventHandlerAttribute):
(WebCore::windowEventHandlerAttribute):
(WebCore::setWindowEventHandlerAttribute):
* bindings/scripts/CodeGeneratorJS.pm:
(GenerateAttributeSetterBodyDefinition):
* bindings/scripts/test/JS/*: Updated.
* dom/Document.cpp:
(WebCore::Document::setWindowAttributeEventListener):
(WebCore::Document::getWindowAttributeEventListener): Deleted.
* dom/Document.h:
* dom/EventTarget.cpp:
(WebCore::EventTarget::setAttributeEventListener):
(WebCore::EventTarget::attributeEventListener):
* dom/EventTarget.h:

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (287292 => 287293)


--- trunk/Source/WebCore/ChangeLog	2021-12-21 01:31:08 UTC (rev 287292)
+++ trunk/Source/WebCore/ChangeLog	2021-12-21 02:15:25 UTC (rev 287293)
@@ -1,3 +1,61 @@
+2021-12-20  Alexey Shvayka  <[email protected]>
+
+        Introduce a fast path for replacing an attribute event listener
+        https://bugs.webkit.org/show_bug.cgi?id=234441
+
+        Reviewed by Chris Dumez.
+
+        This patch makes replacing attribute event listener (via EventHandler IDL attribute)
+        2.6x faster by avoiding creation of intermediate JSEventListener instance.
+
+        Reusing is safe even for JSErrorHandler listeners as they can be replaced only with
+        instances of the same class. Uninitialized JSLazyEventListener can also be "replaced"
+        if m_isInitialized if set, which makes it behave like a regular JSEventListener.
+        All this is caught by existing tests.
+
+        Additionaly, this change slightly (about 3% according to a microbenchmark) speeds up
+        lookup of attribute event listeners by removing virtual isAttribute() call and related
+        downcasts from the hot path. Also, inlines event handler's getters / setters,
+        and simplifies call forwarding.
+
+        Altogether, this patch improves Speedometer2/Inferno-TodoMVC score by 4%.
+
+        No new tests, no behavior change.
+
+        * bindings/js/JSErrorHandler.h:
+        (WebCore::createJSErrorHandler): Deleted.
+        * bindings/js/JSEventListener.cpp:
+        (WebCore::JSEventListener::create):
+        (WebCore::JSEventListener::replaceJSFunctionForAttributeListener):
+        (WebCore::eventHandlerAttribute):
+        (WebCore::createEventListenerForEventHandlerAttribute): Deleted.
+        (WebCore::setEventHandlerAttribute): Deleted.
+        (WebCore::windowEventHandlerAttribute): Deleted.
+        (WebCore::setWindowEventHandlerAttribute): Deleted.
+        * bindings/js/JSEventListener.h:
+
+        Although setWindowEventHandlerAttribute<JSErrorHandler> is currently unused, it's
+        templatized to accommodate a follow-up patch that will fix a web-compat issue.
+        This change carefully preserves current (slightly incorrect) `onerror` behavior.
+
+        While we don't care about performance of `onerror`, using templates improves uniformity
+        (aligns signatures of create() methods) and will simplify code generation in the follow-up.
+
+        (WebCore::setEventHandlerAttribute):
+        (WebCore::windowEventHandlerAttribute):
+        (WebCore::setWindowEventHandlerAttribute):
+        * bindings/scripts/CodeGeneratorJS.pm:
+        (GenerateAttributeSetterBodyDefinition):
+        * bindings/scripts/test/JS/*: Updated.
+        * dom/Document.cpp:
+        (WebCore::Document::setWindowAttributeEventListener):
+        (WebCore::Document::getWindowAttributeEventListener): Deleted.
+        * dom/Document.h:
+        * dom/EventTarget.cpp:
+        (WebCore::EventTarget::setAttributeEventListener):
+        (WebCore::EventTarget::attributeEventListener):
+        * dom/EventTarget.h:
+
 2021-12-19  Simon Fraser  <[email protected]>
 
         Remove EventHandler::scrollDistance()

Modified: trunk/Source/WebCore/bindings/js/JSErrorHandler.h (287292 => 287293)


--- trunk/Source/WebCore/bindings/js/JSErrorHandler.h	2021-12-21 01:31:08 UTC (rev 287292)
+++ trunk/Source/WebCore/bindings/js/JSErrorHandler.h	2021-12-21 02:15:25 UTC (rev 287293)
@@ -45,13 +45,4 @@
     void handleEvent(ScriptExecutionContext&, Event&) final;
 };
 
-// Creates a listener for "onerror" event handler.
-// It has custom implementation because, unlike other event listeners, it accepts three parameters.
-inline RefPtr<JSErrorHandler> createJSErrorHandler(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue listener, JSC::JSObject& wrapper)
-{
-    if (!listener.isObject())
-        return nullptr;
-    return JSErrorHandler::create(*asObject(listener), wrapper, true, currentWorld(lexicalGlobalObject));
-}
-
 } // namespace WebCore

Modified: trunk/Source/WebCore/bindings/js/JSEventListener.cpp (287292 => 287293)


--- trunk/Source/WebCore/bindings/js/JSEventListener.cpp	2021-12-21 01:31:08 UTC (rev 287292)
+++ trunk/Source/WebCore/bindings/js/JSEventListener.cpp	2021-12-21 02:15:25 UTC (rev 287293)
@@ -61,9 +61,9 @@
 
 JSEventListener::~JSEventListener() = default;
 
-Ref<JSEventListener> JSEventListener::create(JSC::JSObject* listener, JSC::JSObject* wrapper, bool isAttribute, DOMWrapperWorld& world)
+Ref<JSEventListener> JSEventListener::create(JSC::JSObject& listener, JSC::JSObject& wrapper, bool isAttribute, DOMWrapperWorld& world)
 {
-    return adoptRef(*new JSEventListener(listener, wrapper, isAttribute, world));
+    return adoptRef(*new JSEventListener(&listener, &wrapper, isAttribute, world));
 }
 
 RefPtr<JSEventListener> JSEventListener::create(JSC::JSValue listener, JSC::JSObject& wrapper, bool isAttribute, DOMWrapperWorld& world)
@@ -71,7 +71,7 @@
     if (UNLIKELY(!listener.isObject()))
         return nullptr;
 
-    return create(JSC::asObject(listener), &wrapper, isAttribute, world);
+    return adoptRef(*new JSEventListener(asObject(listener), &wrapper, isAttribute, world));
 }
 
 JSObject* JSEventListener::initializeJSFunction(ScriptExecutionContext&) const
@@ -79,6 +79,29 @@
     return nullptr;
 }
 
+void JSEventListener::replaceJSFunctionForAttributeListener(JSObject* function, JSObject* wrapper)
+{
+    ASSERT(m_isAttribute);
+    ASSERT(function);
+    ASSERT(wrapper);
+
+    m_jsFunction = Weak { function };
+    m_wrapper = wrapper;
+    m_isInitialized = true;
+}
+
+JSValue eventHandlerAttribute(EventTarget& eventTarget, const AtomString& eventType, DOMWrapperWorld& isolatedWorld)
+{
+    if (auto* jsListener = eventTarget.attributeEventListener(eventType, isolatedWorld)) {
+        if (auto* context = eventTarget.scriptExecutionContext()) {
+            if (auto* jsFunction = jsListener->ensureJSFunction(*context))
+                return jsFunction;
+        }
+    }
+
+    return jsNull();
+}
+
 template<typename Visitor>
 inline void JSEventListener::visitJSFunctionImpl(Visitor& visitor)
 {
@@ -262,58 +285,4 @@
     return handlerFunction->name(vm);
 }
 
-static inline JSC::JSValue eventHandlerAttribute(EventListener* abstractListener, ScriptExecutionContext& context)
-{
-    if (!is<JSEventListener>(abstractListener))
-        return jsNull();
-
-    auto* function = downcast<JSEventListener>(*abstractListener).ensureJSFunction(context);
-    if (!function)
-        return jsNull();
-
-    return function;
-}
-
-static inline RefPtr<JSEventListener> createEventListenerForEventHandlerAttribute(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue listener, JSC::JSObject& wrapper)
-{
-    if (!listener.isObject())
-        return nullptr;
-    return JSEventListener::create(asObject(listener), &wrapper, true, currentWorld(lexicalGlobalObject));
-}
-
-JSC::JSValue eventHandlerAttribute(EventTarget& target, const AtomString& eventType, DOMWrapperWorld& isolatedWorld)
-{
-    auto* context = target.scriptExecutionContext();
-    if (!context)
-        return jsNull();
-    return eventHandlerAttribute(target.attributeEventListener(eventType, isolatedWorld), *context);
-}
-
-void setEventHandlerAttribute(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSObject& wrapper, EventTarget& target, const AtomString& eventType, JSC::JSValue value)
-{
-    target.setAttributeEventListener(eventType, createEventListenerForEventHandlerAttribute(lexicalGlobalObject, value, wrapper), currentWorld(lexicalGlobalObject));
-}
-
-JSC::JSValue windowEventHandlerAttribute(HTMLElement& element, const AtomString& eventType, DOMWrapperWorld& isolatedWorld)
-{
-    auto& document = element.document();
-    return eventHandlerAttribute(document.getWindowAttributeEventListener(eventType, isolatedWorld), document);
-}
-
-void setWindowEventHandlerAttribute(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSObject& wrapper, HTMLElement& element, const AtomString& eventType, JSC::JSValue value)
-{
-    ASSERT(wrapper.globalObject());
-    element.document().setWindowAttributeEventListener(eventType, createEventListenerForEventHandlerAttribute(lexicalGlobalObject, value, *wrapper.globalObject()), currentWorld(lexicalGlobalObject));
-}
-
-JSC::JSValue windowEventHandlerAttribute(DOMWindow& window, const AtomString& eventType, DOMWrapperWorld& isolatedWorld)
-{
-    return eventHandlerAttribute(window, eventType, isolatedWorld);
-}
-
-void setWindowEventHandlerAttribute(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSObject& wrapper, DOMWindow& window, const AtomString& eventType, JSC::JSValue value)
-{
-    setEventHandlerAttribute(lexicalGlobalObject, wrapper, window, eventType, value);
-}
-
 } // namespace WebCore

Modified: trunk/Source/WebCore/bindings/js/JSEventListener.h (287292 => 287293)


--- trunk/Source/WebCore/bindings/js/JSEventListener.h	2021-12-21 01:31:08 UTC (rev 287292)
+++ trunk/Source/WebCore/bindings/js/JSEventListener.h	2021-12-21 02:15:25 UTC (rev 287293)
@@ -19,8 +19,11 @@
 
 #pragma once
 
+#include "DOMWindow.h"
 #include "DOMWrapperWorld.h"
 #include "EventListener.h"
+#include "EventNames.h"
+#include "HTMLElement.h"
 #include <_javascript_Core/StrongInlines.h>
 #include <_javascript_Core/Weak.h>
 #include <_javascript_Core/WeakInlines.h>
@@ -31,14 +34,9 @@
 
 namespace WebCore {
 
-class DOMWindow;
-class Document;
-class EventTarget;
-class HTMLElement;
-
 class JSEventListener : public EventListener {
 public:
-    WEBCORE_EXPORT static Ref<JSEventListener> create(JSC::JSObject* listener, JSC::JSObject* wrapper, bool isAttribute, DOMWrapperWorld&);
+    WEBCORE_EXPORT static Ref<JSEventListener> create(JSC::JSObject& listener, JSC::JSObject& wrapper, bool isAttribute, DOMWrapperWorld&);
     WEBCORE_EXPORT static RefPtr<JSEventListener> create(JSC::JSValue listener, JSC::JSObject& wrapper, bool isAttribute, DOMWrapperWorld&);
 
     virtual ~JSEventListener();
@@ -60,6 +58,8 @@
 
     String functionName() const;
 
+    void replaceJSFunctionForAttributeListener(JSC::JSObject* function, JSC::JSObject* wrapper);
+
 private:
     virtual JSC::JSObject* initializeJSFunction(ScriptExecutionContext&) const;
 
@@ -84,14 +84,39 @@
 
 // For "onxxx" attributes that automatically set up _javascript_ event listeners.
 JSC::JSValue eventHandlerAttribute(EventTarget&, const AtomString& eventType, DOMWrapperWorld&);
-void setEventHandlerAttribute(JSC::JSGlobalObject&, JSC::JSObject&, EventTarget&, const AtomString& eventType, JSC::JSValue);
 
+template<typename JSMaybeErrorEventListener>
+inline void setEventHandlerAttribute(EventTarget& eventTarget, const AtomString& eventType, JSC::JSValue listener, JSC::JSObject& jsEventTarget)
+{
+    eventTarget.setAttributeEventListener<JSMaybeErrorEventListener>(eventType, listener, jsEventTarget);
+}
+
 // Like the functions above, but for attributes that forward event handlers to the window object rather than setting them on the target.
-JSC::JSValue windowEventHandlerAttribute(HTMLElement&, const AtomString& eventType, DOMWrapperWorld&);
-void setWindowEventHandlerAttribute(JSC::JSGlobalObject&, JSC::JSObject&, HTMLElement&, const AtomString& eventType, JSC::JSValue);
-JSC::JSValue windowEventHandlerAttribute(DOMWindow&, const AtomString& eventType, DOMWrapperWorld&);
-void setWindowEventHandlerAttribute(JSC::JSGlobalObject&, JSC::JSObject&, DOMWindow&, const AtomString& eventType, JSC::JSValue);
+inline JSC::JSValue windowEventHandlerAttribute(DOMWindow& window, const AtomString& eventType, DOMWrapperWorld& isolatedWorld)
+{
+    return eventHandlerAttribute(window, eventType, isolatedWorld);
+}
 
+inline JSC::JSValue windowEventHandlerAttribute(HTMLElement& element, const AtomString& eventType, DOMWrapperWorld& isolatedWorld)
+{
+    if (auto* domWindow = element.document().domWindow())
+        return eventHandlerAttribute(*domWindow, eventType, isolatedWorld);
+    return JSC::jsNull();
+}
+
+template<typename JSMaybeErrorEventListener>
+inline void setWindowEventHandlerAttribute(DOMWindow& window, const AtomString& eventType, JSC::JSValue listener, JSC::JSObject& jsEventTarget)
+{
+    window.setAttributeEventListener<JSMaybeErrorEventListener>(eventType, listener, jsEventTarget);
+}
+
+template<typename JSMaybeErrorEventListener>
+inline void setWindowEventHandlerAttribute(HTMLElement& element, const AtomString& eventType, JSC::JSValue listener, JSC::JSObject& jsEventTarget)
+{
+    if (auto* domWindow = element.document().domWindow())
+        setWindowEventHandlerAttribute<JSMaybeErrorEventListener>(*domWindow, eventType, listener, jsEventTarget);
+}
+
 inline JSC::JSObject* JSEventListener::ensureJSFunction(ScriptExecutionContext& scriptExecutionContext) const
 {
     // initializeJSFunction can trigger code that deletes this event listener

Modified: trunk/Source/WebCore/bindings/scripts/CodeGeneratorJS.pm (287292 => 287293)


--- trunk/Source/WebCore/bindings/scripts/CodeGeneratorJS.pm	2021-12-21 01:31:08 UTC (rev 287292)
+++ trunk/Source/WebCore/bindings/scripts/CodeGeneratorJS.pm	2021-12-21 02:15:25 UTC (rev 287293)
@@ -5438,11 +5438,11 @@
         # FIXME: Find a way to do this special case without hardcoding the class and attribute names here.
         if (($interface->type->name eq "DOMWindow" or $interface->type->name eq "WorkerGlobalScope") and $attribute->name eq "onerror") {
             AddToImplIncludes("JSErrorHandler.h", $conditional);
-            push(@$outputArray, "    thisObject.wrapped().setAttributeEventListener($eventName, createJSErrorHandler(lexicalGlobalObject, value, thisObject), worldForDOMObject(thisObject));\n");
+            push(@$outputArray, "    setEventHandlerAttribute<JSErrorHandler>(thisObject.wrapped(), ${eventName}, value, thisObject);\n");
         } else {
             AddToImplIncludes("JSEventListener.h", $conditional);
             my $setter = $attribute->extendedAttributes->{WindowEventHandler} ? "setWindowEventHandlerAttribute" : "setEventHandlerAttribute";
-            push(@$outputArray, "    $setter(lexicalGlobalObject, thisObject, thisObject.wrapped(), ${eventName}, value);\n");
+            push(@$outputArray, "    $setter<JSEventListener>(thisObject.wrapped(), ${eventName}, value, thisObject);\n");
         }
         push(@$outputArray, "    vm.writeBarrier(&thisObject, value);\n");
         push(@$outputArray, "    ensureStillAliveHere(value);\n\n");

Modified: trunk/Source/WebCore/bindings/scripts/test/JS/JSTestDefaultToJSON.cpp (287292 => 287293)


--- trunk/Source/WebCore/bindings/scripts/test/JS/JSTestDefaultToJSON.cpp	2021-12-21 01:31:08 UTC (rev 287292)
+++ trunk/Source/WebCore/bindings/scripts/test/JS/JSTestDefaultToJSON.cpp	2021-12-21 02:15:25 UTC (rev 287293)
@@ -318,7 +318,7 @@
 static inline bool setJSTestDefaultToJSON_eventHandlerAttributeSetter(JSGlobalObject& lexicalGlobalObject, JSTestDefaultToJSON& thisObject, JSValue value)
 {
     auto& vm = JSC::getVM(&lexicalGlobalObject);
-    setEventHandlerAttribute(lexicalGlobalObject, thisObject, thisObject.wrapped(), eventNames().entHandlerAttributeEvent, value);
+    setEventHandlerAttribute<JSEventListener>(thisObject.wrapped(), eventNames().entHandlerAttributeEvent, value, thisObject);
     vm.writeBarrier(&thisObject, value);
     ensureStillAliveHere(value);
 

Modified: trunk/Source/WebCore/bindings/scripts/test/JS/JSTestObj.cpp (287292 => 287293)


--- trunk/Source/WebCore/bindings/scripts/test/JS/JSTestObj.cpp	2021-12-21 01:31:08 UTC (rev 287292)
+++ trunk/Source/WebCore/bindings/scripts/test/JS/JSTestObj.cpp	2021-12-21 02:15:25 UTC (rev 287293)
@@ -4178,7 +4178,7 @@
 static inline bool setJSTestObj_onfooSetter(JSGlobalObject& lexicalGlobalObject, JSTestObj& thisObject, JSValue value)
 {
     auto& vm = JSC::getVM(&lexicalGlobalObject);
-    setEventHandlerAttribute(lexicalGlobalObject, thisObject, thisObject.wrapped(), eventNames().fooEvent, value);
+    setEventHandlerAttribute<JSEventListener>(thisObject.wrapped(), eventNames().fooEvent, value, thisObject);
     vm.writeBarrier(&thisObject, value);
     ensureStillAliveHere(value);
 
@@ -4204,7 +4204,7 @@
 static inline bool setJSTestObj_onwebkitfooSetter(JSGlobalObject& lexicalGlobalObject, JSTestObj& thisObject, JSValue value)
 {
     auto& vm = JSC::getVM(&lexicalGlobalObject);
-    setEventHandlerAttribute(lexicalGlobalObject, thisObject, thisObject.wrapped(), eventNames().fooEvent, value);
+    setEventHandlerAttribute<JSEventListener>(thisObject.wrapped(), eventNames().fooEvent, value, thisObject);
     vm.writeBarrier(&thisObject, value);
     ensureStillAliveHere(value);
 

Modified: trunk/Source/WebCore/dom/Document.cpp (287292 => 287293)


--- trunk/Source/WebCore/dom/Document.cpp	2021-12-21 01:31:08 UTC (rev 287292)
+++ trunk/Source/WebCore/dom/Document.cpp	2021-12-21 02:15:25 UTC (rev 287293)
@@ -5104,13 +5104,6 @@
     setAttributeEventListener(eventType, JSLazyEventListener::create(*this, attributeName, attributeValue), isolatedWorld);
 }
 
-void Document::setWindowAttributeEventListener(const AtomString& eventType, RefPtr<EventListener>&& listener, DOMWrapperWorld& isolatedWorld)
-{
-    if (!m_domWindow)
-        return;
-    m_domWindow->setAttributeEventListener(eventType, WTFMove(listener), isolatedWorld);
-}
-
 void Document::setWindowAttributeEventListener(const AtomString& eventType, const QualifiedName& attributeName, const AtomString& attributeValue, DOMWrapperWorld& isolatedWorld)
 {
     if (!m_domWindow)
@@ -5117,16 +5110,9 @@
         return;
     if (!m_domWindow->frame())
         return;
-    setWindowAttributeEventListener(eventType, JSLazyEventListener::create(*m_domWindow, attributeName, attributeValue), isolatedWorld);
+    m_domWindow->setAttributeEventListener(eventType, JSLazyEventListener::create(*m_domWindow, attributeName, attributeValue), isolatedWorld);
 }
 
-EventListener* Document::getWindowAttributeEventListener(const AtomString& eventType, DOMWrapperWorld& isolatedWorld)
-{
-    if (!m_domWindow)
-        return nullptr;
-    return m_domWindow->attributeEventListener(eventType, isolatedWorld);
-}
-
 void Document::dispatchWindowEvent(Event& event, EventTarget* target)
 {
     ASSERT_WITH_SECURITY_IMPLICATION(ScriptDisallowedScope::InMainThread::isScriptAllowed());

Modified: trunk/Source/WebCore/dom/Document.h (287292 => 287293)


--- trunk/Source/WebCore/dom/Document.h	2021-12-21 01:31:08 UTC (rev 287292)
+++ trunk/Source/WebCore/dom/Document.h	2021-12-21 02:15:25 UTC (rev 287293)
@@ -878,8 +878,6 @@
 
     // Helper functions for forwarding DOMWindow event related tasks to the DOMWindow if it exists.
     void setWindowAttributeEventListener(const AtomString& eventType, const QualifiedName& attributeName, const AtomString& value, DOMWrapperWorld&);
-    void setWindowAttributeEventListener(const AtomString& eventType, RefPtr<EventListener>&&, DOMWrapperWorld&);
-    EventListener* getWindowAttributeEventListener(const AtomString& eventType, DOMWrapperWorld&);
     WEBCORE_EXPORT void dispatchWindowEvent(Event&, EventTarget* = nullptr);
     void dispatchWindowLoadEvent();
 

Modified: trunk/Source/WebCore/dom/EventTarget.cpp (287292 => 287293)


--- trunk/Source/WebCore/dom/EventTarget.cpp	2021-12-21 01:31:08 UTC (rev 287292)
+++ trunk/Source/WebCore/dom/EventTarget.cpp	2021-12-21 02:15:25 UTC (rev 287293)
@@ -39,8 +39,8 @@
 #include "HTMLBodyElement.h"
 #include "HTMLHtmlElement.h"
 #include "InspectorInstrumentation.h"
+#include "JSErrorHandler.h"
 #include "JSEventListener.h"
-#include "JSLazyEventListener.h"
 #include "Logging.h"
 #include "Quirks.h"
 #include "ScriptController.h"
@@ -160,6 +160,27 @@
     return false;
 }
 
+template<typename JSMaybeErrorEventListener>
+void EventTarget::setAttributeEventListener(const AtomString& eventType, JSC::JSValue listener, JSC::JSObject& jsEventTarget)
+{
+    auto& isolatedWorld = worldForDOMObject(jsEventTarget);
+    auto* existingListener = attributeEventListener(eventType, isolatedWorld);
+    if (!listener.isObject()) {
+        if (existingListener)
+            removeEventListener(eventType, *existingListener, false);
+    } else if (existingListener) {
+        bool capture = false;
+
+        InspectorInstrumentation::willRemoveEventListener(*this, eventType, *existingListener, capture);
+        existingListener->replaceJSFunctionForAttributeListener(asObject(listener), &jsEventTarget);
+        InspectorInstrumentation::didAddEventListener(*this, eventType, *existingListener, capture);
+    } else
+        addEventListener(eventType, JSMaybeErrorEventListener::create(*asObject(listener), jsEventTarget, true, isolatedWorld), { });
+}
+
+template void EventTarget::setAttributeEventListener<JSErrorHandler>(const AtomString& eventType, JSC::JSValue listener, JSC::JSObject& jsEventTarget);
+template void EventTarget::setAttributeEventListener<JSEventListener>(const AtomString& eventType, JSC::JSValue listener, JSC::JSObject& jsEventTarget);
+
 bool EventTarget::setAttributeEventListener(const AtomString& eventType, RefPtr<EventListener>&& listener, DOMWrapperWorld& isolatedWorld)
 {
     auto* existingListener = attributeEventListener(eventType, isolatedWorld);
@@ -185,16 +206,16 @@
     return addEventListener(eventType, listener.releaseNonNull(), { });
 }
 
-EventListener* EventTarget::attributeEventListener(const AtomString& eventType, DOMWrapperWorld& isolatedWorld)
+JSEventListener* EventTarget::attributeEventListener(const AtomString& eventType, DOMWrapperWorld& isolatedWorld)
 {
     for (auto& eventListener : eventListeners(eventType)) {
         auto& listener = eventListener->callback();
-        if (!listener.isAttribute())
+        if (listener.type() != EventListener::JSEventListenerType)
             continue;
 
-        auto& listenerWorld = downcast<JSEventListener>(listener).isolatedWorld();
-        if (&listenerWorld == &isolatedWorld)
-            return &listener;
+        auto& jsListener = downcast<JSEventListener>(listener);
+        if (jsListener.isAttribute() && &jsListener.isolatedWorld() == &isolatedWorld)
+            return &jsListener;
     }
 
     return nullptr;

Modified: trunk/Source/WebCore/dom/EventTarget.h (287292 => 287293)


--- trunk/Source/WebCore/dom/EventTarget.h	2021-12-21 01:31:08 UTC (rev 287292)
+++ trunk/Source/WebCore/dom/EventTarget.h	2021-12-21 02:15:25 UTC (rev 287293)
@@ -41,10 +41,16 @@
 #include <wtf/IsoMalloc.h>
 #include <wtf/WeakPtr.h>
 
+namespace JSC {
+class JSValue;
+class JSObject;
+}
+
 namespace WebCore {
 
 struct AddEventListenerOptions;
 class DOMWrapperWorld;
+class JSEventListener;
 
 struct EventTargetData {
     WTF_MAKE_NONCOPYABLE(EventTargetData); WTF_MAKE_FAST_ALLOCATED;
@@ -82,8 +88,10 @@
     WEBCORE_EXPORT virtual void uncaughtExceptionInEventHandler();
 
     // Used for legacy "onevent" attributes.
+    template<typename JSMaybeErrorEventListener>
+    void setAttributeEventListener(const AtomString& eventType, JSC::JSValue listener, JSC::JSObject& jsEventTarget);
     bool setAttributeEventListener(const AtomString& eventType, RefPtr<EventListener>&&, DOMWrapperWorld&);
-    EventListener* attributeEventListener(const AtomString& eventType, DOMWrapperWorld&);
+    JSEventListener* attributeEventListener(const AtomString& eventType, DOMWrapperWorld&);
 
     bool hasEventListeners() const;
     bool hasEventListeners(const AtomString& eventType) const;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to