1use std::cell::{RefCell, RefMut};
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::context::JSContext;
18use js::jsapi::{JS_GetFunctionObject, SupportUnscopables};
19use js::jsval::JSVal;
20use js::rust::wrappers2::CompileFunction;
21use js::rust::{CompileOptionsWrapper, HandleObject, transform_u16_to_source_text};
22use libc::c_char;
23use rustc_hash::{FxBuildHasher, FxHashSet};
24use script_bindings::callback::OwnerWindow;
25use script_bindings::cell::DomRefCell;
26use script_bindings::cformat;
27use script_bindings::reflector::{DomObject, Reflector, reflect_dom_object_with_proto};
28use servo_constellation_traits::ConstellationInterest;
29use servo_url::ServoUrl;
30use style::str::HTML_SPACE_CHARACTERS;
31use stylo_atoms::Atom;
32
33use crate::conversions::Convert;
34use crate::dom::abortsignal::{AbortAlgorithm, RemovableDomEventListener};
35use crate::dom::beforeunloadevent::BeforeUnloadEvent;
36use crate::dom::bindings::callback::{CallbackContainer, CallbackFunction, ExceptionHandling};
37use crate::dom::bindings::codegen::Bindings::BeforeUnloadEventBinding::BeforeUnloadEventMethods;
38use crate::dom::bindings::codegen::Bindings::ErrorEventBinding::ErrorEventMethods;
39use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
40use crate::dom::bindings::codegen::Bindings::EventHandlerBinding::{
41 EventHandlerNonNull, OnBeforeUnloadEventHandlerNonNull, OnErrorEventHandlerNonNull,
42};
43use crate::dom::bindings::codegen::Bindings::EventListenerBinding::EventListener;
44use crate::dom::bindings::codegen::Bindings::EventTargetBinding::{
45 AddEventListenerOptions, EventListenerOptions, EventTargetMethods,
46};
47use crate::dom::bindings::codegen::Bindings::NodeBinding::GetRootNodeOptions;
48use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
49use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRoot_Binding::ShadowRootMethods;
50use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
51use crate::dom::bindings::codegen::GenericBindings::DocumentBinding::Document_Binding::DocumentMethods;
52use crate::dom::bindings::codegen::UnionTypes::{
53 AddEventListenerOptionsOrBoolean, EventListenerOptionsOrBoolean, EventOrString,
54};
55use crate::dom::bindings::error::{Error, Fallible, report_pending_exception};
56use crate::dom::bindings::inheritance::Castable;
57use crate::dom::bindings::reflector::DomGlobal;
58use crate::dom::bindings::root::{Dom, DomRoot};
59use crate::dom::bindings::str::DOMString;
60use crate::dom::bindings::trace::HashMapTracedValues;
61use crate::dom::csp::{CspReporting, InlineCheckType};
62use crate::dom::document::Document;
63use crate::dom::element::Element;
64use crate::dom::errorevent::ErrorEvent;
65use crate::dom::event::{Event, EventBubbles, EventCancelable, EventComposed};
66use crate::dom::globalscope::GlobalScope;
67use crate::dom::html::htmlformelement::FormControlElementHelpers;
68use crate::dom::indexeddb::idbdatabase::IDBDatabase;
69use crate::dom::indexeddb::idbrequest::IDBRequest;
70use crate::dom::indexeddb::idbtransaction::IDBTransaction;
71use crate::dom::node::virtualmethods::VirtualMethods;
72use crate::dom::node::{Node, NodeTraits};
73use crate::dom::shadowroot::ShadowRoot;
74use crate::dom::window::Window;
75use crate::dom::workerglobalscope::WorkerGlobalScope;
76use crate::realms::enter_auto_realm;
77use crate::runtime::script_runtime::IntroductionType;
78
79pub(crate) static CONTENT_EVENT_HANDLER_NAMES: LazyLock<FxHashSet<&str>> = LazyLock::new(|| {
83 FxHashSet::from_iter(include!(concat!(
84 env!("OUT_DIR"),
85 "/ContentEventHandlerNames.rs"
86 )))
87});
88
89#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)]
90#[expect(clippy::enum_variant_names)]
91pub(crate) enum CommonEventHandler {
92 EventHandler(#[conditional_malloc_size_of] Rc<EventHandlerNonNull>),
93
94 ErrorEventHandler(#[conditional_malloc_size_of] Rc<OnErrorEventHandlerNonNull>),
95
96 BeforeUnloadEventHandler(#[conditional_malloc_size_of] Rc<OnBeforeUnloadEventHandlerNonNull>),
97}
98
99impl CommonEventHandler {
100 fn parent(&self) -> &CallbackFunction<crate::DomTypeHolder> {
101 match *self {
102 CommonEventHandler::EventHandler(ref handler) => &handler.parent,
103 CommonEventHandler::ErrorEventHandler(ref handler) => &handler.parent,
104 CommonEventHandler::BeforeUnloadEventHandler(ref handler) => &handler.parent,
105 }
106 }
107}
108
109#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
110pub(crate) enum ListenerPhase {
111 Capturing,
112 Bubbling,
113}
114
115#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)]
117struct InternalRawUncompiledHandler {
118 source: DOMString,
119 #[no_trace]
120 url: ServoUrl,
121 line: usize,
122}
123
124#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)]
126enum InlineEventListener {
127 Uncompiled(InternalRawUncompiledHandler),
128 Compiled(CommonEventHandler),
129 Null,
130}
131
132fn get_compiled_handler(
136 cx: &mut JSContext,
137 inline_listener: &RefCell<InlineEventListener>,
138 owner: &EventTarget,
139 ty: &Atom,
140) -> Option<CommonEventHandler> {
141 let listener = mem::replace(
142 &mut *inline_listener.borrow_mut(),
143 InlineEventListener::Null,
144 );
145 let compiled = match listener {
146 InlineEventListener::Null => None,
147 InlineEventListener::Uncompiled(handler) => {
148 owner.get_compiled_event_handler(cx, handler, ty)
149 },
150 InlineEventListener::Compiled(handler) => Some(handler),
151 };
152 if let Some(ref compiled) = compiled {
153 *inline_listener.borrow_mut() = InlineEventListener::Compiled(compiled.clone());
154 }
155 compiled
156}
157
158#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)]
159enum EventListenerType {
160 Additive(#[conditional_malloc_size_of] Rc<EventListener>),
161 Inline(RefCell<InlineEventListener>),
162}
163
164impl EventListenerType {
165 fn get_compiled_listener(
166 &self,
167 cx: &mut JSContext,
168 owner: &EventTarget,
169 ty: &Atom,
170 ) -> Option<CompiledEventListener> {
171 match *self {
172 EventListenerType::Inline(ref inline) => {
173 get_compiled_handler(cx, inline, owner, ty).map(CompiledEventListener::Handler)
174 },
175 EventListenerType::Additive(ref listener) => {
176 Some(CompiledEventListener::Listener(listener.clone()))
177 },
178 }
179 }
180}
181
182pub(crate) enum CompiledEventListener {
185 Listener(Rc<EventListener>),
186 Handler(CommonEventHandler),
187}
188
189impl CompiledEventListener {
190 #[expect(unsafe_code)]
191 pub(crate) fn associated_global(&self) -> DomRoot<GlobalScope> {
192 let obj = match self {
193 CompiledEventListener::Listener(listener) => listener.callback(),
194 CompiledEventListener::Handler(CommonEventHandler::EventHandler(handler)) => {
195 handler.callback()
196 },
197 CompiledEventListener::Handler(CommonEventHandler::ErrorEventHandler(handler)) => {
198 handler.callback()
199 },
200 CompiledEventListener::Handler(CommonEventHandler::BeforeUnloadEventHandler(
201 handler,
202 )) => handler.callback(),
203 };
204 unsafe { GlobalScope::from_object(obj) }
205 }
206
207 pub(crate) fn call_or_handle_event(
209 &self,
210 cx: &mut JSContext,
211 object: &EventTarget,
212 event: &Event,
213 exception_handle: ExceptionHandling,
214 ) -> Fallible<()> {
215 match *self {
217 CompiledEventListener::Listener(ref listener) => {
218 listener.HandleEvent_(cx, object, event, exception_handle)
219 },
220 CompiledEventListener::Handler(ref handler) => {
221 match *handler {
222 CommonEventHandler::ErrorEventHandler(ref handler) => {
223 if let Some(event) = event.downcast::<ErrorEvent>() &&
224 (object.is::<Window>() || object.is::<WorkerGlobalScope>())
225 {
226 rooted!(&in(cx) let mut error: JSVal);
227 event.Error(error.handle_mut());
228 rooted!(&in(cx) let mut rooted_return_value: JSVal);
229 let return_value = handler.Call_(
230 cx,
231 object,
232 EventOrString::String(event.Message()),
233 Some(event.Filename()),
234 Some(event.Lineno()),
235 Some(event.Colno()),
236 Some(error.handle()),
237 rooted_return_value.handle_mut(),
238 exception_handle,
239 );
240 if let Ok(()) = return_value &&
242 rooted_return_value.handle().is_boolean() &&
243 rooted_return_value.handle().to_boolean()
244 {
245 event.upcast::<Event>().PreventDefault();
246 }
247 return return_value;
248 }
249
250 rooted!(&in(cx) let mut rooted_return_value: JSVal);
251 handler.Call_(
252 cx,
253 object,
254 EventOrString::Event(DomRoot::from_ref(event)),
255 None,
256 None,
257 None,
258 None,
259 rooted_return_value.handle_mut(),
260 exception_handle,
261 )
262 },
263
264 CommonEventHandler::BeforeUnloadEventHandler(ref handler) => {
265 if let Some(event) = event.downcast::<BeforeUnloadEvent>() {
266 match handler.Call_(
268 cx,
269 object,
270 event.upcast::<Event>(),
271 exception_handle,
272 ) {
273 Ok(value) => {
274 let rv = event.ReturnValue();
275 if let Some(v) = value {
276 if rv.is_empty() {
277 event.SetReturnValue(v);
278 }
279 event.upcast::<Event>().PreventDefault();
280 }
281 Ok(())
282 },
283 Err(err) => Err(err),
284 }
285 } else {
286 handler
288 .Call_(cx, object, event.upcast::<Event>(), exception_handle)
289 .map(|_| ())
290 }
291 },
292
293 CommonEventHandler::EventHandler(ref handler) => {
294 rooted!(&in(cx) let mut rooted_return_value: JSVal);
295 match handler.Call_(
296 cx,
297 object,
298 event,
299 rooted_return_value.handle_mut(),
300 exception_handle,
301 ) {
302 Ok(()) => {
303 let value = rooted_return_value.handle();
304
305 let should_cancel = value.is_boolean() && !value.to_boolean();
307
308 if should_cancel {
309 event.PreventDefault();
313 }
314 Ok(())
315 },
316 Err(err) => Err(err),
317 }
318 },
319 }
320 },
321 }
322 }
323}
324
325#[derive(Clone, DenyPublicFields, JSTraceable, MallocSizeOf)]
328pub(crate) struct EventListenerEntry {
330 phase: ListenerPhase,
331 listener: EventListenerType,
332 once: bool,
333 passive: bool,
334 removed: bool,
335}
336
337impl EventListenerEntry {
338 pub(crate) fn phase(&self) -> ListenerPhase {
339 self.phase
340 }
341
342 pub(crate) fn once(&self) -> bool {
343 self.once
344 }
345
346 pub(crate) fn removed(&self) -> bool {
347 self.removed
348 }
349
350 pub(crate) fn get_compiled_listener(
352 &self,
353 cx: &mut JSContext,
354 owner: &EventTarget,
355 ty: &Atom,
356 ) -> Option<CompiledEventListener> {
357 self.listener.get_compiled_listener(cx, owner, ty)
358 }
359}
360
361impl std::cmp::PartialEq for EventListenerEntry {
362 fn eq(&self, other: &Self) -> bool {
363 self.phase == other.phase && self.listener == other.listener
364 }
365}
366
367#[derive(Clone, JSTraceable, MallocSizeOf)]
368pub(crate) struct EventListeners(
370 #[conditional_malloc_size_of] Vec<Rc<RefCell<EventListenerEntry>>>,
371);
372
373impl Deref for EventListeners {
374 type Target = Vec<Rc<RefCell<EventListenerEntry>>>;
375 fn deref(&self) -> &Vec<Rc<RefCell<EventListenerEntry>>> {
376 &self.0
377 }
378}
379
380impl DerefMut for EventListeners {
381 fn deref_mut(&mut self) -> &mut Vec<Rc<RefCell<EventListenerEntry>>> {
382 &mut self.0
383 }
384}
385
386impl EventListeners {
387 fn get_inline_listener(
389 &self,
390 cx: &mut JSContext,
391 owner: &EventTarget,
392 ty: &Atom,
393 ) -> Option<CommonEventHandler> {
394 for entry in &self.0 {
395 if let EventListenerType::Inline(ref inline) = entry.borrow().listener {
396 return get_compiled_handler(cx, inline, owner, ty);
398 }
399 }
400
401 None
403 }
404
405 fn has_listeners(&self) -> bool {
406 !self.0.is_empty()
407 }
408}
409
410type EventListenerMap = HashMapTracedValues<Atom, EventListeners, FxBuildHasher>;
412
413#[dom_struct]
414pub struct EventTarget {
415 reflector_: Reflector,
416 handlers: DomRefCell<Option<Box<EventListenerMap>>>,
417}
418
419impl EventTarget {
420 pub(crate) fn new_inherited() -> EventTarget {
421 EventTarget {
422 reflector_: Reflector::new(),
423 handlers: DomRefCell::new(None),
424 }
425 }
426
427 fn ensure_handlers(&self) -> RefMut<'_, Box<EventListenerMap>> {
429 RefMut::map(self.handlers.borrow_mut(), |handlers| {
430 handlers.get_or_insert_with(Box::default)
431 })
432 }
433
434 fn new(
435 cx: &mut JSContext,
436 global: &GlobalScope,
437 proto: Option<HandleObject>,
438 ) -> DomRoot<EventTarget> {
439 reflect_dom_object_with_proto(cx, Box::new(EventTarget::new_inherited()), global, proto)
440 }
441
442 fn interest_for_event_type(&self, ty: &Atom) -> Option<ConstellationInterest> {
446 if self.is::<Window>() && *ty == atom!("storage") {
447 return Some(ConstellationInterest::StorageEvent);
448 }
449 None
450 }
451
452 fn notify_listener_added(&self, ty: &Atom) {
454 if let Some(interest) = self.interest_for_event_type(ty) {
455 self.global().register_interest(interest);
456 }
457 }
458
459 fn notify_listener_removed(&self, ty: &Atom) {
461 if let Some(interest) = self.interest_for_event_type(ty) {
462 self.global().unregister_interest(interest);
463 }
464 }
465
466 pub(crate) fn has_listeners_for(&self, type_: &Atom) -> bool {
469 match self
470 .handlers
471 .borrow()
472 .as_ref()
473 .and_then(|handlers| handlers.get(type_))
474 {
475 Some(listeners) => listeners.has_listeners(),
476 None => false,
477 }
478 }
479
480 pub(crate) fn get_listeners_for(&self, type_: &Atom) -> EventListeners {
481 self.handlers
482 .borrow()
483 .as_ref()
484 .and_then(|handlers| handlers.get(type_))
485 .map_or(EventListeners(vec![]), |listeners| listeners.clone())
486 }
487
488 pub(crate) fn remove_all_listeners(&self) {
489 let Some(handlers) = self.handlers.borrow_mut().take() else {
492 return;
493 };
494
495 for (ty, entries) in handlers.iter() {
496 entries
497 .iter()
498 .for_each(|entry| entry.borrow_mut().removed = true);
499 self.notify_listener_removed(ty);
500 }
501 }
502
503 fn default_passive_value(&self, ty: &Atom) -> bool {
505 let event_type = ty.trim_matches(HTML_SPACE_CHARACTERS);
507
508 let matches_event_type = event_type.eq_ignore_ascii_case("touchstart") ||
510 event_type.eq_ignore_ascii_case("touchmove") ||
511 event_type.eq_ignore_ascii_case("wheel") ||
512 event_type.eq_ignore_ascii_case("mousewheel");
513
514 if !matches_event_type {
515 return false;
516 }
517
518 if self.is::<Window>() {
520 return true;
521 }
522
523 if let Some(node) = self.downcast::<Node>() {
525 let node_document = node.owner_document();
526 let event_target = self.upcast::<EventTarget>();
527
528 return event_target == node_document.upcast::<EventTarget>()
530 || node_document.GetDocumentElement().is_some_and(|n| n.upcast::<EventTarget>() == event_target)
532 || node_document.GetBody().is_some_and(|n| n.upcast::<EventTarget>() == event_target);
534 }
535
536 false
537 }
538
539 fn set_inline_event_listener(&self, ty: Atom, listener: Option<InlineEventListener>) {
541 let mut handlers = self.ensure_handlers();
542 let entries = match handlers.entry(ty.clone()) {
543 Occupied(entry) => entry.into_mut(),
544 Vacant(entry) => entry.insert(EventListeners(vec![])),
545 };
546
547 let idx = entries
548 .iter()
549 .position(|entry| matches!(entry.borrow().listener, EventListenerType::Inline(_)));
550
551 match idx {
552 Some(idx) => match listener {
553 Some(listener) => {
556 entries[idx].borrow_mut().listener = EventListenerType::Inline(listener.into());
557 },
558 None => {
559 entries.remove(idx).borrow_mut().removed = true;
560 self.notify_listener_removed(&ty);
561 },
562 },
563 None => {
564 if let Some(listener) = listener {
565 entries.push(Rc::new(RefCell::new(EventListenerEntry {
566 phase: ListenerPhase::Bubbling,
567 listener: EventListenerType::Inline(listener.into()),
568 once: false,
569 passive: self.default_passive_value(&ty),
570 removed: false,
571 })));
572 self.notify_listener_added(&ty)
573 }
574 },
575 }
576 }
577
578 pub(crate) fn remove_listener(&self, ty: &Atom, entry: &Rc<RefCell<EventListenerEntry>>) {
579 let mut handlers = self.handlers.borrow_mut();
580
581 if let Some(handlers) = handlers.as_mut() &&
582 let Some(entries) = handlers.get_mut(ty) &&
583 let Some(position) = entries.iter().position(|e| *e == *entry)
584 {
585 entries.remove(position).borrow_mut().removed = true;
586 self.notify_listener_removed(ty);
587 }
588 }
589
590 pub(crate) fn is_passive(&self, listener: &Rc<RefCell<EventListenerEntry>>) -> bool {
592 listener.borrow().passive
593 }
594
595 pub(crate) fn has_non_passive_listener(&self, type_: &Atom) -> bool {
597 self.get_listeners_for(type_)
598 .iter()
599 .any(|listener| !self.is_passive(listener))
600 }
601
602 fn get_inline_event_listener(
603 &self,
604 cx: &mut JSContext,
605 ty: &Atom,
606 ) -> Option<CommonEventHandler> {
607 let handlers = self.handlers.borrow();
608 handlers
609 .as_ref()?
610 .get(ty)
611 .and_then(|entry| entry.get_inline_listener(cx, self, ty))
612 }
613
614 pub(crate) fn set_event_handler_uncompiled(
617 &self,
618 cx: &mut JSContext,
619 url: ServoUrl,
620 line: usize,
621 ty: &str,
622 source: &str,
623 ) {
624 if let Some(element) = self.downcast::<Element>() {
625 let doc = element.owner_document();
626 let global = &doc.global();
627 if global
628 .get_csp_list()
629 .should_elements_inline_type_behavior_be_blocked(
630 cx,
631 global,
632 element.upcast(),
633 InlineCheckType::ScriptAttribute,
634 source,
635 line as u32,
636 )
637 {
638 return;
639 }
640 };
641
642 let handler = InternalRawUncompiledHandler {
643 source: DOMString::from(source),
644 line,
645 url,
646 };
647 self.set_inline_event_listener(
648 Atom::from(ty),
649 Some(InlineEventListener::Uncompiled(handler)),
650 );
651 }
652
653 #[expect(unsafe_code)]
656 fn get_compiled_event_handler(
657 &self,
658 cx: &mut JSContext,
659 handler: InternalRawUncompiledHandler,
660 ty: &Atom,
661 ) -> Option<CommonEventHandler> {
662 let element = self.downcast::<Element>();
664 let document = match element {
665 Some(element) => element.owner_document(),
666 None => self.downcast::<Window>().unwrap().Document(),
667 };
668
669 if !document.scripting_enabled() {
671 return None;
672 }
673
674 let body: Vec<u16> = handler.source.str().encode_utf16().collect();
676
677 let form_owner = element
681 .and_then(|e| e.as_maybe_form_control())
682 .and_then(|f| f.form_owner());
683
684 let window = document.window();
692 let mut realm = enter_auto_realm(cx, window);
693 let cx = &mut realm.current_realm();
694
695 let name = CString::new(format!("on{}", &**ty)).unwrap();
698
699 const ARG_NAMES: &[*const c_char] = &[c"event".as_ptr()];
701 const ERROR_ARG_NAMES: &[*const c_char] = &[
702 c"event".as_ptr(),
703 c"source".as_ptr(),
704 c"lineno".as_ptr(),
705 c"colno".as_ptr(),
706 c"error".as_ptr(),
707 ];
708 let is_error = ty == &atom!("error") && self.is::<Window>();
709 let args = if is_error { ERROR_ARG_NAMES } else { ARG_NAMES };
710
711 let url = cformat!("{}", handler.url);
712 let mut options = CompileOptionsWrapper::new(cx, url, handler.line as u32);
713 options.set_introduction_type(IntroductionType::EVENT_HANDLER);
714
715 let scopechain = js::rust::EnvironmentChain::new(cx, SupportUnscopables::Yes);
717
718 if let Some(element) = element {
719 scopechain.append(document.reflector().get_jsobject().get());
720 if let Some(form_owner) = form_owner {
721 scopechain.append(form_owner.reflector().get_jsobject().get());
722 }
723 scopechain.append(element.reflector().get_jsobject().get());
724 }
725
726 rooted!(&in(cx) let mut handler = unsafe {
727 CompileFunction(
728 cx,
729 scopechain.get(),
730 options.ptr,
731 name.as_ptr(),
732 args.len() as u32,
733 args.as_ptr(),
734 &mut transform_u16_to_source_text(&body),
735 )
736 });
737 if handler.get().is_null() {
738 let mut realm = enter_auto_realm(cx, self);
740 report_pending_exception(&mut realm.current_realm());
741 return None;
742 }
743
744 let funobj = unsafe { JS_GetFunctionObject(handler.get()) };
750 assert!(!funobj.is_null());
751 if is_error {
753 Some(CommonEventHandler::ErrorEventHandler(unsafe {
754 OnErrorEventHandlerNonNull::new(cx, funobj)
755 }))
756 } else if ty == &atom!("beforeunload") {
757 Some(CommonEventHandler::BeforeUnloadEventHandler(unsafe {
758 OnBeforeUnloadEventHandlerNonNull::new(cx, funobj)
759 }))
760 } else {
761 Some(CommonEventHandler::EventHandler(unsafe {
762 EventHandlerNonNull::new(cx, funobj)
763 }))
764 }
765 }
766
767 #[expect(unsafe_code)]
768 pub(crate) fn set_event_handler_common<T: CallbackContainer<crate::DomTypeHolder>>(
769 &self,
770 cx: &mut JSContext,
771 ty: &str,
772 listener: Option<Rc<T>>,
773 ) {
774 let event_listener = listener.map(|listener| {
775 InlineEventListener::Compiled(CommonEventHandler::EventHandler(unsafe {
776 EventHandlerNonNull::new(cx, listener.callback())
777 }))
778 });
779 self.set_inline_event_listener(Atom::from(ty), event_listener);
780 }
781
782 #[expect(unsafe_code)]
783 pub(crate) fn set_error_event_handler<T: CallbackContainer<crate::DomTypeHolder>>(
784 &self,
785 cx: &mut JSContext,
786 ty: &str,
787 listener: Option<Rc<T>>,
788 ) {
789 let event_listener = listener.map(|listener| {
790 InlineEventListener::Compiled(CommonEventHandler::ErrorEventHandler(unsafe {
791 OnErrorEventHandlerNonNull::new(cx, listener.callback())
792 }))
793 });
794 self.set_inline_event_listener(Atom::from(ty), event_listener);
795 }
796
797 #[expect(unsafe_code)]
798 pub(crate) fn set_beforeunload_event_handler<T: CallbackContainer<crate::DomTypeHolder>>(
799 &self,
800 cx: &mut JSContext,
801 ty: &str,
802 listener: Option<Rc<T>>,
803 ) {
804 let event_listener = listener.map(|listener| {
805 InlineEventListener::Compiled(CommonEventHandler::BeforeUnloadEventHandler(unsafe {
806 OnBeforeUnloadEventHandlerNonNull::new(cx, listener.callback())
807 }))
808 });
809 self.set_inline_event_listener(Atom::from(ty), event_listener);
810 }
811
812 #[expect(unsafe_code)]
813 pub(crate) fn get_event_handler_common<T: CallbackContainer<crate::DomTypeHolder>>(
814 &self,
815 cx: &mut JSContext,
816 ty: &str,
817 ) -> Option<Rc<T>> {
818 let listener = self.get_inline_event_listener(cx, &Atom::from(ty));
819 unsafe {
820 listener.map(|listener| {
821 CallbackContainer::new(cx, listener.parent().callback_holder().get())
822 })
823 }
824 }
825
826 pub(crate) fn has_handlers(&self) -> bool {
827 self.handlers
828 .borrow()
829 .as_ref()
830 .is_some_and(|handlers| !handlers.is_empty())
831 }
832
833 pub(crate) fn fire_event(&self, cx: &mut js::context::JSContext, name: Atom) -> bool {
835 self.fire_event_with_params(
836 cx,
837 name,
838 EventBubbles::DoesNotBubble,
839 EventCancelable::NotCancelable,
840 EventComposed::NotComposed,
841 )
842 }
843
844 pub(crate) fn fire_bubbling_event(&self, cx: &mut js::context::JSContext, name: Atom) -> bool {
846 self.fire_event_with_params(
847 cx,
848 name,
849 EventBubbles::Bubbles,
850 EventCancelable::NotCancelable,
851 EventComposed::NotComposed,
852 )
853 }
854
855 pub(crate) fn fire_cancelable_event(
857 &self,
858 cx: &mut js::context::JSContext,
859 name: Atom,
860 ) -> bool {
861 self.fire_event_with_params(
862 cx,
863 name,
864 EventBubbles::DoesNotBubble,
865 EventCancelable::Cancelable,
866 EventComposed::NotComposed,
867 )
868 }
869
870 pub(crate) fn fire_bubbling_cancelable_event(
872 &self,
873 cx: &mut js::context::JSContext,
874 name: Atom,
875 ) -> bool {
876 self.fire_event_with_params(
877 cx,
878 name,
879 EventBubbles::Bubbles,
880 EventCancelable::Cancelable,
881 EventComposed::NotComposed,
882 )
883 }
884
885 pub(crate) fn fire_event_with_params(
887 &self,
888 cx: &mut js::context::JSContext,
889 name: Atom,
890 bubbles: EventBubbles,
891 cancelable: EventCancelable,
892 composed: EventComposed,
893 ) -> bool {
894 let event = Event::new(cx, &self.global(), name, bubbles, cancelable);
895 event.set_composed(composed.into());
896 event.fire(cx, self)
897 }
898
899 pub(crate) fn add_event_listener(
902 &self,
903 ty: DOMString,
904 listener: Option<Rc<EventListener>>,
905 options: AddEventListenerOptions,
906 ) {
907 if let Some(signal) = options.signal.as_ref() {
908 if signal.aborted() {
910 return;
911 }
912 signal.add(&AbortAlgorithm::DomEventListener(
914 RemovableDomEventListener {
915 event_target: Dom::from_ref(self),
916 ty: ty.clone(),
917 listener: listener.clone(),
918 options: options.parent.clone(),
919 },
920 ));
921 }
922 let listener = match listener {
924 Some(l) => l,
925 None => return,
926 };
927 let ty = Atom::from(ty);
928 let mut handlers = self.ensure_handlers();
929 let entries = match handlers.entry(ty.clone()) {
930 Occupied(entry) => entry.into_mut(),
931 Vacant(entry) => entry.insert(EventListeners(vec![])),
932 };
933
934 let phase = if options.parent.capture {
935 ListenerPhase::Capturing
936 } else {
937 ListenerPhase::Bubbling
938 };
939 let new_entry = Rc::new(RefCell::new(EventListenerEntry {
941 phase,
942 listener: EventListenerType::Additive(listener),
943 once: options.once,
944 passive: options.passive.unwrap_or(self.default_passive_value(&ty)),
945 removed: false,
946 }));
947
948 if !entries.contains(&new_entry) {
952 entries.push(new_entry);
953 self.notify_listener_added(&ty);
954 }
955 }
956
957 pub(crate) fn remove_event_listener(
960 &self,
961 ty: DOMString,
962 listener: &Option<Rc<EventListener>>,
963 options: &EventListenerOptions,
964 ) {
965 let Some(listener) = listener else {
966 return;
967 };
968 let ty_atom = Atom::from(ty);
969 let mut handlers = self.handlers.borrow_mut();
970 if let Some(handlers) = handlers.as_mut() &&
971 let Some(entries) = handlers.get_mut(&ty_atom)
972 {
973 let phase = if options.capture {
974 ListenerPhase::Capturing
975 } else {
976 ListenerPhase::Bubbling
977 };
978 let listener_type = EventListenerType::Additive(listener.clone());
979 if let Some(position) = entries
980 .iter()
981 .position(|e| e.borrow().listener == listener_type && e.borrow().phase == phase)
982 {
983 entries.remove(position).borrow_mut().removed = true;
985 self.notify_listener_removed(&ty_atom);
986 }
987 }
988 }
989
990 pub(crate) fn get_the_parent(&self, event: &Event) -> Option<DomRoot<EventTarget>> {
992 if let Some(document) = self.downcast::<Document>() {
993 if event.type_() == atom!("load") || !document.has_browsing_context() {
994 return None;
995 } else {
996 return Some(DomRoot::from_ref(document.window().upcast::<EventTarget>()));
997 }
998 }
999
1000 if let Some(shadow_root) = self.downcast::<ShadowRoot>() {
1001 if event.should_pass_shadow_boundary(shadow_root) {
1002 let host = shadow_root.Host();
1003 return Some(DomRoot::from_ref(host.upcast::<EventTarget>()));
1004 } else {
1005 return None;
1006 }
1007 }
1008
1009 if let Some(node) = self.downcast::<Node>() {
1010 return node.assigned_slot().map(DomRoot::upcast).or_else(|| {
1013 node.GetParentNode()
1014 .map(|parent| DomRoot::from_ref(parent.upcast::<EventTarget>()))
1015 });
1016 }
1017
1018 if let Some(request) = self.downcast::<IDBRequest>() {
1021 return request
1022 .transaction()
1023 .map(|tx| DomRoot::from_ref(tx.upcast::<EventTarget>()));
1024 }
1025
1026 if let Some(transaction) = self.downcast::<IDBTransaction>() {
1028 return Some(DomRoot::from_ref(
1029 transaction.get_db().upcast::<EventTarget>(),
1030 ));
1031 }
1032
1033 if self.is::<IDBDatabase>() {
1035 return None;
1036 }
1037
1038 None
1039 }
1040
1041 pub(crate) fn retarget(&self, b: &Self) -> DomRoot<EventTarget> {
1045 let mut a = DomRoot::from_ref(self);
1047 loop {
1048 let Some(a_node) = a.downcast::<Node>() else {
1054 return a;
1055 };
1056 let a_root = a_node.GetRootNode(&GetRootNodeOptions::empty());
1057 if !a_root.is::<ShadowRoot>() {
1058 return a;
1059 }
1060 if let Some(b_node) = b.downcast::<Node>() &&
1061 a_root.is_shadow_including_inclusive_ancestor_of(b_node)
1062 {
1063 return a;
1064 }
1065
1066 a = DomRoot::from_ref(
1068 a_root
1069 .downcast::<ShadowRoot>()
1070 .unwrap()
1071 .Host()
1072 .upcast::<EventTarget>(),
1073 );
1074 }
1075 }
1076
1077 pub(crate) fn is_content_event_handler(name: &str) -> bool {
1079 CONTENT_EVENT_HANDLER_NAMES.contains(&name)
1080 }
1081
1082 pub(crate) fn summarize_event_listeners_for_devtools(&self) -> Vec<EventListenerInfo> {
1083 let handlers = self.handlers.borrow();
1084 let Some(handlers) = handlers.as_ref() else {
1085 return Vec::new();
1086 };
1087 let mut listener_infos = Vec::with_capacity(handlers.0.len());
1088 for (event_type, event_listeners) in &handlers.0 {
1089 for event_listener in event_listeners.iter() {
1090 let event_listener_entry = event_listener.borrow();
1091 listener_infos.push(EventListenerInfo {
1092 event_type: event_type.to_string(),
1093 capturing: event_listener_entry.phase() == ListenerPhase::Capturing,
1094 });
1095 }
1096 }
1097
1098 listener_infos
1099 }
1100}
1101
1102impl EventTargetMethods<crate::DomTypeHolder> for EventTarget {
1103 fn Constructor(
1105 cx: &mut JSContext,
1106 global: &GlobalScope,
1107 proto: Option<HandleObject>,
1108 ) -> Fallible<DomRoot<EventTarget>> {
1109 Ok(EventTarget::new(cx, global, proto))
1110 }
1111
1112 fn AddEventListener(
1114 &self,
1115 ty: DOMString,
1116 listener: Option<Rc<EventListener>>,
1117 options: AddEventListenerOptionsOrBoolean,
1118 ) {
1119 self.add_event_listener(ty, listener, options.convert())
1120 }
1121
1122 fn RemoveEventListener(
1124 &self,
1125 ty: DOMString,
1126 listener: Option<Rc<EventListener>>,
1127 options: EventListenerOptionsOrBoolean,
1128 ) {
1129 self.remove_event_listener(ty, &listener, &options.convert())
1130 }
1131
1132 fn DispatchEvent(&self, cx: &mut JSContext, event: &Event) -> Fallible<bool> {
1134 if event.dispatching() || !event.initialized() {
1135 return Err(Error::InvalidState(None));
1136 }
1137 event.set_trusted(false);
1138 Ok(event.dispatch(cx, self, false))
1139 }
1140}
1141
1142impl VirtualMethods for EventTarget {
1143 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1144 None
1145 }
1146}
1147
1148impl Convert<AddEventListenerOptions> for AddEventListenerOptionsOrBoolean {
1149 fn convert(self) -> AddEventListenerOptions {
1151 match self {
1154 AddEventListenerOptionsOrBoolean::AddEventListenerOptions(options) => options,
1156 AddEventListenerOptionsOrBoolean::Boolean(capture) => AddEventListenerOptions {
1157 parent: EventListenerOptions { capture },
1158 once: false,
1160 passive: None,
1162 signal: None,
1163 },
1164 }
1165 }
1166}
1167
1168impl Convert<EventListenerOptions> for EventListenerOptionsOrBoolean {
1169 fn convert(self) -> EventListenerOptions {
1170 match self {
1171 EventListenerOptionsOrBoolean::EventListenerOptions(options) => options,
1172 EventListenerOptionsOrBoolean::Boolean(capture) => EventListenerOptions { capture },
1173 }
1174 }
1175}
1176
1177impl OwnerWindow<crate::DomTypeHolder> for EventTarget {
1178 fn owner_window(&self) -> Option<DomRoot<Window>> {
1179 self.downcast::<Node>().map(|node| node.owner_window())
1180 }
1181}