1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-25 19:15:06 +00:00

LibWeb: Separate "event listener" from "EventListener"

I can't imagine how this happened, but it seems we've managed to
conflate the "event listener" and "EventListener" concepts from the DOM
specification in some parts of the code.

We previously had two things:

    - DOM::EventListener
    - DOM::EventTarget::EventListenerRegistration

DOM::EventListener was roughly the "EventListener" IDL type,
and DOM::EventTarget::EventListenerRegistration was roughly the "event
listener" concept. However, they were used interchangeably (and
incorrectly!) in many places.

After this patch, we now have:

    - DOM::IDLEventListener
    - DOM::DOMEventListener

DOM::IDLEventListener is the "EventListener" IDL type,
and DOM::DOMEventListener is the "event listener" concept.

This patch also updates the addEventListener() and removeEventListener()
functions to follow the spec more closely, along with the "inner invoke"
function in our EventDispatcher.
This commit is contained in:
Andreas Kling 2022-02-16 20:43:24 +01:00
parent 0e2cd5540a
commit e76e8e22b5
21 changed files with 223 additions and 129 deletions

View file

@ -1496,13 +1496,13 @@ static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter
if (parameter.type->nullable) { if (parameter.type->nullable) {
scoped_generator.append(R"~~~( scoped_generator.append(R"~~~(
RefPtr<EventListener> @cpp_name@; RefPtr<IDLEventListener> @cpp_name@;
if (!@js_name@@js_suffix@.is_nullish()) { if (!@js_name@@js_suffix@.is_nullish()) {
if (!@js_name@@js_suffix@.is_object()) if (!@js_name@@js_suffix@.is_object())
return vm.throw_completion<JS::TypeError>(global_object, JS::ErrorType::NotAnObject, @js_name@@js_suffix@.to_string_without_side_effects()); return vm.throw_completion<JS::TypeError>(global_object, JS::ErrorType::NotAnObject, @js_name@@js_suffix@.to_string_without_side_effects());
CallbackType callback_type(JS::make_handle(&@js_name@@js_suffix@.as_object()), HTML::incumbent_settings_object()); CallbackType callback_type(JS::make_handle(&@js_name@@js_suffix@.as_object()), HTML::incumbent_settings_object());
@cpp_name@ = adopt_ref(*new EventListener(move(callback_type))); @cpp_name@ = adopt_ref(*new IDLEventListener(move(callback_type)));
} }
)~~~"); )~~~");
} else { } else {
@ -1511,7 +1511,7 @@ static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter
return vm.throw_completion<JS::TypeError>(global_object, JS::ErrorType::NotAnObject, @js_name@@js_suffix@.to_string_without_side_effects()); return vm.throw_completion<JS::TypeError>(global_object, JS::ErrorType::NotAnObject, @js_name@@js_suffix@.to_string_without_side_effects());
CallbackType callback_type(JS::make_handle(&@js_name@@js_suffix@.as_object()), HTML::incumbent_settings_object()); CallbackType callback_type(JS::make_handle(&@js_name@@js_suffix@.as_object()), HTML::incumbent_settings_object());
auto @cpp_name@ = adopt_ref(*new EventListener(move(callback_type))); auto @cpp_name@ = adopt_ref(*new IDLEventListener(move(callback_type)));
)~~~"); )~~~");
} }
} else if (IDL::is_wrappable_type(*parameter.type)) { } else if (IDL::is_wrappable_type(*parameter.type)) {
@ -3793,6 +3793,7 @@ void generate_prototype_implementation(IDL::Interface const& interface)
#include <LibWeb/Bindings/LocationObject.h> #include <LibWeb/Bindings/LocationObject.h>
#include <LibWeb/Bindings/WindowObject.h> #include <LibWeb/Bindings/WindowObject.h>
#include <LibWeb/DOM/Event.h> #include <LibWeb/DOM/Event.h>
#include <LibWeb/DOM/IDLEventListener.h>
#include <LibWeb/DOM/Window.h> #include <LibWeb/DOM/Window.h>
#include <LibWeb/HTML/Scripting/Environments.h> #include <LibWeb/HTML/Scripting/Environments.h>
#include <LibWeb/Origin.h> #include <LibWeb/Origin.h>

View file

@ -7,12 +7,12 @@
#include <LibJS/Runtime/FunctionObject.h> #include <LibJS/Runtime/FunctionObject.h>
#include <LibJS/Runtime/GlobalObject.h> #include <LibJS/Runtime/GlobalObject.h>
#include <LibWeb/Bindings/EventListenerWrapper.h> #include <LibWeb/Bindings/EventListenerWrapper.h>
#include <LibWeb/DOM/EventListener.h> #include <LibWeb/DOM/IDLEventListener.h>
namespace Web { namespace Web {
namespace Bindings { namespace Bindings {
EventListenerWrapper::EventListenerWrapper(JS::GlobalObject& global_object, DOM::EventListener& impl) EventListenerWrapper::EventListenerWrapper(JS::GlobalObject& global_object, DOM::IDLEventListener& impl)
: Wrapper(*global_object.object_prototype()) : Wrapper(*global_object.object_prototype())
, m_impl(impl) , m_impl(impl)
{ {

View file

@ -15,14 +15,14 @@ class EventListenerWrapper final : public Wrapper {
JS_OBJECT(EventListenerWrapper, Wrapper); JS_OBJECT(EventListenerWrapper, Wrapper);
public: public:
EventListenerWrapper(JS::GlobalObject&, DOM::EventListener&); EventListenerWrapper(JS::GlobalObject&, DOM::IDLEventListener&);
virtual ~EventListenerWrapper() override; virtual ~EventListenerWrapper() override;
DOM::EventListener& impl() { return *m_impl; } DOM::IDLEventListener& impl() { return *m_impl; }
const DOM::EventListener& impl() const { return *m_impl; } DOM::IDLEventListener const& impl() const { return *m_impl; }
private: private:
NonnullRefPtr<DOM::EventListener> m_impl; NonnullRefPtr<DOM::IDLEventListener> m_impl;
}; };
} }

View file

@ -63,6 +63,7 @@ set(SOURCES
DOM/CharacterData.idl DOM/CharacterData.idl
DOM/Comment.cpp DOM/Comment.cpp
DOM/CustomEvent.cpp DOM/CustomEvent.cpp
DOM/DOMEventListener.cpp
DOM/DOMImplementation.cpp DOM/DOMImplementation.cpp
DOM/DOMTokenList.cpp DOM/DOMTokenList.cpp
DOM/DOMTokenList.idl DOM/DOMTokenList.idl

View file

@ -9,7 +9,7 @@
#include <LibWeb/CSS/MediaQueryList.h> #include <LibWeb/CSS/MediaQueryList.h>
#include <LibWeb/DOM/Document.h> #include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/EventDispatcher.h> #include <LibWeb/DOM/EventDispatcher.h>
#include <LibWeb/DOM/EventListener.h> #include <LibWeb/DOM/IDLEventListener.h>
#include <LibWeb/HTML/EventHandler.h> #include <LibWeb/HTML/EventHandler.h>
namespace Web::CSS { namespace Web::CSS {
@ -61,7 +61,7 @@ JS::Object* MediaQueryList::create_wrapper(JS::GlobalObject& global_object)
} }
// https://www.w3.org/TR/cssom-view/#dom-mediaquerylist-addlistener // https://www.w3.org/TR/cssom-view/#dom-mediaquerylist-addlistener
void MediaQueryList::add_listener(RefPtr<DOM::EventListener> listener) void MediaQueryList::add_listener(RefPtr<DOM::IDLEventListener> listener)
{ {
// 1. If listener is null, terminate these steps. // 1. If listener is null, terminate these steps.
if (!listener) if (!listener)
@ -75,7 +75,7 @@ void MediaQueryList::add_listener(RefPtr<DOM::EventListener> listener)
} }
// https://www.w3.org/TR/cssom-view/#dom-mediaquerylist-removelistener // https://www.w3.org/TR/cssom-view/#dom-mediaquerylist-removelistener
void MediaQueryList::remove_listener(RefPtr<DOM::EventListener> listener) void MediaQueryList::remove_listener(RefPtr<DOM::IDLEventListener> listener)
{ {
// 1. Remove an event listener from the associated list of event listeners, whose type is change, callback is listener, and capture is false. // 1. Remove an event listener from the associated list of event listeners, whose type is change, callback is listener, and capture is false.
// NOTE: While the spec doesn't technically use remove_event_listener and instead manipulates the list directly, every major engine uses remove_event_listener. // NOTE: While the spec doesn't technically use remove_event_listener and instead manipulates the list directly, every major engine uses remove_event_listener.

View file

@ -45,8 +45,8 @@ public:
virtual void unref_event_target() override { unref(); } virtual void unref_event_target() override { unref(); }
virtual JS::Object* create_wrapper(JS::GlobalObject&) override; virtual JS::Object* create_wrapper(JS::GlobalObject&) override;
void add_listener(RefPtr<DOM::EventListener> listener); void add_listener(RefPtr<DOM::IDLEventListener> listener);
void remove_listener(RefPtr<DOM::EventListener> listener); void remove_listener(RefPtr<DOM::IDLEventListener> listener);
void set_onchange(Optional<Bindings::CallbackType>); void set_onchange(Optional<Bindings::CallbackType>);
Bindings::CallbackType* onchange(); Bindings::CallbackType* onchange();

View file

@ -0,0 +1,20 @@
/*
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/DOM/AbortSignal.h>
#include <LibWeb/DOM/DOMEventListener.h>
#include <LibWeb/DOM/IDLEventListener.h>
namespace Web::DOM {
DOMEventListener::DOMEventListener()
{
}
DOMEventListener::~DOMEventListener()
{
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/FlyString.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"
struct DOMEventListener : RefCounted<DOMEventListener> {
DOMEventListener();
~DOMEventListener();
// type (a string)
FlyString type;
// callback (null or an EventListener object)
RefPtr<IDLEventListener> callback;
// signal (null or an AbortSignal object)
RefPtr<DOM::AbortSignal> signal;
// capture (a boolean, initially false)
bool capture { false };
// passive (a boolean, initially false)
bool passive { false };
// once (a boolean, initially false)
bool once { false };
// removed (a boolean for bookkeeping purposes, initially false)
bool removed { false };
};
}

View file

@ -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 * SPDX-License-Identifier: BSD-2-Clause
*/ */
@ -14,11 +14,12 @@
#include <LibWeb/Bindings/EventWrapperFactory.h> #include <LibWeb/Bindings/EventWrapperFactory.h>
#include <LibWeb/Bindings/IDLAbstractOperations.h> #include <LibWeb/Bindings/IDLAbstractOperations.h>
#include <LibWeb/Bindings/WindowObject.h> #include <LibWeb/Bindings/WindowObject.h>
#include <LibWeb/DOM/AbortSignal.h>
#include <LibWeb/DOM/Document.h> #include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/Event.h> #include <LibWeb/DOM/Event.h>
#include <LibWeb/DOM/EventDispatcher.h> #include <LibWeb/DOM/EventDispatcher.h>
#include <LibWeb/DOM/EventListener.h>
#include <LibWeb/DOM/EventTarget.h> #include <LibWeb/DOM/EventTarget.h>
#include <LibWeb/DOM/IDLEventListener.h>
#include <LibWeb/DOM/Node.h> #include <LibWeb/DOM/Node.h>
#include <LibWeb/DOM/ShadowRoot.h> #include <LibWeb/DOM/ShadowRoot.h>
#include <LibWeb/DOM/Window.h> #include <LibWeb/DOM/Window.h>
@ -50,37 +51,37 @@ static EventTarget* retarget(EventTarget* left, EventTarget* right)
} }
// https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke // 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) bool EventDispatcher::inner_invoke(Event& event, Vector<NonnullRefPtr<DOM::DOMEventListener>>& listeners, Event::Phase phase, bool invocation_target_in_shadow_tree)
{ {
// 1. Let found be false. // 1. Let found be false.
bool found = false; bool found = false;
// 2. For each listener in listeners, whose removed is false: // 2. For each listener in listeners, whose removed is false:
for (auto& listener : listeners) { for (auto& listener : listeners) {
if (listener.listener->removed()) if (listener->removed)
continue; continue;
// 1. If events type attribute value is not listeners type, then continue. // 1. If events type attribute value is not listeners type, then continue.
if (event.type() != listener.listener->type()) if (event.type() != listener->type)
continue; continue;
// 2. Set found to true. // 2. Set found to true.
found = true; found = true;
// 3. If phase is "capturing" and listeners capture is false, then continue. // 3. If phase is "capturing" and listeners capture is false, then continue.
if (phase == Event::Phase::CapturingPhase && !listener.listener->capture()) if (phase == Event::Phase::CapturingPhase && !listener->capture)
continue; continue;
// 4. If phase is "bubbling" and listeners capture is true, then continue. // 4. If phase is "bubbling" and listeners capture is true, then continue.
if (phase == Event::Phase::BubblingPhase && listener.listener->capture()) if (phase == Event::Phase::BubblingPhase && listener->capture)
continue; continue;
// 5. If listeners once is true, then remove listener from events currentTarget attribute values event listener list. // 5. If listeners once is true, then remove listener from events currentTarget attribute values event listener list.
if (listener.listener->once()) if (listener->once)
event.current_target()->remove_from_event_listener_list(listener.listener); event.current_target()->remove_from_event_listener_list(listener);
// 6. Let global be listener callbacks associated Realms global object. // 6. Let global be listener callbacks associated Realms global object.
auto& callback = listener.listener->callback(); auto& callback = listener->callback->callback();
auto& global = callback.callback.cell()->global_object(); auto& global = callback.callback.cell()->global_object();
// 7. Let currentEvent be undefined. // 7. Let currentEvent be undefined.
@ -100,7 +101,7 @@ bool EventDispatcher::inner_invoke(Event& event, Vector<EventTarget::EventListen
} }
// 9. If listeners passive is true, then set events in passive listener flag. // 9. If listeners passive is true, then set events in passive listener flag.
if (listener.listener->passive()) if (listener->passive)
event.set_in_passive_listener(true); event.set_in_passive_listener(true);
// 10. Call a user objects operation with listeners callback, "handleEvent", « event », and events currentTarget attribute value. If this throws an exception, then: // 10. Call a user objects operation with listeners callback, "handleEvent", « event », and events currentTarget attribute value. If this throws an exception, then:
@ -155,7 +156,7 @@ void EventDispatcher::invoke(Event::PathEntry& struct_, Event& event, Event::Pha
event.set_current_target(struct_.invocation_target); event.set_current_target(struct_.invocation_target);
// NOTE: This is an intentional copy. Any event listeners added after this point will not be invoked. // NOTE: This is an intentional copy. Any event listeners added after this point will not be invoked.
auto listeners = event.current_target()->listeners(); auto listeners = event.current_target()->event_listener_list();
bool invocation_target_in_shadow_tree = struct_.invocation_target_in_shadow_tree; bool invocation_target_in_shadow_tree = struct_.invocation_target_in_shadow_tree;
bool found = inner_invoke(event, listeners, phase, invocation_target_in_shadow_tree); bool found = inner_invoke(event, listeners, phase, invocation_target_in_shadow_tree);

View file

@ -18,7 +18,7 @@ public:
private: private:
static void invoke(Event::PathEntry&, Event&, Event::Phase); static void invoke(Event::PathEntry&, Event&, Event::Phase);
static bool inner_invoke(Event&, Vector<EventTarget::EventListenerRegistration>&, Event::Phase, bool); static bool inner_invoke(Event&, Vector<NonnullRefPtr<DOM::DOMEventListener>>&, Event::Phase, bool);
}; };
} }

View file

@ -1,55 +0,0 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/RefCounted.h>
#include <LibJS/Heap/Handle.h>
#include <LibWeb/Bindings/CallbackType.h>
#include <LibWeb/Bindings/Wrappable.h>
namespace Web::DOM {
class EventListener
: public RefCounted<EventListener>
, public Bindings::Wrappable {
public:
using WrapperType = Bindings::EventListenerWrapper;
explicit EventListener(Bindings::CallbackType callback)
: m_callback(move(callback))
{
}
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; }
bool capture() const { return m_capture; }
void set_capture(bool capture) { m_capture = capture; }
bool passive() const { return m_passive; }
void set_passive(bool passive) { m_capture = passive; }
bool once() const { return m_once; }
void set_once(bool once) { m_once = once; }
bool removed() const { return m_removed; }
void set_removed(bool removed) { m_removed = removed; }
private:
FlyString m_type;
Bindings::CallbackType m_callback;
bool m_capture { false };
bool m_passive { false };
bool m_once { false };
bool m_removed { false };
};
}

View file

@ -19,11 +19,12 @@
#include <LibWeb/Bindings/EventWrapperFactory.h> #include <LibWeb/Bindings/EventWrapperFactory.h>
#include <LibWeb/Bindings/IDLAbstractOperations.h> #include <LibWeb/Bindings/IDLAbstractOperations.h>
#include <LibWeb/Bindings/MainThreadVM.h> #include <LibWeb/Bindings/MainThreadVM.h>
#include <LibWeb/DOM/AbortSignal.h>
#include <LibWeb/DOM/Document.h> #include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/Event.h> #include <LibWeb/DOM/Event.h>
#include <LibWeb/DOM/EventDispatcher.h> #include <LibWeb/DOM/EventDispatcher.h>
#include <LibWeb/DOM/EventListener.h>
#include <LibWeb/DOM/EventTarget.h> #include <LibWeb/DOM/EventTarget.h>
#include <LibWeb/DOM/IDLEventListener.h>
#include <LibWeb/DOM/Window.h> #include <LibWeb/DOM/Window.h>
#include <LibWeb/HTML/ErrorEvent.h> #include <LibWeb/HTML/ErrorEvent.h>
#include <LibWeb/HTML/EventHandler.h> #include <LibWeb/HTML/EventHandler.h>
@ -44,38 +45,87 @@ EventTarget::~EventTarget()
{ {
} }
// https://dom.spec.whatwg.org/#add-an-event-listener // https://dom.spec.whatwg.org/#dom-eventtarget-addeventlistener
void EventTarget::add_event_listener(const FlyString& event_name, RefPtr<EventListener> listener) void EventTarget::add_event_listener(FlyString const& type, RefPtr<IDLEventListener> callback)
{ {
if (listener.is_null()) // FIXME: 1. Let capture, passive, once, and signal be the result of flattening more options.
bool capture = false;
bool passive = false;
bool once = false;
RefPtr<AbortSignal> signal = nullptr;
// 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);
event_listener->type = type;
event_listener->callback = move(callback);
event_listener->signal = move(signal);
event_listener->capture = capture;
event_listener->passive = passive;
event_listener->once = once;
add_an_event_listener(move(event_listener));
}
// https://dom.spec.whatwg.org/#add-an-event-listener
void EventTarget::add_an_event_listener(NonnullRefPtr<DOMEventListener> listener)
{
// FIXME: 1. If eventTarget is a ServiceWorkerGlobalScope object, its service workers script resources has ever been evaluated flag is set,
// and listeners 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 listeners signal is not null and is aborted, then return.
if (listener->signal && listener->signal->aborted())
return; return;
auto existing_listener = m_listeners.first_matching([&](auto& entry) {
return entry.listener->type() == event_name && entry.listener->callback().callback.cell() == listener->callback().callback.cell() && entry.listener->capture() == listener->capture(); // 3. If listeners callback is null, then return.
if (listener->callback.is_null())
return;
// 4. If eventTargets event listener list does not contain an event listener whose type is listeners type, callback is listeners callback,
// and capture is listeners capture, then append listener to eventTargets 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;
}); });
if (existing_listener.has_value()) if (it == m_event_listener_list.end())
return; m_event_listener_list.append(listener);
listener->set_type(event_name);
m_listeners.append({ event_name, listener.release_nonnull() }); // FIXME: 5. If listeners signal is not null, then add the following abort steps to it:
// FIXME: 1. Remove an event listener with eventTarget and listener.
}
// https://dom.spec.whatwg.org/#dom-eventtarget-removeeventlistener
void EventTarget::remove_event_listener(FlyString const& type, RefPtr<IDLEventListener> callback)
{
// FIXME: 1. Let capture be the result of flattening options.
bool capture = false;
// 2. If thiss 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 it = m_event_listener_list.find_if([&](auto& entry) {
return entry->type == type
&& entry->callback->callback().callback.cell() == callback->callback().callback.cell()
&& entry->capture == capture;
});
if (it != m_event_listener_list.end())
remove_an_event_listener(*it);
} }
// https://dom.spec.whatwg.org/#remove-an-event-listener // https://dom.spec.whatwg.org/#remove-an-event-listener
void EventTarget::remove_event_listener(const FlyString& event_name, RefPtr<EventListener> listener) void EventTarget::remove_an_event_listener(DOMEventListener& listener)
{ {
if (listener.is_null()) // FIXME: 1. If eventTarget is a ServiceWorkerGlobalScope object and its service workers set of event types to handle contains type,
return; // then report a warning to the console that this might not give the expected results. [SERVICE-WORKERS]
m_listeners.remove_first_matching([&](auto& entry) {
auto matches = entry.event_name == event_name && entry.listener->callback().callback.cell() == listener->callback().callback.cell() && entry.listener->capture() == listener->capture(); // 2. Set listeners removed to true and remove listener from eventTargets event listener list.
if (matches) listener.removed = true;
entry.listener->set_removed(true); m_event_listener_list.remove_first_matching([&](auto& entry) { return entry.ptr() == &listener; });
return matches;
});
} }
void EventTarget::remove_from_event_listener_list(NonnullRefPtr<EventListener> listener) void EventTarget::remove_from_event_listener_list(DOMEventListener& listener)
{ {
m_listeners.remove_first_matching([&](auto& entry) { m_event_listener_list.remove_first_matching([&](auto& entry) { return entry.ptr() == &listener; });
return entry.listener->type() == listener->type() && &entry.listener->callback() == &listener->callback() && entry.listener->capture() == listener->capture();
});
} }
// https://dom.spec.whatwg.org/#dom-eventtarget-dispatchevent // https://dom.spec.whatwg.org/#dom-eventtarget-dispatchevent
@ -433,11 +483,12 @@ void EventTarget::activate_event_handler(FlyString const& name, HTML::EventHandl
Bindings::CallbackType callback { JS::make_handle(static_cast<JS::Object*>(callback_function)), verify_cast<HTML::EnvironmentSettingsObject>(*global_object->associated_realm()->host_defined()) }; 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. // 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))); auto listener = adopt_ref(*new DOMEventListener);
listener->type = name;
listener->callback = adopt_ref(*new IDLEventListener(move(callback)));
// 6. Add an event listener with eventTarget and listener. // 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_an_event_listener(listener);
add_event_listener(name, listener);
// 7. Set eventHandler's listener to listener. // 7. Set eventHandler's listener to listener.
event_handler.listener = listener; event_handler.listener = listener;
@ -460,8 +511,7 @@ void EventTarget::deactivate_event_handler(FlyString const& name)
// 5. If listener is not null, then remove an event listener with eventTarget and listener. // 5. If listener is not null, then remove an event listener with eventTarget and listener.
if (event_handler.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_an_event_listener(*event_handler.listener);
remove_event_listener(name, event_handler.listener);
} }
// 6. Set eventHandler's listener to null. // 6. Set eventHandler's listener to null.

