1use dom_struct::dom_struct;
6use js::jsapi::Heap;
7use js::jsval::JSVal;
8use js::rust::{HandleObject, HandleValue, MutableHandleValue};
9use stylo_atoms::Atom;
10
11use crate::dom::bindings::cell::DomRefCell;
12use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
13use crate::dom::bindings::codegen::Bindings::MessageEventBinding;
14use crate::dom::bindings::codegen::Bindings::MessageEventBinding::MessageEventMethods;
15use crate::dom::bindings::codegen::UnionTypes::WindowProxyOrMessagePortOrServiceWorker;
16use crate::dom::bindings::error::Fallible;
17use crate::dom::bindings::frozenarray::CachedFrozenArray;
18use crate::dom::bindings::inheritance::Castable;
19use crate::dom::bindings::reflector::reflect_dom_object_with_proto;
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 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
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<DOMString>,
63 source: DomRefCell<Option<SrcObject>>,
64 lastEventId: DomRefCell<DOMString>,
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),
83 lastEventId: DomRefCell::new(lastEventId),
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(global: &GlobalScope, can_gc: CanGc) -> DomRoot<MessageEvent> {
95 Self::new_uninitialized_with_proto(global, None, can_gc)
96 }
97
98 fn new_uninitialized_with_proto(
99 global: &GlobalScope,
100 proto: Option<HandleObject>,
101 can_gc: CanGc,
102 ) -> DomRoot<MessageEvent> {
103 MessageEvent::new_initialized(
104 global,
105 proto,
106 HandleValue::undefined(),
107 DOMString::new(),
108 None,
109 DOMString::new(),
110 vec![],
111 can_gc,
112 )
113 }
114
115 #[allow(clippy::too_many_arguments)]
116 fn new_initialized(
117 global: &GlobalScope,
118 proto: Option<HandleObject>,
119 data: HandleValue,
120 origin: DOMString,
121 source: Option<&WindowProxyOrMessagePortOrServiceWorker>,
122 lastEventId: DOMString,
123 ports: Vec<DomRoot<MessagePort>>,
124 can_gc: CanGc,
125 ) -> DomRoot<MessageEvent> {
126 let ev = Box::new(MessageEvent::new_inherited(
127 origin,
128 source,
129 lastEventId,
130 ports,
131 ));
132 let ev = reflect_dom_object_with_proto(ev, global, proto, can_gc);
133 ev.data.set(data.get());
134
135 ev
136 }
137
138 #[allow(clippy::too_many_arguments)]
139 pub(crate) fn new(
140 global: &GlobalScope,
141 type_: Atom,
142 bubbles: bool,
143 cancelable: bool,
144 data: HandleValue,
145 origin: DOMString,
146 source: Option<&WindowProxyOrMessagePortOrServiceWorker>,
147 lastEventId: DOMString,
148 ports: Vec<DomRoot<MessagePort>>,
149 can_gc: CanGc,
150 ) -> DomRoot<MessageEvent> {
151 Self::new_with_proto(
152 global,
153 None,
154 type_,
155 bubbles,
156 cancelable,
157 data,
158 origin,
159 source,
160 lastEventId,
161 ports,
162 can_gc,
163 )
164 }
165
166 #[allow(clippy::too_many_arguments)]
167 fn new_with_proto(
168 global: &GlobalScope,
169 proto: Option<HandleObject>,
170 type_: Atom,
171 bubbles: bool,
172 cancelable: bool,
173 data: HandleValue,
174 origin: DOMString,
175 source: Option<&WindowProxyOrMessagePortOrServiceWorker>,
176 lastEventId: DOMString,
177 ports: Vec<DomRoot<MessagePort>>,
178 can_gc: CanGc,
179 ) -> DomRoot<MessageEvent> {
180 let ev = MessageEvent::new_initialized(
181 global,
182 proto,
183 data,
184 origin,
185 source,
186 lastEventId,
187 ports,
188 can_gc,
189 );
190 {
191 let event = ev.upcast::<Event>();
192 event.init_event(type_, bubbles, cancelable);
193 }
194 ev
195 }
196
197 pub(crate) fn dispatch_jsval(
198 target: &EventTarget,
199 scope: &GlobalScope,
200 message: HandleValue,
201 origin: Option<&str>,
202 source: Option<&WindowProxy>,
203 ports: Vec<DomRoot<MessagePort>>,
204 can_gc: CanGc,
205 ) {
206 let messageevent = MessageEvent::new(
207 scope,
208 atom!("message"),
209 false,
210 false,
211 message,
212 DOMString::from(origin.unwrap_or("")),
213 source
214 .map(|source| {
215 WindowProxyOrMessagePortOrServiceWorker::WindowProxy(DomRoot::from_ref(source))
216 })
217 .as_ref(),
218 DOMString::new(),
219 ports,
220 can_gc,
221 );
222 messageevent.upcast::<Event>().fire(target, can_gc);
223 }
224
225 pub(crate) fn dispatch_error(
226 cx: &mut js::context::JSContext,
227 target: &EventTarget,
228 scope: &GlobalScope,
229 ) {
230 let init = MessageEventBinding::MessageEventInit::empty();
231 let messageevent = MessageEvent::new(
232 scope,
233 atom!("messageerror"),
234 init.parent.bubbles,
235 init.parent.cancelable,
236 init.data.handle(),
237 init.origin.clone(),
238 init.source.as_ref(),
239 init.lastEventId.clone(),
240 init.ports.clone(),
241 CanGc::from_cx(cx),
242 );
243 messageevent
244 .upcast::<Event>()
245 .fire(target, CanGc::from_cx(cx));
246 }
247}
248
249impl MessageEventMethods<crate::DomTypeHolder> for MessageEvent {
250 fn Constructor(
252 global: &GlobalScope,
253 proto: Option<HandleObject>,
254 can_gc: CanGc,
255 type_: DOMString,
256 init: RootedTraceableBox<MessageEventBinding::MessageEventInit>,
257 ) -> Fallible<DomRoot<MessageEvent>> {
258 let ev = MessageEvent::new_with_proto(
259 global,
260 proto,
261 Atom::from(type_),
262 init.parent.bubbles,
263 init.parent.cancelable,
264 init.data.handle(),
265 init.origin.clone(),
266 init.source.as_ref(),
267 init.lastEventId.clone(),
268 init.ports.clone(),
269 can_gc,
270 );
271 Ok(ev)
272 }
273
274 fn Data(&self, _cx: JSContext, mut retval: MutableHandleValue) {
276 retval.set(self.data.get())
277 }
278
279 fn Origin(&self) -> DOMString {
281 self.origin.borrow().clone()
282 }
283
284 fn GetSource(&self) -> Option<WindowProxyOrMessagePortOrServiceWorker> {
286 match &*self.source.borrow() {
287 Some(SrcObject::WindowProxy(i)) => Some(
288 WindowProxyOrMessagePortOrServiceWorker::WindowProxy(DomRoot::from_ref(i)),
289 ),
290 Some(SrcObject::MessagePort(i)) => Some(
291 WindowProxyOrMessagePortOrServiceWorker::MessagePort(DomRoot::from_ref(i)),
292 ),
293 Some(SrcObject::ServiceWorker(i)) => Some(
294 WindowProxyOrMessagePortOrServiceWorker::ServiceWorker(DomRoot::from_ref(i)),
295 ),
296 None => None,
297 }
298 }
299
300 fn LastEventId(&self) -> DOMString {
302 self.lastEventId.borrow().clone()
303 }
304
305 fn IsTrusted(&self) -> bool {
307 self.event.IsTrusted()
308 }
309
310 fn Ports(&self, cx: JSContext, can_gc: CanGc, retval: MutableHandleValue) {
312 self.frozen_ports.get_or_init(
313 || {
314 self.ports
315 .borrow()
316 .iter()
317 .map(|port| DomRoot::from_ref(&**port))
318 .collect()
319 },
320 cx,
321 retval,
322 can_gc,
323 );
324 }
325
326 #[expect(non_snake_case)]
328 fn InitMessageEvent(
329 &self,
330 _cx: JSContext,
331 type_: DOMString,
332 bubbles: bool,
333 cancelable: bool,
334 data: HandleValue,
335 origin: DOMString,
336 lastEventId: DOMString,
337 source: Option<WindowProxyOrMessagePortOrServiceWorker>,
338 ports: Vec<DomRoot<MessagePort>>,
339 ) {
340 self.data.set(data.get());
341 *self.origin.borrow_mut() = origin;
342 *self.source.borrow_mut() = source.as_ref().map(|source| source.into());
343 *self.lastEventId.borrow_mut() = lastEventId;
344 *self.ports.borrow_mut() = ports
345 .into_iter()
346 .map(|port| Dom::from_ref(&*port))
347 .collect();
348 self.frozen_ports.clear();
349 self.event
350 .init_event(Atom::from(type_), bubbles, cancelable);
351 }
352}