diff --git a/Libraries/LibWeb/DOM/DOMEventListener.h b/Libraries/LibWeb/DOM/DOMEventListener.h index 6231ee047ea..361edb9e543 100644 --- a/Libraries/LibWeb/DOM/DOMEventListener.h +++ b/Libraries/LibWeb/DOM/DOMEventListener.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2022, Andreas Kling + * Copyright (c) 2024, Glenn Skrzypczak * * SPDX-License-Identifier: BSD-2-Clause */ @@ -35,8 +36,8 @@ public: // capture (a boolean, initially false) bool capture { false }; - // passive (a boolean, initially false) - bool passive { false }; + // passive (null or a boolean, initially null) + Optional passive; // once (a boolean, initially false) bool once { false }; diff --git a/Libraries/LibWeb/DOM/EventDispatcher.cpp b/Libraries/LibWeb/DOM/EventDispatcher.cpp index 2a0673d772c..4d7a1064b06 100644 --- a/Libraries/LibWeb/DOM/EventDispatcher.cpp +++ b/Libraries/LibWeb/DOM/EventDispatcher.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2020-2022, Andreas Kling + * Copyright (c) 2024, Glenn Skrzypczak * * SPDX-License-Identifier: BSD-2-Clause */ @@ -81,7 +82,7 @@ bool EventDispatcher::inner_invoke(Event& event, Vectorpassive) + if (listener->passive == true) event.set_in_passive_listener(true); // FIXME: 10. If global is a Window object, then record timing info for event listener given event and listener. diff --git a/Libraries/LibWeb/DOM/EventTarget.cpp b/Libraries/LibWeb/DOM/EventTarget.cpp index 9a1d0533753..ef635ae9542 100644 --- a/Libraries/LibWeb/DOM/EventTarget.cpp +++ b/Libraries/LibWeb/DOM/EventTarget.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2020-2022, Andreas Kling * Copyright (c) 2022, Luke Wilde + * Copyright (c) 2024, Glenn Skrzypczak * * SPDX-License-Identifier: BSD-2-Clause */ @@ -110,7 +111,7 @@ static bool flatten_event_listener_options(Variant passive; bool once { false }; GC::Ptr signal; }; @@ -121,22 +122,25 @@ static FlattenedAddEventListenerOptions flatten_add_event_listener_options(Varia // 1. Let capture be the result of flattening options. bool capture = flatten_event_listener_options(options); - // 2. Let once and passive be false. + // 2. Let once be false. bool once = false; - bool passive = false; - // 3. Let signal be null. + // 3. Let passive and signal be null. + Optional passive; GC::Ptr signal; // 4. If options is a dictionary, then: if (options.has()) { - auto& add_event_listener_options = options.get(); + auto const& add_event_listener_options = options.get(); - // 1. Set passive to options["passive"] and once to options["once"]. - passive = add_event_listener_options.passive; + // 1. Set once to options["once"]. once = add_event_listener_options.once; - // 2. If options["signal"] exists, then set signal to options["signal"]. + // 2. If options["passive"] exists, then set passive to options["passive"]. + if (add_event_listener_options.passive.has_value()) + passive = add_event_listener_options.passive; + + // 3. If options["signal"] exists, then set signal to options["signal"]. if (add_event_listener_options.signal) signal = add_event_listener_options.signal; } @@ -145,6 +149,28 @@ static FlattenedAddEventListenerOptions flatten_add_event_listener_options(Varia return FlattenedAddEventListenerOptions { .capture = capture, .passive = passive, .once = once, .signal = signal.ptr() }; } +// https://dom.spec.whatwg.org/#default-passive-value +static bool default_passive_value(FlyString const& type, EventTarget* event_target) +{ + // 1. Return true if all of the following are true: + // - type is one of "touchstart", "touchmove", "wheel", or "mousewheel". + // - eventTarget is a Window object, or is a node whose node document is eventTarget, or is a node whose node document’s document element is eventTarget, + // or is a node whose node document’s body element is eventTarget. + if (AK::first_is_one_of(type, "touchstart"sv, "touchmove"sv, "wheel"sv, "mousewheel"sv)) { + if (is(event_target)) + return true; + + if (is(event_target)) { + auto* node = verify_cast(event_target); + if (&node->document() == event_target || node->document().document_element() == event_target || node->document().body() == event_target) + return true; + } + } + + // 2. Return false. + return false; +} + // https://dom.spec.whatwg.org/#dom-eventtarget-addeventlistener void EventTarget::add_event_listener(FlyString const& type, IDLEventListener* callback, Variant const& options) { @@ -186,7 +212,12 @@ void EventTarget::add_an_event_listener(DOMEventListener& listener) 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, + // 4. If listener’s passive is null, then set it to the default passive value given listener’s type and eventTarget. + if (!listener.passive.has_value()) { + listener.passive = default_passive_value(listener.type, this); + } + + // 5. 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 = event_listener_list.find_if([&](auto& entry) { return entry->type == listener.type @@ -196,7 +227,7 @@ void EventTarget::add_an_event_listener(DOMEventListener& listener) if (it == event_listener_list.end()) event_listener_list.append(listener); - // 5. If listener’s signal is not null, then add the following abort steps to it: + // 6. If listener’s signal is not null, then add the following abort steps to it: if (listener.signal) { // NOTE: `this` and `listener` are protected by AbortSignal using GC::HeapFunction. listener.signal->add_abort_algorithm([this, &listener] { diff --git a/Libraries/LibWeb/DOM/EventTarget.idl b/Libraries/LibWeb/DOM/EventTarget.idl index d30fc3847c7..5c765882ba4 100644 --- a/Libraries/LibWeb/DOM/EventTarget.idl +++ b/Libraries/LibWeb/DOM/EventTarget.idl @@ -20,7 +20,7 @@ dictionary EventListenerOptions { }; dictionary AddEventListenerOptions : EventListenerOptions { - boolean passive = false; + boolean passive; boolean once = false; AbortSignal signal; }; diff --git a/Libraries/LibWeb/DOM/IDLEventListener.h b/Libraries/LibWeb/DOM/IDLEventListener.h index f8bc9b06c50..1abef5f1df3 100644 --- a/Libraries/LibWeb/DOM/IDLEventListener.h +++ b/Libraries/LibWeb/DOM/IDLEventListener.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2020-2022, Andreas Kling + * Copyright (c) 2024, Glenn Skrzypczak * * SPDX-License-Identifier: BSD-2-Clause */ @@ -19,7 +20,7 @@ struct EventListenerOptions { }; struct AddEventListenerOptions : public EventListenerOptions { - bool passive { false }; + Optional passive; bool once { false }; GC::Ptr signal; }; diff --git a/Tests/LibWeb/Text/expected/wpt-import/dom/events/passive-by-default.txt b/Tests/LibWeb/Text/expected/wpt-import/dom/events/passive-by-default.txt index ecc4653d70b..40d4c49ccd3 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/dom/events/passive-by-default.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/dom/events/passive-by-default.txt @@ -2,82 +2,81 @@ Harness status: OK Found 100 tests -68 Pass -32 Fail -Fail touchstart listener is passive by default for Window -Fail touchstart listener is passive with {passive:undefined} for Window +100 Pass +Pass touchstart listener is passive by default for Window +Pass touchstart listener is passive with {passive:undefined} for Window Pass touchstart listener is non-passive with {passive:false} for Window Pass touchstart listener is passive with {passive:true} for Window -Fail touchstart listener is passive by default for HTMLDocument -Fail touchstart listener is passive with {passive:undefined} for HTMLDocument +Pass touchstart listener is passive by default for HTMLDocument +Pass touchstart listener is passive with {passive:undefined} for HTMLDocument Pass touchstart listener is non-passive with {passive:false} for HTMLDocument Pass touchstart listener is passive with {passive:true} for HTMLDocument -Fail touchstart listener is passive by default for HTMLHtmlElement -Fail touchstart listener is passive with {passive:undefined} for HTMLHtmlElement +Pass touchstart listener is passive by default for HTMLHtmlElement +Pass touchstart listener is passive with {passive:undefined} for HTMLHtmlElement Pass touchstart listener is non-passive with {passive:false} for HTMLHtmlElement Pass touchstart listener is passive with {passive:true} for HTMLHtmlElement -Fail touchstart listener is passive by default for HTMLBodyElement -Fail touchstart listener is passive with {passive:undefined} for HTMLBodyElement +Pass touchstart listener is passive by default for HTMLBodyElement +Pass touchstart listener is passive with {passive:undefined} for HTMLBodyElement Pass touchstart listener is non-passive with {passive:false} for HTMLBodyElement Pass touchstart listener is passive with {passive:true} for HTMLBodyElement Pass touchstart listener is non-passive by default for HTMLDivElement Pass touchstart listener is non-passive with {passive:undefined} for HTMLDivElement Pass touchstart listener is non-passive with {passive:false} for HTMLDivElement Pass touchstart listener is passive with {passive:true} for HTMLDivElement -Fail touchmove listener is passive by default for Window -Fail touchmove listener is passive with {passive:undefined} for Window +Pass touchmove listener is passive by default for Window +Pass touchmove listener is passive with {passive:undefined} for Window Pass touchmove listener is non-passive with {passive:false} for Window Pass touchmove listener is passive with {passive:true} for Window -Fail touchmove listener is passive by default for HTMLDocument -Fail touchmove listener is passive with {passive:undefined} for HTMLDocument +Pass touchmove listener is passive by default for HTMLDocument +Pass touchmove listener is passive with {passive:undefined} for HTMLDocument Pass touchmove listener is non-passive with {passive:false} for HTMLDocument Pass touchmove listener is passive with {passive:true} for HTMLDocument -Fail touchmove listener is passive by default for HTMLHtmlElement -Fail touchmove listener is passive with {passive:undefined} for HTMLHtmlElement +Pass touchmove listener is passive by default for HTMLHtmlElement +Pass touchmove listener is passive with {passive:undefined} for HTMLHtmlElement Pass touchmove listener is non-passive with {passive:false} for HTMLHtmlElement Pass touchmove listener is passive with {passive:true} for HTMLHtmlElement -Fail touchmove listener is passive by default for HTMLBodyElement -Fail touchmove listener is passive with {passive:undefined} for HTMLBodyElement +Pass touchmove listener is passive by default for HTMLBodyElement +Pass touchmove listener is passive with {passive:undefined} for HTMLBodyElement Pass touchmove listener is non-passive with {passive:false} for HTMLBodyElement Pass touchmove listener is passive with {passive:true} for HTMLBodyElement Pass touchmove listener is non-passive by default for HTMLDivElement Pass touchmove listener is non-passive with {passive:undefined} for HTMLDivElement Pass touchmove listener is non-passive with {passive:false} for HTMLDivElement Pass touchmove listener is passive with {passive:true} for HTMLDivElement -Fail wheel listener is passive by default for Window -Fail wheel listener is passive with {passive:undefined} for Window +Pass wheel listener is passive by default for Window +Pass wheel listener is passive with {passive:undefined} for Window Pass wheel listener is non-passive with {passive:false} for Window Pass wheel listener is passive with {passive:true} for Window -Fail wheel listener is passive by default for HTMLDocument -Fail wheel listener is passive with {passive:undefined} for HTMLDocument +Pass wheel listener is passive by default for HTMLDocument +Pass wheel listener is passive with {passive:undefined} for HTMLDocument Pass wheel listener is non-passive with {passive:false} for HTMLDocument Pass wheel listener is passive with {passive:true} for HTMLDocument -Fail wheel listener is passive by default for HTMLHtmlElement -Fail wheel listener is passive with {passive:undefined} for HTMLHtmlElement +Pass wheel listener is passive by default for HTMLHtmlElement +Pass wheel listener is passive with {passive:undefined} for HTMLHtmlElement Pass wheel listener is non-passive with {passive:false} for HTMLHtmlElement Pass wheel listener is passive with {passive:true} for HTMLHtmlElement -Fail wheel listener is passive by default for HTMLBodyElement -Fail wheel listener is passive with {passive:undefined} for HTMLBodyElement +Pass wheel listener is passive by default for HTMLBodyElement +Pass wheel listener is passive with {passive:undefined} for HTMLBodyElement Pass wheel listener is non-passive with {passive:false} for HTMLBodyElement Pass wheel listener is passive with {passive:true} for HTMLBodyElement Pass wheel listener is non-passive by default for HTMLDivElement Pass wheel listener is non-passive with {passive:undefined} for HTMLDivElement Pass wheel listener is non-passive with {passive:false} for HTMLDivElement Pass wheel listener is passive with {passive:true} for HTMLDivElement -Fail mousewheel listener is passive by default for Window -Fail mousewheel listener is passive with {passive:undefined} for Window +Pass mousewheel listener is passive by default for Window +Pass mousewheel listener is passive with {passive:undefined} for Window Pass mousewheel listener is non-passive with {passive:false} for Window Pass mousewheel listener is passive with {passive:true} for Window -Fail mousewheel listener is passive by default for HTMLDocument -Fail mousewheel listener is passive with {passive:undefined} for HTMLDocument +Pass mousewheel listener is passive by default for HTMLDocument +Pass mousewheel listener is passive with {passive:undefined} for HTMLDocument Pass mousewheel listener is non-passive with {passive:false} for HTMLDocument Pass mousewheel listener is passive with {passive:true} for HTMLDocument -Fail mousewheel listener is passive by default for HTMLHtmlElement -Fail mousewheel listener is passive with {passive:undefined} for HTMLHtmlElement +Pass mousewheel listener is passive by default for HTMLHtmlElement +Pass mousewheel listener is passive with {passive:undefined} for HTMLHtmlElement Pass mousewheel listener is non-passive with {passive:false} for HTMLHtmlElement Pass mousewheel listener is passive with {passive:true} for HTMLHtmlElement -Fail mousewheel listener is passive by default for HTMLBodyElement -Fail mousewheel listener is passive with {passive:undefined} for HTMLBodyElement +Pass mousewheel listener is passive by default for HTMLBodyElement +Pass mousewheel listener is passive with {passive:undefined} for HTMLBodyElement Pass mousewheel listener is non-passive with {passive:false} for HTMLBodyElement Pass mousewheel listener is passive with {passive:true} for HTMLBodyElement Pass mousewheel listener is non-passive by default for HTMLDivElement