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; 108] = [
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 "onafterprint",
169 "onbeforeprint",
170 "onbeforeunload",
171 "onhashchange",
172 "onlanguagechange",
173 "onmessage",
174 "onmessageerror",
175 "onoffline",
176 "ononline",
177 "onpagehide",
178 "onpagereveal",
179 "onpageshow",
180 "onpageswap",
181 "onpopstate",
182 "onrejectionhandled",
183 "onstorage",
184 "onunhandledrejection",
185 "onunload",
186 "onencrypted",
188 "onwaitingforkey",
189 "onbegin",
191 "onend",
192 "onrepeat",
193];
194
195#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)]
196#[expect(clippy::enum_variant_names)]
197pub(crate) enum CommonEventHandler {
198 EventHandler(#[conditional_malloc_size_of] Rc<EventHandlerNonNull>),
199
200 ErrorEventHandler(#[conditional_malloc_size_of] Rc<OnErrorEventHandlerNonNull>),
201
202 BeforeUnloadEventHandler(#[conditional_malloc_size_of] Rc<OnBeforeUnloadEventHandlerNonNull>),
203}
204
205impl CommonEventHandler {
206 fn parent(&self) -> &CallbackFunction<crate::DomTypeHolder> {
207 match *self {
208 CommonEventHandler::EventHandler(ref handler) => &handler.parent,
209 CommonEventHandler::ErrorEventHandler(ref handler) => &handler.parent,
210 CommonEventHandler::BeforeUnloadEventHandler(ref handler) => &handler.parent,
211 }
212 }
213}
214
215#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
216pub(crate) enum ListenerPhase {
217 Capturing,
218 Bubbling,
219}
220
221#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)]
223struct InternalRawUncompiledHandler {
224 source: DOMString,
225 #[no_trace]
226 url: ServoUrl,
227 line: usize,
228}
229
230#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)]
232enum InlineEventListener {
233 Uncompiled(InternalRawUncompiledHandler),
234 Compiled(CommonEventHandler),
235 Null,
236}
237
238fn get_compiled_handler(
242 inline_listener: &RefCell<InlineEventListener>,
243 owner: &EventTarget,
244 ty: &Atom,
245 can_gc: CanGc,
246) -> Option<CommonEventHandler> {
247 let listener = mem::replace(
248 &mut *inline_listener.borrow_mut(),
249 InlineEventListener::Null,
250 );
251 let compiled = match listener {
252 InlineEventListener::Null => None,
253 InlineEventListener::Uncompiled(handler) => {
254 owner.get_compiled_event_handler(handler, ty, can_gc)
255 },
256 InlineEventListener::Compiled(handler) => Some(handler),
257 };
258 if let Some(ref compiled) = compiled {
259 *inline_listener.borrow_mut() = InlineEventListener::Compiled(compiled.clone());
260 }
261 compiled
262}
263
264#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)]
265enum EventListenerType {
266 Additive(#[conditional_malloc_size_of] Rc<EventListener>),
267 Inline(RefCell<InlineEventListener>),
268}
269
270impl EventListenerType {
271 fn get_compiled_listener(
272 &self,
273 owner: &EventTarget,
274 ty: &Atom,
275 can_gc: CanGc,
276 ) -> Option<CompiledEventListener> {
277 match *self {
278 EventListenerType::Inline(ref inline) => {
279 get_compiled_handler(inline, owner, ty, can_gc).map(CompiledEventListener::Handler)
280 },
281 EventListenerType::Additive(ref listener) => {
282 Some(CompiledEventListener::Listener(listener.clone()))
283 },
284 }
285 }
286}
287
288pub(crate) enum CompiledEventListener {
291 Listener(Rc<EventListener>),
292 Handler(CommonEventHandler),
293}
294
295impl CompiledEventListener {
296 #[expect(unsafe_code)]
297 pub(crate) fn associated_global(&self) -> DomRoot<GlobalScope> {
298 let obj = match self {
299 CompiledEventListener::Listener(listener) => listener.callback(),
300 CompiledEventListener::Handler(CommonEventHandler::EventHandler(handler)) => {
301 handler.callback()
302 },
303 CompiledEventListener::Handler(CommonEventHandler::ErrorEventHandler(handler)) => {
304 handler.callback()
305 },
306 CompiledEventListener::Handler(CommonEventHandler::BeforeUnloadEventHandler(
307 handler,
308 )) => handler.callback(),
309 };
310 unsafe { GlobalScope::from_object(obj) }
311 }
312
313 pub(crate) fn call_or_handle_event(
315 &self,
316 object: &EventTarget,
317 event: &Event,
318 exception_handle: ExceptionHandling,
319 can_gc: CanGc,
320 ) -> Fallible<()> {
321 match *self {
323 CompiledEventListener::Listener(ref listener) => {
324 listener.HandleEvent_(object, event, exception_handle, can_gc)
325 },
326 CompiledEventListener::Handler(ref handler) => {
327 match *handler {
328 CommonEventHandler::ErrorEventHandler(ref handler) => {
329 if let Some(event) = event.downcast::<ErrorEvent>() {
330 if object.is::<Window>() || object.is::<WorkerGlobalScope>() {
331 let cx = GlobalScope::get_cx();
332 rooted!(in(*cx) let mut error: JSVal);
333 event.Error(cx, error.handle_mut());
334 rooted!(in(*cx) let mut rooted_return_value: JSVal);
335 let return_value = handler.Call_(
336 object,
337 EventOrString::String(event.Message()),
338 Some(event.Filename()),
339 Some(event.Lineno()),
340 Some(event.Colno()),
341 Some(error.handle()),
342 rooted_return_value.handle_mut(),
343 exception_handle,
344 can_gc,
345 );
346 if let Ok(()) = return_value {
348 if rooted_return_value.handle().is_boolean() &&
349 rooted_return_value.handle().to_boolean()
350 {
351 event.upcast::<Event>().PreventDefault();
352 }
353 }
354 return return_value;
355 }
356 }
357
358 rooted!(in(*GlobalScope::get_cx()) let mut rooted_return_value: JSVal);
359 handler.Call_(
360 object,
361 EventOrString::Event(DomRoot::from_ref(event)),
362 None,
363 None,
364 None,
365 None,
366 rooted_return_value.handle_mut(),
367 exception_handle,
368 can_gc,
369 )
370 },
371
372 CommonEventHandler::BeforeUnloadEventHandler(ref handler) => {
373 if let Some(event) = event.downcast::<BeforeUnloadEvent>() {
374 match handler.Call_(
376 object,
377 event.upcast::<Event>(),
378 exception_handle,
379 can_gc,
380 ) {
381 Ok(value) => {
382 let rv = event.ReturnValue();
383 if let Some(v) = value {
384 if rv.is_empty() {
385 event.SetReturnValue(v);
386 }
387 event.upcast::<Event>().PreventDefault();
388 }
389 Ok(())
390 },
391 Err(err) => Err(err),
392 }
393 } else {
394 handler
396 .Call_(object, event.upcast::<Event>(), exception_handle, can_gc)
397 .map(|_| ())
398 }
399 },
400
401 CommonEventHandler::EventHandler(ref handler) => {
402 let cx = GlobalScope::get_cx();
403 rooted!(in(*cx) let mut rooted_return_value: JSVal);
404 match handler.Call_(
405 object,
406 event,
407 rooted_return_value.handle_mut(),
408 exception_handle,
409 can_gc,
410 ) {
411 Ok(()) => {
412 let value = rooted_return_value.handle();
413
414 let should_cancel = value.is_boolean() && !value.to_boolean();
416
417 if should_cancel {
418 event.PreventDefault();
422 }
423 Ok(())
424 },
425 Err(err) => Err(err),
426 }
427 },
428 }
429 },
430 }
431 }
432}
433
434#[derive(Clone, DenyPublicFields, JSTraceable, MallocSizeOf)]
437pub(crate) struct EventListenerEntry {
439 phase: ListenerPhase,
440 listener: EventListenerType,
441 once: bool,
442 passive: bool,
443 removed: bool,
444}
445
446impl EventListenerEntry {
447 pub(crate) fn phase(&self) -> ListenerPhase {
448 self.phase
449 }
450
451 pub(crate) fn once(&self) -> bool {
452 self.once
453 }
454
455 pub(crate) fn removed(&self) -> bool {
456 self.removed
457 }
458
459 pub(crate) fn get_compiled_listener(
461 &self,
462 owner: &EventTarget,
463 ty: &Atom,
464 can_gc: CanGc,
465 ) -> Option<CompiledEventListener> {
466 self.listener.get_compiled_listener(owner, ty, can_gc)
467 }
468}
469
470impl std::cmp::PartialEq for EventListenerEntry {
471 fn eq(&self, other: &Self) -> bool {
472 self.phase == other.phase && self.listener == other.listener
473 }
474}
475
476#[derive(Clone, JSTraceable, MallocSizeOf)]
477pub(crate) struct EventListeners(
479 #[conditional_malloc_size_of] Vec<Rc<RefCell<EventListenerEntry>>>,
480);
481
482impl Deref for EventListeners {
483 type Target = Vec<Rc<RefCell<EventListenerEntry>>>;
484 fn deref(&self) -> &Vec<Rc<RefCell<EventListenerEntry>>> {
485 &self.0
486 }
487}
488
489impl DerefMut for EventListeners {
490 fn deref_mut(&mut self) -> &mut Vec<Rc<RefCell<EventListenerEntry>>> {
491 &mut self.0
492 }
493}
494
495impl EventListeners {
496 fn get_inline_listener(
498 &self,
499 owner: &EventTarget,
500 ty: &Atom,
501 can_gc: CanGc,
502 ) -> Option<CommonEventHandler> {
503 for entry in &self.0 {
504 if let EventListenerType::Inline(ref inline) = entry.borrow().listener {
505 return get_compiled_handler(inline, owner, ty, can_gc);
507 }
508 }
509
510 None
512 }
513
514 fn has_listeners(&self) -> bool {
515 !self.0.is_empty()
516 }
517}
518
519#[dom_struct]
520pub struct EventTarget {
521 reflector_: Reflector,
522 handlers: DomRefCell<HashMapTracedValues<Atom, EventListeners, FxBuildHasher>>,
523}
524
525impl EventTarget {
526 pub(crate) fn new_inherited() -> EventTarget {
527 EventTarget {
528 reflector_: Reflector::new(),
529 handlers: DomRefCell::new(Default::default()),
530 }
531 }
532
533 fn new(
534 global: &GlobalScope,
535 proto: Option<HandleObject>,
536 can_gc: CanGc,
537 ) -> DomRoot<EventTarget> {
538 reflect_dom_object_with_proto(
539 Box::new(EventTarget::new_inherited()),
540 global,
541 proto,
542 can_gc,
543 )
544 }
545
546 pub(crate) fn has_listeners_for(&self, type_: &Atom) -> bool {
549 match self.handlers.borrow().get(type_) {
550 Some(listeners) => listeners.has_listeners(),
551 None => false,
552 }
553 }
554
555 pub(crate) fn get_listeners_for(&self, type_: &Atom) -> EventListeners {
556 self.handlers
557 .borrow()
558 .get(type_)
559 .map_or(EventListeners(vec![]), |listeners| listeners.clone())
560 }
561
562 pub(crate) fn dispatch_event(&self, event: &Event, can_gc: CanGc) -> bool {
563 event.dispatch(self, false, can_gc)
564 }
565
566 pub(crate) fn remove_all_listeners(&self) {
567 let mut handlers = self.handlers.borrow_mut();
568 for (_, entries) in handlers.iter() {
569 entries
570 .iter()
571 .for_each(|entry| entry.borrow_mut().removed = true);
572 }
573
574 *handlers = Default::default();
575 }
576
577 fn default_passive_value(&self, ty: &Atom) -> bool {
579 let event_type = ty.to_ascii_lowercase();
581
582 let matches_event_type = matches!(
584 event_type.trim_matches(HTML_SPACE_CHARACTERS),
585 "touchstart" | "touchmove" | "wheel" | "mousewheel"
586 );
587
588 if !matches_event_type {
589 return false;
590 }
591
592 if self.is::<Window>() {
594 return true;
595 }
596
597 if let Some(node) = self.downcast::<Node>() {
599 let node_document = node.owner_document();
600 let event_target = self.upcast::<EventTarget>();
601
602 return event_target == node_document.upcast::<EventTarget>()
604 || node_document.GetDocumentElement().is_some_and(|n| n.upcast::<EventTarget>() == event_target)
606 || node_document.GetBody().is_some_and(|n| n.upcast::<EventTarget>() == event_target);
608 }
609
610 false
611 }
612
613 fn set_inline_event_listener(&self, ty: Atom, listener: Option<InlineEventListener>) {
615 let mut handlers = self.handlers.borrow_mut();
616 let entries = match handlers.entry(ty.clone()) {
617 Occupied(entry) => entry.into_mut(),
618 Vacant(entry) => entry.insert(EventListeners(vec![])),
619 };
620
621 let idx = entries
622 .iter()
623 .position(|entry| matches!(entry.borrow().listener, EventListenerType::Inline(_)));
624
625 match idx {
626 Some(idx) => match listener {
627 Some(listener) => {
630 entries[idx].borrow_mut().listener = EventListenerType::Inline(listener.into());
631 },
632 None => {
633 entries.remove(idx).borrow_mut().removed = true;
634 },
635 },
636 None => {
637 if let Some(listener) = listener {
638 entries.push(Rc::new(RefCell::new(EventListenerEntry {
639 phase: ListenerPhase::Bubbling,
640 listener: EventListenerType::Inline(listener.into()),
641 once: false,
642 passive: self.default_passive_value(&ty),
643 removed: false,
644 })));
645 }
646 },
647 }
648 }
649
650 pub(crate) fn remove_listener(&self, ty: &Atom, entry: &Rc<RefCell<EventListenerEntry>>) {
651 let mut handlers = self.handlers.borrow_mut();
652
653 if let Some(entries) = handlers.get_mut(ty) {
654 if let Some(position) = entries.iter().position(|e| *e == *entry) {
655 entries.remove(position).borrow_mut().removed = true;
656 }
657 }
658 }
659
660 pub(crate) fn is_passive(&self, listener: &Rc<RefCell<EventListenerEntry>>) -> bool {
662 listener.borrow().passive
663 }
664
665 fn get_inline_event_listener(&self, ty: &Atom, can_gc: CanGc) -> Option<CommonEventHandler> {
666 let handlers = self.handlers.borrow();
667 handlers
668 .get(ty)
669 .and_then(|entry| entry.get_inline_listener(self, ty, can_gc))
670 }
671
672 pub(crate) fn set_event_handler_uncompiled(
675 &self,
676 url: ServoUrl,
677 line: usize,
678 ty: &str,
679 source: &str,
680 ) {
681 if let Some(element) = self.downcast::<Element>() {
682 let doc = element.owner_document();
683 let global = &doc.global();
684 if global
685 .get_csp_list()
686 .should_elements_inline_type_behavior_be_blocked(
687 global,
688 element.upcast(),
689 InlineCheckType::ScriptAttribute,
690 source,
691 line as u32,
692 )
693 {
694 return;
695 }
696 };
697
698 let handler = InternalRawUncompiledHandler {
699 source: DOMString::from(source),
700 line,
701 url,
702 };
703 self.set_inline_event_listener(
704 Atom::from(ty),
705 Some(InlineEventListener::Uncompiled(handler)),
706 );
707 }
708
709 #[expect(unsafe_code)]
714 fn get_compiled_event_handler(
715 &self,
716 handler: InternalRawUncompiledHandler,
717 ty: &Atom,
718 can_gc: CanGc,
719 ) -> Option<CommonEventHandler> {
720 let element = self.downcast::<Element>();
722 let document = match element {
723 Some(element) => element.owner_document(),
724 None => self.downcast::<Window>().unwrap().Document(),
725 };
726
727 if !document.scripting_enabled() {
729 return None;
730 }
731
732 let body: Vec<u16> = handler.source.str().encode_utf16().collect();
734
735 let form_owner = element
739 .and_then(|e| e.as_maybe_form_control())
740 .and_then(|f| f.form_owner());
741
742 let window = document.window();
750 let _ac = enter_realm(window);
751
752 let name = CString::new(format!("on{}", &**ty)).unwrap();
755
756 const ARG_NAMES: &[*const c_char] = &[c"event".as_ptr()];
758 const ERROR_ARG_NAMES: &[*const c_char] = &[
759 c"event".as_ptr(),
760 c"source".as_ptr(),
761 c"lineno".as_ptr(),
762 c"colno".as_ptr(),
763 c"error".as_ptr(),
764 ];
765 let is_error = ty == &atom!("error") && self.is::<Window>();
766 let args = if is_error { ERROR_ARG_NAMES } else { ARG_NAMES };
767
768 let cx = GlobalScope::get_cx();
769 let url = cformat!("{}", handler.url);
770 let options = unsafe { CompileOptionsWrapper::new_raw(*cx, url, handler.line as u32) };
771
772 let scopechain = js::rust::EnvironmentChain::new(*cx, SupportUnscopables::Yes);
774
775 if let Some(element) = element {
776 scopechain.append(document.reflector().get_jsobject().get());
777 if let Some(form_owner) = form_owner {
778 scopechain.append(form_owner.reflector().get_jsobject().get());
779 }
780 scopechain.append(element.reflector().get_jsobject().get());
781 }
782
783 rooted!(in(*cx) let mut handler = unsafe {
784 CompileFunction(
785 *cx,
786 scopechain.get(),
787 options.ptr,
788 name.as_ptr(),
789 args.len() as u32,
790 args.as_ptr(),
791 &mut transform_u16_to_source_text(&body),
792 )
793 });
794 if handler.get().is_null() {
795 let ar = enter_realm(self);
797 report_pending_exception(cx, false, InRealm::Entered(&ar), can_gc);
799 return None;
800 }
801
802 let funobj = unsafe { JS_GetFunctionObject(handler.get()) };
808 assert!(!funobj.is_null());
809 if is_error {
811 Some(CommonEventHandler::ErrorEventHandler(unsafe {
812 OnErrorEventHandlerNonNull::new(cx, funobj)
813 }))
814 } else if ty == &atom!("beforeunload") {
815 Some(CommonEventHandler::BeforeUnloadEventHandler(unsafe {
816 OnBeforeUnloadEventHandlerNonNull::new(cx, funobj)
817 }))
818 } else {
819 Some(CommonEventHandler::EventHandler(unsafe {
820 EventHandlerNonNull::new(cx, funobj)
821 }))
822 }
823 }
824
825 #[expect(unsafe_code)]
826 pub(crate) fn set_event_handler_common<T: CallbackContainer<crate::DomTypeHolder>>(
827 &self,
828 ty: &str,
829 listener: Option<Rc<T>>,
830 ) {
831 let cx = GlobalScope::get_cx();
832
833 let event_listener = listener.map(|listener| {
834 InlineEventListener::Compiled(CommonEventHandler::EventHandler(unsafe {
835 EventHandlerNonNull::new(cx, listener.callback())
836 }))
837 });
838 self.set_inline_event_listener(Atom::from(ty), event_listener);
839 }
840
841 #[expect(unsafe_code)]
842 pub(crate) fn set_error_event_handler<T: CallbackContainer<crate::DomTypeHolder>>(
843 &self,
844 ty: &str,
845 listener: Option<Rc<T>>,
846 ) {
847 let cx = GlobalScope::get_cx();
848
849 let event_listener = listener.map(|listener| {
850 InlineEventListener::Compiled(CommonEventHandler::ErrorEventHandler(unsafe {
851 OnErrorEventHandlerNonNull::new(cx, listener.callback())
852 }))
853 });
854 self.set_inline_event_listener(Atom::from(ty), event_listener);
855 }
856
857 #[expect(unsafe_code)]
858 pub(crate) fn set_beforeunload_event_handler<T: CallbackContainer<crate::DomTypeHolder>>(
859 &self,
860 ty: &str,
861 listener: Option<Rc<T>>,
862 ) {
863 let cx = GlobalScope::get_cx();
864
865 let event_listener = listener.map(|listener| {
866 InlineEventListener::Compiled(CommonEventHandler::BeforeUnloadEventHandler(unsafe {
867 OnBeforeUnloadEventHandlerNonNull::new(cx, listener.callback())
868 }))
869 });
870 self.set_inline_event_listener(Atom::from(ty), event_listener);
871 }
872
873 #[expect(unsafe_code)]
874 pub(crate) fn get_event_handler_common<T: CallbackContainer<crate::DomTypeHolder>>(
875 &self,
876 ty: &str,
877 can_gc: CanGc,
878 ) -> Option<Rc<T>> {
879 let cx = GlobalScope::get_cx();
880 let listener = self.get_inline_event_listener(&Atom::from(ty), can_gc);
881 unsafe {
882 listener.map(|listener| {
883 CallbackContainer::new(cx, listener.parent().callback_holder().get())
884 })
885 }
886 }
887
888 pub(crate) fn has_handlers(&self) -> bool {
889 !self.handlers.borrow().is_empty()
890 }
891
892 pub(crate) fn fire_event(&self, name: Atom, can_gc: CanGc) -> bool {
894 self.fire_event_with_params(
895 name,
896 EventBubbles::DoesNotBubble,
897 EventCancelable::NotCancelable,
898 EventComposed::NotComposed,
899 can_gc,
900 )
901 }
902
903 pub(crate) fn fire_bubbling_event(&self, name: Atom, can_gc: CanGc) -> bool {
905 self.fire_event_with_params(
906 name,
907 EventBubbles::Bubbles,
908 EventCancelable::NotCancelable,
909 EventComposed::NotComposed,
910 can_gc,
911 )
912 }
913
914 pub(crate) fn fire_cancelable_event(&self, name: Atom, can_gc: CanGc) -> bool {
916 self.fire_event_with_params(
917 name,
918 EventBubbles::DoesNotBubble,
919 EventCancelable::Cancelable,
920 EventComposed::NotComposed,
921 can_gc,
922 )
923 }
924
925 pub(crate) fn fire_bubbling_cancelable_event(&self, name: Atom, can_gc: CanGc) -> bool {
927 self.fire_event_with_params(
928 name,
929 EventBubbles::Bubbles,
930 EventCancelable::Cancelable,
931 EventComposed::NotComposed,
932 can_gc,
933 )
934 }
935
936 pub(crate) fn fire_event_with_params(
938 &self,
939 name: Atom,
940 bubbles: EventBubbles,
941 cancelable: EventCancelable,
942 composed: EventComposed,
943 can_gc: CanGc,
944 ) -> bool {
945 let event = Event::new(&self.global(), name, bubbles, cancelable, can_gc);
946 event.set_composed(composed.into());
947 event.fire(self, can_gc)
948 }
949
950 pub(crate) fn add_event_listener(
953 &self,
954 ty: DOMString,
955 listener: Option<Rc<EventListener>>,
956 options: AddEventListenerOptions,
957 ) {
958 if let Some(signal) = options.signal.as_ref() {
959 if signal.aborted() {
961 return;
962 }
963 signal.add(&AbortAlgorithm::DomEventListener(
965 RemovableDomEventListener {
966 event_target: Dom::from_ref(self),
967 ty: ty.clone(),
968 listener: listener.clone(),
969 options: options.parent.clone(),
970 },
971 ));
972 }
973 let listener = match listener {
975 Some(l) => l,
976 None => return,
977 };
978 let mut handlers = self.handlers.borrow_mut();
979 let ty = Atom::from(ty);
980 let entries = match handlers.entry(ty.clone()) {
981 Occupied(entry) => entry.into_mut(),
982 Vacant(entry) => entry.insert(EventListeners(vec![])),
983 };
984
985 let phase = if options.parent.capture {
986 ListenerPhase::Capturing
987 } else {
988 ListenerPhase::Bubbling
989 };
990 let new_entry = Rc::new(RefCell::new(EventListenerEntry {
992 phase,
993 listener: EventListenerType::Additive(listener),
994 once: options.once,
995 passive: options.passive.unwrap_or(self.default_passive_value(&ty)),
996 removed: false,
997 }));
998
999 if !entries.contains(&new_entry) {
1003 entries.push(new_entry);
1004 }
1005 }
1006
1007 pub(crate) fn remove_event_listener(
1010 &self,
1011 ty: DOMString,
1012 listener: &Option<Rc<EventListener>>,
1013 options: &EventListenerOptions,
1014 ) {
1015 let Some(listener) = listener else {
1016 return;
1017 };
1018 let mut handlers = self.handlers.borrow_mut();
1019 if let Some(entries) = handlers.get_mut(&Atom::from(ty)) {
1020 let phase = if options.capture {
1021 ListenerPhase::Capturing
1022 } else {
1023 ListenerPhase::Bubbling
1024 };
1025 let listener_type = EventListenerType::Additive(listener.clone());
1026 if let Some(position) = entries
1027 .iter()
1028 .position(|e| e.borrow().listener == listener_type && e.borrow().phase == phase)
1029 {
1030 entries.remove(position).borrow_mut().removed = true;
1032 }
1033 }
1034 }
1035
1036 pub(crate) fn get_the_parent(&self, event: &Event) -> Option<DomRoot<EventTarget>> {
1038 if let Some(document) = self.downcast::<Document>() {
1039 if event.type_() == atom!("load") || !document.has_browsing_context() {
1040 return None;
1041 } else {
1042 return Some(DomRoot::from_ref(document.window().upcast::<EventTarget>()));
1043 }
1044 }
1045
1046 if let Some(shadow_root) = self.downcast::<ShadowRoot>() {
1047 if event.should_pass_shadow_boundary(shadow_root) {
1048 let host = shadow_root.Host();
1049 return Some(DomRoot::from_ref(host.upcast::<EventTarget>()));
1050 } else {
1051 return None;
1052 }
1053 }
1054
1055 if let Some(node) = self.downcast::<Node>() {
1056 return node.assigned_slot().map(DomRoot::upcast).or_else(|| {
1059 node.GetParentNode()
1060 .map(|parent| DomRoot::from_ref(parent.upcast::<EventTarget>()))
1061 });
1062 }
1063
1064 None
1065 }
1066
1067 pub(crate) fn retarget(&self, b: &Self) -> DomRoot<EventTarget> {
1071 let mut a = DomRoot::from_ref(self);
1073 loop {
1074 let Some(a_node) = a.downcast::<Node>() else {
1080 return a;
1081 };
1082 let a_root = a_node.GetRootNode(&GetRootNodeOptions::empty());
1083 if !a_root.is::<ShadowRoot>() {
1084 return a;
1085 }
1086 if let Some(b_node) = b.downcast::<Node>() {
1087 if a_root.is_shadow_including_inclusive_ancestor_of(b_node) {
1088 return a;
1089 }
1090 }
1091
1092 a = DomRoot::from_ref(
1094 a_root
1095 .downcast::<ShadowRoot>()
1096 .unwrap()
1097 .Host()
1098 .upcast::<EventTarget>(),
1099 );
1100 }
1101 }
1102
1103 pub(crate) fn is_content_event_handler(name: &str) -> bool {
1105 CONTENT_EVENT_HANDLER_NAMES.contains(&name)
1106 }
1107
1108 pub(crate) fn summarize_event_listeners_for_devtools(&self) -> Vec<EventListenerInfo> {
1109 let handlers = self.handlers.borrow();
1110 let mut listener_infos = Vec::with_capacity(handlers.0.len());
1111 for (event_type, event_listeners) in &handlers.0 {
1112 for event_listener in event_listeners.iter() {
1113 let event_listener_entry = event_listener.borrow();
1114 listener_infos.push(EventListenerInfo {
1115 event_type: event_type.to_string(),
1116 capturing: event_listener_entry.phase() == ListenerPhase::Capturing,
1117 });
1118 }
1119 }
1120
1121 listener_infos
1122 }
1123}
1124
1125impl EventTargetMethods<crate::DomTypeHolder> for EventTarget {
1126 fn Constructor(
1128 global: &GlobalScope,
1129 proto: Option<HandleObject>,
1130 can_gc: CanGc,
1131 ) -> Fallible<DomRoot<EventTarget>> {
1132 Ok(EventTarget::new(global, proto, can_gc))
1133 }
1134
1135 fn AddEventListener(
1137 &self,
1138 ty: DOMString,
1139 listener: Option<Rc<EventListener>>,
1140 options: AddEventListenerOptionsOrBoolean,
1141 ) {
1142 self.add_event_listener(ty, listener, options.convert())
1143 }
1144
1145 fn RemoveEventListener(
1147 &self,
1148 ty: DOMString,
1149 listener: Option<Rc<EventListener>>,
1150 options: EventListenerOptionsOrBoolean,
1151 ) {
1152 self.remove_event_listener(ty, &listener, &options.convert())
1153 }
1154
1155 fn DispatchEvent(&self, event: &Event, can_gc: CanGc) -> Fallible<bool> {
1157 if event.dispatching() || !event.initialized() {
1158 return Err(Error::InvalidState(None));
1159 }
1160 event.set_trusted(false);
1161 Ok(self.dispatch_event(event, can_gc))
1162 }
1163}
1164
1165impl VirtualMethods for EventTarget {
1166 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1167 None
1168 }
1169}
1170
1171impl Convert<AddEventListenerOptions> for AddEventListenerOptionsOrBoolean {
1172 fn convert(self) -> AddEventListenerOptions {
1174 match self {
1177 AddEventListenerOptionsOrBoolean::AddEventListenerOptions(options) => options,
1179 AddEventListenerOptionsOrBoolean::Boolean(capture) => AddEventListenerOptions {
1180 parent: EventListenerOptions { capture },
1181 once: false,
1183 passive: None,
1185 signal: None,
1186 },
1187 }
1188 }
1189}
1190
1191impl Convert<EventListenerOptions> for EventListenerOptionsOrBoolean {
1192 fn convert(self) -> EventListenerOptions {
1193 match self {
1194 EventListenerOptionsOrBoolean::EventListenerOptions(options) => options,
1195 EventListenerOptionsOrBoolean::Boolean(capture) => EventListenerOptions { capture },
1196 }
1197 }
1198}