1use std::cell::Cell;
6
7use base::generic_channel::{GenericSend, GenericSender};
8use dom_struct::dom_struct;
9use js::context::JSContext;
10use profile_traits::generic_channel::channel;
11use storage_traits::indexeddb::{IndexedDBThreadMsg, KeyPath, SyncOperation};
12use stylo_atoms::Atom;
13use uuid::Uuid;
14
15use crate::dom::bindings::cell::DomRefCell;
16use crate::dom::bindings::codegen::Bindings::IDBDatabaseBinding::{
17 IDBDatabaseMethods, IDBObjectStoreParameters, IDBTransactionOptions,
18};
19use crate::dom::bindings::codegen::Bindings::IDBTransactionBinding::IDBTransactionMode;
20use crate::dom::bindings::codegen::UnionTypes::StringOrStringSequence;
21use crate::dom::bindings::error::{Error, Fallible};
22use crate::dom::bindings::inheritance::Castable;
23use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
24use crate::dom::bindings::root::{DomRoot, MutNullableDom};
25use crate::dom::bindings::str::DOMString;
26use crate::dom::domstringlist::DOMStringList;
27use crate::dom::eventtarget::EventTarget;
28use crate::dom::globalscope::GlobalScope;
29use crate::dom::indexeddb::idbobjectstore::IDBObjectStore;
30use crate::dom::indexeddb::idbtransaction::IDBTransaction;
31use crate::dom::indexeddb::idbversionchangeevent::IDBVersionChangeEvent;
32use crate::indexeddb::is_valid_key_path;
33use crate::script_runtime::CanGc;
34
35#[dom_struct]
36pub struct IDBDatabase {
37 eventtarget: EventTarget,
38 name: DOMString,
40 version: Cell<u64>,
42 object_store_names: DomRefCell<Vec<DOMString>>,
44 upgrade_transaction: MutNullableDom<IDBTransaction>,
46
47 #[no_trace]
48 #[ignore_malloc_size_of = "Uuid"]
49 id: Uuid,
50
51 close_pending: Cell<bool>,
54}
55
56impl IDBDatabase {
57 pub fn new_inherited(name: DOMString, id: Uuid, version: u64) -> IDBDatabase {
58 IDBDatabase {
59 eventtarget: EventTarget::new_inherited(),
60 name,
61 id,
62 version: Cell::new(version),
63 object_store_names: Default::default(),
64 upgrade_transaction: Default::default(),
65 close_pending: Cell::new(false),
66 }
67 }
68
69 pub fn new(
70 global: &GlobalScope,
71 name: DOMString,
72 id: Uuid,
73 version: u64,
74 can_gc: CanGc,
75 ) -> DomRoot<IDBDatabase> {
76 reflect_dom_object(
77 Box::new(IDBDatabase::new_inherited(name, id, version)),
78 global,
79 can_gc,
80 )
81 }
82
83 fn get_idb_thread(&self) -> GenericSender<IndexedDBThreadMsg> {
84 self.global().storage_threads().sender()
85 }
86
87 pub fn get_name(&self) -> DOMString {
88 self.name.clone()
89 }
90
91 pub fn object_stores(&self) -> DomRoot<DOMStringList> {
92 DOMStringList::new(
93 &self.global(),
94 self.object_store_names.borrow().clone(),
95 CanGc::note(),
96 )
97 }
98
99 pub(crate) fn object_store_names_snapshot(&self) -> Vec<DOMString> {
100 self.object_store_names.borrow().clone()
104 }
105
106 pub(crate) fn set_object_store_names_from_backend(&self, names: Vec<String>) {
107 *self.object_store_names.borrow_mut() = names.into_iter().map(Into::into).collect();
110 }
111
112 pub(crate) fn restore_object_store_names(&self, names: Vec<DOMString>) {
113 *self.object_store_names.borrow_mut() = names;
116 }
117
118 pub(crate) fn object_store_exists(&self, name: &DOMString) -> bool {
119 self.object_store_names
120 .borrow()
121 .iter()
122 .any(|store_name| store_name == name)
123 }
124
125 pub(crate) fn version(&self) -> u64 {
127 self.version.get()
129 }
130
131 pub(crate) fn set_version(&self, version: u64) {
132 self.version.set(version);
133 }
134
135 pub fn set_transaction(&self, transaction: &IDBTransaction) {
136 self.upgrade_transaction.set(Some(transaction));
137 }
138
139 pub(crate) fn clear_upgrade_transaction(&self, transaction: &IDBTransaction) {
140 let current = self
141 .upgrade_transaction
142 .get()
143 .expect("clear_upgrade_transaction called but no upgrade transaction is set");
144
145 debug_assert!(
146 &*current == transaction,
147 "clear_upgrade_transaction called with non-current transaction"
148 );
149
150 self.upgrade_transaction.set(None);
151 }
152
153 pub fn dispatch_versionchange(
155 &self,
156 old_version: u64,
157 new_version: Option<u64>,
158 can_gc: CanGc,
159 ) {
160 let global = self.global();
161 let _ = IDBVersionChangeEvent::fire_version_change_event(
162 &global,
163 self.upcast(),
164 Atom::from("versionchange"),
165 old_version,
166 new_version,
167 can_gc,
168 );
169 }
170}
171
172impl IDBDatabaseMethods<crate::DomTypeHolder> for IDBDatabase {
173 fn Transaction(
175 &self,
176 store_names: StringOrStringSequence,
177 mode: IDBTransactionMode,
178 _options: &IDBTransactionOptions,
179 ) -> Fallible<DomRoot<IDBTransaction>> {
180 if self.close_pending.get() {
186 return Err(Error::InvalidState(None));
187 }
188
189 let transaction = match store_names {
191 StringOrStringSequence::String(name) => IDBTransaction::new(
192 &self.global(),
193 self,
194 mode,
195 &DOMStringList::new(&self.global(), vec![name], CanGc::note()),
196 CanGc::note(),
197 ),
198 StringOrStringSequence::StringSequence(sequence) => {
199 IDBTransaction::new(
202 &self.global(),
203 self,
204 mode,
205 &DOMStringList::new(&self.global(), sequence, CanGc::note()),
206 CanGc::note(),
207 )
208 },
209 };
210
211 if mode != IDBTransactionMode::Readonly && mode != IDBTransactionMode::Readwrite {
214 return Err(Error::Type(c"Invalid transaction mode".to_owned()));
215 }
216
217 transaction.set_cleanup_event_loop();
220 self.global()
227 .get_indexeddb()
228 .register_indexeddb_transaction(&transaction);
229
230 Ok(transaction)
231 }
232
233 fn CreateObjectStore(
235 &self,
236 cx: &mut JSContext,
237 name: DOMString,
238 options: &IDBObjectStoreParameters,
239 ) -> Fallible<DomRoot<IDBObjectStore>> {
240 let upgrade_transaction = match self.upgrade_transaction.get() {
242 Some(txn) => txn,
243 None => return Err(Error::InvalidState(None)),
244 };
245
246 if !upgrade_transaction.is_active() {
248 return Err(Error::TransactionInactive(None));
249 }
250
251 let key_path = options.keyPath.as_ref();
253
254 if let Some(path) = key_path {
256 if !is_valid_key_path(cx, path)? {
257 return Err(Error::Syntax(None));
258 }
259 }
260
261 if self.object_store_names.borrow().contains(&name) {
263 return Err(Error::Constraint(None));
264 }
265
266 let auto_increment = options.autoIncrement;
268
269 if auto_increment {
271 match key_path {
272 Some(StringOrStringSequence::String(path)) => {
273 if path.is_empty() {
274 return Err(Error::InvalidAccess(None));
275 }
276 },
277 Some(StringOrStringSequence::StringSequence(_)) => {
278 return Err(Error::InvalidAccess(None));
279 },
280 None => {},
281 }
282 }
283
284 let object_store = IDBObjectStore::new(
286 &self.global(),
287 self.name.clone(),
288 name.clone(),
289 Some(options),
290 if auto_increment { Some(1) } else { None },
291 CanGc::from_cx(cx),
292 &upgrade_transaction,
293 );
294
295 let (sender, receiver) = channel(self.global().time_profiler_chan().clone()).unwrap();
296
297 let key_paths = key_path.map(|p| match p {
298 StringOrStringSequence::String(s) => KeyPath::String(s.to_string()),
299 StringOrStringSequence::StringSequence(s) => {
300 KeyPath::Sequence(s.iter().map(|s| s.to_string()).collect())
301 },
302 });
303 let operation = SyncOperation::CreateObjectStore(
304 sender,
305 self.global().origin().immutable().clone(),
306 self.name.to_string(),
307 name.to_string(),
308 key_paths,
309 auto_increment,
310 );
311
312 self.get_idb_thread()
313 .send(IndexedDBThreadMsg::Sync(operation))
314 .unwrap();
315
316 if receiver
317 .recv()
318 .expect("Could not receive object store creation status")
319 .is_err()
320 {
321 warn!("Object store creation failed in idb thread");
322 return Err(Error::InvalidState(None));
323 };
324
325 self.object_store_names.borrow_mut().push(name);
326 Ok(object_store)
327 }
328
329 fn DeleteObjectStore(&self, name: DOMString) -> Fallible<()> {
331 let transaction = self.upgrade_transaction.get();
333 let transaction = match transaction {
334 Some(transaction) => transaction,
335 None => return Err(Error::InvalidState(None)),
336 };
337
338 if !transaction.is_active() {
340 return Err(Error::TransactionInactive(None));
341 }
342
343 if !self.object_store_names.borrow().contains(&name) {
345 return Err(Error::NotFound(None));
346 }
347
348 self.object_store_names
350 .borrow_mut()
351 .retain(|store_name| *store_name != name);
352
353 let (sender, receiver) = channel(self.global().time_profiler_chan().clone()).unwrap();
358
359 let operation = SyncOperation::DeleteObjectStore(
360 sender,
361 self.global().origin().immutable().clone(),
362 self.name.to_string(),
363 name.to_string(),
364 );
365
366 self.get_idb_thread()
367 .send(IndexedDBThreadMsg::Sync(operation))
368 .unwrap();
369
370 if receiver
371 .recv()
372 .expect("Could not receive object store deletion status")
373 .is_err()
374 {
375 warn!("Object store deletion failed in idb thread");
376 return Err(Error::InvalidState(None));
377 };
378 Ok(())
379 }
380
381 fn Name(&self) -> DOMString {
383 self.name.clone()
384 }
385
386 fn Version(&self) -> u64 {
388 self.version()
389 }
390
391 fn ObjectStoreNames(&self, can_gc: CanGc) -> DomRoot<DOMStringList> {
393 DOMStringList::new_sorted(&self.global(), &*self.object_store_names.borrow(), can_gc)
394 }
395
396 fn Close(&self) {
398 self.close_pending.set(true);
403
404 let operation = SyncOperation::CloseDatabase(
406 self.global().origin().immutable().clone(),
407 self.id,
408 self.name.to_string(),
409 );
410 let _ = self
411 .get_idb_thread()
412 .send(IndexedDBThreadMsg::Sync(operation));
413 }
414
415 event_handler!(abort, GetOnabort, SetOnabort);
417
418 event_handler!(close, GetOnclose, SetOnclose);
420
421 event_handler!(error, GetOnerror, SetOnerror);
423
424 event_handler!(versionchange, GetOnversionchange, SetOnversionchange);
426}