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