View file

@ -11,6 +11,7 @@
#include <AK/Noncopyable.h> #include <AK/Noncopyable.h>
#include <AK/Vector.h> #include <AK/Vector.h>
#include <LibJS/Forward.h> #include <LibJS/Forward.h>
#include <LibWeb/DOM/DOMEventListener.h>
#include <LibWeb/DOM/ExceptionOr.h> #include <LibWeb/DOM/ExceptionOr.h>
#include <LibWeb/Forward.h> #include <LibWeb/Forward.h>
#include <LibWeb/HTML/EventHandler.h> #include <LibWeb/HTML/EventHandler.h>
@ -29,10 +30,8 @@ public:
virtual bool is_focusable() const { return false; } virtual bool is_focusable() const { return false; }
void add_event_listener(const FlyString& event_name, RefPtr<EventListener>); void add_event_listener(FlyString const& type, RefPtr<IDLEventListener> callback);
void remove_event_listener(const FlyString& event_name, RefPtr<EventListener>); void remove_event_listener(FlyString const& type, RefPtr<IDLEventListener> callback);
void remove_from_event_listener_list(NonnullRefPtr<EventListener>);
virtual bool dispatch_event(NonnullRefPtr<Event>); virtual bool dispatch_event(NonnullRefPtr<Event>);
ExceptionOr<bool> dispatch_event_binding(NonnullRefPtr<Event>); ExceptionOr<bool> dispatch_event_binding(NonnullRefPtr<Event>);
@ -41,13 +40,12 @@ public:
virtual EventTarget* get_parent(const Event&) { return nullptr; } virtual EventTarget* get_parent(const Event&) { return nullptr; }
struct EventListenerRegistration { void add_an_event_listener(NonnullRefPtr<DOMEventListener>);
FlyString event_name; void remove_an_event_listener(DOMEventListener&);
NonnullRefPtr<EventListener> listener; void remove_from_event_listener_list(DOMEventListener&);
};
Vector<EventListenerRegistration>& listeners() { return m_listeners; } auto& event_listener_list() { return m_event_listener_list; }
const Vector<EventListenerRegistration>& listeners() const { return m_listeners; } auto const& event_listener_list() const { return m_event_listener_list; }
Function<void(const Event&)> activation_behavior; Function<void(const Event&)> activation_behavior;
@ -70,7 +68,7 @@ protected:
void element_event_handler_attribute_changed(FlyString const& local_name, String const& value); void element_event_handler_attribute_changed(FlyString const& local_name, String const& value);
private: private:
Vector<EventListenerRegistration> m_listeners; Vector<NonnullRefPtr<DOMEventListener>> m_event_listener_list;
// https://html.spec.whatwg.org/multipage/webappapis.html#event-handler-map // 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. // 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.

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/RefCounted.h>
#include <LibJS/Heap/Handle.h>
#include <LibWeb/Bindings/CallbackType.h>
#include <LibWeb/Bindings/Wrappable.h>
namespace Web::DOM {
class IDLEventListener
: public RefCounted<IDLEventListener>
, public Bindings::Wrappable {
public:
using WrapperType = Bindings::EventListenerWrapper;
explicit IDLEventListener(Bindings::CallbackType callback)
: m_callback(move(callback))
{
}
virtual ~IDLEventListener() = default;
Bindings::CallbackType& callback() { return m_callback; }
private:
Bindings::CallbackType m_callback;
};
}

