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::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
410#[dom_struct]
411pub struct EventTarget {
412 reflector_: Reflector,
413 handlers: DomRefCell<HashMapTracedValues<Atom, EventListeners, FxBuildHasher>>,
414}
415
416impl EventTarget {
417 pub(crate) fn new_inherited() -> EventTarget {
418 EventTarget {
419 reflector_: Reflector::new(),
420 handlers: DomRefCell::new(Default::default()),
421 }
422 }
423
424 fn new(
425 cx: &mut JSContext,
426 global: &GlobalScope,
427 proto: Option<HandleObject>,
428 ) -> DomRoot<EventTarget> {
429 reflect_dom_object_with_proto(cx, Box::new(EventTarget::new_inherited()), global, proto)
430 }
431
432 fn interest_for_event_type(&self, ty: &Atom) -> Option<ConstellationInterest> {
436 if self.is::<Window>() && *ty == atom!("storage") {
437 return Some(ConstellationInterest::StorageEvent);
438 }
439 None
440 }
441
442 fn notify_listener_added(&self, ty: &Atom) {
444 if let Some(interest) = self.interest_for_event_type(ty) {
445 self.global().register_interest(interest);
446 }
447 }
448
449 fn notify_listener_removed(&self, ty: &Atom) {
451 if let Some(interest) = self.interest_for_event_type(ty) {
452 self.global().unregister_interest(interest);
453 }
454 }
455
456 pub(crate) fn has_listeners_for(&self, type_: &Atom) -> bool {
459 match self.handlers.borrow().get(type_) {
460 Some(listeners) => listeners.has_listeners(),
461 None => false,
462 }
463 }
464
465 pub(crate) fn get_listeners_for(&self, type_: &Atom) -> EventListeners {
466 self.handlers
467 .borrow()
468 .get(type_)
469 .map_or(EventListeners(vec![]), |listeners| listeners.clone())
470 }
471
472 pub(crate) fn remove_all_listeners(&self) {
473 let mut handlers = self.handlers.borrow_mut();
474 for (ty, entries) in handlers.iter() {
475 entries
476 .iter()
477 .for_each(|entry| entry.borrow_mut().removed = true);
478 self.notify_listener_removed(ty);
479 }
480
481 *handlers = Default::default();
482 }
483
484 fn default_passive_value(&self, ty: &Atom) -> bool {
486 let event_type = ty.trim_matches(HTML_SPACE_CHARACTERS);
488
489 let matches_event_type = event_type.eq_ignore_ascii_case("touchstart") ||
491 event_type.eq_ignore_ascii_case("touchmove") ||
492 event_type.eq_ignore_ascii_case("wheel") ||
493 event_type.eq_ignore_ascii_case("mousewheel");
494
495 if !matches_event_type {
496 return false;
497 }
498
499 if self.is::<Window>() {
501 return true;
502 }
503
504 if let Some(node) = self.downcast::<Node>() {
506 let node_document = node.owner_document();
507 let event_target = self.upcast::<EventTarget>();
508
509 return event_target == node_document.upcast::<EventTarget>()
511 || node_document.GetDocumentElement().is_some_and(|n| n.upcast::<EventTarget>() == event_target)
513 || node_document.GetBody().is_some_and(|n| n.upcast::<EventTarget>() == event_target);
515 }
516
517 false
518 }
519
520 fn set_inline_event_listener(&self, ty: Atom, listener: Option<InlineEventListener>) {
522 let mut handlers = self.handlers.borrow_mut();
523 let entries = match handlers.entry(ty.clone()) {
524 Occupied(entry) => entry.into_mut(),
525 Vacant(entry) => entry.insert(EventListeners(vec![])),
526 };
527
528 let idx = entries
529 .iter()
530 .position(|entry| matches!(entry.borrow().listener, EventListenerType::Inline(_)));
531
532 match idx {
533 Some(idx) => match listener {
534 Some(listener) => {
537 entries[idx].borrow_mut().listener = EventListenerType::Inline(listener.into());
538 },
539 None => {
540 entries.remove(idx).borrow_mut().removed = true;
541 self.notify_listener_removed(&ty);
542 },
543 },
544 None => {
545 if let Some(listener) = listener {
546 entries.push(Rc::new(RefCell::new(EventListenerEntry {
547 phase: ListenerPhase::Bubbling,
548 listener: EventListenerType::Inline(listener.into()),
549 once: false,
550 passive: self.default_passive_value(&ty),
551 removed: false,
552 })));
553 self.notify_listener_added(&ty)
554 }
555 },
556 }
557 }
558
559 pub(crate) fn remove_listener(&self, ty: &Atom, entry: &Rc<RefCell<EventListenerEntry>>) {
560 let mut handlers = self.handlers.borrow_mut();
561
562 if let Some(entries) = handlers.get_mut(ty) &&
563 let Some(position) = entries.iter().position(|e| *e == *entry)
564 {
565 entries.remove(position).borrow_mut().removed = true;
566 self.notify_listener_removed(ty);
567 }
568 }
569
570 pub(crate) fn is_passive(&self, listener: &Rc<RefCell<EventListenerEntry>>) -> bool {
572 listener.borrow().passive
573 }
574
575 pub(crate) fn has_non_passive_listener(&self, type_: &Atom) -> bool {
577 self.get_listeners_for(type_)
578 .iter()
579 .any(|listener| !self.is_passive(listener))
580 }
581
582 fn get_inline_event_listener(
583 &self,
584 cx: &mut JSContext,
585 ty: &Atom,
586 ) -> Option<CommonEventHandler> {
587 let handlers = self.handlers.borrow();
588 handlers
589 .get(ty)
590 .and_then(|entry| entry.get_inline_listener(cx, self, ty))
591 }
592
593 pub(crate) fn set_event_handler_uncompiled(
596 &self,
597 cx: &mut JSContext,
598 url: ServoUrl,
599 line: usize,
600 ty: &str,
601 source: &str,
602 ) {
603 if let Some(element) = self.downcast::<Element>() {
604 let doc = element.owner_document();
605 let global = &doc.global();
606 if global
607 .get_csp_list()
608 .should_elements_inline_type_behavior_be_blocked(
609 cx,
610 global,
611 element.upcast(),
612 InlineCheckType::ScriptAttribute,
613 source,
614 line as u32,
615 )
616 {
617 return;
618 }
619 };
620
621 let handler = InternalRawUncompiledHandler {
622 source: DOMString::from(source),
623 line,
624 url,
625 };
626 self.set_inline_event_listener(
627 Atom::from(ty),
628 Some(InlineEventListener::Uncompiled(handler)),
629 );
630 }
631
632 #[expect(unsafe_code)]
635 fn get_compiled_event_handler(
636 &self,
637 cx: &mut JSContext,
638 handler: InternalRawUncompiledHandler,
639 ty: &Atom,
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 mut realm = enter_auto_realm(cx, window);
672 let cx = &mut realm.current_realm();
673
674 let name = CString::new(format!("on{}", &**ty)).unwrap();
677
678 const ARG_NAMES: &[*const c_char] = &[c"event".as_ptr()];
680 const ERROR_ARG_NAMES: &[*const c_char] = &[
681 c"event".as_ptr(),
682 c"source".as_ptr(),
683 c"lineno".as_ptr(),
684 c"colno".as_ptr(),
685 c"error".as_ptr(),
686 ];
687 let is_error = ty == &atom!("error") && self.is::<Window>();
688 let args = if is_error { ERROR_ARG_NAMES } else { ARG_NAMES };
689
690 let url = cformat!("{}", handler.url);
691 let mut options = CompileOptionsWrapper::new(cx, url, handler.line as u32);
692 options.set_introduction_type(IntroductionType::EVENT_HANDLER);
693
694 let scopechain = js::rust::EnvironmentChain::new(cx, SupportUnscopables::Yes);
696
697 if let Some(element) = element {
698 scopechain.append(document.reflector().get_jsobject().get());
699 if let Some(form_owner) = form_owner {
700 scopechain.append(form_owner.reflector().get_jsobject().get());
701 }
702 scopechain.append(element.reflector().get_jsobject().get());
703 }
704
705 rooted!(&in(cx) let mut handler = unsafe {
706 CompileFunction(
707 cx,
708 scopechain.get(),
709 options.ptr,
710 name.as_ptr(),
711 args.len() as u32,
712 args.as_ptr(),
713 &mut transform_u16_to_source_text(&body),
714 )
715 });
716 if handler.get().is_null() {
717 let mut realm = enter_auto_realm(cx, self);
719 report_pending_exception(&mut realm.current_realm());
720 return None;
721 }
722
723 let funobj = unsafe { JS_GetFunctionObject(handler.get()) };
729 assert!(!funobj.is_null());
730 if is_error {
732 Some(CommonEventHandler::ErrorEventHandler(unsafe {
733 OnErrorEventHandlerNonNull::new(cx, funobj)
734 }))
735 } else if ty == &atom!("beforeunload") {
736 Some(CommonEventHandler::BeforeUnloadEventHandler(unsafe {
737 OnBeforeUnloadEventHandlerNonNull::new(cx, funobj)
738 }))
739 } else {
740 Some(CommonEventHandler::EventHandler(unsafe {
741 EventHandlerNonNull::new(cx, funobj)
742 }))
743 }
744 }
745
746 #[expect(unsafe_code)]
747 pub(crate) fn set_event_handler_common<T: CallbackContainer<crate::DomTypeHolder>>(
748 &self,
749 cx: &mut JSContext,
750 ty: &str,
751 listener: Option<Rc<T>>,
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 cx: &mut JSContext,
765 ty: &str,
766 listener: Option<Rc<T>>,
767 ) {
768 let event_listener = listener.map(|listener| {
769 InlineEventListener::Compiled(CommonEventHandler::ErrorEventHandler(unsafe {
770 OnErrorEventHandlerNonNull::new(cx, listener.callback())
771 }))
772 });
773 self.set_inline_event_listener(Atom::from(ty), event_listener);
774 }
775
776 #[expect(unsafe_code)]
777 pub(crate) fn set_beforeunload_event_handler<T: CallbackContainer<crate::DomTypeHolder>>(
778 &self,
779 cx: &mut JSContext,
780 ty: &str,
781 listener: Option<Rc<T>>,
782 ) {
783 let event_listener = listener.map(|listener| {
784 InlineEventListener::Compiled(CommonEventHandler::BeforeUnloadEventHandler(unsafe {
785 OnBeforeUnloadEventHandlerNonNull::new(cx, listener.callback())
786 }))
787 });
788 self.set_inline_event_listener(Atom::from(ty), event_listener);
789 }
790
791 #[expect(unsafe_code)]
792 pub(crate) fn get_event_handler_common<T: CallbackContainer<crate::DomTypeHolder>>(
793 &self,
794 cx: &mut JSContext,
795 ty: &str,
796 ) -> Option<Rc<T>> {
797 let listener = self.get_inline_event_listener(cx, &Atom::from(ty));
798 unsafe {
799 listener.map(|listener| {
800 CallbackContainer::new(cx, listener.parent().callback_holder().get())
801 })
802 }
803 }
804
805 pub(crate) fn has_handlers(&self) -> bool {
806 !self.handlers.borrow().is_empty()
807 }
808
809 pub(crate) fn fire_event(&self, cx: &mut js::context::JSContext, name: Atom) -> bool {
811 self.fire_event_with_params(
812 cx,
813 name,
814 EventBubbles::DoesNotBubble,
815 EventCancelable::NotCancelable,
816 EventComposed::NotComposed,
817 )
818 }
819
820 pub(crate) fn fire_bubbling_event(&self, cx: &mut js::context::JSContext, name: Atom) -> bool {
822 self.fire_event_with_params(
823 cx,
824 name,
825 EventBubbles::Bubbles,
826 EventCancelable::NotCancelable,
827 EventComposed::NotComposed,
828 )
829 }
830
831 pub(crate) fn fire_cancelable_event(
833 &self,
834 cx: &mut js::context::JSContext,
835 name: Atom,
836 ) -> bool {
837 self.fire_event_with_params(
838 cx,
839 name,
840 EventBubbles::DoesNotBubble,
841 EventCancelable::Cancelable,
842 EventComposed::NotComposed,
843 )
844 }
845
846 pub(crate) fn fire_bubbling_cancelable_event(
848 &self,
849 cx: &mut js::context::JSContext,
850 name: Atom,
851 ) -> bool {
852 self.fire_event_with_params(
853 cx,
854 name,
855 EventBubbles::Bubbles,
856 EventCancelable::Cancelable,
857 EventComposed::NotComposed,
858 )
859 }
860
861 pub(crate) fn fire_event_with_params(
863 &self,
864 cx: &mut js::context::JSContext,
865 name: Atom,
866 bubbles: EventBubbles,
867 cancelable: EventCancelable,
868 composed: EventComposed,
869 ) -> bool {
870 let event = Event::new(cx, &self.global(), name, bubbles, cancelable);
871 event.set_composed(composed.into());
872 event.fire(cx, self)
873 }
874
875 pub(crate) fn add_event_listener(
878 &self,
879 ty: DOMString,
880 listener: Option<Rc<EventListener>>,
881 options: AddEventListenerOptions,
882 ) {
883 if let Some(signal) = options.signal.as_ref() {
884 if signal.aborted() {
886 return;
887 }
888 signal.add(&AbortAlgorithm::DomEventListener(
890 RemovableDomEventListener {
891 event_target: Dom::from_ref(self),
892 ty: ty.clone(),
893 listener: listener.clone(),
894 options: options.parent.clone(),
895 },
896 ));
897 }
898 let listener = match listener {
900 Some(l) => l,
901 None => return,
902 };
903 let ty = Atom::from(ty);
904 let mut handlers = self.handlers.borrow_mut();
905 let entries = match handlers.entry(ty.clone()) {
906 Occupied(entry) => entry.into_mut(),
907 Vacant(entry) => entry.insert(EventListeners(vec![])),
908 };
909
910 let phase = if options.parent.capture {
911 ListenerPhase::Capturing
912 } else {
913 ListenerPhase::Bubbling
914 };
915 let new_entry = Rc::new(RefCell::new(EventListenerEntry {
917 phase,
918 listener: EventListenerType::Additive(listener),
919 once: options.once,
920 passive: options.passive.unwrap_or(self.default_passive_value(&ty)),
921 removed: false,
922 }));
923
924 if !entries.contains(&new_entry) {
928 entries.push(new_entry);
929 self.notify_listener_added(&ty);
930 }
931 }
932
933 pub(crate) fn remove_event_listener(
936 &self,
937 ty: DOMString,
938 listener: &Option<Rc<EventListener>>,
939 options: &EventListenerOptions,
940 ) {
941 let Some(listener) = listener else {
942 return;
943 };
944 let ty_atom = Atom::from(ty);
945 let mut handlers = self.handlers.borrow_mut();
946 if let Some(entries) = handlers.get_mut(&ty_atom) {
947 let phase = if options.capture {
948 ListenerPhase::Capturing
949 } else {
950 ListenerPhase::Bubbling
951 };
952 let listener_type = EventListenerType::Additive(listener.clone());
953 if let Some(position) = entries
954 .iter()
955 .position(|e| e.borrow().listener == listener_type && e.borrow().phase == phase)
956 {
957 entries.remove(position).borrow_mut().removed = true;
959 self.notify_listener_removed(&ty_atom);
960 }
961 }
962 }
963
964 pub(crate) fn get_the_parent(&self, event: &Event) -> Option<DomRoot<EventTarget>> {
966 if let Some(document) = self.downcast::<Document>() {
967 if event.type_() == atom!("load") || !document.has_browsing_context() {
968 return None;
969 } else {
970 return Some(DomRoot::from_ref(document.window().upcast::<EventTarget>()));
971 }
972 }
973
974 if let Some(shadow_root) = self.downcast::<ShadowRoot>() {
975 if event.should_pass_shadow_boundary(shadow_root) {
976 let host = shadow_root.Host();
977 return Some(DomRoot::from_ref(host.upcast::<EventTarget>()));
978 } else {
979 return None;
980 }
981 }
982
983 if let Some(node) = self.downcast::<Node>() {
984 return node.assigned_slot().map(DomRoot::upcast).or_else(|| {
987 node.GetParentNode()
988 .map(|parent| DomRoot::from_ref(parent.upcast::<EventTarget>()))
989 });
990 }
991
992 if let Some(request) = self.downcast::<IDBRequest>() {
995 return request
996 .transaction()
997 .map(|tx| DomRoot::from_ref(tx.upcast::<EventTarget>()));
998 }
999
1000 if let Some(transaction) = self.downcast::<IDBTransaction>() {
1002 return Some(DomRoot::from_ref(
1003 transaction.get_db().upcast::<EventTarget>(),
1004 ));
1005 }
1006
1007 if self.is::<IDBDatabase>() {
1009 return None;
1010 }
1011
1012 None
1013 }
1014
1015 pub(crate) fn retarget(&self, b: &Self) -> DomRoot<EventTarget> {
1019 let mut a = DomRoot::from_ref(self);
1021 loop {
1022 let Some(a_node) = a.downcast::<Node>() else {
1028 return a;
1029 };
1030 let a_root = a_node.GetRootNode(&GetRootNodeOptions::empty());
1031 if !a_root.is::<ShadowRoot>() {
1032 return a;
1033 }
1034 if let Some(b_node) = b.downcast::<Node>() &&
1035 a_root.is_shadow_including_inclusive_ancestor_of(b_node)
1036 {
1037 return a;
1038 }
1039
1040 a = DomRoot::from_ref(
1042 a_root
1043 .downcast::<ShadowRoot>()
1044 .unwrap()
1045 .Host()
1046 .upcast::<EventTarget>(),
1047 );
1048 }
1049 }
1050
1051 pub(crate) fn is_content_event_handler(name: &str) -> bool {
1053 CONTENT_EVENT_HANDLER_NAMES.contains(&name)
1054 }
1055
1056 pub(crate) fn summarize_event_listeners_for_devtools(&self) -> Vec<EventListenerInfo> {
1057 let handlers = self.handlers.borrow();
1058 let mut listener_infos = Vec::with_capacity(handlers.0.len());
1059 for (event_type, event_listeners) in &handlers.0 {
1060 for event_listener in event_listeners.iter() {
1061 let event_listener_entry = event_listener.borrow();
1062 listener_infos.push(EventListenerInfo {
1063 event_type: event_type.to_string(),
1064 capturing: event_listener_entry.phase() == ListenerPhase::Capturing,
1065 });
1066 }
1067 }
1068
1069 listener_infos
1070 }
1071}
1072
1073impl EventTargetMethods<crate::DomTypeHolder> for EventTarget {
1074 fn Constructor(
1076 cx: &mut JSContext,
1077 global: &GlobalScope,
1078 proto: Option<HandleObject>,
1079 ) -> Fallible<DomRoot<EventTarget>> {
1080 Ok(EventTarget::new(cx, global, proto))
1081 }
1082
1083 fn AddEventListener(
1085 &self,
1086 ty: DOMString,
1087 listener: Option<Rc<EventListener>>,
1088 options: AddEventListenerOptionsOrBoolean,
1089 ) {
1090 self.add_event_listener(ty, listener, options.convert())
1091 }
1092
1093 fn RemoveEventListener(
1095 &self,
1096 ty: DOMString,
1097 listener: Option<Rc<EventListener>>,
1098 options: EventListenerOptionsOrBoolean,
1099 ) {
1100 self.remove_event_listener(ty, &listener, &options.convert())
1101 }
1102
1103 fn DispatchEvent(&self, cx: &mut JSContext, event: &Event) -> Fallible<bool> {
1105 if event.dispatching() || !event.initialized() {
1106 return Err(Error::InvalidState(None));
1107 }
1108 event.set_trusted(false);
1109 Ok(event.dispatch(cx, self, false))
1110 }
1111}
1112
1113impl VirtualMethods for EventTarget {
1114 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1115 None
1116 }
1117}
1118
1119impl Convert<AddEventListenerOptions> for AddEventListenerOptionsOrBoolean {
1120 fn convert(self) -> AddEventListenerOptions {
1122 match self {
1125 AddEventListenerOptionsOrBoolean::AddEventListenerOptions(options) => options,
1127 AddEventListenerOptionsOrBoolean::Boolean(capture) => AddEventListenerOptions {
1128 parent: EventListenerOptions { capture },
1129 once: false,
1131 passive: None,
1133 signal: None,
1134 },
1135 }
1136 }
1137}
1138
1139impl Convert<EventListenerOptions> for EventListenerOptionsOrBoolean {
1140 fn convert(self) -> EventListenerOptions {
1141 match self {
1142 EventListenerOptionsOrBoolean::EventListenerOptions(options) => options,
1143 EventListenerOptionsOrBoolean::Boolean(capture) => EventListenerOptions { capture },
1144 }
1145 }
1146}
1147
1148impl OwnerWindow<crate::DomTypeHolder> for EventTarget {
1149 fn owner_window(&self) -> Option<DomRoot<Window>> {
1150 self.downcast::<Node>().map(|node| node.owner_window())
1151 }
1152}