<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[287293] trunk/Source/WebCore</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/287293">287293</a></dd>
<dt>Author</dt> <dd>commit-queue@webkit.org</dd>
<dt>Date</dt> <dd>2021-12-20 18:15:25 -0800 (Mon, 20 Dec 2021)</dd>
</dl>

<h3>Log Message</h3>
<pre>Introduce a fast path for replacing an attribute event listener
https://bugs.webkit.org/show_bug.cgi?id=234441

Patch by Alexey Shvayka <ashvayka@apple.com> 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:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCorebindingsjsJSErrorHandlerh">trunk/Source/WebCore/bindings/js/JSErrorHandler.h</a></li>
<li><a href="#trunkSourceWebCorebindingsjsJSEventListenercpp">trunk/Source/WebCore/bindings/js/JSEventListener.cpp</a></li>
<li><a href="#trunkSourceWebCorebindingsjsJSEventListenerh">trunk/Source/WebCore/bindings/js/JSEventListener.h</a></li>
<li><a href="#trunkSourceWebCorebindingsscriptsCodeGeneratorJSpm">trunk/Source/WebCore/bindings/scripts/CodeGeneratorJS.pm</a></li>
<li><a href="#trunkSourceWebCorebindingsscriptstestJSJSTestDefaultToJSONcpp">trunk/Source/WebCore/bindings/scripts/test/JS/JSTestDefaultToJSON.cpp</a></li>
<li><a href="#trunkSourceWebCorebindingsscriptstestJSJSTestObjcpp">trunk/Source/WebCore/bindings/scripts/test/JS/JSTestObj.cpp</a></li>
<li><a href="#trunkSourceWebCoredomDocumentcpp">trunk/Source/WebCore/dom/Document.cpp</a></li>
<li><a href="#trunkSourceWebCoredomDocumenth">trunk/Source/WebCore/dom/Document.h</a></li>
<li><a href="#trunkSourceWebCoredomEventTargetcpp">trunk/Source/WebCore/dom/EventTarget.cpp</a></li>
<li><a href="#trunkSourceWebCoredomEventTargeth">trunk/Source/WebCore/dom/EventTarget.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (287292 => 287293)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -1,3 +1,61 @@
</span><ins>+2021-12-20  Alexey Shvayka  <ashvayka@apple.com>
+
+        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:
+
</ins><span class="cx"> 2021-12-19  Simon Fraser  <simon.fraser@apple.com>
</span><span class="cx"> 
</span><span class="cx">         Remove EventHandler::scrollDistance()
</span></span></pre></div>
<a id="trunkSourceWebCorebindingsjsJSErrorHandlerh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/bindings/js/JSErrorHandler.h (287292 => 287293)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -45,13 +45,4 @@
</span><span class="cx">     void handleEvent(ScriptExecutionContext&, Event&) final;
</span><span class="cx"> };
</span><span class="cx"> 
</span><del>-// 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));
-}
-
</del><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCorebindingsjsJSEventListenercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/bindings/js/JSEventListener.cpp (287292 => 287293)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -61,9 +61,9 @@
</span><span class="cx"> 
</span><span class="cx"> JSEventListener::~JSEventListener() = default;
</span><span class="cx"> 
</span><del>-Ref<JSEventListener> JSEventListener::create(JSC::JSObject* listener, JSC::JSObject* wrapper, bool isAttribute, DOMWrapperWorld& world)
</del><ins>+Ref<JSEventListener> JSEventListener::create(JSC::JSObject& listener, JSC::JSObject& wrapper, bool isAttribute, DOMWrapperWorld& world)
</ins><span class="cx"> {
</span><del>-    return adoptRef(*new JSEventListener(listener, wrapper, isAttribute, world));
</del><ins>+    return adoptRef(*new JSEventListener(&listener, &wrapper, isAttribute, world));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> RefPtr<JSEventListener> JSEventListener::create(JSC::JSValue listener, JSC::JSObject& wrapper, bool isAttribute, DOMWrapperWorld& world)
</span><span class="lines">@@ -71,7 +71,7 @@
</span><span class="cx">     if (UNLIKELY(!listener.isObject()))
</span><span class="cx">         return nullptr;
</span><span class="cx"> 
</span><del>-    return create(JSC::asObject(listener), &wrapper, isAttribute, world);
</del><ins>+    return adoptRef(*new JSEventListener(asObject(listener), &wrapper, isAttribute, world));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> JSObject* JSEventListener::initializeJSFunction(ScriptExecutionContext&) const
</span><span class="lines">@@ -79,6 +79,29 @@
</span><span class="cx">     return nullptr;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+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();
+}
+
</ins><span class="cx"> template<typename Visitor>
</span><span class="cx"> inline void JSEventListener::visitJSFunctionImpl(Visitor& visitor)
</span><span class="cx"> {
</span><span class="lines">@@ -262,58 +285,4 @@
</span><span class="cx">     return handlerFunction->name(vm);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-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);
-}
-
</del><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCorebindingsjsJSEventListenerh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/bindings/js/JSEventListener.h (287292 => 287293)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -19,8 +19,11 @@
</span><span class="cx"> 
</span><span class="cx"> #pragma once
</span><span class="cx"> 
</span><ins>+#include "DOMWindow.h"
</ins><span class="cx"> #include "DOMWrapperWorld.h"
</span><span class="cx"> #include "EventListener.h"
</span><ins>+#include "EventNames.h"
+#include "HTMLElement.h"
</ins><span class="cx"> #include <JavaScriptCore/StrongInlines.h>
</span><span class="cx"> #include <JavaScriptCore/Weak.h>
</span><span class="cx"> #include <JavaScriptCore/WeakInlines.h>
</span><span class="lines">@@ -31,14 +34,9 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><del>-class DOMWindow;
-class Document;
-class EventTarget;
-class HTMLElement;
-
</del><span class="cx"> class JSEventListener : public EventListener {
</span><span class="cx"> public:
</span><del>-    WEBCORE_EXPORT static Ref<JSEventListener> create(JSC::JSObject* listener, JSC::JSObject* wrapper, bool isAttribute, DOMWrapperWorld&);
</del><ins>+    WEBCORE_EXPORT static Ref<JSEventListener> create(JSC::JSObject& listener, JSC::JSObject& wrapper, bool isAttribute, DOMWrapperWorld&);
</ins><span class="cx">     WEBCORE_EXPORT static RefPtr<JSEventListener> create(JSC::JSValue listener, JSC::JSObject& wrapper, bool isAttribute, DOMWrapperWorld&);
</span><span class="cx"> 
</span><span class="cx">     virtual ~JSEventListener();
</span><span class="lines">@@ -60,6 +58,8 @@
</span><span class="cx"> 
</span><span class="cx">     String functionName() const;
</span><span class="cx"> 
</span><ins>+    void replaceJSFunctionForAttributeListener(JSC::JSObject* function, JSC::JSObject* wrapper);
+
</ins><span class="cx"> private:
</span><span class="cx">     virtual JSC::JSObject* initializeJSFunction(ScriptExecutionContext&) const;
</span><span class="cx"> 
</span><span class="lines">@@ -84,14 +84,39 @@
</span><span class="cx"> 
</span><span class="cx"> // For "onxxx" attributes that automatically set up JavaScript event listeners.
</span><span class="cx"> JSC::JSValue eventHandlerAttribute(EventTarget&, const AtomString& eventType, DOMWrapperWorld&);
</span><del>-void setEventHandlerAttribute(JSC::JSGlobalObject&, JSC::JSObject&, EventTarget&, const AtomString& eventType, JSC::JSValue);
</del><span class="cx"> 
</span><ins>+template<typename JSMaybeErrorEventListener>
+inline void setEventHandlerAttribute(EventTarget& eventTarget, const AtomString& eventType, JSC::JSValue listener, JSC::JSObject& jsEventTarget)
+{
+    eventTarget.setAttributeEventListener<JSMaybeErrorEventListener>(eventType, listener, jsEventTarget);
+}
+
</ins><span class="cx"> // Like the functions above, but for attributes that forward event handlers to the window object rather than setting them on the target.
</span><del>-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);
</del><ins>+inline JSC::JSValue windowEventHandlerAttribute(DOMWindow& window, const AtomString& eventType, DOMWrapperWorld& isolatedWorld)
+{
+    return eventHandlerAttribute(window, eventType, isolatedWorld);
+}
</ins><span class="cx"> 
</span><ins>+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);
+}
+
</ins><span class="cx"> inline JSC::JSObject* JSEventListener::ensureJSFunction(ScriptExecutionContext& scriptExecutionContext) const
</span><span class="cx"> {
</span><span class="cx">     // initializeJSFunction can trigger code that deletes this event listener
</span></span></pre></div>
<a id="trunkSourceWebCorebindingsscriptsCodeGeneratorJSpm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/bindings/scripts/CodeGeneratorJS.pm (287292 => 287293)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -5438,11 +5438,11 @@
</span><span class="cx">         # FIXME: Find a way to do this special case without hardcoding the class and attribute names here.
</span><span class="cx">         if (($interface->type->name eq "DOMWindow" or $interface->type->name eq "WorkerGlobalScope") and $attribute->name eq "onerror") {
</span><span class="cx">             AddToImplIncludes("JSErrorHandler.h", $conditional);
</span><del>-            push(@$outputArray, "    thisObject.wrapped().setAttributeEventListener($eventName, createJSErrorHandler(lexicalGlobalObject, value, thisObject), worldForDOMObject(thisObject));\n");
</del><ins>+            push(@$outputArray, "    setEventHandlerAttribute<JSErrorHandler>(thisObject.wrapped(), ${eventName}, value, thisObject);\n");
</ins><span class="cx">         } else {
</span><span class="cx">             AddToImplIncludes("JSEventListener.h", $conditional);
</span><span class="cx">             my $setter = $attribute->extendedAttributes->{WindowEventHandler} ? "setWindowEventHandlerAttribute" : "setEventHandlerAttribute";
</span><del>-            push(@$outputArray, "    $setter(lexicalGlobalObject, thisObject, thisObject.wrapped(), ${eventName}, value);\n");
</del><ins>+            push(@$outputArray, "    $setter<JSEventListener>(thisObject.wrapped(), ${eventName}, value, thisObject);\n");
</ins><span class="cx">         }
</span><span class="cx">         push(@$outputArray, "    vm.writeBarrier(&thisObject, value);\n");
</span><span class="cx">         push(@$outputArray, "    ensureStillAliveHere(value);\n\n");
</span></span></pre></div>
<a id="trunkSourceWebCorebindingsscriptstestJSJSTestDefaultToJSONcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/bindings/scripts/test/JS/JSTestDefaultToJSON.cpp (287292 => 287293)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -318,7 +318,7 @@
</span><span class="cx"> static inline bool setJSTestDefaultToJSON_eventHandlerAttributeSetter(JSGlobalObject& lexicalGlobalObject, JSTestDefaultToJSON& thisObject, JSValue value)
</span><span class="cx"> {
</span><span class="cx">     auto& vm = JSC::getVM(&lexicalGlobalObject);
</span><del>-    setEventHandlerAttribute(lexicalGlobalObject, thisObject, thisObject.wrapped(), eventNames().entHandlerAttributeEvent, value);
</del><ins>+    setEventHandlerAttribute<JSEventListener>(thisObject.wrapped(), eventNames().entHandlerAttributeEvent, value, thisObject);
</ins><span class="cx">     vm.writeBarrier(&thisObject, value);
</span><span class="cx">     ensureStillAliveHere(value);
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCorebindingsscriptstestJSJSTestObjcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/bindings/scripts/test/JS/JSTestObj.cpp (287292 => 287293)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -4178,7 +4178,7 @@
</span><span class="cx"> static inline bool setJSTestObj_onfooSetter(JSGlobalObject& lexicalGlobalObject, JSTestObj& thisObject, JSValue value)
</span><span class="cx"> {
</span><span class="cx">     auto& vm = JSC::getVM(&lexicalGlobalObject);
</span><del>-    setEventHandlerAttribute(lexicalGlobalObject, thisObject, thisObject.wrapped(), eventNames().fooEvent, value);
</del><ins>+    setEventHandlerAttribute<JSEventListener>(thisObject.wrapped(), eventNames().fooEvent, value, thisObject);
</ins><span class="cx">     vm.writeBarrier(&thisObject, value);
</span><span class="cx">     ensureStillAliveHere(value);
</span><span class="cx"> 
</span><span class="lines">@@ -4204,7 +4204,7 @@
</span><span class="cx"> static inline bool setJSTestObj_onwebkitfooSetter(JSGlobalObject& lexicalGlobalObject, JSTestObj& thisObject, JSValue value)
</span><span class="cx"> {
</span><span class="cx">     auto& vm = JSC::getVM(&lexicalGlobalObject);
</span><del>-    setEventHandlerAttribute(lexicalGlobalObject, thisObject, thisObject.wrapped(), eventNames().fooEvent, value);
</del><ins>+    setEventHandlerAttribute<JSEventListener>(thisObject.wrapped(), eventNames().fooEvent, value, thisObject);
</ins><span class="cx">     vm.writeBarrier(&thisObject, value);
</span><span class="cx">     ensureStillAliveHere(value);
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoredomDocumentcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Document.cpp (287292 => 287293)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -5104,13 +5104,6 @@
</span><span class="cx">     setAttributeEventListener(eventType, JSLazyEventListener::create(*this, attributeName, attributeValue), isolatedWorld);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void Document::setWindowAttributeEventListener(const AtomString& eventType, RefPtr<EventListener>&& listener, DOMWrapperWorld& isolatedWorld)
-{
-    if (!m_domWindow)
-        return;
-    m_domWindow->setAttributeEventListener(eventType, WTFMove(listener), isolatedWorld);
-}
-
</del><span class="cx"> void Document::setWindowAttributeEventListener(const AtomString& eventType, const QualifiedName& attributeName, const AtomString& attributeValue, DOMWrapperWorld& isolatedWorld)
</span><span class="cx"> {
</span><span class="cx">     if (!m_domWindow)
</span><span class="lines">@@ -5117,16 +5110,9 @@
</span><span class="cx">         return;
</span><span class="cx">     if (!m_domWindow->frame())
</span><span class="cx">         return;
</span><del>-    setWindowAttributeEventListener(eventType, JSLazyEventListener::create(*m_domWindow, attributeName, attributeValue), isolatedWorld);
</del><ins>+    m_domWindow->setAttributeEventListener(eventType, JSLazyEventListener::create(*m_domWindow, attributeName, attributeValue), isolatedWorld);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-EventListener* Document::getWindowAttributeEventListener(const AtomString& eventType, DOMWrapperWorld& isolatedWorld)
-{
-    if (!m_domWindow)
-        return nullptr;
-    return m_domWindow->attributeEventListener(eventType, isolatedWorld);
-}
-
</del><span class="cx"> void Document::dispatchWindowEvent(Event& event, EventTarget* target)
</span><span class="cx"> {
</span><span class="cx">     ASSERT_WITH_SECURITY_IMPLICATION(ScriptDisallowedScope::InMainThread::isScriptAllowed());
</span></span></pre></div>
<a id="trunkSourceWebCoredomDocumenth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Document.h (287292 => 287293)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -878,8 +878,6 @@
</span><span class="cx"> 
</span><span class="cx">     // Helper functions for forwarding DOMWindow event related tasks to the DOMWindow if it exists.
</span><span class="cx">     void setWindowAttributeEventListener(const AtomString& eventType, const QualifiedName& attributeName, const AtomString& value, DOMWrapperWorld&);
</span><del>-    void setWindowAttributeEventListener(const AtomString& eventType, RefPtr<EventListener>&&, DOMWrapperWorld&);
-    EventListener* getWindowAttributeEventListener(const AtomString& eventType, DOMWrapperWorld&);
</del><span class="cx">     WEBCORE_EXPORT void dispatchWindowEvent(Event&, EventTarget* = nullptr);
</span><span class="cx">     void dispatchWindowLoadEvent();
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoredomEventTargetcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/EventTarget.cpp (287292 => 287293)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -39,8 +39,8 @@
</span><span class="cx"> #include "HTMLBodyElement.h"
</span><span class="cx"> #include "HTMLHtmlElement.h"
</span><span class="cx"> #include "InspectorInstrumentation.h"
</span><ins>+#include "JSErrorHandler.h"
</ins><span class="cx"> #include "JSEventListener.h"
</span><del>-#include "JSLazyEventListener.h"
</del><span class="cx"> #include "Logging.h"
</span><span class="cx"> #include "Quirks.h"
</span><span class="cx"> #include "ScriptController.h"
</span><span class="lines">@@ -160,6 +160,27 @@
</span><span class="cx">     return false;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+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);
+
</ins><span class="cx"> bool EventTarget::setAttributeEventListener(const AtomString& eventType, RefPtr<EventListener>&& listener, DOMWrapperWorld& isolatedWorld)
</span><span class="cx"> {
</span><span class="cx">     auto* existingListener = attributeEventListener(eventType, isolatedWorld);
</span><span class="lines">@@ -185,16 +206,16 @@
</span><span class="cx">     return addEventListener(eventType, listener.releaseNonNull(), { });
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-EventListener* EventTarget::attributeEventListener(const AtomString& eventType, DOMWrapperWorld& isolatedWorld)
</del><ins>+JSEventListener* EventTarget::attributeEventListener(const AtomString& eventType, DOMWrapperWorld& isolatedWorld)
</ins><span class="cx"> {
</span><span class="cx">     for (auto& eventListener : eventListeners(eventType)) {
</span><span class="cx">         auto& listener = eventListener->callback();
</span><del>-        if (!listener.isAttribute())
</del><ins>+        if (listener.type() != EventListener::JSEventListenerType)
</ins><span class="cx">             continue;
</span><span class="cx"> 
</span><del>-        auto& listenerWorld = downcast<JSEventListener>(listener).isolatedWorld();
-        if (&listenerWorld == &isolatedWorld)
-            return &listener;
</del><ins>+        auto& jsListener = downcast<JSEventListener>(listener);
+        if (jsListener.isAttribute() && &jsListener.isolatedWorld() == &isolatedWorld)
+            return &jsListener;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     return nullptr;
</span></span></pre></div>
<a id="trunkSourceWebCoredomEventTargeth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/EventTarget.h (287292 => 287293)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -41,10 +41,16 @@
</span><span class="cx"> #include <wtf/IsoMalloc.h>
</span><span class="cx"> #include <wtf/WeakPtr.h>
</span><span class="cx"> 
</span><ins>+namespace JSC {
+class JSValue;
+class JSObject;
+}
+
</ins><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><span class="cx"> struct AddEventListenerOptions;
</span><span class="cx"> class DOMWrapperWorld;
</span><ins>+class JSEventListener;
</ins><span class="cx"> 
</span><span class="cx"> struct EventTargetData {
</span><span class="cx">     WTF_MAKE_NONCOPYABLE(EventTargetData); WTF_MAKE_FAST_ALLOCATED;
</span><span class="lines">@@ -82,8 +88,10 @@
</span><span class="cx">     WEBCORE_EXPORT virtual void uncaughtExceptionInEventHandler();
</span><span class="cx"> 
</span><span class="cx">     // Used for legacy "onevent" attributes.
</span><ins>+    template<typename JSMaybeErrorEventListener>
+    void setAttributeEventListener(const AtomString& eventType, JSC::JSValue listener, JSC::JSObject& jsEventTarget);
</ins><span class="cx">     bool setAttributeEventListener(const AtomString& eventType, RefPtr<EventListener>&&, DOMWrapperWorld&);
</span><del>-    EventListener* attributeEventListener(const AtomString& eventType, DOMWrapperWorld&);
</del><ins>+    JSEventListener* attributeEventListener(const AtomString& eventType, DOMWrapperWorld&);
</ins><span class="cx"> 
</span><span class="cx">     bool hasEventListeners() const;
</span><span class="cx">     bool hasEventListeners(const AtomString& eventType) const;
</span></span></pre>
</div>
</div>

</body>
</html>