Skip to main content

script/dom/serviceworker/
serviceworker.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 std::cell::Cell;
6
7use dom_struct::dom_struct;
8use js::context::JSContext;
9use js::jsapi::JSObject;
10use js::rust::{CustomAutoRooterGuard, HandleValue};
11use script_bindings::cell::DomRefCell;
12use script_bindings::reflector::reflect_dom_object;
13use servo_base::id::ServiceWorkerId;
14use servo_constellation_traits::{
15    DOMMessage, ScriptToConstellationMessage, ServiceWorkerState as ConstellationServiceWorkerState,
16};
17use servo_url::ServoUrl;
18
19use crate::dom::abstractworker::SimpleWorkerErrorHandler;
20use crate::dom::bindings::codegen::Bindings::MessagePortBinding::StructuredSerializeOptions;
21use crate::dom::bindings::codegen::Bindings::ServiceWorkerBinding::{
22    ServiceWorkerMethods, ServiceWorkerState,
23};
24use crate::dom::bindings::error::{Error, ErrorResult};
25use crate::dom::bindings::inheritance::Castable;
26use crate::dom::bindings::refcounted::Trusted;
27use crate::dom::bindings::reflector::DomGlobal;
28use crate::dom::bindings::root::DomRoot;
29use crate::dom::bindings::str::USVString;
30use crate::dom::bindings::structuredclone;
31use crate::dom::eventtarget::EventTarget;
32use crate::dom::globalscope::GlobalScope;
33use crate::tasks::task::TaskOnce;
34
35pub(crate) type TrustedServiceWorkerAddress = Trusted<ServiceWorker>;
36
37#[dom_struct]
38pub(crate) struct ServiceWorker {
39    eventtarget: EventTarget,
40    script_url: DomRefCell<String>,
41    #[no_trace]
42    scope_url: ServoUrl,
43    state: Cell<ServiceWorkerState>,
44    #[no_trace]
45    worker_id: ServiceWorkerId,
46}
47
48impl ServiceWorker {
49    fn new_inherited(
50        script_url: &str,
51        scope_url: ServoUrl,
52        worker_id: ServiceWorkerId,
53    ) -> ServiceWorker {
54        ServiceWorker {
55            eventtarget: EventTarget::new_inherited(),
56            script_url: DomRefCell::new(String::from(script_url)),
57            state: Cell::new(ServiceWorkerState::Installing),
58            scope_url,
59            worker_id,
60        }
61    }
62
63    pub(crate) fn new(
64        cx: &mut js::context::JSContext,
65        global: &GlobalScope,
66        script_url: ServoUrl,
67        scope_url: ServoUrl,
68        worker_id: ServiceWorkerId,
69    ) -> DomRoot<ServiceWorker> {
70        reflect_dom_object(
71            cx,
72            Box::new(ServiceWorker::new_inherited(
73                script_url.as_str(),
74                scope_url,
75                worker_id,
76            )),
77            global,
78        )
79    }
80
81    pub(crate) fn dispatch_simple_error(cx: &mut JSContext, address: TrustedServiceWorkerAddress) {
82        let service_worker = address.root();
83        service_worker.upcast().fire_event(cx, atom!("error"));
84    }
85
86    pub(crate) fn get_script_url(&self) -> ServoUrl {
87        ServoUrl::parse(&self.script_url.borrow().clone()).unwrap()
88    }
89
90    pub(crate) fn set_state(&self, state: ConstellationServiceWorkerState) {
91        match state {
92            ConstellationServiceWorkerState::Parsed => {
93                debug_assert!(false, "An update is never for the parsed state.")
94            },
95            ConstellationServiceWorkerState::Installing => {
96                self.state.set(ServiceWorkerState::Installing);
97            },
98            ConstellationServiceWorkerState::Installed => {
99                self.state.set(ServiceWorkerState::Installed);
100            },
101            ConstellationServiceWorkerState::Activating => {
102                self.state.set(ServiceWorkerState::Activating);
103            },
104            ConstellationServiceWorkerState::Activated => {
105                self.state.set(ServiceWorkerState::Activated);
106            },
107            ConstellationServiceWorkerState::Redundant => {
108                self.state.set(ServiceWorkerState::Redundant);
109            },
110        }
111    }
112
113    /// <https://w3c.github.io/ServiceWorker/#service-worker-postmessage>
114    fn post_message_impl(
115        &self,
116        cx: &mut JSContext,
117        message: HandleValue,
118        transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
119    ) -> ErrorResult {
120        // Step 1
121        if let ServiceWorkerState::Redundant = self.state.get() {
122            return Err(Error::InvalidState(None));
123        }
124        // Step 7
125        let data = structuredclone::write(cx, message, Some(transfer))?;
126        let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
127        let pipeline_id = incumbent.pipeline_id();
128        let msg_vec = DOMMessage {
129            origin: incumbent.origin().immutable().clone(),
130            pipeline_id,
131            data,
132        };
133        let _ = self.global().script_to_constellation_chan().send(
134            ScriptToConstellationMessage::ForwardDOMMessage(msg_vec, self.scope_url.clone()),
135        );
136        Ok(())
137    }
138}
139
140impl ServiceWorkerMethods<crate::DomTypeHolder> for ServiceWorker {
141    /// <https://w3c.github.io/ServiceWorker/#service-worker-state-attribute>
142    fn State(&self) -> ServiceWorkerState {
143        self.state.get()
144    }
145
146    /// <https://w3c.github.io/ServiceWorker/#service-worker-url-attribute>
147    fn ScriptURL(&self) -> USVString {
148        USVString(self.script_url.borrow().clone())
149    }
150
151    /// <https://w3c.github.io/ServiceWorker/#service-worker-postmessage>
152    fn PostMessage(
153        &self,
154        cx: &mut JSContext,
155        message: HandleValue,
156        transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
157    ) -> ErrorResult {
158        self.post_message_impl(cx, message, transfer)
159    }
160
161    /// <https://w3c.github.io/ServiceWorker/#service-worker-postmessage>
162    fn PostMessage_(
163        &self,
164        cx: &mut JSContext,
165        message: HandleValue,
166        options: &StructuredSerializeOptions,
167    ) -> ErrorResult {
168        auto_root!(&in(cx) let guard =
169            options
170                .transfer
171                .iter()
172                .map(|js| js.get())
173                .collect::<Vec<_>>());
174        self.post_message_impl(cx, message, guard)
175    }
176
177    // https://w3c.github.io/ServiceWorker/#service-worker-container-onerror-attribute
178    event_handler!(error, GetOnerror, SetOnerror);
179
180    // https://w3c.github.io/ServiceWorker/#ref-for-service-worker-onstatechange-attribute-1
181    event_handler!(statechange, GetOnstatechange, SetOnstatechange);
182}
183
184impl TaskOnce for SimpleWorkerErrorHandler<ServiceWorker> {
185    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
186    fn run_once(self, cx: &mut JSContext) {
187        ServiceWorker::dispatch_simple_error(cx, self.addr);
188    }
189}