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