script/dom/workers/
serviceworker.rs1use std::cell::Cell;
6
7use dom_struct::dom_struct;
8use js::context::JSContext;
9use js::jsapi::{Heap, JSObject};
10use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
11use script_bindings::cell::DomRefCell;
12use script_bindings::reflector::reflect_dom_object;
13use servo_base::id::ServiceWorkerId;
14use servo_constellation_traits::{DOMMessage, ScriptToConstellationMessage};
15use servo_url::ServoUrl;
16
17use crate::dom::abstractworker::SimpleWorkerErrorHandler;
18use crate::dom::bindings::codegen::Bindings::MessagePortBinding::StructuredSerializeOptions;
19use crate::dom::bindings::codegen::Bindings::ServiceWorkerBinding::{
20 ServiceWorkerMethods, ServiceWorkerState,
21};
22use crate::dom::bindings::error::{Error, ErrorResult};
23use crate::dom::bindings::inheritance::Castable;
24use crate::dom::bindings::refcounted::Trusted;
25use crate::dom::bindings::reflector::DomGlobal;
26use crate::dom::bindings::root::DomRoot;
27use crate::dom::bindings::str::USVString;
28use crate::dom::bindings::structuredclone;
29use crate::dom::bindings::trace::RootedTraceableBox;
30use crate::dom::eventtarget::EventTarget;
31use crate::dom::globalscope::GlobalScope;
32use crate::script_runtime::CanGc;
33use crate::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 global: &GlobalScope,
65 script_url: ServoUrl,
66 scope_url: ServoUrl,
67 worker_id: ServiceWorkerId,
68 can_gc: CanGc,
69 ) -> DomRoot<ServiceWorker> {
70 reflect_dom_object(
71 Box::new(ServiceWorker::new_inherited(
72 script_url.as_str(),
73 scope_url,
74 worker_id,
75 )),
76 global,
77 can_gc,
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 set_transition_state(&self, cx: &mut JSContext, state: ServiceWorkerState) {
87 self.state.set(state);
88 self.upcast::<EventTarget>()
89 .fire_event(cx, atom!("statechange"));
90 }
91
92 pub(crate) fn get_script_url(&self) -> ServoUrl {
93 ServoUrl::parse(&self.script_url.borrow().clone()).unwrap()
94 }
95
96 fn post_message_impl(
98 &self,
99 cx: &mut JSContext,
100 message: HandleValue,
101 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
102 ) -> ErrorResult {
103 if let ServiceWorkerState::Redundant = self.state.get() {
105 return Err(Error::InvalidState(None));
106 }
107 let data = structuredclone::write(cx, message, Some(transfer))?;
109 let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
110 let msg_vec = DOMMessage {
111 origin: incumbent.origin().immutable().clone(),
112 data,
113 };
114 let _ = self.global().script_to_constellation_chan().send(
115 ScriptToConstellationMessage::ForwardDOMMessage(msg_vec, self.scope_url.clone()),
116 );
117 Ok(())
118 }
119}
120
121impl ServiceWorkerMethods<crate::DomTypeHolder> for ServiceWorker {
122 fn State(&self) -> ServiceWorkerState {
124 self.state.get()
125 }
126
127 fn ScriptURL(&self) -> USVString {
129 USVString(self.script_url.borrow().clone())
130 }
131
132 fn PostMessage(
134 &self,
135 cx: &mut JSContext,
136 message: HandleValue,
137 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
138 ) -> ErrorResult {
139 self.post_message_impl(cx, message, transfer)
140 }
141
142 fn PostMessage_(
144 &self,
145 cx: &mut JSContext,
146 message: HandleValue,
147 options: RootedTraceableBox<StructuredSerializeOptions>,
148 ) -> ErrorResult {
149 let mut rooted = CustomAutoRooter::new(
150 options
151 .transfer
152 .iter()
153 .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
154 .collect(),
155 );
156 #[expect(unsafe_code)]
157 let guard = unsafe { CustomAutoRooterGuard::new(cx.raw_cx(), &mut rooted) };
158 self.post_message_impl(cx, message, guard)
159 }
160
161 event_handler!(error, GetOnerror, SetOnerror);
163
164 event_handler!(statechange, GetOnstatechange, SetOnstatechange);
166}
167
168impl TaskOnce for SimpleWorkerErrorHandler<ServiceWorker> {
169 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
170 fn run_once(self, cx: &mut JSContext) {
171 ServiceWorker::dispatch_simple_error(cx, self.addr);
172 }
173}