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