1use base::generic_channel::GenericSend;
6use dom_struct::dom_struct;
7use js::jsval::UndefinedValue;
8use js::rust::HandleValue;
9use profile_traits::generic_callback::GenericCallback;
10use script_bindings::conversions::SafeToJSValConvertible;
11use storage_traits::indexeddb::{BackendResult, IndexedDBThreadMsg, SyncOperation};
12use stylo_atoms::Atom;
13use uuid::Uuid;
14
15use crate::dom::bindings::codegen::Bindings::IDBOpenDBRequestBinding::IDBOpenDBRequestMethods;
16use crate::dom::bindings::codegen::Bindings::IDBTransactionBinding::IDBTransactionMode;
17use crate::dom::bindings::error::{Error, ErrorToJsval};
18use crate::dom::bindings::inheritance::Castable;
19use crate::dom::bindings::refcounted::Trusted;
20use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
21use crate::dom::bindings::root::{DomRoot, MutNullableDom};
22use crate::dom::bindings::str::DOMString;
23use crate::dom::event::{Event, EventBubbles, EventCancelable};
24use crate::dom::globalscope::GlobalScope;
25use crate::dom::indexeddb::idbdatabase::IDBDatabase;
26use crate::dom::indexeddb::idbrequest::IDBRequest;
27use crate::dom::indexeddb::idbtransaction::IDBTransaction;
28use crate::dom::indexeddb::idbversionchangeevent::IDBVersionChangeEvent;
29use crate::indexeddb::map_backend_error_to_dom_error;
30use crate::realms::enter_realm;
31use crate::script_runtime::CanGc;
32
33#[derive(Clone)]
34struct OpenRequestListener {
35 open_request: Trusted<IDBOpenDBRequest>,
36}
37
38impl OpenRequestListener {
39 fn handle_delete_db(&self, result: BackendResult<u64>, can_gc: CanGc) {
42 let open_request = self.open_request.root();
53 let global = open_request.global();
54
55 open_request.idbrequest.set_ready_state_done();
57
58 let cx = GlobalScope::get_cx();
59 rooted!(in(*cx) let mut rval = UndefinedValue());
60
61 let _ac = enter_realm(&*open_request);
62
63 match result {
64 Ok(version) => {
65 open_request.set_result(rval.handle());
70 let event = IDBVersionChangeEvent::new(
71 &global,
72 Atom::from("success"),
73 EventBubbles::DoesNotBubble,
74 EventCancelable::NotCancelable,
75 version,
76 None,
77 CanGc::note(),
78 );
79 event.upcast::<Event>().fire(open_request.upcast(), can_gc);
80 },
81 Err(err) => {
82 let error = map_backend_error_to_dom_error(err);
91 let cx = GlobalScope::get_cx();
92 rooted!(in(*cx) let mut rval = UndefinedValue());
93 error
94 .clone()
95 .to_jsval(cx, &global, rval.handle_mut(), can_gc);
96 open_request.set_result(rval.handle());
97 let event = Event::new(
98 &global,
99 Atom::from("error"),
100 EventBubbles::Bubbles,
101 EventCancelable::Cancelable,
102 can_gc,
103 );
104 event.fire(open_request.upcast(), can_gc);
105 },
106 }
107 }
108}
109
110#[dom_struct]
111pub struct IDBOpenDBRequest {
112 idbrequest: IDBRequest,
113 pending_connection: MutNullableDom<IDBDatabase>,
114
115 #[no_trace]
116 id: Uuid,
117}
118
119impl IDBOpenDBRequest {
120 pub fn new_inherited() -> IDBOpenDBRequest {
121 IDBOpenDBRequest {
122 idbrequest: IDBRequest::new_inherited(),
123 pending_connection: Default::default(),
124 id: Uuid::new_v4(),
125 }
126 }
127
128 pub fn new(global: &GlobalScope, can_gc: CanGc) -> DomRoot<IDBOpenDBRequest> {
129 reflect_dom_object(Box::new(IDBOpenDBRequest::new_inherited()), global, can_gc)
130 }
131
132 pub(crate) fn get_id(&self) -> Uuid {
133 self.id
134 }
135
136 pub(crate) fn set_connection(&self, connection: &IDBDatabase) {
137 self.pending_connection.set(Some(connection));
138 }
139
140 pub(crate) fn upgrade_db_version(
144 &self,
145 connection: &IDBDatabase,
146 old_version: u64,
147 version: u64,
148 transaction: u64,
149 can_gc: CanGc,
150 ) {
151 let global = self.global();
152 let cx = GlobalScope::get_cx();
153
154 let transaction = IDBTransaction::new_with_id(
157 &global,
158 connection,
159 IDBTransactionMode::Versionchange,
160 &connection.object_stores(),
161 transaction,
162 Some(self.get_id()),
163 can_gc,
164 );
165 connection.set_transaction(&transaction);
166
167 rooted!(in(*cx) let mut connection_val = UndefinedValue());
168 connection.safe_to_jsval(cx, connection_val.handle_mut(), can_gc);
169
170 self.idbrequest.set_result(connection_val.handle());
172
173 self.idbrequest.set_transaction(&transaction);
175
176 self.idbrequest.set_ready_state_done();
178
179 transaction.set_active_flag(true);
182
183 let event = IDBVersionChangeEvent::new(
187 &global,
188 Atom::from("upgradeneeded"),
189 EventBubbles::DoesNotBubble,
190 EventCancelable::NotCancelable,
191 old_version,
192 Some(version),
193 CanGc::note(),
194 );
195
196 let _did_throw = event.upcast::<Event>().fire(self.upcast(), can_gc);
198
199 if transaction.is_active() {
201 transaction.set_active_flag(false);
203
204 if global
213 .storage_threads()
214 .send(IndexedDBThreadMsg::OpenTransactionInactive {
215 name: connection.get_name().to_string(),
216 origin: global.origin().immutable().clone(),
217 })
218 .is_err()
219 {
220 error!("Failed to send OpenTransactionInactive.");
221 }
222 }
223 }
224
225 pub fn set_result(&self, result: HandleValue) {
226 self.idbrequest.set_result(result);
227 }
228
229 pub fn set_error(&self, error: Option<Error>, can_gc: CanGc) {
230 self.idbrequest.set_error(error, can_gc);
231 }
232
233 pub fn delete_database(&self, name: String) -> Result<(), ()> {
234 let global = self.global();
235
236 let task_source = global
237 .task_manager()
238 .database_access_task_source()
239 .to_sendable();
240 let response_listener = OpenRequestListener {
241 open_request: Trusted::new(self),
242 };
243 let callback = GenericCallback::new(global.time_profiler_chan().clone(), move |message| {
244 let response_listener = response_listener.clone();
245 task_source.queue(task!(request_callback: move || {
246 response_listener.handle_delete_db(message.unwrap(), CanGc::note());
247 }))
248 })
249 .expect("Could not create delete database callback");
250
251 let delete_operation = SyncOperation::DeleteDatabase(
252 callback,
253 global.origin().immutable().clone(),
254 name,
255 self.get_id(),
256 );
257
258 if global
259 .storage_threads()
260 .send(IndexedDBThreadMsg::Sync(delete_operation))
261 .is_err()
262 {
263 return Err(());
264 }
265 Ok(())
266 }
267
268 pub fn dispatch_success(&self, name: String, version: u64, upgraded: bool, can_gc: CanGc) {
269 let global = self.global();
270 let result = self.pending_connection.or_init(|| {
271 debug_assert!(!upgraded, "A connection should exist for the upgraded db.");
272 IDBDatabase::new(
273 &global,
274 DOMString::from_string(name.clone()),
275 version,
276 can_gc,
277 )
278 });
279 self.idbrequest.set_ready_state_done();
280 let cx = GlobalScope::get_cx();
281
282 let _ac = enter_realm(&*result);
283 rooted!(in(*cx) let mut result_val = UndefinedValue());
284 result.safe_to_jsval(cx, result_val.handle_mut(), CanGc::note());
285 self.set_result(result_val.handle());
286
287 let event = Event::new(
288 &global,
289 Atom::from("success"),
290 EventBubbles::DoesNotBubble,
291 EventCancelable::NotCancelable,
292 CanGc::note(),
293 );
294 event.fire(self.upcast(), CanGc::note());
295 }
296}
297
298impl IDBOpenDBRequestMethods<crate::DomTypeHolder> for IDBOpenDBRequest {
299 event_handler!(blocked, GetOnblocked, SetOnblocked);
301
302 event_handler!(upgradeneeded, GetOnupgradeneeded, SetOnupgradeneeded);
304}