1use std::cell::Cell;
6
7use dom_struct::dom_struct;
8use js::context::JSContext;
9use profile_traits::generic_channel::channel;
10use servo_base::generic_channel::{GenericSend, GenericSender};
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 transaction = match self.upgrade_transaction.get() {
245 Some(txn) => txn,
246 None => return Err(Error::InvalidState(None)),
247 };
248
249 if !transaction.is_active() {
252 return Err(Error::TransactionInactive(None));
253 }
254
255 let key_path = options.keyPath.as_ref();
258
259 if let Some(path) = key_path {
262 if !is_valid_key_path(cx, path)? {
263 return Err(Error::Syntax(None));
264 }
265 }
266
267 if self.object_store_names.borrow().contains(&name) {
270 return Err(Error::Constraint(None));
271 }
272
273 let auto_increment = options.autoIncrement;
275
276 if auto_increment {
279 match key_path {
280 Some(StringOrStringSequence::String(path)) => {
281 if path.is_empty() {
282 return Err(Error::InvalidAccess(None));
283 }
284 },
285 Some(StringOrStringSequence::StringSequence(_)) => {
286 return Err(Error::InvalidAccess(None));
287 },
288 None => {},
289 }
290 }
291
292 let object_store = IDBObjectStore::new(
297 &self.global(),
298 self.name.clone(),
299 name.clone(),
300 Some(options),
301 if auto_increment { Some(1) } else { None },
302 CanGc::from_cx(cx),
303 &transaction,
304 );
305
306 let (sender, receiver) = channel(self.global().time_profiler_chan().clone()).unwrap();
307
308 let key_paths = key_path.map(|p| match p {
309 StringOrStringSequence::String(s) => KeyPath::String(s.to_string()),
310 StringOrStringSequence::StringSequence(s) => {
311 KeyPath::Sequence(s.iter().map(|s| s.to_string()).collect())
312 },
313 });
314 let operation = SyncOperation::CreateObjectStore(
315 sender,
316 self.global().origin().immutable().clone(),
317 self.name.to_string(),
318 name.to_string(),
319 key_paths,
320 auto_increment,
321 );
322
323 self.get_idb_thread()
324 .send(IndexedDBThreadMsg::Sync(operation))
325 .unwrap();
326
327 if receiver
328 .recv()
329 .expect("Could not receive object store creation status")
330 .is_err()
331 {
332 warn!("Object store creation failed in idb thread");
333 return Err(Error::InvalidState(None));
334 };
335
336 self.object_store_names.borrow_mut().push(name);
337
338 Ok(object_store)
340 }
341
342 fn DeleteObjectStore(&self, name: DOMString) -> Fallible<()> {
344 let transaction = self.upgrade_transaction.get();
346 let transaction = match transaction {
347 Some(transaction) => transaction,
348 None => return Err(Error::InvalidState(None)),
349 };
350
351 if !transaction.is_active() {
353 return Err(Error::TransactionInactive(None));
354 }
355
356 if !self.object_store_names.borrow().contains(&name) {
358 return Err(Error::NotFound(None));
359 }
360
361 self.object_store_names
363 .borrow_mut()
364 .retain(|store_name| *store_name != name);
365
366 let (sender, receiver) = channel(self.global().time_profiler_chan().clone()).unwrap();
371
372 let operation = SyncOperation::DeleteObjectStore(
373 sender,
374 self.global().origin().immutable().clone(),
375 self.name.to_string(),
376 name.to_string(),
377 );
378
379 self.get_idb_thread()
380 .send(IndexedDBThreadMsg::Sync(operation))
381 .unwrap();
382
383 if receiver
384 .recv()
385 .expect("Could not receive object store deletion status")
386 .is_err()
387 {
388 warn!("Object store deletion failed in idb thread");
389 return Err(Error::InvalidState(None));
390 };
391 Ok(())
392 }
393
394 fn Name(&self) -> DOMString {
396 self.name.clone()
397 }
398
399 fn Version(&self) -> u64 {
401 self.version()
402 }
403
404 fn ObjectStoreNames(&self, can_gc: CanGc) -> DomRoot<DOMStringList> {
406 DOMStringList::new_sorted(&self.global(), &*self.object_store_names.borrow(), can_gc)
407 }
408
409 fn Close(&self) {
411 self.close_pending.set(true);
416
417 let operation = SyncOperation::CloseDatabase(
419 self.global().origin().immutable().clone(),
420 self.id,
421 self.name.to_string(),
422 );
423 let _ = self
424 .get_idb_thread()
425 .send(IndexedDBThreadMsg::Sync(operation));
426 }
427
428 event_handler!(abort, GetOnabort, SetOnabort);
430
431 event_handler!(close, GetOnclose, SetOnclose);
433
434 event_handler!(error, GetOnerror, SetOnerror);
436
437 event_handler!(versionchange, GetOnversionchange, SetOnversionchange);
439}