script/dom/
eventtarget.rs

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