summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibWeb
diff options
context:
space:
mode:
authorLuke Wilde <lukew@serenityos.org>2021-10-14 18:03:08 +0100
committerLinus Groh <mail@linusgroh.de>2022-02-08 17:47:44 +0000
commit5aacec65ab6da8feb5d2f29405ef4ed3cd89501f (patch)
tree2845921fb0bba58c40dc89acad0e9918657cc596 /Userland/Libraries/LibWeb
parent3bb5c6207fbdbe80041bd603882063165b440e00 (diff)
downloadserenity-5aacec65ab6da8feb5d2f29405ef4ed3cd89501f.zip
LibWeb: Rewrite EventTarget to more closely match the spec
This isn't perfect (especially the global object situation in activate_event_handler), but I believe it's in a much more complete state now :^) This fixes the issue of crashing in prepare_for_ordinary_call with the `i < m_size` crash, as it now uses the IDL callback functions which requires the Environment Settings Object. The environment settings object for the callback is fetched at the time the callback is created, for example, WrapperGenerator gets the incumbent settings object for the callback at the time of wrapping. This allows us to remove passing in ScriptExecutionContext into EventTarget's constructor. With this, we can now drop ScriptExecutionContext.
Diffstat (limited to 'Userland/Libraries/LibWeb')
-rw-r--r--Userland/Libraries/LibWeb/Bindings/IDLAbstractOperations.h166
-rw-r--r--Userland/Libraries/LibWeb/Bindings/ScriptExecutionContext.cpp15
-rw-r--r--Userland/Libraries/LibWeb/Bindings/ScriptExecutionContext.h22
-rw-r--r--Userland/Libraries/LibWeb/Bindings/WindowObject.cpp72
-rw-r--r--Userland/Libraries/LibWeb/CMakeLists.txt2
-rw-r--r--Userland/Libraries/LibWeb/CSS/MediaQueryList.cpp6
-rw-r--r--Userland/Libraries/LibWeb/CSS/MediaQueryList.h4
-rw-r--r--Userland/Libraries/LibWeb/DOM/AbortController.cpp4
-rw-r--r--Userland/Libraries/LibWeb/DOM/AbortController.h10
-rw-r--r--Userland/Libraries/LibWeb/DOM/AbortSignal.cpp10
-rw-r--r--Userland/Libraries/LibWeb/DOM/AbortSignal.h14
-rw-r--r--Userland/Libraries/LibWeb/DOM/Document.h4
-rw-r--r--Userland/Libraries/LibWeb/DOM/EventDispatcher.cpp35
-rw-r--r--Userland/Libraries/LibWeb/DOM/EventListener.cpp18
-rw-r--r--Userland/Libraries/LibWeb/DOM/EventListener.h15
-rw-r--r--Userland/Libraries/LibWeb/DOM/EventTarget.cpp491
-rw-r--r--Userland/Libraries/LibWeb/DOM/EventTarget.h27
-rw-r--r--Userland/Libraries/LibWeb/DOM/Node.cpp2
-rw-r--r--Userland/Libraries/LibWeb/DOM/Timer.cpp12
-rw-r--r--Userland/Libraries/LibWeb/DOM/Timer.h10
-rw-r--r--Userland/Libraries/LibWeb/DOM/Window.cpp28
-rw-r--r--Userland/Libraries/LibWeb/DOM/Window.h8
-rw-r--r--Userland/Libraries/LibWeb/Forward.h2
-rw-r--r--Userland/Libraries/LibWeb/HTML/EventHandler.h24
-rw-r--r--Userland/Libraries/LibWeb/HTML/GlobalEventHandlers.cpp4
-rw-r--r--Userland/Libraries/LibWeb/HTML/GlobalEventHandlers.h6
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLElement.cpp8
-rw-r--r--Userland/Libraries/LibWeb/HTML/MessageChannel.cpp8
-rw-r--r--Userland/Libraries/LibWeb/HTML/MessageChannel.h6
-rw-r--r--Userland/Libraries/LibWeb/HTML/MessagePort.cpp21
-rw-r--r--Userland/Libraries/LibWeb/HTML/MessagePort.h12
-rw-r--r--Userland/Libraries/LibWeb/HTML/WebSocket.cpp18
-rw-r--r--Userland/Libraries/LibWeb/HTML/WebSocket.h6
-rw-r--r--Userland/Libraries/LibWeb/HighResolutionTime/Performance.cpp2
-rw-r--r--Userland/Libraries/LibWeb/XHR/XMLHttpRequest.cpp6
-rw-r--r--Userland/Libraries/LibWeb/XHR/XMLHttpRequest.h4
-rw-r--r--Userland/Libraries/LibWeb/XHR/XMLHttpRequestEventTarget.cpp16
-rw-r--r--Userland/Libraries/LibWeb/XHR/XMLHttpRequestEventTarget.h10
38 files changed, 847 insertions, 281 deletions
diff --git a/Userland/Libraries/LibWeb/Bindings/IDLAbstractOperations.h b/Userland/Libraries/LibWeb/Bindings/IDLAbstractOperations.h
index c6940d6989..0960a029ea 100644
--- a/Userland/Libraries/LibWeb/Bindings/IDLAbstractOperations.h
+++ b/Userland/Libraries/LibWeb/Bindings/IDLAbstractOperations.h
@@ -9,10 +9,176 @@
#include <AK/Forward.h>
#include <LibJS/Forward.h>
+#include <LibJS/Runtime/AbstractOperations.h>
+#include <LibJS/Runtime/FunctionObject.h>
+#include <LibWeb/Bindings/CallbackType.h>
+#include <LibWeb/HTML/Scripting/Environments.h>
namespace Web::Bindings::IDL {
bool is_an_array_index(JS::GlobalObject&, JS::PropertyKey const&);
Optional<ByteBuffer> get_buffer_source_copy(JS::Object const& buffer_source);
+// https://webidl.spec.whatwg.org/#call-user-object-operation-return
+inline JS::Completion clean_up_on_return(HTML::EnvironmentSettingsObject& stored_settings, HTML::EnvironmentSettingsObject& relevant_settings, JS::Completion& completion)
+{
+ // Return: at this point completion will be set to an ECMAScript completion value.
+
+ // 1. Clean up after running a callback with stored settings.
+ stored_settings.clean_up_after_running_callback();
+
+ // 2. Clean up after running script with relevant settings.
+ relevant_settings.clean_up_after_running_script();
+
+ // 3. If completion is a normal completion, return completion.
+ if (completion.type() == JS::Completion::Type::Normal)
+ return completion;
+
+ // 4. If completion is an abrupt completion and the operation has a return type that is not a promise type, return completion.
+ // FIXME: This does not handle promises and thus always returns completion at this point.
+ return completion;
+
+ // FIXME: 5. Let rejectedPromise be ! Call(%Promise.reject%, %Promise%, «completion.[[Value]]»).
+
+ // FIXME: 6. Return the result of converting rejectedPromise to the operation’s return type.
+}
+
+// https://webidl.spec.whatwg.org/#call-a-user-objects-operation
+template<typename... Args>
+JS::Completion call_user_object_operation(Bindings::CallbackType& callback, String const& operation_name, Optional<JS::Value> this_argument, Args&&... args)
+{
+ // 1. Let completion be an uninitialized variable.
+ JS::Completion completion;
+
+ // 2. If thisArg was not given, let thisArg be undefined.
+ if (!this_argument.has_value())
+ this_argument = JS::js_undefined();
+
+ // 3. Let O be the ECMAScript object corresponding to value.
+ auto& object = *callback.callback.cell();
+
+ // 4. Let realm be O’s associated Realm.
+ auto& global_object = object.global_object();
+ auto& realm = *global_object.associated_realm();
+
+ // 5. Let relevant settings be realm’s settings object.
+ auto& relevant_settings = verify_cast<HTML::EnvironmentSettingsObject>(*realm.host_defined());
+
+ // 6. Let stored settings be value’s callback context.
+ auto& stored_settings = callback.callback_context;
+
+ // 7. Prepare to run script with relevant settings.
+ relevant_settings.prepare_to_run_script();
+
+ // 8. Prepare to run a callback with stored settings.
+ stored_settings.prepare_to_run_callback();
+
+ // 9. Let X be O.
+ auto* actual_function_object = &object;
+
+ // 10. If ! IsCallable(O) is false, then:
+ if (!object.is_function()) {
+ // 1. Let getResult be Get(O, opName).
+ auto get_result = object.get(operation_name);
+
+ // 2. If getResult is an abrupt completion, set completion to getResult and jump to the step labeled return.
+ if (get_result.is_throw_completion()) {
+ completion = get_result.throw_completion();
+ return clean_up_on_return(stored_settings, relevant_settings, completion);
+ }
+
+ // 4. If ! IsCallable(X) is false, then set completion to a new Completion{[[Type]]: throw, [[Value]]: a newly created TypeError object, [[Target]]: empty}, and jump to the step labeled return.
+ if (!get_result.value().is_function()) {
+ completion = realm.vm().template throw_completion<JS::TypeError>(global_object, JS::ErrorType::NotAFunction, get_result.value().to_string_without_side_effects());
+ return clean_up_on_return(stored_settings, relevant_settings, completion);
+ }
+
+ // 3. Set X to getResult.[[Value]].
+ // NOTE: This is done out of order because `actual_function_object` is of type JS::Object and we cannot assign to it until we know for sure getResult.[[Value]] is a JS::Object.
+ actual_function_object = &get_result.release_value().as_object();
+
+ // 5. Set thisArg to O (overriding the provided value).
+ this_argument = &object;
+ }
+
+ // FIXME: 11. Let esArgs be the result of converting args to an ECMAScript arguments list. If this throws an exception, set completion to the completion value representing the thrown exception and jump to the step labeled return.
+ // For simplicity, we currently make the caller do this. However, this means we can't throw exceptions at this point like the spec wants us to.
+
+ // 12. Let callResult be Call(X, thisArg, esArgs).
+ VERIFY(actual_function_object);
+ auto call_result = JS::call(global_object, verify_cast<JS::FunctionObject>(*actual_function_object), this_argument.value(), forward<Args>(args)...);
+
+ // 13. If callResult is an abrupt completion, set completion to callResult and jump to the step labeled return.
+ if (call_result.is_throw_completion()) {
+ completion = call_result.throw_completion();
+ return clean_up_on_return(stored_settings, relevant_settings, completion);
+ }
+
+ // 14. Set completion to the result of converting callResult.[[Value]] to an IDL value of the same type as the operation’s return type.
+ // FIXME: This does no conversion.
+ completion = call_result.value();
+
+ return clean_up_on_return(stored_settings, relevant_settings, completion);
+}
+
+// https://webidl.spec.whatwg.org/#invoke-a-callback-function
+template<typename... Args>
+JS::Completion invoke_callback(Bindings::CallbackType& callback, Optional<JS::Value> this_argument, Args&&... args)
+{
+ // 1. Let completion be an uninitialized variable.
+ JS::Completion completion;
+
+ // 2. If thisArg was not given, let thisArg be undefined.
+ if (!this_argument.has_value())
+ this_argument = JS::js_undefined();
+
+ // 3. Let F be the ECMAScript object corresponding to callable.
+ auto* function_object = callback.callback.cell();
+ VERIFY(function_object);
+
+ // 4. If ! IsCallable(F) is false:
+ if (!function_object->is_function()) {
+ // 1. Note: This is only possible when the callback function came from an attribute marked with [LegacyTreatNonObjectAsNull].
+
+ // 2. Return the result of converting undefined to the callback function’s return type.
+ // FIXME: This does no conversion.
+ return { JS::js_undefined() };
+ }
+
+ // 5. Let realm be F’s associated Realm.
+ // See the comment about associated realm on step 4 of call_user_object_operation.
+ auto& global_object = function_object->global_object();
+ auto& realm = *global_object.associated_realm();
+
+ // 6. Let relevant settings be realm’s settings object.
+ auto& relevant_settings = verify_cast<HTML::EnvironmentSettingsObject>(*realm.host_defined());
+
+ // 7. Let stored settings be value’s callback context.
+ auto& stored_settings = callback.callback_context;
+
+ // 8. Prepare to run script with relevant settings.
+ relevant_settings.prepare_to_run_script();
+
+ // 9. Prepare to run a callback with stored settings.
+ stored_settings.prepare_to_run_callback();
+
+ // FIXME: 10. Let esArgs be the result of converting args to an ECMAScript arguments list. If this throws an exception, set completion to the completion value representing the thrown exception and jump to the step labeled return.
+ // For simplicity, we currently make the caller do this. However, this means we can't throw exceptions at this point like the spec wants us to.
+
+ // 11. Let callResult be Call(F, thisArg, esArgs).
+ auto call_result = JS::call(global_object, verify_cast<JS::FunctionObject>(*function_object), this_argument.value(), forward<Args>(args)...);
+
+ // 12. If callResult is an abrupt completion, set completion to callResult and jump to the step labeled return.
+ if (call_result.is_throw_completion()) {
+ completion = call_result.throw_completion();
+ return clean_up_on_return(stored_settings, relevant_settings, completion);
+ }
+
+ // 13. Set completion to the result of converting callResult.[[Value]] to an IDL value of the same type as the operation’s return type.
+ // FIXME: This does no conversion.
+ completion = call_result.value();
+
+ return clean_up_on_return(stored_settings, relevant_settings, completion);
+}
+
}
diff --git a/Userland/Libraries/LibWeb/Bindings/ScriptExecutionContext.cpp b/Userland/Libraries/LibWeb/Bindings/ScriptExecutionContext.cpp
deleted file mode 100644
index 02cbd567c8..0000000000
--- a/Userland/Libraries/LibWeb/Bindings/ScriptExecutionContext.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
-
-#include <LibWeb/Bindings/ScriptExecutionContext.h>
-
-namespace Web::Bindings {
-
-ScriptExecutionContext::~ScriptExecutionContext()
-{
-}
-
-}
diff --git a/Userland/Libraries/LibWeb/Bindings/ScriptExecutionContext.h b/Userland/Libraries/LibWeb/Bindings/ScriptExecutionContext.h
deleted file mode 100644
index 3219a80a95..0000000000
--- a/Userland/Libraries/LibWeb/Bindings/ScriptExecutionContext.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
-
-#pragma once
-
-#include <AK/Weakable.h>
-#include <LibJS/Forward.h>
-#include <LibWeb/Forward.h>
-
-namespace Web::Bindings {
-
-class ScriptExecutionContext {
-public:
- virtual ~ScriptExecutionContext();
-
- virtual JS::Realm& realm() = 0;
-};
-
-}
diff --git a/Userland/Libraries/LibWeb/Bindings/WindowObject.cpp b/Userland/Libraries/LibWeb/Bindings/WindowObject.cpp
index 254a9a15de..62c701b3b7 100644
--- a/Userland/Libraries/LibWeb/Bindings/WindowObject.cpp
+++ b/Userland/Libraries/LibWeb/Bindings/WindowObject.cpp
@@ -223,16 +223,16 @@ JS_DEFINE_NATIVE_FUNCTION(WindowObject::set_interval)
auto* impl = TRY(impl_from(vm, global_object));
if (!vm.argument_count())
return vm.throw_completion<JS::TypeError>(global_object, JS::ErrorType::BadArgCountAtLeastOne, "setInterval");
- JS::FunctionObject* callback;
+ JS::Object* callback_object;
if (vm.argument(0).is_function()) {
- callback = &vm.argument(0).as_function();
+ callback_object = &vm.argument(0).as_function();
} else {
auto script_source = TRY(vm.argument(0).to_string(global_object));
// FIXME: This needs more work once we have a environment settings object.
// The spec wants us to use a task for the "run function or script string" part,
// using a NativeFunction for the latter is a workaround so that we can reuse the
// DOM::Timer API unaltered (always expects a JS::FunctionObject).
- callback = JS::NativeFunction::create(global_object, "", [impl, script_source = move(script_source)](auto&, auto&) mutable {
+ callback_object = JS::NativeFunction::create(global_object, "", [impl, script_source = move(script_source)](auto&, auto&) mutable {
auto& settings_object = verify_cast<HTML::EnvironmentSettingsObject>(*impl->associated_document().realm().host_defined());
auto script = HTML::ClassicScript::create(impl->associated_document().url().to_string(), script_source, settings_object, AK::URL());
return script->run();
@@ -244,8 +244,10 @@ JS_DEFINE_NATIVE_FUNCTION(WindowObject::set_interval)
if (interval < 0)
interval = 0;
}
+
+ NonnullOwnPtr<Bindings::CallbackType> callback = adopt_own(*new Bindings::CallbackType(JS::make_handle(callback_object), HTML::incumbent_settings_object()));
// FIXME: Pass ...arguments to the callback function when it's invoked
- auto timer_id = impl->set_interval(*callback, interval);
+ auto timer_id = impl->set_interval(move(callback), interval);
return JS::Value(timer_id);
}
@@ -257,16 +259,16 @@ JS_DEFINE_NATIVE_FUNCTION(WindowObject::set_timeout)
auto* impl = TRY(impl_from(vm, global_object));
if (!vm.argument_count())
return vm.throw_completion<JS::TypeError>(global_object, JS::ErrorType::BadArgCountAtLeastOne, "setTimeout");
- JS::FunctionObject* callback;
+ JS::Object* callback_object;
if (vm.argument(0).is_function()) {
- callback = &vm.argument(0).as_function();
+ callback_object = &vm.argument(0).as_function();
} else {
auto script_source = TRY(vm.argument(0).to_string(global_object));
// FIXME: This needs more work once we have a environment settings object.
// The spec wants us to use a task for the "run function or script string" part,
// using a NativeFunction for the latter is a workaround so that we can reuse the
// DOM::Timer API unaltered (always expects a JS::FunctionObject).
- callback = JS::NativeFunction::create(global_object, "", [impl, script_source = move(script_source)](auto&, auto&) mutable {
+ callback_object = JS::NativeFunction::create(global_object, "", [impl, script_source = move(script_source)](auto&, auto&) mutable {
auto& settings_object = verify_cast<HTML::EnvironmentSettingsObject>(*impl->associated_document().realm().host_defined());
auto script = HTML::ClassicScript::create(impl->associated_document().url().to_string(), script_source, settings_object, AK::URL());
return script->run();
@@ -278,8 +280,11 @@ JS_DEFINE_NATIVE_FUNCTION(WindowObject::set_timeout)
if (interval < 0)
interval = 0;
}
+
+ NonnullOwnPtr<Bindings::CallbackType> callback = adopt_own(*new Bindings::CallbackType(JS::make_handle(callback_object), HTML::incumbent_settings_object()));
+
// FIXME: Pass ...arguments to the callback function when it's invoked
- auto timer_id = impl->set_timeout(*callback, interval);
+ auto timer_id = impl->set_timeout(move(callback), interval);
return JS::Value(timer_id);
}
@@ -311,7 +316,8 @@ JS_DEFINE_NATIVE_FUNCTION(WindowObject::request_animation_frame)
auto* callback_object = TRY(vm.argument(0).to_object(global_object));
if (!callback_object->is_function())
return vm.throw_completion<JS::TypeError>(global_object, JS::ErrorType::NotAFunctionNoParam);
- return JS::Value(impl->request_animation_frame(*static_cast<JS::FunctionObject*>(callback_object)));
+ NonnullOwnPtr<Bindings::CallbackType> callback = adopt_own(*new Bindings::CallbackType(JS::make_handle(callback_object), HTML::incumbent_settings_object()));
+ return JS::Value(impl->request_animation_frame(move(callback)));
}
JS_DEFINE_NATIVE_FUNCTION(WindowObject::cancel_animation_frame)
@@ -333,7 +339,9 @@ JS_DEFINE_NATIVE_FUNCTION(WindowObject::queue_microtask)
if (!callback_object->is_function())
return vm.throw_completion<JS::TypeError>(global_object, JS::ErrorType::NotAFunctionNoParam);
- impl->queue_microtask(static_cast<JS::FunctionObject&>(*callback_object));
+ auto callback = adopt_own(*new Bindings::CallbackType(JS::make_handle(callback_object), HTML::incumbent_settings_object()));
+
+ impl->queue_microtask(move(callback));
return JS::js_undefined();
}
@@ -638,31 +646,25 @@ JS_DEFINE_NATIVE_FUNCTION(WindowObject::screen_y_getter)
return JS::Value(impl->screen_y());
}
-#define __ENUMERATE(attribute, event_name) \
- JS_DEFINE_NATIVE_FUNCTION(WindowObject::attribute##_getter) \
- { \
- auto* impl = TRY(impl_from(vm, global_object)); \
- auto retval = impl->attribute(); \
- if (retval.callback.is_null()) \
- return JS::js_null(); \
- return retval.callback.cell(); \
- } \
- JS_DEFINE_NATIVE_FUNCTION(WindowObject::attribute##_setter) \
- { \
- auto* impl = TRY(impl_from(vm, global_object)); \
- auto value = vm.argument(0); \
- HTML::EventHandler cpp_value; \
- if (value.is_function()) { \
- cpp_value.callback = JS::make_handle(&value.as_function()); \
- } else if (value.is_string()) { \
- cpp_value.string = value.as_string().string(); \
- } else { \
- return JS::js_undefined(); \
- } \
- TRY(throw_dom_exception_if_needed(global_object, [&] { \
- return impl->set_##attribute(cpp_value); \
- })); \
- return JS::js_undefined(); \
+#define __ENUMERATE(attribute, event_name) \
+ JS_DEFINE_NATIVE_FUNCTION(WindowObject::attribute##_getter) \
+ { \
+ auto* impl = TRY(impl_from(vm, global_object)); \
+ auto retval = impl->attribute(); \
+ if (!retval) \
+ return JS::js_null(); \
+ return retval->callback.cell(); \
+ } \
+ JS_DEFINE_NATIVE_FUNCTION(WindowObject::attribute##_setter) \
+ { \
+ auto* impl = TRY(impl_from(vm, global_object)); \
+ auto value = vm.argument(0); \
+ Optional<Bindings::CallbackType> cpp_value; \
+ if (value.is_object()) { \
+ cpp_value = Bindings::CallbackType { JS::make_handle(&value.as_object()), HTML::incumbent_settings_object() }; \
+ } \
+ impl->set_##attribute(cpp_value); \
+ return JS::js_undefined(); \
}
ENUMERATE_GLOBAL_EVENT_HANDLERS(__ENUMERATE)
#undef __ENUMERATE
diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt
index dad66cc02d..d567364dce 100644
--- a/Userland/Libraries/LibWeb/CMakeLists.txt
+++ b/Userland/Libraries/LibWeb/CMakeLists.txt
@@ -11,7 +11,6 @@ set(SOURCES
Bindings/MainThreadVM.cpp
Bindings/NavigatorObject.cpp
Bindings/NodeWrapperFactory.cpp
- Bindings/ScriptExecutionContext.cpp
Bindings/WindowObject.cpp
Bindings/Wrappable.cpp
Crypto/Crypto.cpp
@@ -74,7 +73,6 @@ set(SOURCES
DOM/ElementFactory.cpp
DOM/Event.cpp
DOM/EventDispatcher.cpp
- DOM/EventListener.cpp
DOM/EventTarget.cpp
DOM/HTMLCollection.cpp
DOM/LiveNodeList.cpp
diff --git a/Userland/Libraries/LibWeb/CSS/MediaQueryList.cpp b/Userland/Libraries/LibWeb/CSS/MediaQueryList.cpp
index 2bde03a70e..11fad62317 100644
--- a/Userland/Libraries/LibWeb/CSS/MediaQueryList.cpp
+++ b/Userland/Libraries/LibWeb/CSS/MediaQueryList.cpp
@@ -15,7 +15,7 @@
namespace Web::CSS {
MediaQueryList::MediaQueryList(DOM::Document& document, NonnullRefPtrVector<MediaQuery>&& media)
- : DOM::EventTarget(static_cast<Bindings::ScriptExecutionContext&>(document))
+ : DOM::EventTarget()
, m_document(document)
, m_media(move(media))
{
@@ -83,12 +83,12 @@ void MediaQueryList::remove_listener(RefPtr<DOM::EventListener> listener)
remove_event_listener(HTML::EventNames::change, listener);
}
-void MediaQueryList::set_onchange(HTML::EventHandler event_handler)
+void MediaQueryList::set_onchange(Optional<Bindings::CallbackType> event_handler)
{
set_event_handler_attribute(HTML::EventNames::change, event_handler);
}
-HTML::EventHandler MediaQueryList::onchange()
+Bindings::CallbackType* MediaQueryList::onchange()
{
return event_handler_attribute(HTML::EventNames::change);
}
diff --git a/Userland/Libraries/LibWeb/CSS/MediaQueryList.h b/Userland/Libraries/LibWeb/CSS/MediaQueryList.h
index 0585e759e1..8b3c621251 100644
--- a/Userland/Libraries/LibWeb/CSS/MediaQueryList.h
+++ b/Userland/Libraries/LibWeb/CSS/MediaQueryList.h
@@ -48,8 +48,8 @@ public:
void add_listener(RefPtr<DOM::EventListener> listener);
void remove_listener(RefPtr<DOM::EventListener> listener);
- void set_onchange(HTML::EventHandler);
- HTML::EventHandler onchange();
+ void set_onchange(Optional<Bindings::CallbackType>);
+ Bindings::CallbackType* onchange();
private:
MediaQueryList(DOM::Document&, NonnullRefPtrVector<MediaQuery>&&);
diff --git a/Userland/Libraries/LibWeb/DOM/AbortController.cpp b/Userland/Libraries/LibWeb/DOM/AbortController.cpp
index 2723894900..f218c5fe4e 100644
--- a/Userland/Libraries/LibWeb/DOM/AbortController.cpp
+++ b/Userland/Libraries/LibWeb/DOM/AbortController.cpp
@@ -10,8 +10,8 @@
namespace Web::DOM {
// https://dom.spec.whatwg.org/#dom-abortcontroller-abortcontroller
-AbortController::AbortController(Document& document)
- : m_signal(AbortSignal::create(document))
+AbortController::AbortController()
+ : m_signal(AbortSignal::create())
{
}
diff --git a/Userland/Libraries/LibWeb/DOM/AbortController.h b/Userland/Libraries/LibWeb/DOM/AbortController.h
index d07146e8cd..1e6119eb73 100644
--- a/Userland/Libraries/LibWeb/DOM/AbortController.h
+++ b/Userland/Libraries/LibWeb/DOM/AbortController.h
@@ -24,14 +24,14 @@ class AbortController final
public:
using WrapperType = Bindings::AbortControllerWrapper;
- static NonnullRefPtr<AbortController> create(Document& document)
+ static NonnullRefPtr<AbortController> create()
{
- return adopt_ref(*new AbortController(document));
+ return adopt_ref(*new AbortController());
}
- static NonnullRefPtr<AbortController> create_with_global_object(Bindings::WindowObject& window_object)
+ static NonnullRefPtr<AbortController> create_with_global_object(Bindings::WindowObject&)
{
- return AbortController::create(window_object.impl().associated_document());
+ return AbortController::create();
}
virtual ~AbortController() override;
@@ -42,7 +42,7 @@ public:
void abort(JS::Value reason);
private:
- AbortController(Document& document);
+ AbortController();
// https://dom.spec.whatwg.org/#abortcontroller-signal
NonnullRefPtr<AbortSignal> m_signal;
diff --git a/Userland/Libraries/LibWeb/DOM/AbortSignal.cpp b/Userland/Libraries/LibWeb/DOM/AbortSignal.cpp
index a8b887c77f..f019050513 100644
--- a/Userland/Libraries/LibWeb/DOM/AbortSignal.cpp
+++ b/Userland/Libraries/LibWeb/DOM/AbortSignal.cpp
@@ -14,8 +14,8 @@
namespace Web::DOM {
-AbortSignal::AbortSignal(Document& document)
- : EventTarget(static_cast<Bindings::ScriptExecutionContext&>(document))
+AbortSignal::AbortSignal()
+ : EventTarget()
{
}
@@ -63,12 +63,12 @@ void AbortSignal::signal_abort(JS::Value reason)
dispatch_event(Event::create(HTML::EventNames::abort));
}
-void AbortSignal::set_onabort(HTML::EventHandler event_handler)
+void AbortSignal::set_onabort(Optional<Bindings::CallbackType> event_handler)
{
- set_event_handler_attribute(HTML::EventNames::abort, event_handler);
+ set_event_handler_attribute(HTML::EventNames::abort, move(event_handler));
}
-HTML::EventHandler AbortSignal::onabort()
+Bindings::CallbackType* AbortSignal::onabort()
{
return event_handler_attribute(HTML::EventNames::abort);
}
diff --git a/Userland/Libraries/LibWeb/DOM/AbortSignal.h b/Userland/Libraries/LibWeb/DOM/AbortSignal.h
index 300c7758bd..2f91984ff8 100644
--- a/Userland/Libraries/LibWeb/DOM/AbortSignal.h
+++ b/Userland/Libraries/LibWeb/DOM/AbortSignal.h
@@ -27,14 +27,14 @@ public:
using RefCounted::ref;
using RefCounted::unref;
- static NonnullRefPtr<AbortSignal> create(Document& document)
+ static NonnullRefPtr<AbortSignal> create()
{
- return adopt_ref(*new AbortSignal(document));
+ return adopt_ref(*new AbortSignal());
}
- static NonnullRefPtr<AbortSignal> create_with_global_object(Bindings::WindowObject& window_object)
+ static NonnullRefPtr<AbortSignal> create_with_global_object(Bindings::WindowObject&)
{
- return AbortSignal::create(window_object.impl().associated_document());
+ return AbortSignal::create();
}
virtual ~AbortSignal() override;
@@ -47,8 +47,8 @@ public:
void signal_abort(JS::Value reason);
- void set_onabort(HTML::EventHandler);
- HTML::EventHandler onabort();
+ void set_onabort(Optional<Bindings::CallbackType>);
+ Bindings::CallbackType* onabort();
// https://dom.spec.whatwg.org/#dom-abortsignal-reason
JS::Value reason() const { return m_abort_reason; }
@@ -63,7 +63,7 @@ public:
virtual JS::Object* create_wrapper(JS::GlobalObject&) override;
private:
- AbortSignal(Document& document);
+ AbortSignal();
// https://dom.spec.whatwg.org/#abortsignal-abort-reason
// An AbortSignal object has an associated abort reason, which is a JavaScript value. It is undefined unless specified otherwise.
diff --git a/Userland/Libraries/LibWeb/DOM/Document.h b/Userland/Libraries/LibWeb/DOM/Document.h
index 8a3740bdde..1b67116601 100644
--- a/Userland/Libraries/LibWeb/DOM/Document.h
+++ b/Userland/Libraries/LibWeb/DOM/Document.h
@@ -16,7 +16,6 @@
#include <AK/WeakPtr.h>
#include <LibCore/Forward.h>
#include <LibJS/Forward.h>
-#include <LibWeb/Bindings/ScriptExecutionContext.h>
#include <LibWeb/Bindings/WindowObject.h>
#include <LibWeb/CSS/CSSStyleSheet.h>
#include <LibWeb/CSS/StyleComputer.h>
@@ -41,8 +40,7 @@ enum class QuirksMode {
class Document
: public ParentNode
, public NonElementParentNode<Document>
- , public HTML::GlobalEventHandlers
- , public Bindings::ScriptExecutionContext {
+ , public HTML::GlobalEventHandlers {
public:
using WrapperType = Bindings::DocumentWrapper;
diff --git a/Userland/Libraries/LibWeb/DOM/EventDispatcher.cpp b/Userland/Libraries/LibWeb/DOM/EventDispatcher.cpp
index 45c3f53611..52f4331926 100644
--- a/Userland/Libraries/LibWeb/DOM/EventDispatcher.cpp
+++ b/Userland/Libraries/LibWeb/DOM/EventDispatcher.cpp
@@ -12,7 +12,7 @@
#include <LibWeb/Bindings/EventTargetWrapperFactory.h>
#include <LibWeb/Bindings/EventWrapper.h>
#include <LibWeb/Bindings/EventWrapperFactory.h>
-#include <LibWeb/Bindings/ScriptExecutionContext.h>
+#include <LibWeb/Bindings/IDLAbstractOperations.h>
#include <LibWeb/Bindings/WindowObject.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/Event.h>
@@ -52,68 +52,87 @@ static EventTarget* retarget(EventTarget* left, EventTarget* right)
// https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke
bool EventDispatcher::inner_invoke(Event& event, Vector<EventTarget::EventListenerRegistration>& listeners, Event::Phase phase, bool invocation_target_in_shadow_tree)
{
+ // 1. Let found be false.
bool found = false;
+ // 2. For each listener in listeners, whose removed is false:
for (auto& listener : listeners) {
if (listener.listener->removed())
continue;
+ // 1. If event’s type attribute value is not listener’s type, then continue.
if (event.type() != listener.listener->type())
continue;
+ // 2. Set found to true.
found = true;
+ // 3. If phase is "capturing" and listener’s capture is false, then continue.
if (phase == Event::Phase::CapturingPhase && !listener.listener->capture())
continue;
+ // 4. If phase is "bubbling" and listener’s capture is true, then continue.
if (phase == Event::Phase::BubblingPhase && listener.listener->capture())
continue;
+ // 5. If listener’s once is true, then remove listener from event’s currentTarget attribute value’s event listener list.
if (listener.listener->once())
event.current_target()->remove_from_event_listener_list(listener.listener);
- auto& function = listener.listener->function();
- auto& global = function.global_object();
+ // 6. Let global be listener callback’s associated Realm’s global object.
+ auto& callback = listener.listener->callback();
+ auto& global = callback.callback.cell()->global_object();
+ // 7. Let currentEvent be undefined.
RefPtr<Event> current_event;
+ // 8. If global is a Window object, then:
if (is<Bindings::WindowObject>(global)) {
auto& bindings_window_global = verify_cast<Bindings::WindowObject>(global);
auto& window_impl = bindings_window_global.impl();
+
+ // 1. Set currentEvent to global’s current event.
current_event = window_impl.current_event();
+
+ // 2. If invocationTargetInShadowTree is false, then set global’s current event to event.
if (!invocation_target_in_shadow_tree)
window_impl.set_current_event(&event);
}
+ // 9. If listener’s passive is true, then set event’s in passive listener flag.
if (listener.listener->passive())
event.set_in_passive_listener(true);
+ // 10. Call a user object’s operation with listener’s callback, "handleEvent", « event », and event’s currentTarget attribute value. If this throws an exception, then:
+ // FIXME: These should be wrapped for us in call_user_object_operation, but it currently doesn't do that.
auto* this_value = Bindings::wrap(global, *event.current_target());
auto* wrapped_event = Bindings::wrap(global, event);
-
- // 10. Call a user object’s operation with listener’s callback, "handleEvent", « event », and event’s currentTarget attribute value.
- auto result = JS::call(global, function, this_value, wrapped_event);
+ auto result = Bindings::IDL::call_user_object_operation(callback, "handleEvent", this_value, wrapped_event);
// If this throws an exception, then:
if (result.is_error()) {
// 1. Report the exception.
- VERIFY(result.throw_completion().value().has_value());
- HTML::report_exception(*result.throw_completion().value());
+ HTML::report_exception(result);
// FIXME: 2. Set legacyOutputDidListenersThrowFlag if given. (Only used by IndexedDB currently)
}
+ // 11. Unset event’s in passive listener flag.
event.set_in_passive_listener(false);
+
+ // 12. If global is a Window object, then set global’s current event to currentEvent.
if (is<Bindings::WindowObject>(global)) {
auto& bindings_window_global = verify_cast<Bindings::WindowObject>(global);
auto& window_impl = bindings_window_global.impl();
window_impl.set_current_event(current_event);
}
+ // 13. If event’s stop immediate propagation flag is set, then return found.
if (event.should_stop_immediate_propagation())
return found;
}
+ // 3. Return found.
return found;
}
diff --git a/Userland/Libraries/LibWeb/DOM/EventListener.cpp b/Userland/Libraries/LibWeb/DOM/EventListener.cpp
deleted file mode 100644
index 29669cf12e..0000000000
--- a/Userland/Libraries/LibWeb/DOM/EventListener.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
-
-#include <LibJS/Runtime/FunctionObject.h>
-#include <LibWeb/DOM/EventListener.h>
-
-namespace Web::DOM {
-
-JS::FunctionObject& EventListener::function()
-{
- VERIFY(m_function.cell());
- return *m_function.cell();
-}
-
-}
diff --git a/Userland/Libraries/LibWeb/DOM/EventListener.h b/Userland/Libraries/LibWeb/DOM/EventListener.h
index 8291d722b1..7b76115a76 100644
--- a/Userland/Libraries/LibWeb/DOM/EventListener.h
+++ b/Userland/Libraries/LibWeb/DOM/EventListener.h
@@ -8,6 +8,7 @@
#include <AK/RefCounted.h>
#include <LibJS/Heap/Handle.h>
+#include <LibWeb/Bindings/CallbackType.h>
#include <LibWeb/Bindings/Wrappable.h>
namespace Web::DOM {
@@ -18,13 +19,14 @@ class EventListener
public:
using WrapperType = Bindings::EventListenerWrapper;
- explicit EventListener(JS::Handle<JS::FunctionObject> function, bool is_attribute = false)
- : m_function(move(function))
- , m_attribute(is_attribute)
+ explicit EventListener(Bindings::CallbackType callback)
+ : m_callback(move(callback))
{
}
- JS::FunctionObject& function();
+ virtual ~EventListener() = default;
+
+ Bindings::CallbackType& callback() { return m_callback; }
const FlyString& type() const { return m_type; }
void set_type(const FlyString& type) { m_type = type; }
@@ -41,16 +43,13 @@ public:
bool removed() const { return m_removed; }
void set_removed(bool removed) { m_removed = removed; }
- bool is_attribute() const { return m_attribute; }
-
private:
FlyString m_type;
- JS::Handle<JS::FunctionObject> m_function;
+ Bindings::CallbackType m_callback;
bool m_capture { false };
bool m_passive { false };
bool m_once { false };
bool m_removed { false };
- bool m_attribute { false };
};
}
diff --git a/Userland/Libraries/LibWeb/DOM/EventTarget.cpp b/Userland/Libraries/LibWeb/DOM/EventTarget.cpp
index 7b672de40c..ec32a62227 100644
--- a/Userland/Libraries/LibWeb/DOM/EventTarget.cpp
+++ b/Userland/Libraries/LibWeb/DOM/EventTarget.cpp
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2022, Luke Wilde <lukew@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@@ -7,24 +8,35 @@
#include <AK/StringBuilder.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Parser.h>
+#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
-#include <LibWeb/Bindings/ScriptExecutionContext.h>
+#include <LibJS/Runtime/NativeFunction.h>
+#include <LibJS/Runtime/ObjectEnvironment.h>
+#include <LibJS/Runtime/VM.h>
+#include <LibWeb/Bindings/DocumentWrapper.h>
+#include <LibWeb/Bindings/EventTargetWrapperFactory.h>
+#include <LibWeb/Bindings/EventWrapper.h>
+#include <LibWeb/Bindings/EventWrapperFactory.h>
+#include <LibWeb/Bindings/IDLAbstractOperations.h>
+#include <LibWeb/Bindings/MainThreadVM.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/Event.h>
#include <LibWeb/DOM/EventDispatcher.h>
#include <LibWeb/DOM/EventListener.h>
#include <LibWeb/DOM/EventTarget.h>
#include <LibWeb/DOM/Window.h>
+#include <LibWeb/HTML/ErrorEvent.h>
#include <LibWeb/HTML/EventHandler.h>
#include <LibWeb/HTML/EventNames.h>
+#include <LibWeb/HTML/FormAssociatedElement.h>
#include <LibWeb/HTML/HTMLBodyElement.h>
+#include <LibWeb/HTML/HTMLFormElement.h>
#include <LibWeb/HTML/HTMLFrameSetElement.h>
#include <LibWeb/UIEvents/EventNames.h>
namespace Web::DOM {
-EventTarget::EventTarget(Bindings::ScriptExecutionContext& script_execution_context)
- : m_script_execution_context(&script_execution_context)
+EventTarget::EventTarget()
{
}
@@ -38,7 +50,7 @@ void EventTarget::add_event_listener(const FlyString& event_name, RefPtr<EventLi
if (listener.is_null())
return;
auto existing_listener = m_listeners.first_matching([&](auto& entry) {
- return entry.listener->type() == event_name && &entry.listener->function() == &listener->function() && entry.listener->capture() == listener->capture();
+ return entry.listener->type() == event_name && entry.listener->callback().callback.cell() == listener->callback().callback.cell() && entry.listener->capture() == listener->capture();
});
if (existing_listener.has_value())
return;
@@ -52,7 +64,7 @@ void EventTarget::remove_event_listener(const FlyString& event_name, RefPtr<Even
if (listener.is_null())
return;
m_listeners.remove_first_matching([&](auto& entry) {
- auto matches = entry.event_name == event_name && &entry.listener->function() == &listener->function() && entry.listener->capture() == listener->capture();
+ auto matches = entry.event_name == event_name && entry.listener->callback().callback.cell() == listener->callback().callback.cell() && entry.listener->capture() == listener->capture();
if (matches)
entry.listener->set_removed(true);
return matches;
@@ -62,7 +74,7 @@ void EventTarget::remove_event_listener(const FlyString& event_name, RefPtr<Even
void EventTarget::remove_from_event_listener_list(NonnullRefPtr<EventListener> listener)
{
m_listeners.remove_first_matching([&](auto& entry) {
- return entry.listener->type() == listener->type() && &entry.listener->function() == &listener->function() && entry.listener->capture() == listener->capture();
+ return entry.listener->type() == listener->type() && &entry.listener->callback() == &listener->callback() && entry.listener->capture() == listener->capture();
});
}
@@ -139,51 +151,460 @@ static EventTarget* determine_target_of_event_handler(EventTarget& event_target,
return &event_target_element.document().window();
}
-HTML::EventHandler EventTarget::event_handler_attribute(FlyString const& name)
+// https://html.spec.whatwg.org/multipage/webappapis.html#event-handler-attributes:event-handler-idl-attributes-2
+Bindings::CallbackType* EventTarget::event_handler_attribute(FlyString const& name)
{
+ // 1. Let eventTarget be the result of determining the target of an event handler given this object and name.
auto target = determine_target_of_event_handler(*this, name);
+
+ // 2. If eventTarget is null, then return null.
if (!target)
- return {};
+ return nullptr;
- for (auto& listener : target->listeners()) {
- if (listener.event_name == name && listener.listener->is_attribute()) {
- return HTML::EventHandler { JS::make_handle(&listener.listener->function()) };
- }
- }
- return {};
+ // 3. Return the result of getting the current value of the event handler given eventTarget and name.
+ return target->get_current_value_of_event_handler(name);
}
-void EventTarget::set_event_handler_attribute(FlyString const& name, HTML::EventHandler value)
+// https://html.spec.whatwg.org/multipage/webappapis.html#getting-the-current-value-of-the-event-handler
+Bindings::CallbackType* EventTarget::get_current_value_of_event_handler(FlyString const& name)
{
- auto target = determine_target_of_event_handler(*this, name);
- if (!target)
- return;
+ // 1. Let handlerMap be eventTarget's event handler map. (NOTE: Not necessary)
- RefPtr<DOM::EventListener> listener;
- if (!value.callback.is_null()) {
- listener = adopt_ref(*new DOM::EventListener(move(value.callback), true));
- } else {
+ // 2. Let eventHandler be handlerMap[name].
+ auto event_handler_iterator = m_event_handler_map.find(name);
+
+ // Optimization: The spec creates all the event handlers exposed on an object up front and has the initial value of the handler set to null.
+ // If the event handler hasn't been set, null would be returned in step 4.
+ // However, this would be very allocation heavy. For example, each DOM::Element includes GlobalEventHandlers, which defines 60+(!) event handler attributes.
+ // Plus, the vast majority of these allocations would be likely wasted, as I imagine web content will only use a handful of these attributes on certain elements, if any at all.
+ // Thus, we treat the event handler not being in the event handler map as being equivalent to an event handler with an initial null value.
+ if (event_handler_iterator == m_event_handler_map.end())
+ return nullptr;
+
+ auto& event_handler = event_handler_iterator->value;
+
+ // 3. If eventHandler's value is an internal raw uncompiled handler, then:
+ if (event_handler.value.has<String>()) {
+ // 1. If eventTarget is an element, then let element be eventTarget, and document be element's node document.
+ // Otherwise, eventTarget is a Window object, let element be null, and document be eventTarget's associated Document.
+ RefPtr<Element> element;
+ RefPtr<Document> document;
+
+ if (is<Element>(this)) {
+ auto* element_event_target = verify_cast<Element>(this);
+ element = element_event_target;
+ document = element_event_target->document();
+ } else {
+ VERIFY(is<Window>(this));
+ auto* window_event_target = verify_cast<Window>(this);
+ document = window_event_target->associated_document();
+ }
+
+ VERIFY(document);
+
+ // 2. If scripting is disabled for document, then return null.
+ if (document->is_scripting_disabled())
+ return nullptr;
+
+ // 3. Let body be the uncompiled script body in eventHandler's value.
+ auto& body = event_handler.value.get<String>();
+
+ // FIXME: 4. Let location be the location where the script body originated, as given by eventHandler's value.
+
+ // 5. If element is not null and element has a form owner, let form owner be that form owner. Otherwise, let form owner be null.
+ RefPtr<HTML::HTMLFormElement> form_owner;
+ if (is<HTML::FormAssociatedElement>(element.ptr())) {
+ auto& form_associated_element = verify_cast<HTML::FormAssociatedElement>(*element);
+ if (form_associated_element.form())
+ form_owner = form_associated_element.form();
+ }
+
+ // 6. Let settings object be the relevant settings object of document.
+ auto& settings_object = document->relevant_settings_object();
+
+ // NOTE: ECMAScriptFunctionObject::create expects a parsed body as input, so we must do the spec's sourceText steps here.
StringBuilder builder;
- builder.appendff("function {}(event) {{\n{}\n}}", name, value.string);
- auto parser = JS::Parser(JS::Lexer(builder.string_view()));
+
+ // sourceText
+ if (name == HTML::EventNames::error && is<Window>(this)) {
+ // -> If name is onerror and eventTarget is a Window object
+ // The string formed by concatenating "function ", name, "(event, source, lineno, colno, error) {", U+000A LF, body, U+000A LF, and "}".
+ builder.appendff("function {}(event, source, lineno, colno, error) {{\n{}\n}}", name, body);
+ } else {
+ // -> Otherwise
+ // The string formed by concatenating "function ", name, "(event) {", U+000A LF, body, U+000A LF, and "}".
+ builder.appendff("function {}(event) {{\n{}\n}}", name, body);
+ }
+
+ auto source_text = builder.to_string();
+
+ auto parser = JS::Parser(JS::Lexer(source_text));
+
+ // FIXME: This should only be parsing the `body` instead of `source_text` and therefore use `JS::FunctionBody` instead of `JS::FunctionExpression`.
+ // However, JS::ECMAScriptFunctionObject::create wants parameters and length and JS::FunctionBody does not inherit JS::FunctionNode.
auto program = parser.parse_function_node<JS::FunctionExpression>();
+
+ // 7. If body is not parsable as FunctionBody or if parsing detects an early error, then follow these substeps:
if (parser.has_errors()) {
- dbgln("Failed to parse script in event handler attribute '{}'", name);
- return;
+ // 1. Set eventHandler's value to null.
+ // Note: This does not deactivate the event handler, which additionally removes the event handler's listener (if present).
+ m_event_handler_map.remove(event_handler_iterator);
+
+ // FIXME: 2. Report the error for the appropriate script and with the appropriate position (line number and column number) given by location, using settings object's global object.
+ // If the error is still not handled after this, then the error may be reported to a developer console.
+
+ // 3. Return null.
+ return nullptr;
}
- auto* function = JS::ECMAScriptFunctionObject::create(target->script_execution_context()->realm().global_object(), name, program->source_text(), program->body(), program->parameters(), program->function_length(), nullptr, nullptr, JS::FunctionKind::Normal, false, false);
+
+ // 8. Push settings object's realm execution context onto the JavaScript execution context stack; it is now the running JavaScript execution context.
+ auto& global_object = settings_object.global_object();
+ global_object.vm().push_execution_context(settings_object.realm_execution_context(), global_object);
+
+ // 9. Let function be the result of calling OrdinaryFunctionCreate, with arguments:
+ // functionPrototype
+ // %Function.prototype% (This is enforced by using JS::ECMAScriptFunctionObject)
+
+ // sourceText was handled above.
+
+ // ParameterList
+ // If name is onerror and eventTarget is a Window object
+ // Let the function have five arguments, named event, source, lineno, colno, and error.
+ // Otherwise
+ // Let the function have a single argument called event.
+ // (This was handled above for us by the parser using sourceText)
+
+ // body
+ // The result of parsing body above. (This is given by program->body())
+
+ // thisMode
+ // non-lexical-this (For JS::ECMAScriptFunctionObject, this means passing is_arrow_function as false)
+ constexpr bool is_arrow_function = false;
+
+ // scope
+ // 1. Let realm be settings object's Realm.
+ auto& realm = settings_object.realm();
+
+ // 2. Let scope be realm.[[GlobalEnv]].
+ JS::Environment* scope = &realm.global_environment();
+
+ // 3. If eventHandler is an element's event handler, then set scope to NewObjectEnvironment(document, true, scope).
+ // (Otherwise, eventHandler is a Window object's event handler.)
+ if (is<Element>(this)) {
+ auto* wrapped_document = Bindings::wrap(global_object, *document);
+ scope = JS::new_object_environment(*wrapped_document, true, scope);
+ }
+
+ // 4. If form owner is not null, then set scope to NewObjectEnvironment(form owner, true, scope).
+ if (form_owner) {
+ auto* wrapped_form_owner = Bindings::wrap(global_object, *form_owner);
+ scope = JS::new_object_environment(*wrapped_form_owner, true, scope);
+ }
+
+ // 5. If element is not null, then set scope to NewObjectEnvironment(element, true, scope).
+ if (element) {
+ auto* wrapped_element = Bindings::wrap(global_object, *element);
+ scope = JS::new_object_environment(*wrapped_element, true, scope);
+ }
+
+ // 6. Return scope. (NOTE: Not necessary)
+
+ auto* function = JS::ECMAScriptFunctionObject::create(global_object, name, builder.to_string(), program->body(), program->parameters(), program->function_length(), scope, nullptr, JS::FunctionKind::Normal, program->is_strict_mode(), program->might_need_arguments_object(), is_arrow_function);
VERIFY(function);
- listener = adopt_ref(*new DOM::EventListener(JS::make_handle(static_cast<JS::FunctionObject*>(function)), true));
+
+ // 10. Remove settings object's realm execution context from the JavaScript execution context stack.
+ VERIFY(global_object.vm().execution_context_stack().last() == &settings_object.realm_execution_context());
+ global_object.vm().pop_execution_context();
+
+ // 11. Set function.[[ScriptOrModule]] to null.
+ function->set_script_or_module({});
+
+ // 12. Set eventHandler's value to the result of creating a Web IDL EventHandler callback function object whose object reference is function and whose callback context is settings object.
+ event_handler.value = Bindings::CallbackType { JS::make_handle(static_cast<JS::Object*>(function)), settings_object };
+ }
+
+ // 4. Return eventHandler's value.
+ VERIFY(event_handler.value.has<Bindings::CallbackType>());
+ return event_handler.value.get_pointer<Bindings::CallbackType>();
+}
+
+// https://html.spec.whatwg.org/multipage/webappapis.html#event-handler-attributes:event-handler-idl-attributes-3
+void EventTarget::set_event_handler_attribute(FlyString const& name, Optional<Bindings::CallbackType> value)
+{
+ // 1. Let eventTarget be the result of determining the target of an event handler given this object and name.
+ auto event_target = determine_target_of_event_handler(*this, name);
+
+ // 2. If eventTarget is null, then return.
+ if (!event_target)
+ return;
+
+ // 3. If the given value is null, then deactivate an event handler given eventTarget and name.
+ if (!value.has_value()) {
+ event_target->deactivate_event_handler(name);
+ return;
+ }
+
+ // 4. Otherwise:
+ // 1. Let handlerMap be eventTarget's event handler map.
+ auto& handler_map = event_target->m_event_handler_map;
+
+ // 2. Let eventHandler be handlerMap[name].
+ auto event_handler_iterator = handler_map.find(name);
+
+ // 3. Set eventHandler's value to the given value.
+ if (event_handler_iterator == handler_map.end()) {
+ // NOTE: See the optimization comment in get_current_value_of_event_handler about why this is done.
+ HTML::EventHandler new_event_handler { move(value.value()) };
+
+ // 4. Activate an event handler given eventTarget and name.
+ // Optimization: We pass in the event handler here instead of having activate_event_handler do another hash map lookup just to get the same object.
+ // This handles a new event handler while the other path handles an existing event handler. As such, both paths must have their own
+ // unique call to activate_event_handler.
+ event_target->activate_event_handler(name, new_event_handler, IsAttribute::No);
+
+ handler_map.set(name, move(new_event_handler));
+ return;
}
- if (listener) {
- for (auto& registered_listener : target->listeners()) {
- if (registered_listener.event_name == name && registered_listener.listener->is_attribute()) {
- target->remove_event_listener(name, registered_listener.listener);
- break;
- }
+
+ auto& event_handler = event_handler_iterator->value;
+
+ event_handler.value = move(value.value());
+
+ // 4. Activate an event handler given eventTarget and name.
+ // NOTE: See the optimization comment above.
+ event_target->activate_event_handler(name, event_handler, IsAttribute::No);
+}
+
+// https://html.spec.whatwg.org/multipage/webappapis.html#activate-an-event-handler
+void EventTarget::activate_event_handler(FlyString const& name, HTML::EventHandler& event_handler, IsAttribute is_attribute)
+{
+ // 1. Let handlerMap be eventTarget's event handler map.
+ // 2. Let eventHandler be handlerMap[name].
+ // NOTE: These are achieved by using the passed in event handler.
+
+ // 3. If eventHandler's listener is not null, then return.
+ if (event_handler.listener)
+ return;
+
+ // 4. Let callback be the result of creating a Web IDL EventListener instance representing a reference to a function of one argument that executes the steps of the event handler processing algorithm, given eventTarget, name, and its argument.
+ // The EventListener's callback context can be arbitrary; it does not impact the steps of the event handler processing algorithm. [DOM]
+
+ // FIXME: This is guess work on what global object the NativeFunction should be allocated on.
+ // For <body> or <frameset> elements who just had an element attribute set, it will be this's wrapper, as `this` is the result of determine_target_of_event_handler
+ // returning the element's document's global object, which is the DOM::Window object.
+ // For any other HTMLElement who just had an element attribute set, `this` will be that HTMLElement, so the global object is this's document's realm's global object.
+ // For anything else, it came from JavaScript, so use the global object of the provided callback function.
+ // Sadly, this doesn't work if an element attribute is set on a <body> element before any script is run, as Window::wrapper() will be null.
+ JS::GlobalObject* global_object = nullptr;
+ if (is_attribute == IsAttribute::Yes) {
+ if (is<Window>(this)) {
+ auto* window_global_object = verify_cast<Window>(this)->wrapper();
+ global_object = static_cast<JS::GlobalObject*>(window_global_object);
+ } else {
+ auto* html_element = verify_cast<HTML::HTMLElement>(this);
+ global_object = &html_element->document().realm().global_object();
}
- target->add_event_listener(name, listener.release_nonnull());
+ } else {
+ global_object = &event_handler.value.get<Bindings::CallbackType>().callback.cell()->global_object();
}
+
+ VERIFY(global_object);
+
+ // NOTE: The callback must keep `this` alive. For example:
+ // document.body.onunload = () => { console.log("onunload called!"); }
+ // document.body.remove();
+ // location.reload();
+ // The body element is no longer in the DOM and there is no variable holding onto it. However, the onunload handler is still called, meaning the callback keeps the body element alive.
+ auto callback_function = JS::NativeFunction::create(*global_object, "", [event_target = NonnullRefPtr(*this), name](JS::VM& vm, auto&) mutable -> JS::ThrowCompletionOr<JS::Value> {
+ // The event dispatcher should only call this with one argument.
+ VERIFY(vm.argument_count() == 1);
+
+ // The argument must be an object and it must be an EventWrapper.
+ auto event_wrapper_argument = vm.argument(0);
+ VERIFY(event_wrapper_argument.is_object());
+ auto& event_wrapper = verify_cast<Bindings::EventWrapper>(event_wrapper_argument.as_object());
+ auto& event = event_wrapper.impl();
+
+ TRY(event_target->process_event_handler_for_event(name, event));
+ return JS::js_undefined();
+ });
+
+ // NOTE: As per the spec, the callback context is arbitrary.
+ Bindings::CallbackType callback { JS::make_handle(static_cast<JS::Object*>(callback_function)), verify_cast<HTML::EnvironmentSettingsObject>(*global_object->associated_realm()->host_defined()) };
+
+ // 5. Let listener be a new event listener whose type is the event handler event type corresponding to eventHandler and callback is callback.
+ auto listener = adopt_ref(*new EventListener(move(callback)));
+
+ // 6. Add an event listener with eventTarget and listener.
+ // FIXME: Make add_event_listener follow the spec more tightly. (Namely, don't allow taking a name and having a separate bindings version)
+ add_event_listener(name, listener);
+
+ // 7. Set eventHandler's listener to listener.
+ event_handler.listener = listener;
+}
+
+void EventTarget::deactivate_event_handler(FlyString const& name)
+{
+ // 1. Let handlerMap be eventTarget's event handler map. (NOTE: Not necessary)
+
+ // 2. Let eventHandler be handlerMap[name].
+ auto event_handler_iterator = m_event_handler_map.find(name);
+
+ // NOTE: See the optimization comment in get_current_value_of_event_handler about why this is done.
+ if (event_handler_iterator == m_event_handler_map.end())
+ return;
+
+ auto& event_handler = event_handler_iterator->value;
+
+ // 4. Let listener be eventHandler's listener. (NOTE: Not necessary)
+
+ // 5. If listener is not null, then remove an event listener with eventTarget and listener.
+ if (event_handler.listener) {
+ // FIXME: Make remove_event_listener follow the spec more tightly. (Namely, don't allow taking a name and having a separate bindings version)
+ remove_event_listener(name, event_handler.listener);
+ }
+
+ // 6. Set eventHandler's listener to null.
+ event_handler.listener = nullptr;
+
+ // 3. Set eventHandler's value to null.
+ // NOTE: This is done out of order since our equivalent of setting value to null is removing the event handler from the map.
+ // Given that event_handler is a reference to an entry, this would invalidate event_handler if we did it in order.
+ m_event_handler_map.remove(event_handler_iterator);
+}
+
+// https://html.spec.whatwg.org/multipage/webappapis.html#the-event-handler-processing-algorithm
+JS::ThrowCompletionOr<void> EventTarget::process_event_handler_for_event(FlyString const& name, Event& event)
+{
+ // 1. Let callback be the result of getting the current value of the event handler given eventTarget and name.
+ auto* callback = get_current_value_of_event_handler(name);
+
+ // 2. If callback is null, then return.
+ if (!callback)
+ return {};
+
+ // 3. Let special error event handling be true if event is an ErrorEvent object, event's type is error, and event's currentTarget implements the WindowOrWorkerGlobalScope mixin.
+ // Otherwise, let special error event handling be false.
+ // FIXME: This doesn't check for WorkerGlobalScape as we don't currently have it.
+ bool special_error_event_handling = is<HTML::ErrorEvent>(event) && event.type() == HTML::EventNames::error && is<Window>(event.current_target().ptr());
+
+ // 4. Process the Event object event as follows:
+ JS::Completion return_value_or_error;
+
+ // Needed for wrapping.
+ auto* callback_object = callback->callback.cell();
+
+ if (special_error_event_handling) {
+ // -> If special error event handling is true
+ // Invoke callback with five arguments, the first one having the value of event's message attribute, the second having the value of event's filename attribute, the third having the value of event's lineno attribute,
+ // the fourth having the value of event's colno attribute, the fifth having the value of event's error attribute, and with the callback this value set to event's currentTarget.
+ // Let return value be the callback's return value. [WEBIDL]
+ auto& error_event = verify_cast<HTML::ErrorEvent>(event);
+ auto* wrapped_message = JS::js_string(callback_object->heap(), error_event.message());
+ auto* wrapped_filename = JS::js_string(callback_object->heap(), error_event.filename());
+ auto wrapped_lineno = JS::Value(error_event.lineno());
+ auto wrapped_colno = JS::Value(error_event.colno());
+
+ // NOTE: error_event.error() is a JS::Value, so it does not require wrapping.
+
+ // NOTE: current_target is always non-null here, as the event dispatcher takes care to make sure it's non-null (and uses it as the this value for the callback!)
+ // FIXME: This is rewrapping the this value of the callback defined in activate_event_handler. While I don't think this is observable as the event dispatcher
+ // calls directly into the callback without considering things such as proxies, it is a waste. However, if it observable, then we must reuse the this_value that was given to the callback.
+ auto* this_value = Bindings::wrap(callback_object->global_object(), *error_event.current_target());
+
+ return_value_or_error = Bindings::IDL::invoke_callback(*callback, this_value, wrapped_message, wrapped_filename, wrapped_lineno, wrapped_colno, error_event.error());
+ } else {
+ // -> Otherwise
+ // Invoke callback with one argument, the value of which is the Event object event, with the callback this value set to event's currentTarget. Let return value be the callback's return value. [WEBIDL]
+
+ // FIXME: This has the same rewrapping issue as this_value.
+ auto* wrapped_event = Bindings::wrap(callback_object->global_object(), event);
+
+ // FIXME: The comments about this in the special_error_event_handling path also apply here.
+ auto* this_value = Bindings::wrap(callback_object->global_object(), *event.current_target());
+
+ return_value_or_error = Bindings::IDL::invoke_callback(*callback, this_value, wrapped_event);
+ }
+
+ // If an exception gets thrown by the callback, end these steps and allow the exception to propagate. (It will propagate to the DOM event dispatch logic, which will then report the exception.)
+ if (return_value_or_error.is_error())
+ return return_value_or_error.release_error();
+
+ // FIXME: Ideally, invoke_callback would convert JS::Value to the appropriate return type for us as per the spec, but it doesn't currently.
+ auto return_value = *return_value_or_error.value();
+
+ // FIXME: If event is a BeforeUnloadEvent object and event's type is beforeunload
+ // If return value is not null, then: (NOTE: When implementing, if we still return a JS::Value from invoke_callback, use is_nullish instead of is_null, as "null" refers to IDL null, which is JS null or undefined)
+ // 1. Set event's canceled flag.
+ // 2. If event's returnValue attribute's value is the empty string, then set event's returnValue attribute's value to return value.
+
+ if (special_error_event_handling) {
+ // -> If special error event handling is true
+ // If return value is true, then set event's canceled flag.
+ // NOTE: the return type of EventHandler is `any`, so no coercion happens, meaning we have to check if it's a boolean first.
+ if (return_value.is_boolean() && return_value.as_bool())
+ event.set_cancelled(true);
+ } else {
+ // -> Otherwise
+ // If return value is false, then set event's canceled flag.
+ // NOTE: the return type of EventHandler is `any`, so no coercion happens, meaning we have to check if it's a boolean first.
+ if (return_value.is_boolean() && !return_value.as_bool())
+ event.set_cancelled(true);
+ }
+
+ return {};
+}
+
+// https://html.spec.whatwg.org/multipage/webappapis.html#event-handler-attributes:concept-element-attributes-change-ext
+void EventTarget::element_event_handler_attribute_changed(FlyString const& local_name, String const& value)
+{
+ // NOTE: Step 1 of this algorithm was handled in HTMLElement::parse_attribute.
+
+ // 2. Let eventTarget be the result of determining the target of an event handler given element and localName.
+ // NOTE: element is `this`.
+ auto* event_target = determine_target_of_event_handler(*this, local_name);
+
+ // 3. If eventTarget is null, then return.
+ if (!event_target)
+ return;
+
+ // 4. If value is null, then deactivate an event handler given eventTarget and localName.
+ if (value.is_null()) {
+ event_target->deactivate_event_handler(local_name);
+ return;
+ }
+
+ // 5. Otherwise:
+ // FIXME: 1. If the Should element's inline behavior be blocked by Content Security Policy? algorithm returns "Blocked" when executed upon element, "script attribute", and value, then return. [CSP]
+
+ // 2. Let handlerMap be eventTarget's event handler map.
+ auto& handler_map = event_target->m_event_handler_map;
+
+ // 3. Let eventHandler be handlerMap[localName].
+ auto event_handler_iterator = handler_map.find(local_name);
+
+ // FIXME: 4. Let location be the script location that triggered the execution of these steps.
+
+ // FIXME: 5. Set eventHandler's value to the internal raw uncompiled handler value/location.
+ // (This currently sets the value to the uncompiled source code instead of the named struct)
+
+ // NOTE: See the optimization comments in set_event_handler_attribute.
+
+ if (event_handler_iterator == handler_map.end()) {
+ HTML::EventHandler new_event_handler { value };
+
+ // 6. Activate an event handler given eventTarget and name.
+ event_target->activate_event_handler(local_name, new_event_handler, IsAttribute::Yes);
+
+ handler_map.set(local_name, move(new_event_handler));
+ return;
+ }
+
+ auto& event_handler = event_handler_iterator->value;
+
+ // 6. Activate an event handler given eventTarget and name.
+ event_handler.value = value;
+ event_target->activate_event_handler(local_name, event_handler, IsAttribute::Yes);
}
bool EventTarget::dispatch_event(NonnullRefPtr<Event> event)
diff --git a/Userland/Libraries/LibWeb/DOM/EventTarget.h b/Userland/Libraries/LibWeb/DOM/EventTarget.h
index d3a118c9aa..65d7054baa 100644
--- a/Userland/Libraries/LibWeb/DOM/EventTarget.h
+++ b/Userland/Libraries/LibWeb/DOM/EventTarget.h
@@ -13,6 +13,7 @@
#include <LibJS/Forward.h>
#include <LibWeb/DOM/ExceptionOr.h>
#include <LibWeb/Forward.h>
+#include <LibWeb/HTML/EventHandler.h>
namespace Web::DOM {
@@ -37,7 +38,6 @@ public:
ExceptionOr<bool> dispatch_event_binding(NonnullRefPtr<Event>);
virtual JS::Object* create_wrapper(JS::GlobalObject&) = 0;
- Bindings::ScriptExecutionContext* script_execution_context() { return m_script_execution_context; }
virtual EventTarget* get_parent(const Event&) { return nullptr; }
@@ -55,20 +55,33 @@ public:
Function<void()> legacy_pre_activation_behavior;
Function<void()> legacy_cancelled_activation_behavior;
- HTML::EventHandler event_handler_attribute(FlyString const& name);
- void set_event_handler_attribute(FlyString const& name, HTML::EventHandler);
+ Bindings::CallbackType* event_handler_attribute(FlyString const& name);
+ void set_event_handler_attribute(FlyString const& name, Optional<Bindings::CallbackType>);
protected:
- explicit EventTarget(Bindings::ScriptExecutionContext&);
+ EventTarget();
virtual void ref_event_target() = 0;
virtual void unref_event_target() = 0;
-private:
- // FIXME: This should not be a raw pointer.
- Bindings::ScriptExecutionContext* m_script_execution_context { nullptr };
+ void element_event_handler_attribute_changed(FlyString const& local_name, String const& value);
+private:
Vector<EventListenerRegistration> m_listeners;
+
+ // https://html.spec.whatwg.org/multipage/webappapis.html#event-handler-map
+ // Spec Note: The order of the entries of event handler map could be arbitrary. It is not observable through any algorithms that operate on the map.
+ HashMap<FlyString, HTML::EventHandler> m_event_handler_map;
+
+ enum class IsAttribute {
+ No,
+ Yes,
+ };
+
+ Bindings::CallbackType* get_current_value_of_event_handler(FlyString const& name);
+ void activate_event_handler(FlyString const& name, HTML::EventHandler& event_handler, IsAttribute is_attribute);
+ void deactivate_event_handler(FlyString const& name);
+ JS::ThrowCompletionOr<void> process_event_handler_for_event(FlyString const& name, Event& event);
};
}
diff --git a/Userland/Libraries/LibWeb/DOM/Node.cpp b/Userland/Libraries/LibWeb/DOM/Node.cpp
index aa49f7bac0..9c0371bcc7 100644
--- a/Userland/Libraries/LibWeb/DOM/Node.cpp
+++ b/Userland/Libraries/LibWeb/DOM/Node.cpp
@@ -57,7 +57,7 @@ Node* Node::from_id(i32 node_id)
}
Node::Node(Document& document, NodeType type)
- : EventTarget(static_cast<Bindings::ScriptExecutionContext&>(document))
+ : EventTarget()
, m_document(&document)
, m_type(type)
, m_id(allocate_node_id(this))
diff --git a/Userland/Libraries/LibWeb/DOM/Timer.cpp b/Userland/Libraries/LibWeb/DOM/Timer.cpp
index 3f1f943dc2..5e5afbd50b 100644
--- a/Userland/Libraries/LibWeb/DOM/Timer.cpp
+++ b/Userland/Libraries/LibWeb/DOM/Timer.cpp
@@ -11,20 +11,20 @@
namespace Web::DOM {
-NonnullRefPtr<Timer> Timer::create_interval(Window& window, int milliseconds, JS::FunctionObject& callback)
+NonnullRefPtr<Timer> Timer::create_interval(Window& window, int milliseconds, NonnullOwnPtr<Bindings::CallbackType> callback)
{
- return adopt_ref(*new Timer(window, Type::Interval, milliseconds, callback));
+ return adopt_ref(*new Timer(window, Type::Interval, milliseconds, move(callback)));
}
-NonnullRefPtr<Timer> Timer::create_timeout(Window& window, int milliseconds, JS::FunctionObject& callback)
+NonnullRefPtr<Timer> Timer::create_timeout(Window& window, int milliseconds, NonnullOwnPtr<Bindings::CallbackType> callback)
{
- return adopt_ref(*new Timer(window, Type::Timeout, milliseconds, callback));
+ return adopt_ref(*new Timer(window, Type::Timeout, milliseconds, move(callback)));
}
-Timer::Timer(Window& window, Type type, int milliseconds, JS::FunctionObject& callback)
+Timer::Timer(Window& window, Type type, int milliseconds, NonnullOwnPtr<Bindings::CallbackType> callback)
: m_window(window)
, m_type(type)
- , m_callback(JS::make_handle(&callback))
+ , m_callback(move(callback))
{
m_id = window.allocate_timer_id({});
m_timer = Core::Timer::construct(milliseconds, [this] { m_window.timer_did_fire({}, *this); });
diff --git a/Userland/Libraries/LibWeb/DOM/Timer.h b/Userland/Libraries/LibWeb/DOM/Timer.h
index e948d6bfc4..cd253ead02 100644
--- a/Userland/Libraries/LibWeb/DOM/Timer.h
+++ b/Userland/Libraries/LibWeb/DOM/Timer.h
@@ -21,24 +21,24 @@ public:
Timeout,
};
- static NonnullRefPtr<Timer> create_interval(Window&, int milliseconds, JS::FunctionObject&);
- static NonnullRefPtr<Timer> create_timeout(Window&, int milliseconds, JS::FunctionObject&);
+ static NonnullRefPtr<Timer> create_interval(Window&, int milliseconds, NonnullOwnPtr<Bindings::CallbackType> callback);
+ static NonnullRefPtr<Timer> create_timeout(Window&, int milliseconds, NonnullOwnPtr<Bindings::CallbackType> callback);
~Timer();
i32 id() const { return m_id; }
Type type() const { return m_type; }
- JS::FunctionObject& callback() { return *m_callback.cell(); }
+ Bindings::CallbackType& callback() { return *m_callback; }
private:
- Timer(Window&, Type, int ms, JS::FunctionObject&);
+ Timer(Window&, Type, int ms, NonnullOwnPtr<Bindings::CallbackType> callback);
Window& m_window;
RefPtr<Core::Timer> m_timer;
Type m_type;
int m_id { 0 };
- JS::Handle<JS::FunctionObject> m_callback;
+ NonnullOwnPtr<Bindings::CallbackType> m_callback;
};
}
diff --git a/Userland/Libraries/LibWeb/DOM/Window.cpp b/Userland/Libraries/LibWeb/DOM/Window.cpp
index 389088a634..130b694bdf 100644
--- a/Userland/Libraries/LibWeb/DOM/Window.cpp
+++ b/Userland/Libraries/LibWeb/DOM/Window.cpp
@@ -8,6 +8,7 @@
#include <LibGUI/DisplayLink.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/FunctionObject.h>
+#include <LibWeb/Bindings/IDLAbstractOperations.h>
#include <LibWeb/CSS/Parser/Parser.h>
#include <LibWeb/CSS/ResolvedCSSStyleDeclaration.h>
#include <LibWeb/Crypto/Crypto.h>
@@ -109,7 +110,7 @@ NonnullRefPtr<Window> Window::create_with_document(Document& document)
}
Window::Window(Document& document)
- : EventTarget(static_cast<Bindings::ScriptExecutionContext&>(document))
+ : EventTarget()
, m_associated_document(document)
, m_performance(make<HighResolutionTime::Performance>(*this))
, m_crypto(Crypto::Crypto::create())
@@ -146,16 +147,16 @@ String Window::prompt(String const& message, String const& default_)
return {};
}
-i32 Window::set_interval(JS::FunctionObject& callback, i32 interval)
+i32 Window::set_interval(NonnullOwnPtr<Bindings::CallbackType> callback, i32 interval)
{
- auto timer = Timer::create_interval(*this, interval, callback);
+ auto timer = Timer::create_interval(*this, interval, move(callback));
m_timers.set(timer->id(), timer);
return timer->id();
}
-i32 Window::set_timeout(JS::FunctionObject& callback, i32 interval)
+i32 Window::set_timeout(NonnullOwnPtr<Bindings::CallbackType> callback, i32 interval)
{
- auto timer = Timer::create_timeout(*this, interval, callback);
+ auto timer = Timer::create_timeout(*this, interval, move(callback));
m_timers.set(timer->id(), timer);
return timer->id();
}
@@ -172,7 +173,7 @@ void Window::timer_did_fire(Badge<Timer>, Timer& timer)
VERIFY(wrapper());
HTML::queue_global_task(HTML::Task::Source::TimerTask, *wrapper(), [this, strong_this = NonnullRefPtr(*this), strong_timer = NonnullRefPtr(timer)]() mutable {
- auto result = JS::call(wrapper()->global_object(), strong_timer->callback(), wrapper());
+ auto result = Bindings::IDL::invoke_callback(strong_timer->callback(), wrapper());
if (result.is_error())
HTML::report_exception(result);
});
@@ -199,17 +200,15 @@ void Window::clear_interval(i32 timer_id)
}
// https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#run-the-animation-frame-callbacks
-i32 Window::request_animation_frame(JS::FunctionObject& js_callback)
+i32 Window::request_animation_frame(NonnullOwnPtr<Bindings::CallbackType> js_callback)
{
- auto callback = request_animation_frame_driver().add([this, handle = JS::make_handle(&js_callback)](i32 id) mutable {
- auto& function = *handle.cell();
+ auto callback = request_animation_frame_driver().add([this, js_callback = move(js_callback)](i32 id) mutable {
// 3. Invoke callback, passing now as the only argument,
- auto result = JS::call(function.global_object(), function, JS::js_undefined(), JS::Value(performance().now()));
+ auto result = Bindings::IDL::invoke_callback(*js_callback, {}, JS::Value(performance().now()));
// and if an exception is thrown, report the exception.
if (result.is_error())
HTML::report_exception(result);
-
m_request_animation_frame_callbacks.remove(id);
});
m_request_animation_frame_callbacks.set(callback->id(), callback);
@@ -399,10 +398,11 @@ void Window::fire_a_page_transition_event(FlyString const& event_name, bool pers
}
// https://html.spec.whatwg.org/#dom-queuemicrotask
-void Window::queue_microtask(JS::FunctionObject& callback)
+void Window::queue_microtask(NonnullOwnPtr<Bindings::CallbackType> callback)
{
- HTML::queue_a_microtask(&associated_document(), [&callback, handle = JS::make_handle(&callback)]() {
- auto result = JS::call(callback.global_object(), callback, JS::js_null());
+ // The queueMicrotask(callback) method must queue a microtask to invoke callback,
+ HTML::queue_a_microtask(&associated_document(), [callback = move(callback)]() mutable {
+ auto result = Bindings::IDL::invoke_callback(*callback, {});
// and if callback throws an exception, report the exception.
if (result.is_error())
HTML::report_exception(result);
diff --git a/Userland/Libraries/LibWeb/DOM/Window.h b/Userland/Libraries/LibWeb/DOM/Window.h
index 93f7d1c6ba..f4ff0187cc 100644
--- a/Userland/Libraries/LibWeb/DOM/Window.h
+++ b/Userland/Libraries/LibWeb/DOM/Window.h
@@ -47,15 +47,15 @@ public:
void alert(String const&);
bool confirm(String const&);
String prompt(String const&, String const&);
- i32 request_animation_frame(JS::FunctionObject&);
+ i32 request_animation_frame(NonnullOwnPtr<Bindings::CallbackType> js_callback);
void cancel_animation_frame(i32);
- i32 set_timeout(JS::FunctionObject&, i32);
- i32 set_interval(JS::FunctionObject&, i32);
+ i32 set_timeout(NonnullOwnPtr<Bindings::CallbackType> callback, i32);
+ i32 set_interval(NonnullOwnPtr<Bindings::CallbackType> callback, i32);
void clear_timeout(i32);
void clear_interval(i32);
- void queue_microtask(JS::FunctionObject&);
+ void queue_microtask(NonnullOwnPtr<Bindings::CallbackType> callback);
int inner_width() const;
int inner_height() const;
diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h
index 39d50aa604..f0da95d503 100644
--- a/Userland/Libraries/LibWeb/Forward.h
+++ b/Userland/Libraries/LibWeb/Forward.h
@@ -312,6 +312,7 @@ namespace Web::Bindings {
class AbortControllerWrapper;
class AbortSignalWrapper;
class AttributeWrapper;
+struct CallbackType;
class CanvasGradientWrapper;
class CanvasRenderingContext2DWrapper;
class CharacterDataWrapper;
@@ -438,7 +439,6 @@ class RangePrototype;
class RangeWrapper;
class ResizeObserverWrapper;
class ScreenWrapper;
-class ScriptExecutionContext;
class SelectionWrapper;
class StyleSheetListWrapper;
class StyleSheetWrapper;
diff --git a/Userland/Libraries/LibWeb/HTML/EventHandler.h b/Userland/Libraries/LibWeb/HTML/EventHandler.h
index f1a51f95f9..26d0969495 100644
--- a/Userland/Libraries/LibWeb/HTML/EventHandler.h
+++ b/Userland/Libraries/LibWeb/HTML/EventHandler.h
@@ -7,28 +7,34 @@
#pragma once
#include <AK/String.h>
+#include <AK/Variant.h>
#include <LibJS/Heap/Handle.h>
#include <LibJS/Runtime/FunctionObject.h>
+#include <LibWeb/Bindings/CallbackType.h>
+#include <LibWeb/DOM/EventListener.h>
namespace Web::HTML {
struct EventHandler {
- EventHandler()
- {
- }
-
EventHandler(String s)
- : string(move(s))
+ : value(move(s))
{
}
- EventHandler(JS::Handle<JS::FunctionObject> c)
- : callback(move(c))
+ EventHandler(Bindings::CallbackType c)
+ : value(move(c))
{
}
- String string;
- JS::Handle<JS::FunctionObject> callback;
+ // Either uncompiled source code or a callback.
+ // https://html.spec.whatwg.org/multipage/webappapis.html#event-handler-value
+ // NOTE: This does not contain Empty as part of the optimization of not allocating all event handler attributes up front.
+ // FIXME: The string should actually be an "internal raw uncompiled handler" struct. This struct is just the uncompiled source code plus a source location for reporting parse errors.
+ // https://html.spec.whatwg.org/multipage/webappapis.html#internal-raw-uncompiled-handler
+ Variant<String, Bindings::CallbackType> value;
+
+ // https://html.spec.whatwg.org/multipage/webappapis.html#event-handler-listener
+ RefPtr<DOM::EventListener> listener;
};
}
diff --git a/Userland/Libraries/LibWeb/HTML/GlobalEventHandlers.cpp b/Userland/Libraries/LibWeb/HTML/GlobalEventHandlers.cpp
index 82a3cd5def..e31a3a1d32 100644
--- a/Userland/Libraries/LibWeb/HTML/GlobalEventHandlers.cpp
+++ b/Userland/Libraries/LibWeb/HTML/GlobalEventHandlers.cpp
@@ -17,11 +17,11 @@ namespace Web::HTML {
#undef __ENUMERATE
#define __ENUMERATE(attribute_name, event_name) \
- void GlobalEventHandlers::set_##attribute_name(HTML::EventHandler value) \
+ void GlobalEventHandlers::set_##attribute_name(Optional<Bindings::CallbackType> value) \
{ \
global_event_handlers_to_event_target().set_event_handler_attribute(event_name, move(value)); \
} \
- HTML::EventHandler GlobalEventHandlers::attribute_name() \
+ Bindings::CallbackType* GlobalEventHandlers::attribute_name() \
{ \
return global_event_handlers_to_event_target().event_handler_attribute(event_name); \
}
diff --git a/Userland/Libraries/LibWeb/HTML/GlobalEventHandlers.h b/Userland/Libraries/LibWeb/HTML/GlobalEventHandlers.h
index 9d8ee80e00..7fad45cc29 100644
--- a/Userland/Libraries/LibWeb/HTML/GlobalEventHandlers.h
+++ b/Userland/Libraries/LibWeb/HTML/GlobalEventHandlers.h
@@ -84,9 +84,9 @@ public:
virtual ~GlobalEventHandlers();
#undef __ENUMERATE
-#define __ENUMERATE(attribute_name, event_name) \
- void set_##attribute_name(HTML::EventHandler); \
- HTML::EventHandler attribute_name();
+#define __ENUMERATE(attribute_name, event_name) \
+ void set_##attribute_name(Optional<Bindings::CallbackType>); \
+ Bindings::CallbackType* attribute_name();
ENUMERATE_GLOBAL_EVENT_HANDLERS(__ENUMERATE)
#undef __ENUMERATE
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp
index 296e2b07e0..170d18fd94 100644
--- a/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp
+++ b/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp
@@ -172,10 +172,12 @@ void HTMLElement::parse_attribute(const FlyString& name, const String& value)
{
Element::parse_attribute(name, value);
+ // 1. If namespace is not null, or localName is not the name of an event handler content attribute on element, then return.
+ // FIXME: Add the namespace part once we support attribute namespaces.
#undef __ENUMERATE
-#define __ENUMERATE(attribute_name, event_name) \
- if (name == HTML::AttributeNames::attribute_name) { \
- set_event_handler_attribute(event_name, EventHandler { value }); \
+#define __ENUMERATE(attribute_name, event_name) \
+ if (name == HTML::AttributeNames::attribute_name) { \
+ element_event_handler_attribute_changed(event_name, value); \
}
ENUMERATE_GLOBAL_EVENT_HANDLERS(__ENUMERATE)
#undef __ENUMERATE
diff --git a/Userland/Libraries/LibWeb/HTML/MessageChannel.cpp b/Userland/Libraries/LibWeb/HTML/MessageChannel.cpp
index f45d7c081f..bd47c9da86 100644
--- a/Userland/Libraries/LibWeb/HTML/MessageChannel.cpp
+++ b/Userland/Libraries/LibWeb/HTML/MessageChannel.cpp
@@ -10,15 +10,13 @@
namespace Web::HTML {
-MessageChannel::MessageChannel(Bindings::WindowObject& global_object)
+MessageChannel::MessageChannel()
{
- auto& context = global_object.impl().associated_document();
-
// 1. Set this's port 1 to a new MessagePort in this's relevant Realm.
- m_port1 = MessagePort::create(context);
+ m_port1 = MessagePort::create();
// 2. Set this's port 2 to a new MessagePort in this's relevant Realm.
- m_port2 = MessagePort::create(context);
+ m_port2 = MessagePort::create();
// 3. Entangle this's port 1 and this's port 2.
m_port1->entangle_with({}, *m_port2);
diff --git a/Userland/Libraries/LibWeb/HTML/MessageChannel.h b/Userland/Libraries/LibWeb/HTML/MessageChannel.h
index 9abe9b91d1..a61851d21c 100644
--- a/Userland/Libraries/LibWeb/HTML/MessageChannel.h
+++ b/Userland/Libraries/LibWeb/HTML/MessageChannel.h
@@ -24,9 +24,9 @@ public:
using RefCounted::ref;
using RefCounted::unref;
- static NonnullRefPtr<MessageChannel> create_with_global_object(Bindings::WindowObject& global_object)
+ static NonnullRefPtr<MessageChannel> create_with_global_object(Bindings::WindowObject&)
{
- return adopt_ref(*new MessageChannel(global_object));
+ return adopt_ref(*new MessageChannel());
}
virtual ~MessageChannel() override;
@@ -38,7 +38,7 @@ public:
MessagePort const* port2() const { return m_port2; }
private:
- explicit MessageChannel(Bindings::WindowObject&);
+ MessageChannel();
RefPtr<MessagePort> m_port1;
RefPtr<MessagePort> m_port2;
diff --git a/Userland/Libraries/LibWeb/HTML/MessagePort.cpp b/Userland/Libraries/LibWeb/HTML/MessagePort.cpp
index 2ae143647e..b467d84020 100644
--- a/Userland/Libraries/LibWeb/HTML/MessagePort.cpp
+++ b/Userland/Libraries/LibWeb/HTML/MessagePort.cpp
@@ -5,7 +5,6 @@
*/
#include <LibWeb/Bindings/MessagePortWrapper.h>
-#include <LibWeb/Bindings/ScriptExecutionContext.h>
#include <LibWeb/DOM/EventDispatcher.h>
#include <LibWeb/HTML/EventHandler.h>
#include <LibWeb/HTML/EventLoop/EventLoop.h>
@@ -15,8 +14,8 @@
namespace Web::HTML {
-MessagePort::MessagePort(Bindings::ScriptExecutionContext& context)
- : DOM::EventTarget(context)
+MessagePort::MessagePort()
+ : DOM::EventTarget()
{
}
@@ -109,14 +108,14 @@ void MessagePort::close()
}
#undef __ENUMERATE
-#define __ENUMERATE(attribute_name, event_name) \
- void MessagePort::set_##attribute_name(HTML::EventHandler value) \
- { \
- set_event_handler_attribute(event_name, move(value)); \
- } \
- HTML::EventHandler MessagePort::attribute_name() \
- { \
- return event_handler_attribute(event_name); \
+#define __ENUMERATE(attribute_name, event_name) \
+ void MessagePort::set_##attribute_name(Optional<Bindings::CallbackType> value) \
+ { \
+ set_event_handler_attribute(event_name, move(value)); \
+ } \
+ Bindings::CallbackType* MessagePort::attribute_name() \
+ { \
+ return event_handler_attribute(event_name); \
}
ENUMERATE_MESSAGE_PORT_EVENT_HANDLERS(__ENUMERATE)
#undef __ENUMERATE
diff --git a/Userland/Libraries/LibWeb/HTML/MessagePort.h b/Userland/Libraries/LibWeb/HTML/MessagePort.h
index d6db384464..926781f0f1 100644
--- a/Userland/Libraries/LibWeb/HTML/MessagePort.h
+++ b/Userland/Libraries/LibWeb/HTML/MessagePort.h
@@ -30,9 +30,9 @@ public:
using RefCounted::ref;
using RefCounted::unref;
- static NonnullRefPtr<MessagePort> create(Bindings::ScriptExecutionContext& context)
+ static NonnullRefPtr<MessagePort> create()
{
- return adopt_ref(*new MessagePort(context));
+ return adopt_ref(*new MessagePort());
}
virtual ~MessagePort() override;
@@ -53,14 +53,14 @@ public:
void close();
#undef __ENUMERATE
-#define __ENUMERATE(attribute_name, event_name) \
- void set_##attribute_name(HTML::EventHandler); \
- HTML::EventHandler attribute_name();
+#define __ENUMERATE(attribute_name, event_name) \
+ void set_##attribute_name(Optional<Bindings::CallbackType>); \
+ Bindings::CallbackType* attribute_name();
ENUMERATE_MESSAGE_PORT_EVENT_HANDLERS(__ENUMERATE)
#undef __ENUMERATE
private:
- explicit MessagePort(Bindings::ScriptExecutionContext&);
+ MessagePort();
bool is_entangled() const { return m_remote_port; }
void disentangle();
diff --git a/Userland/Libraries/LibWeb/HTML/WebSocket.cpp b/Userland/Libraries/LibWeb/HTML/WebSocket.cpp
index 5555b53471..f20fe1a87e 100644
--- a/Userland/Libraries/LibWeb/HTML/WebSocket.cpp
+++ b/Userland/Libraries/LibWeb/HTML/WebSocket.cpp
@@ -67,7 +67,7 @@ DOM::ExceptionOr<NonnullRefPtr<WebSocket>> WebSocket::create_with_global_object(
}
WebSocket::WebSocket(DOM::Window& window, AK::URL& url)
- : EventTarget(static_cast<Bindings::ScriptExecutionContext&>(window.associated_document()))
+ : EventTarget()
, m_window(window)
{
// FIXME: Integrate properly with FETCH as per https://fetch.spec.whatwg.org/#websocket-opening-handshake
@@ -226,14 +226,14 @@ JS::Object* WebSocket::create_wrapper(JS::GlobalObject& global_object)
}
#undef __ENUMERATE
-#define __ENUMERATE(attribute_name, event_name) \
- void WebSocket::set_##attribute_name(HTML::EventHandler value) \
- { \
- set_event_handler_attribute(event_name, move(value)); \
- } \
- HTML::EventHandler WebSocket::attribute_name() \
- { \
- return event_handler_attribute(event_name); \
+#define __ENUMERATE(attribute_name, event_name) \
+ void WebSocket::set_##attribute_name(Optional<Bindings::CallbackType> value) \
+ { \
+ set_event_handler_attribute(event_name, move(value)); \
+ } \
+ Bindings::CallbackType* WebSocket::attribute_name() \
+ { \
+ return event_handler_attribute(event_name); \
}
ENUMERATE_WEBSOCKET_EVENT_HANDLERS(__ENUMERATE)
#undef __ENUMERATE
diff --git a/Userland/Libraries/LibWeb/HTML/WebSocket.h b/Userland/Libraries/LibWeb/HTML/WebSocket.h
index ba4990e3d5..29a141be89 100644
--- a/Userland/Libraries/LibWeb/HTML/WebSocket.h
+++ b/Userland/Libraries/LibWeb/HTML/WebSocket.h
@@ -74,9 +74,9 @@ public:
String url() const { return m_url.to_string(); }
#undef __ENUMERATE
-#define __ENUMERATE(attribute_name, event_name) \
- void set_##attribute_name(HTML::EventHandler); \
- HTML::EventHandler attribute_name();
+#define __ENUMERATE(attribute_name, event_name) \
+ void set_##attribute_name(Optional<Bindings::CallbackType>); \
+ Bindings::CallbackType* attribute_name();
ENUMERATE_WEBSOCKET_EVENT_HANDLERS(__ENUMERATE)
#undef __ENUMERATE
diff --git a/Userland/Libraries/LibWeb/HighResolutionTime/Performance.cpp b/Userland/Libraries/LibWeb/HighResolutionTime/Performance.cpp
index 8f584c7036..9b6863be21 100644
--- a/Userland/Libraries/LibWeb/HighResolutionTime/Performance.cpp
+++ b/Userland/Libraries/LibWeb/HighResolutionTime/Performance.cpp
@@ -14,7 +14,7 @@
namespace Web::HighResolutionTime {
Performance::Performance(DOM::Window& window)
- : DOM::EventTarget(static_cast<Bindings::ScriptExecutionContext&>(window.associated_document()))
+ : DOM::EventTarget()
, m_window(window)
, m_timing(make<NavigationTiming::PerformanceTiming>(window))
{
diff --git a/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.cpp b/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.cpp
index dd45b90027..d7224f87a5 100644
--- a/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.cpp
+++ b/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.cpp
@@ -28,7 +28,7 @@
namespace Web::XHR {
XMLHttpRequest::XMLHttpRequest(DOM::Window& window)
- : XMLHttpRequestEventTarget(static_cast<Bindings::ScriptExecutionContext&>(window.associated_document()))
+ : XMLHttpRequestEventTarget()
, m_window(window)
{
}
@@ -263,12 +263,12 @@ JS::Object* XMLHttpRequest::create_wrapper(JS::GlobalObject& global_object)
return wrap(global_object, *this);
}
-HTML::EventHandler XMLHttpRequest::onreadystatechange()
+Bindings::CallbackType* XMLHttpRequest::onreadystatechange()
{
return event_handler_attribute(Web::XHR::EventNames::readystatechange);
}
-void XMLHttpRequest::set_onreadystatechange(HTML::EventHandler value)
+void XMLHttpRequest::set_onreadystatechange(Optional<Bindings::CallbackType> value)
{
set_event_handler_attribute(Web::XHR::EventNames::readystatechange, move(value));
}
diff --git a/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.h b/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.h
index 1905f169ae..62e7e420b4 100644
--- a/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.h
+++ b/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.h
@@ -59,8 +59,8 @@ public:
String get_response_header(const String& name) { return m_response_headers.get(name).value_or({}); }
String get_all_response_headers() const;
- HTML::EventHandler onreadystatechange();
- void set_onreadystatechange(HTML::EventHandler);
+ Bindings::CallbackType* onreadystatechange();
+ void set_onreadystatechange(Optional<Bindings::CallbackType>);
private:
virtual void ref_event_target() override { ref(); }
diff --git a/Userland/Libraries/LibWeb/XHR/XMLHttpRequestEventTarget.cpp b/Userland/Libraries/LibWeb/XHR/XMLHttpRequestEventTarget.cpp
index 4d7fd01e3b..ee2114d7b9 100644
--- a/Userland/Libraries/LibWeb/XHR/XMLHttpRequestEventTarget.cpp
+++ b/Userland/Libraries/LibWeb/XHR/XMLHttpRequestEventTarget.cpp
@@ -11,14 +11,14 @@
namespace Web::XHR {
#undef __ENUMERATE
-#define __ENUMERATE(attribute_name, event_name) \
- void XMLHttpRequestEventTarget::set_##attribute_name(HTML::EventHandler value) \
- { \
- set_event_handler_attribute(event_name, move(value)); \
- } \
- HTML::EventHandler XMLHttpRequestEventTarget::attribute_name() \
- { \
- return event_handler_attribute(event_name); \
+#define __ENUMERATE(attribute_name, event_name) \
+ void XMLHttpRequestEventTarget::set_##attribute_name(Optional<Bindings::CallbackType> value) \
+ { \
+ set_event_handler_attribute(event_name, move(value)); \
+ } \
+ Bindings::CallbackType* XMLHttpRequestEventTarget::attribute_name() \
+ { \
+ return event_handler_attribute(event_name); \
}
ENUMERATE_XML_HTTP_REQUEST_EVENT_TARGET_EVENT_HANDLERS(__ENUMERATE)
#undef __ENUMERATE
diff --git a/Userland/Libraries/LibWeb/XHR/XMLHttpRequestEventTarget.h b/Userland/Libraries/LibWeb/XHR/XMLHttpRequestEventTarget.h
index 043d72b134..9a3e1a8adb 100644
--- a/Userland/Libraries/LibWeb/XHR/XMLHttpRequestEventTarget.h
+++ b/Userland/Libraries/LibWeb/XHR/XMLHttpRequestEventTarget.h
@@ -30,15 +30,15 @@ public:
virtual ~XMLHttpRequestEventTarget() override {};
#undef __ENUMERATE
-#define __ENUMERATE(attribute_name, event_name) \
- void set_##attribute_name(HTML::EventHandler); \
- HTML::EventHandler attribute_name();
+#define __ENUMERATE(attribute_name, event_name) \
+ void set_##attribute_name(Optional<Bindings::CallbackType>); \
+ Bindings::CallbackType* attribute_name();
ENUMERATE_XML_HTTP_REQUEST_EVENT_TARGET_EVENT_HANDLERS(__ENUMERATE)
#undef __ENUMERATE
protected:
- explicit XMLHttpRequestEventTarget(Bindings::ScriptExecutionContext& script_execution_context)
- : DOM::EventTarget(script_execution_context)
+ XMLHttpRequestEventTarget()
+ : DOM::EventTarget()
{
}