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