1use 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
74static 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 "onanimationstart",
159 "onanimationiteration",
160 "onanimationend",
161 "onanimationcancel",
162 "ontransitionrun",
164 "ontransitionend",
165 "ontransitioncancel",
166 "onselectstart",
168 "onselectionchange",
169 "onpointercancel",
171 "onpointerdown",
172 "onpointerup",
173 "onpointermove",
174 "onpointerout",
175 "onpointerover",
176 "onpointerenter",
177 "onpointerleave",
178 "ongotpointercapture",
179 "onlostpointercapture",
180 "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 "onencrypted",
201 "onwaitingforkey",
202 "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#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)]
237struct InternalRawUncompiledHandler {
238 source: DOMString,
239 #[no_trace]
240 url: ServoUrl,
241 line: usize,
242}
243
244#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)]
246enum InlineEventListener {
247 Uncompiled(InternalRawUncompiledHandler),
248 Compiled(CommonEventHandler),
249 Null,
250}
251
252fn 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
302pub(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 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 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 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 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 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 let should_cancel = value.is_boolean() && !value.to_boolean();
430
431 if should_cancel {
432 event.PreventDefault();
436 }
437 Ok(())
438 },
439 Err(err) => Err(err),
440 }
441 },
442 }
443 },
444 }
445 }
446}
447
448#[derive(Clone, DenyPublicFields, JSTraceable, MallocSizeOf)]
451pub(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 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)]
491pub(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 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 return get_compiled_handler(inline, owner, ty, can_gc);
521 }
522 }
523
524 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 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 fn default_passive_value(&self, ty: &Atom) -> bool {
593 let event_type = ty.to_ascii_lowercase();
595
596 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 if self.is::<Window>() {
608 return true;
609 }
610
611 if let Some(node) = self.downcast::<Node>() {
613 let node_document = node.owner_document();
614 let event_target = self.upcast::<EventTarget>();
615
616 return event_target == node_document.upcast::<EventTarget>()
618 || node_document.GetDocumentElement().is_some_and(|n| n.upcast::<EventTarget>() == event_target)
620 || node_document.GetBody().is_some_and(|n| n.upcast::<EventTarget>() == event_target);
622 }
623
624 false
625 }
626
627 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 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 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 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 #[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 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 if !document.scripting_enabled() {
743 return None;
744 }
745
746 let body: Vec<u16> = handler.source.str().encode_utf16().collect();
748
749 let form_owner = element
753 .and_then(|e| e.as_maybe_form_control())
754 .and_then(|f| f.form_owner());
755
756 let window = document.window();
764 let _ac = enter_realm(window);
765
766 let name = CString::new(format!("on{}", &**ty)).unwrap();
769
770 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 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 let ar = enter_realm(self);
811 report_pending_exception(cx, InRealm::Entered(&ar), can_gc);
812 return None;
813 }
814
815 let funobj = unsafe { JS_GetFunctionObject(handler.get()) };
821 assert!(!funobj.is_null());
822 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 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 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 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 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 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 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 if signal.aborted() {
974 return;
975 }
976 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 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 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 if !entries.contains(&new_entry) {
1016 entries.push(new_entry);
1017 }
1018 }
1019
1020 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 entries.remove(position).borrow_mut().removed = true;
1045 }
1046 }
1047 }
1048
1049 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 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 pub(crate) fn retarget(&self, b: &Self) -> DomRoot<EventTarget> {
1084 let mut a = DomRoot::from_ref(self);
1086 loop {
1087 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 a = DomRoot::from_ref(
1107 a_root
1108 .downcast::<ShadowRoot>()
1109 .unwrap()
1110 .Host()
1111 .upcast::<EventTarget>(),
1112 );
1113 }
1114 }
1115
1116 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 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 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 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 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 fn convert(self) -> AddEventListenerOptions {
1187 match self {
1190 AddEventListenerOptionsOrBoolean::AddEventListenerOptions(options) => options,
1192 AddEventListenerOptionsOrBoolean::Boolean(capture) => AddEventListenerOptions {
1193 parent: EventListenerOptions { capture },
1194 once: false,
1196 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}