Skip to main content

script/dom/event/
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::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/// <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        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    /// <https://w3c.github.io/ServiceWorker/#dom-extendablemessageevent-extendablemessageevent>
236    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    /// <https://w3c.github.io/ServiceWorker/#dom-extendablemessageevent-data>
263    fn Data(&self, _cx: JSContext, mut retval: MutableHandleValue) {
264        retval.set(self.data.get())
265    }
266
267    /// <https://w3c.github.io/ServiceWorker/#dom-extendablemessageevent-origin>
268    fn Origin(&self) -> DOMString {
269        self.origin.clone()
270    }
271
272    /// <https://w3c.github.io/ServiceWorker/#dom-extendablemessageevent-lasteventid>
273    fn LastEventId(&self) -> DOMString {
274        self.lastEventId.clone()
275    }
276
277    /// <https://dom.spec.whatwg.org/#dom-event-istrusted>
278    fn IsTrusted(&self) -> bool {
279        self.event.IsTrusted()
280    }
281
282    /// <https://w3c.github.io/ServiceWorker/#dom-extendablemessageevent-source>
283    fn GetSource(&self) -> Option<ClientOrServiceWorkerOrMessagePort> {
284        self.source.clone().map(|s| s.into())
285    }
286
287    /// <https://w3c.github.io/ServiceWorker/#extendablemessage-event-ports>
288    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}