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