Skip to main content

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