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