Skip to main content

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