1use dom_struct::dom_struct;
6use js::jsapi::Heap;
7use js::jsval::JSVal;
8use js::rust::{HandleObject, HandleValue, MutableHandleValue};
9use script_bindings::reflector::reflect_dom_object_with_proto;
10use stylo_atoms::Atom;
11
12use crate::dom::bindings::codegen::Bindings::ExtendableEventBinding::ExtendableEvent_Binding::ExtendableEventMethods;
13use crate::dom::bindings::codegen::Bindings::ExtendableMessageEventBinding;
14use crate::dom::bindings::codegen::Bindings::ExtendableMessageEventBinding::ExtendableMessageEventMethods;
15use crate::dom::bindings::codegen::UnionTypes::ClientOrServiceWorkerOrMessagePort;
16use crate::dom::bindings::error::Fallible;
17use crate::dom::bindings::frozenarray::CachedFrozenArray;
18use crate::dom::bindings::inheritance::Castable;
19use crate::dom::bindings::root::{Dom, DomRoot};
20use crate::dom::bindings::str::DOMString;
21use crate::dom::bindings::trace::RootedTraceableBox;
22use crate::dom::client::Client;
23use crate::dom::event::Event;
24use crate::dom::eventtarget::EventTarget;
25use crate::dom::extendableevent::ExtendableEvent;
26use crate::dom::globalscope::GlobalScope;
27use crate::dom::messageport::MessagePort;
28use crate::dom::serviceworker::ServiceWorker;
29use crate::dom::serviceworkerglobalscope::ServiceWorkerGlobalScope;
30use crate::script_runtime::{CanGc, JSContext};
31
32#[derive(Clone, JSTraceable, MallocSizeOf)]
34pub(crate) enum MessageSource {
35 Client(DomRoot<Client>),
36 ServiceWorker(DomRoot<ServiceWorker>),
37 MessagePort(DomRoot<MessagePort>),
38}
39
40impl From<ClientOrServiceWorkerOrMessagePort> for MessageSource {
41 fn from(value: ClientOrServiceWorkerOrMessagePort) -> Self {
42 match value {
43 ClientOrServiceWorkerOrMessagePort::Client(client) => MessageSource::Client(client),
44 ClientOrServiceWorkerOrMessagePort::ServiceWorker(sw) => {
45 MessageSource::ServiceWorker(sw)
46 },
47 ClientOrServiceWorkerOrMessagePort::MessagePort(port) => {
48 MessageSource::MessagePort(port)
49 },
50 }
51 }
52}
53
54impl From<&ClientOrServiceWorkerOrMessagePort> for MessageSource {
55 fn from(value: &ClientOrServiceWorkerOrMessagePort) -> Self {
56 match value {
57 ClientOrServiceWorkerOrMessagePort::Client(client) => {
58 MessageSource::Client(DomRoot::from_ref(client))
59 },
60 ClientOrServiceWorkerOrMessagePort::ServiceWorker(sw) => {
61 MessageSource::ServiceWorker(DomRoot::from_ref(sw))
62 },
63 ClientOrServiceWorkerOrMessagePort::MessagePort(port) => {
64 MessageSource::MessagePort(DomRoot::from_ref(port))
65 },
66 }
67 }
68}
69
70impl From<MessageSource> for ClientOrServiceWorkerOrMessagePort {
71 fn from(value: MessageSource) -> Self {
72 match value {
73 MessageSource::Client(client) => ClientOrServiceWorkerOrMessagePort::Client(client),
74 MessageSource::ServiceWorker(sw) => {
75 ClientOrServiceWorkerOrMessagePort::ServiceWorker(sw)
76 },
77 MessageSource::MessagePort(port) => {
78 ClientOrServiceWorkerOrMessagePort::MessagePort(port)
79 },
80 }
81 }
82}
83
84#[dom_struct]
85#[expect(non_snake_case)]
86pub(crate) struct ExtendableMessageEvent {
87 event: ExtendableEvent,
89 #[ignore_malloc_size_of = "mozjs"]
91 data: Heap<JSVal>,
92 origin: DOMString,
94 lastEventId: DOMString,
96 source: Option<MessageSource>,
98 ports: Vec<Dom<MessagePort>>,
100 #[ignore_malloc_size_of = "mozjs"]
101 frozen_ports: CachedFrozenArray,
102}
103
104#[expect(non_snake_case)]
105impl ExtendableMessageEvent {
106 pub(crate) fn new_inherited(
107 origin: DOMString,
108 lastEventId: DOMString,
109 source: Option<MessageSource>,
110 ports: Vec<DomRoot<MessagePort>>,
111 ) -> ExtendableMessageEvent {
112 ExtendableMessageEvent {
113 event: ExtendableEvent::new_inherited(),
114 data: Heap::default(),
115 origin,
116 lastEventId,
117 source,
118 ports: ports
119 .into_iter()
120 .map(|port| Dom::from_ref(&*port))
121 .collect(),
122 frozen_ports: CachedFrozenArray::new(),
123 }
124 }
125
126 #[allow(clippy::too_many_arguments)]
127 pub(crate) fn new(
128 global: &GlobalScope,
129 type_: Atom,
130 bubbles: bool,
131 cancelable: bool,
132 data: HandleValue,
133 origin: DOMString,
134 lastEventId: DOMString,
135 source: Option<MessageSource>,
136 ports: Vec<DomRoot<MessagePort>>,
137 can_gc: CanGc,
138 ) -> DomRoot<ExtendableMessageEvent> {
139 Self::new_with_proto(
140 global,
141 None,
142 type_,
143 bubbles,
144 cancelable,
145 data,
146 origin,
147 lastEventId,
148 source,
149 ports,
150 can_gc,
151 )
152 }
153
154 #[allow(clippy::too_many_arguments)]
155 fn new_with_proto(
156 global: &GlobalScope,
157 proto: Option<HandleObject>,
158 type_: Atom,
159 bubbles: bool,
160 cancelable: bool,
161 data: HandleValue,
162 origin: DOMString,
163 lastEventId: DOMString,
164 source: Option<MessageSource>,
165 ports: Vec<DomRoot<MessagePort>>,
166 can_gc: CanGc,
167 ) -> DomRoot<ExtendableMessageEvent> {
168 let ev = Box::new(ExtendableMessageEvent::new_inherited(
169 origin,
170 lastEventId,
171 source,
172 ports,
173 ));
174 let ev = reflect_dom_object_with_proto(ev, global, proto, can_gc);
175 {
176 let event = ev.upcast::<Event>();
177 event.init_event(type_, bubbles, cancelable);
178 }
179 ev.data.set(data.get());
180
181 ev
182 }
183}
184
185#[expect(non_snake_case)]
186impl ExtendableMessageEvent {
187 pub(crate) fn dispatch_jsval(
188 cx: &mut js::context::JSContext,
189 target: &EventTarget,
190 scope: &GlobalScope,
191 message: HandleValue,
192 source: Option<MessageSource>,
193 ports: Vec<DomRoot<MessagePort>>,
194 ) {
195 let Extendablemessageevent = ExtendableMessageEvent::new(
196 scope,
197 atom!("message"),
198 false,
199 false,
200 message,
201 DOMString::new(),
202 DOMString::new(),
203 source,
204 ports,
205 CanGc::from_cx(cx),
206 );
207 Extendablemessageevent.upcast::<Event>().fire(cx, target);
208 }
209
210 pub(crate) fn dispatch_error(
211 cx: &mut js::context::JSContext,
212 target: &EventTarget,
213 scope: &GlobalScope,
214 ) {
215 let init = ExtendableMessageEventBinding::ExtendableMessageEventInit::empty();
216 let ExtendableMsgEvent = ExtendableMessageEvent::new(
217 scope,
218 atom!("messageerror"),
219 init.parent.parent.bubbles,
220 init.parent.parent.cancelable,
221 init.data.handle(),
222 init.origin.clone(),
223 init.lastEventId.clone(),
224 init.source
225 .as_ref()
226 .and_then(|s| s.as_ref().map(|s| s.into())),
227 init.ports.clone(),
228 CanGc::from_cx(cx),
229 );
230 ExtendableMsgEvent.upcast::<Event>().fire(cx, target);
231 }
232}
233
234impl ExtendableMessageEventMethods<crate::DomTypeHolder> for ExtendableMessageEvent {
235 fn Constructor(
237 worker: &ServiceWorkerGlobalScope,
238 proto: Option<HandleObject>,
239 can_gc: CanGc,
240 type_: DOMString,
241 init: RootedTraceableBox<ExtendableMessageEventBinding::ExtendableMessageEventInit>,
242 ) -> Fallible<DomRoot<ExtendableMessageEvent>> {
243 let global = worker.upcast::<GlobalScope>();
244 let ev = ExtendableMessageEvent::new_with_proto(
245 global,
246 proto,
247 Atom::from(type_),
248 init.parent.parent.bubbles,
249 init.parent.parent.cancelable,
250 init.data.handle(),
251 init.origin.clone(),
252 init.lastEventId.clone(),
253 init.source
254 .as_ref()
255 .and_then(|s| s.as_ref().map(|s| s.into())),
256 vec![],
257 can_gc,
258 );
259 Ok(ev)
260 }
261
262 fn Data(&self, _cx: JSContext, mut retval: MutableHandleValue) {
264 retval.set(self.data.get())
265 }
266
267 fn Origin(&self) -> DOMString {
269 self.origin.clone()
270 }
271
272 fn LastEventId(&self) -> DOMString {
274 self.lastEventId.clone()
275 }
276
277 fn IsTrusted(&self) -> bool {
279 self.event.IsTrusted()
280 }
281
282 fn GetSource(&self) -> Option<ClientOrServiceWorkerOrMessagePort> {
284 self.source.clone().map(|s| s.into())
285 }
286
287 fn Ports(&self, cx: JSContext, can_gc: CanGc, retval: MutableHandleValue) {
289 self.frozen_ports.get_or_init(
290 || {
291 self.ports
292 .iter()
293 .map(|port| DomRoot::from_ref(&**port))
294 .collect()
295 },
296 cx,
297 retval,
298 can_gc,
299 );
300 }
301}