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:
parent
0e2cd5540a
commit
e76e8e22b5
21 changed files with 223 additions and 129 deletions
|
@ -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>
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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();
|
||||||
|
|
20
Userland/Libraries/LibWeb/DOM/DOMEventListener.cpp
Normal file
20
Userland/Libraries/LibWeb/DOM/DOMEventListener.cpp
Normal 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()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
42
Userland/Libraries/LibWeb/DOM/DOMEventListener.h
Normal file
42
Userland/Libraries/LibWeb/DOM/DOMEventListener.h
Normal 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 };
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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 event’s type attribute value is not listener’s type, then continue.
|
// 1. If event’s type attribute value is not listener’s 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 listener’s capture is false, then continue.
|
// 3. If phase is "capturing" and listener’s 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 listener’s capture is true, then continue.
|
// 4. If phase is "bubbling" and listener’s capture is true, then continue.
|
||||||
if (phase == Event::Phase::BubblingPhase && listener.listener->capture())
|
if (phase == Event::Phase::BubblingPhase && listener->capture)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// 5. If listener’s once is true, then remove listener from event’s currentTarget attribute value’s event listener list.
|
// 5. If listener’s once is true, then remove listener from event’s currentTarget attribute value’s 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 callback’s associated Realm’s global object.
|
// 6. Let global be listener callback’s associated Realm’s 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 listener’s passive is true, then set event’s in passive listener flag.
|
// 9. If listener’s passive is true, then set event’s 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 object’s operation with listener’s callback, "handleEvent", « event », and event’s currentTarget attribute value. If this throws an exception, then:
|
// 10. Call a user object’s operation with listener’s callback, "handleEvent", « event », and event’s currentTarget attribute value. If this throws an exception, then:
|
||||||
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 };
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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 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())
|
||||||
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 listener’s callback is null, then return.
|
||||||
|
if (listener->callback.is_null())
|
||||||
|
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;
|
||||||
});
|
});
|
||||||
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 listener’s 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 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 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 worker’s 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 listener’s removed to true and remove listener from eventTarget’s 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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
35
Userland/Libraries/LibWeb/DOM/IDLEventListener.h
Normal file
35
Userland/Libraries/LibWeb/DOM/IDLEventListener.h
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue