script/dom/
eventtarget.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::RefCell;
6use std::collections::hash_map::Entry::{Occupied, Vacant};
7use std::default::Default;
8use std::ffi::CString;
9use std::mem;
10use std::ops::{Deref, DerefMut};
11use std::rc::Rc;
12
13use deny_public_fields::DenyPublicFields;
14use dom_struct::dom_struct;
15use js::jsapi::JS::CompileFunction;
16use js::jsapi::{JS_GetFunctionObject, SupportUnscopables};
17use js::jsval::JSVal;
18use js::rust::{CompileOptionsWrapper, HandleObject, transform_u16_to_source_text};
19use libc::c_char;
20use rustc_hash::FxBuildHasher;
21use servo_url::ServoUrl;
22use style::str::HTML_SPACE_CHARACTERS;
23use stylo_atoms::Atom;
24
25use crate::conversions::Convert;
26use crate::dom::abortsignal::{AbortAlgorithm, RemovableDomEventListener};
27use crate::dom::beforeunloadevent::BeforeUnloadEvent;
28use crate::dom::bindings::callback::{CallbackContainer, CallbackFunction, ExceptionHandling};
29use crate::dom::bindings::cell::DomRefCell;
30use crate::dom::bindings::codegen::Bindings::BeforeUnloadEventBinding::BeforeUnloadEventMethods;
31use crate::dom::bindings::codegen::Bindings::ErrorEventBinding::ErrorEventMethods;
32use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
33use crate::dom::bindings::codegen::Bindings::EventHandlerBinding::{
34    EventHandlerNonNull, OnBeforeUnloadEventHandlerNonNull, OnErrorEventHandlerNonNull,
35};
36use crate::dom::bindings::codegen::Bindings::EventListenerBinding::EventListener;
37use crate::dom::bindings::codegen::Bindings::EventTargetBinding::{
38    AddEventListenerOptions, EventListenerOptions, EventTargetMethods,
39};
40use crate::dom::bindings::codegen::Bindings::NodeBinding::GetRootNodeOptions;
41use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
42use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRoot_Binding::ShadowRootMethods;
43use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
44use crate::dom::bindings::codegen::GenericBindings::DocumentBinding::Document_Binding::DocumentMethods;
45use crate::dom::bindings::codegen::UnionTypes::{
46    AddEventListenerOptionsOrBoolean, EventListenerOptionsOrBoolean, EventOrString,
47};
48use crate::dom::bindings::error::{Error, Fallible, report_pending_exception};
49use crate::dom::bindings::inheritance::Castable;
50use crate::dom::bindings::refcounted::Trusted;
51use crate::dom::bindings::reflector::{
52    DomGlobal, DomObject, Reflector, reflect_dom_object_with_proto,
53};
54use crate::dom::bindings::root::DomRoot;
55use crate::dom::bindings::str::DOMString;
56use crate::dom::bindings::trace::HashMapTracedValues;
57use crate::dom::csp::{CspReporting, InlineCheckType};
58use crate::dom::document::Document;
59use crate::dom::element::Element;
60use crate::dom::errorevent::ErrorEvent;
61use crate::dom::event::{Event, EventBubbles, EventCancelable, EventComposed};
62use crate::dom::globalscope::GlobalScope;
63use crate::dom::html::htmlformelement::FormControlElementHelpers;
64use crate::dom::node::{Node, NodeTraits};
65use crate::dom::shadowroot::ShadowRoot;
66use crate::dom::virtualmethods::VirtualMethods;
67use crate::dom::window::Window;
68use crate::dom::workerglobalscope::WorkerGlobalScope;
69use crate::realms::{InRealm, enter_realm};
70use crate::script_runtime::CanGc;
71
72/// <https://html.spec.whatwg.org/multipage/#event-handler-content-attributes>
73/// containing the values from
74/// <https://html.spec.whatwg.org/multipage/#globaleventhandlers> and
75/// <https://html.spec.whatwg.org/multipage/#windoweventhandlers> as well as
76/// specific attributes for elements
77static CONTENT_EVENT_HANDLER_NAMES: [&str; 108] = [
78    "onabort",
79    "onauxclick",
80    "onbeforeinput",
81    "onbeforematch",
82    "onbeforetoggle",
83    "onblur",
84    "oncancel",
85    "oncanplay",
86    "oncanplaythrough",
87    "onchange",
88    "onclick",
89    "onclose",
90    "oncommand",
91    "oncontextlost",
92    "oncontextmenu",
93    "oncontextrestored",
94    "oncopy",
95    "oncuechange",
96    "oncut",
97    "ondblclick",
98    "ondrag",
99    "ondragend",
100    "ondragenter",
101    "ondragleave",
102    "ondragover",
103    "ondragstart",
104    "ondrop",
105    "ondurationchange",
106    "onemptied",
107    "onended",
108    "onerror",
109    "onfocus",
110    "onformdata",
111    "oninput",
112    "oninvalid",
113    "onkeydown",
114    "onkeypress",
115    "onkeyup",
116    "onload",
117    "onloadeddata",
118    "onloadedmetadata",
119    "onloadstart",
120    "onmousedown",
121    "onmouseenter",
122    "onmouseleave",
123    "onmousemove",
124    "onmouseout",
125    "onmouseover",
126    "onmouseup",
127    "onpaste",
128    "onpause",
129    "onplay",
130    "onplaying",
131    "onprogress",
132    "onratechange",
133    "onreset",
134    "onresize",
135    "onscroll",
136    "onscrollend",
137    "onsecuritypolicyviolation",
138    "onseeked",
139    "onseeking",
140    "onselect",
141    "onslotchange",
142    "onstalled",
143    "onsubmit",
144    "onsuspend",
145    "ontimeupdate",
146    "ontoggle",
147    "onvolumechange",
148    "onwaiting",
149    "onwebkitanimationend",
150    "onwebkitanimationiteration",
151    "onwebkitanimationstart",
152    "onwebkittransitionend",
153    "onwheel",
154    // https://drafts.csswg.org/css-animations/#interface-globaleventhandlers-idl
155    "onanimationstart",
156    "onanimationiteration",
157    "onanimationend",
158    "onanimationcancel",
159    // https://drafts.csswg.org/css-transitions/#interface-globaleventhandlers-idl
160    "ontransitionrun",
161    "ontransitionend",
162    "ontransitioncancel",
163    // https://w3c.github.io/selection-api/#extensions-to-globaleventhandlers-interface
164    "onselectstart",
165    "onselectionchange",
166    // https://html.spec.whatwg.org/multipage/#windoweventhandlers
167    "onafterprint",
168    "onbeforeprint",
169    "onbeforeunload",
170    "onhashchange",
171    "onlanguagechange",
172    "onmessage",
173    "onmessageerror",
174    "onoffline",
175    "ononline",
176    "onpagehide",
177    "onpagereveal",
178    "onpageshow",
179    "onpageswap",
180    "onpopstate",
181    "onrejectionhandled",
182    "onstorage",
183    "onunhandledrejection",
184    "onunload",
185    // https://w3c.github.io/encrypted-media/#attributes-3
186    "onencrypted",
187    "onwaitingforkey",
188    // https://svgwg.org/svg2-draft/interact.html#AnimationEvents
189    "onbegin",
190    "onend",
191    "onrepeat",
192];
193
194#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)]
195#[allow(clippy::enum_variant_names)]
196pub(crate) enum CommonEventHandler {
197    EventHandler(#[conditional_malloc_size_of] Rc<EventHandlerNonNull>),
198
199    ErrorEventHandler(#[conditional_malloc_size_of] Rc<OnErrorEventHandlerNonNull>),
200
201    BeforeUnloadEventHandler(#[conditional_malloc_size_of] Rc<OnBeforeUnloadEventHandlerNonNull>),
202}
203
204impl CommonEventHandler {
205    fn parent(&self) -> &CallbackFunction<crate::DomTypeHolder> {
206        match *self {
207            CommonEventHandler::EventHandler(ref handler) => &handler.parent,
208            CommonEventHandler::ErrorEventHandler(ref handler) => &handler.parent,
209            CommonEventHandler::BeforeUnloadEventHandler(ref handler) => &handler.parent,
210        }
211    }
212}
213
214#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
215pub(crate) enum ListenerPhase {
216    Capturing,
217    Bubbling,
218}
219
220/// <https://html.spec.whatwg.org/multipage/#internal-raw-uncompiled-handler>
221#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)]
222struct InternalRawUncompiledHandler {
223    source: DOMString,
224    #[no_trace]
225    url: ServoUrl,
226    line: usize,
227}
228
229/// A representation of an event handler, either compiled or uncompiled raw source, or null.
230#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)]
231enum InlineEventListener {
232    Uncompiled(InternalRawUncompiledHandler),
233    Compiled(CommonEventHandler),
234    Null,
235}
236
237/// Get a compiled representation of this event handler, compiling it from its
238/// raw source if necessary.
239/// <https://html.spec.whatwg.org/multipage/#getting-the-current-value-of-the-event-handler>
240fn get_compiled_handler(
241    inline_listener: &RefCell<InlineEventListener>,
242    owner: &EventTarget,
243    ty: &Atom,
244    can_gc: CanGc,
245) -> Option<CommonEventHandler> {
246    let listener = mem::replace(
247        &mut *inline_listener.borrow_mut(),
248        InlineEventListener::Null,
249    );
250    let compiled = match listener {
251        InlineEventListener::Null => None,
252        InlineEventListener::Uncompiled(handler) => {
253            owner.get_compiled_event_handler(handler, ty, can_gc)
254        },
255        InlineEventListener::Compiled(handler) => Some(handler),
256    };
257    if let Some(ref compiled) = compiled {
258        *inline_listener.borrow_mut() = InlineEventListener::Compiled(compiled.clone());
259    }
260    compiled
261}
262
263#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)]
264enum EventListenerType {
265    Additive(#[conditional_malloc_size_of] Rc<EventListener>),
266    Inline(RefCell<InlineEventListener>),
267}
268
269impl EventListenerType {
270    fn get_compiled_listener(
271        &self,
272        owner: &EventTarget,
273        ty: &Atom,
274        can_gc: CanGc,
275    ) -> Option<CompiledEventListener> {
276        match *self {
277            EventListenerType::Inline(ref inline) => {
278                get_compiled_handler(inline, owner, ty, can_gc).map(CompiledEventListener::Handler)
279            },
280            EventListenerType::Additive(ref listener) => {
281                Some(CompiledEventListener::Listener(listener.clone()))
282            },
283        }
284    }
285}
286
287/// A representation of an EventListener/EventHandler object that has previously
288/// been compiled successfully, if applicable.
289pub(crate) enum CompiledEventListener {
290    Listener(Rc<EventListener>),
291    Handler(CommonEventHandler),
292}
293
294impl CompiledEventListener {
295    #[expect(unsafe_code)]
296    pub(crate) fn associated_global(&self) -> DomRoot<GlobalScope> {
297        let obj = match self {
298            CompiledEventListener::Listener(listener) => listener.callback(),
299            CompiledEventListener::Handler(CommonEventHandler::EventHandler(handler)) => {
300                handler.callback()
301            },
302            CompiledEventListener::Handler(CommonEventHandler::ErrorEventHandler(handler)) => {
303                handler.callback()
304            },
305            CompiledEventListener::Handler(CommonEventHandler::BeforeUnloadEventHandler(
306                handler,
307            )) => handler.callback(),
308        };
309        unsafe { GlobalScope::from_object(obj) }
310    }
311
312    // https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm
313    pub(crate) fn call_or_handle_event(
314        &self,
315        object: &EventTarget,
316        event: &Event,
317        exception_handle: ExceptionHandling,
318        can_gc: CanGc,
319    ) {
320        // Step 3
321        match *self {
322            CompiledEventListener::Listener(ref listener) => {
323                let _ = listener.HandleEvent_(object, event, exception_handle, can_gc);
324            },
325            CompiledEventListener::Handler(ref handler) => {
326                match *handler {
327                    CommonEventHandler::ErrorEventHandler(ref handler) => {
328                        if let Some(event) = event.downcast::<ErrorEvent>() {
329                            if object.is::<Window>() || object.is::<WorkerGlobalScope>() {
330                                let cx = GlobalScope::get_cx();
331                                rooted!(in(*cx) let mut error: JSVal);
332                                event.Error(cx, error.handle_mut());
333                                rooted!(in(*cx) let mut rooted_return_value: JSVal);
334                                let return_value = handler.Call_(
335                                    object,
336                                    EventOrString::String(event.Message()),
337                                    Some(event.Filename()),
338                                    Some(event.Lineno()),
339                                    Some(event.Colno()),
340                                    Some(error.handle()),
341                                    rooted_return_value.handle_mut(),
342                                    exception_handle,
343                                    can_gc,
344                                );
345                                // Step 4
346                                if let Ok(()) = return_value {
347                                    if rooted_return_value.handle().is_boolean() &&
348                                        rooted_return_value.handle().to_boolean()
349                                    {
350                                        event.upcast::<Event>().PreventDefault();
351                                    }
352                                }
353                                return;
354                            }
355                        }
356
357                        rooted!(in(*GlobalScope::get_cx()) let mut rooted_return_value: JSVal);
358                        let _ = handler.Call_(
359                            object,
360                            EventOrString::Event(DomRoot::from_ref(event)),
361                            None,
362                            None,
363                            None,
364                            None,
365                            rooted_return_value.handle_mut(),
366                            exception_handle,
367                            can_gc,
368                        );
369                    },
370
371                    CommonEventHandler::BeforeUnloadEventHandler(ref handler) => {
372                        if let Some(event) = event.downcast::<BeforeUnloadEvent>() {
373                            // Step 5
374                            if let Ok(value) = handler.Call_(
375                                object,
376                                event.upcast::<Event>(),
377                                exception_handle,
378                                can_gc,
379                            ) {
380                                let rv = event.ReturnValue();
381                                if let Some(v) = value {
382                                    if rv.is_empty() {
383                                        event.SetReturnValue(v);
384                                    }
385                                    event.upcast::<Event>().PreventDefault();
386                                }
387                            }
388                        } else {
389                            // Step 5, "Otherwise" clause
390                            let _ = handler.Call_(
391                                object,
392                                event.upcast::<Event>(),
393                                exception_handle,
394                                can_gc,
395                            );
396                        }
397                    },
398
399                    CommonEventHandler::EventHandler(ref handler) => {
400                        let cx = GlobalScope::get_cx();
401                        rooted!(in(*cx) let mut rooted_return_value: JSVal);
402                        if let Ok(()) = handler.Call_(
403                            object,
404                            event,
405                            rooted_return_value.handle_mut(),
406                            exception_handle,
407                            can_gc,
408                        ) {
409                            let value = rooted_return_value.handle();
410
411                            // Step 5
412                            let should_cancel = value.is_boolean() && !value.to_boolean();
413
414                            if should_cancel {
415                                // FIXME: spec says to set the cancelled flag directly
416                                // here, not just to prevent default;
417                                // can that ever make a difference?
418                                event.PreventDefault();
419                            }
420                        }
421                    },
422                }
423            },
424        }
425    }
426}
427
428// https://dom.spec.whatwg.org/#concept-event-listener
429// (as distinct from https://dom.spec.whatwg.org/#callbackdef-eventlistener)
430#[derive(Clone, DenyPublicFields, JSTraceable, MallocSizeOf)]
431/// A listener in a collection of event listeners.
432pub(crate) struct EventListenerEntry {
433    phase: ListenerPhase,
434    listener: EventListenerType,
435    once: bool,
436    passive: bool,
437    removed: bool,
438}
439
440impl EventListenerEntry {
441    pub(crate) fn phase(&self) -> ListenerPhase {
442        self.phase
443    }
444
445    pub(crate) fn once(&self) -> bool {
446        self.once
447    }
448
449    pub(crate) fn removed(&self) -> bool {
450        self.removed
451    }
452
453    /// <https://html.spec.whatwg.org/multipage/#getting-the-current-value-of-the-event-handler>
454    pub(crate) fn get_compiled_listener(
455        &self,
456        owner: &EventTarget,
457        ty: &Atom,
458        can_gc: CanGc,
459    ) -> Option<CompiledEventListener> {
460        self.listener.get_compiled_listener(owner, ty, can_gc)
461    }
462}
463
464impl std::cmp::PartialEq for EventListenerEntry {
465    fn eq(&self, other: &Self) -> bool {
466        self.phase == other.phase && self.listener == other.listener
467    }
468}
469
470#[derive(Clone, JSTraceable, MallocSizeOf)]
471/// A mix of potentially uncompiled and compiled event listeners of the same type.
472pub(crate) struct EventListeners(
473    #[conditional_malloc_size_of] Vec<Rc<RefCell<EventListenerEntry>>>,
474);
475
476impl Deref for EventListeners {
477    type Target = Vec<Rc<RefCell<EventListenerEntry>>>;
478    fn deref(&self) -> &Vec<Rc<RefCell<EventListenerEntry>>> {
479        &self.0
480    }
481}
482
483impl DerefMut for EventListeners {
484    fn deref_mut(&mut self) -> &mut Vec<Rc<RefCell<EventListenerEntry>>> {
485        &mut self.0
486    }
487}
488
489impl EventListeners {
490    /// <https://html.spec.whatwg.org/multipage/#getting-the-current-value-of-the-event-handler>
491    fn get_inline_listener(
492        &self,
493        owner: &EventTarget,
494        ty: &Atom,
495        can_gc: CanGc,
496    ) -> Option<CommonEventHandler> {
497        for entry in &self.0 {
498            if let EventListenerType::Inline(ref inline) = entry.borrow().listener {
499                // Step 1.1-1.8 and Step 2
500                return get_compiled_handler(inline, owner, ty, can_gc);
501            }
502        }
503
504        // Step 2
505        None
506    }
507
508    fn has_listeners(&self) -> bool {
509        !self.0.is_empty()
510    }
511}
512
513#[dom_struct]
514pub struct EventTarget {
515    reflector_: Reflector,
516    handlers: DomRefCell<HashMapTracedValues<Atom, EventListeners, FxBuildHasher>>,
517}
518
519impl EventTarget {
520    pub(crate) fn new_inherited() -> EventTarget {
521        EventTarget {
522            reflector_: Reflector::new(),
523            handlers: DomRefCell::new(Default::default()),
524        }
525    }
526
527    fn new(
528        global: &GlobalScope,
529        proto: Option<HandleObject>,
530        can_gc: CanGc,
531    ) -> DomRoot<EventTarget> {
532        reflect_dom_object_with_proto(
533            Box::new(EventTarget::new_inherited()),
534            global,
535            proto,
536            can_gc,
537        )
538    }
539
540    /// Determine if there are any listeners for a given event type.
541    /// See <https://github.com/whatwg/dom/issues/453>.
542    pub(crate) fn has_listeners_for(&self, type_: &Atom) -> bool {
543        match self.handlers.borrow().get(type_) {
544            Some(listeners) => listeners.has_listeners(),
545            None => false,
546        }
547    }
548
549    pub(crate) fn get_listeners_for(&self, type_: &Atom) -> EventListeners {
550        self.handlers
551            .borrow()
552            .get(type_)
553            .map_or(EventListeners(vec![]), |listeners| listeners.clone())
554    }
555
556    pub(crate) fn dispatch_event(&self, event: &Event, can_gc: CanGc) -> bool {
557        event.dispatch(self, false, can_gc)
558    }
559
560    pub(crate) fn remove_all_listeners(&self) {
561        let mut handlers = self.handlers.borrow_mut();
562        for (_, entries) in handlers.iter() {
563            entries
564                .iter()
565                .for_each(|entry| entry.borrow_mut().removed = true);
566        }
567
568        *handlers = Default::default();
569    }
570
571    /// <https://dom.spec.whatwg.org/#default-passive-value>
572    fn default_passive_value(&self, ty: &Atom) -> bool {
573        // Return true if all of the following are true:
574        let event_type = ty.to_ascii_lowercase();
575
576        // type is one of "touchstart", "touchmove", "wheel", or "mousewheel"
577        let matches_event_type = matches!(
578            event_type.trim_matches(HTML_SPACE_CHARACTERS),
579            "touchstart" | "touchmove" | "wheel" | "mousewheel"
580        );
581
582        if !matches_event_type {
583            return false;
584        }
585
586        // eventTarget is a Window object
587        if self.is::<Window>() {
588            return true;
589        }
590
591        // or ...
592        if let Some(node) = self.downcast::<Node>() {
593            let node_document = node.owner_document();
594            let event_target = self.upcast::<EventTarget>();
595
596            // is a node whose node document is eventTarget
597            return event_target == node_document.upcast::<EventTarget>()
598                // or is a node whose node document’s document element is eventTarget
599                || node_document.GetDocumentElement().is_some_and(|n| n.upcast::<EventTarget>() == event_target)
600                // or is a node whose node document’s body element is eventTarget
601                || node_document.GetBody().is_some_and(|n| n.upcast::<EventTarget>() == event_target);
602        }
603
604        false
605    }
606
607    /// <https://html.spec.whatwg.org/multipage/#event-handler-attributes:event-handlers-11>
608    fn set_inline_event_listener(&self, ty: Atom, listener: Option<InlineEventListener>) {
609        let mut handlers = self.handlers.borrow_mut();
610        let entries = match handlers.entry(ty.clone()) {
611            Occupied(entry) => entry.into_mut(),
612            Vacant(entry) => entry.insert(EventListeners(vec![])),
613        };
614
615        let idx = entries
616            .iter()
617            .position(|entry| matches!(entry.borrow().listener, EventListenerType::Inline(_)));
618
619        match idx {
620            Some(idx) => match listener {
621                // Replace if there's something to replace with,
622                // but remove entirely if there isn't.
623                Some(listener) => {
624                    entries[idx].borrow_mut().listener = EventListenerType::Inline(listener.into());
625                },
626                None => {
627                    entries.remove(idx).borrow_mut().removed = true;
628                },
629            },
630            None => {
631                if let Some(listener) = listener {
632                    entries.push(Rc::new(RefCell::new(EventListenerEntry {
633                        phase: ListenerPhase::Bubbling,
634                        listener: EventListenerType::Inline(listener.into()),
635                        once: false,
636                        passive: self.default_passive_value(&ty),
637                        removed: false,
638                    })));
639                }
640            },
641        }
642    }
643
644    pub(crate) fn remove_listener(&self, ty: &Atom, entry: &Rc<RefCell<EventListenerEntry>>) {
645        let mut handlers = self.handlers.borrow_mut();
646
647        if let Some(entries) = handlers.get_mut(ty) {
648            if let Some(position) = entries.iter().position(|e| *e == *entry) {
649                entries.remove(position).borrow_mut().removed = true;
650            }
651        }
652    }
653
654    /// Determines the `passive` attribute of an associated event listener
655    pub(crate) fn is_passive(&self, listener: &Rc<RefCell<EventListenerEntry>>) -> bool {
656        listener.borrow().passive
657    }
658
659    fn get_inline_event_listener(&self, ty: &Atom, can_gc: CanGc) -> Option<CommonEventHandler> {
660        let handlers = self.handlers.borrow();
661        handlers
662            .get(ty)
663            .and_then(|entry| entry.get_inline_listener(self, ty, can_gc))
664    }
665
666    /// Store the raw uncompiled event handler for on-demand compilation later.
667    /// <https://html.spec.whatwg.org/multipage/#event-handler-attributes:event-handler-content-attributes-3>
668    pub(crate) fn set_event_handler_uncompiled(
669        &self,
670        url: ServoUrl,
671        line: usize,
672        ty: &str,
673        source: &str,
674    ) {
675        if let Some(element) = self.downcast::<Element>() {
676            let doc = element.owner_document();
677            let global = &doc.global();
678            if global
679                .get_csp_list()
680                .should_elements_inline_type_behavior_be_blocked(
681                    global,
682                    element.upcast(),
683                    InlineCheckType::ScriptAttribute,
684                    source,
685                    line as u32,
686                )
687            {
688                return;
689            }
690        };
691
692        let handler = InternalRawUncompiledHandler {
693            source: DOMString::from(source),
694            line,
695            url,
696        };
697        self.set_inline_event_listener(
698            Atom::from(ty),
699            Some(InlineEventListener::Uncompiled(handler)),
700        );
701    }
702
703    // https://html.spec.whatwg.org/multipage/#getting-the-current-value-of-the-event-handler
704    // step 3
705    // While the CanGc argument appears unused, it reflects the fact that the CompileFunction
706    // API call can trigger a GC operation.
707    #[expect(unsafe_code)]
708    fn get_compiled_event_handler(
709        &self,
710        handler: InternalRawUncompiledHandler,
711        ty: &Atom,
712        can_gc: CanGc,
713    ) -> Option<CommonEventHandler> {
714        // Step 3.1
715        let element = self.downcast::<Element>();
716        let document = match element {
717            Some(element) => element.owner_document(),
718            None => self.downcast::<Window>().unwrap().Document(),
719        };
720
721        // Step 3.2
722        if !document.scripting_enabled() {
723            return None;
724        }
725
726        // Step 3.3
727        let body: Vec<u16> = handler.source.str().encode_utf16().collect();
728
729        // Step 3.4 is handler.line
730
731        // Step 3.5
732        let form_owner = element
733            .and_then(|e| e.as_maybe_form_control())
734            .and_then(|f| f.form_owner());
735
736        // Step 3.6 TODO: settings objects not implemented
737
738        // Step 3.7 is written as though we call the parser separately
739        // from the compiler; since we just call CompileFunction with
740        // source text, we handle parse errors later
741
742        // Step 3.8 TODO: settings objects not implemented
743        let window = document.window();
744        let _ac = enter_realm(window);
745
746        // Step 3.9
747
748        let name = CString::new(format!("on{}", &**ty)).unwrap();
749
750        // Step 3.9, subsection ParameterList
751        const ARG_NAMES: &[*const c_char] = &[c"event".as_ptr()];
752        const ERROR_ARG_NAMES: &[*const c_char] = &[
753            c"event".as_ptr(),
754            c"source".as_ptr(),
755            c"lineno".as_ptr(),
756            c"colno".as_ptr(),
757            c"error".as_ptr(),
758        ];
759        let is_error = ty == &atom!("error") && self.is::<Window>();
760        let args = if is_error { ERROR_ARG_NAMES } else { ARG_NAMES };
761
762        let cx = GlobalScope::get_cx();
763        let options = unsafe {
764            CompileOptionsWrapper::new(*cx, &handler.url.to_string(), handler.line as u32)
765        };
766
767        // Step 3.9, subsection Scope steps 1-6
768        let scopechain = js::rust::EnvironmentChain::new(*cx, SupportUnscopables::Yes);
769
770        if let Some(element) = element {
771            scopechain.append(document.reflector().get_jsobject().get());
772            if let Some(form_owner) = form_owner {
773                scopechain.append(form_owner.reflector().get_jsobject().get());
774            }
775            scopechain.append(element.reflector().get_jsobject().get());
776        }
777
778        rooted!(in(*cx) let mut handler = unsafe {
779            CompileFunction(
780                *cx,
781                scopechain.get(),
782                options.ptr,
783                name.as_ptr(),
784                args.len() as u32,
785                args.as_ptr(),
786                &mut transform_u16_to_source_text(&body),
787            )
788        });
789        if handler.get().is_null() {
790            // Step 3.7
791            let ar = enter_realm(self);
792            // FIXME(#13152): dispatch error event.
793            report_pending_exception(cx, false, InRealm::Entered(&ar), can_gc);
794            return None;
795        }
796
797        // Step 3.10 happens when we drop _ac
798
799        // TODO Step 3.11
800
801        // Step 3.12
802        let funobj = unsafe { JS_GetFunctionObject(handler.get()) };
803        assert!(!funobj.is_null());
804        // Step 1.14
805        if is_error {
806            Some(CommonEventHandler::ErrorEventHandler(unsafe {
807                OnErrorEventHandlerNonNull::new(cx, funobj)
808            }))
809        } else if ty == &atom!("beforeunload") {
810            Some(CommonEventHandler::BeforeUnloadEventHandler(unsafe {
811                OnBeforeUnloadEventHandlerNonNull::new(cx, funobj)
812            }))
813        } else {
814            Some(CommonEventHandler::EventHandler(unsafe {
815                EventHandlerNonNull::new(cx, funobj)
816            }))
817        }
818    }
819
820    #[expect(unsafe_code)]
821    pub(crate) fn set_event_handler_common<T: CallbackContainer<crate::DomTypeHolder>>(
822        &self,
823        ty: &str,
824        listener: Option<Rc<T>>,
825    ) {
826        let cx = GlobalScope::get_cx();
827
828        let event_listener = listener.map(|listener| {
829            InlineEventListener::Compiled(CommonEventHandler::EventHandler(unsafe {
830                EventHandlerNonNull::new(cx, listener.callback())
831            }))
832        });
833        self.set_inline_event_listener(Atom::from(ty), event_listener);
834    }
835
836    #[expect(unsafe_code)]
837    pub(crate) fn set_error_event_handler<T: CallbackContainer<crate::DomTypeHolder>>(
838        &self,
839        ty: &str,
840        listener: Option<Rc<T>>,
841    ) {
842        let cx = GlobalScope::get_cx();
843
844        let event_listener = listener.map(|listener| {
845            InlineEventListener::Compiled(CommonEventHandler::ErrorEventHandler(unsafe {
846                OnErrorEventHandlerNonNull::new(cx, listener.callback())
847            }))
848        });
849        self.set_inline_event_listener(Atom::from(ty), event_listener);
850    }
851
852    #[expect(unsafe_code)]
853    pub(crate) fn set_beforeunload_event_handler<T: CallbackContainer<crate::DomTypeHolder>>(
854        &self,
855        ty: &str,
856        listener: Option<Rc<T>>,
857    ) {
858        let cx = GlobalScope::get_cx();
859
860        let event_listener = listener.map(|listener| {
861            InlineEventListener::Compiled(CommonEventHandler::BeforeUnloadEventHandler(unsafe {
862                OnBeforeUnloadEventHandlerNonNull::new(cx, listener.callback())
863            }))
864        });
865        self.set_inline_event_listener(Atom::from(ty), event_listener);
866    }
867
868    #[expect(unsafe_code)]
869    pub(crate) fn get_event_handler_common<T: CallbackContainer<crate::DomTypeHolder>>(
870        &self,
871        ty: &str,
872        can_gc: CanGc,
873    ) -> Option<Rc<T>> {
874        let cx = GlobalScope::get_cx();
875        let listener = self.get_inline_event_listener(&Atom::from(ty), can_gc);
876        unsafe {
877            listener.map(|listener| {
878                CallbackContainer::new(cx, listener.parent().callback_holder().get())
879            })
880        }
881    }
882
883    pub(crate) fn has_handlers(&self) -> bool {
884        !self.handlers.borrow().is_empty()
885    }
886
887    // https://dom.spec.whatwg.org/#concept-event-fire
888    pub(crate) fn fire_event(&self, name: Atom, can_gc: CanGc) -> bool {
889        self.fire_event_with_params(
890            name,
891            EventBubbles::DoesNotBubble,
892            EventCancelable::NotCancelable,
893            EventComposed::NotComposed,
894            can_gc,
895        )
896    }
897
898    // https://dom.spec.whatwg.org/#concept-event-fire
899    pub(crate) fn fire_bubbling_event(&self, name: Atom, can_gc: CanGc) -> bool {
900        self.fire_event_with_params(
901            name,
902            EventBubbles::Bubbles,
903            EventCancelable::NotCancelable,
904            EventComposed::NotComposed,
905            can_gc,
906        )
907    }
908
909    // https://dom.spec.whatwg.org/#concept-event-fire
910    pub(crate) fn fire_cancelable_event(&self, name: Atom, can_gc: CanGc) -> bool {
911        self.fire_event_with_params(
912            name,
913            EventBubbles::DoesNotBubble,
914            EventCancelable::Cancelable,
915            EventComposed::NotComposed,
916            can_gc,
917        )
918    }
919
920    // https://dom.spec.whatwg.org/#concept-event-fire
921    pub(crate) fn fire_bubbling_cancelable_event(&self, name: Atom, can_gc: CanGc) -> bool {
922        self.fire_event_with_params(
923            name,
924            EventBubbles::Bubbles,
925            EventCancelable::Cancelable,
926            EventComposed::NotComposed,
927            can_gc,
928        )
929    }
930
931    /// <https://dom.spec.whatwg.org/#concept-event-fire>
932    pub(crate) fn fire_event_with_params(
933        &self,
934        name: Atom,
935        bubbles: EventBubbles,
936        cancelable: EventCancelable,
937        composed: EventComposed,
938        can_gc: CanGc,
939    ) -> bool {
940        let event = Event::new(&self.global(), name, bubbles, cancelable, can_gc);
941        event.set_composed(composed.into());
942        event.fire(self, can_gc)
943    }
944
945    /// <https://dom.spec.whatwg.org/#dom-eventtarget-addeventlistener>
946    /// and <https://dom.spec.whatwg.org/#add-an-event-listener>
947    pub(crate) fn add_event_listener(
948        &self,
949        ty: DOMString,
950        listener: Option<Rc<EventListener>>,
951        options: AddEventListenerOptions,
952    ) {
953        if let Some(signal) = options.signal.as_ref() {
954            // Step 2. If listener’s signal is not null and is aborted, then return.
955            if signal.aborted() {
956                return;
957            }
958            // Step 6. If listener’s signal is not null, then add the following abort steps to it:
959            signal.add(&AbortAlgorithm::DomEventListener(
960                RemovableDomEventListener {
961                    event_target: Trusted::new(self),
962                    ty: ty.clone(),
963                    listener: listener.clone(),
964                    options: options.parent.clone(),
965                },
966            ));
967        }
968        // Step 3. If listener’s callback is null, then return.
969        let listener = match listener {
970            Some(l) => l,
971            None => return,
972        };
973        let mut handlers = self.handlers.borrow_mut();
974        let ty = Atom::from(ty);
975        let entries = match handlers.entry(ty.clone()) {
976            Occupied(entry) => entry.into_mut(),
977            Vacant(entry) => entry.insert(EventListeners(vec![])),
978        };
979
980        let phase = if options.parent.capture {
981            ListenerPhase::Capturing
982        } else {
983            ListenerPhase::Bubbling
984        };
985        // Step 4. If listener’s passive is null, then set it to the default passive value given listener’s type and eventTarget.
986        let new_entry = Rc::new(RefCell::new(EventListenerEntry {
987            phase,
988            listener: EventListenerType::Additive(listener),
989            once: options.once,
990            passive: options.passive.unwrap_or(self.default_passive_value(&ty)),
991            removed: false,
992        }));
993
994        // Step 5. If eventTarget’s event listener list does not contain
995        // an event listener whose type is listener’s type, callback is listener’s callback,
996        // and capture is listener’s capture, then append listener to eventTarget’s event listener list.
997        if !entries.contains(&new_entry) {
998            entries.push(new_entry);
999        }
1000    }
1001
1002    /// <https://dom.spec.whatwg.org/#dom-eventtarget-removeeventlistener>
1003    /// and <https://dom.spec.whatwg.org/#remove-an-event-listener>
1004    pub(crate) fn remove_event_listener(
1005        &self,
1006        ty: DOMString,
1007        listener: &Option<Rc<EventListener>>,
1008        options: &EventListenerOptions,
1009    ) {
1010        let Some(listener) = listener else {
1011            return;
1012        };
1013        let mut handlers = self.handlers.borrow_mut();
1014        if let Some(entries) = handlers.get_mut(&Atom::from(ty)) {
1015            let phase = if options.capture {
1016                ListenerPhase::Capturing
1017            } else {
1018                ListenerPhase::Bubbling
1019            };
1020            let listener_type = EventListenerType::Additive(listener.clone());
1021            if let Some(position) = entries
1022                .iter()
1023                .position(|e| e.borrow().listener == listener_type && e.borrow().phase == phase)
1024            {
1025                // Step 2. Set listener’s removed to true and remove listener from eventTarget’s event listener list.
1026                entries.remove(position).borrow_mut().removed = true;
1027            }
1028        }
1029    }
1030
1031    /// <https://dom.spec.whatwg.org/#get-the-parent>
1032    pub(crate) fn get_the_parent(&self, event: &Event) -> Option<DomRoot<EventTarget>> {
1033        if let Some(document) = self.downcast::<Document>() {
1034            if event.type_() == atom!("load") || !document.has_browsing_context() {
1035                return None;
1036            } else {
1037                return Some(DomRoot::from_ref(document.window().upcast::<EventTarget>()));
1038            }
1039        }
1040
1041        if let Some(shadow_root) = self.downcast::<ShadowRoot>() {
1042            if event.should_pass_shadow_boundary(shadow_root) {
1043                let host = shadow_root.Host();
1044                return Some(DomRoot::from_ref(host.upcast::<EventTarget>()));
1045            } else {
1046                return None;
1047            }
1048        }
1049
1050        if let Some(node) = self.downcast::<Node>() {
1051            // > A node’s get the parent algorithm, given an event, returns the node’s assigned slot,
1052            // > if node is assigned; otherwise node’s parent.
1053            return node.assigned_slot().map(DomRoot::upcast).or_else(|| {
1054                node.GetParentNode()
1055                    .map(|parent| DomRoot::from_ref(parent.upcast::<EventTarget>()))
1056            });
1057        }
1058
1059        None
1060    }
1061
1062    // FIXME: This algorithm operates on "objects", which may not be event targets.
1063    // All our current use-cases only work on event targets, but this might change in the future
1064    /// <https://dom.spec.whatwg.org/#retarget>
1065    pub(crate) fn retarget(&self, b: &Self) -> DomRoot<EventTarget> {
1066        // To retarget an object A against an object B, repeat these steps until they return an object:
1067        let mut a = DomRoot::from_ref(self);
1068        loop {
1069            // Step 1. If one of the following is true
1070            // * A is not a node
1071            // * A’s root is not a shadow root
1072            // * B is a node and A’s root is a shadow-including inclusive ancestor of B
1073            // then return A.
1074            let Some(a_node) = a.downcast::<Node>() else {
1075                return a;
1076            };
1077            let a_root = a_node.GetRootNode(&GetRootNodeOptions::empty());
1078            if !a_root.is::<ShadowRoot>() {
1079                return a;
1080            }
1081            if let Some(b_node) = b.downcast::<Node>() {
1082                if a_root.is_shadow_including_inclusive_ancestor_of(b_node) {
1083                    return a;
1084                }
1085            }
1086
1087            // Step 2. Set A to A’s root’s host.
1088            a = DomRoot::from_ref(
1089                a_root
1090                    .downcast::<ShadowRoot>()
1091                    .unwrap()
1092                    .Host()
1093                    .upcast::<EventTarget>(),
1094            );
1095        }
1096    }
1097
1098    /// <https://html.spec.whatwg.org/multipage/#event-handler-content-attributes>
1099    pub(crate) fn is_content_event_handler(name: &str) -> bool {
1100        CONTENT_EVENT_HANDLER_NAMES.contains(&name)
1101    }
1102}
1103
1104impl EventTargetMethods<crate::DomTypeHolder> for EventTarget {
1105    /// <https://dom.spec.whatwg.org/#dom-eventtarget-eventtarget>
1106    fn Constructor(
1107        global: &GlobalScope,
1108        proto: Option<HandleObject>,
1109        can_gc: CanGc,
1110    ) -> Fallible<DomRoot<EventTarget>> {
1111        Ok(EventTarget::new(global, proto, can_gc))
1112    }
1113
1114    /// <https://dom.spec.whatwg.org/#dom-eventtarget-addeventlistener>
1115    fn AddEventListener(
1116        &self,
1117        ty: DOMString,
1118        listener: Option<Rc<EventListener>>,
1119        options: AddEventListenerOptionsOrBoolean,
1120    ) {
1121        self.add_event_listener(ty, listener, options.convert())
1122    }
1123
1124    /// <https://dom.spec.whatwg.org/#dom-eventtarget-removeeventlistener>
1125    fn RemoveEventListener(
1126        &self,
1127        ty: DOMString,
1128        listener: Option<Rc<EventListener>>,
1129        options: EventListenerOptionsOrBoolean,
1130    ) {
1131        self.remove_event_listener(ty, &listener, &options.convert())
1132    }
1133
1134    /// <https://dom.spec.whatwg.org/#dom-eventtarget-dispatchevent>
1135    fn DispatchEvent(&self, event: &Event, can_gc: CanGc) -> Fallible<bool> {
1136        if event.dispatching() || !event.initialized() {
1137            return Err(Error::InvalidState(None));
1138        }
1139        event.set_trusted(false);
1140        Ok(self.dispatch_event(event, can_gc))
1141    }
1142}
1143
1144impl VirtualMethods for EventTarget {
1145    fn super_type(&self) -> Option<&dyn VirtualMethods> {
1146        None
1147    }
1148}
1149
1150impl Convert<AddEventListenerOptions> for AddEventListenerOptionsOrBoolean {
1151    /// <https://dom.spec.whatwg.org/#event-flatten-more>
1152    fn convert(self) -> AddEventListenerOptions {
1153        // Step 1. Let capture be the result of flattening options.
1154        // Step 5. Return capture, passive, once, and signal.
1155        match self {
1156            // Step 4. If options is a dictionary:
1157            AddEventListenerOptionsOrBoolean::AddEventListenerOptions(options) => options,
1158            AddEventListenerOptionsOrBoolean::Boolean(capture) => AddEventListenerOptions {
1159                parent: EventListenerOptions { capture },
1160                // Step 2. Let once be false.
1161                once: false,
1162                // Step 3. Let passive and signal be null.
1163                passive: None,
1164                signal: None,
1165            },
1166        }
1167    }
1168}
1169
1170impl Convert<EventListenerOptions> for EventListenerOptionsOrBoolean {
1171    fn convert(self) -> EventListenerOptions {
1172        match self {
1173            EventListenerOptionsOrBoolean::EventListenerOptions(options) => options,
1174            EventListenerOptionsOrBoolean::Boolean(capture) => EventListenerOptions { capture },
1175        }
1176    }
1177}