1use std::cell::{Cell, RefCell};
6use std::ptr;
7use std::rc::Rc;
8
9use base::id::{MessagePortId, MessagePortIndex};
10use constellation_traits::{MessagePortImpl, PortMessageTask};
11use dom_struct::dom_struct;
12use js::context::JSContext;
13use js::jsapi::{Heap, JS_NewObject, JSObject};
14use js::jsval::UndefinedValue;
15use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
16use rustc_hash::FxHashMap;
17use script_bindings::conversions::SafeToJSValConvertible;
18
19use crate::dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
20use crate::dom::bindings::codegen::Bindings::MessagePortBinding::{
21 MessagePortMethods, StructuredSerializeOptions,
22};
23use crate::dom::bindings::conversions::root_from_object;
24use crate::dom::bindings::error::{Error, ErrorResult, ErrorToJsval, Fallible};
25use crate::dom::bindings::inheritance::Castable;
26use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
27use crate::dom::bindings::root::DomRoot;
28use crate::dom::bindings::structuredclone::{self, StructuredData};
29use crate::dom::bindings::trace::RootedTraceableBox;
30use crate::dom::bindings::transferable::Transferable;
31use crate::dom::bindings::utils::set_dictionary_property;
32use crate::dom::eventtarget::EventTarget;
33use crate::dom::globalscope::GlobalScope;
34use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
35
36#[dom_struct]
37pub(crate) struct MessagePort {
39 eventtarget: EventTarget,
40 #[no_trace]
41 message_port_id: MessagePortId,
42 #[no_trace]
43 entangled_port: RefCell<Option<MessagePortId>>,
44 detached: Cell<bool>,
45}
46
47impl MessagePort {
48 fn new_inherited(message_port_id: MessagePortId) -> MessagePort {
49 MessagePort {
50 eventtarget: EventTarget::new_inherited(),
51 entangled_port: RefCell::new(None),
52 detached: Cell::new(false),
53 message_port_id,
54 }
55 }
56
57 pub(crate) fn new(owner: &GlobalScope, can_gc: CanGc) -> DomRoot<MessagePort> {
59 let port_id = MessagePortId::new();
60 reflect_dom_object(Box::new(MessagePort::new_inherited(port_id)), owner, can_gc)
61 }
62
63 pub(crate) fn new_transferred(
65 owner: &GlobalScope,
66 transferred_port: MessagePortId,
67 entangled_port: Option<MessagePortId>,
68 can_gc: CanGc,
69 ) -> DomRoot<MessagePort> {
70 reflect_dom_object(
71 Box::new(MessagePort {
72 message_port_id: transferred_port,
73 eventtarget: EventTarget::new_inherited(),
74 detached: Cell::new(false),
75 entangled_port: RefCell::new(entangled_port),
76 }),
77 owner,
78 can_gc,
79 )
80 }
81
82 pub(crate) fn entangle(&self, other_id: MessagePortId) {
84 *self.entangled_port.borrow_mut() = Some(other_id);
85 }
86
87 pub(crate) fn disentangle(&self) -> Option<MessagePortId> {
89 self.entangled_port.borrow_mut().take()
92 }
93
94 pub(crate) fn disentangled(&self) -> bool {
98 self.entangled_port.borrow().is_none()
99 }
100
101 pub(crate) fn message_port_id(&self) -> &MessagePortId {
102 &self.message_port_id
103 }
104
105 pub(crate) fn detached(&self) -> bool {
106 self.detached.get()
107 }
108
109 fn set_onmessage(&self, listener: Option<Rc<EventHandlerNonNull>>) {
111 let eventtarget = self.upcast::<EventTarget>();
112 eventtarget.set_event_handler_common("message", listener);
113 }
114
115 #[expect(unsafe_code)]
117 fn post_message_impl(
118 &self,
119 cx: SafeJSContext,
120 message: HandleValue,
121 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
122 ) -> ErrorResult {
123 if self.detached.get() {
124 return Ok(());
125 }
126
127 let target_port = self.entangled_port.borrow();
130
131 let mut doomed = false;
133
134 let ports = transfer
135 .iter()
136 .filter_map(|&obj| unsafe { root_from_object::<MessagePort>(obj, cx.raw_cx()).ok() });
137 for port in ports {
138 if port.message_port_id() == self.message_port_id() {
140 return Err(Error::DataClone(None));
141 }
142
143 if let Some(target_id) = target_port.as_ref() {
145 if port.message_port_id() == target_id {
146 doomed = true;
147 }
148 }
149 }
150
151 let data = structuredclone::write(cx, message, Some(transfer))?;
153
154 if doomed {
155 return Ok(());
157 }
158
159 let incumbent = match GlobalScope::incumbent() {
162 None => unreachable!("postMessage called with no incumbent global"),
163 Some(incumbent) => incumbent,
164 };
165
166 let task = PortMessageTask {
168 origin: incumbent.origin().immutable().clone(),
169 data,
170 };
171
172 self.global()
174 .post_messageport_msg(*self.message_port_id(), task);
175 Ok(())
176 }
177
178 pub(crate) fn cross_realm_transform_send_error(&self, error: HandleValue, can_gc: CanGc) {
180 let _ = self.pack_and_post_message("error", error, can_gc);
183 }
184
185 pub(crate) fn pack_and_post_message_handling_error(
187 &self,
188 type_: &str,
189 value: HandleValue,
190 can_gc: CanGc,
191 ) -> ErrorResult {
192 let result = self.pack_and_post_message(type_, value, can_gc);
194
195 if let Err(error) = result.as_ref() {
197 let cx = GlobalScope::get_cx();
199 rooted!(in(*cx) let mut rooted_error = UndefinedValue());
200 error
201 .clone()
202 .to_jsval(cx, &self.global(), rooted_error.handle_mut(), can_gc);
203 self.cross_realm_transform_send_error(rooted_error.handle(), can_gc);
204 }
205
206 result
207 }
208
209 #[expect(unsafe_code)]
211 pub(crate) fn pack_and_post_message(
212 &self,
213 type_: &str,
214 value: HandleValue,
215 can_gc: CanGc,
216 ) -> ErrorResult {
217 let cx = GlobalScope::get_cx();
218
219 rooted!(in(*cx) let mut message = unsafe { JS_NewObject(*cx, ptr::null()) });
221 rooted!(in(*cx) let mut type_string = UndefinedValue());
222 type_.safe_to_jsval(cx, type_string.handle_mut(), can_gc);
223
224 set_dictionary_property(cx, message.handle(), c"type", type_string.handle())
226 .expect("Setting the message type should not fail.");
227
228 set_dictionary_property(cx, message.handle(), c"value", value)
230 .expect("Setting the message value should not fail.");
231
232 let mut rooted = CustomAutoRooter::new(vec![]);
237 let transfer = CustomAutoRooterGuard::new(*cx, &mut rooted);
238
239 rooted!(in(*cx) let mut message_val = UndefinedValue());
241 message.safe_to_jsval(cx, message_val.handle_mut(), can_gc);
242 self.post_message_impl(cx, message_val.handle(), transfer)
243 }
244}
245
246impl Transferable for MessagePort {
247 type Index = MessagePortIndex;
248 type Data = MessagePortImpl;
249
250 fn transfer(
252 &self,
253 _cx: &mut js::context::JSContext,
254 ) -> Fallible<(MessagePortId, MessagePortImpl)> {
255 if self.detached.get() {
260 return Err(Error::DataClone(None));
261 }
262
263 self.detached.set(true);
264 let id = self.message_port_id();
265
266 let transferred_port = self.global().mark_port_as_transferred(id);
268
269 Ok((*id, transferred_port))
270 }
271
272 fn transfer_receive(
274 cx: &mut js::context::JSContext,
275 owner: &GlobalScope,
276 id: MessagePortId,
277 port_impl: MessagePortImpl,
278 ) -> Result<DomRoot<Self>, ()> {
279 let transferred_port = MessagePort::new_transferred(
280 owner,
281 id,
282 port_impl.entangled_port_id(),
283 CanGc::from_cx(cx),
284 );
285 owner.track_message_port(&transferred_port, Some(port_impl));
286 Ok(transferred_port)
287 }
288
289 fn serialized_storage<'a>(
290 data: StructuredData<'a, '_>,
291 ) -> &'a mut Option<FxHashMap<MessagePortId, Self::Data>> {
292 match data {
293 StructuredData::Reader(r) => &mut r.port_impls,
294 StructuredData::Writer(w) => &mut w.ports,
295 }
296 }
297}
298
299impl MessagePortMethods<crate::DomTypeHolder> for MessagePort {
300 fn PostMessage(
302 &self,
303 cx: &mut JSContext,
304 message: HandleValue,
305 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
306 ) -> ErrorResult {
307 if self.detached.get() {
308 return Ok(());
309 }
310 self.post_message_impl(cx.into(), message, transfer)
311 }
312
313 fn PostMessage_(
315 &self,
316 cx: &mut JSContext,
317 message: HandleValue,
318 options: RootedTraceableBox<StructuredSerializeOptions>,
319 ) -> ErrorResult {
320 if self.detached.get() {
321 return Ok(());
322 }
323 let mut rooted = CustomAutoRooter::new(
324 options
325 .transfer
326 .iter()
327 .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
328 .collect(),
329 );
330 #[expect(unsafe_code)]
331 let guard = unsafe { CustomAutoRooterGuard::new(cx.raw_cx(), &mut rooted) };
332 self.post_message_impl(cx.into(), message, guard)
333 }
334
335 fn Start(&self, can_gc: CanGc) {
337 if self.detached.get() {
338 return;
339 }
340 self.global()
341 .start_message_port(self.message_port_id(), can_gc);
342 }
343
344 fn Close(&self, can_gc: CanGc) {
346 self.detached.set(true);
348
349 let global = self.global();
350 global.close_message_port(self.message_port_id());
351
352 global.disentangle_port(self, can_gc);
354 }
355
356 fn GetOnmessage(&self, can_gc: CanGc) -> Option<Rc<EventHandlerNonNull>> {
358 if self.detached.get() {
359 return None;
360 }
361 let eventtarget = self.upcast::<EventTarget>();
362 eventtarget.get_event_handler_common("message", can_gc)
363 }
364
365 fn SetOnmessage(&self, listener: Option<Rc<EventHandlerNonNull>>, can_gc: CanGc) {
367 if self.detached.get() {
368 return;
369 }
370 self.set_onmessage(listener);
371 self.global()
373 .start_message_port(self.message_port_id(), can_gc);
374 }
375
376 event_handler!(messageerror, GetOnmessageerror, SetOnmessageerror);
378
379 event_handler!(close, GetOnclose, SetOnclose);
381}