mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 22:37:35 +00:00
LibWeb: Move event listeners, handlers and callbacks to the GC heap
This patch moves the following things to being GC-allocated: - Bindings::CallbackType - HTML::EventHandler - DOM::IDLEventListener - DOM::DOMEventListener - DOM::NodeFilter Note that we only use PlatformObject for things that might be exposed to web content. Anything that is only used internally inherits directly from JS::Cell instead, making them a bit more lightweight.
This commit is contained in:
parent
967a3e5a45
commit
8cda70c892
57 changed files with 425 additions and 345 deletions
25
Userland/Libraries/LibWeb/Bindings/CallbackType.cpp
Normal file
25
Userland/Libraries/LibWeb/Bindings/CallbackType.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/Object.h>
|
||||
#include <LibWeb/Bindings/CallbackType.h>
|
||||
|
||||
namespace Web::Bindings {
|
||||
|
||||
CallbackType::CallbackType(JS::Object& callback, HTML::EnvironmentSettingsObject& callback_context)
|
||||
: callback(callback)
|
||||
, callback_context(callback_context)
|
||||
{
|
||||
}
|
||||
|
||||
StringView CallbackType::class_name() const { return "CallbackType"sv; }
|
||||
void CallbackType::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Cell::visit_edges(visitor);
|
||||
visitor.visit(&callback);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,28 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
|
||||
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Heap/Handle.h>
|
||||
#include <LibJS/Heap/Cell.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
|
||||
namespace Web::Bindings {
|
||||
|
||||
// https://heycam.github.io/webidl/#idl-callback-interface
|
||||
struct CallbackType {
|
||||
CallbackType(JS::Handle<JS::Object> callback, HTML::EnvironmentSettingsObject& callback_context)
|
||||
: callback(move(callback))
|
||||
, callback_context(callback_context)
|
||||
{
|
||||
}
|
||||
class CallbackType final : public JS::Cell {
|
||||
public:
|
||||
CallbackType(JS::Object& callback, HTML::EnvironmentSettingsObject& callback_context);
|
||||
|
||||
JS::Handle<JS::Object> callback;
|
||||
JS::Object& callback;
|
||||
|
||||
// https://heycam.github.io/webidl/#dfn-callback-context
|
||||
HTML::EnvironmentSettingsObject& callback_context;
|
||||
|
||||
private:
|
||||
virtual StringView class_name() const override;
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/FunctionObject.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibWeb/Bindings/EventListenerWrapper.h>
|
||||
#include <LibWeb/DOM/IDLEventListener.h>
|
||||
|
||||
namespace Web {
|
||||
namespace Bindings {
|
||||
|
||||
EventListenerWrapper::EventListenerWrapper(JS::Realm& realm, DOM::IDLEventListener& impl)
|
||||
: Wrapper(*realm.intrinsics().object_prototype())
|
||||
, m_impl(impl)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/Bindings/Wrapper.h>
|
||||
|
||||
namespace Web {
|
||||
namespace Bindings {
|
||||
|
||||
class EventListenerWrapper final : public Wrapper {
|
||||
JS_OBJECT(EventListenerWrapper, Wrapper);
|
||||
|
||||
public:
|
||||
EventListenerWrapper(JS::Realm& realm, DOM::IDLEventListener&);
|
||||
virtual ~EventListenerWrapper() override = default;
|
||||
|
||||
DOM::IDLEventListener& impl() { return *m_impl; }
|
||||
DOM::IDLEventListener const& impl() const { return *m_impl; }
|
||||
|
||||
private:
|
||||
NonnullRefPtr<DOM::IDLEventListener> m_impl;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -92,11 +92,10 @@ JS::Completion invoke_callback(Bindings::CallbackType& callback, Optional<JS::Va
|
|||
this_argument = JS::js_undefined();
|
||||
|
||||
// 3. Let F be the ECMAScript object corresponding to callable.
|
||||
auto* function_object = callback.callback.cell();
|
||||
VERIFY(function_object);
|
||||
auto& function_object = callback.callback;
|
||||
|
||||
// 4. If ! IsCallable(F) is false:
|
||||
if (!function_object->is_function()) {
|
||||
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.
|
||||
|
@ -106,7 +105,7 @@ JS::Completion invoke_callback(Bindings::CallbackType& callback, Optional<JS::Va
|
|||
|
||||
// 5. Let realm be F’s associated Realm.
|
||||
// See the comment about associated realm on step 4 of call_user_object_operation.
|
||||
auto& realm = function_object->shape().realm();
|
||||
auto& realm = function_object.shape().realm();
|
||||
|
||||
// 6. Let relevant settings be realm’s settings object.
|
||||
auto& relevant_settings = verify_cast<HTML::EnvironmentSettingsObject>(*realm.host_defined());
|
||||
|
@ -124,8 +123,8 @@ JS::Completion invoke_callback(Bindings::CallbackType& callback, Optional<JS::Va
|
|||
// 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& vm = function_object->vm();
|
||||
auto call_result = JS::call(vm, verify_cast<JS::FunctionObject>(*function_object), this_argument.value(), move(args));
|
||||
auto& vm = function_object.vm();
|
||||
auto call_result = JS::call(vm, verify_cast<JS::FunctionObject>(function_object), this_argument.value(), move(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()) {
|
||||
|
|
|
@ -54,7 +54,7 @@ JS::Completion call_user_object_operation(Bindings::CallbackType& callback, Stri
|
|||
this_argument = JS::js_undefined();
|
||||
|
||||
// 3. Let O be the ECMAScript object corresponding to value.
|
||||
auto& object = *callback.callback.cell();
|
||||
auto& object = callback.callback;
|
||||
|
||||
// 4. Let realm be O’s associated Realm.
|
||||
auto& realm = object.shape().realm();
|
||||
|
@ -126,8 +126,7 @@ JS::Completion invoke_callback(Bindings::CallbackType& callback, Optional<JS::Va
|
|||
template<typename... Args>
|
||||
JS::Completion invoke_callback(Bindings::CallbackType& callback, Optional<JS::Value> this_argument, Args&&... args)
|
||||
{
|
||||
auto* function_object = callback.callback.cell();
|
||||
VERIFY(function_object);
|
||||
auto& function_object = callback.callback;
|
||||
|
||||
JS::MarkedVector<JS::Value> arguments_list { function_object->vm().heap() };
|
||||
(arguments_list.append(forward<Args>(args)), ...);
|
||||
|
|
|
@ -229,7 +229,7 @@ JS_DEFINE_NATIVE_FUNCTION(WindowObject::prompt)
|
|||
static JS::ThrowCompletionOr<TimerHandler> make_timer_handler(JS::VM& vm, JS::Value handler)
|
||||
{
|
||||
if (handler.is_function())
|
||||
return Bindings::CallbackType(JS::make_handle<JS::Object>(handler.as_function()), HTML::incumbent_settings_object());
|
||||
return JS::make_handle(vm.heap().allocate_without_realm<Bindings::CallbackType>(handler.as_function(), HTML::incumbent_settings_object()));
|
||||
return TRY(handler.to_string(vm));
|
||||
}
|
||||
|
||||
|
@ -311,8 +311,8 @@ JS_DEFINE_NATIVE_FUNCTION(WindowObject::request_animation_frame)
|
|||
auto* callback_object = TRY(vm.argument(0).to_object(vm));
|
||||
if (!callback_object->is_function())
|
||||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAFunctionNoParam);
|
||||
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)));
|
||||
auto* callback = vm.heap().allocate_without_realm<Bindings::CallbackType>(*callback_object, HTML::incumbent_settings_object());
|
||||
return JS::Value(impl->request_animation_frame(*callback));
|
||||
}
|
||||
|
||||
JS_DEFINE_NATIVE_FUNCTION(WindowObject::cancel_animation_frame)
|
||||
|
@ -334,9 +334,9 @@ JS_DEFINE_NATIVE_FUNCTION(WindowObject::queue_microtask)
|
|||
if (!callback_object->is_function())
|
||||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAFunctionNoParam);
|
||||
|
||||
auto callback = adopt_own(*new Bindings::CallbackType(JS::make_handle(callback_object), HTML::incumbent_settings_object()));
|
||||
auto* callback = vm.heap().allocate_without_realm<Bindings::CallbackType>(*callback_object, HTML::incumbent_settings_object());
|
||||
|
||||
impl->queue_microtask(move(callback));
|
||||
impl->queue_microtask(*callback);
|
||||
return JS::js_undefined();
|
||||
}
|
||||
|
||||
|
@ -350,9 +350,9 @@ JS_DEFINE_NATIVE_FUNCTION(WindowObject::request_idle_callback)
|
|||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAFunctionNoParam);
|
||||
// FIXME: accept options object
|
||||
|
||||
auto callback = adopt_own(*new Bindings::CallbackType(JS::make_handle(callback_object), HTML::incumbent_settings_object()));
|
||||
auto* callback = vm.heap().allocate_without_realm<Bindings::CallbackType>(*callback_object, HTML::incumbent_settings_object());
|
||||
|
||||
return JS::Value(impl->request_idle_callback(move(callback)));
|
||||
return JS::Value(impl->request_idle_callback(*callback));
|
||||
}
|
||||
|
||||
JS_DEFINE_NATIVE_FUNCTION(WindowObject::cancel_idle_callback)
|
||||
|
@ -748,25 +748,26 @@ JS_DEFINE_NATIVE_FUNCTION(WindowObject::name_setter)
|
|||
return JS::js_undefined();
|
||||
}
|
||||
|
||||
#define __ENUMERATE(attribute, event_name) \
|
||||
JS_DEFINE_NATIVE_FUNCTION(WindowObject::attribute##_getter) \
|
||||
{ \
|
||||
auto* impl = TRY(impl_from(vm)); \
|
||||
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)); \
|
||||
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(); \
|
||||
#define __ENUMERATE(attribute, event_name) \
|
||||
JS_DEFINE_NATIVE_FUNCTION(WindowObject::attribute##_getter) \
|
||||
{ \
|
||||
auto* impl = TRY(impl_from(vm)); \
|
||||
auto retval = impl->attribute(); \
|
||||
if (!retval) \
|
||||
return JS::js_null(); \
|
||||
return &retval->callback; \
|
||||
} \
|
||||
JS_DEFINE_NATIVE_FUNCTION(WindowObject::attribute##_setter) \
|
||||
{ \
|
||||
auto* impl = TRY(impl_from(vm)); \
|
||||
auto value = vm.argument(0); \
|
||||
Bindings::CallbackType* cpp_value; \
|
||||
if (value.is_object()) { \
|
||||
cpp_value = vm.heap().allocate_without_realm<Bindings::CallbackType>( \
|
||||
value.as_object(), HTML::incumbent_settings_object()); \
|
||||
} \
|
||||
impl->set_##attribute(cpp_value); \
|
||||
return JS::js_undefined(); \
|
||||
}
|
||||
ENUMERATE_GLOBAL_EVENT_HANDLERS(__ENUMERATE)
|
||||
ENUMERATE_WINDOW_EVENT_HANDLERS(__ENUMERATE)
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace Web {
|
|||
namespace Bindings {
|
||||
|
||||
// https://html.spec.whatwg.org/#timerhandler
|
||||
using TimerHandler = Variant<CallbackType, String>;
|
||||
using TimerHandler = Variant<JS::Handle<CallbackType>, String>;
|
||||
|
||||
class WindowObject
|
||||
: public JS::GlobalObject
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue