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