1use std::cell::Cell;
6
7use dom_struct::dom_struct;
8use js::context::JSContext;
9use script_bindings::cell::DomRefCell;
10use script_bindings::reflector::reflect_dom_object_with_cx;
11use servo_base::generic_channel::{GenericSend, GenericSender};
12use storage_traits::indexeddb::{AsyncSchemaOperation, IndexedDBThreadMsg, KeyPath, SyncOperation};
13use stylo_atoms::Atom;
14use uuid::Uuid;
15
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;
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, IDBObjectStoreAbortState};
30use crate::dom::indexeddb::idbtransaction::IDBTransaction;
31use crate::dom::indexeddb::idbversionchangeevent::IDBVersionChangeEvent;
32use crate::indexeddb::is_valid_key_path;
33
34#[dom_struct]
35pub struct IDBDatabase {
36 eventtarget: EventTarget,
37 name: DOMString,
39 version: Cell<u64>,
41 object_store_names: DomRefCell<Vec<DOMString>>,
43 upgrade_transaction: MutNullableDom<IDBTransaction>,
45
46 #[no_trace]
47 #[ignore_malloc_size_of = "Uuid"]
48 id: Uuid,
49
50 close_pending: Cell<bool>,
53}
54
55impl IDBDatabase {
56 pub fn new_inherited(
57 name: DOMString,
58 id: Uuid,
59 version: u64,
60 object_store_names: Vec<String>,
61 ) -> IDBDatabase {
62 IDBDatabase {
63 eventtarget: EventTarget::new_inherited(),
64 name,
65 id,
66 version: Cell::new(version),
67 object_store_names: DomRefCell::new(
68 object_store_names.into_iter().map(Into::into).collect(),
69 ),
70 upgrade_transaction: Default::default(),
71 close_pending: Cell::new(false),
72 }
73 }
74
75 pub fn new(
76 cx: &mut JSContext,
77 global: &GlobalScope,
78 name: DOMString,
79 id: Uuid,
80 version: u64,
81 object_store_names: Vec<String>,
82 ) -> DomRoot<IDBDatabase> {
83 reflect_dom_object_with_cx(
84 Box::new(IDBDatabase::new_inherited(
85 name,
86 id,
87 version,
88 object_store_names,
89 )),
90 global,
91 cx,
92 )
93 }
94
95 fn get_idb_thread(&self) -> GenericSender<IndexedDBThreadMsg> {
96 self.global().storage_threads().sender()
97 }
98
99 pub fn get_name(&self) -> DOMString {
100 self.name.clone()
101 }
102
103 pub fn object_stores(&self, cx: &mut JSContext) -> DomRoot<DOMStringList> {
104 DOMStringList::new(cx, &self.global(), self.object_store_names.borrow().clone())
105 }
106
107 pub(crate) fn object_store_names_snapshot(&self) -> Vec<DOMString> {
108 self.object_store_names.borrow().clone()
112 }
113
114 pub(crate) fn restore_object_store_names(&self, names: Vec<DOMString>) {
115 *self.object_store_names.borrow_mut() = names;
118 }
119
120 pub(crate) fn rename_object_store_name(&self, old_name: &DOMString, new_name: DOMString) {
121 let mut object_store_names = self.object_store_names.borrow_mut();
122 if let Some(position) = object_store_names.iter().position(|name| name == old_name) {
123 object_store_names[position] = new_name;
124 }
125 }
126
127 pub(crate) fn object_store_exists(&self, name: &DOMString) -> bool {
128 self.object_store_names
129 .borrow()
130 .iter()
131 .any(|store_name| store_name == name)
132 }
133
134 pub(crate) fn version(&self) -> u64 {
136 self.version.get()
138 }
139
140 pub(crate) fn set_version(&self, version: u64) {
141 self.version.set(version);
142 }
143
144 pub fn set_transaction(&self, transaction: &IDBTransaction) {
145 self.upgrade_transaction.set(Some(transaction));
146 }
147
148 pub(crate) fn clear_upgrade_transaction(&self, transaction: &IDBTransaction) {
149 let current = self
150 .upgrade_transaction
151 .get()
152 .expect("clear_upgrade_transaction called but no upgrade transaction is set");
153
154 debug_assert!(
155 &*current == transaction,
156 "clear_upgrade_transaction called with non-current transaction"
157 );
158
159 self.upgrade_transaction.set(None);
160 }
161
162 pub fn dispatch_versionchange(
164 &self,
165 cx: &mut JSContext,
166 old_version: u64,
167 new_version: Option<u64>,
168 ) {
169 let global = self.global();
170 let _ = IDBVersionChangeEvent::fire_version_change_event(
171 cx,
172 &global,
173 self.upcast(),
174 Atom::from("versionchange"),
175 old_version,
176 new_version,
177 );
178 }
179
180 pub(crate) fn close_a_database_connection(&self, _forced: bool) {
182 self.close_pending.set(true);
184
185 let operation = SyncOperation::CloseDatabase(
188 self.global().origin().immutable().clone(),
189 self.id,
190 self.name.to_string(),
191 );
192 let _ = self
193 .get_idb_thread()
194 .send(IndexedDBThreadMsg::Sync(operation));
195 }
196}
197
198impl IDBDatabaseMethods<crate::DomTypeHolder> for IDBDatabase {
199 fn Transaction(
201 &self,
202 cx: &mut JSContext,
203 store_names: StringOrStringSequence,
204 mode: IDBTransactionMode,
205 options: &IDBTransactionOptions,
206 ) -> Fallible<DomRoot<IDBTransaction>> {
207 if self.upgrade_transaction.get().is_some() {
210 return Err(Error::InvalidState(None));
211 }
212
213 if self.close_pending.get() {
216 return Err(Error::InvalidState(None));
217 }
218
219 let mut scope = match store_names {
222 StringOrStringSequence::String(name) => vec![name],
223 StringOrStringSequence::StringSequence(sequence) => sequence,
224 };
225 scope.sort_unstable_by(|left, right| {
226 left.str().encode_utf16().cmp(right.str().encode_utf16())
227 });
228 scope.dedup();
229
230 if scope.iter().any(|name| !self.object_store_exists(name)) {
233 return Err(Error::NotFound(None));
234 }
235
236 if scope.is_empty() {
238 return Err(Error::InvalidAccess(None));
239 }
240
241 if mode != IDBTransactionMode::Readonly && mode != IDBTransactionMode::Readwrite {
243 return Err(Error::Type(c"Invalid transaction mode".to_owned()));
244 }
245
246 let durability = options.durability;
250 let scope = DOMStringList::new(cx, &self.global(), scope);
251 let transaction = IDBTransaction::new(cx, &self.global(), self, mode, durability, &scope);
252
253 transaction.set_cleanup_event_loop();
255 self.global()
262 .ensure_indexeddb_factory(cx)
263 .register_indexeddb_transaction(&transaction);
264
265 Ok(transaction)
267 }
268
269 fn CreateObjectStore(
271 &self,
272 cx: &mut JSContext,
273 name: DOMString,
274 options: &IDBObjectStoreParameters,
275 ) -> Fallible<DomRoot<IDBObjectStore>> {
276 let transaction = match self.upgrade_transaction.get() {
281 Some(txn) => txn,
282 None => return Err(Error::InvalidState(None)),
283 };
284
285 if !transaction.is_active() {
288 return Err(Error::TransactionInactive(None));
289 }
290
291 let key_path = options.keyPath.as_ref();
294
295 if let Some(path) = key_path &&
298 !is_valid_key_path(cx, path)?
299 {
300 return Err(Error::Syntax(None));
301 }
302
303 if self.object_store_names.borrow().contains(&name) {
306 return Err(Error::Constraint(None));
307 }
308
309 let auto_increment = options.autoIncrement;
311
312 if auto_increment {
315 match key_path {
316 Some(StringOrStringSequence::String(path)) if path.is_empty() => {
317 return Err(Error::InvalidAccess(None));
318 },
319 Some(StringOrStringSequence::StringSequence(_)) => {
320 return Err(Error::InvalidAccess(None));
321 },
322 _ => {},
323 }
324 }
325
326 let object_store = IDBObjectStore::new(
331 cx,
332 &self.global(),
333 self.name.clone(),
334 name.clone(),
335 Some(options),
336 IDBObjectStoreAbortState {
337 newly_created_during_transaction: true,
338 rollback_indexes_on_abort: vec![],
339 key_generator_current_number: if auto_increment { Some(1_i64) } else { None },
340 },
341 &transaction,
342 );
343
344 let key_paths = key_path.map(|p| match p {
345 StringOrStringSequence::String(s) => KeyPath::String(s.to_string()),
346 StringOrStringSequence::StringSequence(s) => {
347 KeyPath::Sequence(s.iter().map(|s| s.to_string()).collect())
348 },
349 });
350
351 let operation = AsyncSchemaOperation::CreateObjectStore {
352 callback: transaction.create_abort_callback(),
353 key_path: key_paths,
354 auto_increment,
355 };
356
357 self.get_idb_thread()
358 .send(IndexedDBThreadMsg::AsyncSchemaOperation {
359 origin: self.global().origin().immutable().clone(),
360 database_name: self.name.to_string(),
361 store_name: name.to_string(),
362 operation,
363 transaction_serial_number: transaction.get_serial_number(),
364 })
365 .unwrap();
366
367 self.object_store_names.borrow_mut().push(name);
368 transaction.register_object_store_handle(&object_store.get_name(), &object_store);
369
370 Ok(object_store)
372 }
373
374 fn DeleteObjectStore(&self, name: DOMString) -> Fallible<()> {
376 let transaction = self.upgrade_transaction.get();
378 let transaction = match transaction {
379 Some(transaction) => transaction,
380 None => return Err(Error::InvalidState(None)),
381 };
382
383 if !transaction.is_active() {
385 return Err(Error::TransactionInactive(None));
386 }
387
388 if !self.object_store_names.borrow().contains(&name) {
390 return Err(Error::NotFound(None));
391 }
392
393 self.object_store_names
395 .borrow_mut()
396 .retain(|store_name| *store_name != name);
397
398 let operation = AsyncSchemaOperation::DeleteObjectStore {
403 callback: transaction.create_abort_callback(),
404 };
405 self.get_idb_thread()
406 .send(IndexedDBThreadMsg::AsyncSchemaOperation {
407 origin: self.global().origin().immutable().clone(),
408 database_name: self.name.to_string(),
409 store_name: name.to_string(),
410 operation,
411 transaction_serial_number: transaction.get_serial_number(),
412 })
413 .unwrap();
414
415 Ok(())
416 }
417
418 fn Name(&self) -> DOMString {
420 self.name.clone()
421 }
422
423 fn Version(&self) -> u64 {
425 self.version()
426 }
427
428 fn ObjectStoreNames(&self, cx: &mut JSContext) -> DomRoot<DOMStringList> {
430 DOMStringList::new_sorted(cx, &self.global(), &*self.object_store_names.borrow())
431 }
432
433 fn Close(&self) {
435 self.close_a_database_connection(false);
437 }
438
439 event_handler!(abort, GetOnabort, SetOnabort);
441
442 event_handler!(close, GetOnclose, SetOnclose);
444
445 event_handler!(error, GetOnerror, SetOnerror);
447
448 event_handler!(versionchange, GetOnversionchange, SetOnversionchange);
450}