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