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