1
Fork 0
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:
Andreas Kling 2022-08-08 14:12:01 +02:00
parent 967a3e5a45
commit 8cda70c892
57 changed files with 425 additions and 345 deletions

View 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);
}
}

View file

@ -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;
};
}

View file

@ -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)
{
}
}
}

View file

@ -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;
};
}
}

View file

@ -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 functions return type.
@ -106,7 +105,7 @@ JS::Completion invoke_callback(Bindings::CallbackType& callback, Optional<JS::Va
// 5. Let realm be Fs 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 realms 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()) {

View file

@ -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 Os 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)), ...);

View file

@ -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)

View file

@ -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