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::reflector::{
51 DomGlobal, DomObject, Reflector, reflect_dom_object_with_proto,
52};
53use crate::dom::bindings::root::{Dom, DomRoot};
54use crate::dom::bindings::str::DOMString;
55use crate::dom::bindings::trace::HashMapTracedValues;
56use crate::dom::csp::{CspReporting, InlineCheckType};
57use crate::dom::document::Document;
58use crate::dom::element::Element;
59use crate::dom::errorevent::ErrorEvent;
60use crate::dom::event::{Event, EventBubbles, EventCancelable, EventComposed};
61use crate::dom::globalscope::GlobalScope;
62use crate::dom::html::htmlformelement::FormControlElementHelpers;
63use crate::dom::node::{Node, NodeTraits};
64use crate::dom::shadowroot::ShadowRoot;
65use crate::dom::virtualmethods::VirtualMethods;
66use crate::dom::window::Window;
67use crate::dom::workerglobalscope::WorkerGlobalScope;
68use crate::realms::{InRealm, enter_realm};
69use crate::script_runtime::CanGc;
70
71static CONTENT_EVENT_HANDLER_NAMES: [&str; 108] = [
77 "onabort",
78 "onauxclick",
79 "onbeforeinput",
80 "onbeforematch",
81 "onbeforetoggle",
82 "onblur",
83 "oncancel",
84 "oncanplay",
85 "oncanplaythrough",
86 "onchange",
87 "onclick",
88 "onclose",
89 "oncommand",
90 "oncontextlost",
91 "oncontextmenu",
92 "oncontextrestored",
93 "oncopy",
94 "oncuechange",
95 "oncut",
96 "ondblclick",
97 "ondrag",
98 "ondragend",
99 "ondragenter",
100 "ondragleave",
101 "ondragover",
102 "ondragstart",
103 "ondrop",
104 "ondurationchange",
105 "onemptied",
106 "onended",
107 "onerror",
108 "onfocus",
109 "onformdata",
110 "oninput",
111 "oninvalid",
112 "onkeydown",
113 "onkeypress",
114 "onkeyup",
115 "onload",
116 "onloadeddata",
117 "onloadedmetadata",
118 "onloadstart",
119 "onmousedown",
120 "onmouseenter",
121 "onmouseleave",
122 "onmousemove",
123 "onmouseout",
124 "onmouseover",
125 "onmouseup",
126 "onpaste",
127 "onpause",
128 "onplay",
129 "onplaying",
130 "onprogress",
131 "onratechange",
132 "onreset",
133 "onresize",
134 "onscroll",
135 "onscrollend",
136 "onsecuritypolicyviolation",
137 "onseeked",
138 "onseeking",
139 "onselect",
140 "onslotchange",
141 "onstalled",
142 "onsubmit",
143 "onsuspend",
144 "ontimeupdate",
145 "ontoggle",
146 "onvolumechange",
147 "onwaiting",
148 "onwebkitanimationend",
149 "onwebkitanimationiteration",
150 "onwebkitanimationstart",
151 "onwebkittransitionend",
152 "onwheel",
153 "onanimationstart",
155 "onanimationiteration",
156 "onanimationend",
157 "onanimationcancel",
158 "ontransitionrun",
160 "ontransitionend",
161 "ontransitioncancel",
162 "onselectstart",
164 "onselectionchange",
165 "onafterprint",
167 "onbeforeprint",
168 "onbeforeunload",
169 "onhashchange",
170 "onlanguagechange",
171 "onmessage",
172 "onmessageerror",
173 "onoffline",
174 "ononline",
175 "onpagehide",
176 "onpagereveal",
177 "onpageshow",
178 "onpageswap",
179 "onpopstate",
180 "onrejectionhandled",
181 "onstorage",
182 "onunhandledrejection",
183 "onunload",
184 "onencrypted",
186 "onwaitingforkey",
187 "onbegin",
189 "onend",
190 "onrepeat",
191];
192
193#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)]
194#[allow(clippy::enum_variant_names)]
195pub(crate) enum CommonEventHandler {
196 EventHandler(#[conditional_malloc_size_of] Rc<EventHandlerNonNull>),
197
198 ErrorEventHandler(#[conditional_malloc_size_of] Rc<OnErrorEventHandlerNonNull>),
199
200 BeforeUnloadEventHandler(#[conditional_malloc_size_of] Rc<OnBeforeUnloadEventHandlerNonNull>),
201}
202
203impl CommonEventHandler {
204 fn parent(&self) -> &CallbackFunction<crate::DomTypeHolder> {
205 match *self {
206 CommonEventHandler::EventHandler(ref handler) => &handler.parent,
207 CommonEventHandler::ErrorEventHandler(ref handler) => &handler.parent,
208 CommonEventHandler::BeforeUnloadEventHandler(ref handler) => &handler.parent,
209 }
210 }
211}
212
213#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
214pub(crate) enum ListenerPhase {
215 Capturing,
216 Bubbling,
217}
218
219#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)]
221struct InternalRawUncompiledHandler {
222 source: DOMString,
223 #[no_trace]
224 url: ServoUrl,
225 line: usize,
226}
227
228#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)]
230enum InlineEventListener {
231 Uncompiled(InternalRawUncompiledHandler),
232 Compiled(CommonEventHandler),
233 Null,
234}
235
236fn get_compiled_handler(
240 inline_listener: &RefCell<InlineEventListener>,
241 owner: &EventTarget,
242 ty: &Atom,
243 can_gc: CanGc,
244) -> Option<CommonEventHandler> {
245 let listener = mem::replace(
246 &mut *inline_listener.borrow_mut(),
247 InlineEventListener::Null,
248 );
249 let compiled = match listener {
250 InlineEventListener::Null => None,
251 InlineEventListener::Uncompiled(handler) => {
252 owner.get_compiled_event_handler(handler, ty, can_gc)
253 },
254 InlineEventListener::Compiled(handler) => Some(handler),
255 };
256 if let Some(ref compiled) = compiled {
257 *inline_listener.borrow_mut() = InlineEventListener::Compiled(compiled.clone());
258 }
259 compiled
260}
261
262#[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)]
263enum EventListenerType {
264 Additive(#[conditional_malloc_size_of] Rc<EventListener>),
265 Inline(RefCell<InlineEventListener>),
266}
267
268impl EventListenerType {
269 fn get_compiled_listener(
270 &self,
271 owner: &EventTarget,
272 ty: &Atom,
273 can_gc: CanGc,
274 ) -> Option<CompiledEventListener> {
275 match *self {
276 EventListenerType::Inline(ref inline) => {
277 get_compiled_handler(inline, owner, ty, can_gc).map(CompiledEventListener::Handler)
278 },
279 EventListenerType::Additive(ref listener) => {
280 Some(CompiledEventListener::Listener(listener.clone()))
281 },
282 }
283 }
284}
285
286pub(crate) enum CompiledEventListener {
289 Listener(Rc<EventListener>),
290 Handler(CommonEventHandler),
291}
292
293impl CompiledEventListener {
294 #[expect(unsafe_code)]
295 pub(crate) fn associated_global(&self) -> DomRoot<GlobalScope> {
296 let obj = match self {
297 CompiledEventListener::Listener(listener) => listener.callback(),
298 CompiledEventListener::Handler(CommonEventHandler::EventHandler(handler)) => {
299 handler.callback()
300 },
301 CompiledEventListener::Handler(CommonEventHandler::ErrorEventHandler(handler)) => {
302 handler.callback()
303 },
304 CompiledEventListener::Handler(CommonEventHandler::BeforeUnloadEventHandler(
305 handler,
306 )) => handler.callback(),
307 };
308 unsafe { GlobalScope::from_object(obj) }
309 }
310
311 pub(crate) fn call_or_handle_event(
313 &self,
314 object: &EventTarget,
315 event: &Event,
316 exception_handle: ExceptionHandling,
317 can_gc: CanGc,
318 ) {
319 match *self {
321 CompiledEventListener::Listener(ref listener) => {
322 let _ = listener.HandleEvent_(object, event, exception_handle, can_gc);
323 },
324 CompiledEventListener::Handler(ref handler) => {
325 match *handler {
326 CommonEventHandler::ErrorEventHandler(ref handler) => {
327 if let Some(event) = event.downcast::<ErrorEvent>() {
328 if object.is::<Window>() || object.is::<WorkerGlobalScope>() {
329 let cx = GlobalScope::get_cx();
330 rooted!(in(*cx) let mut error: JSVal);
331 event.Error(cx, error.handle_mut());
332 rooted!(in(*cx) let mut rooted_return_value: JSVal);
333 let return_value = handler.Call_(
334 object,
335 EventOrString::String(event.Message()),
336 Some(event.Filename()),
337 Some(event.Lineno()),
338 Some(event.Colno()),
339 Some(error.handle()),
340 rooted_return_value.handle_mut(),
341 exception_handle,
342 can_gc,
343 );
344 if let Ok(()) = return_value {
346 if rooted_return_value.handle().is_boolean() &&
347 rooted_return_value.handle().to_boolean()
348 {
349 event.upcast::<Event>().PreventDefault();
350 }
351 }
352 return;
353 }
354 }
355
356 rooted!(in(*GlobalScope::get_cx()) let mut rooted_return_value: JSVal);
357 let _ = handler.Call_(
358 object,
359 EventOrString::Event(DomRoot::from_ref(event)),
360 None,
361 None,
362 None,
363 None,
364 rooted_return_value.handle_mut(),
365 exception_handle,
366 can_gc,
367 );
368 },
369
370 CommonEventHandler::BeforeUnloadEventHandler(ref handler) => {
371 if let Some(event) = event.downcast::<BeforeUnloadEvent>() {
372 if let Ok(value) = handler.Call_(
374 object,
375 event.upcast::<Event>(),
376 exception_handle,
377 can_gc,
378 ) {
379 let rv = event.ReturnValue();
380 if let Some(v) = value {
381 if rv.is_empty() {
382 event.SetReturnValue(v);
383 }
384 event.upcast::<Event>().PreventDefault();
385 }
386 }
387 } else {
388 let _ = handler.Call_(
390 object,
391 event.upcast::<Event>(),
392 exception_handle,
393 can_gc,
394 );
395 }
396 },
397
398 CommonEventHandler::EventHandler(ref handler) => {
399 let cx = GlobalScope::get_cx();
400 rooted!(in(*cx) let mut rooted_return_value: JSVal);
401 if let Ok(()) = handler.Call_(
402 object,
403 event,
404 rooted_return_value.handle_mut(),
405 exception_handle,
406 can_gc,
407 ) {
408 let value = rooted_return_value.handle();
409
410 let should_cancel = value.is_boolean() && !value.to_boolean();
412
413 if should_cancel {
414 event.PreventDefault();
418 }
419 }
420 },
421 }
422 },
423 }
424 }
425}
426
427#[derive(Clone, DenyPublicFields, JSTraceable, MallocSizeOf)]
430pub(crate) struct EventListenerEntry {
432 phase: ListenerPhase,
433 listener: EventListenerType,
434 once: bool,
435 passive: bool,
436 removed: bool,
437}
438
439impl EventListenerEntry {
440 pub(crate) fn phase(&self) -> ListenerPhase {
441 self.phase
442 }
443
444 pub(crate) fn once(&self) -> bool {
445 self.once
446 }
447
448 pub(crate) fn removed(&self) -> bool {
449 self.removed
450 }
451
452 pub(crate) fn get_compiled_listener(
454 &self,
455 owner: &EventTarget,
456 ty: &Atom,
457 can_gc: CanGc,
458 ) -> Option<CompiledEventListener> {
459 self.listener.get_compiled_listener(owner, ty, can_gc)
460 }
461}
462
463impl std::cmp::PartialEq for EventListenerEntry {
464 fn eq(&self, other: &Self) -> bool {
465 self.phase == other.phase && self.listener == other.listener
466 }
467}
468
469#[derive(Clone, JSTraceable, MallocSizeOf)]
470pub(crate) struct EventListeners(
472 #[conditional_malloc_size_of] Vec<Rc<RefCell<EventListenerEntry>>>,
473);
474
475impl Deref for EventListeners {
476 type Target = Vec<Rc<RefCell<EventListenerEntry>>>;
477 fn deref(&self) -> &Vec<Rc<RefCell<EventListenerEntry>>> {
478 &self.0
479 }
480}
481
482impl DerefMut for EventListeners {
483 fn deref_mut(&mut self) -> &mut Vec<Rc<RefCell<EventListenerEntry>>> {
484 &mut self.0
485 }
486}
487
488impl EventListeners {
489 fn get_inline_listener(
491 &self,
492 owner: &EventTarget,
493 ty: &Atom,
494 can_gc: CanGc,
495 ) -> Option<CommonEventHandler> {
496 for entry in &self.0 {
497 if let EventListenerType::Inline(ref inline) = entry.borrow().listener {
498 return get_compiled_handler(inline, owner, ty, can_gc);
500 }
501 }
502
503 None
505 }
506
507 fn has_listeners(&self) -> bool {
508 !self.0.is_empty()
509 }
510}
511
512#[dom_struct]
513pub struct EventTarget {
514 reflector_: Reflector,
515 handlers: DomRefCell<HashMapTracedValues<Atom, EventListeners, FxBuildHasher>>,
516}
517
518impl EventTarget {
519 pub(crate) fn new_inherited() -> EventTarget {
520 EventTarget {
521 reflector_: Reflector::new(),
522 handlers: DomRefCell::new(Default::default()),
523 }
524 }
525
526 fn new(
527 global: &GlobalScope,
528 proto: Option<HandleObject>,
529 can_gc: CanGc,
530 ) -> DomRoot<EventTarget> {
531 reflect_dom_object_with_proto(
532 Box::new(EventTarget::new_inherited()),
533 global,
534 proto,
535 can_gc,
536 )
537 }
538
539 pub(crate) fn has_listeners_for(&self, type_: &Atom) -> bool {
542 match self.handlers.borrow().get(type_) {
543 Some(listeners) => listeners.has_listeners(),
544 None => false,
545 }
546 }
547
548 pub(crate) fn get_listeners_for(&self, type_: &Atom) -> EventListeners {
549 self.handlers
550 .borrow()
551 .get(type_)
552 .map_or(EventListeners(vec![]), |listeners| listeners.clone())
553 }
554
555 pub(crate) fn dispatch_event(&self, event: &Event, can_gc: CanGc) -> bool {
556 event.dispatch(self, false, can_gc)
557 }
558
559 pub(crate) fn remove_all_listeners(&self) {
560 let mut handlers = self.handlers.borrow_mut();
561 for (_, entries) in handlers.iter() {
562 entries
563 .iter()
564 .for_each(|entry| entry.borrow_mut().removed = true);
565 }
566
567 *handlers = Default::default();
568 }
569
570 fn default_passive_value(&self, ty: &Atom) -> bool {
572 let event_type = ty.to_ascii_lowercase();
574
575 let matches_event_type = matches!(
577 event_type.trim_matches(HTML_SPACE_CHARACTERS),
578 "touchstart" | "touchmove" | "wheel" | "mousewheel"
579 );
580
581 if !matches_event_type {
582 return false;
583 }
584
585 if self.is::<Window>() {
587 return true;
588 }
589
590 if let Some(node) = self.downcast::<Node>() {
592 let node_document = node.owner_document();
593 let event_target = self.upcast::<EventTarget>();
594
595 return event_target == node_document.upcast::<EventTarget>()
597 || node_document.GetDocumentElement().is_some_and(|n| n.upcast::<EventTarget>() == event_target)
599 || node_document.GetBody().is_some_and(|n| n.upcast::<EventTarget>() == event_target);
601 }
602
603 false
604 }
605
606 fn set_inline_event_listener(&self, ty: Atom, listener: Option<InlineEventListener>) {
608 let mut handlers = self.handlers.borrow_mut();
609 let entries = match handlers.entry(ty.clone()) {
610 Occupied(entry) => entry.into_mut(),
611 Vacant(entry) => entry.insert(EventListeners(vec![])),
612 };
613
614 let idx = entries
615 .iter()
616 .position(|entry| matches!(entry.borrow().listener, EventListenerType::Inline(_)));
617
618 match idx {
619 Some(idx) => match listener {
620 Some(listener) => {
623 entries[idx].borrow_mut().listener = EventListenerType::Inline(listener.into());
624 },
625 None => {
626 entries.remove(idx).borrow_mut().removed = true;
627 },
628 },
629 None => {
630 if let Some(listener) = listener {
631 entries.push(Rc::new(RefCell::new(EventListenerEntry {
632 phase: ListenerPhase::Bubbling,
633 listener: EventListenerType::Inline(listener.into()),
634 once: false,
635 passive: self.default_passive_value(&ty),
636 removed: false,
637 })));
638 }
639 },
640 }
641 }
642
643 pub(crate) fn remove_listener(&self, ty: &Atom, entry: &Rc<RefCell<EventListenerEntry>>) {
644 let mut handlers = self.handlers.borrow_mut();
645
646 if let Some(entries) = handlers.get_mut(ty) {
647 if let Some(position) = entries.iter().position(|e| *e == *entry) {
648 entries.remove(position).borrow_mut().removed = true;
649 }
650 }
651 }
652
653 pub(crate) fn is_passive(&self, listener: &Rc<RefCell<EventListenerEntry>>) -> bool {
655 listener.borrow().passive
656 }
657
658 fn get_inline_event_listener(&self, ty: &Atom, can_gc: CanGc) -> Option<CommonEventHandler> {
659 let handlers = self.handlers.borrow();
660 handlers
661 .get(ty)
662 .and_then(|entry| entry.get_inline_listener(self, ty, can_gc))
663 }
664
665 pub(crate) fn set_event_handler_uncompiled(
668 &self,
669 url: ServoUrl,
670 line: usize,
671 ty: &str,
672 source: &str,
673 ) {
674 if let Some(element) = self.downcast::<Element>() {
675 let doc = element.owner_document();
676 let global = &doc.global();
677 if global
678 .get_csp_list()
679 .should_elements_inline_type_behavior_be_blocked(
680 global,
681 element.upcast(),
682 InlineCheckType::ScriptAttribute,
683 source,
684 line as u32,
685 )
686 {
687 return;
688 }
689 };
690
691 let handler = InternalRawUncompiledHandler {
692 source: DOMString::from(source),
693 line,
694 url,
695 };
696 self.set_inline_event_listener(
697 Atom::from(ty),
698 Some(InlineEventListener::Uncompiled(handler)),
699 );
700 }
701
702 #[expect(unsafe_code)]
707 fn get_compiled_event_handler(
708 &self,
709 handler: InternalRawUncompiledHandler,
710 ty: &Atom,
711 can_gc: CanGc,
712 ) -> Option<CommonEventHandler> {
713 let element = self.downcast::<Element>();
715 let document = match element {
716 Some(element) => element.owner_document(),
717 None => self.downcast::<Window>().unwrap().Document(),
718 };
719
720 if !document.scripting_enabled() {
722 return None;
723 }
724
725 let body: Vec<u16> = handler.source.str().encode_utf16().collect();
727
728 let form_owner = element
732 .and_then(|e| e.as_maybe_form_control())
733 .and_then(|f| f.form_owner());
734
735 let window = document.window();
743 let _ac = enter_realm(window);
744
745 let name = CString::new(format!("on{}", &**ty)).unwrap();
748
749 const ARG_NAMES: &[*const c_char] = &[c"event".as_ptr()];
751 const ERROR_ARG_NAMES: &[*const c_char] = &[
752 c"event".as_ptr(),
753 c"source".as_ptr(),
754 c"lineno".as_ptr(),
755 c"colno".as_ptr(),
756 c"error".as_ptr(),
757 ];
758 let is_error = ty == &atom!("error") && self.is::<Window>();
759 let args = if is_error { ERROR_ARG_NAMES } else { ARG_NAMES };
760
761 let cx = GlobalScope::get_cx();
762 let options = unsafe {
763 CompileOptionsWrapper::new_raw(*cx, &handler.url.to_string(), handler.line as u32)
764 };
765
766 let scopechain = js::rust::EnvironmentChain::new(*cx, SupportUnscopables::Yes);
768
769 if let Some(element) = element {
770 scopechain.append(document.reflector().get_jsobject().get());
771 if let Some(form_owner) = form_owner {
772 scopechain.append(form_owner.reflector().get_jsobject().get());
773 }
774 scopechain.append(element.reflector().get_jsobject().get());
775 }
776
777 rooted!(in(*cx) let mut handler = unsafe {
778 CompileFunction(
779 *cx,
780 scopechain.get(),
781 options.ptr,
782 name.as_ptr(),
783 args.len() as u32,
784 args.as_ptr(),
785 &mut transform_u16_to_source_text(&body),
786 )
787 });
788 if handler.get().is_null() {
789 let ar = enter_realm(self);
791 report_pending_exception(cx, false, InRealm::Entered(&ar), can_gc);
793 return None;
794 }
795
796 let funobj = unsafe { JS_GetFunctionObject(handler.get()) };
802 assert!(!funobj.is_null());
803 if is_error {
805 Some(CommonEventHandler::ErrorEventHandler(unsafe {
806 OnErrorEventHandlerNonNull::new(cx, funobj)
807 }))
808 } else if ty == &atom!("beforeunload") {
809 Some(CommonEventHandler::BeforeUnloadEventHandler(unsafe {
810 OnBeforeUnloadEventHandlerNonNull::new(cx, funobj)
811 }))
812 } else {
813 Some(CommonEventHandler::EventHandler(unsafe {
814 EventHandlerNonNull::new(cx, funobj)
815 }))
816 }
817 }
818
819 #[expect(unsafe_code)]
820 pub(crate) fn set_event_handler_common<T: CallbackContainer<crate::DomTypeHolder>>(
821 &self,
822 ty: &str,
823 listener: Option<Rc<T>>,
824 ) {
825 let cx = GlobalScope::get_cx();
826
827 let event_listener = listener.map(|listener| {
828 InlineEventListener::Compiled(CommonEventHandler::EventHandler(unsafe {
829 EventHandlerNonNull::new(cx, listener.callback())
830 }))
831 });
832 self.set_inline_event_listener(Atom::from(ty), event_listener);
833 }
834
835 #[expect(unsafe_code)]
836 pub(crate) fn set_error_event_handler<T: CallbackContainer<crate::DomTypeHolder>>(
837 &self,
838 ty: &str,
839 listener: Option<Rc<T>>,
840 ) {
841 let cx = GlobalScope::get_cx();
842
843 let event_listener = listener.map(|listener| {
844 InlineEventListener::Compiled(CommonEventHandler::ErrorEventHandler(unsafe {
845 OnErrorEventHandlerNonNull::new(cx, listener.callback())
846 }))
847 });
848 self.set_inline_event_listener(Atom::from(ty), event_listener);
849 }
850
851 #[expect(unsafe_code)]
852 pub(crate) fn set_beforeunload_event_handler<T: CallbackContainer<crate::DomTypeHolder>>(
853 &self,
854 ty: &str,
855 listener: Option<Rc<T>>,
856 ) {
857 let cx = GlobalScope::get_cx();
858
859 let event_listener = listener.map(|listener| {
860 InlineEventListener::Compiled(CommonEventHandler::BeforeUnloadEventHandler(unsafe {
861 OnBeforeUnloadEventHandlerNonNull::new(cx, listener.callback())
862 }))
863 });
864 self.set_inline_event_listener(Atom::from(ty), event_listener);
865 }
866
867 #[expect(unsafe_code)]
868 pub(crate) fn get_event_handler_common<T: CallbackContainer<crate::DomTypeHolder>>(
869 &self,
870 ty: &str,
871 can_gc: CanGc,
872 ) -> Option<Rc<T>> {
873 let cx = GlobalScope::get_cx();
874 let listener = self.get_inline_event_listener(&Atom::from(ty), can_gc);
875 unsafe {
876 listener.map(|listener| {
877 CallbackContainer::new(cx, listener.parent().callback_holder().get())
878 })
879 }
880 }
881
882 pub(crate) fn has_handlers(&self) -> bool {
883 !self.handlers.borrow().is_empty()
884 }
885
886 pub(crate) fn fire_event(&self, name: Atom, can_gc: CanGc) -> bool {
888 self.fire_event_with_params(
889 name,
890 EventBubbles::DoesNotBubble,
891 EventCancelable::NotCancelable,
892 EventComposed::NotComposed,
893 can_gc,
894 )
895 }
896
897 pub(crate) fn fire_bubbling_event(&self, name: Atom, can_gc: CanGc) -> bool {
899 self.fire_event_with_params(
900 name,
901 EventBubbles::Bubbles,
902 EventCancelable::NotCancelable,
903 EventComposed::NotComposed,
904 can_gc,
905 )
906 }
907
908 pub(crate) fn fire_cancelable_event(&self, name: Atom, can_gc: CanGc) -> bool {
910 self.fire_event_with_params(
911 name,
912 EventBubbles::DoesNotBubble,
913 EventCancelable::Cancelable,
914 EventComposed::NotComposed,
915 can_gc,
916 )
917 }
918
919 pub(crate) fn fire_bubbling_cancelable_event(&self, name: Atom, can_gc: CanGc) -> bool {
921 self.fire_event_with_params(
922 name,
923 EventBubbles::Bubbles,
924 EventCancelable::Cancelable,
925 EventComposed::NotComposed,
926 can_gc,
927 )
928 }
929
930 pub(crate) fn fire_event_with_params(
932 &self,
933 name: Atom,
934 bubbles: EventBubbles,
935 cancelable: EventCancelable,
936 composed: EventComposed,
937 can_gc: CanGc,
938 ) -> bool {
939 let event = Event::new(&self.global(), name, bubbles, cancelable, can_gc);
940 event.set_composed(composed.into());
941 event.fire(self, can_gc)
942 }
943
944 pub(crate) fn add_event_listener(
947 &self,
948 ty: DOMString,
949 listener: Option<Rc<EventListener>>,
950 options: AddEventListenerOptions,
951 ) {
952 if let Some(signal) = options.signal.as_ref() {
953 if signal.aborted() {
955 return;
956 }
957 signal.add(&AbortAlgorithm::DomEventListener(
959 RemovableDomEventListener {
960 event_target: Dom::from_ref(self),
961 ty: ty.clone(),
962 listener: listener.clone(),
963 options: options.parent.clone(),
964 },
965 ));
966 }
967 let listener = match listener {
969 Some(l) => l,
970 None => return,
971 };
972 let mut handlers = self.handlers.borrow_mut();
973 let ty = Atom::from(ty);
974 let entries = match handlers.entry(ty.clone()) {
975 Occupied(entry) => entry.into_mut(),
976 Vacant(entry) => entry.insert(EventListeners(vec![])),
977 };
978
979 let phase = if options.parent.capture {
980 ListenerPhase::Capturing
981 } else {
982 ListenerPhase::Bubbling
983 };
984 let new_entry = Rc::new(RefCell::new(EventListenerEntry {
986 phase,
987 listener: EventListenerType::Additive(listener),
988 once: options.once,
989 passive: options.passive.unwrap_or(self.default_passive_value(&ty)),
990 removed: false,
991 }));
992
993 if !entries.contains(&new_entry) {
997 entries.push(new_entry);
998 }
999 }
1000
1001 pub(crate) fn remove_event_listener(
1004 &self,
1005 ty: DOMString,
1006 listener: &Option<Rc<EventListener>>,
1007 options: &EventListenerOptions,
1008 ) {
1009 let Some(listener) = listener else {
1010 return;
1011 };
1012 let mut handlers = self.handlers.borrow_mut();
1013 if let Some(entries) = handlers.get_mut(&Atom::from(ty)) {
1014 let phase = if options.capture {
1015 ListenerPhase::Capturing
1016 } else {
1017 ListenerPhase::Bubbling
1018 };
1019 let listener_type = EventListenerType::Additive(listener.clone());
1020 if let Some(position) = entries
1021 .iter()
1022 .position(|e| e.borrow().listener == listener_type && e.borrow().phase == phase)
1023 {
1024 entries.remove(position).borrow_mut().removed = true;
1026 }
1027 }
1028 }
1029
1030 pub(crate) fn get_the_parent(&self, event: &Event) -> Option<DomRoot<EventTarget>> {
1032 if let Some(document) = self.downcast::<Document>() {
1033 if event.type_() == atom!("load") || !document.has_browsing_context() {
1034 return None;
1035 } else {
1036 return Some(DomRoot::from_ref(document.window().upcast::<EventTarget>()));
1037 }
1038 }
1039
1040 if let Some(shadow_root) = self.downcast::<ShadowRoot>() {
1041 if event.should_pass_shadow_boundary(shadow_root) {
1042 let host = shadow_root.Host();
1043 return Some(DomRoot::from_ref(host.upcast::<EventTarget>()));
1044 } else {
1045 return None;
1046 }
1047 }
1048
1049 if let Some(node) = self.downcast::<Node>() {
1050 return node.assigned_slot().map(DomRoot::upcast).or_else(|| {
1053 node.GetParentNode()
1054 .map(|parent| DomRoot::from_ref(parent.upcast::<EventTarget>()))
1055 });
1056 }
1057
1058 None
1059 }
1060
1061 pub(crate) fn retarget(&self, b: &Self) -> DomRoot<EventTarget> {
1065 let mut a = DomRoot::from_ref(self);
1067 loop {
1068 let Some(a_node) = a.downcast::<Node>() else {
1074 return a;
1075 };
1076 let a_root = a_node.GetRootNode(&GetRootNodeOptions::empty());
1077 if !a_root.is::<ShadowRoot>() {
1078 return a;
1079 }
1080 if let Some(b_node) = b.downcast::<Node>() {
1081 if a_root.is_shadow_including_inclusive_ancestor_of(b_node) {
1082 return a;
1083 }
1084 }
1085
1086 a = DomRoot::from_ref(
1088 a_root
1089 .downcast::<ShadowRoot>()
1090 .unwrap()
1091 .Host()
1092 .upcast::<EventTarget>(),
1093 );
1094 }
1095 }
1096
1097 pub(crate) fn is_content_event_handler(name: &str) -> bool {
1099 CONTENT_EVENT_HANDLER_NAMES.contains(&name)
1100 }
1101}
1102
1103impl EventTargetMethods<crate::DomTypeHolder> for EventTarget {
1104 fn Constructor(
1106 global: &GlobalScope,
1107 proto: Option<HandleObject>,
1108 can_gc: CanGc,
1109 ) -> Fallible<DomRoot<EventTarget>> {
1110 Ok(EventTarget::new(global, proto, can_gc))
1111 }
1112
1113 fn AddEventListener(
1115 &self,
1116 ty: DOMString,
1117 listener: Option<Rc<EventListener>>,
1118 options: AddEventListenerOptionsOrBoolean,
1119 ) {
1120 self.add_event_listener(ty, listener, options.convert())
1121 }
1122
1123 fn RemoveEventListener(
1125 &self,
1126 ty: DOMString,
1127 listener: Option<Rc<EventListener>>,
1128 options: EventListenerOptionsOrBoolean,
1129 ) {
1130 self.remove_event_listener(ty, &listener, &options.convert())
1131 }
1132
1133 fn DispatchEvent(&self, event: &Event, can_gc: CanGc) -> Fallible<bool> {
1135 if event.dispatching() || !event.initialized() {
1136 return Err(Error::InvalidState(None));
1137 }
1138 event.set_trusted(false);
1139 Ok(self.dispatch_event(event, can_gc))
1140 }
1141}
1142
1143impl VirtualMethods for EventTarget {
1144 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1145 None
1146 }
1147}
1148
1149impl Convert<AddEventListenerOptions> for AddEventListenerOptionsOrBoolean {
1150 fn convert(self) -> AddEventListenerOptions {
1152 match self {
1155 AddEventListenerOptionsOrBoolean::AddEventListenerOptions(options) => options,
1157 AddEventListenerOptionsOrBoolean::Boolean(capture) => AddEventListenerOptions {
1158 parent: EventListenerOptions { capture },
1159 once: false,
1161 passive: None,
1163 signal: None,
1164 },
1165 }
1166 }
1167}
1168
1169impl Convert<EventListenerOptions> for EventListenerOptionsOrBoolean {
1170 fn convert(self) -> EventListenerOptions {
1171 match self {
1172 EventListenerOptionsOrBoolean::EventListenerOptions(options) => options,
1173 EventListenerOptionsOrBoolean::Boolean(capture) => EventListenerOptions { capture },
1174 }
1175 }
1176}