View file

@ -19,7 +19,7 @@
#include <LibWeb/DOM/ElementFactory.h> #include <LibWeb/DOM/ElementFactory.h>
#include <LibWeb/DOM/Event.h> #include <LibWeb/DOM/Event.h>
#include <LibWeb/DOM/EventDispatcher.h> #include <LibWeb/DOM/EventDispatcher.h>
#include <LibWeb/DOM/EventListener.h> #include <LibWeb/DOM/IDLEventListener.h>
#include <LibWeb/DOM/LiveNodeList.h> #include <LibWeb/DOM/LiveNodeList.h>
#include <LibWeb/DOM/Node.h> #include <LibWeb/DOM/Node.h>
#include <LibWeb/DOM/ProcessingInstruction.h> #include <LibWeb/DOM/ProcessingInstruction.h>

View file

@ -84,15 +84,16 @@ class Document;
class DocumentFragment; class DocumentFragment;
class DocumentLoadEventDelayer; class DocumentLoadEventDelayer;
class DocumentType; class DocumentType;
class DOMEventListener;
class DOMException; class DOMException;
class DOMImplementation; class DOMImplementation;
class DOMTokenList; class DOMTokenList;
class Element; class Element;
class Event; class Event;
class EventHandler; class EventHandler;
class EventListener;
class EventTarget; class EventTarget;
class HTMLCollection; class HTMLCollection;
class IDLEventListener;
class LiveNodeList; class LiveNodeList;
class NamedNodeMap; class NamedNodeMap;
class Node; class Node;

