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