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(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    /// <https://w3c.github.io/ServiceWorker/#extendableevent>
88    event: ExtendableEvent,
89    /// <https://w3c.github.io/ServiceWorker/#dom-extendablemessageevent-data>
90    #[ignore_malloc_size_of = "mozjs"]
91    data: Heap<JSVal>,
92    /// <https://w3c.github.io/ServiceWorker/#extendablemessage-event-origin>
93    origin: DOMString,
94    /// <https://w3c.github.io/ServiceWorker/#dom-extendablemessageevent-lasteventid>
95    lastEventId: DOMString,
96    /// <https://w3c.github.io/ServiceWorker/#dom-extendablemessageevent-source>
97    source: Option<MessageSource>,
98    /// <https://w3c.github.io/ServiceWorker/#dom-extendablemessageevent-ports>
99    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        cx: &mut JSContext,
129        global: &GlobalScope,
130        type_: Atom,
131        bubbles: bool,
132        cancelable: bool,
133        data: HandleValue,
134        origin: DOMString,
135        lastEventId: DOMString,
136        source: Option<MessageSource>,
137        ports: Vec<DomRoot<MessagePort>>,
138    ) -> DomRoot<ExtendableMessageEvent> {
139        Self::new_with_proto(
140            cx,
141            global,
142            None,
143            type_,
144            bubbles,
145            cancelable,
146            data,
147            origin,
148            lastEventId,
149            source,
150            ports,
151        )
152    }
153
154    #[allow(clippy::too_many_arguments)]
155    fn new_with_proto(
156        cx: &mut JSContext,
157        global: &GlobalScope,
158        proto: Option<HandleObject>,
159        type_: Atom,
160        bubbles: bool,
161        cancelable: bool,
162        data: HandleValue,
163        origin: DOMString,
164        lastEventId: DOMString,
165        source: Option<MessageSource>,
166        ports: Vec<DomRoot<MessagePort>>,
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_and_cx(ev, global, proto, cx);
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 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            cx,
197            scope,
198            atom!("message"),
199            false,
200            false,
201            message,
202            DOMString::new(),
203            DOMString::new(),
204            source,
205            ports,
206        );
207        Extendablemessageevent.upcast::<Event>().fire(cx, target);
208    }
209
210    pub(crate) fn dispatch_error(cx: &mut JSContext, target: &EventTarget, scope: &GlobalScope) {
211        let init = ExtendableMessageEventBinding::ExtendableMessageEventInit::empty();
212        let ExtendableMsgEvent = ExtendableMessageEvent::new(
213            cx,
214            scope,
215            atom!("messageerror"),
216            init.parent.parent.bubbles,
217            init.parent.parent.cancelable,
218            init.data.handle(),
219            init.origin.clone(),
220            init.lastEventId.clone(),
221            init.source
222                .as_ref()
223                .and_then(|s| s.as_ref().map(|s| s.into())),
224            init.ports.clone(),
225        );
226        ExtendableMsgEvent.upcast::<Event>().fire(cx, target);
227    }
228}
229
230impl ExtendableMessageEventMethods<crate::DomTypeHolder> for ExtendableMessageEvent {
231    /// <https://w3c.github.io/ServiceWorker/#dom-extendablemessageevent-extendablemessageevent>
232    fn Constructor(
233        cx: &mut JSContext,
234        worker: &ServiceWorkerGlobalScope,
235        proto: Option<HandleObject>,
236        type_: DOMString,
237        init: RootedTraceableBox<ExtendableMessageEventBinding::ExtendableMessageEventInit>,
238    ) -> Fallible<DomRoot<ExtendableMessageEvent>> {
239        let global = worker.upcast::<GlobalScope>();
240        let ev = ExtendableMessageEvent::new_with_proto(
241            cx,
242            global,
243            proto,
244            Atom::from(type_),
245            init.parent.parent.bubbles,
246            init.parent.parent.cancelable,
247            init.data.handle(),
248            init.origin.clone(),
249            init.lastEventId.clone(),
250            init.source
251                .as_ref()
252                .and_then(|s| s.as_ref().map(|s| s.into())),
253            vec![],
254        );
255        Ok(ev)
256    }
257
258    /// <https://w3c.github.io/ServiceWorker/#dom-extendablemessageevent-data>
259    fn Data(&self, _cx: &mut JSContext, mut retval: MutableHandleValue) {
260        retval.set(self.data.get())
261    }
262
263    /// <https://w3c.github.io/ServiceWorker/#dom-extendablemessageevent-origin>
264    fn Origin(&self) -> DOMString {
265        self.origin.clone()
266    }
267
268    /// <https://w3c.github.io/ServiceWorker/#dom-extendablemessageevent-lasteventid>
269    fn LastEventId(&self) -> DOMString {
270        self.lastEventId.clone()
271    }
272
273    /// <https://dom.spec.whatwg.org/#dom-event-istrusted>
274    fn IsTrusted(&self) -> bool {
275        self.event.IsTrusted()
276    }
277
278    /// <https://w3c.github.io/ServiceWorker/#dom-extendablemessageevent-source>
279    fn GetSource(&self) -> Option<ClientOrServiceWorkerOrMessagePort> {
280        self.source.clone().map(|s| s.into())
281    }
282
283    /// <https://w3c.github.io/ServiceWorker/#extendablemessage-event-ports>
284    fn Ports(&self, cx: &mut JSContext, retval: MutableHandleValue) {
285        self.frozen_ports.get_or_init(
286            cx,
287            || {
288                self.ports
289                    .iter()
290                    .map(|port| DomRoot::from_ref(&**port))
291                    .collect()
292            },
293            retval,
294        );
295    }
296}