Skip to main content

script/dom/event/
messageevent.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use 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::domstring::TracedDOMString;
12use script_bindings::reflector::reflect_dom_object_with_proto;
13use stylo_atoms::Atom;
14
15use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
16use crate::dom::bindings::codegen::Bindings::MessageEventBinding::{
17    MessageEventInit, MessageEventMethods,
18};
19use crate::dom::bindings::codegen::UnionTypes::WindowProxyOrMessagePortOrServiceWorker;
20use crate::dom::bindings::error::Fallible;
21use crate::dom::bindings::frozenarray::CachedFrozenArray;
22use crate::dom::bindings::inheritance::Castable;
23use crate::dom::bindings::root::{AsHandleValue, Dom, DomRoot};
24use crate::dom::bindings::str::DOMString;
25use crate::dom::event::Event;
26use crate::dom::eventtarget::EventTarget;
27use crate::dom::globalscope::GlobalScope;
28use crate::dom::messageport::MessagePort;
29use crate::dom::serviceworker::ServiceWorker;
30use crate::dom::windowproxy::WindowProxy;
31
32#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
33#[derive(JSTraceable, MallocSizeOf)]
34enum SrcObject {
35    WindowProxy(Dom<WindowProxy>),
36    MessagePort(Dom<MessagePort>),
37    ServiceWorker(Dom<ServiceWorker>),
38}
39
40impl From<&WindowProxyOrMessagePortOrServiceWorker> for SrcObject {
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#[expect(non_snake_case)]
58pub(crate) struct MessageEvent {
59    event: Event,
60    #[ignore_malloc_size_of = "mozjs"]
61    data: Heap<JSVal>,
62    origin: DomRefCell<TracedDOMString>,
63    source: DomRefCell<Option<SrcObject>>,
64    lastEventId: DomRefCell<TracedDOMString>,
65    ports: DomRefCell<Vec<Dom<MessagePort>>>,
66    #[ignore_malloc_size_of = "mozjs"]
67    frozen_ports: CachedFrozenArray,
68}
69
70#[expect(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.traced()),
83            lastEventId: DomRefCell::new(lastEventId.traced()),
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(
95        cx: &mut JSContext,
96        global: &GlobalScope,
97    ) -> DomRoot<MessageEvent> {
98        Self::new_uninitialized_with_proto(cx, global, None)
99    }
100
101    fn new_uninitialized_with_proto(
102        cx: &mut JSContext,
103        global: &GlobalScope,
104        proto: Option<HandleObject>,
105    ) -> DomRoot<MessageEvent> {
106        MessageEvent::new_initialized(
107            cx,
108            global,
109            proto,
110            HandleValue::undefined(),
111            DOMString::new(),
112            None,
113            DOMString::new(),
114            vec![],
115        )
116    }
117
118    #[expect(clippy::too_many_arguments)]
119    fn new_initialized(
120        cx: &mut JSContext,
121        global: &GlobalScope,
122        proto: Option<HandleObject>,
123        data: HandleValue,
124        origin: DOMString,
125        source: Option<&WindowProxyOrMessagePortOrServiceWorker>,
126        lastEventId: DOMString,
127        ports: Vec<DomRoot<MessagePort>>,
128    ) -> DomRoot<MessageEvent> {
129        let ev = Box::new(MessageEvent::new_inherited(
130            origin,
131            source,
132            lastEventId,
133            ports,
134        ));
135        let ev = reflect_dom_object_with_proto(cx, ev, global, proto);
136        ev.data.set(data.get());
137
138        ev
139    }
140
141    #[expect(clippy::too_many_arguments)]
142    pub(crate) fn new(
143        cx: &mut JSContext,
144        global: &GlobalScope,
145        type_: Atom,
146        bubbles: bool,
147        cancelable: bool,
148        data: HandleValue,
149        origin: DOMString,
150        source: Option<&WindowProxyOrMessagePortOrServiceWorker>,
151        lastEventId: DOMString,
152        ports: Vec<DomRoot<MessagePort>>,
153    ) -> DomRoot<MessageEvent> {
154        Self::new_with_proto(
155            cx,
156            global,
157            None,
158            type_,
159            bubbles,
160            cancelable,
161            data,
162            origin,
163            source,
164            lastEventId,
165            ports,
166        )
167    }
168
169    #[expect(clippy::too_many_arguments)]
170    fn new_with_proto(
171        cx: &mut JSContext,
172        global: &GlobalScope,
173        proto: Option<HandleObject>,
174        type_: Atom,
175        bubbles: bool,
176        cancelable: bool,
177        data: HandleValue,
178        origin: DOMString,
179        source: Option<&WindowProxyOrMessagePortOrServiceWorker>,
180        lastEventId: DOMString,
181        ports: Vec<DomRoot<MessagePort>>,
182    ) -> DomRoot<MessageEvent> {
183        let ev = MessageEvent::new_initialized(
184            cx,
185            global,
186            proto,
187            data,
188            origin,
189            source,
190            lastEventId,
191            ports,
192        );
193        {
194            let event = ev.upcast::<Event>();
195            event.init_event(type_, bubbles, cancelable);
196        }
197        ev
198    }
199
200    pub(crate) fn dispatch_jsval(
201        cx: &mut JSContext,
202        target: &EventTarget,
203        scope: &GlobalScope,
204        message: HandleValue,
205        origin: Option<&str>,
206        source: Option<&WindowProxy>,
207        ports: Vec<DomRoot<MessagePort>>,
208    ) {
209        let messageevent = MessageEvent::new(
210            cx,
211            scope,
212            atom!("message"),
213            false,
214            false,
215            message,
216            DOMString::from(origin.unwrap_or("")),
217            source
218                .map(|source| {
219                    WindowProxyOrMessagePortOrServiceWorker::WindowProxy(DomRoot::from_ref(source))
220                })
221                .as_ref(),
222            DOMString::new(),
223            ports,
224        );
225        messageevent.upcast::<Event>().fire(cx, target);
226    }
227
228    pub(crate) fn dispatch_error(cx: &mut JSContext, target: &EventTarget, scope: &GlobalScope) {
229        let init = MessageEventInit::empty();
230        let messageevent = MessageEvent::new(
231            cx,
232            scope,
233            atom!("messageerror"),
234            init.parent.bubbles,
235            init.parent.cancelable,
236            init.data.as_handle_value(),
237            init.origin.clone(),
238            init.source.as_ref(),
239            init.lastEventId.clone(),
240            init.ports.clone(),
241        );
242        messageevent.upcast::<Event>().fire(cx, target);
243    }
244}
245
246impl MessageEventMethods<crate::DomTypeHolder> for MessageEvent {
247    /// <https://html.spec.whatwg.org/multipage/#messageevent>
248    fn Constructor(
249        cx: &mut JSContext,
250        global: &GlobalScope,
251        proto: Option<HandleObject>,
252        type_: DOMString,
253        init: &MessageEventInit,
254    ) -> Fallible<DomRoot<MessageEvent>> {
255        let ev = MessageEvent::new_with_proto(
256            cx,
257            global,
258            proto,
259            Atom::from(type_),
260            init.parent.bubbles,
261            init.parent.cancelable,
262            init.data.as_handle_value(),
263            init.origin.clone(),
264            init.source.as_ref(),
265            init.lastEventId.clone(),
266            init.ports.clone(),
267        );
268        Ok(ev)
269    }
270
271    /// <https://html.spec.whatwg.org/multipage/#dom-messageevent-data>
272    fn Data(&self, _cx: &mut JSContext, mut retval: MutableHandleValue) {
273        retval.set(self.data.get())
274    }
275
276    /// <https://html.spec.whatwg.org/multipage/#dom-messageevent-origin>
277    fn Origin(&self) -> DOMString {
278        self.origin.borrow().clone().root()
279    }
280
281    /// <https://html.spec.whatwg.org/multipage/#dom-messageevent-source>
282    fn GetSource(&self) -> Option<WindowProxyOrMessagePortOrServiceWorker> {
283        match &*self.source.borrow() {
284            Some(SrcObject::WindowProxy(i)) => Some(
285                WindowProxyOrMessagePortOrServiceWorker::WindowProxy(DomRoot::from_ref(i)),
286            ),
287            Some(SrcObject::MessagePort(i)) => Some(
288                WindowProxyOrMessagePortOrServiceWorker::MessagePort(DomRoot::from_ref(i)),
289            ),
290            Some(SrcObject::ServiceWorker(i)) => Some(
291                WindowProxyOrMessagePortOrServiceWorker::ServiceWorker(DomRoot::from_ref(i)),
292            ),
293            None => None,
294        }
295    }
296
297    /// <https://html.spec.whatwg.org/multipage/#dom-messageevent-lasteventid>
298    fn LastEventId(&self) -> DOMString {
299        self.lastEventId.borrow().clone().root()
300    }
301
302    /// <https://dom.spec.whatwg.org/#dom-event-istrusted>
303    fn IsTrusted(&self) -> bool {
304        self.event.IsTrusted()
305    }
306
307    /// <https://html.spec.whatwg.org/multipage/#dom-messageevent-ports>
308    fn Ports(&self, cx: &mut JSContext, retval: MutableHandleValue) {
309        self.frozen_ports.get_or_init(
310            cx,
311            || {
312                self.ports
313                    .borrow()
314                    .iter()
315                    .map(|port| DomRoot::from_ref(&**port))
316                    .collect()
317            },
318            retval,
319        );
320    }
321
322    /// <https://html.spec.whatwg.org/multipage/#dom-messageevent-initmessageevent>
323    #[expect(non_snake_case)]
324    fn InitMessageEvent(
325        &self,
326        cx: &mut JSContext,
327        type_: DOMString,
328        bubbles: bool,
329        cancelable: bool,
330        data: HandleValue,
331        origin: DOMString,
332        lastEventId: DOMString,
333        source: Option<WindowProxyOrMessagePortOrServiceWorker>,
334        ports: Vec<DomRoot<MessagePort>>,
335    ) {
336        self.data.set(data.get());
337        *self.origin.safe_borrow_mut(cx.no_gc()) = origin.traced();
338        *self.source.safe_borrow_mut(cx.no_gc()) = source.as_ref().map(|source| source.into());
339        *self.lastEventId.safe_borrow_mut(cx.no_gc()) = lastEventId.traced();
340        *self.ports.safe_borrow_mut(cx.no_gc()) = ports
341            .into_iter()
342            .map(|port| Dom::from_ref(&*port))
343            .collect();
344        self.frozen_ports.clear();
345        self.event
346            .init_event(Atom::from(type_), bubbles, cancelable);
347    }
348}