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, allow(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#[allow(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#[allow(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(target: &EventTarget, scope: &GlobalScope, can_gc: CanGc) {
226 let init = MessageEventBinding::MessageEventInit::empty();
227 let messageevent = MessageEvent::new(
228 scope,
229 atom!("messageerror"),
230 init.parent.bubbles,
231 init.parent.cancelable,
232 init.data.handle(),
233 init.origin.clone(),
234 init.source.as_ref(),
235 init.lastEventId.clone(),
236 init.ports.clone(),
237 can_gc,
238 );
239 messageevent.upcast::<Event>().fire(target, can_gc);
240 }
241}
242
243impl MessageEventMethods<crate::DomTypeHolder> for MessageEvent {
244 fn Constructor(
246 global: &GlobalScope,
247 proto: Option<HandleObject>,
248 can_gc: CanGc,
249 type_: DOMString,
250 init: RootedTraceableBox<MessageEventBinding::MessageEventInit>,
251 ) -> Fallible<DomRoot<MessageEvent>> {
252 let ev = MessageEvent::new_with_proto(
253 global,
254 proto,
255 Atom::from(type_),
256 init.parent.bubbles,
257 init.parent.cancelable,
258 init.data.handle(),
259 init.origin.clone(),
260 init.source.as_ref(),
261 init.lastEventId.clone(),
262 init.ports.clone(),
263 can_gc,
264 );
265 Ok(ev)
266 }
267
268 fn Data(&self, _cx: JSContext, mut retval: MutableHandleValue) {
270 retval.set(self.data.get())
271 }
272
273 fn Origin(&self) -> DOMString {
275 self.origin.borrow().clone()
276 }
277
278 fn GetSource(&self) -> Option<WindowProxyOrMessagePortOrServiceWorker> {
280 match &*self.source.borrow() {
281 Some(SrcObject::WindowProxy(i)) => Some(
282 WindowProxyOrMessagePortOrServiceWorker::WindowProxy(DomRoot::from_ref(i)),
283 ),
284 Some(SrcObject::MessagePort(i)) => Some(
285 WindowProxyOrMessagePortOrServiceWorker::MessagePort(DomRoot::from_ref(i)),
286 ),
287 Some(SrcObject::ServiceWorker(i)) => Some(
288 WindowProxyOrMessagePortOrServiceWorker::ServiceWorker(DomRoot::from_ref(i)),
289 ),
290 None => None,
291 }
292 }
293
294 fn LastEventId(&self) -> DOMString {
296 self.lastEventId.borrow().clone()
297 }
298
299 fn IsTrusted(&self) -> bool {
301 self.event.IsTrusted()
302 }
303
304 fn Ports(&self, cx: JSContext, can_gc: CanGc, retval: MutableHandleValue) {
306 self.frozen_ports.get_or_init(
307 || {
308 self.ports
309 .borrow()
310 .iter()
311 .map(|port| DomRoot::from_ref(&**port))
312 .collect()
313 },
314 cx,
315 retval,
316 can_gc,
317 );
318 }
319
320 #[allow(non_snake_case)]
322 fn InitMessageEvent(
323 &self,
324 _cx: JSContext,
325 type_: DOMString,
326 bubbles: bool,
327 cancelable: bool,
328 data: HandleValue,
329 origin: DOMString,
330 lastEventId: DOMString,
331 source: Option<WindowProxyOrMessagePortOrServiceWorker>,
332 ports: Vec<DomRoot<MessagePort>>,
333 ) {
334 self.data.set(data.get());
335 *self.origin.borrow_mut() = origin.clone();
336 *self.source.borrow_mut() = source.as_ref().map(|source| source.into());
337 *self.lastEventId.borrow_mut() = lastEventId.clone();
338 *self.ports.borrow_mut() = ports
339 .into_iter()
340 .map(|port| Dom::from_ref(&*port))
341 .collect();
342 self.frozen_ports.clear();
343 self.event
344 .init_event(Atom::from(type_), bubbles, cancelable);
345 }
346}