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