1use std::cell::Cell;
6use std::collections::HashMap;
7
8use dom_struct::dom_struct;
9use ipc_channel::ipc::IpcSender;
10use net_traits::IpcSend;
11use net_traits::indexeddb_thread::{IndexedDBThreadMsg, SyncOperation};
12use profile_traits::ipc;
13use stylo_atoms::Atom;
14
15use crate::dom::bindings::cell::DomRefCell;
16use crate::dom::bindings::codegen::Bindings::DOMStringListBinding::DOMStringListMethods;
17use crate::dom::bindings::codegen::Bindings::IDBTransactionBinding::{
18 IDBTransactionMethods, IDBTransactionMode,
19};
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::{Dom, DomRoot, MutNullableDom};
25use crate::dom::bindings::str::DOMString;
26use crate::dom::domexception::DOMException;
27use crate::dom::domstringlist::DOMStringList;
28use crate::dom::event::{Event, EventBubbles, EventCancelable};
29use crate::dom::eventtarget::EventTarget;
30use crate::dom::globalscope::GlobalScope;
31use crate::dom::idbdatabase::IDBDatabase;
32use crate::dom::idbobjectstore::IDBObjectStore;
33use crate::dom::idbrequest::IDBRequest;
34use crate::script_runtime::CanGc;
35
36#[dom_struct]
37pub struct IDBTransaction {
38 eventtarget: EventTarget,
39 object_store_names: Dom<DOMStringList>,
40 mode: IDBTransactionMode,
41 db: Dom<IDBDatabase>,
42 error: MutNullableDom<DOMException>,
43
44 store_handles: DomRefCell<HashMap<String, Dom<IDBObjectStore>>>,
45 requests: DomRefCell<Vec<Dom<IDBRequest>>>,
47 active: Cell<bool>,
49 finished: Cell<bool>,
51 serial_number: u64,
54}
55
56impl IDBTransaction {
57 fn new_inherited(
58 connection: &IDBDatabase,
59 mode: IDBTransactionMode,
60 scope: &DOMStringList,
61 serial_number: u64,
62 ) -> IDBTransaction {
63 IDBTransaction {
64 eventtarget: EventTarget::new_inherited(),
65 object_store_names: Dom::from_ref(scope),
66 mode,
67 db: Dom::from_ref(connection),
68 error: Default::default(),
69
70 store_handles: Default::default(),
71 requests: Default::default(),
72 active: Cell::new(true),
73 finished: Cell::new(false),
74 serial_number,
75 }
76 }
77
78 pub fn new(
79 global: &GlobalScope,
80 connection: &IDBDatabase,
81 mode: IDBTransactionMode,
82 scope: &DOMStringList,
83 can_gc: CanGc,
84 ) -> DomRoot<IDBTransaction> {
85 let serial_number = IDBTransaction::register_new(global, connection.get_name());
86 reflect_dom_object(
87 Box::new(IDBTransaction::new_inherited(
88 connection,
89 mode,
90 scope,
91 serial_number,
92 )),
93 global,
94 can_gc,
95 )
96 }
97
98 fn register_new(global: &GlobalScope, db_name: DOMString) -> u64 {
104 let (sender, receiver) = ipc::channel(global.time_profiler_chan().clone()).unwrap();
105
106 global
107 .resource_threads()
108 .send(IndexedDBThreadMsg::Sync(SyncOperation::RegisterNewTxn(
109 sender,
110 global.origin().immutable().clone(),
111 db_name.to_string(),
112 )))
113 .unwrap();
114
115 receiver.recv().unwrap()
116 }
117
118 pub fn wait(&self) {
120 let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
122
123 let start_operation = SyncOperation::StartTransaction(
124 sender,
125 self.global().origin().immutable().clone(),
126 self.db.get_name().to_string(),
127 self.serial_number,
128 );
129
130 self.get_idb_thread()
131 .send(IndexedDBThreadMsg::Sync(start_operation))
132 .unwrap();
133
134 if receiver.recv().is_err() {
136 warn!("IDBtransaction failed to run");
137 };
138 }
139
140 pub fn set_active_flag(&self, status: bool) {
141 self.active.set(status)
142 }
143
144 pub fn is_active(&self) -> bool {
145 self.active.get()
146 }
147
148 pub fn get_mode(&self) -> IDBTransactionMode {
149 self.mode
150 }
151
152 pub fn get_db_name(&self) -> DOMString {
153 self.db.get_name()
154 }
155
156 pub fn get_serial_number(&self) -> u64 {
157 self.serial_number
158 }
159
160 pub fn add_request(&self, request: &IDBRequest) {
161 self.requests.borrow_mut().push(Dom::from_ref(request));
162 }
163
164 pub fn upgrade_db_version(&self, version: u64) {
165 self.wait();
167 let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
169 let upgrade_version_operation = SyncOperation::UpgradeVersion(
170 sender,
171 self.global().origin().immutable().clone(),
172 self.db.get_name().to_string(),
173 self.serial_number,
174 version,
175 );
176 self.get_idb_thread()
177 .send(IndexedDBThreadMsg::Sync(upgrade_version_operation))
178 .unwrap();
179 let _ = receiver.recv().unwrap();
182 }
183
184 fn dispatch_complete(&self) {
185 let global = self.global();
186 let this = Trusted::new(self);
187 global.task_manager().database_access_task_source().queue(
188 task!(send_complete_notification: move || {
189 let this = this.root();
190 let global = this.global();
191 let event = Event::new(
192 &global,
193 Atom::from("complete"),
194 EventBubbles::DoesNotBubble,
195 EventCancelable::NotCancelable,
196 CanGc::note()
197 );
198 event.fire(this.upcast(), CanGc::note());
199 }),
200 );
201 }
202
203 fn get_idb_thread(&self) -> IpcSender<IndexedDBThreadMsg> {
204 self.global().resource_threads().sender()
205 }
206}
207
208impl IDBTransactionMethods<crate::DomTypeHolder> for IDBTransaction {
209 fn Db(&self) -> DomRoot<IDBDatabase> {
211 DomRoot::from_ref(&*self.db)
212 }
213
214 fn ObjectStore(&self, name: DOMString) -> Fallible<DomRoot<IDBObjectStore>> {
216 if self.finished.get() {
218 return Err(Error::InvalidState);
219 }
220
221 if !self.object_store_names.Contains(name.clone()) {
223 return Err(Error::NotFound);
224 }
225
226 let mut store_handles = self.store_handles.borrow_mut();
230 let store = store_handles.entry(name.to_string()).or_insert_with(|| {
231 let store = IDBObjectStore::new(
233 &self.global(),
234 self.db.get_name(),
235 name,
236 None,
237 CanGc::note(),
238 self,
239 );
240 Dom::from_ref(&*store)
241 });
242
243 Ok(DomRoot::from_ref(&*store))
244 }
245
246 fn Commit(&self) -> Fallible<()> {
248 let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
250 let start_operation = SyncOperation::Commit(
251 sender,
252 self.global().origin().immutable().clone(),
253 self.db.get_name().to_string(),
254 self.serial_number,
255 );
256
257 self.get_idb_thread()
258 .send(IndexedDBThreadMsg::Sync(start_operation))
259 .unwrap();
260
261 let result = receiver.recv().unwrap();
262
263 if let Err(_result) = result {
265 return Err(Error::QuotaExceeded {
267 quota: None,
268 requested: None,
269 });
270 }
271
272 self.dispatch_complete();
277
278 Ok(())
279 }
280
281 fn Abort(&self) -> Fallible<()> {
283 if self.finished.get() {
287 return Err(Error::InvalidState);
288 }
289
290 self.active.set(false);
291
292 Ok(())
293 }
294
295 fn ObjectStoreNames(&self) -> DomRoot<DOMStringList> {
297 self.object_store_names.as_rooted()
298 }
299
300 fn Mode(&self) -> IDBTransactionMode {
302 self.mode
303 }
304
305 fn GetError(&self) -> Option<DomRoot<DOMException>> {
313 self.error.get()
314 }
315
316 event_handler!(abort, GetOnabort, SetOnabort);
318
319 event_handler!(complete, GetOncomplete, SetOncomplete);
321
322 event_handler!(error, GetOnerror, SetOnerror);
324}