Skip to main content

script/dom/serviceworker/
client.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, JSObject};
8use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
9use script_bindings::error::ErrorResult;
10use script_bindings::reflector::{Reflector, reflect_dom_object_with_cx};
11use script_bindings::root::DomRoot;
12use servo_base::generic_channel::GenericSender;
13use servo_base::id::ServiceWorkerId;
14use servo_constellation_traits::ServiceWorkerMsg;
15use servo_url::ServoUrl;
16
17use crate::dom::bindings::codegen::Bindings::ClientBinding::{ClientMethods, FrameType};
18use crate::dom::bindings::codegen::Bindings::MessagePortBinding::StructuredSerializeOptions;
19use crate::dom::bindings::error::Error;
20use crate::dom::bindings::reflector::DomGlobal;
21use crate::dom::bindings::str::{DOMString, USVString};
22use crate::dom::bindings::structuredclone;
23use crate::dom::bindings::trace::RootedTraceableBox;
24use crate::dom::globalscope::GlobalScope;
25
26#[dom_struct]
27pub(crate) struct Client {
28    reflector_: Reflector,
29
30    /// <https://w3c.github.io/ServiceWorker/#dfn-service-worker-client-service-worker-client>
31    /// Note: client concept implemented via messaging with the service worker manager.
32    #[no_trace]
33    swmanager_sender: GenericSender<ServiceWorkerMsg>,
34
35    #[no_trace]
36    url: ServoUrl,
37
38    /// <https://w3c.github.io/ServiceWorker/#dfn-service-worker-client-frame-type>
39    frame_type: FrameType,
40
41    #[no_trace]
42    worker_id: ServiceWorkerId,
43}
44
45impl Client {
46    fn new_inherited(
47        swmanager_sender: GenericSender<ServiceWorkerMsg>,
48        url: ServoUrl,
49        frame_type: FrameType,
50        worker_id: ServiceWorkerId,
51    ) -> Client {
52        Client {
53            reflector_: Reflector::new(),
54            swmanager_sender,
55            url,
56            frame_type,
57            worker_id,
58        }
59    }
60
61    pub(crate) fn new(
62        cx: &mut JSContext,
63        global: &GlobalScope,
64        swmanager_sender: GenericSender<ServiceWorkerMsg>,
65        url: ServoUrl,
66        frame_type: FrameType,
67        worker_id: ServiceWorkerId,
68    ) -> DomRoot<Client> {
69        reflect_dom_object_with_cx(
70            Box::new(Client::new_inherited(
71                swmanager_sender,
72                url,
73                frame_type,
74                worker_id,
75            )),
76            global,
77            cx,
78        )
79    }
80
81    /// <https://w3c.github.io/ServiceWorker/#dom-client-postmessage-message-options>
82    fn post_message_impl(
83        &self,
84        cx: &mut JSContext,
85        message: HandleValue,
86        transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
87    ) -> ErrorResult {
88        // Step 1: Let contextObject be this.
89        // Note: we're passing the worker id in the message to track contextObject.
90
91        // Step 2: Let sourceSettings be the contextObject’s relevant settings object.
92        let global = self.reflector_.global();
93
94        // Step 4.5.1: Let origin be sourceSettings’s origin.
95        // Note: done here and passing origin along in the message.
96        let origin = global.origin();
97
98        let data = structuredclone::write(cx, message, Some(transfer))?;
99        self.swmanager_sender
100            .send(ServiceWorkerMsg::ForwardWorkerMessage {
101                data,
102                source: self.worker_id,
103                url: self.url.clone(),
104                origin: origin.immutable().clone(),
105            })
106            .map_err(|_| {
107                Error::Type(c"Failed to send message to service worker manager".to_owned())
108            })
109    }
110}
111
112impl ClientMethods<crate::DomTypeHolder> for Client {
113    /// <https://w3c.github.io/ServiceWorker/#dom-client-postmessage>
114    fn PostMessage(
115        &self,
116        cx: &mut JSContext,
117        message: HandleValue,
118        transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
119    ) -> ErrorResult {
120        self.post_message_impl(cx, message, transfer)
121    }
122
123    /// <https://w3c.github.io/ServiceWorker/#dom-client-postmessage-message-options>
124    fn PostMessage_(
125        &self,
126        cx: &mut JSContext,
127        message: HandleValue,
128        options: RootedTraceableBox<StructuredSerializeOptions>,
129    ) -> ErrorResult {
130        let mut rooted = CustomAutoRooter::new(
131            options
132                .transfer
133                .iter()
134                .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
135                .collect(),
136        );
137        #[expect(unsafe_code)]
138        let guard = unsafe { CustomAutoRooterGuard::new(cx.raw_cx(), &mut rooted) };
139        self.post_message_impl(cx, message, guard)
140    }
141
142    /// <https://w3c.github.io/ServiceWorker/#client-url-attribute>
143    fn Url(&self) -> USVString {
144        USVString(self.url.as_str().to_owned())
145    }
146
147    /// <https://w3c.github.io/ServiceWorker/#client-frametype>
148    fn FrameType(&self) -> FrameType {
149        self.frame_type
150    }
151
152    /// <https://w3c.github.io/ServiceWorker/#client-id>
153    fn Id(&self) -> DOMString {
154        format!("{}", self.worker_id).into()
155    }
156}