1use std::cell::Cell;
6use std::collections::HashMap;
7
8use base::IpcSend;
9use dom_struct::dom_struct;
10use ipc_channel::ipc::IpcSender;
11use profile_traits::ipc;
12use script_bindings::codegen::GenericUnionTypes::StringOrStringSequence;
13use storage_traits::indexeddb_thread::{IndexedDBThreadMsg, KeyPath, SyncOperation};
14use stylo_atoms::Atom;
15
16use crate::dom::bindings::cell::DomRefCell;
17use crate::dom::bindings::codegen::Bindings::DOMStringListBinding::DOMStringListMethods;
18use crate::dom::bindings::codegen::Bindings::IDBDatabaseBinding::IDBObjectStoreParameters;
19use crate::dom::bindings::codegen::Bindings::IDBTransactionBinding::{
20 IDBTransactionMethods, IDBTransactionMode,
21};
22use crate::dom::bindings::error::{Error, Fallible};
23use crate::dom::bindings::inheritance::Castable;
24use crate::dom::bindings::refcounted::Trusted;
25use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
26use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
27use crate::dom::bindings::str::DOMString;
28use crate::dom::domexception::DOMException;
29use crate::dom::domstringlist::DOMStringList;
30use crate::dom::event::{Event, EventBubbles, EventCancelable};
31use crate::dom::eventtarget::EventTarget;
32use crate::dom::globalscope::GlobalScope;
33use crate::dom::idbdatabase::IDBDatabase;
34use crate::dom::idbobjectstore::IDBObjectStore;
35use crate::dom::idbrequest::IDBRequest;
36use crate::script_runtime::CanGc;
37
38#[dom_struct]
39pub struct IDBTransaction {
40 eventtarget: EventTarget,
41 object_store_names: Dom<DOMStringList>,
42 mode: IDBTransactionMode,
43 db: Dom<IDBDatabase>,
44 error: MutNullableDom<DOMException>,
45
46 store_handles: DomRefCell<HashMap<String, Dom<IDBObjectStore>>>,
47 requests: DomRefCell<Vec<Dom<IDBRequest>>>,
49 active: Cell<bool>,
51 finished: Cell<bool>,
53 serial_number: u64,
56}
57
58impl IDBTransaction {
59 fn new_inherited(
60 connection: &IDBDatabase,
61 mode: IDBTransactionMode,
62 scope: &DOMStringList,
63 serial_number: u64,
64 ) -> IDBTransaction {
65 IDBTransaction {
66 eventtarget: EventTarget::new_inherited(),
67 object_store_names: Dom::from_ref(scope),
68 mode,
69 db: Dom::from_ref(connection),
70 error: Default::default(),
71
72 store_handles: Default::default(),
73 requests: Default::default(),
74 active: Cell::new(true),
75 finished: Cell::new(false),
76 serial_number,
77 }
78 }
79
80 pub fn new(
81 global: &GlobalScope,
82 connection: &IDBDatabase,
83 mode: IDBTransactionMode,
84 scope: &DOMStringList,
85 can_gc: CanGc,
86 ) -> DomRoot<IDBTransaction> {
87 let serial_number = IDBTransaction::register_new(global, connection.get_name());
88 reflect_dom_object(
89 Box::new(IDBTransaction::new_inherited(
90 connection,
91 mode,
92 scope,
93 serial_number,
94 )),
95 global,
96 can_gc,
97 )
98 }
99
100 fn register_new(global: &GlobalScope, db_name: DOMString) -> u64 {
106 let (sender, receiver) = ipc::channel(global.time_profiler_chan().clone()).unwrap();
107
108 global
109 .storage_threads()
110 .send(IndexedDBThreadMsg::Sync(SyncOperation::RegisterNewTxn(
111 sender,
112 global.origin().immutable().clone(),
113 db_name.to_string(),
114 )))
115 .unwrap();
116
117 receiver.recv().unwrap()
118 }
119
120 pub fn wait(&self) {
122 let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
124
125 let start_operation = SyncOperation::StartTransaction(
126 sender,
127 self.global().origin().immutable().clone(),
128 self.db.get_name().to_string(),
129 self.serial_number,
130 );
131
132 self.get_idb_thread()
133 .send(IndexedDBThreadMsg::Sync(start_operation))
134 .unwrap();
135
136 if receiver.recv().is_err() {
138 warn!("IDBtransaction failed to run");
139 };
140 }
141
142 pub fn set_active_flag(&self, status: bool) {
143 self.active.set(status)
144 }
145
146 pub fn is_active(&self) -> bool {
147 self.active.get()
148 }
149
150 pub fn get_mode(&self) -> IDBTransactionMode {
151 self.mode
152 }
153
154 pub fn get_db_name(&self) -> DOMString {
155 self.db.get_name()
156 }
157
158 pub fn get_serial_number(&self) -> u64 {
159 self.serial_number
160 }
161
162 pub fn add_request(&self, request: &IDBRequest) {
163 self.requests.borrow_mut().push(Dom::from_ref(request));
164 }
165
166 pub fn upgrade_db_version(&self, version: u64) {
167 self.wait();
169 let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
171 let upgrade_version_operation = SyncOperation::UpgradeVersion(
172 sender,
173 self.global().origin().immutable().clone(),
174 self.db.get_name().to_string(),
175 self.serial_number,
176 version,
177 );
178 self.get_idb_thread()
179 .send(IndexedDBThreadMsg::Sync(upgrade_version_operation))
180 .unwrap();
181 let _ = receiver.recv().unwrap();
184 }
185
186 fn dispatch_complete(&self) {
187 let global = self.global();
188 let this = Trusted::new(self);
189 global.task_manager().database_access_task_source().queue(
190 task!(send_complete_notification: move || {
191 let this = this.root();
192 let global = this.global();
193 let event = Event::new(
194 &global,
195 Atom::from("complete"),
196 EventBubbles::DoesNotBubble,
197 EventCancelable::NotCancelable,
198 CanGc::note()
199 );
200 event.fire(this.upcast(), CanGc::note());
201 }),
202 );
203 }
204
205 fn get_idb_thread(&self) -> IpcSender<IndexedDBThreadMsg> {
206 self.global().storage_threads().sender()
207 }
208
209 fn object_store_parameters(
210 &self,
211 object_store_name: &DOMString,
212 ) -> Option<IDBObjectStoreParameters> {
213 let global = self.global();
214 let idb_sender = global.storage_threads().sender();
215 let (sender, receiver) =
216 ipc::channel(global.time_profiler_chan().clone()).expect("failed to create channel");
217
218 let origin = global.origin().immutable().clone();
219 let db_name = self.db.get_name().to_string();
220 let object_store_name = object_store_name.to_string();
221
222 let operation = SyncOperation::HasKeyGenerator(
223 sender,
224 origin.clone(),
225 db_name.clone(),
226 object_store_name.clone(),
227 );
228
229 let _ = idb_sender.send(IndexedDBThreadMsg::Sync(operation));
230
231 let auto_increment = receiver.recv().ok()?.ok()?;
234
235 let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).ok()?;
236 let operation = SyncOperation::KeyPath(sender, origin, db_name, object_store_name);
237
238 let _ = idb_sender.send(IndexedDBThreadMsg::Sync(operation));
239
240 let key_path = receiver.recv().unwrap().ok()?;
243 let key_path = key_path.map(|key_path| match key_path {
244 KeyPath::String(s) => StringOrStringSequence::String(DOMString::from_string(s)),
245 KeyPath::Sequence(seq) => StringOrStringSequence::StringSequence(
246 seq.into_iter().map(DOMString::from_string).collect(),
247 ),
248 });
249 Some(IDBObjectStoreParameters {
250 autoIncrement: auto_increment,
251 keyPath: key_path,
252 })
253 }
254}
255
256impl IDBTransactionMethods<crate::DomTypeHolder> for IDBTransaction {
257 fn Db(&self) -> DomRoot<IDBDatabase> {
259 DomRoot::from_ref(&*self.db)
260 }
261
262 fn ObjectStore(&self, name: DOMString) -> Fallible<DomRoot<IDBObjectStore>> {
264 if self.finished.get() {
266 return Err(Error::InvalidState(None));
267 }
268
269 if !self.object_store_names.Contains(name.clone()) {
271 return Err(Error::NotFound(None));
272 }
273
274 let mut store_handles = self.store_handles.borrow_mut();
278 let store = store_handles.entry(name.to_string()).or_insert_with(|| {
279 let parameters = self.object_store_parameters(&name);
280 let store = IDBObjectStore::new(
281 &self.global(),
282 self.db.get_name(),
283 name,
284 parameters.as_ref(),
285 CanGc::note(),
286 self,
287 );
288 Dom::from_ref(&*store)
289 });
290
291 Ok(DomRoot::from_ref(&*store))
292 }
293
294 fn Commit(&self) -> Fallible<()> {
296 let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
298 let start_operation = SyncOperation::Commit(
299 sender,
300 self.global().origin().immutable().clone(),
301 self.db.get_name().to_string(),
302 self.serial_number,
303 );
304
305 self.get_idb_thread()
306 .send(IndexedDBThreadMsg::Sync(start_operation))
307 .unwrap();
308
309 let result = receiver.recv().unwrap();
310
311 if let Err(_result) = result {
313 return Err(Error::QuotaExceeded {
315 quota: None,
316 requested: None,
317 });
318 }
319
320 self.dispatch_complete();
325
326 Ok(())
327 }
328
329 fn Abort(&self) -> Fallible<()> {
331 if self.finished.get() {
335 return Err(Error::InvalidState(None));
336 }
337
338 self.active.set(false);
339
340 Ok(())
341 }
342
343 fn ObjectStoreNames(&self) -> DomRoot<DOMStringList> {
345 self.object_store_names.as_rooted()
346 }
347
348 fn Mode(&self) -> IDBTransactionMode {
350 self.mode
351 }
352
353 fn GetError(&self) -> Option<DomRoot<DOMException>> {
361 self.error.get()
362 }
363
364 event_handler!(abort, GetOnabort, SetOnabort);
366
367 event_handler!(complete, GetOncomplete, SetOncomplete);
369
370 event_handler!(error, GetOnerror, SetOnerror);
372}