View file

@ -11,7 +11,7 @@
#include <LibJS/Heap/Handle.h> #include <LibJS/Heap/Handle.h>
#include <LibJS/Runtime/FunctionObject.h> #include <LibJS/Runtime/FunctionObject.h>
#include <LibWeb/Bindings/CallbackType.h> #include <LibWeb/Bindings/CallbackType.h>
#include <LibWeb/DOM/EventListener.h> #include <LibWeb/DOM/DOMEventListener.h>
namespace Web::HTML { namespace Web::HTML {
@ -34,7 +34,7 @@ struct EventHandler {
Variant<String, Bindings::CallbackType> value; Variant<String, Bindings::CallbackType> value;
// https://html.spec.whatwg.org/multipage/webappapis.html#event-handler-listener // https://html.spec.whatwg.org/multipage/webappapis.html#event-handler-listener
RefPtr<DOM::EventListener> listener; RefPtr<DOM::DOMEventListener> listener;
}; };
} }

View file

@ -7,7 +7,7 @@
#include <LibJS/Interpreter.h> #include <LibJS/Interpreter.h>
#include <LibJS/Parser.h> #include <LibJS/Parser.h>
#include <LibWeb/DOM/Document.h> #include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/EventListener.h> #include <LibWeb/DOM/IDLEventListener.h>
#include <LibWeb/HTML/EventHandler.h> #include <LibWeb/HTML/EventHandler.h>
#include <LibWeb/HTML/EventNames.h> #include <LibWeb/HTML/EventNames.h>
#include <LibWeb/HTML/GlobalEventHandlers.h> #include <LibWeb/HTML/GlobalEventHandlers.h>

