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 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
72static 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 "onanimationstart",
156 "onanimationiteration",
157 "onanimationend",
158 "onanimationcancel",
159 "ontransitionrun",
161 "ontransitionend",
162 "ontransitioncancel",
163 "onselectstart",
165 "onselectionchange",
166 "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 "onencrypted",
187 "onwaitingforkey",
188 "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#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)]
222struct InternalRawUncompiledHandler {
223 source: DOMString,
224 #[no_trace]
225 url: ServoUrl,
226 line: usize,
227}
228
229#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)]
231enum InlineEventListener {
232 Uncompiled(InternalRawUncompiledHandler),
233 Compiled(CommonEventHandler),
234 Null,
235}
236
237fn 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
287pub(crate) enum CompiledEventListener {
290 Listener(Rc<EventListener>),
291 Handler(CommonEventHandler),
292}
293
294impl CompiledEventListener {
295 #[expect(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 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 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 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 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 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 let should_cancel = value.is_boolean() && !value.to_boolean();
413
414 if should_cancel {
415 event.PreventDefault();
419 }
420 }
421 },
422 }
423 },
424 }
425 }
426}
427
428#[derive(Clone, DenyPublicFields, JSTraceable, MallocSizeOf)]
431pub(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 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)]
471pub(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 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 return get_compiled_handler(inline, owner, ty, can_gc);
501 }
502 }
503
504 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 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 fn default_passive_value(&self, ty: &Atom) -> bool {
573 let event_type = ty.to_ascii_lowercase();
575
576 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 if self.is::<Window>() {
588 return true;
589 }
590
591 if let Some(node) = self.downcast::<Node>() {
593 let node_document = node.owner_document();
594 let event_target = self.upcast::<EventTarget>();
595
596 return event_target == node_document.upcast::<EventTarget>()
598 || node_document.GetDocumentElement().is_some_and(|n| n.upcast::<EventTarget>() == event_target)
600 || node_document.GetBody().is_some_and(|n| n.upcast::<EventTarget>() == event_target);
602 }
603
604 false
605 }
606
607 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 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 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 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 line as u32,
686 )
687 {
688 return;
689 }
690 };
691
692 let handler = InternalRawUncompiledHandler {
693 source: DOMString::from(source),
694 line,
695 url,
696 };
697 self.set_inline_event_listener(
698 Atom::from(ty),
699 Some(InlineEventListener::Uncompiled(handler)),
700 );
701 }
702
703 #[expect(unsafe_code)]
708 fn get_compiled_event_handler(
709 &self,
710 handler: InternalRawUncompiledHandler,
711 ty: &Atom,
712 can_gc: CanGc,
713 ) -> Option<CommonEventHandler> {
714 let element = self.downcast::<Element>();
716 let document = match element {
717 Some(element) => element.owner_document(),
718 None => self.downcast::<Window>().unwrap().Document(),
719 };
720
721 if !document.scripting_enabled() {
723 return None;
724 }
725
726 let body: Vec<u16> = handler.source.str().encode_utf16().collect();
728
729 let form_owner = element
733 .and_then(|e| e.as_maybe_form_control())
734 .and_then(|f| f.form_owner());
735
736 let window = document.window();
744 let _ac = enter_realm(window);
745
746 let name = CString::new(format!("on{}", &**ty)).unwrap();
749
750 const ARG_NAMES: &[*const c_char] = &[c"event".as_ptr()];
752 const ERROR_ARG_NAMES: &[*const c_char] = &[
753 c"event".as_ptr(),
754 c"source".as_ptr(),
755 c"lineno".as_ptr(),
756 c"colno".as_ptr(),
757 c"error".as_ptr(),
758 ];
759 let is_error = ty == &atom!("error") && self.is::<Window>();
760 let args = if is_error { ERROR_ARG_NAMES } else { ARG_NAMES };
761
762 let cx = GlobalScope::get_cx();
763 let options = unsafe {
764 CompileOptionsWrapper::new(*cx, &handler.url.to_string(), handler.line as u32)
765 };
766
767 let scopechain = js::rust::EnvironmentChain::new(*cx, SupportUnscopables::Yes);
769
770 if let Some(element) = element {
771 scopechain.append(document.reflector().get_jsobject().get());
772 if let Some(form_owner) = form_owner {
773 scopechain.append(form_owner.reflector().get_jsobject().get());
774 }
775 scopechain.append(element.reflector().get_jsobject().get());
776 }
777
778 rooted!(in(*cx) let mut handler = unsafe {
779 CompileFunction(
780 *cx,
781 scopechain.get(),
782 options.ptr,
783 name.as_ptr(),
784 args.len() as u32,
785 args.as_ptr(),
786 &mut transform_u16_to_source_text(&body),
787 )
788 });
789 if handler.get().is_null() {
790 let ar = enter_realm(self);
792 report_pending_exception(cx, false, InRealm::Entered(&ar), can_gc);
794 return None;
795 }
796
797 let funobj = unsafe { JS_GetFunctionObject(handler.get()) };
803 assert!(!funobj.is_null());
804 if is_error {
806 Some(CommonEventHandler::ErrorEventHandler(unsafe {
807 OnErrorEventHandlerNonNull::new(cx, funobj)
808 }))
809 } else if ty == &atom!("beforeunload") {
810 Some(CommonEventHandler::BeforeUnloadEventHandler(unsafe {
811 OnBeforeUnloadEventHandlerNonNull::new(cx, funobj)
812 }))
813 } else {
814 Some(CommonEventHandler::EventHandler(unsafe {
815 EventHandlerNonNull::new(cx, funobj)
816 }))
817 }
818 }
819
820 #[expect(unsafe_code)]
821 pub(crate) fn set_event_handler_common<T: CallbackContainer<crate::DomTypeHolder>>(
822 &self,
823 ty: &str,
824 listener: Option<Rc<T>>,
825 ) {
826 let cx = GlobalScope::get_cx();
827
828 let event_listener = listener.map(|listener| {
829 InlineEventListener::Compiled(CommonEventHandler::EventHandler(unsafe {
830 EventHandlerNonNull::new(cx, listener.callback())
831 }))
832 });
833 self.set_inline_event_listener(Atom::from(ty), event_listener);
834 }
835
836 #[expect(unsafe_code)]
837 pub(crate) fn set_error_event_handler<T: CallbackContainer<crate::DomTypeHolder>>(
838 &self,
839 ty: &str,
840 listener: Option<Rc<T>>,
841 ) {
842 let cx = GlobalScope::get_cx();
843
844 let event_listener = listener.map(|listener| {
845 InlineEventListener::Compiled(CommonEventHandler::ErrorEventHandler(unsafe {
846 OnErrorEventHandlerNonNull::new(cx, listener.callback())
847 }))
848 });
849 self.set_inline_event_listener(Atom::from(ty), event_listener);
850 }
851
852 #[expect(unsafe_code)]
853 pub(crate) fn set_beforeunload_event_handler<T: CallbackContainer<crate::DomTypeHolder>>(
854 &self,
855 ty: &str,
856 listener: Option<Rc<T>>,
857 ) {
858 let cx = GlobalScope::get_cx();
859
860 let event_listener = listener.map(|listener| {
861 InlineEventListener::Compiled(CommonEventHandler::BeforeUnloadEventHandler(unsafe {
862 OnBeforeUnloadEventHandlerNonNull::new(cx, listener.callback())
863 }))
864 });
865 self.set_inline_event_listener(Atom::from(ty), event_listener);
866 }
867
868 #[expect(unsafe_code)]
869 pub(crate) fn get_event_handler_common<T: CallbackContainer<crate::DomTypeHolder>>(
870 &self,
871 ty: &str,
872 can_gc: CanGc,
873 ) -> Option<Rc<T>> {
874 let cx = GlobalScope::get_cx();
875 let listener = self.get_inline_event_listener(&Atom::from(ty), can_gc);
876 unsafe {
877 listener.map(|listener| {
878 CallbackContainer::new(cx, listener.parent().callback_holder().get())
879 })
880 }
881 }
882
883 pub(crate) fn has_handlers(&self) -> bool {
884 !self.handlers.borrow().is_empty()
885 }
886
887 pub(crate) fn fire_event(&self, name: Atom, can_gc: CanGc) -> bool {
889 self.fire_event_with_params(
890 name,
891 EventBubbles::DoesNotBubble,
892 EventCancelable::NotCancelable,
893 EventComposed::NotComposed,
894 can_gc,
895 )
896 }
897
898 pub(crate) fn fire_bubbling_event(&self, name: Atom, can_gc: CanGc) -> bool {
900 self.fire_event_with_params(
901 name,
902 EventBubbles::Bubbles,
903 EventCancelable::NotCancelable,
904 EventComposed::NotComposed,
905 can_gc,
906 )
907 }
908
909 pub(crate) fn fire_cancelable_event(&self, name: Atom, can_gc: CanGc) -> bool {
911 self.fire_event_with_params(
912 name,
913 EventBubbles::DoesNotBubble,
914 EventCancelable::Cancelable,
915 EventComposed::NotComposed,
916 can_gc,
917 )
918 }
919
920 pub(crate) fn fire_bubbling_cancelable_event(&self, name: Atom, can_gc: CanGc) -> bool {
922 self.fire_event_with_params(
923 name,
924 EventBubbles::Bubbles,
925 EventCancelable::Cancelable,
926 EventComposed::NotComposed,
927 can_gc,
928 )
929 }
930
931 pub(crate) fn fire_event_with_params(
933 &self,
934 name: Atom,
935 bubbles: EventBubbles,
936 cancelable: EventCancelable,
937 composed: EventComposed,
938 can_gc: CanGc,
939 ) -> bool {
940 let event = Event::new(&self.global(), name, bubbles, cancelable, can_gc);
941 event.set_composed(composed.into());
942 event.fire(self, can_gc)
943 }
944
945 pub(crate) fn add_event_listener(
948 &self,
949 ty: DOMString,
950 listener: Option<Rc<EventListener>>,
951 options: AddEventListenerOptions,
952 ) {
953 if let Some(signal) = options.signal.as_ref() {
954 if signal.aborted() {
956 return;
957 }
958 signal.add(&AbortAlgorithm::DomEventListener(
960 RemovableDomEventListener {
961 event_target: Trusted::new(self),
962 ty: ty.clone(),
963 listener: listener.clone(),
964 options: options.parent.clone(),
965 },
966 ));
967 }
968 let listener = match listener {
970 Some(l) => l,
971 None => return,
972 };
973 let mut handlers = self.handlers.borrow_mut();
974 let ty = Atom::from(ty);
975 let entries = match handlers.entry(ty.clone()) {
976 Occupied(entry) => entry.into_mut(),
977 Vacant(entry) => entry.insert(EventListeners(vec![])),
978 };
979
980 let phase = if options.parent.capture {
981 ListenerPhase::Capturing
982 } else {
983 ListenerPhase::Bubbling
984 };
985 let new_entry = Rc::new(RefCell::new(EventListenerEntry {
987 phase,
988 listener: EventListenerType::Additive(listener),
989 once: options.once,
990 passive: options.passive.unwrap_or(self.default_passive_value(&ty)),
991 removed: false,
992 }));
993
994 if !entries.contains(&new_entry) {
998 entries.push(new_entry);
999 }
1000 }
1001
1002 pub(crate) fn remove_event_listener(
1005 &self,
1006 ty: DOMString,
1007 listener: &Option<Rc<EventListener>>,
1008 options: &EventListenerOptions,
1009 ) {
1010 let Some(listener) = listener else {
1011 return;
1012 };
1013 let mut handlers = self.handlers.borrow_mut();
1014 if let Some(entries) = handlers.get_mut(&Atom::from(ty)) {
1015 let phase = if options.capture {
1016 ListenerPhase::Capturing
1017 } else {
1018 ListenerPhase::Bubbling
1019 };
1020 let listener_type = EventListenerType::Additive(listener.clone());
1021 if let Some(position) = entries
1022 .iter()
1023 .position(|e| e.borrow().listener == listener_type && e.borrow().phase == phase)
1024 {
1025 entries.remove(position).borrow_mut().removed = true;
1027 }
1028 }
1029 }
1030
1031 pub(crate) fn get_the_parent(&self, event: &Event) -> Option<DomRoot<EventTarget>> {
1033 if let Some(document) = self.downcast::<Document>() {
1034 if event.type_() == atom!("load") || !document.has_browsing_context() {
1035 return None;
1036 } else {
1037 return Some(DomRoot::from_ref(document.window().upcast::<EventTarget>()));
1038 }
1039 }
1040
1041 if let Some(shadow_root) = self.downcast::<ShadowRoot>() {
1042 if event.should_pass_shadow_boundary(shadow_root) {
1043 let host = shadow_root.Host();
1044 return Some(DomRoot::from_ref(host.upcast::<EventTarget>()));
1045 } else {
1046 return None;
1047 }
1048 }
1049
1050 if let Some(node) = self.downcast::<Node>() {
1051 return node.assigned_slot().map(DomRoot::upcast).or_else(|| {
1054 node.GetParentNode()
1055 .map(|parent| DomRoot::from_ref(parent.upcast::<EventTarget>()))
1056 });
1057 }
1058
1059 None
1060 }
1061
1062 pub(crate) fn retarget(&self, b: &Self) -> DomRoot<EventTarget> {
1066 let mut a = DomRoot::from_ref(self);
1068 loop {
1069 let Some(a_node) = a.downcast::<Node>() else {
1075 return a;
1076 };
1077 let a_root = a_node.GetRootNode(&GetRootNodeOptions::empty());
1078 if !a_root.is::<ShadowRoot>() {
1079 return a;
1080 }
1081 if let Some(b_node) = b.downcast::<Node>() {
1082 if a_root.is_shadow_including_inclusive_ancestor_of(b_node) {
1083 return a;
1084 }
1085 }
1086
1087 a = DomRoot::from_ref(
1089 a_root
1090 .downcast::<ShadowRoot>()
1091 .unwrap()
1092 .Host()
1093 .upcast::<EventTarget>(),
1094 );
1095 }
1096 }
1097
1098 pub(crate) fn is_content_event_handler(name: &str) -> bool {
1100 CONTENT_EVENT_HANDLER_NAMES.contains(&name)
1101 }
1102}
1103
1104impl EventTargetMethods<crate::DomTypeHolder> for EventTarget {
1105 fn Constructor(
1107 global: &GlobalScope,
1108 proto: Option<HandleObject>,
1109 can_gc: CanGc,
1110 ) -> Fallible<DomRoot<EventTarget>> {
1111 Ok(EventTarget::new(global, proto, can_gc))
1112 }
1113
1114 fn AddEventListener(
1116 &self,
1117 ty: DOMString,
1118 listener: Option<Rc<EventListener>>,
1119 options: AddEventListenerOptionsOrBoolean,
1120 ) {
1121 self.add_event_listener(ty, listener, options.convert())
1122 }
1123
1124 fn RemoveEventListener(
1126 &self,
1127 ty: DOMString,
1128 listener: Option<Rc<EventListener>>,
1129 options: EventListenerOptionsOrBoolean,
1130 ) {
1131 self.remove_event_listener(ty, &listener, &options.convert())
1132 }
1133
1134 fn DispatchEvent(&self, event: &Event, can_gc: CanGc) -> Fallible<bool> {
1136 if event.dispatching() || !event.initialized() {
1137 return Err(Error::InvalidState(None));
1138 }
1139 event.set_trusted(false);
1140 Ok(self.dispatch_event(event, can_gc))
1141 }
1142}
1143
1144impl VirtualMethods for EventTarget {
1145 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1146 None
1147 }
1148}
1149
1150impl Convert<AddEventListenerOptions> for AddEventListenerOptionsOrBoolean {
1151 fn convert(self) -> AddEventListenerOptions {
1153 match self {
1156 AddEventListenerOptionsOrBoolean::AddEventListenerOptions(options) => options,
1158 AddEventListenerOptionsOrBoolean::Boolean(capture) => AddEventListenerOptions {
1159 parent: EventListenerOptions { capture },
1160 once: false,
1162 passive: None,
1164 signal: None,
1165 },
1166 }
1167 }
1168}
1169
1170impl Convert<EventListenerOptions> for EventListenerOptionsOrBoolean {
1171 fn convert(self) -> EventListenerOptions {
1172 match self {
1173 EventListenerOptionsOrBoolean::EventListenerOptions(options) => options,
1174 EventListenerOptionsOrBoolean::Boolean(capture) => EventListenerOptions { capture },
1175 }
1176 }
1177}