mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
script: use passive event listener option on AddEventListenerOptions (#35877)
Signed-off-by: Shane Handley <shanehandley@fastmail.com>
This commit is contained in:
parent
1b6b21cb85
commit
7fc5dc5c69
6 changed files with 87 additions and 217 deletions
|
@ -95,6 +95,9 @@ pub(crate) struct Event {
|
|||
|
||||
/// <https://dom.spec.whatwg.org/#event-relatedtarget>
|
||||
related_target: MutNullableDom<EventTarget>,
|
||||
|
||||
/// <https://dom.spec.whatwg.org/#in-passive-listener-flag>
|
||||
in_passive_listener: Cell<bool>,
|
||||
}
|
||||
|
||||
/// An element on an [event path](https://dom.spec.whatwg.org/#event-path)
|
||||
|
@ -140,6 +143,7 @@ impl Event {
|
|||
time_stamp: CrossProcessInstant::now(),
|
||||
path: DomRefCell::default(),
|
||||
related_target: Default::default(),
|
||||
in_passive_listener: Cell::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -217,6 +221,10 @@ impl Event {
|
|||
self.target.set(target_);
|
||||
}
|
||||
|
||||
pub(crate) fn set_in_passive_listener(&self, value: bool) {
|
||||
self.in_passive_listener.set(value);
|
||||
}
|
||||
|
||||
/// <https://dom.spec.whatwg.org/#concept-event-path-append>
|
||||
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
|
||||
pub(crate) fn append_to_path(
|
||||
|
@ -728,6 +736,13 @@ impl Event {
|
|||
|
||||
&*target_node.GetRootNode(&GetRootNodeOptions::empty()) != shadow_root.upcast::<Node>()
|
||||
}
|
||||
|
||||
/// <https://dom.spec.whatwg.org/#set-the-canceled-flag>
|
||||
fn set_the_cancelled_flag(&self) {
|
||||
if self.cancelable.get() && !self.in_passive_listener.get() {
|
||||
self.canceled.set(EventDefault::Prevented)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EventMethods<crate::DomTypeHolder> for Event {
|
||||
|
@ -917,9 +932,7 @@ impl EventMethods<crate::DomTypeHolder> for Event {
|
|||
|
||||
/// <https://dom.spec.whatwg.org/#dom-event-preventdefault>
|
||||
fn PreventDefault(&self) {
|
||||
if self.cancelable.get() {
|
||||
self.canceled.set(EventDefault::Prevented)
|
||||
}
|
||||
self.set_the_cancelled_flag();
|
||||
}
|
||||
|
||||
/// <https://dom.spec.whatwg.org/#dom-event-stoppropagation>
|
||||
|
@ -951,7 +964,7 @@ impl EventMethods<crate::DomTypeHolder> for Event {
|
|||
/// <https://dom.spec.whatwg.org/#dom-event-returnvalue>
|
||||
fn SetReturnValue(&self, val: bool) {
|
||||
if !val {
|
||||
self.PreventDefault();
|
||||
self.set_the_cancelled_flag();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1224,13 +1237,14 @@ fn inner_invoke(
|
|||
// TODO Step 2.3 If phase is "capturing" and listener’s capture is false, then continue.
|
||||
// TODO Step 2.4 If phase is "bubbling" and listener’s capture is true, then continue.
|
||||
|
||||
let event_target = event
|
||||
.GetCurrentTarget()
|
||||
.expect("event target was initialized as part of \"invoke\"");
|
||||
|
||||
// Step 2.5 If listener’s once is true, then remove an event listener given event’s currentTarget
|
||||
// attribute value and listener.
|
||||
if let CompiledEventListener::Listener(event_listener) = listener {
|
||||
event
|
||||
.GetCurrentTarget()
|
||||
.expect("event target was initialized as part of \"invoke\"")
|
||||
.remove_listener_if_once(&event.type_(), event_listener);
|
||||
event_target.remove_listener_if_once(&event.type_(), event_listener);
|
||||
}
|
||||
|
||||
// Step 2.6 Let global be listener callback’s associated realm’s global object.
|
||||
|
@ -1249,7 +1263,10 @@ fn inner_invoke(
|
|||
}
|
||||
}
|
||||
|
||||
// TODO Step 2.9 If listener’s passive is true, then set event’s in passive listener flag.
|
||||
// Step 2.9 If listener’s passive is true, then set event's in passive listener flag.
|
||||
if let CompiledEventListener::Listener(event_listener) = listener {
|
||||
event.set_in_passive_listener(event_target.is_passive(&event.type_(), event_listener));
|
||||
}
|
||||
|
||||
// Step 2.10 If global is a Window object, then record timing info for event listener given event and listener.
|
||||
// Step 2.11 Call a user object’s operation with listener’s callback, "handleEvent", « event »,
|
||||
|
@ -1258,19 +1275,13 @@ fn inner_invoke(
|
|||
// associated realm’s global object.
|
||||
// TODO Step 2.10.2 Set legacyOutputDidListenersThrowFlag if given.
|
||||
let marker = TimelineMarker::start("DOMEvent".to_owned());
|
||||
listener.call_or_handle_event(
|
||||
&event
|
||||
.GetCurrentTarget()
|
||||
.expect("event target was initialized as part of \"invoke\""),
|
||||
event,
|
||||
ExceptionHandling::Report,
|
||||
can_gc,
|
||||
);
|
||||
listener.call_or_handle_event(&event_target, event, ExceptionHandling::Report, can_gc);
|
||||
if let Some(window) = timeline_window {
|
||||
window.emit_timeline_marker(marker.end());
|
||||
}
|
||||
|
||||
// TODO Step 2.12 Unset event’s in passive listener flag.
|
||||
// Step 2.12 Unset event’s in passive listener flag.
|
||||
event.set_in_passive_listener(false);
|
||||
|
||||
// Step 2.13 If global is a Window object, then set global’s current event to currentEvent.
|
||||
if let Some(window) = global.downcast::<Window>() {
|
||||
|
|
|
@ -23,6 +23,7 @@ use js::rust::{
|
|||
use libc::c_char;
|
||||
use servo_atoms::Atom;
|
||||
use servo_url::ServoUrl;
|
||||
use style::str::HTML_SPACE_CHARACTERS;
|
||||
|
||||
use crate::dom::beforeunloadevent::BeforeUnloadEvent;
|
||||
use crate::dom::bindings::callback::{CallbackContainer, CallbackFunction, ExceptionHandling};
|
||||
|
@ -41,6 +42,7 @@ use crate::dom::bindings::codegen::Bindings::NodeBinding::GetRootNodeOptions;
|
|||
use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRoot_Binding::ShadowRootMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
||||
use crate::dom::bindings::codegen::GenericBindings::DocumentBinding::Document_Binding::DocumentMethods;
|
||||
use crate::dom::bindings::codegen::UnionTypes::{
|
||||
AddEventListenerOptionsOrBoolean, EventListenerOptionsOrBoolean, EventOrString,
|
||||
};
|
||||
|
@ -308,6 +310,7 @@ struct EventListenerEntry {
|
|||
phase: ListenerPhase,
|
||||
listener: EventListenerType,
|
||||
once: bool,
|
||||
passive: Option<bool>,
|
||||
}
|
||||
|
||||
impl std::cmp::PartialEq for EventListenerEntry {
|
||||
|
@ -438,6 +441,42 @@ impl EventTarget {
|
|||
*self.handlers.borrow_mut() = Default::default();
|
||||
}
|
||||
|
||||
/// <https://dom.spec.whatwg.org/#default-passive-value>
|
||||
fn default_passive_value(&self, ty: &Atom) -> bool {
|
||||
// Return true if all of the following are true:
|
||||
let event_type = ty.to_ascii_lowercase();
|
||||
|
||||
// type is one of "touchstart", "touchmove", "wheel", or "mousewheel"
|
||||
let matches_event_type = matches!(
|
||||
event_type.trim_matches(HTML_SPACE_CHARACTERS),
|
||||
"touchstart" | "touchmove" | "wheel" | "mousewheel"
|
||||
);
|
||||
|
||||
if !matches_event_type {
|
||||
return false;
|
||||
}
|
||||
|
||||
// eventTarget is a Window object
|
||||
if self.is::<Window>() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// or ...
|
||||
if let Some(node) = self.downcast::<Node>() {
|
||||
let node_document = node.owner_document();
|
||||
let event_target = self.upcast::<EventTarget>();
|
||||
|
||||
// is a node whose node document is eventTarget
|
||||
return event_target == node_document.upcast::<EventTarget>()
|
||||
// or is a node whose node document’s document element is eventTarget
|
||||
|| node_document.GetDocumentElement().is_some_and(|n| n.upcast::<EventTarget>() == event_target)
|
||||
// or is a node whose node document’s body element is eventTarget
|
||||
|| node_document.GetBody().is_some_and(|n| n.upcast::<EventTarget>() == event_target);
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#event-handler-attributes:event-handlers-11>
|
||||
fn set_inline_event_listener(&self, ty: Atom, listener: Option<InlineEventListener>) {
|
||||
let mut handlers = self.handlers.borrow_mut();
|
||||
|
@ -467,6 +506,7 @@ impl EventTarget {
|
|||
phase: ListenerPhase::Bubbling,
|
||||
listener: EventListenerType::Inline(listener.into()),
|
||||
once: false,
|
||||
passive: None,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
@ -482,6 +522,17 @@ impl EventTarget {
|
|||
}
|
||||
}
|
||||
|
||||
/// Determines the `passive` attribute of an associated event listener
|
||||
pub(crate) fn is_passive(&self, ty: &Atom, listener: &Rc<EventListener>) -> bool {
|
||||
let handlers = self.handlers.borrow();
|
||||
let listener_instance = EventListenerType::Additive(listener.clone());
|
||||
|
||||
handlers
|
||||
.get(ty)
|
||||
.and_then(|entries| entries.iter().find(|e| e.listener == listener_instance))
|
||||
.is_some_and(|entry| entry.passive.unwrap_or(self.default_passive_value(ty)))
|
||||
}
|
||||
|
||||
fn get_inline_event_listener(&self, ty: &Atom, can_gc: CanGc) -> Option<CommonEventHandler> {
|
||||
let handlers = self.handlers.borrow();
|
||||
handlers
|
||||
|
@ -745,7 +796,8 @@ impl EventTarget {
|
|||
event.fire(self, can_gc);
|
||||
event
|
||||
}
|
||||
// https://dom.spec.whatwg.org/#dom-eventtarget-addeventlistener
|
||||
|
||||
/// <https://dom.spec.whatwg.org/#dom-eventtarget-addeventlistener>
|
||||
pub(crate) fn add_event_listener(
|
||||
&self,
|
||||
ty: DOMString,
|
||||
|
@ -771,6 +823,7 @@ impl EventTarget {
|
|||
phase,
|
||||
listener: EventListenerType::Additive(listener),
|
||||
once: options.once,
|
||||
passive: options.passive,
|
||||
};
|
||||
if !entry.contains(&new_entry) {
|
||||
entry.push(new_entry);
|
||||
|
@ -799,6 +852,7 @@ impl EventTarget {
|
|||
phase,
|
||||
listener: EventListenerType::Additive(listener.clone()),
|
||||
once: false,
|
||||
passive: None,
|
||||
};
|
||||
if let Some(position) = entry.iter().position(|e| *e == old_entry) {
|
||||
entry.remove(position);
|
||||
|
@ -929,6 +983,7 @@ impl From<AddEventListenerOptionsOrBoolean> for AddEventListenerOptions {
|
|||
AddEventListenerOptionsOrBoolean::Boolean(capture) => Self {
|
||||
parent: EventListenerOptions { capture },
|
||||
once: false,
|
||||
passive: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -106,6 +106,7 @@ impl MediaQueryListMethods<crate::DomTypeHolder> for MediaQueryList {
|
|||
AddEventListenerOptions {
|
||||
parent: EventListenerOptions { capture: false },
|
||||
once: false,
|
||||
passive: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -29,6 +29,6 @@ dictionary EventListenerOptions {
|
|||
};
|
||||
|
||||
dictionary AddEventListenerOptions : EventListenerOptions {
|
||||
// boolean passive = false;
|
||||
boolean passive;
|
||||
boolean once = false;
|
||||
};
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
[AddEventListenerOptions-passive.any.worker.html]
|
||||
[Supports passive option on addEventListener only]
|
||||
expected: FAIL
|
||||
|
||||
[preventDefault should be ignored if-and-only-if the passive option is true]
|
||||
expected: FAIL
|
||||
|
||||
[returnValue should be ignored if-and-only-if the passive option is true]
|
||||
expected: FAIL
|
||||
|
||||
[passive behavior of one listener should be unaffected by the presence of other listeners]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[AddEventListenerOptions-passive.any.html]
|
||||
[Supports passive option on addEventListener only]
|
||||
expected: FAIL
|
||||
|
||||
[preventDefault should be ignored if-and-only-if the passive option is true]
|
||||
expected: FAIL
|
||||
|
||||
[returnValue should be ignored if-and-only-if the passive option is true]
|
||||
expected: FAIL
|
||||
|
||||
[passive behavior of one listener should be unaffected by the presence of other listeners]
|
||||
expected: FAIL
|
|
@ -1,171 +0,0 @@
|
|||
[passive-by-default.html]
|
||||
[touchstart listener is passive by default for Window]
|
||||
expected: FAIL
|
||||
|
||||
[touchstart listener is passive with {passive:undefined} for Window]
|
||||
expected: FAIL
|
||||
|
||||
[touchstart listener is passive with {passive:true} for Window]
|
||||
expected: FAIL
|
||||
|
||||
[touchstart listener is passive by default for Document]
|
||||
expected: FAIL
|
||||
|
||||
[touchstart listener is passive with {passive:undefined} for Document]
|
||||
expected: FAIL
|
||||
|
||||
[touchstart listener is passive with {passive:true} for Document]
|
||||
expected: FAIL
|
||||
|
||||
[touchstart listener is passive by default for HTMLHtmlElement]
|
||||
expected: FAIL
|
||||
|
||||
[touchstart listener is passive with {passive:undefined} for HTMLHtmlElement]
|
||||
expected: FAIL
|
||||
|
||||
[touchstart listener is passive with {passive:true} for HTMLHtmlElement]
|
||||
expected: FAIL
|
||||
|
||||
[touchstart listener is passive by default for HTMLBodyElement]
|
||||
expected: FAIL
|
||||
|
||||
[touchstart listener is passive with {passive:undefined} for HTMLBodyElement]
|
||||
expected: FAIL
|
||||
|
||||
[touchstart listener is passive with {passive:true} for HTMLBodyElement]
|
||||
expected: FAIL
|
||||
|
||||
[touchstart listener is passive with {passive:true} for HTMLDivElement]
|
||||
expected: FAIL
|
||||
|
||||
[touchmove listener is passive by default for Window]
|
||||
expected: FAIL
|
||||
|
||||
[touchmove listener is passive with {passive:undefined} for Window]
|
||||
expected: FAIL
|
||||
|
||||
[touchmove listener is passive with {passive:true} for Window]
|
||||
expected: FAIL
|
||||
|
||||
[touchmove listener is passive by default for Document]
|
||||
expected: FAIL
|
||||
|
||||
[touchmove listener is passive with {passive:undefined} for Document]
|
||||
expected: FAIL
|
||||
|
||||
[touchmove listener is passive with {passive:true} for Document]
|
||||
expected: FAIL
|
||||
|
||||
[touchmove listener is passive by default for HTMLHtmlElement]
|
||||
expected: FAIL
|
||||
|
||||
[touchmove listener is passive with {passive:undefined} for HTMLHtmlElement]
|
||||
expected: FAIL
|
||||
|
||||
[touchmove listener is passive with {passive:true} for HTMLHtmlElement]
|
||||
expected: FAIL
|
||||
|
||||
[touchmove listener is passive by default for HTMLBodyElement]
|
||||
expected: FAIL
|
||||
|
||||
[touchmove listener is passive with {passive:undefined} for HTMLBodyElement]
|
||||
expected: FAIL
|
||||
|
||||
[touchmove listener is passive with {passive:true} for HTMLBodyElement]
|
||||
expected: FAIL
|
||||
|
||||
[touchmove listener is passive with {passive:true} for HTMLDivElement]
|
||||
expected: FAIL
|
||||
|
||||
[wheel listener is passive by default for Window]
|
||||
expected: FAIL
|
||||
|
||||
[wheel listener is passive with {passive:undefined} for Window]
|
||||
expected: FAIL
|
||||
|
||||
[wheel listener is passive with {passive:true} for Window]
|
||||
expected: FAIL
|
||||
|
||||
[wheel listener is passive by default for Document]
|
||||
expected: FAIL
|
||||
|
||||
[wheel listener is passive with {passive:undefined} for Document]
|
||||
expected: FAIL
|
||||
|
||||
[wheel listener is passive with {passive:true} for Document]
|
||||
expected: FAIL
|
||||
|
||||
[wheel listener is passive by default for HTMLHtmlElement]
|
||||
expected: FAIL
|
||||
|
||||
[wheel listener is passive with {passive:undefined} for HTMLHtmlElement]
|
||||
expected: FAIL
|
||||
|
||||
[wheel listener is passive with {passive:true} for HTMLHtmlElement]
|
||||
expected: FAIL
|
||||
|
||||
[wheel listener is passive by default for HTMLBodyElement]
|
||||
expected: FAIL
|
||||
|
||||
[wheel listener is passive with {passive:undefined} for HTMLBodyElement]
|
||||
expected: FAIL
|
||||
|
||||
[wheel listener is passive with {passive:true} for HTMLBodyElement]
|
||||
expected: FAIL
|
||||
|
||||
[wheel listener is passive with {passive:true} for HTMLDivElement]
|
||||
expected: FAIL
|
||||
|
||||
[mousewheel listener is passive by default for Window]
|
||||
expected: FAIL
|
||||
|
||||
[mousewheel listener is passive with {passive:undefined} for Window]
|
||||
expected: FAIL
|
||||
|
||||
[mousewheel listener is passive with {passive:true} for Window]
|
||||
expected: FAIL
|
||||
|
||||
[mousewheel listener is passive by default for Document]
|
||||
expected: FAIL
|
||||
|
||||
[mousewheel listener is passive with {passive:undefined} for Document]
|
||||
expected: FAIL
|
||||
|
||||
[mousewheel listener is passive with {passive:true} for Document]
|
||||
expected: FAIL
|
||||
|
||||
[mousewheel listener is passive by default for HTMLHtmlElement]
|
||||
expected: FAIL
|
||||
|
||||
[mousewheel listener is passive with {passive:undefined} for HTMLHtmlElement]
|
||||
expected: FAIL
|
||||
|
||||
[mousewheel listener is passive with {passive:true} for HTMLHtmlElement]
|
||||
expected: FAIL
|
||||
|
||||
[mousewheel listener is passive by default for HTMLBodyElement]
|
||||
expected: FAIL
|
||||
|
||||
[mousewheel listener is passive with {passive:undefined} for HTMLBodyElement]
|
||||
expected: FAIL
|
||||
|
||||
[mousewheel listener is passive with {passive:true} for HTMLBodyElement]
|
||||
expected: FAIL
|
||||
|
||||
[mousewheel listener is passive with {passive:true} for HTMLDivElement]
|
||||
expected: FAIL
|
||||
|
||||
[touchend listener is passive with {passive:true} for Window]
|
||||
expected: FAIL
|
||||
|
||||
[touchend listener is passive with {passive:true} for Document]
|
||||
expected: FAIL
|
||||
|
||||
[touchend listener is passive with {passive:true} for HTMLHtmlElement]
|
||||
expected: FAIL
|
||||
|
||||
[touchend listener is passive with {passive:true} for HTMLBodyElement]
|
||||
expected: FAIL
|
||||
|
||||
[touchend listener is passive with {passive:true} for HTMLDivElement]
|
||||
expected: FAIL
|
Loading…
Add table
Add a link
Reference in a new issue