1use dom_struct::dom_struct;
6use js::context::JSContext;
7use js::jsval::UndefinedValue;
8use js::rust::HandleValue;
9use profile_traits::generic_callback::GenericCallback;
10use script_bindings::conversions::SafeToJSValConvertible;
11use script_bindings::reflector::reflect_dom_object_with_cx;
12use servo_base::generic_channel::GenericSend;
13use servo_url::origin::ImmutableOrigin;
14use storage_traits::client_storage::StorageProxyMap;
15use storage_traits::indexeddb::{BackendResult, IndexedDBThreadMsg, SyncOperation};
16use stylo_atoms::Atom;
17use uuid::Uuid;
18
19use crate::dom::bindings::codegen::Bindings::IDBDatabaseBinding::IDBTransactionDurability;
20use crate::dom::bindings::codegen::Bindings::IDBOpenDBRequestBinding::IDBOpenDBRequestMethods;
21use crate::dom::bindings::codegen::Bindings::IDBTransactionBinding::IDBTransactionMode;
22use crate::dom::bindings::error::Error;
23use crate::dom::bindings::inheritance::Castable;
24use crate::dom::bindings::refcounted::Trusted;
25use crate::dom::bindings::reflector::DomGlobal;
26use crate::dom::bindings::root::{DomRoot, MutNullableDom};
27use crate::dom::event::{Event, EventBubbles, EventCancelable};
28use crate::dom::globalscope::GlobalScope;
29use crate::dom::indexeddb::idbdatabase::IDBDatabase;
30use crate::dom::indexeddb::idbrequest::IDBRequest;
31use crate::dom::indexeddb::idbtransaction::IDBTransaction;
32use crate::dom::indexeddb::idbversionchangeevent::IDBVersionChangeEvent;
33use crate::indexeddb::map_backend_error_to_dom_error;
34use crate::realms::enter_auto_realm;
35
36#[derive(Clone)]
37struct OpenRequestListener {
38 open_request: Trusted<IDBOpenDBRequest>,
39}
40
41impl OpenRequestListener {
42 fn handle_delete_db(&self, cx: &mut JSContext, result: BackendResult<u64>) {
45 let open_request = self.open_request.root();
55 let global = open_request.global();
56
57 open_request.idbrequest.set_ready_state_done();
59
60 rooted!(&in(cx) let mut rval = UndefinedValue());
61
62 let mut realm = enter_auto_realm(cx, &*open_request);
63 let cx = &mut realm.current_realm();
64
65 match result {
66 Ok(version) => {
67 open_request.set_result(rval.handle());
72 let _ = IDBVersionChangeEvent::fire_version_change_event(
73 cx,
74 &global,
75 open_request.upcast(),
76 Atom::from("success"),
77 version,
78 None,
79 );
80 },
81 Err(err) => {
82 let error = map_backend_error_to_dom_error(err);
89 open_request.set_error(cx, Some(error));
90 let event = Event::new(
91 cx,
92 &global,
93 Atom::from("error"),
94 EventBubbles::Bubbles,
95 EventCancelable::Cancelable,
96 );
97 event.fire(cx, open_request.upcast());
98 },
99 }
100 }
101}
102
103#[dom_struct]
104pub struct IDBOpenDBRequest {
105 idbrequest: IDBRequest,
106 pending_connection: MutNullableDom<IDBDatabase>,
107
108 #[no_trace]
110 id: Uuid,
111}
112
113impl IDBOpenDBRequest {
114 pub fn new_inherited() -> IDBOpenDBRequest {
115 IDBOpenDBRequest {
116 idbrequest: IDBRequest::new_inherited(),
117 pending_connection: Default::default(),
118 id: Uuid::new_v4(),
119 }
120 }
121
122 pub fn new(cx: &mut JSContext, global: &GlobalScope) -> DomRoot<IDBOpenDBRequest> {
123 reflect_dom_object_with_cx(Box::new(IDBOpenDBRequest::new_inherited()), global, cx)
124 }
125
126 pub(crate) fn get_id(&self) -> Uuid {
127 self.id
128 }
129
130 pub(crate) fn connection(&self) -> DomRoot<IDBDatabase> {
131 self.pending_connection
132 .get()
133 .expect("A connection should exist for the db.")
134 }
135
136 pub(crate) fn get_or_init_connection(
137 &self,
138 cx: &mut JSContext,
139 global: &GlobalScope,
140 name: String,
141 version: u64,
142 object_store_names: Vec<String>,
143 upgraded: bool,
144 ) -> DomRoot<IDBDatabase> {
145 self.pending_connection.or_init(|| {
146 debug_assert!(!upgraded, "A connection should exist for the upgraded db.");
147 IDBDatabase::new(
148 cx,
149 global,
150 name.into(),
151 self.get_id(),
152 version,
153 object_store_names,
154 )
155 })
156 }
157
158 pub(crate) fn upgrade_db_version(
162 &self,
163 cx: &mut JSContext,
164 connection: &IDBDatabase,
165 old_version: u64,
166 version: u64,
167 transaction: u64,
168 ) {
169 let global = self.global();
170
171 let scope = connection.object_stores(cx);
172
173 let transaction = IDBTransaction::new_with_serial(
174 cx,
175 &global,
176 connection,
177 IDBTransactionMode::Versionchange,
178 IDBTransactionDurability::Default,
179 &scope,
180 transaction,
181 );
182 transaction.set_versionchange_old_version(old_version);
183 connection.set_transaction(&transaction);
184 transaction.set_active_flag(false);
186
187 rooted!(&in(cx) let mut connection_val = UndefinedValue());
188 connection.safe_to_jsval(cx, connection_val.handle_mut());
189
190 self.idbrequest.set_result(connection_val.handle());
192
193 self.idbrequest.set_transaction(&transaction);
195
196 self.idbrequest.set_ready_state_done();
198
199 transaction.set_active_flag(true);
201
202 let did_throw = IDBVersionChangeEvent::fire_version_change_event(
205 cx,
206 &global,
207 self.upcast(),
208 Atom::from("upgradeneeded"),
209 old_version,
210 Some(version),
211 );
212
213 if transaction.is_active() {
215 transaction.set_active_flag(false);
217
218 if did_throw {
221 transaction.initiate_abort(cx, Error::Abort(None));
222 transaction.request_backend_abort();
223 } else {
224 transaction.maybe_commit(cx);
226 }
227 }
228 }
229
230 pub(crate) fn delete_database(
231 &self,
232 storage_key: ImmutableOrigin,
233 name: String,
234 proxy_map: StorageProxyMap,
235 ) -> Result<(), ()> {
236 let global = self.global();
237
238 let task_source = global
239 .task_manager()
240 .database_access_task_source()
241 .to_sendable();
242 let response_listener = OpenRequestListener {
243 open_request: Trusted::new(self),
244 };
245 let callback = GenericCallback::new(global.time_profiler_chan().clone(), move |message| {
246 let response_listener = response_listener.clone();
247 task_source.queue(task!(request_callback: move |cx| {
248 response_listener.handle_delete_db(cx, message.unwrap());
249 }))
250 })
251 .expect("Could not create delete database callback");
252
253 let delete_operation =
254 SyncOperation::DeleteDatabase(callback, storage_key, name, proxy_map, self.get_id());
255
256 if global
257 .storage_threads()
258 .send(IndexedDBThreadMsg::Sync(delete_operation))
259 .is_err()
260 {
261 return Err(());
262 }
263 Ok(())
264 }
265
266 pub fn set_result(&self, result: HandleValue) {
267 self.idbrequest.set_result(result);
268 }
269
270 pub fn set_ready_state_done(&self) {
271 self.idbrequest.set_ready_state_done();
272 }
273
274 pub fn set_error(&self, cx: &mut JSContext, error: Option<Error>) {
275 self.idbrequest.set_error(cx, error);
276 }
277
278 pub fn clear_transaction(&self) {
279 self.idbrequest.clear_transaction();
280 }
281
282 pub(crate) fn clear_transaction_if_matches(&self, transaction: &IDBTransaction) -> bool {
283 let matches = self
284 .idbrequest
285 .transaction()
286 .is_some_and(|current| &*current == transaction);
287 if matches {
288 self.idbrequest.clear_transaction();
289 }
290 matches
291 }
292
293 pub fn dispatch_success(&self, cx: &mut JSContext, result: DomRoot<IDBDatabase>) {
294 let global = self.global();
295 self.idbrequest.set_ready_state_done();
296
297 let mut realm = enter_auto_realm(cx, &*result);
298 let cx = &mut realm.current_realm();
299 rooted!(&in(cx) let mut result_val = UndefinedValue());
300 result.safe_to_jsval(cx, result_val.handle_mut());
301 self.set_result(result_val.handle());
302
303 let event = Event::new(
304 cx,
305 &global,
306 Atom::from("success"),
307 EventBubbles::DoesNotBubble,
308 EventCancelable::NotCancelable,
309 );
310 event.fire(cx, self.upcast());
311 }
312
313 pub fn dispatch_blocked(&self, cx: &mut JSContext, old_version: u64, new_version: Option<u64>) {
315 let global = self.global();
316 let _ = IDBVersionChangeEvent::fire_version_change_event(
317 cx,
318 &global,
319 self.upcast(),
320 Atom::from("blocked"),
321 old_version,
322 new_version,
323 );
324 }
325}
326
327impl IDBOpenDBRequestMethods<crate::DomTypeHolder> for IDBOpenDBRequest {
328 event_handler!(blocked, GetOnblocked, SetOnblocked);
330
331 event_handler!(upgradeneeded, GetOnupgradeneeded, SetOnupgradeneeded);
333}