script/dom/serviceworker/
serviceworker.rs1use 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 fn post_message_impl(
115 &self,
116 cx: &mut JSContext,
117 message: HandleValue,
118 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
119 ) -> ErrorResult {
120 if let ServiceWorkerState::Redundant = self.state.get() {
122 return Err(Error::InvalidState(None));
123 }
124 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 fn State(&self) -> ServiceWorkerState {
143 self.state.get()
144 }
145
146 fn ScriptURL(&self) -> USVString {
148 USVString(self.script_url.borrow().clone())
149 }
150
151 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 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 event_handler!(error, GetOnerror, SetOnerror);
179
180 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}