1use std::cell::Cell;
6
7use dom_struct::dom_struct;
8use js::context::JSContext;
9use profile_traits::generic_channel::channel;
10use script_bindings::cell::DomRefCell;
11use script_bindings::reflector::reflect_dom_object;
12use servo_base::generic_channel::{GenericSend, GenericSender};
13use storage_traits::indexeddb::{IndexedDBThreadMsg, KeyPath, SyncOperation};
14use stylo_atoms::Atom;
15use uuid::Uuid;
16
17use crate::dom::bindings::codegen::Bindings::IDBDatabaseBinding::{
18 IDBDatabaseMethods, IDBObjectStoreParameters, IDBTransactionOptions,
19};
20use crate::dom::bindings::codegen::Bindings::IDBTransactionBinding::IDBTransactionMode;
21use crate::dom::bindings::codegen::UnionTypes::StringOrStringSequence;
22use crate::dom::bindings::error::{Error, Fallible};
23use crate::dom::bindings::inheritance::Castable;
24use crate::dom::bindings::reflector::DomGlobal;
25use crate::dom::bindings::root::{DomRoot, MutNullableDom};
26use crate::dom::bindings::str::DOMString;
27use crate::dom::domstringlist::DOMStringList;
28use crate::dom::eventtarget::EventTarget;
29use crate::dom::globalscope::GlobalScope;
30use crate::dom::indexeddb::idbobjectstore::{IDBObjectStore, IDBObjectStoreAbortState};
31use crate::dom::indexeddb::idbtransaction::IDBTransaction;
32use crate::dom::indexeddb::idbversionchangeevent::IDBVersionChangeEvent;
33use crate::indexeddb::is_valid_key_path;
34use crate::script_runtime::CanGc;
35
36#[dom_struct]
37pub struct IDBDatabase {
38 eventtarget: EventTarget,
39 name: DOMString,
41 version: Cell<u64>,
43 object_store_names: DomRefCell<Vec<DOMString>>,
45 upgrade_transaction: MutNullableDom<IDBTransaction>,
47
48 #[no_trace]
49 #[ignore_malloc_size_of = "Uuid"]
50 id: Uuid,
51
52 close_pending: Cell<bool>,
55}
56
57impl IDBDatabase {
58 pub fn new_inherited(name: DOMString, id: Uuid, version: u64) -> IDBDatabase {
59 IDBDatabase {
60 eventtarget: EventTarget::new_inherited(),
61 name,
62 id,
63 version: Cell::new(version),
64 object_store_names: Default::default(),
65 upgrade_transaction: Default::default(),
66 close_pending: Cell::new(false),
67 }
68 }
69
70 pub fn new(
71 global: &GlobalScope,
72 name: DOMString,
73 id: Uuid,
74 version: u64,
75 can_gc: CanGc,
76 ) -> DomRoot<IDBDatabase> {
77 reflect_dom_object(
78 Box::new(IDBDatabase::new_inherited(name, id, version)),
79 global,
80 can_gc,
81 )
82 }
83
84 fn get_idb_thread(&self) -> GenericSender<IndexedDBThreadMsg> {
85 self.global().storage_threads().sender()
86 }
87
88 pub fn get_name(&self) -> DOMString {
89 self.name.clone()
90 }
91
92 pub fn object_stores(&self) -> DomRoot<DOMStringList> {
93 DOMStringList::new(
94 &self.global(),
95 self.object_store_names.borrow().clone(),
96 CanGc::deprecated_note(),
97 )
98 }
99
100 pub(crate) fn object_store_names_snapshot(&self) -> Vec<DOMString> {
101 self.object_store_names.borrow().clone()
105 }
106
107 pub(crate) fn set_object_store_names_from_backend(&self, names: Vec<String>) {
108 *self.object_store_names.borrow_mut() = names.into_iter().map(Into::into).collect();
111 }
112
113 pub(crate) fn restore_object_store_names(&self, names: Vec<DOMString>) {
114 *self.object_store_names.borrow_mut() = names;
117 }
118
119 pub(crate) fn rename_object_store_name(&self, old_name: &DOMString, new_name: DOMString) {
120 let mut object_store_names = self.object_store_names.borrow_mut();
121 if let Some(position) = object_store_names.iter().position(|name| name == old_name) {
122 object_store_names[position] = new_name;
123 }
124 }
125
126 pub(crate) fn object_store_exists(&self, name: &DOMString) -> bool {
127 self.object_store_names
128 .borrow()
129 .iter()
130 .any(|store_name| store_name == name)
131 }
132
133 pub(crate) fn version(&self) -> u64 {
135 self.version.get()
137 }
138
139 pub(crate) fn set_version(&self, version: u64) {
140 self.version.set(version);
141 }
142
143 pub fn set_transaction(&self, transaction: &IDBTransaction) {
144 self.upgrade_transaction.set(Some(transaction));
145 }
146
147 pub(crate) fn clear_upgrade_transaction(&self, transaction: &IDBTransaction) {
148 let current = self
149 .upgrade_transaction
150 .get()
151 .expect("clear_upgrade_transaction called but no upgrade transaction is set");
152
153 debug_assert!(
154 &*current == transaction,
155 "clear_upgrade_transaction called with non-current transaction"
156 );
157
158 self.upgrade_transaction.set(None);
159 }
160
161 pub fn dispatch_versionchange(
163 &self,
164 cx: &mut JSContext,
165 old_version: u64,
166 new_version: Option<u64>,
167 ) {
168 let global = self.global();
169 let _ = IDBVersionChangeEvent::fire_version_change_event(
170 cx,
171 &global,
172 self.upcast(),
173 Atom::from("versionchange"),
174 old_version,
175 new_version,
176 );
177 }
178}
179
180impl IDBDatabaseMethods<crate::DomTypeHolder> for IDBDatabase {
181 fn Transaction(
183 &self,
184 store_names: StringOrStringSequence,
185 mode: IDBTransactionMode,
186 options: &IDBTransactionOptions,
187 ) -> Fallible<DomRoot<IDBTransaction>> {
188 if self.upgrade_transaction.get().is_some() {
191 return Err(Error::InvalidState(None));
192 }
193
194 if self.close_pending.get() {
197 return Err(Error::InvalidState(None));
198 }
199
200 let mut scope = match store_names {
203 StringOrStringSequence::String(name) => vec![name],
204 StringOrStringSequence::StringSequence(sequence) => sequence,
205 };
206 scope.sort_unstable_by(|left, right| {
207 left.str().encode_utf16().cmp(right.str().encode_utf16())
208 });
209 scope.dedup();
210
211 if scope.iter().any(|name| !self.object_store_exists(name)) {
214 return Err(Error::NotFound(None));
215 }
216
217 if scope.is_empty() {
219 return Err(Error::InvalidAccess(None));
220 }
221
222 if mode != IDBTransactionMode::Readonly && mode != IDBTransactionMode::Readwrite {
224 return Err(Error::Type(c"Invalid transaction mode".to_owned()));
225 }
226
227 let durability = options.durability;
231 let scope = DOMStringList::new(&self.global(), scope, CanGc::deprecated_note());
232 let transaction = IDBTransaction::new(
233 &self.global(),
234 self,
235 mode,
236 durability,
237 &scope,
238 CanGc::deprecated_note(),
239 );
240
241 transaction.set_cleanup_event_loop();
243 self.global()
250 .get_indexeddb()
251 .register_indexeddb_transaction(&transaction);
252
253 Ok(transaction)
255 }
256
257 fn CreateObjectStore(
259 &self,
260 cx: &mut JSContext,
261 name: DOMString,
262 options: &IDBObjectStoreParameters,
263 ) -> Fallible<DomRoot<IDBObjectStore>> {
264 let transaction = match self.upgrade_transaction.get() {
269 Some(txn) => txn,
270 None => return Err(Error::InvalidState(None)),
271 };
272
273 if !transaction.is_active() {
276 return Err(Error::TransactionInactive(None));
277 }
278
279 let key_path = options.keyPath.as_ref();
282
283 if let Some(path) = key_path &&
286 !is_valid_key_path(cx, path)?
287 {
288 return Err(Error::Syntax(None));
289 }
290
291 if self.object_store_names.borrow().contains(&name) {
294 return Err(Error::Constraint(None));
295 }
296
297 let auto_increment = options.autoIncrement;
299
300 if auto_increment {
303 match key_path {
304 Some(StringOrStringSequence::String(path)) if path.is_empty() => {
305 return Err(Error::InvalidAccess(None));
306 },
307 Some(StringOrStringSequence::StringSequence(_)) => {
308 return Err(Error::InvalidAccess(None));
309 },
310 _ => {},
311 }
312 }
313
314 let object_store = IDBObjectStore::new(
319 &self.global(),
320 self.name.clone(),
321 name.clone(),
322 Some(options),
323 IDBObjectStoreAbortState {
324 newly_created_during_transaction: true,
325 rollback_indexes_on_abort: vec![],
326 key_generator_current_number: if auto_increment { Some(1_i64) } else { None },
327 },
328 CanGc::from_cx(cx),
329 &transaction,
330 );
331
332 let (sender, receiver) = channel(self.global().time_profiler_chan().clone()).unwrap();
333
334 let key_paths = key_path.map(|p| match p {
335 StringOrStringSequence::String(s) => KeyPath::String(s.to_string()),
336 StringOrStringSequence::StringSequence(s) => {
337 KeyPath::Sequence(s.iter().map(|s| s.to_string()).collect())
338 },
339 });
340 let operation = SyncOperation::CreateObjectStore(
341 sender,
342 self.global().origin().immutable().clone(),
343 self.name.to_string(),
344 name.to_string(),
345 key_paths,
346 auto_increment,
347 );
348
349 self.get_idb_thread()
350 .send(IndexedDBThreadMsg::Sync(operation))
351 .unwrap();
352
353 if receiver
354 .recv()
355 .expect("Could not receive object store creation status")
356 .is_err()
357 {
358 warn!("Object store creation failed in idb thread");
359 return Err(Error::InvalidState(None));
360 };
361
362 self.object_store_names.borrow_mut().push(name);
363 transaction.register_object_store_handle(&object_store.get_name(), &object_store);
364
365 Ok(object_store)
367 }
368
369 fn DeleteObjectStore(&self, name: DOMString) -> Fallible<()> {
371 let transaction = self.upgrade_transaction.get();
373 let transaction = match transaction {
374 Some(transaction) => transaction,
375 None => return Err(Error::InvalidState(None)),
376 };
377
378 if !transaction.is_active() {
380 return Err(Error::TransactionInactive(None));
381 }
382
383 if !self.object_store_names.borrow().contains(&name) {
385 return Err(Error::NotFound(None));
386 }
387
388 self.object_store_names
390 .borrow_mut()
391 .retain(|store_name| *store_name != name);
392
393 let (sender, receiver) = channel(self.global().time_profiler_chan().clone()).unwrap();
398
399 let operation = SyncOperation::DeleteObjectStore(
400 sender,
401 self.global().origin().immutable().clone(),
402 self.name.to_string(),
403 String::from(name),
404 );
405
406 self.get_idb_thread()
407 .send(IndexedDBThreadMsg::Sync(operation))
408 .unwrap();
409
410 if receiver
411 .recv()
412 .expect("Could not receive object store deletion status")
413 .is_err()
414 {
415 warn!("Object store deletion failed in idb thread");
416 return Err(Error::InvalidState(None));
417 };
418 Ok(())
419 }
420
421 fn Name(&self) -> DOMString {
423 self.name.clone()
424 }
425
426 fn Version(&self) -> u64 {
428 self.version()
429 }
430
431 fn ObjectStoreNames(&self, can_gc: CanGc) -> DomRoot<DOMStringList> {
433 DOMStringList::new_sorted(&self.global(), &*self.object_store_names.borrow(), can_gc)
434 }
435
436 fn Close(&self) {
438 self.close_pending.set(true);
443
444 let operation = SyncOperation::CloseDatabase(
446 self.global().origin().immutable().clone(),
447 self.id,
448 self.name.to_string(),
449 );
450 let _ = self
451 .get_idb_thread()
452 .send(IndexedDBThreadMsg::Sync(operation));
453 }
454
455 event_handler!(abort, GetOnabort, SetOnabort);
457
458 event_handler!(close, GetOnclose, SetOnclose);
460
461 event_handler!(error, GetOnerror, SetOnerror);
463
464 event_handler!(versionchange, GetOnversionchange, SetOnversionchange);
466}