script/dom/
event.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::Cell;
6use std::default::Default;
7
8use base::cross_process_instant::CrossProcessInstant;
9use devtools_traits::{TimelineMarker, TimelineMarkerType};
10use dom_struct::dom_struct;
11use js::rust::HandleObject;
12use stylo_atoms::Atom;
13
14use crate::dom::bindings::callback::ExceptionHandling;
15use crate::dom::bindings::cell::DomRefCell;
16use crate::dom::bindings::codegen::Bindings::EventBinding;
17use crate::dom::bindings::codegen::Bindings::EventBinding::{EventConstants, EventMethods};
18use crate::dom::bindings::codegen::Bindings::NodeBinding::GetRootNodeOptions;
19use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
20use crate::dom::bindings::codegen::Bindings::PerformanceBinding::DOMHighResTimeStamp;
21use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
22    ShadowRootMethods, ShadowRootMode,
23};
24use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
25use crate::dom::bindings::error::Fallible;
26use crate::dom::bindings::inheritance::Castable;
27use crate::dom::bindings::refcounted::Trusted;
28use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
29use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
30use crate::dom::bindings::str::DOMString;
31use crate::dom::element::Element;
32use crate::dom::eventtarget::{EventListeners, EventTarget, ListenerPhase};
33use crate::dom::globalscope::GlobalScope;
34use crate::dom::html::htmlinputelement::InputActivationState;
35use crate::dom::html::htmlslotelement::HTMLSlotElement;
36use crate::dom::mouseevent::MouseEvent;
37use crate::dom::node::{Node, NodeTraits};
38use crate::dom::shadowroot::ShadowRoot;
39use crate::dom::virtualmethods::vtable_for;
40use crate::dom::window::Window;
41use crate::script_runtime::CanGc;
42use crate::task::TaskOnce;
43
44/// <https://dom.spec.whatwg.org/#concept-event>
45#[dom_struct]
46pub(crate) struct Event {
47    reflector_: Reflector,
48
49    /// <https://dom.spec.whatwg.org/#dom-event-currenttarget>
50    current_target: MutNullableDom<EventTarget>,
51
52    /// <https://dom.spec.whatwg.org/#event-target>
53    target: MutNullableDom<EventTarget>,
54
55    /// <https://dom.spec.whatwg.org/#dom-event-type>
56    #[no_trace]
57    type_: DomRefCell<Atom>,
58
59    /// <https://dom.spec.whatwg.org/#dom-event-eventphase>
60    phase: Cell<EventPhase>,
61
62    /// <https://dom.spec.whatwg.org/#canceled-flag>
63    canceled: Cell<EventDefault>,
64
65    /// <https://dom.spec.whatwg.org/#stop-propagation-flag>
66    stop_propagation: Cell<bool>,
67
68    /// <https://dom.spec.whatwg.org/#stop-immediate-propagation-flag>
69    stop_immediate_propagation: Cell<bool>,
70
71    /// <https://dom.spec.whatwg.org/#dom-event-cancelable>
72    cancelable: Cell<bool>,
73
74    /// <https://dom.spec.whatwg.org/#dom-event-bubbles>
75    bubbles: Cell<bool>,
76
77    /// <https://dom.spec.whatwg.org/#dom-event-composed>
78    composed: Cell<bool>,
79
80    /// <https://dom.spec.whatwg.org/#dom-event-istrusted>
81    is_trusted: Cell<bool>,
82
83    /// <https://dom.spec.whatwg.org/#dispatch-flag>
84    dispatch: Cell<bool>,
85
86    /// <https://dom.spec.whatwg.org/#initialized-flag>
87    initialized: Cell<bool>,
88
89    /// <https://dom.spec.whatwg.org/#dom-event-timestamp>
90    #[no_trace]
91    time_stamp: CrossProcessInstant,
92
93    /// <https://dom.spec.whatwg.org/#event-path>
94    path: DomRefCell<Vec<EventPathSegment>>,
95
96    /// <https://dom.spec.whatwg.org/#event-relatedtarget>
97    related_target: MutNullableDom<EventTarget>,
98
99    /// <https://dom.spec.whatwg.org/#in-passive-listener-flag>
100    in_passive_listener: Cell<bool>,
101}
102
103/// An element on an [event path](https://dom.spec.whatwg.org/#event-path)
104#[derive(JSTraceable, MallocSizeOf)]
105#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
106pub(crate) struct EventPathSegment {
107    /// <https://dom.spec.whatwg.org/#event-path-invocation-target>
108    invocation_target: Dom<EventTarget>,
109
110    /// <https://dom.spec.whatwg.org/#event-path-invocation-target-in-shadow-tree>
111    invocation_target_in_shadow_tree: bool,
112
113    /// <https://dom.spec.whatwg.org/#event-path-shadow-adjusted-target>
114    shadow_adjusted_target: Option<Dom<EventTarget>>,
115
116    /// <https://dom.spec.whatwg.org/#event-path-relatedtarget>
117    related_target: Option<Dom<EventTarget>>,
118
119    /// <https://dom.spec.whatwg.org/#event-path-root-of-closed-tree>
120    root_of_closed_tree: bool,
121
122    /// <https://dom.spec.whatwg.org/#event-path-slot-in-closed-tree>
123    slot_in_closed_tree: bool,
124}
125
126impl Event {
127    pub(crate) fn new_inherited() -> Event {
128        Event {
129            reflector_: Reflector::new(),
130            current_target: Default::default(),
131            target: Default::default(),
132            type_: DomRefCell::new(atom!("")),
133            phase: Cell::new(EventPhase::None),
134            canceled: Cell::new(EventDefault::Allowed),
135            stop_propagation: Cell::new(false),
136            stop_immediate_propagation: Cell::new(false),
137            cancelable: Cell::new(false),
138            bubbles: Cell::new(false),
139            composed: Cell::new(false),
140            is_trusted: Cell::new(false),
141            dispatch: Cell::new(false),
142            initialized: Cell::new(false),
143            time_stamp: CrossProcessInstant::now(),
144            path: DomRefCell::default(),
145            related_target: Default::default(),
146            in_passive_listener: Cell::new(false),
147        }
148    }
149
150    pub(crate) fn new_uninitialized(global: &GlobalScope, can_gc: CanGc) -> DomRoot<Event> {
151        Self::new_uninitialized_with_proto(global, None, can_gc)
152    }
153
154    pub(crate) fn new_uninitialized_with_proto(
155        global: &GlobalScope,
156        proto: Option<HandleObject>,
157        can_gc: CanGc,
158    ) -> DomRoot<Event> {
159        reflect_dom_object_with_proto(Box::new(Event::new_inherited()), global, proto, can_gc)
160    }
161
162    pub(crate) fn new(
163        global: &GlobalScope,
164        type_: Atom,
165        bubbles: EventBubbles,
166        cancelable: EventCancelable,
167        can_gc: CanGc,
168    ) -> DomRoot<Event> {
169        Self::new_with_proto(global, None, type_, bubbles, cancelable, can_gc)
170    }
171
172    fn new_with_proto(
173        global: &GlobalScope,
174        proto: Option<HandleObject>,
175        type_: Atom,
176        bubbles: EventBubbles,
177        cancelable: EventCancelable,
178        can_gc: CanGc,
179    ) -> DomRoot<Event> {
180        let event = Event::new_uninitialized_with_proto(global, proto, can_gc);
181
182        // NOTE: The spec doesn't tell us to call init event here, it just happens to do what we need.
183        event.init_event(type_, bool::from(bubbles), bool::from(cancelable));
184        event
185    }
186
187    /// <https://dom.spec.whatwg.org/#dom-event-initevent>
188    /// and <https://dom.spec.whatwg.org/#concept-event-initialize>
189    pub(crate) fn init_event(&self, type_: Atom, bubbles: bool, cancelable: bool) {
190        // https://dom.spec.whatwg.org/#dom-event-initevent
191        if self.dispatch.get() {
192            return;
193        }
194
195        // https://dom.spec.whatwg.org/#concept-event-initialize
196        // Step 1. Set event’s initialized flag.
197        self.initialized.set(true);
198
199        // Step 2. Unset event’s stop propagation flag, stop immediate propagation flag, and canceled flag.
200        self.stop_propagation.set(false);
201        self.stop_immediate_propagation.set(false);
202        self.canceled.set(EventDefault::Allowed);
203
204        // Step 3. Set event’s isTrusted attribute to false.
205        self.is_trusted.set(false);
206
207        // Step 4. Set event’s target to null.
208        self.target.set(None);
209
210        // Step 5. Set event’s type attribute to type.
211        *self.type_.borrow_mut() = type_;
212
213        // Step 6. Set event’s bubbles attribute to bubbles.
214        self.bubbles.set(bubbles);
215
216        // Step 7. Set event’s cancelable attribute to cancelable.
217        self.cancelable.set(cancelable);
218    }
219
220    pub(crate) fn set_target(&self, target_: Option<&EventTarget>) {
221        self.target.set(target_);
222    }
223
224    pub(crate) fn set_in_passive_listener(&self, value: bool) {
225        self.in_passive_listener.set(value);
226    }
227
228    /// <https://dom.spec.whatwg.org/#concept-event-path-append>
229    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
230    pub(crate) fn append_to_path(
231        &self,
232        invocation_target: &EventTarget,
233        shadow_adjusted_target: Option<&EventTarget>,
234        related_target: Option<&EventTarget>,
235        slot_in_closed_tree: bool,
236    ) {
237        // Step 1. Let invocationTargetInShadowTree be false.
238        let mut invocation_target_in_shadow_tree = false;
239
240        // Step 2. If invocationTarget is a node and its root is a shadow root,
241        // then set invocationTargetInShadowTree to true.
242        if invocation_target
243            .downcast::<Node>()
244            .is_some_and(Node::is_in_a_shadow_tree)
245        {
246            invocation_target_in_shadow_tree = true;
247        }
248
249        // Step 3. Let root-of-closed-tree be false.
250        let mut root_of_closed_tree = false;
251
252        // Step 4. If invocationTarget is a shadow root whose mode is "closed", then set root-of-closed-tree to true.
253        if invocation_target
254            .downcast::<ShadowRoot>()
255            .is_some_and(|shadow_root| shadow_root.Mode() == ShadowRootMode::Closed)
256        {
257            root_of_closed_tree = true;
258        }
259
260        // Step 5. Append a new struct to event’s path whose invocation target is invocationTarget,
261        // invocation-target-in-shadow-tree is invocationTargetInShadowTree, shadow-adjusted target is
262        // shadowAdjustedTarget, relatedTarget is relatedTarget, touch target list is touchTargets,
263        // root-of-closed-tree is root-of-closed-tree, and slot-in-closed-tree is slot-in-closed-tree.
264        let event_path_segment = EventPathSegment {
265            invocation_target: Dom::from_ref(invocation_target),
266            shadow_adjusted_target: shadow_adjusted_target.map(Dom::from_ref),
267            related_target: related_target.map(Dom::from_ref),
268            invocation_target_in_shadow_tree,
269            root_of_closed_tree,
270            slot_in_closed_tree,
271        };
272        self.path.borrow_mut().push(event_path_segment);
273    }
274
275    /// <https://dom.spec.whatwg.org/#concept-event-dispatch>
276    pub(crate) fn dispatch(
277        &self,
278        target: &EventTarget,
279        legacy_target_override: bool,
280        can_gc: CanGc,
281        // TODO legacy_did_output_listeners_throw_flag for indexeddb
282    ) -> bool {
283        let mut target = DomRoot::from_ref(target);
284
285        // Step 1. Set event’s dispatch flag.
286        self.dispatch.set(true);
287
288        // Step 2. Let targetOverride be target, if legacy target override flag is not given,
289        // and target’s associated Document otherwise.
290        let target_override_document; // upcasted EventTarget's lifetime depends on this
291        let target_override = if legacy_target_override {
292            target_override_document = target
293                .downcast::<Window>()
294                .expect("legacy_target_override must be true only when target is a Window")
295                .Document();
296            DomRoot::from_ref(target_override_document.upcast::<EventTarget>())
297        } else {
298            target.clone()
299        };
300
301        // Step 3. Let activationTarget be null.
302        let mut activation_target = None;
303
304        // Step 4. Let relatedTarget be the result of retargeting event’s relatedTarget against target.
305        let related_target = self
306            .related_target
307            .get()
308            .map(|related_target| related_target.retarget(&target));
309
310        // Step 5. If target is not relatedTarget or target is event’s relatedTarget:
311        // Variables declared by the spec inside Step 5 but used later:
312        // TODO: https://github.com/whatwg/dom/issues/1344
313        let mut clear_targets = false;
314        let mut pre_activation_result: Option<InputActivationState> = None;
315        if related_target.as_ref() != Some(&target) ||
316            self.related_target.get().as_ref() == Some(&target)
317        {
318            // TODO Step 5.1 Let touchTargets be a new list.
319            // TODO Step 5.2 For each touchTarget of event’s touch target list, append the result of retargeting
320            // touchTarget against target to touchTargets.
321
322            // Step 5.3 Append to an event path with event, target, targetOverride, relatedTarget,
323            // touchTargets, and false.
324            self.append_to_path(
325                &target,
326                Some(target_override.upcast::<EventTarget>()),
327                related_target.as_deref(),
328                false,
329            );
330
331            // Step 5.4 Let isActivationEvent be true, if event is a MouseEvent object and
332            // event’s type attribute is "click"; otherwise false.
333            let is_activation_event = self.is::<MouseEvent>() && self.type_() == atom!("click");
334
335            // Step 5.5 If isActivationEvent is true and target has activation behavior,
336            // then set activationTarget to target.
337            if is_activation_event {
338                if let Some(element) = target.downcast::<Element>() {
339                    if element.as_maybe_activatable().is_some() {
340                        activation_target = Some(DomRoot::from_ref(element));
341                    }
342                }
343            }
344
345            // Step 5.6 Let slottable be target, if target is a slottable and is assigned, and null otherwise.
346            let mut slottable = if target
347                .downcast::<Node>()
348                .and_then(Node::assigned_slot)
349                .is_some()
350            {
351                Some(target.clone())
352            } else {
353                None
354            };
355
356            // Step 5.7 Let slot-in-closed-tree be false
357            let mut slot_in_closed_tree = false;
358
359            // Step 5.8 Let parent be the result of invoking target’s get the parent with event.
360            let mut parent_or_none = target.get_the_parent(self);
361            let mut done = false;
362
363            // Step 5.9 While parent is non-null:
364            while let Some(parent) = parent_or_none.clone() {
365                // Step 5.9.1 If slottable is non-null:
366                if slottable.is_some() {
367                    // Step 5.9.1.1 Assert: parent is a slot.
368                    let slot = parent
369                        .downcast::<HTMLSlotElement>()
370                        .expect("parent of slottable is not a slot");
371
372                    // Step 5.9.1.2 Set slottable to null.
373                    slottable = None;
374
375                    // Step 5.9.1.3 If parent’s root is a shadow root whose mode is "closed",
376                    // then set slot-in-closed-tree to true.
377                    if slot
378                        .containing_shadow_root()
379                        .is_some_and(|root| root.Mode() == ShadowRootMode::Closed)
380                    {
381                        slot_in_closed_tree = true;
382                    }
383                }
384
385                // Step 5.9.2 If parent is a slottable and is assigned, then set slottable to parent.
386                if parent
387                    .downcast::<Node>()
388                    .and_then(Node::assigned_slot)
389                    .is_some()
390                {
391                    slottable = Some(parent.clone());
392                }
393
394                // Step 5.9.3 Let relatedTarget be the result of retargeting event’s relatedTarget against parent.
395                let related_target = self
396                    .related_target
397                    .get()
398                    .map(|related_target| related_target.retarget(&target));
399
400                // TODO: Step 5.9.4 Let touchTargets be a new list.
401                // Step 5.9.5 For each touchTarget of event’s touch target list, append the result of retargeting
402                // touchTarget against parent to touchTargets.
403
404                // Step 5.9.6 If parent is a Window object, or parent is a node and target’s root is a
405                // shadow-including inclusive ancestor of parent:
406                let root_is_shadow_inclusive_ancestor = parent
407                    .downcast::<Node>()
408                    .zip(target.downcast::<Node>())
409                    .is_some_and(|(parent, target)| {
410                        target
411                            .GetRootNode(&GetRootNodeOptions::empty())
412                            .is_shadow_including_inclusive_ancestor_of(parent)
413                    });
414                if parent.is::<Window>() || root_is_shadow_inclusive_ancestor {
415                    // Step 5.9.6.1 If isActivationEvent is true, event’s bubbles attribute is true, activationTarget
416                    // is null, and parent has activation behavior, then set activationTarget to parent.
417                    if is_activation_event && activation_target.is_none() && self.bubbles.get() {
418                        if let Some(element) = parent.downcast::<Element>() {
419                            if element.as_maybe_activatable().is_some() {
420                                activation_target = Some(DomRoot::from_ref(element));
421                            }
422                        }
423                    }
424
425                    // Step 5.9.6.2 Append to an event path with event, parent, null, relatedTarget, touchTargets,
426                    // and slot-in-closed-tree.
427                    self.append_to_path(
428                        &parent,
429                        None,
430                        related_target.as_deref(),
431                        slot_in_closed_tree,
432                    );
433                }
434                // Step 5.9.7 Otherwise, if parent is relatedTarget, then set parent to null.
435                else if Some(&parent) == related_target.as_ref() {
436                    // NOTE: This causes some lifetime shenanigans. Instead of making things complicated,
437                    // we just remember to treat parent as null later
438                    done = true;
439                }
440                // Step 5.9.8 Otherwise:
441                else {
442                    // Step 5.9.8.1 Set target to parent.
443                    target = parent.clone();
444
445                    // Step 5.9.8.2 If isActivationEvent is true, activationTarget is null, and target has
446                    // activation behavior, then set activationTarget to target.
447                    if is_activation_event && activation_target.is_none() {
448                        if let Some(element) = parent.downcast::<Element>() {
449                            if element.as_maybe_activatable().is_some() {
450                                activation_target = Some(DomRoot::from_ref(element));
451                            }
452                        }
453                    }
454
455                    // Step 5.9.8.3 Append to an event path with event, parent, target, relatedTarget,
456                    // touchTargets, and slot-in-closed-tree.
457                    self.append_to_path(
458                        &parent,
459                        Some(&target),
460                        related_target.as_deref(),
461                        slot_in_closed_tree,
462                    );
463                }
464
465                // Step 5.9.9 If parent is non-null, then set parent to the result of invoking parent’s
466                // get the parent with event
467                if !done {
468                    parent_or_none = parent.get_the_parent(self);
469                }
470
471                // Step 5.9.10 Set slot-in-closed-tree to false.
472                slot_in_closed_tree = false;
473            }
474
475            // Step 5.10 Let clearTargetsStruct be the last struct in event’s path whose shadow-adjusted target
476            // is non-null.
477            // Step 5.11 Let clearTargets be true if clearTargetsStruct’s shadow-adjusted target,
478            // clearTargetsStruct’s relatedTarget, or an EventTarget object in clearTargetsStruct’s
479            // touch target list is a node and its root is a shadow root; otherwise false.
480            // TODO: Handle touch target list
481            clear_targets = self
482                .path
483                .borrow()
484                .iter()
485                .rev()
486                .find(|segment| segment.shadow_adjusted_target.is_some())
487                // This is "clearTargetsStruct"
488                .is_some_and(|clear_targets| {
489                    clear_targets
490                        .shadow_adjusted_target
491                        .as_ref()
492                        .and_then(|target| target.downcast::<Node>())
493                        .is_some_and(Node::is_in_a_shadow_tree) ||
494                        clear_targets
495                            .related_target
496                            .as_ref()
497                            .and_then(|target| target.downcast::<Node>())
498                            .is_some_and(Node::is_in_a_shadow_tree)
499                });
500
501            // Step 5.12 If activationTarget is non-null and activationTarget has legacy-pre-activation behavior,
502            // then run activationTarget’s legacy-pre-activation behavior.
503            if let Some(activation_target) = activation_target.as_ref() {
504                // Not specified in dispatch spec overtly; this is because
505                // the legacy canceled activation behavior of a checkbox
506                // or radio button needs to know what happened in the
507                // corresponding pre-activation behavior.
508                pre_activation_result = activation_target
509                    .as_maybe_activatable()
510                    .and_then(|activatable| activatable.legacy_pre_activation_behavior(can_gc));
511            }
512
513            let timeline_window = DomRoot::downcast::<Window>(target.global())
514                .filter(|window| window.need_emit_timeline_marker(TimelineMarkerType::DOMEvent));
515
516            // Step 5.13 For each struct in event’s path, in reverse order:
517            for (index, segment) in self.path.borrow().iter().enumerate().rev() {
518                // Step 5.13.1 If struct’s shadow-adjusted target is non-null, then set event’s
519                // eventPhase attribute to AT_TARGET.
520                if segment.shadow_adjusted_target.is_some() {
521                    self.phase.set(EventPhase::AtTarget);
522                }
523                // Step 5.13.2 Otherwise, set event’s eventPhase attribute to CAPTURING_PHASE.
524                else {
525                    self.phase.set(EventPhase::Capturing);
526                }
527
528                // Step 5.13.3 Invoke with struct, event, "capturing", and legacyOutputDidListenersThrowFlag if given.
529                invoke(
530                    segment,
531                    index,
532                    self,
533                    ListenerPhase::Capturing,
534                    timeline_window.as_deref(),
535                    can_gc,
536                )
537            }
538
539            // Step 5.14 For each struct in event’s path:
540            for (index, segment) in self.path.borrow().iter().enumerate() {
541                // Step 5.14.1 If struct’s shadow-adjusted target is non-null, then set event’s
542                // eventPhase attribute to AT_TARGET.
543                if segment.shadow_adjusted_target.is_some() {
544                    self.phase.set(EventPhase::AtTarget);
545                }
546                // Step 5.14.2 Otherwise:
547                else {
548                    // Step 5.14.2.1 If event’s bubbles attribute is false, then continue.
549                    if !self.bubbles.get() {
550                        continue;
551                    }
552
553                    // Step 5.14.2.2 Set event’s eventPhase attribute to BUBBLING_PHASE.
554                    self.phase.set(EventPhase::Bubbling);
555                }
556
557                // Step 5.14.3 Invoke with struct, event, "bubbling", and legacyOutputDidListenersThrowFlag if given.
558                invoke(
559                    segment,
560                    index,
561                    self,
562                    ListenerPhase::Bubbling,
563                    timeline_window.as_deref(),
564                    can_gc,
565                );
566            }
567        }
568
569        // Step 6. Set event’s eventPhase attribute to NONE.
570        self.phase.set(EventPhase::None);
571
572        // FIXME: The UIEvents spec still expects firing an event
573        // to carry a "default action" semantic, but the HTML spec
574        // has removed this concept. Nothing in either spec currently
575        // (as of Jan 11 2020) says that, e.g., a keydown event on an
576        // input element causes a character to be typed; the UIEvents
577        // spec assumes the HTML spec is covering it, and the HTML spec
578        // no longer specifies any UI event other than mouse click as
579        // causing an element to perform an action.
580        // Compare:
581        // https://w3c.github.io/uievents/#default-action
582        // https://dom.spec.whatwg.org/#action-versus-occurance
583        if !self.DefaultPrevented() {
584            if let Some(target) = self.GetTarget() {
585                if let Some(node) = target.downcast::<Node>() {
586                    let vtable = vtable_for(node);
587                    vtable.handle_event(self, can_gc);
588                }
589            }
590        }
591
592        // Step 7. Set event’s currentTarget attribute to null.
593        self.current_target.set(None);
594
595        // Step 8. Set event’s path to the empty list.
596        self.path.borrow_mut().clear();
597
598        // Step 9. Unset event’s dispatch flag, stop propagation flag, and stop immediate propagation flag.
599        self.dispatch.set(false);
600        self.stop_propagation.set(false);
601        self.stop_immediate_propagation.set(false);
602
603        // Step 10. If clearTargets is true:
604        if clear_targets {
605            // Step 10.1 Set event’s target to null.
606            self.target.set(None);
607
608            // Step 10.2 Set event’s relatedTarget to null.
609            self.related_target.set(None);
610
611            // TODO Step 10.3 Set event’s touch target list to the empty list.
612        }
613
614        // Step 11. If activationTarget is non-null:
615        if let Some(activation_target) = activation_target {
616            // NOTE: The activation target may have been disabled by an event handler
617            if let Some(activatable) = activation_target.as_maybe_activatable() {
618                // Step 11.1 If event’s canceled flag is unset, then run activationTarget’s
619                // activation behavior with event.
620                if !self.DefaultPrevented() {
621                    activatable.activation_behavior(self, &target, can_gc);
622                }
623                // Step 11.2 Otherwise, if activationTarget has legacy-canceled-activation behavior, then run
624                // activationTarget’s legacy-canceled-activation behavior.
625                else {
626                    activatable.legacy_canceled_activation_behavior(pre_activation_result, can_gc);
627                }
628            }
629        }
630
631        // Step 12 Return false if event’s canceled flag is set; otherwise true.
632        !self.DefaultPrevented()
633    }
634
635    #[inline]
636    pub(crate) fn dispatching(&self) -> bool {
637        self.dispatch.get()
638    }
639
640    #[inline]
641    pub(crate) fn initialized(&self) -> bool {
642        self.initialized.get()
643    }
644
645    #[inline]
646    pub(crate) fn type_(&self) -> Atom {
647        self.type_.borrow().clone()
648    }
649
650    #[inline]
651    pub(crate) fn mark_as_handled(&self) {
652        self.canceled.set(EventDefault::Handled);
653    }
654
655    #[inline]
656    pub(crate) fn get_cancel_state(&self) -> EventDefault {
657        self.canceled.get()
658    }
659
660    pub(crate) fn set_trusted(&self, trusted: bool) {
661        self.is_trusted.set(trusted);
662    }
663
664    pub(crate) fn set_composed(&self, composed: bool) {
665        self.composed.set(composed);
666    }
667
668    /// <https://dom.spec.whatwg.org/#firing-events>
669    pub(crate) fn fire(&self, target: &EventTarget, can_gc: CanGc) -> bool {
670        self.set_trusted(true);
671        target.dispatch_event(self, can_gc)
672    }
673
674    /// <https://dom.spec.whatwg.org/#inner-event-creation-steps>
675    fn inner_creation_steps(
676        global: &GlobalScope,
677        proto: Option<HandleObject>,
678        init: &EventBinding::EventInit,
679        can_gc: CanGc,
680    ) -> DomRoot<Event> {
681        // Step 1. Let event be the result of creating a new object using eventInterface.
682        // If realm is non-null, then use that realm; otherwise, use the default behavior defined in Web IDL.
683        let event = Event::new_uninitialized_with_proto(global, proto, can_gc);
684
685        // Step 2. Set event’s initialized flag.
686        event.initialized.set(true);
687
688        // Step 3. Initialize event’s timeStamp attribute to the relative high resolution
689        // coarse time given time and event’s relevant global object.
690        // NOTE: This is done inside Event::new_inherited
691
692        // Step 3. For each member → value in dictionary, if event has an attribute whose
693        // identifier is member, then initialize that attribute to value.#
694        event.bubbles.set(init.bubbles);
695        event.cancelable.set(init.cancelable);
696        event.composed.set(init.composed);
697
698        // Step 5. Run the event constructing steps with event and dictionary.
699        // NOTE: Event construction steps may be defined by subclasses
700
701        // Step 6. Return event.
702        event
703    }
704
705    /// Implements the logic behind the [get the parent](https://dom.spec.whatwg.org/#get-the-parent)
706    /// algorithm for shadow roots.
707    pub(crate) fn should_pass_shadow_boundary(&self, shadow_root: &ShadowRoot) -> bool {
708        debug_assert!(self.dispatching());
709
710        // > A shadow root’s get the parent algorithm, given an event, returns null if event’s composed flag
711        // > is unset and shadow root is the root of event’s path’s first struct’s invocation target;
712        // > otherwise shadow root’s host.
713        if self.Composed() {
714            return true;
715        }
716
717        let path = self.path.borrow();
718        let first_invocation_target = &path
719            .first()
720            .expect("Event path is empty despite event currently being dispatched")
721            .invocation_target
722            .as_rooted();
723
724        // The spec doesn't tell us what should happen if the invocation target is not a node
725        let Some(target_node) = first_invocation_target.downcast::<Node>() else {
726            return false;
727        };
728
729        &*target_node.GetRootNode(&GetRootNodeOptions::empty()) != shadow_root.upcast::<Node>()
730    }
731
732    /// <https://dom.spec.whatwg.org/#set-the-canceled-flag>
733    fn set_the_cancelled_flag(&self) {
734        if self.cancelable.get() && !self.in_passive_listener.get() {
735            self.canceled.set(EventDefault::Prevented)
736        }
737    }
738}
739
740impl EventMethods<crate::DomTypeHolder> for Event {
741    /// <https://dom.spec.whatwg.org/#concept-event-constructor>
742    fn Constructor(
743        global: &GlobalScope,
744        proto: Option<HandleObject>,
745        can_gc: CanGc,
746        type_: DOMString,
747        init: &EventBinding::EventInit,
748    ) -> Fallible<DomRoot<Event>> {
749        // Step 1. Let event be the result of running the inner event creation steps with
750        // this interface, null, now, and eventInitDict.
751        let event = Event::inner_creation_steps(global, proto, init, can_gc);
752
753        // Step 2. Initialize event’s type attribute to type.
754        *event.type_.borrow_mut() = Atom::from(type_);
755
756        // Step 3. Return event.
757        Ok(event)
758    }
759
760    /// <https://dom.spec.whatwg.org/#dom-event-eventphase>
761    fn EventPhase(&self) -> u16 {
762        self.phase.get() as u16
763    }
764
765    /// <https://dom.spec.whatwg.org/#dom-event-type>
766    fn Type(&self) -> DOMString {
767        DOMString::from(&*self.type_()) // FIXME(ajeffrey): Directly convert from Atom to DOMString
768    }
769
770    /// <https://dom.spec.whatwg.org/#dom-event-target>
771    fn GetTarget(&self) -> Option<DomRoot<EventTarget>> {
772        self.target.get()
773    }
774
775    /// <https://dom.spec.whatwg.org/#dom-event-srcelement>
776    fn GetSrcElement(&self) -> Option<DomRoot<EventTarget>> {
777        self.target.get()
778    }
779
780    /// <https://dom.spec.whatwg.org/#dom-event-currenttarget>
781    fn GetCurrentTarget(&self) -> Option<DomRoot<EventTarget>> {
782        self.current_target.get()
783    }
784
785    /// <https://dom.spec.whatwg.org/#dom-event-composedpath>
786    fn ComposedPath(&self) -> Vec<DomRoot<EventTarget>> {
787        // Step 1. Let composedPath be an empty list.
788        let mut composed_path = vec![];
789
790        // Step 2. Let path be this’s path.
791        let path = self.path.borrow();
792
793        // Step 3. If path is empty, then return composedPath.
794        if path.is_empty() {
795            return composed_path;
796        }
797
798        // Step 4. Let currentTarget be this’s currentTarget attribute value.
799        let current_target = self.GetCurrentTarget();
800
801        // Step 5. Append currentTarget to composedPath.
802        // TODO: https://github.com/whatwg/dom/issues/1343
803        composed_path.push(current_target.clone().expect(
804            "Since the event's path is not empty it is being dispatched and must have a current target",
805        ));
806
807        // Step 6. Let currentTargetIndex be 0.
808        let mut current_target_index = 0;
809
810        // Step 7. Let currentTargetHiddenSubtreeLevel be 0.
811        let mut current_target_hidden_subtree_level = 0;
812
813        // Step 8. Let index be path’s size − 1.
814        // Step 9. While index is greater than or equal to 0:
815        // NOTE: This is just iterating the path in reverse
816        for (index, element) in path.iter().enumerate().rev() {
817            // Step 9.1 If path[index]'s root-of-closed-tree is true, then increase
818            // currentTargetHiddenSubtreeLevel by 1.
819            if element.root_of_closed_tree {
820                current_target_hidden_subtree_level += 1;
821            }
822
823            // Step 9.2 If path[index]'s invocation target is currentTarget, then set
824            // currentTargetIndex to index and break.
825            if current_target
826                .as_ref()
827                .is_some_and(|target| target.as_traced() == element.invocation_target)
828            {
829                current_target_index = index;
830                break;
831            }
832
833            // Step 9.3 If path[index]'s slot-in-closed-tree is true, then decrease
834            // currentTargetHiddenSubtreeLevel by 1.
835            if element.slot_in_closed_tree {
836                current_target_hidden_subtree_level -= 1;
837            }
838
839            // Step 9.4 Decrease index by 1.
840        }
841
842        // Step 10. Let currentHiddenLevel and maxHiddenLevel be currentTargetHiddenSubtreeLevel.
843        let mut current_hidden_level = current_target_hidden_subtree_level;
844        let mut max_hidden_level = current_target_hidden_subtree_level;
845
846        // Step 11. Set index to currentTargetIndex − 1.
847        // Step 12. While index is greater than or equal to 0:
848        // NOTE: This is just iterating part of the path in reverse
849        for element in path.iter().take(current_target_index).rev() {
850            // Step 12.1 If path[index]'s root-of-closed-tree is true, then increase currentHiddenLevel by 1.
851            if element.root_of_closed_tree {
852                current_hidden_level += 1;
853            }
854
855            // Step 12.2 If currentHiddenLevel is less than or equal to maxHiddenLevel,
856            // then prepend path[index]'s invocation target to composedPath.
857            if current_hidden_level <= max_hidden_level {
858                composed_path.insert(0, element.invocation_target.as_rooted());
859            }
860
861            // Step 12.3 If path[index]'s slot-in-closed-tree is true:
862            if element.slot_in_closed_tree {
863                // Step 12.3.1 Decrease currentHiddenLevel by 1.
864                current_hidden_level -= 1;
865
866                // Step 12.3.2 If currentHiddenLevel is less than maxHiddenLevel, then set
867                // maxHiddenLevel to currentHiddenLevel.
868                if current_hidden_level < max_hidden_level {
869                    max_hidden_level = current_hidden_level;
870                }
871            }
872
873            // Step 12.4 Decrease index by 1.
874        }
875
876        // Step 13. Set currentHiddenLevel and maxHiddenLevel to currentTargetHiddenSubtreeLevel.
877        current_hidden_level = current_target_hidden_subtree_level;
878        max_hidden_level = current_target_hidden_subtree_level;
879
880        // Step 14. Set index to currentTargetIndex + 1.
881        // Step 15. While index is less than path’s size:
882        // NOTE: This is just iterating the list and skipping the first current_target_index + 1 elements
883        //       (The +1 is necessary because the index is 0-based and the skip method is not)
884        for element in path.iter().skip(current_target_index + 1) {
885            // Step 15.1 If path[index]'s slot-in-closed-tree is true, then increase currentHiddenLevel by 1.
886            if element.slot_in_closed_tree {
887                current_hidden_level += 1;
888            }
889
890            // Step 15.2 If currentHiddenLevel is less than or equal to maxHiddenLevel,
891            // then append path[index]'s invocation target to composedPath.
892            if current_hidden_level <= max_hidden_level {
893                composed_path.push(element.invocation_target.as_rooted());
894            }
895
896            // Step 15.3 If path[index]'s root-of-closed-tree is true:
897            if element.root_of_closed_tree {
898                // Step 15.3.1 Decrease currentHiddenLevel by 1.
899                current_hidden_level -= 1;
900
901                // Step 15.3.2 If currentHiddenLevel is less than maxHiddenLevel, then set
902                // maxHiddenLevel to currentHiddenLevel.
903                if current_hidden_level < max_hidden_level {
904                    max_hidden_level = current_hidden_level;
905                }
906            }
907
908            // Step 15.4 Increase index by 1.
909        }
910
911        // Step 16. Return composedPath.
912        composed_path
913    }
914
915    /// <https://dom.spec.whatwg.org/#dom-event-defaultprevented>
916    fn DefaultPrevented(&self) -> bool {
917        self.canceled.get() == EventDefault::Prevented
918    }
919
920    /// <https://dom.spec.whatwg.org/#dom-event-composed>
921    fn Composed(&self) -> bool {
922        self.composed.get()
923    }
924
925    /// <https://dom.spec.whatwg.org/#dom-event-preventdefault>
926    fn PreventDefault(&self) {
927        self.set_the_cancelled_flag();
928    }
929
930    /// <https://dom.spec.whatwg.org/#dom-event-stoppropagation>
931    fn StopPropagation(&self) {
932        self.stop_propagation.set(true);
933    }
934
935    /// <https://dom.spec.whatwg.org/#dom-event-stopimmediatepropagation>
936    fn StopImmediatePropagation(&self) {
937        self.stop_immediate_propagation.set(true);
938        self.stop_propagation.set(true);
939    }
940
941    /// <https://dom.spec.whatwg.org/#dom-event-bubbles>
942    fn Bubbles(&self) -> bool {
943        self.bubbles.get()
944    }
945
946    /// <https://dom.spec.whatwg.org/#dom-event-cancelable>
947    fn Cancelable(&self) -> bool {
948        self.cancelable.get()
949    }
950
951    /// <https://dom.spec.whatwg.org/#dom-event-returnvalue>
952    fn ReturnValue(&self) -> bool {
953        self.canceled.get() == EventDefault::Allowed
954    }
955
956    /// <https://dom.spec.whatwg.org/#dom-event-returnvalue>
957    fn SetReturnValue(&self, val: bool) {
958        if !val {
959            self.set_the_cancelled_flag();
960        }
961    }
962
963    /// <https://dom.spec.whatwg.org/#dom-event-cancelbubble>
964    fn CancelBubble(&self) -> bool {
965        self.stop_propagation.get()
966    }
967
968    /// <https://dom.spec.whatwg.org/#dom-event-cancelbubble>
969    fn SetCancelBubble(&self, value: bool) {
970        if value {
971            self.stop_propagation.set(true)
972        }
973    }
974
975    /// <https://dom.spec.whatwg.org/#dom-event-timestamp>
976    fn TimeStamp(&self) -> DOMHighResTimeStamp {
977        self.global()
978            .performance()
979            .to_dom_high_res_time_stamp(self.time_stamp)
980    }
981
982    /// <https://dom.spec.whatwg.org/#dom-event-initevent>
983    fn InitEvent(&self, type_: DOMString, bubbles: bool, cancelable: bool) {
984        self.init_event(Atom::from(type_), bubbles, cancelable)
985    }
986
987    /// <https://dom.spec.whatwg.org/#dom-event-istrusted>
988    fn IsTrusted(&self) -> bool {
989        self.is_trusted.get()
990    }
991}
992
993#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
994pub(crate) enum EventBubbles {
995    Bubbles,
996    DoesNotBubble,
997}
998
999impl From<bool> for EventBubbles {
1000    fn from(boolean: bool) -> Self {
1001        if boolean {
1002            EventBubbles::Bubbles
1003        } else {
1004            EventBubbles::DoesNotBubble
1005        }
1006    }
1007}
1008
1009impl From<EventBubbles> for bool {
1010    fn from(bubbles: EventBubbles) -> Self {
1011        match bubbles {
1012            EventBubbles::Bubbles => true,
1013            EventBubbles::DoesNotBubble => false,
1014        }
1015    }
1016}
1017
1018#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
1019pub(crate) enum EventCancelable {
1020    Cancelable,
1021    NotCancelable,
1022}
1023
1024impl From<bool> for EventCancelable {
1025    fn from(boolean: bool) -> Self {
1026        if boolean {
1027            EventCancelable::Cancelable
1028        } else {
1029            EventCancelable::NotCancelable
1030        }
1031    }
1032}
1033
1034impl From<EventCancelable> for bool {
1035    fn from(cancelable: EventCancelable) -> Self {
1036        match cancelable {
1037            EventCancelable::Cancelable => true,
1038            EventCancelable::NotCancelable => false,
1039        }
1040    }
1041}
1042
1043#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
1044pub(crate) enum EventComposed {
1045    Composed,
1046    NotComposed,
1047}
1048
1049impl From<bool> for EventComposed {
1050    fn from(boolean: bool) -> Self {
1051        if boolean {
1052            EventComposed::Composed
1053        } else {
1054            EventComposed::NotComposed
1055        }
1056    }
1057}
1058
1059impl From<EventComposed> for bool {
1060    fn from(composed: EventComposed) -> Self {
1061        match composed {
1062            EventComposed::Composed => true,
1063            EventComposed::NotComposed => false,
1064        }
1065    }
1066}
1067
1068#[derive(Clone, Copy, Debug, Eq, JSTraceable, PartialEq)]
1069#[repr(u16)]
1070#[derive(MallocSizeOf)]
1071pub(crate) enum EventPhase {
1072    None = EventConstants::NONE,
1073    Capturing = EventConstants::CAPTURING_PHASE,
1074    AtTarget = EventConstants::AT_TARGET,
1075    Bubbling = EventConstants::BUBBLING_PHASE,
1076}
1077
1078/// An enum to indicate whether the default action of an event is allowed.
1079///
1080/// This should've been a bool. Instead, it's an enum, because, aside from the allowed/canceled
1081/// states, we also need something to stop the event from being handled again (without cancelling
1082/// the event entirely). For example, an Up/Down `KeyEvent` inside a `textarea` element will
1083/// trigger the cursor to go up/down if the text inside the element spans multiple lines. This enum
1084/// helps us to prevent such events from being [sent to the constellation][msg] where it will be
1085/// handled once again for page scrolling (which is definitely not what we'd want).
1086///
1087/// [msg]: https://doc.servo.org/compositing/enum.ConstellationMsg.html#variant.KeyEvent
1088///
1089#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
1090pub(crate) enum EventDefault {
1091    /// The default action of the event is allowed (constructor's default)
1092    Allowed,
1093    /// The default action has been prevented by calling `PreventDefault`
1094    Prevented,
1095    /// The event has been handled somewhere in the DOM, and it should be prevented from being
1096    /// re-handled elsewhere. This doesn't affect the judgement of `DefaultPrevented`
1097    Handled,
1098}
1099
1100/// <https://dom.spec.whatwg.org/#concept-event-fire>
1101pub(crate) struct EventTask {
1102    pub(crate) target: Trusted<EventTarget>,
1103    pub(crate) name: Atom,
1104    pub(crate) bubbles: EventBubbles,
1105    pub(crate) cancelable: EventCancelable,
1106}
1107
1108impl TaskOnce for EventTask {
1109    fn run_once(self) {
1110        let target = self.target.root();
1111        let bubbles = self.bubbles;
1112        let cancelable = self.cancelable;
1113        target.fire_event_with_params(
1114            self.name,
1115            bubbles,
1116            cancelable,
1117            EventComposed::NotComposed,
1118            CanGc::note(),
1119        );
1120    }
1121}
1122
1123/// <https://html.spec.whatwg.org/multipage/#fire-a-simple-event>
1124pub(crate) struct SimpleEventTask {
1125    pub(crate) target: Trusted<EventTarget>,
1126    pub(crate) name: Atom,
1127}
1128
1129impl TaskOnce for SimpleEventTask {
1130    fn run_once(self) {
1131        let target = self.target.root();
1132        target.fire_event(self.name, CanGc::note());
1133    }
1134}
1135
1136/// <https://dom.spec.whatwg.org/#concept-event-listener-invoke>
1137fn invoke(
1138    segment: &EventPathSegment,
1139    segment_index_in_path: usize,
1140    event: &Event,
1141    phase: ListenerPhase,
1142    timeline_window: Option<&Window>,
1143    can_gc: CanGc,
1144    // TODO legacy_output_did_listeners_throw for indexeddb
1145) {
1146    // Step 1. Set event’s target to the shadow-adjusted target of the last struct in event’s path,
1147    // that is either struct or preceding struct, whose shadow-adjusted target is non-null.
1148    event.target.set(
1149        event.path.borrow()[..segment_index_in_path + 1]
1150            .iter()
1151            .rev()
1152            .flat_map(|segment| segment.shadow_adjusted_target.clone())
1153            .next()
1154            .as_deref(),
1155    );
1156
1157    // Step 2. Set event’s relatedTarget to struct’s relatedTarget.
1158    event.related_target.set(segment.related_target.as_deref());
1159
1160    // TODO: Set event’s touch target list to struct’s touch target list.
1161
1162    // Step 4. If event’s stop propagation flag is set, then return.
1163    if event.stop_propagation.get() {
1164        return;
1165    }
1166
1167    // Step 5. Initialize event’s currentTarget attribute to struct’s invocation target.
1168    event.current_target.set(Some(&segment.invocation_target));
1169
1170    // Step 6. Let listeners be a clone of event’s currentTarget attribute value’s event listener list.
1171    let listeners = segment.invocation_target.get_listeners_for(&event.type_());
1172
1173    // Step 7. Let invocationTargetInShadowTree be struct’s invocation-target-in-shadow-tree.
1174    let invocation_target_in_shadow_tree = segment.invocation_target_in_shadow_tree;
1175
1176    // Step 8. Let found be the result of running inner invoke with event, listeners, phase,
1177    // invocationTargetInShadowTree, and legacyOutputDidListenersThrowFlag if given.
1178    let found = inner_invoke(
1179        event,
1180        &listeners,
1181        phase,
1182        invocation_target_in_shadow_tree,
1183        timeline_window,
1184        can_gc,
1185    );
1186
1187    // Step 9. If found is false and event’s isTrusted attribute is true:
1188    if !found && event.is_trusted.get() {
1189        // Step 9.1 Let originalEventType be event’s type attribute value.
1190        let original_type = event.type_();
1191
1192        // Step 9.2 If event’s type attribute value is a match for any of the strings in the first column
1193        // in the following table, set event’s type attribute value to the string in the second column on
1194        // the same row as the matching string, and return otherwise.
1195        let legacy_type = match event.type_() {
1196            atom!("animationend") => atom!("webkitAnimationEnd"),
1197            atom!("animationiteration") => atom!("webkitAnimationIteration"),
1198            atom!("animationstart") => atom!("webkitAnimationStart"),
1199            atom!("transitionend") => atom!("webkitTransitionEnd"),
1200            atom!("transitionrun") => atom!("webkitTransitionRun"),
1201            _ => return,
1202        };
1203        *event.type_.borrow_mut() = legacy_type;
1204
1205        // Step 9.3 Inner invoke with event, listeners, phase, invocationTargetInShadowTree,
1206        // and legacyOutputDidListenersThrowFlag if given.
1207        inner_invoke(
1208            event,
1209            &listeners,
1210            phase,
1211            invocation_target_in_shadow_tree,
1212            timeline_window,
1213            can_gc,
1214        );
1215
1216        // Step 9.4 Set event’s type attribute value to originalEventType.
1217        *event.type_.borrow_mut() = original_type;
1218    }
1219}
1220
1221/// <https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke>
1222fn inner_invoke(
1223    event: &Event,
1224    listeners: &EventListeners,
1225    phase: ListenerPhase,
1226    invocation_target_in_shadow_tree: bool,
1227    timeline_window: Option<&Window>,
1228    can_gc: CanGc,
1229) -> bool {
1230    // Step 1. Let found be false.
1231    let mut found = false;
1232
1233    // Step 2. For each listener in listeners, whose removed is false:
1234    for listener in listeners.iter() {
1235        if listener.borrow().removed() {
1236            continue;
1237        }
1238
1239        // Step 2.1 If event’s type attribute value is not listener’s type, then continue.
1240
1241        // Step 2.2. Set found to true.
1242        found = true;
1243
1244        // Step 2.3 If phase is "capturing" and listener’s capture is false, then continue.
1245        // Step 2.4 If phase is "bubbling" and listener’s capture is true, then continue.
1246        if listener.borrow().phase() != phase {
1247            continue;
1248        }
1249
1250        let event_target = event
1251            .GetCurrentTarget()
1252            .expect("event target was initialized as part of \"invoke\"");
1253
1254        // Step 2.5 If listener’s once is true, then remove an event listener given event’s currentTarget
1255        // attribute value and listener.
1256        if listener.borrow().once() {
1257            event_target.remove_listener(&event.type_(), listener);
1258        }
1259
1260        let Some(compiled_listener) =
1261            listener
1262                .borrow()
1263                .get_compiled_listener(&event_target, &event.type_(), can_gc)
1264        else {
1265            continue;
1266        };
1267
1268        // Step 2.6 Let global be listener callback’s associated realm’s global object.
1269        let global = compiled_listener.associated_global();
1270
1271        // Step 2.7 Let currentEvent be undefined.
1272        let mut current_event = None;
1273        // Step 2.8 If global is a Window object:
1274        if let Some(window) = global.downcast::<Window>() {
1275            // Step 2.8.1 Set currentEvent to global’s current event.
1276            current_event = window.current_event();
1277
1278            // Step 2.8.2 If invocationTargetInShadowTree is false, then set global’s current event to event.
1279            if !invocation_target_in_shadow_tree {
1280                current_event = window.set_current_event(Some(event))
1281            }
1282        }
1283
1284        // Step 2.9 If listener’s passive is true, then set event's in passive listener flag.
1285        event.set_in_passive_listener(event_target.is_passive(&event.type_(), listener));
1286
1287        // Step 2.10 If global is a Window object, then record timing info for event listener
1288        // given event and listener.
1289        // Step 2.11 Call a user object’s operation with listener’s callback, "handleEvent", « event »,
1290        // and event’s currentTarget attribute value. If this throws an exception exception:
1291        //     Step 2.10.1 Report exception for listener’s callback’s corresponding JavaScript object’s
1292        //     associated realm’s global object.
1293        //     TODO Step 2.10.2 Set legacyOutputDidListenersThrowFlag if given.
1294        let marker = TimelineMarker::start("DOMEvent".to_owned());
1295        compiled_listener.call_or_handle_event(
1296            &event_target,
1297            event,
1298            ExceptionHandling::Report,
1299            can_gc,
1300        );
1301        if let Some(window) = timeline_window {
1302            window.emit_timeline_marker(marker.end());
1303        }
1304
1305        // Step 2.12 Unset event’s in passive listener flag.
1306        event.set_in_passive_listener(false);
1307
1308        // Step 2.13 If global is a Window object, then set global’s current event to currentEvent.
1309        if let Some(window) = global.downcast::<Window>() {
1310            window.set_current_event(current_event.as_deref());
1311        }
1312
1313        // Step 2.13: If event’s stop immediate propagation flag is set, then break.
1314        if event.stop_immediate_propagation.get() {
1315            break;
1316        }
1317    }
1318
1319    // Step 3.
1320    found
1321}