1use dom_struct::dom_struct;
6use js::context::JSContext;
7use js::jsapi::Heap;
8use js::jsval::JSVal;
9use js::rust::{HandleObject, HandleValue, MutableHandleValue};
10use script_bindings::cell::DomRefCell;
11use script_bindings::domstring::TracedDOMString;
12use script_bindings::reflector::reflect_dom_object_with_proto;
13use stylo_atoms::Atom;
14
15use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
16use crate::dom::bindings::codegen::Bindings::MessageEventBinding::{
17 MessageEventInit, MessageEventMethods,
18};
19use crate::dom::bindings::codegen::UnionTypes::WindowProxyOrMessagePortOrServiceWorker;
20use crate::dom::bindings::error::Fallible;
21use crate::dom::bindings::frozenarray::CachedFrozenArray;
22use crate::dom::bindings::inheritance::Castable;
23use crate::dom::bindings::root::{AsHandleValue, Dom, DomRoot};
24use crate::dom::bindings::str::DOMString;
25use crate::dom::event::Event;
26use crate::dom::eventtarget::EventTarget;
27use crate::dom::globalscope::GlobalScope;
28use crate::dom::messageport::MessagePort;
29use crate::dom::serviceworker::ServiceWorker;
30use crate::dom::windowproxy::WindowProxy;
31
32#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
33#[derive(JSTraceable, MallocSizeOf)]
34enum SrcObject {
35 WindowProxy(Dom<WindowProxy>),
36 MessagePort(Dom<MessagePort>),
37 ServiceWorker(Dom<ServiceWorker>),
38}
39
40impl From<&WindowProxyOrMessagePortOrServiceWorker> for SrcObject {
41 fn from(src_object: &WindowProxyOrMessagePortOrServiceWorker) -> SrcObject {
42 match src_object {
43 WindowProxyOrMessagePortOrServiceWorker::WindowProxy(blob) => {
44 SrcObject::WindowProxy(Dom::from_ref(blob))
45 },
46 WindowProxyOrMessagePortOrServiceWorker::MessagePort(stream) => {
47 SrcObject::MessagePort(Dom::from_ref(stream))
48 },
49 WindowProxyOrMessagePortOrServiceWorker::ServiceWorker(stream) => {
50 SrcObject::ServiceWorker(Dom::from_ref(stream))
51 },
52 }
53 }
54}
55
56#[dom_struct]
57#[expect(non_snake_case)]
58pub(crate) struct MessageEvent {
59 event: Event,
60 #[ignore_malloc_size_of = "mozjs"]
61 data: Heap<JSVal>,
62 origin: DomRefCell<TracedDOMString>,
63 source: DomRefCell<Option<SrcObject>>,
64 lastEventId: DomRefCell<TracedDOMString>,
65 ports: DomRefCell<Vec<Dom<MessagePort>>>,
66 #[ignore_malloc_size_of = "mozjs"]
67 frozen_ports: CachedFrozenArray,
68}
69
70#[expect(non_snake_case)]
71impl MessageEvent {
72 pub(crate) fn new_inherited(
73 origin: DOMString,
74 source: Option<&WindowProxyOrMessagePortOrServiceWorker>,
75 lastEventId: DOMString,
76 ports: Vec<DomRoot<MessagePort>>,
77 ) -> MessageEvent {
78 MessageEvent {
79 event: Event::new_inherited(),
80 data: Heap::default(),
81 source: DomRefCell::new(source.map(|source| source.into())),
82 origin: DomRefCell::new(origin.traced()),
83 lastEventId: DomRefCell::new(lastEventId.traced()),
84 ports: DomRefCell::new(
85 ports
86 .into_iter()
87 .map(|port| Dom::from_ref(&*port))
88 .collect(),
89 ),
90 frozen_ports: CachedFrozenArray::new(),
91 }
92 }
93
94 pub(crate) fn new_uninitialized(
95 cx: &mut JSContext,
96 global: &GlobalScope,
97 ) -> DomRoot<MessageEvent> {
98 Self::new_uninitialized_with_proto(cx, global, None)
99 }
100
101 fn new_uninitialized_with_proto(
102 cx: &mut JSContext,
103 global: &GlobalScope,
104 proto: Option<HandleObject>,
105 ) -> DomRoot<MessageEvent> {
106 MessageEvent::new_initialized(
107 cx,
108 global,
109 proto,
110 HandleValue::undefined(),
111 DOMString::new(),
112 None,
113 DOMString::new(),
114 vec![],
115 )
116 }
117
118 #[expect(clippy::too_many_arguments)]
119 fn new_initialized(
120 cx: &mut JSContext,
121 global: &GlobalScope,
122 proto: Option<HandleObject>,
123 data: HandleValue,
124 origin: DOMString,
125 source: Option<&WindowProxyOrMessagePortOrServiceWorker>,
126 lastEventId: DOMString,
127 ports: Vec<DomRoot<MessagePort>>,
128 ) -> DomRoot<MessageEvent> {
129 let ev = Box::new(MessageEvent::new_inherited(
130 origin,
131 source,
132 lastEventId,
133 ports,
134 ));
135 let ev = reflect_dom_object_with_proto(cx, ev, global, proto);
136 ev.data.set(data.get());
137
138 ev
139 }
140
141 #[expect(clippy::too_many_arguments)]
142 pub(crate) fn new(
143 cx: &mut JSContext,
144 global: &GlobalScope,
145 type_: Atom,
146 bubbles: bool,
147 cancelable: bool,
148 data: HandleValue,
149 origin: DOMString,
150 source: Option<&WindowProxyOrMessagePortOrServiceWorker>,
151 lastEventId: DOMString,
152 ports: Vec<DomRoot<MessagePort>>,
153 ) -> DomRoot<MessageEvent> {
154 Self::new_with_proto(
155 cx,
156 global,
157 None,
158 type_,
159 bubbles,
160 cancelable,
161 data,
162 origin,
163 source,
164 lastEventId,
165 ports,
166 )
167 }
168
169 #[expect(clippy::too_many_arguments)]
170 fn new_with_proto(
171 cx: &mut JSContext,
172 global: &GlobalScope,
173 proto: Option<HandleObject>,
174 type_: Atom,
175 bubbles: bool,
176 cancelable: bool,
177 data: HandleValue,
178 origin: DOMString,
179 source: Option<&WindowProxyOrMessagePortOrServiceWorker>,
180 lastEventId: DOMString,
181 ports: Vec<DomRoot<MessagePort>>,
182 ) -> DomRoot<MessageEvent> {
183 let ev = MessageEvent::new_initialized(
184 cx,
185 global,
186 proto,
187 data,
188 origin,
189 source,
190 lastEventId,
191 ports,
192 );
193 {
194 let event = ev.upcast::<Event>();
195 event.init_event(type_, bubbles, cancelable);
196 }
197 ev
198 }
199
200 pub(crate) fn dispatch_jsval(
201 cx: &mut JSContext,
202 target: &EventTarget,
203 scope: &GlobalScope,
204 message: HandleValue,
205 origin: Option<&str>,
206 source: Option<&WindowProxy>,
207 ports: Vec<DomRoot<MessagePort>>,
208 ) {
209 let messageevent = MessageEvent::new(
210 cx,
211 scope,
212 atom!("message"),
213 false,
214 false,
215 message,
216 DOMString::from(origin.unwrap_or("")),
217 source
218 .map(|source| {
219 WindowProxyOrMessagePortOrServiceWorker::WindowProxy(DomRoot::from_ref(source))
220 })
221 .as_ref(),
222 DOMString::new(),
223 ports,
224 );
225 messageevent.upcast::<Event>().fire(cx, target);
226 }
227
228 pub(crate) fn dispatch_error(cx: &mut JSContext, target: &EventTarget, scope: &GlobalScope) {
229 let init = MessageEventInit::empty();
230 let messageevent = MessageEvent::new(
231 cx,
232 scope,
233 atom!("messageerror"),
234 init.parent.bubbles,
235 init.parent.cancelable,
236 init.data.as_handle_value(),
237 init.origin.clone(),
238 init.source.as_ref(),
239 init.lastEventId.clone(),
240 init.ports.clone(),
241 );
242 messageevent.upcast::<Event>().fire(cx, target);
243 }
244}
245
246impl MessageEventMethods<crate::DomTypeHolder> for MessageEvent {
247 fn Constructor(
249 cx: &mut JSContext,
250 global: &GlobalScope,
251 proto: Option<HandleObject>,
252 type_: DOMString,
253 init: &MessageEventInit,
254 ) -> Fallible<DomRoot<MessageEvent>> {
255 let ev = MessageEvent::new_with_proto(
256 cx,
257 global,
258 proto,
259 Atom::from(type_),
260 init.parent.bubbles,
261 init.parent.cancelable,
262 init.data.as_handle_value(),
263 init.origin.clone(),
264 init.source.as_ref(),
265 init.lastEventId.clone(),
266 init.ports.clone(),
267 );
268 Ok(ev)
269 }
270
271 fn Data(&self, _cx: &mut JSContext, mut retval: MutableHandleValue) {
273 retval.set(self.data.get())
274 }
275
276 fn Origin(&self) -> DOMString {
278 self.origin.borrow().clone().root()
279 }
280
281 fn GetSource(&self) -> Option<WindowProxyOrMessagePortOrServiceWorker> {
283 match &*self.source.borrow() {
284 Some(SrcObject::WindowProxy(i)) => Some(
285 WindowProxyOrMessagePortOrServiceWorker::WindowProxy(DomRoot::from_ref(i)),
286 ),
287 Some(SrcObject::MessagePort(i)) => Some(
288 WindowProxyOrMessagePortOrServiceWorker::MessagePort(DomRoot::from_ref(i)),
289 ),
290 Some(SrcObject::ServiceWorker(i)) => Some(
291 WindowProxyOrMessagePortOrServiceWorker::ServiceWorker(DomRoot::from_ref(i)),
292 ),
293 None => None,
294 }
295 }
296
297 fn LastEventId(&self) -> DOMString {
299 self.lastEventId.borrow().clone().root()
300 }
301
302 fn IsTrusted(&self) -> bool {
304 self.event.IsTrusted()
305 }
306
307 fn Ports(&self, cx: &mut JSContext, retval: MutableHandleValue) {
309 self.frozen_ports.get_or_init(
310 cx,
311 || {
312 self.ports
313 .borrow()
314 .iter()
315 .map(|port| DomRoot::from_ref(&**port))
316 .collect()
317 },
318 retval,
319 );
320 }
321
322 #[expect(non_snake_case)]
324 fn InitMessageEvent(
325 &self,
326 cx: &mut JSContext,
327 type_: DOMString,
328 bubbles: bool,
329 cancelable: bool,
330 data: HandleValue,
331 origin: DOMString,
332 lastEventId: DOMString,
333 source: Option<WindowProxyOrMessagePortOrServiceWorker>,
334 ports: Vec<DomRoot<MessagePort>>,
335 ) {
336 self.data.set(data.get());
337 *self.origin.safe_borrow_mut(cx.no_gc()) = origin.traced();
338 *self.source.safe_borrow_mut(cx.no_gc()) = source.as_ref().map(|source| source.into());
339 *self.lastEventId.safe_borrow_mut(cx.no_gc()) = lastEventId.traced();
340 *self.ports.safe_borrow_mut(cx.no_gc()) = ports
341 .into_iter()
342 .map(|port| Dom::from_ref(&*port))
343 .collect();
344 self.frozen_ports.clear();
345 self.event
346 .init_event(Atom::from(type_), bubbles, cancelable);
347 }
348}