mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 01:27:34 +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
|
@ -63,9 +63,9 @@ void AbortSignal::signal_abort(JS::Value reason)
|
|||
dispatch_event(Event::create(HTML::EventNames::abort));
|
||||
}
|
||||
|
||||
void AbortSignal::set_onabort(Optional<Bindings::CallbackType> event_handler)
|
||||
void AbortSignal::set_onabort(Bindings::CallbackType* event_handler)
|
||||
{
|
||||
set_event_handler_attribute(HTML::EventNames::abort, move(event_handler));
|
||||
set_event_handler_attribute(HTML::EventNames::abort, event_handler);
|
||||
}
|
||||
|
||||
Bindings::CallbackType* AbortSignal::onabort()
|
||||
|
|
|
@ -47,7 +47,7 @@ public:
|
|||
|
||||
void signal_abort(JS::Value reason);
|
||||
|
||||
void set_onabort(Optional<Bindings::CallbackType>);
|
||||
void set_onabort(Bindings::CallbackType*);
|
||||
Bindings::CallbackType* onabort();
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-abortsignal-reason
|
||||
|
|
|
@ -9,6 +9,14 @@
|
|||
#include <LibWeb/DOM/IDLEventListener.h>
|
||||
|
||||
namespace Web::DOM {
|
||||
|
||||
DOMEventListener::DOMEventListener() = default;
|
||||
DOMEventListener::~DOMEventListener() = default;
|
||||
|
||||
void DOMEventListener::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Cell::visit_edges(visitor);
|
||||
visitor.visit(callback.ptr());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,13 +7,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/FlyString.h>
|
||||
#include <LibJS/Heap/GCPtr.h>
|
||||
#include <LibJS/Runtime/Object.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
|
||||
namespace Web::DOM {
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-event-listener
|
||||
// NOTE: The spec calls this "event listener", and it's *importantly* not the same as "EventListener"
|
||||
class DOMEventListener : public RefCounted<DOMEventListener> {
|
||||
class DOMEventListener : public JS::Cell {
|
||||
public:
|
||||
DOMEventListener();
|
||||
~DOMEventListener();
|
||||
|
@ -22,7 +24,7 @@ public:
|
|||
FlyString type;
|
||||
|
||||
// callback (null or an EventListener object)
|
||||
RefPtr<IDLEventListener> callback;
|
||||
JS::GCPtr<IDLEventListener> callback;
|
||||
|
||||
// signal (null or an AbortSignal object)
|
||||
RefPtr<DOM::AbortSignal> signal;
|
||||
|
@ -38,6 +40,10 @@ public:
|
|||
|
||||
// removed (a boolean for bookkeeping purposes, initially false)
|
||||
bool removed { false };
|
||||
|
||||
private:
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
virtual StringView class_name() const override { return "DOMEventListener"sv; }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1744,15 +1744,15 @@ ExceptionOr<Document::PrefixAndTagName> Document::validate_qualified_name(String
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-document-createnodeiterator
|
||||
NonnullRefPtr<NodeIterator> Document::create_node_iterator(Node& root, unsigned what_to_show, RefPtr<NodeFilter> filter)
|
||||
NonnullRefPtr<NodeIterator> Document::create_node_iterator(Node& root, unsigned what_to_show, NodeFilter* filter)
|
||||
{
|
||||
return NodeIterator::create(root, what_to_show, move(filter));
|
||||
return NodeIterator::create(root, what_to_show, filter);
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-document-createtreewalker
|
||||
NonnullRefPtr<TreeWalker> Document::create_tree_walker(Node& root, unsigned what_to_show, RefPtr<NodeFilter> filter)
|
||||
NonnullRefPtr<TreeWalker> Document::create_tree_walker(Node& root, unsigned what_to_show, NodeFilter* filter)
|
||||
{
|
||||
return TreeWalker::create(root, what_to_show, move(filter));
|
||||
return TreeWalker::create(root, what_to_show, filter);
|
||||
}
|
||||
|
||||
void Document::register_node_iterator(Badge<NodeIterator>, NodeIterator& node_iterator)
|
||||
|
|
|
@ -350,8 +350,8 @@ public:
|
|||
};
|
||||
static ExceptionOr<PrefixAndTagName> validate_qualified_name(String const& qualified_name);
|
||||
|
||||
NonnullRefPtr<NodeIterator> create_node_iterator(Node& root, unsigned what_to_show, RefPtr<NodeFilter>);
|
||||
NonnullRefPtr<TreeWalker> create_tree_walker(Node& root, unsigned what_to_show, RefPtr<NodeFilter>);
|
||||
NonnullRefPtr<NodeIterator> create_node_iterator(Node& root, unsigned what_to_show, NodeFilter*);
|
||||
NonnullRefPtr<TreeWalker> create_tree_walker(Node& root, unsigned what_to_show, NodeFilter*);
|
||||
|
||||
void register_node_iterator(Badge<NodeIterator>, NodeIterator&);
|
||||
void unregister_node_iterator(Badge<NodeIterator>, NodeIterator&);
|
||||
|
|
|
@ -57,7 +57,7 @@ static EventTarget* retarget(EventTarget* left, EventTarget* right)
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke
|
||||
bool EventDispatcher::inner_invoke(Event& event, Vector<NonnullRefPtr<DOM::DOMEventListener>>& listeners, Event::Phase phase, bool invocation_target_in_shadow_tree)
|
||||
bool EventDispatcher::inner_invoke(Event& event, Vector<JS::Handle<DOM::DOMEventListener>>& listeners, Event::Phase phase, bool invocation_target_in_shadow_tree)
|
||||
{
|
||||
// 1. Let found be false.
|
||||
bool found = false;
|
||||
|
@ -84,11 +84,11 @@ bool EventDispatcher::inner_invoke(Event& event, Vector<NonnullRefPtr<DOM::DOMEv
|
|||
|
||||
// 5. If listener’s once is true, then remove listener from event’s currentTarget attribute value’s event listener list.
|
||||
if (listener->once)
|
||||
event.current_target()->remove_from_event_listener_list(listener);
|
||||
event.current_target()->remove_from_event_listener_list(*listener);
|
||||
|
||||
// 6. Let global be listener callback’s associated Realm’s global object.
|
||||
auto& callback = listener->callback->callback();
|
||||
auto& realm = callback.callback->shape().realm();
|
||||
auto& realm = callback.callback.shape().realm();
|
||||
auto& global = realm.global_object();
|
||||
|
||||
// 7. Let currentEvent be undefined.
|
||||
|
|
|
@ -18,7 +18,7 @@ public:
|
|||
|
||||
private:
|
||||
static void invoke(Event::PathEntry&, Event&, Event::Phase);
|
||||
static bool inner_invoke(Event&, Vector<NonnullRefPtr<DOM::DOMEventListener>>&, Event::Phase, bool);
|
||||
static bool inner_invoke(Event&, Vector<JS::Handle<DOM::DOMEventListener>>&, Event::Phase, bool);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <LibWeb/Bindings/IDLAbstractOperations.h>
|
||||
#include <LibWeb/Bindings/MainThreadVM.h>
|
||||
#include <LibWeb/DOM/AbortSignal.h>
|
||||
#include <LibWeb/DOM/DOMEventListener.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/DOM/Event.h>
|
||||
#include <LibWeb/DOM/EventDispatcher.h>
|
||||
|
@ -99,89 +100,91 @@ static FlattenedAddEventListenerOptions flatten_add_event_listener_options(Varia
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-eventtarget-addeventlistener
|
||||
void EventTarget::add_event_listener(FlyString const& type, RefPtr<IDLEventListener> callback, Variant<AddEventListenerOptions, bool> const& options)
|
||||
void EventTarget::add_event_listener(FlyString const& type, IDLEventListener* callback, Variant<AddEventListenerOptions, bool> const& options)
|
||||
{
|
||||
// 1. Let capture, passive, once, and signal be the result of flattening more options.
|
||||
auto flattened_options = flatten_add_event_listener_options(options);
|
||||
|
||||
// 2. Add an event listener with this and an event listener whose type is type, callback is callback, capture is capture, passive is passive,
|
||||
// once is once, and signal is signal.
|
||||
auto event_listener = adopt_ref(*new DOMEventListener);
|
||||
|
||||
// FIXME: Don't use main_thread_internal_window_object() when EventTarget can find its own heap.
|
||||
auto* event_listener = Bindings::main_thread_internal_window_object().heap().allocate_without_realm<DOMEventListener>();
|
||||
event_listener->type = type;
|
||||
event_listener->callback = move(callback);
|
||||
event_listener->callback = callback;
|
||||
event_listener->signal = move(flattened_options.signal);
|
||||
event_listener->capture = flattened_options.capture;
|
||||
event_listener->passive = flattened_options.passive;
|
||||
event_listener->once = flattened_options.once;
|
||||
add_an_event_listener(move(event_listener));
|
||||
add_an_event_listener(*event_listener);
|
||||
}
|
||||
|
||||
void EventTarget::add_event_listener_without_options(FlyString const& type, RefPtr<IDLEventListener> callback)
|
||||
void EventTarget::add_event_listener_without_options(FlyString const& type, IDLEventListener& callback)
|
||||
{
|
||||
add_event_listener(type, move(callback), AddEventListenerOptions {});
|
||||
add_event_listener(type, &callback, AddEventListenerOptions {});
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#add-an-event-listener
|
||||
void EventTarget::add_an_event_listener(NonnullRefPtr<DOMEventListener> listener)
|
||||
void EventTarget::add_an_event_listener(DOMEventListener& listener)
|
||||
{
|
||||
// FIXME: 1. If eventTarget is a ServiceWorkerGlobalScope object, its service worker’s script resource’s has ever been evaluated flag is set,
|
||||
// and listener’s type matches the type attribute value of any of the service worker events, then report a warning to the console
|
||||
// that this might not give the expected results. [SERVICE-WORKERS]
|
||||
|
||||
// 2. If listener’s signal is not null and is aborted, then return.
|
||||
if (listener->signal && listener->signal->aborted())
|
||||
if (listener.signal && listener.signal->aborted())
|
||||
return;
|
||||
|
||||
// 3. If listener’s callback is null, then return.
|
||||
if (listener->callback.is_null())
|
||||
if (!listener.callback)
|
||||
return;
|
||||
|
||||
// 4. If eventTarget’s event listener list does not contain an event listener whose type is listener’s type, callback is listener’s callback,
|
||||
// and capture is listener’s capture, then append listener to eventTarget’s event listener list.
|
||||
auto it = m_event_listener_list.find_if([&](auto& entry) {
|
||||
return entry->type == listener->type
|
||||
&& entry->callback->callback().callback.cell() == listener->callback->callback().callback.cell()
|
||||
&& entry->capture == listener->capture;
|
||||
return entry->type == listener.type
|
||||
&& &entry->callback->callback().callback == &listener.callback->callback().callback
|
||||
&& entry->capture == listener.capture;
|
||||
});
|
||||
if (it == m_event_listener_list.end())
|
||||
m_event_listener_list.append(listener);
|
||||
m_event_listener_list.append(JS::make_handle(listener));
|
||||
|
||||
// 5. If listener’s signal is not null, then add the following abort steps to it:
|
||||
if (listener->signal) {
|
||||
listener->signal->add_abort_algorithm([strong_event_target = NonnullRefPtr(*this), listener]() mutable {
|
||||
if (listener.signal) {
|
||||
listener.signal->add_abort_algorithm([strong_event_target = NonnullRefPtr(*this), listener = JS::make_handle(&listener)]() mutable {
|
||||
// 1. Remove an event listener with eventTarget and listener.
|
||||
strong_event_target->remove_an_event_listener(listener);
|
||||
strong_event_target->remove_an_event_listener(*listener);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-eventtarget-removeeventlistener
|
||||
void EventTarget::remove_event_listener(FlyString const& type, RefPtr<IDLEventListener> callback, Variant<EventListenerOptions, bool> const& options)
|
||||
void EventTarget::remove_event_listener(FlyString const& type, IDLEventListener* callback, Variant<EventListenerOptions, bool> const& options)
|
||||
{
|
||||
// 1. Let capture be the result of flattening options.
|
||||
bool capture = flatten_event_listener_options(options);
|
||||
|
||||
// 2. If this’s event listener list contains an event listener whose type is type, callback is callback, and capture is capture,
|
||||
// then remove an event listener with this and that event listener.
|
||||
auto callbacks_match = [&](NonnullRefPtr<DOMEventListener>& entry) {
|
||||
if (entry->callback.is_null() && callback.is_null())
|
||||
auto callbacks_match = [&](DOMEventListener& entry) {
|
||||
if (!entry.callback && !callback)
|
||||
return true;
|
||||
if (entry->callback.is_null() || callback.is_null())
|
||||
if (!entry.callback || !callback)
|
||||
return false;
|
||||
return entry->callback->callback().callback.cell() == callback->callback().callback.cell();
|
||||
return &entry.callback->callback().callback == &callback->callback().callback;
|
||||
};
|
||||
auto it = m_event_listener_list.find_if([&](auto& entry) {
|
||||
return entry->type == type
|
||||
&& callbacks_match(entry)
|
||||
&& callbacks_match(*entry)
|
||||
&& entry->capture == capture;
|
||||
});
|
||||
if (it != m_event_listener_list.end())
|
||||
remove_an_event_listener(*it);
|
||||
remove_an_event_listener(**it);
|
||||
}
|
||||
|
||||
void EventTarget::remove_event_listener_without_options(FlyString const& type, RefPtr<IDLEventListener> callback)
|
||||
void EventTarget::remove_event_listener_without_options(FlyString const& type, IDLEventListener& callback)
|
||||
{
|
||||
remove_event_listener(type, move(callback), EventListenerOptions {});
|
||||
remove_event_listener(type, &callback, EventListenerOptions {});
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#remove-an-event-listener
|
||||
|
@ -192,12 +195,12 @@ void EventTarget::remove_an_event_listener(DOMEventListener& listener)
|
|||
|
||||
// 2. Set listener’s removed to true and remove listener from eventTarget’s event listener list.
|
||||
listener.removed = true;
|
||||
m_event_listener_list.remove_first_matching([&](auto& entry) { return entry.ptr() == &listener; });
|
||||
m_event_listener_list.remove_first_matching([&](auto& entry) { return entry.cell() == &listener; });
|
||||
}
|
||||
|
||||
void EventTarget::remove_from_event_listener_list(DOMEventListener& listener)
|
||||
{
|
||||
m_event_listener_list.remove_first_matching([&](auto& entry) { return entry.ptr() == &listener; });
|
||||
m_event_listener_list.remove_first_matching([&](auto& entry) { return entry.cell() == &listener; });
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-eventtarget-dispatchevent
|
||||
|
@ -309,7 +312,7 @@ Bindings::CallbackType* EventTarget::get_current_value_of_event_handler(FlyStrin
|
|||
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>()) {
|
||||
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;
|
||||
|
@ -332,7 +335,7 @@ Bindings::CallbackType* EventTarget::get_current_value_of_event_handler(FlyStrin
|
|||
return nullptr;
|
||||
|
||||
// 3. Let body be the uncompiled script body in eventHandler's value.
|
||||
auto& body = event_handler.value.get<String>();
|
||||
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.
|
||||
|
||||
|
@ -448,16 +451,16 @@ Bindings::CallbackType* EventTarget::get_current_value_of_event_handler(FlyStrin
|
|||
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 };
|
||||
event_handler->value = realm.heap().allocate_without_realm<Bindings::CallbackType>(*function, settings_object);
|
||||
}
|
||||
|
||||
// 4. Return eventHandler's value.
|
||||
VERIFY(event_handler.value.has<Bindings::CallbackType>());
|
||||
return event_handler.value.get_pointer<Bindings::CallbackType>();
|
||||
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)
|
||||
void EventTarget::set_event_handler_attribute(FlyString const& name, 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);
|
||||
|
@ -467,7 +470,7 @@ void EventTarget::set_event_handler_attribute(FlyString const& name, Optional<Bi
|
|||
return;
|
||||
|
||||
// 3. If the given value is null, then deactivate an event handler given eventTarget and name.
|
||||
if (!value.has_value()) {
|
||||
if (!value) {
|
||||
event_target->deactivate_event_handler(name);
|
||||
return;
|
||||
}
|
||||
|
@ -482,25 +485,25 @@ void EventTarget::set_event_handler_attribute(FlyString const& name, Optional<Bi
|
|||
// 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()) };
|
||||
auto* new_event_handler = Bindings::main_thread_internal_window_object().heap().allocate_without_realm<HTML::EventHandler>(*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);
|
||||
event_target->activate_event_handler(name, *new_event_handler);
|
||||
|
||||
handler_map.set(name, move(new_event_handler));
|
||||
handler_map.set(name, JS::make_handle(new_event_handler));
|
||||
return;
|
||||
}
|
||||
|
||||
auto& event_handler = event_handler_iterator->value;
|
||||
|
||||
event_handler.value = move(value.value());
|
||||
event_handler->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);
|
||||
event_target->activate_event_handler(name, *event_handler);
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#activate-an-event-handler
|
||||
|
@ -516,8 +519,8 @@ void EventTarget::activate_event_handler(FlyString const& name, HTML::EventHandl
|
|||
|
||||
JS::Realm* realm = nullptr;
|
||||
// See step 3.1. in get_current_value_of_event_handler(), which explains these assumptions.
|
||||
if (event_handler.value.has<Bindings::CallbackType>()) {
|
||||
realm = &event_handler.value.get<Bindings::CallbackType>().callback_context.realm();
|
||||
if (event_handler.value.has<Bindings::CallbackType*>()) {
|
||||
realm = &event_handler.value.get<Bindings::CallbackType*>()->callback_context.realm();
|
||||
} else if (is<Element>(this)) {
|
||||
realm = &verify_cast<Element>(*this).document().realm();
|
||||
} else {
|
||||
|
@ -555,15 +558,15 @@ void EventTarget::activate_event_handler(FlyString const& name, HTML::EventHandl
|
|||
0, "", realm);
|
||||
|
||||
// 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>(*realm->host_defined()) };
|
||||
auto* callback = realm->heap().allocate_without_realm<Bindings::CallbackType>(*callback_function, verify_cast<HTML::EnvironmentSettingsObject>(*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 DOMEventListener);
|
||||
auto* listener = realm->heap().allocate_without_realm<DOMEventListener>();
|
||||
listener->type = name;
|
||||
listener->callback = adopt_ref(*new IDLEventListener(move(callback)));
|
||||
listener->callback = IDLEventListener::create(*realm, *callback).ptr();
|
||||
|
||||
// 6. Add an event listener with eventTarget and listener.
|
||||
add_an_event_listener(listener);
|
||||
add_an_event_listener(*listener);
|
||||
|
||||
// 7. Set eventHandler's listener to listener.
|
||||
event_handler.listener = listener;
|
||||
|
@ -585,12 +588,12 @@ void EventTarget::deactivate_event_handler(FlyString const& name)
|
|||
// 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) {
|
||||
remove_an_event_listener(*event_handler.listener);
|
||||
if (event_handler->listener) {
|
||||
remove_an_event_listener(*event_handler->listener);
|
||||
}
|
||||
|
||||
// 6. Set eventHandler's listener to null.
|
||||
event_handler.listener = nullptr;
|
||||
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.
|
||||
|
@ -617,7 +620,7 @@ JS::ThrowCompletionOr<void> EventTarget::process_event_handler_for_event(FlyStri
|
|||
JS::Completion return_value_or_error;
|
||||
|
||||
// Needed for wrapping.
|
||||
auto* callback_object = callback->callback.cell();
|
||||
auto* callback_object = &callback->callback;
|
||||
auto& realm = callback_object->shape().realm();
|
||||
|
||||
if (special_error_event_handling) {
|
||||
|
@ -717,20 +720,20 @@ void EventTarget::element_event_handler_attribute_changed(FlyString const& local
|
|||
// NOTE: See the optimization comments in set_event_handler_attribute.
|
||||
|
||||
if (event_handler_iterator == handler_map.end()) {
|
||||
HTML::EventHandler new_event_handler { value };
|
||||
auto* new_event_handler = Bindings::main_thread_internal_window_object().heap().allocate_without_realm<HTML::EventHandler>(value);
|
||||
|
||||
// 6. Activate an event handler given eventTarget and name.
|
||||
event_target->activate_event_handler(local_name, new_event_handler);
|
||||
event_target->activate_event_handler(local_name, *new_event_handler);
|
||||
|
||||
handler_map.set(local_name, move(new_event_handler));
|
||||
handler_map.set(local_name, JS::make_handle(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);
|
||||
event_handler->value = value;
|
||||
event_target->activate_event_handler(local_name, *event_handler);
|
||||
}
|
||||
|
||||
bool EventTarget::dispatch_event(NonnullRefPtr<Event> event)
|
||||
|
|
|
@ -29,12 +29,12 @@ public:
|
|||
|
||||
virtual bool is_focusable() const { return false; }
|
||||
|
||||
void add_event_listener(FlyString const& type, RefPtr<IDLEventListener> callback, Variant<AddEventListenerOptions, bool> const& options);
|
||||
void remove_event_listener(FlyString const& type, RefPtr<IDLEventListener> callback, Variant<EventListenerOptions, bool> const& options);
|
||||
void add_event_listener(FlyString const& type, IDLEventListener* callback, Variant<AddEventListenerOptions, bool> const& options);
|
||||
void remove_event_listener(FlyString const& type, IDLEventListener* callback, Variant<EventListenerOptions, bool> const& options);
|
||||
|
||||
// NOTE: These are for internal use only. They operate as though addEventListener(type, callback) was called instead of addEventListener(type, callback, options).
|
||||
void add_event_listener_without_options(FlyString const& type, RefPtr<IDLEventListener> callback);
|
||||
void remove_event_listener_without_options(FlyString const& type, RefPtr<IDLEventListener> callback);
|
||||
void add_event_listener_without_options(FlyString const& type, IDLEventListener& callback);
|
||||
void remove_event_listener_without_options(FlyString const& type, IDLEventListener& callback);
|
||||
|
||||
virtual bool dispatch_event(NonnullRefPtr<Event>);
|
||||
ExceptionOr<bool> dispatch_event_binding(NonnullRefPtr<Event>);
|
||||
|
@ -43,7 +43,7 @@ public:
|
|||
|
||||
virtual EventTarget* get_parent(Event const&) { return nullptr; }
|
||||
|
||||
void add_an_event_listener(NonnullRefPtr<DOMEventListener>);
|
||||
void add_an_event_listener(DOMEventListener&);
|
||||
void remove_an_event_listener(DOMEventListener&);
|
||||
void remove_from_event_listener_list(DOMEventListener&);
|
||||
|
||||
|
@ -58,7 +58,7 @@ public:
|
|||
virtual void legacy_cancelled_activation_behavior_was_not_called() { }
|
||||
|
||||
Bindings::CallbackType* event_handler_attribute(FlyString const& name);
|
||||
void set_event_handler_attribute(FlyString const& name, Optional<Bindings::CallbackType>);
|
||||
void set_event_handler_attribute(FlyString const& name, Bindings::CallbackType*);
|
||||
|
||||
protected:
|
||||
EventTarget();
|
||||
|
@ -69,11 +69,11 @@ protected:
|
|||
void element_event_handler_attribute_changed(FlyString const& local_name, String const& value);
|
||||
|
||||
private:
|
||||
Vector<NonnullRefPtr<DOMEventListener>> m_event_listener_list;
|
||||
Vector<JS::Handle<DOMEventListener>> m_event_listener_list;
|
||||
|
||||
// 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;
|
||||
HashMap<FlyString, JS::Handle<HTML::EventHandler>> m_event_handler_map;
|
||||
|
||||
Bindings::CallbackType* get_current_value_of_event_handler(FlyString const& name);
|
||||
void activate_event_handler(FlyString const& name, HTML::EventHandler& event_handler);
|
||||
|
|
29
Userland/Libraries/LibWeb/DOM/IDLEventListener.cpp
Normal file
29
Userland/Libraries/LibWeb/DOM/IDLEventListener.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/Bindings/WindowObject.h>
|
||||
#include <LibWeb/DOM/IDLEventListener.h>
|
||||
|
||||
namespace Web::DOM {
|
||||
|
||||
JS::NonnullGCPtr<IDLEventListener> IDLEventListener::create(JS::Realm& realm, JS::NonnullGCPtr<Bindings::CallbackType> callback)
|
||||
{
|
||||
return *realm.heap().allocate<IDLEventListener>(realm, realm, move(callback));
|
||||
}
|
||||
|
||||
IDLEventListener::IDLEventListener(JS::Realm& realm, JS::NonnullGCPtr<Bindings::CallbackType> callback)
|
||||
: JS::Object(*realm.intrinsics().object_prototype())
|
||||
, m_callback(move(callback))
|
||||
{
|
||||
}
|
||||
|
||||
void IDLEventListener::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_callback.ptr());
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -25,23 +25,28 @@ struct AddEventListenerOptions : public EventListenerOptions {
|
|||
Optional<NonnullRefPtr<AbortSignal>> signal;
|
||||
};
|
||||
|
||||
class IDLEventListener
|
||||
: public RefCounted<IDLEventListener>
|
||||
, public Bindings::Wrappable {
|
||||
public:
|
||||
using WrapperType = Bindings::EventListenerWrapper;
|
||||
class IDLEventListener final : public JS::Object {
|
||||
JS_OBJECT(IDLEventListener, JS::Object);
|
||||
|
||||
explicit IDLEventListener(Bindings::CallbackType callback)
|
||||
: m_callback(move(callback))
|
||||
{
|
||||
}
|
||||
public:
|
||||
IDLEventListener& impl() { return *this; }
|
||||
|
||||
static JS::NonnullGCPtr<IDLEventListener> create(JS::Realm&, JS::NonnullGCPtr<Bindings::CallbackType>);
|
||||
IDLEventListener(JS::Realm&, JS::NonnullGCPtr<Bindings::CallbackType>);
|
||||
|
||||
virtual ~IDLEventListener() = default;
|
||||
|
||||
Bindings::CallbackType& callback() { return m_callback; }
|
||||
Bindings::CallbackType& callback() { return *m_callback; }
|
||||
|
||||
private:
|
||||
Bindings::CallbackType m_callback;
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
JS::NonnullGCPtr<Bindings::CallbackType> m_callback;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace Web::Bindings {
|
||||
inline JS::Object* wrap(JS::Realm&, Web::DOM::IDLEventListener& object) { return &object; }
|
||||
using EventListenerWrapper = Web::DOM::IDLEventListener;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
namespace Web::DOM {
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-mutationobserver-mutationobserver
|
||||
MutationObserver::MutationObserver(Bindings::WindowObject& window_object, Bindings::CallbackType callback)
|
||||
MutationObserver::MutationObserver(Bindings::WindowObject& window_object, JS::Handle<Bindings::CallbackType> callback)
|
||||
: m_callback(move(callback))
|
||||
{
|
||||
// 1. Set this’s callback to callback.
|
||||
|
|
|
@ -34,9 +34,9 @@ class MutationObserver final
|
|||
public:
|
||||
using WrapperType = Bindings::MutationObserverWrapper;
|
||||
|
||||
static NonnullRefPtr<MutationObserver> create_with_global_object(Bindings::WindowObject& window_object, Bindings::CallbackType callback)
|
||||
static NonnullRefPtr<MutationObserver> create_with_global_object(Bindings::WindowObject& window_object, Bindings::CallbackType* callback)
|
||||
{
|
||||
return adopt_ref(*new MutationObserver(window_object, move(callback)));
|
||||
return adopt_ref(*new MutationObserver(window_object, JS::make_handle(callback)));
|
||||
}
|
||||
|
||||
virtual ~MutationObserver() override = default;
|
||||
|
@ -48,7 +48,7 @@ public:
|
|||
Vector<WeakPtr<Node>>& node_list() { return m_node_list; }
|
||||
Vector<WeakPtr<Node>> const& node_list() const { return m_node_list; }
|
||||
|
||||
Bindings::CallbackType& callback() { return m_callback; }
|
||||
Bindings::CallbackType& callback() { return *m_callback; }
|
||||
|
||||
void enqueue_record(Badge<Node>, NonnullRefPtr<MutationRecord> mutation_record)
|
||||
{
|
||||
|
@ -56,10 +56,10 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
MutationObserver(Bindings::WindowObject& window_object, Bindings::CallbackType callback);
|
||||
MutationObserver(Bindings::WindowObject& window_object, JS::Handle<Bindings::CallbackType> callback);
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-mo-callback
|
||||
Bindings::CallbackType m_callback;
|
||||
JS::Handle<Bindings::CallbackType> m_callback;
|
||||
|
||||
// https://dom.spec.whatwg.org/#mutationobserver-node-list
|
||||
Vector<WeakPtr<Node>> m_node_list;
|
||||
|
|
29
Userland/Libraries/LibWeb/DOM/NodeFilter.cpp
Normal file
29
Userland/Libraries/LibWeb/DOM/NodeFilter.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/Bindings/WindowObject.h>
|
||||
#include <LibWeb/DOM/NodeFilter.h>
|
||||
|
||||
namespace Web::DOM {
|
||||
|
||||
JS::NonnullGCPtr<NodeFilter> NodeFilter::create(JS::Realm& realm, Bindings::CallbackType& callback)
|
||||
{
|
||||
return *realm.heap().allocate<NodeFilter>(realm, realm, callback);
|
||||
}
|
||||
|
||||
NodeFilter::NodeFilter(JS::Realm& realm, Bindings::CallbackType& callback)
|
||||
: PlatformObject(*realm.intrinsics().object_prototype())
|
||||
, m_callback(callback)
|
||||
{
|
||||
}
|
||||
|
||||
void NodeFilter::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(&m_callback);
|
||||
}
|
||||
|
||||
}
|
|
@ -6,26 +6,22 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/RefCounted.h>
|
||||
#include <LibJS/Heap/Handle.h>
|
||||
#include <LibWeb/Bindings/CallbackType.h>
|
||||
#include <LibWeb/Bindings/Wrappable.h>
|
||||
#include <LibWeb/Bindings/PlatformObject.h>
|
||||
|
||||
namespace Web::DOM {
|
||||
|
||||
class NodeFilter
|
||||
: public RefCounted<NodeFilter>
|
||||
, public Bindings::Wrappable {
|
||||
public:
|
||||
using WrapperType = Bindings::NodeFilterWrapper;
|
||||
class NodeFilter final : public Bindings::PlatformObject {
|
||||
JS_OBJECT(NodeFilter, Bindings::PlatformObject);
|
||||
|
||||
explicit NodeFilter(Bindings::CallbackType callback)
|
||||
: m_callback(move(callback))
|
||||
{
|
||||
}
|
||||
public:
|
||||
static JS::NonnullGCPtr<NodeFilter> create(JS::Realm&, Bindings::CallbackType&);
|
||||
NodeFilter(JS::Realm&, Bindings::CallbackType&);
|
||||
|
||||
virtual ~NodeFilter() = default;
|
||||
|
||||
NodeFilter& impl() { return *this; }
|
||||
|
||||
Bindings::CallbackType& callback() { return m_callback; }
|
||||
|
||||
enum Result {
|
||||
|
@ -35,12 +31,14 @@ public:
|
|||
};
|
||||
|
||||
private:
|
||||
Bindings::CallbackType m_callback;
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
Bindings::CallbackType& m_callback;
|
||||
};
|
||||
|
||||
inline JS::Object* wrap(JS::Realm&, Web::DOM::NodeFilter& filter)
|
||||
{
|
||||
return filter.callback().callback.cell();
|
||||
return &filter.callback().callback;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ NodeIterator::~NodeIterator()
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-document-createnodeiterator
|
||||
NonnullRefPtr<NodeIterator> NodeIterator::create(Node& root, unsigned what_to_show, RefPtr<NodeFilter> filter)
|
||||
NonnullRefPtr<NodeIterator> NodeIterator::create(Node& root, unsigned what_to_show, NodeFilter* filter)
|
||||
{
|
||||
// 1. Let iterator be a new NodeIterator object.
|
||||
// 2. Set iterator’s root and iterator’s reference to root.
|
||||
|
@ -37,7 +37,7 @@ NonnullRefPtr<NodeIterator> NodeIterator::create(Node& root, unsigned what_to_sh
|
|||
iterator->m_what_to_show = what_to_show;
|
||||
|
||||
// 5. Set iterator’s filter to filter.
|
||||
iterator->m_filter = move(filter);
|
||||
iterator->m_filter = JS::make_handle(filter);
|
||||
|
||||
// 6. Return iterator.
|
||||
return iterator;
|
||||
|
@ -133,7 +133,7 @@ JS::ThrowCompletionOr<NodeFilter::Result> NodeIterator::filter(Node& node)
|
|||
return NodeFilter::FILTER_SKIP;
|
||||
|
||||
// 4. If traverser’s filter is null, then return FILTER_ACCEPT.
|
||||
if (!m_filter)
|
||||
if (!m_filter.cell())
|
||||
return NodeFilter::FILTER_ACCEPT;
|
||||
|
||||
// 5. Set traverser’s active flag.
|
||||
|
|
|
@ -21,14 +21,14 @@ public:
|
|||
|
||||
virtual ~NodeIterator() override;
|
||||
|
||||
static NonnullRefPtr<NodeIterator> create(Node& root, unsigned what_to_show, RefPtr<NodeFilter>);
|
||||
static NonnullRefPtr<NodeIterator> create(Node& root, unsigned what_to_show, NodeFilter*);
|
||||
|
||||
NonnullRefPtr<Node> root() { return m_root; }
|
||||
NonnullRefPtr<Node> reference_node() { return m_reference.node; }
|
||||
bool pointer_before_reference_node() const { return m_reference.is_before_node; }
|
||||
unsigned what_to_show() const { return m_what_to_show; }
|
||||
|
||||
NodeFilter* filter() { return m_filter; }
|
||||
NodeFilter* filter() { return m_filter.cell(); }
|
||||
|
||||
JS::ThrowCompletionOr<RefPtr<Node>> next_node();
|
||||
JS::ThrowCompletionOr<RefPtr<Node>> previous_node();
|
||||
|
@ -72,7 +72,7 @@ private:
|
|||
unsigned m_what_to_show { 0 };
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-traversal-filter
|
||||
RefPtr<DOM::NodeFilter> m_filter;
|
||||
JS::Handle<DOM::NodeFilter> m_filter;
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-traversal-active
|
||||
bool m_active { false };
|
||||
|
|
|
@ -23,7 +23,7 @@ TreeWalker::TreeWalker(Node& root)
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-document-createtreewalker
|
||||
NonnullRefPtr<TreeWalker> TreeWalker::create(Node& root, unsigned what_to_show, RefPtr<NodeFilter> filter)
|
||||
NonnullRefPtr<TreeWalker> TreeWalker::create(Node& root, unsigned what_to_show, NodeFilter* filter)
|
||||
{
|
||||
// 1. Let walker be a new TreeWalker object.
|
||||
// 2. Set walker’s root and walker’s current to root.
|
||||
|
@ -33,7 +33,7 @@ NonnullRefPtr<TreeWalker> TreeWalker::create(Node& root, unsigned what_to_show,
|
|||
walker->m_what_to_show = what_to_show;
|
||||
|
||||
// 4. Set walker’s filter to filter.
|
||||
walker->m_filter = move(filter);
|
||||
walker->m_filter = JS::make_handle(filter);
|
||||
|
||||
// 5. Return walker.
|
||||
return walker;
|
||||
|
@ -236,7 +236,7 @@ JS::ThrowCompletionOr<NodeFilter::Result> TreeWalker::filter(Node& node)
|
|||
return NodeFilter::FILTER_SKIP;
|
||||
|
||||
// 4. If traverser’s filter is null, then return FILTER_ACCEPT.
|
||||
if (!m_filter)
|
||||
if (!m_filter.cell())
|
||||
return NodeFilter::FILTER_ACCEPT;
|
||||
|
||||
// 5. Set traverser’s active flag.
|
||||
|
|
|
@ -19,7 +19,7 @@ class TreeWalker
|
|||
public:
|
||||
using WrapperType = Bindings::TreeWalkerWrapper;
|
||||
|
||||
static NonnullRefPtr<TreeWalker> create(Node& root, unsigned what_to_show, RefPtr<NodeFilter>);
|
||||
static NonnullRefPtr<TreeWalker> create(Node& root, unsigned what_to_show, NodeFilter*);
|
||||
virtual ~TreeWalker() override = default;
|
||||
|
||||
NonnullRefPtr<Node> current_node() const;
|
||||
|
@ -35,7 +35,7 @@ public:
|
|||
|
||||
NonnullRefPtr<Node> root() { return m_root; }
|
||||
|
||||
NodeFilter* filter() { return m_filter; }
|
||||
NodeFilter* filter() { return m_filter.cell(); }
|
||||
|
||||
unsigned what_to_show() const { return m_what_to_show; }
|
||||
|
||||
|
@ -66,7 +66,7 @@ private:
|
|||
unsigned m_what_to_show { 0 };
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-traversal-filter
|
||||
RefPtr<DOM::NodeFilter> m_filter;
|
||||
JS::Handle<DOM::NodeFilter> m_filter;
|
||||
|
||||
// https://dom.spec.whatwg.org/#concept-traversal-active
|
||||
bool m_active { false };
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue