script/dom/
serviceworker.rs1use 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 fn post_message_impl(
96 &self,
97 cx: JSContext,
98 message: HandleValue,
99 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
100 ) -> ErrorResult {
101 if let ServiceWorkerState::Redundant = self.state.get() {
103 return Err(Error::InvalidState);
104 }
105 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 fn State(&self) -> ServiceWorkerState {
122 self.state.get()
123 }
124
125 fn ScriptURL(&self) -> USVString {
127 USVString(self.script_url.borrow().clone())
128 }
129
130 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 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 event_handler!(error, GetOnerror, SetOnerror);
160
161 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}