mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 20:47:45 +00:00
LibWeb: Add support for the options variant of {add,remove}EventListener
This also adds a variant of {add,remove}_event_listener called {add,remove}_event_listener_with_options. This is used internally to perform {add,remove}_event_listener with a default constructed options struct. It was done like this because default constructing the Variant with the options struct requires the struct defintions to be present, which requires us to include AbortSignal.h, which would cause a circular include as AbortSignal.h includes EventTarget.h.
This commit is contained in:
parent
0e6c7eea0f
commit
3479f1c4e8
6 changed files with 121 additions and 22 deletions
|
@ -71,7 +71,7 @@ void MediaQueryList::add_listener(RefPtr<DOM::IDLEventListener> listener)
|
||||||
// callback set to listener, and capture set to false, unless there already is an event listener
|
// callback set to listener, and capture set to false, unless there already is an event listener
|
||||||
// in that list with the same type, callback, and capture.
|
// in that list with the same type, callback, and capture.
|
||||||
// (NOTE: capture is set to false by default)
|
// (NOTE: capture is set to false by default)
|
||||||
add_event_listener(HTML::EventNames::change, listener);
|
add_event_listener_without_options(HTML::EventNames::change, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://www.w3.org/TR/cssom-view/#dom-mediaquerylist-removelistener
|
// https://www.w3.org/TR/cssom-view/#dom-mediaquerylist-removelistener
|
||||||
|
@ -80,7 +80,7 @@ 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.
|
||||||
// This means if an event listener removes another event listener that comes after it, the removed event listener will not be invoked.
|
// This means if an event listener removes another event listener that comes after it, the removed event listener will not be invoked.
|
||||||
remove_event_listener(HTML::EventNames::change, listener);
|
remove_event_listener_without_options(HTML::EventNames::change, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaQueryList::set_onchange(Optional<Bindings::CallbackType> event_handler)
|
void MediaQueryList::set_onchange(Optional<Bindings::CallbackType> event_handler)
|
||||||
|
|
|
@ -45,27 +45,87 @@ EventTarget::~EventTarget()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#dom-eventtarget-addeventlistener
|
// https://dom.spec.whatwg.org/#concept-flatten-options
|
||||||
void EventTarget::add_event_listener(FlyString const& type, RefPtr<IDLEventListener> callback, bool use_capture)
|
static bool flatten_event_listener_options(Variant<EventListenerOptions, bool> const& options)
|
||||||
{
|
{
|
||||||
// FIXME: 1. Let capture, passive, once, and signal be the result of flattening more options.
|
// 1. If options is a boolean, then return options.
|
||||||
bool capture = use_capture;
|
if (options.has<bool>())
|
||||||
bool passive = false;
|
return options.get<bool>();
|
||||||
|
|
||||||
|
// 2. Return options["capture"].
|
||||||
|
return options.get<EventListenerOptions>().capture;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool flatten_event_listener_options(Variant<AddEventListenerOptions, bool> const& options)
|
||||||
|
{
|
||||||
|
// 1. If options is a boolean, then return options.
|
||||||
|
if (options.has<bool>())
|
||||||
|
return options.get<bool>();
|
||||||
|
|
||||||
|
// 2. Return options["capture"].
|
||||||
|
return options.get<AddEventListenerOptions>().capture;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FlattenedAddEventListenerOptions {
|
||||||
|
bool capture { false };
|
||||||
|
bool passive { false };
|
||||||
|
bool once { false };
|
||||||
|
RefPtr<AbortSignal> signal;
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://dom.spec.whatwg.org/#event-flatten-more
|
||||||
|
static FlattenedAddEventListenerOptions flatten_add_event_listener_options(Variant<AddEventListenerOptions, bool> const& options)
|
||||||
|
{
|
||||||
|
// 1. Let capture be the result of flattening options.
|
||||||
|
bool capture = flatten_event_listener_options(options);
|
||||||
|
|
||||||
|
// 2. Let once and passive be false.
|
||||||
bool once = false;
|
bool once = false;
|
||||||
RefPtr<AbortSignal> signal = nullptr;
|
bool passive = false;
|
||||||
|
|
||||||
|
// 3. Let signal be null.
|
||||||
|
RefPtr<AbortSignal> signal;
|
||||||
|
|
||||||
|
// 4. If options is a dictionary, then:
|
||||||
|
if (options.has<AddEventListenerOptions>()) {
|
||||||
|
auto& add_event_listener_options = options.get<AddEventListenerOptions>();
|
||||||
|
|
||||||
|
// 1. Set passive to options["passive"] and once to options["once"].
|
||||||
|
passive = add_event_listener_options.passive;
|
||||||
|
once = add_event_listener_options.once;
|
||||||
|
|
||||||
|
// 2. If options["signal"] exists, then set signal to options["signal"].
|
||||||
|
if (add_event_listener_options.signal.has_value())
|
||||||
|
signal = add_event_listener_options.signal.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Return capture, passive, once, and signal.
|
||||||
|
return FlattenedAddEventListenerOptions { .capture = capture, .passive = passive, .once = once, .signal = signal };
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://dom.spec.whatwg.org/#dom-eventtarget-addeventlistener
|
||||||
|
void EventTarget::add_event_listener(FlyString const& type, RefPtr<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,
|
// 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.
|
// once is once, and signal is signal.
|
||||||
auto event_listener = adopt_ref(*new DOMEventListener);
|
auto event_listener = adopt_ref(*new DOMEventListener);
|
||||||
event_listener->type = type;
|
event_listener->type = type;
|
||||||
event_listener->callback = move(callback);
|
event_listener->callback = move(callback);
|
||||||
event_listener->signal = move(signal);
|
event_listener->signal = move(flattened_options.signal);
|
||||||
event_listener->capture = capture;
|
event_listener->capture = flattened_options.capture;
|
||||||
event_listener->passive = passive;
|
event_listener->passive = flattened_options.passive;
|
||||||
event_listener->once = once;
|
event_listener->once = flattened_options.once;
|
||||||
add_an_event_listener(move(event_listener));
|
add_an_event_listener(move(event_listener));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EventTarget::add_event_listener_without_options(FlyString const& type, RefPtr<IDLEventListener> callback)
|
||||||
|
{
|
||||||
|
add_event_listener(type, move(callback), AddEventListenerOptions {});
|
||||||
|
}
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#add-an-event-listener
|
// https://dom.spec.whatwg.org/#add-an-event-listener
|
||||||
void EventTarget::add_an_event_listener(NonnullRefPtr<DOMEventListener> listener)
|
void EventTarget::add_an_event_listener(NonnullRefPtr<DOMEventListener> listener)
|
||||||
{
|
{
|
||||||
|
@ -91,15 +151,20 @@ void EventTarget::add_an_event_listener(NonnullRefPtr<DOMEventListener> listener
|
||||||
if (it == m_event_listener_list.end())
|
if (it == m_event_listener_list.end())
|
||||||
m_event_listener_list.append(listener);
|
m_event_listener_list.append(listener);
|
||||||
|
|
||||||
// FIXME: 5. If listener’s signal is not null, then add the following abort steps to it:
|
// 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.
|
if (listener->signal) {
|
||||||
|
listener->signal->add_abort_algorithm([strong_event_target = NonnullRefPtr(*this), listener]() mutable {
|
||||||
|
// 1. Remove an event listener with eventTarget and listener.
|
||||||
|
strong_event_target->remove_an_event_listener(listener);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#dom-eventtarget-removeeventlistener
|
// https://dom.spec.whatwg.org/#dom-eventtarget-removeeventlistener
|
||||||
void EventTarget::remove_event_listener(FlyString const& type, RefPtr<IDLEventListener> callback, bool use_capture)
|
void EventTarget::remove_event_listener(FlyString const& type, RefPtr<IDLEventListener> callback, Variant<EventListenerOptions, bool> const& options)
|
||||||
{
|
{
|
||||||
// FIXME: 1. Let capture be the result of flattening options.
|
// 1. Let capture be the result of flattening options.
|
||||||
bool capture = use_capture;
|
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,
|
// 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.
|
// then remove an event listener with this and that event listener.
|
||||||
|
@ -112,6 +177,11 @@ void EventTarget::remove_event_listener(FlyString const& type, RefPtr<IDLEventLi
|
||||||
remove_an_event_listener(*it);
|
remove_an_event_listener(*it);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EventTarget::remove_event_listener_without_options(FlyString const& type, RefPtr<IDLEventListener> callback)
|
||||||
|
{
|
||||||
|
remove_event_listener(type, move(callback), EventListenerOptions {});
|
||||||
|
}
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#remove-an-event-listener
|
// https://dom.spec.whatwg.org/#remove-an-event-listener
|
||||||
void EventTarget::remove_an_event_listener(DOMEventListener& listener)
|
void EventTarget::remove_an_event_listener(DOMEventListener& listener)
|
||||||
{
|
{
|
||||||
|
|
|
@ -29,8 +29,12 @@ public:
|
||||||
|
|
||||||
virtual bool is_focusable() const { return false; }
|
virtual bool is_focusable() const { return false; }
|
||||||
|
|
||||||
void add_event_listener(FlyString const& type, RefPtr<IDLEventListener> callback, bool use_capture = 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, bool use_capture = false);
|
void remove_event_listener(FlyString const& type, RefPtr<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);
|
||||||
|
|
||||||
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>);
|
||||||
|
|
|
@ -1,9 +1,20 @@
|
||||||
|
#import <DOM/AbortSignal.idl>
|
||||||
|
|
||||||
interface EventTarget {
|
interface EventTarget {
|
||||||
|
|
||||||
// FIXME: Both of these should take in options
|
undefined addEventListener(DOMString type, EventListener? callback, optional (AddEventListenerOptions or boolean) options = {});
|
||||||
undefined addEventListener(DOMString type, EventListener? callback, optional boolean useCapture = false);
|
undefined removeEventListener(DOMString type, EventListener? callback, optional (EventListenerOptions or boolean) options = {});
|
||||||
undefined removeEventListener(DOMString type, EventListener? callback, optional boolean useCapture = false);
|
|
||||||
|
|
||||||
[ImplementedAs=dispatch_event_binding] boolean dispatchEvent(Event event);
|
[ImplementedAs=dispatch_event_binding] boolean dispatchEvent(Event event);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
dictionary EventListenerOptions {
|
||||||
|
boolean capture = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
dictionary AddEventListenerOptions : EventListenerOptions {
|
||||||
|
boolean passive = false;
|
||||||
|
boolean once = false;
|
||||||
|
AbortSignal signal;
|
||||||
|
};
|
||||||
|
|
|
@ -10,9 +10,21 @@
|
||||||
#include <LibJS/Heap/Handle.h>
|
#include <LibJS/Heap/Handle.h>
|
||||||
#include <LibWeb/Bindings/CallbackType.h>
|
#include <LibWeb/Bindings/CallbackType.h>
|
||||||
#include <LibWeb/Bindings/Wrappable.h>
|
#include <LibWeb/Bindings/Wrappable.h>
|
||||||
|
#include <LibWeb/DOM/AbortSignal.h>
|
||||||
|
|
||||||
namespace Web::DOM {
|
namespace Web::DOM {
|
||||||
|
|
||||||
|
// NOTE: Even though these dictionaries are defined in EventTarget.idl, they are here to prevent a circular include between EventTarget.h and AbortSignal.h.
|
||||||
|
struct EventListenerOptions {
|
||||||
|
bool capture { false };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AddEventListenerOptions : public EventListenerOptions {
|
||||||
|
bool passive { false };
|
||||||
|
bool once { false };
|
||||||
|
Optional<NonnullRefPtr<AbortSignal>> signal;
|
||||||
|
};
|
||||||
|
|
||||||
class IDLEventListener
|
class IDLEventListener
|
||||||
: public RefCounted<IDLEventListener>
|
: public RefCounted<IDLEventListener>
|
||||||
, public Bindings::Wrappable {
|
, public Bindings::Wrappable {
|
||||||
|
|
|
@ -108,6 +108,8 @@ class Text;
|
||||||
class Timer;
|
class Timer;
|
||||||
class Window;
|
class Window;
|
||||||
enum class QuirksMode;
|
enum class QuirksMode;
|
||||||
|
struct EventListenerOptions;
|
||||||
|
struct AddEventListenerOptions;
|
||||||
|
|
||||||
template<typename ValueType>
|
template<typename ValueType>
|
||||||
class ExceptionOr;
|
class ExceptionOr;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue