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