1use std::cell::Cell;
6
7use base::IpcSend;
8use dom_struct::dom_struct;
9use ipc_channel::ipc::IpcSender;
10use profile_traits::ipc;
11use storage_traits::indexeddb_thread::{IndexedDBThreadMsg, KeyPath, SyncOperation};
12use stylo_atoms::Atom;
13
14use crate::dom::bindings::cell::DomRefCell;
15use crate::dom::bindings::codegen::Bindings::IDBDatabaseBinding::{
16 IDBDatabaseMethods, IDBObjectStoreParameters, IDBTransactionOptions,
17};
18use crate::dom::bindings::codegen::Bindings::IDBTransactionBinding::IDBTransactionMode;
19use crate::dom::bindings::codegen::UnionTypes::StringOrStringSequence;
20use crate::dom::bindings::error::{Error, Fallible};
21use crate::dom::bindings::inheritance::Castable;
22use crate::dom::bindings::refcounted::Trusted;
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::event::{Event, EventBubbles, EventCancelable};
28use crate::dom::eventtarget::EventTarget;
29use crate::dom::globalscope::GlobalScope;
30use crate::dom::idbobjectstore::IDBObjectStore;
31use crate::dom::idbtransaction::IDBTransaction;
32use crate::dom::idbversionchangeevent::IDBVersionChangeEvent;
33use crate::indexed_db::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 closing: Cell<bool>,
51}
52
53impl IDBDatabase {
54 pub fn new_inherited(name: DOMString, version: u64) -> IDBDatabase {
55 IDBDatabase {
56 eventtarget: EventTarget::new_inherited(),
57 name,
58 version: Cell::new(version),
59 object_store_names: Default::default(),
60
61 upgrade_transaction: Default::default(),
62 closing: Cell::new(false),
63 }
64 }
65
66 pub fn new(
67 global: &GlobalScope,
68 name: DOMString,
69 version: u64,
70 can_gc: CanGc,
71 ) -> DomRoot<IDBDatabase> {
72 reflect_dom_object(
73 Box::new(IDBDatabase::new_inherited(name, version)),
74 global,
75 can_gc,
76 )
77 }
78
79 fn get_idb_thread(&self) -> IpcSender<IndexedDBThreadMsg> {
80 self.global().storage_threads().sender()
81 }
82
83 pub fn get_name(&self) -> DOMString {
84 self.name.clone()
85 }
86
87 pub fn object_stores(&self) -> DomRoot<DOMStringList> {
88 DOMStringList::new(
89 &self.global(),
90 self.object_store_names.borrow().clone(),
91 CanGc::note(),
92 )
93 }
94
95 pub(crate) fn object_store_exists(&self, name: &DOMString) -> bool {
96 self.object_store_names
97 .borrow()
98 .iter()
99 .any(|store_name| store_name == name)
100 }
101
102 pub fn version(&self) -> u64 {
103 let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
104 let operation = SyncOperation::Version(
105 sender,
106 self.global().origin().immutable().clone(),
107 self.name.to_string(),
108 );
109
110 let _ = self
111 .get_idb_thread()
112 .send(IndexedDBThreadMsg::Sync(operation));
113
114 receiver.recv().unwrap().unwrap_or_else(|e| {
115 error!("{e:?}");
116 u64::MAX
117 })
118 }
119
120 pub fn set_transaction(&self, transaction: &IDBTransaction) {
121 self.upgrade_transaction.set(Some(transaction));
122 }
123
124 #[allow(dead_code)] pub fn dispatch_versionchange(
126 &self,
127 old_version: u64,
128 new_version: Option<u64>,
129 _can_gc: CanGc,
130 ) {
131 let global = self.global();
132 let this = Trusted::new(self);
133 global.task_manager().database_access_task_source().queue(
134 task!(send_versionchange_notification: move || {
135 let this = this.root();
136 let global = this.global();
137 let event = IDBVersionChangeEvent::new(
138 &global,
139 Atom::from("versionchange"),
140 EventBubbles::DoesNotBubble,
141 EventCancelable::NotCancelable,
142 old_version,
143 new_version,
144 CanGc::note()
145 );
146 event.upcast::<Event>().fire(this.upcast(), CanGc::note());
147 }),
148 );
149 }
150}
151
152impl IDBDatabaseMethods<crate::DomTypeHolder> for IDBDatabase {
153 fn Transaction(
155 &self,
156 store_names: StringOrStringSequence,
157 mode: IDBTransactionMode,
158 _options: &IDBTransactionOptions,
159 ) -> Fallible<DomRoot<IDBTransaction>> {
160 if self.closing.get() {
166 return Err(Error::InvalidState(None));
167 }
168
169 Ok(match store_names {
171 StringOrStringSequence::String(name) => IDBTransaction::new(
172 &self.global(),
173 self,
174 mode,
175 &DOMStringList::new(&self.global(), vec![name], CanGc::note()),
176 CanGc::note(),
177 ),
178 StringOrStringSequence::StringSequence(sequence) => {
179 IDBTransaction::new(
182 &self.global(),
183 self,
184 mode,
185 &DOMStringList::new(&self.global(), sequence, CanGc::note()),
186 CanGc::note(),
187 )
188 },
189 })
190 }
191
192 fn CreateObjectStore(
194 &self,
195 name: DOMString,
196 options: &IDBObjectStoreParameters,
197 ) -> Fallible<DomRoot<IDBObjectStore>> {
198 let upgrade_transaction = match self.upgrade_transaction.get() {
200 Some(txn) => txn,
201 None => return Err(Error::InvalidState(None)),
202 };
203
204 if !upgrade_transaction.is_active() {
206 return Err(Error::TransactionInactive);
207 }
208
209 let key_path = options.keyPath.as_ref();
211
212 if let Some(path) = key_path {
214 if !is_valid_key_path(path)? {
215 return Err(Error::Syntax(None));
216 }
217 }
218
219 if self.object_store_names.borrow().contains(&name) {
221 return Err(Error::Constraint);
222 }
223
224 let auto_increment = options.autoIncrement;
226
227 if auto_increment {
229 match key_path {
230 Some(StringOrStringSequence::String(path)) => {
231 if path.is_empty() {
232 return Err(Error::InvalidAccess);
233 }
234 },
235 Some(StringOrStringSequence::StringSequence(_)) => {
236 return Err(Error::InvalidAccess);
237 },
238 None => {},
239 }
240 }
241
242 let object_store = IDBObjectStore::new(
244 &self.global(),
245 self.name.clone(),
246 name.clone(),
247 Some(options),
248 CanGc::note(),
249 &upgrade_transaction,
250 );
251
252 let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
253
254 let key_paths = key_path.map(|p| match p {
255 StringOrStringSequence::String(s) => KeyPath::String(s.to_string()),
256 StringOrStringSequence::StringSequence(s) => {
257 KeyPath::Sequence(s.iter().map(|s| s.to_string()).collect())
258 },
259 });
260 let operation = SyncOperation::CreateObjectStore(
261 sender,
262 self.global().origin().immutable().clone(),
263 self.name.to_string(),
264 name.to_string(),
265 key_paths,
266 auto_increment,
267 );
268
269 self.get_idb_thread()
270 .send(IndexedDBThreadMsg::Sync(operation))
271 .unwrap();
272
273 if receiver
274 .recv()
275 .expect("Could not receive object store creation status")
276 .is_err()
277 {
278 warn!("Object store creation failed in idb thread");
279 return Err(Error::InvalidState(None));
280 };
281
282 self.object_store_names.borrow_mut().push(name);
283 Ok(object_store)
284 }
285
286 fn DeleteObjectStore(&self, name: DOMString) -> Fallible<()> {
288 let transaction = self.upgrade_transaction.get();
290 let transaction = match transaction {
291 Some(transaction) => transaction,
292 None => return Err(Error::InvalidState(None)),
293 };
294
295 if !transaction.is_active() {
297 return Err(Error::TransactionInactive);
298 }
299
300 if !self.object_store_names.borrow().contains(&name) {
302 return Err(Error::NotFound(None));
303 }
304
305 self.object_store_names
307 .borrow_mut()
308 .retain(|store_name| *store_name != name);
309
310 let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
315
316 let operation = SyncOperation::DeleteObjectStore(
317 sender,
318 self.global().origin().immutable().clone(),
319 self.name.to_string(),
320 name.to_string(),
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 deletion status")
330 .is_err()
331 {
332 warn!("Object store deletion failed in idb thread");
333 return Err(Error::InvalidState(None));
334 };
335 Ok(())
336 }
337
338 fn Name(&self) -> DOMString {
340 self.name.clone()
341 }
342
343 fn Version(&self) -> u64 {
345 self.version()
346 }
347
348 fn ObjectStoreNames(&self) -> DomRoot<DOMStringList> {
350 DOMStringList::new(
352 &self.global(),
353 self.object_store_names.borrow().clone(),
354 CanGc::note(),
355 )
356 }
357
358 fn Close(&self) {
360 self.closing.set(true);
362
363 let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
369 let operation = SyncOperation::CloseDatabase(
370 sender,
371 self.global().origin().immutable().clone(),
372 self.name.to_string(),
373 );
374 let _ = self
375 .get_idb_thread()
376 .send(IndexedDBThreadMsg::Sync(operation));
377
378 if receiver.recv().is_err() {
379 warn!("Database close failed in idb thread");
380 };
381 }
382
383 event_handler!(abort, GetOnabort, SetOnabort);
385
386 event_handler!(close, GetOnclose, SetOnclose);
388
389 event_handler!(error, GetOnerror, SetOnerror);
391
392 event_handler!(versionchange, GetOnversionchange, SetOnversionchange);
394}