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