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