View file

@ -9,8 +9,8 @@
#include <LibJS/Parser.h> #include <LibJS/Parser.h>
#include <LibWeb/DOM/DOMException.h> #include <LibWeb/DOM/DOMException.h>
#include <LibWeb/DOM/Document.h> #include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/EventListener.h>
#include <LibWeb/DOM/ExceptionOr.h> #include <LibWeb/DOM/ExceptionOr.h>
#include <LibWeb/DOM/IDLEventListener.h>
#include <LibWeb/DOM/Window.h> #include <LibWeb/DOM/Window.h>
#include <LibWeb/HTML/BrowsingContext.h> #include <LibWeb/HTML/BrowsingContext.h>
#include <LibWeb/HTML/BrowsingContextContainer.h> #include <LibWeb/HTML/BrowsingContextContainer.h>

View file

@ -16,8 +16,8 @@
#include <LibWeb/DOM/Document.h> #include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/Event.h> #include <LibWeb/DOM/Event.h>
#include <LibWeb/DOM/EventDispatcher.h> #include <LibWeb/DOM/EventDispatcher.h>
#include <LibWeb/DOM/EventListener.h>
#include <LibWeb/DOM/ExceptionOr.h> #include <LibWeb/DOM/ExceptionOr.h>
#include <LibWeb/DOM/IDLEventListener.h>
#include <LibWeb/DOM/Window.h> #include <LibWeb/DOM/Window.h>
#include <LibWeb/HTML/CloseEvent.h> #include <LibWeb/HTML/CloseEvent.h>
#include <LibWeb/HTML/EventHandler.h> #include <LibWeb/HTML/EventHandler.h>

View file

@ -20,8 +20,8 @@
#include <LibWeb/DOM/Document.h> #include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/Event.h> #include <LibWeb/DOM/Event.h>
#include <LibWeb/DOM/EventDispatcher.h> #include <LibWeb/DOM/EventDispatcher.h>
#include <LibWeb/DOM/EventListener.h>
#include <LibWeb/DOM/ExceptionOr.h> #include <LibWeb/DOM/ExceptionOr.h>
#include <LibWeb/DOM/IDLEventListener.h>
#include <LibWeb/DOM/Window.h> #include <LibWeb/DOM/Window.h>
#include <LibWeb/Fetch/AbstractOperations.h> #include <LibWeb/Fetch/AbstractOperations.h>
#include <LibWeb/HTML/EventHandler.h> #include <LibWeb/HTML/EventHandler.h>