script/dom/serviceworker/
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_with_cx;
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::task::TaskOnce;
33
34pub(crate) type TrustedServiceWorkerAddress = Trusted<ServiceWorker>;
35
36#[dom_struct]
37pub(crate) struct ServiceWorker {
38 eventtarget: EventTarget,
39 script_url: DomRefCell<String>,
40 #[no_trace]
41 scope_url: ServoUrl,
42 state: Cell<ServiceWorkerState>,
43 #[no_trace]
44 worker_id: ServiceWorkerId,
45}
46
47impl ServiceWorker {
48 fn new_inherited(
49 script_url: &str,
50 scope_url: ServoUrl,
51 worker_id: ServiceWorkerId,
52 ) -> ServiceWorker {
53 ServiceWorker {
54 eventtarget: EventTarget::new_inherited(),
55 script_url: DomRefCell::new(String::from(script_url)),
56 state: Cell::new(ServiceWorkerState::Installing),
57 scope_url,
58 worker_id,
59 }
60 }
61
62 pub(crate) fn new(
63 cx: &mut js::context::JSContext,
64 global: &GlobalScope,
65 script_url: ServoUrl,
66 scope_url: ServoUrl,
67 worker_id: ServiceWorkerId,
68 ) -> DomRoot<ServiceWorker> {
69 reflect_dom_object_with_cx(
70 Box::new(ServiceWorker::new_inherited(
71 script_url.as_str(),
72 scope_url,
73 worker_id,
74 )),
75 global,
76 cx,
77 )
78 }
79
80 pub(crate) fn dispatch_simple_error(cx: &mut JSContext, address: TrustedServiceWorkerAddress) {
81 let service_worker = address.root();
82 service_worker.upcast().fire_event(cx, atom!("error"));
83 }
84
85 pub(crate) fn get_script_url(&self) -> ServoUrl {
86 ServoUrl::parse(&self.script_url.borrow().clone()).unwrap()
87 }
88
89 fn post_message_impl(
91 &self,
92 cx: &mut JSContext,
93 message: HandleValue,
94 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
95 ) -> ErrorResult {
96 if let ServiceWorkerState::Redundant = self.state.get() {
98 return Err(Error::InvalidState(None));
99 }
100 let data = structuredclone::write(cx, message, Some(transfer))?;
102 let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
103 let pipeline_id = incumbent.pipeline_id();
104 let msg_vec = DOMMessage {
105 origin: incumbent.origin().immutable().clone(),
106 pipeline_id,
107 data,
108 };
109 let _ = self.global().script_to_constellation_chan().send(
110 ScriptToConstellationMessage::ForwardDOMMessage(msg_vec, self.scope_url.clone()),
111 );
112 Ok(())
113 }
114}
115
116impl ServiceWorkerMethods<crate::DomTypeHolder> for ServiceWorker {
117 fn State(&self) -> ServiceWorkerState {
119 self.state.get()
120 }
121
122 fn ScriptURL(&self) -> USVString {
124 USVString(self.script_url.borrow().clone())
125 }
126
127 fn PostMessage(
129 &self,
130 cx: &mut JSContext,
131 message: HandleValue,
132 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
133 ) -> ErrorResult {
134 self.post_message_impl(cx, message, transfer)
135 }
136
137 fn PostMessage_(
139 &self,
140 cx: &mut JSContext,
141 message: HandleValue,
142 options: RootedTraceableBox<StructuredSerializeOptions>,
143 ) -> ErrorResult {
144 let mut rooted = CustomAutoRooter::new(
145 options
146 .transfer
147 .iter()
148 .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
149 .collect(),
150 );
151 #[expect(unsafe_code)]
152 let guard = unsafe { CustomAutoRooterGuard::new(cx.raw_cx(), &mut rooted) };
153 self.post_message_impl(cx, message, guard)
154 }
155
156 event_handler!(error, GetOnerror, SetOnerror);
158
159 event_handler!(statechange, GetOnstatechange, SetOnstatechange);
161}
162
163impl TaskOnce for SimpleWorkerErrorHandler<ServiceWorker> {
164 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
165 fn run_once(self, cx: &mut JSContext) {
166 ServiceWorker::dispatch_simple_error(cx, self.addr);
167 }
168}