1use dom_struct::dom_struct;
5use js::context::JSContext;
6use js::conversions::ToJSValConvertible;
7use js::gc::MutableHandleValue;
8use js::rust::HandleValue;
9use script_bindings::cell::DomRefCell;
10use script_bindings::codegen::GenericBindings::IDBIndexBinding::IDBIndexMethods;
11use script_bindings::codegen::GenericBindings::IDBTransactionBinding::IDBTransactionMode;
12use script_bindings::error::{Error, ErrorResult, Fallible};
13use script_bindings::reflector::{Reflector, reflect_dom_object};
14use script_bindings::str::DOMString;
15use storage_traits::indexeddb::{AsyncOperation, AsyncReadOnlyOperation};
16
17use crate::dom::bindings::codegen::Bindings::IDBCursorBinding::IDBCursorDirection;
18use crate::dom::bindings::refcounted::Trusted;
19use crate::dom::bindings::reflector::DomGlobal;
20use crate::dom::bindings::root::{Dom, DomRoot};
21use crate::dom::globalscope::GlobalScope;
22use crate::dom::idbobjectstore::KeyPath;
23use crate::dom::indexeddb::idbcursor::{IDBCursor, IterationParam, ObjectStoreOrIndex};
24use crate::dom::indexeddb::idbcursorwithvalue::IDBCursorWithValue;
25use crate::dom::indexeddb::idbobjectstore::IDBObjectStore;
26use crate::dom::indexeddb::idbrequest::IDBRequest;
27use crate::dom::indexeddb::key::convert_value_to_key_range;
28
29#[dom_struct]
30pub(crate) struct IDBIndex {
31 reflector_: Reflector,
32 object_store: Dom<IDBObjectStore>,
33 name: DomRefCell<DOMString>,
34 multi_entry: bool,
35 unique: bool,
36 key_path: KeyPath,
37}
38
39impl IDBIndex {
40 pub fn new_inherited(
41 object_store: &IDBObjectStore,
42 name: DOMString,
43 multi_entry: bool,
44 unique: bool,
45 key_path: KeyPath,
46 ) -> IDBIndex {
47 IDBIndex {
48 reflector_: Reflector::new(),
49 object_store: Dom::from_ref(object_store),
50 name: DomRefCell::new(name),
51 multi_entry,
52 unique,
53 key_path,
54 }
55 }
56
57 pub fn new(
58 cx: &mut JSContext,
59 global: &GlobalScope,
60 object_store: &IDBObjectStore,
61 name: DOMString,
62 multi_entry: bool,
63 unique: bool,
64 key_path: KeyPath,
65 ) -> DomRoot<IDBIndex> {
66 reflect_dom_object(
67 cx,
68 Box::new(IDBIndex::new_inherited(
69 object_store,
70 name,
71 multi_entry,
72 unique,
73 key_path,
74 )),
75 global,
76 )
77 }
78
79 pub(crate) fn object_store(&self) -> DomRoot<IDBObjectStore> {
80 DomRoot::from_ref(&self.object_store)
81 }
82
83 pub(crate) fn verify_not_deleted(&self) -> ErrorResult {
84 let stored_name = self.name.borrow().clone();
85 let transaction = self.object_store.transaction();
86 if !self.object_store.has_index(&stored_name) ||
87 !transaction
88 .get_db()
89 .object_store_exists(&self.object_store.get_name())
90 {
91 return Err(Error::InvalidState(Some(
92 "Index or its object store has been deleted".to_owned(),
93 )));
94 }
95 Ok(())
96 }
97
98 fn open_cursor(
101 &self,
102 cx: &mut JSContext,
103 query: HandleValue,
104 direction: IDBCursorDirection,
105 key_only: bool,
106 ) -> Fallible<DomRoot<IDBRequest>> {
107 let transaction = self.object_store.transaction();
109
110 self.verify_not_deleted()?;
112
113 if !transaction.is_active() || !transaction.is_usable() {
115 return Err(Error::TransactionInactive(Some(
116 "Transaction is not active".to_owned(),
117 )));
118 }
119
120 let range = convert_value_to_key_range(cx, query, Some(false))?;
122
123 let cursor = if key_only {
125 IDBCursor::new(
126 cx,
127 &self.global(),
128 &transaction,
129 direction,
130 false,
131 ObjectStoreOrIndex::Index(Dom::from_ref(self)),
132 range.clone(),
133 key_only,
134 )
135 } else {
136 DomRoot::upcast(IDBCursorWithValue::new(
137 cx,
138 &self.global(),
139 &transaction,
140 direction,
141 false,
142 ObjectStoreOrIndex::Index(Dom::from_ref(self)),
143 range.clone(),
144 key_only,
145 ))
146 };
147
148 let iteration_param = IterationParam {
150 cursor: Trusted::new(&cursor),
151 key: None,
152 primary_key: None,
153 count: None,
154 };
155
156 IDBRequest::execute_async(
157 cx,
158 &self.object_store,
159 |callback| {
160 AsyncOperation::ReadOnly(AsyncReadOnlyOperation::Iterate {
161 callback,
162 key_range: range,
163 })
164 },
165 None,
166 Some(iteration_param),
167 )
168 .inspect(|request| cursor.set_request(request))
169 }
170}
171
172impl IDBIndexMethods<crate::DomTypeHolder> for IDBIndex {
173 fn Name(&self) -> DOMString {
175 self.name.borrow().clone()
176 }
177
178 fn SetName(&self, name: DOMString) -> ErrorResult {
180 let transaction = self.object_store.transaction();
183
184 if transaction.get_mode() != IDBTransactionMode::Versionchange {
189 return Err(Error::InvalidState(Some(
190 "Transaction is not an upgrade transaction".to_owned(),
191 )));
192 }
193
194 if !transaction.is_active() {
196 return Err(Error::TransactionInactive(Some(
197 "Transaction is not active while updating index name".to_owned(),
198 )));
199 }
200
201 let mut stored_name = self.name.borrow_mut();
203 if !self.object_store.has_index(&stored_name) ||
204 !transaction
205 .get_db()
206 .object_store_exists(&self.object_store.get_name())
207 {
208 return Err(Error::InvalidState(Some(
209 "Index or its object store has been deleted".to_owned(),
210 )));
211 }
212
213 if *stored_name == name {
215 return Ok(());
216 }
217
218 if self.object_store.has_index(&name) {
220 return Err(Error::Constraint(Some(
221 "An index with the given name already exists".to_owned(),
222 )));
223 }
224
225 self.object_store.rename_index(&stored_name, &name);
227
228 *stored_name = name;
230 Ok(())
231 }
232
233 fn ObjectStore(&self) -> DomRoot<IDBObjectStore> {
235 self.object_store.as_rooted()
236 }
237
238 fn MultiEntry(&self) -> bool {
240 self.multi_entry
241 }
242
243 fn Unique(&self) -> bool {
245 self.unique
246 }
247
248 fn KeyPath(&self, cx: &mut JSContext, retval: MutableHandleValue) {
250 match &self.key_path {
251 KeyPath::String(string) => {
252 string.to_jsval(cx, retval);
253 },
254 KeyPath::StringSequence(sequence) => {
255 sequence.to_jsval(cx, retval);
256 },
257 }
258 }
259
260 fn OpenCursor(
262 &self,
263 cx: &mut JSContext,
264 query: HandleValue,
265 direction: IDBCursorDirection,
266 ) -> Fallible<DomRoot<IDBRequest>> {
267 self.open_cursor(cx, query, direction, false)
268 }
269
270 fn OpenKeyCursor(
272 &self,
273 cx: &mut JSContext,
274 query: HandleValue,
275 direction: IDBCursorDirection,
276 ) -> Fallible<DomRoot<IDBRequest>> {
277 self.open_cursor(cx, query, direction, true)
278 }
279}