6c09eafd6756cd4058b043b2ab811b0201ea2bf0
[WebKit-https.git] / Source / WebCore / Modules / indexeddb / IDBIndex.cpp
1 /*
2  * Copyright (C) 2015 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "IDBIndex.h"
28
29 #if ENABLE(INDEXED_DATABASE)
30
31 #include "IDBBindingUtilities.h"
32 #include "IDBCursor.h"
33 #include "IDBDatabase.h"
34 #include "IDBKeyRangeData.h"
35 #include "IDBObjectStore.h"
36 #include "IDBRequest.h"
37 #include "IDBTransaction.h"
38 #include "Logging.h"
39 #include <JavaScriptCore/HeapInlines.h>
40
41 namespace WebCore {
42 using namespace JSC;
43
44 IDBIndex::IDBIndex(ScriptExecutionContext& context, const IDBIndexInfo& info, IDBObjectStore& objectStore)
45     : ActiveDOMObject(&context)
46     , m_info(info)
47     , m_originalInfo(info)
48     , m_objectStore(objectStore)
49 {
50     ASSERT(&m_objectStore.transaction().database().originThread() == &Thread::current());
51
52     suspendIfNeeded();
53 }
54
55 IDBIndex::~IDBIndex()
56 {
57     ASSERT(&m_objectStore.transaction().database().originThread() == &Thread::current());
58 }
59
60 const char* IDBIndex::activeDOMObjectName() const
61 {
62     return "IDBIndex";
63 }
64
65 bool IDBIndex::canSuspendForDocumentSuspension() const
66 {
67     return false;
68 }
69
70 bool IDBIndex::hasPendingActivity() const
71 {
72     return m_objectStore.transaction().hasPendingActivity();
73 }
74
75 const String& IDBIndex::name() const
76 {
77     ASSERT(&m_objectStore.transaction().database().originThread() == &Thread::current());
78     return m_info.name();
79 }
80
81 ExceptionOr<void> IDBIndex::setName(const String& name)
82 {
83     ASSERT(&m_objectStore.transaction().database().originThread() == &Thread::current());
84
85     if (m_deleted)
86         return Exception { InvalidStateError, "Failed set property 'name' on 'IDBIndex': The index has been deleted."_s };
87
88     if (m_objectStore.isDeleted())
89         return Exception { InvalidStateError, "Failed set property 'name' on 'IDBIndex': The index's object store has been deleted."_s };
90
91     if (!m_objectStore.transaction().isVersionChange())
92         return Exception { InvalidStateError, "Failed set property 'name' on 'IDBIndex': The index's transaction is not a version change transaction."_s };
93
94     if (!m_objectStore.transaction().isActive())
95         return Exception { TransactionInactiveError, "Failed set property 'name' on 'IDBIndex': The index's transaction is not active."_s };
96
97     if (m_info.name() == name)
98         return { };
99
100     if (m_objectStore.info().hasIndex(name))
101         return Exception { ConstraintError, makeString("Failed set property 'name' on 'IDBIndex': The owning object store already has an index named '", name, "'.") };
102
103     m_objectStore.transaction().database().renameIndex(*this, name);
104     m_info.rename(name);
105
106     return { };
107 }
108
109 IDBObjectStore& IDBIndex::objectStore()
110 {
111     ASSERT(&m_objectStore.transaction().database().originThread() == &Thread::current());
112     return m_objectStore;
113 }
114
115 const IDBKeyPath& IDBIndex::keyPath() const
116 {
117     ASSERT(&m_objectStore.transaction().database().originThread() == &Thread::current());
118     return m_info.keyPath();
119 }
120
121 bool IDBIndex::unique() const
122 {
123     ASSERT(&m_objectStore.transaction().database().originThread() == &Thread::current());
124     return m_info.unique();
125 }
126
127 bool IDBIndex::multiEntry() const
128 {
129     ASSERT(&m_objectStore.transaction().database().originThread() == &Thread::current());
130     return m_info.multiEntry();
131 }
132
133 void IDBIndex::rollbackInfoForVersionChangeAbort()
134 {
135     ASSERT(&m_objectStore.transaction().database().originThread() == &Thread::current());
136
137     // Only rollback to the original info if this index still exists in the rolled-back database info.
138     auto* objectStoreInfo = m_objectStore.transaction().database().info().infoForExistingObjectStore(m_objectStore.info().identifier());
139     if (!objectStoreInfo)
140         return;
141
142     if (!objectStoreInfo->hasIndex(m_info.identifier())) {
143         m_deleted = true;
144         return;
145     }
146
147     m_info = m_originalInfo;
148     m_deleted = false;
149 }
150
151 ExceptionOr<Ref<IDBRequest>> IDBIndex::openCursor(ExecState& execState, IDBKeyRange* range, IDBCursorDirection direction)
152 {
153     LOG(IndexedDB, "IDBIndex::openCursor");
154     ASSERT(&m_objectStore.transaction().database().originThread() == &Thread::current());
155
156     if (m_deleted || m_objectStore.isDeleted())
157         return Exception { InvalidStateError, "Failed to execute 'openCursor' on 'IDBIndex': The index or its object store has been deleted."_s };
158
159     if (!m_objectStore.transaction().isActive())
160         return Exception { TransactionInactiveError, "Failed to execute 'openCursor' on 'IDBIndex': The transaction is inactive or finished."_s };
161
162     IDBKeyRangeData rangeData = range;
163     if (rangeData.lowerKey.isNull())
164         rangeData.lowerKey = IDBKeyData::minimum();
165     if (rangeData.upperKey.isNull())
166         rangeData.upperKey = IDBKeyData::maximum();
167
168     auto info = IDBCursorInfo::indexCursor(m_objectStore.transaction(), m_objectStore.info().identifier(), m_info.identifier(), rangeData, direction, IndexedDB::CursorType::KeyAndValue);
169     return m_objectStore.transaction().requestOpenCursor(execState, *this, info);
170 }
171
172 ExceptionOr<Ref<IDBRequest>> IDBIndex::openCursor(ExecState& execState, JSValue key, IDBCursorDirection direction)
173 {
174     LOG(IndexedDB, "IDBIndex::openCursor");
175     ASSERT(&m_objectStore.transaction().database().originThread() == &Thread::current());
176
177     auto keyRange = IDBKeyRange::only(execState, key);
178     if (keyRange.hasException())
179         return Exception { DataError, "Failed to execute 'openCursor' on 'IDBIndex': The parameter is not a valid key."_s };
180
181     return openCursor(execState, keyRange.releaseReturnValue().ptr(), direction);
182 }
183
184 ExceptionOr<Ref<IDBRequest>> IDBIndex::openKeyCursor(ExecState& execState, IDBKeyRange* range, IDBCursorDirection direction)
185 {
186     LOG(IndexedDB, "IDBIndex::openKeyCursor");
187     ASSERT(&m_objectStore.transaction().database().originThread() == &Thread::current());
188
189     if (m_deleted || m_objectStore.isDeleted())
190         return Exception { InvalidStateError, "Failed to execute 'openKeyCursor' on 'IDBIndex': The index or its object store has been deleted."_s };
191
192     if (!m_objectStore.transaction().isActive())
193         return Exception { TransactionInactiveError, "Failed to execute 'openKeyCursor' on 'IDBIndex': The transaction is inactive or finished."_s };
194
195     auto info = IDBCursorInfo::indexCursor(m_objectStore.transaction(), m_objectStore.info().identifier(), m_info.identifier(), range, direction, IndexedDB::CursorType::KeyOnly);
196     return m_objectStore.transaction().requestOpenCursor(execState, *this, info);
197 }
198
199 ExceptionOr<Ref<IDBRequest>> IDBIndex::openKeyCursor(ExecState& execState, JSValue key, IDBCursorDirection direction)
200 {
201     LOG(IndexedDB, "IDBIndex::openKeyCursor");
202
203     auto keyRange = IDBKeyRange::only(execState, key);
204     if (keyRange.hasException())
205         return Exception { DataError, "Failed to execute 'openKeyCursor' on 'IDBIndex': The parameter is not a valid key."_s };
206     return openKeyCursor(execState, keyRange.releaseReturnValue().ptr(), direction);
207 }
208
209 ExceptionOr<Ref<IDBRequest>> IDBIndex::count(ExecState& execState, IDBKeyRange* range)
210 {
211     LOG(IndexedDB, "IDBIndex::count");
212
213     return doCount(execState, range ? IDBKeyRangeData(range) : IDBKeyRangeData::allKeys());
214 }
215
216 ExceptionOr<Ref<IDBRequest>> IDBIndex::count(ExecState& execState, JSValue key)
217 {
218     LOG(IndexedDB, "IDBIndex::count");
219
220     auto idbKey = scriptValueToIDBKey(execState, key);
221     if (!idbKey->isValid())
222         return Exception { DataError, "Failed to execute 'count' on 'IDBIndex': The parameter is not a valid key."_s };
223
224     return doCount(execState, IDBKeyRangeData(idbKey.ptr()));
225 }
226
227 ExceptionOr<Ref<IDBRequest>> IDBIndex::doCount(ExecState& execState, const IDBKeyRangeData& range)
228 {
229     ASSERT(&m_objectStore.transaction().database().originThread() == &Thread::current());
230
231     if (m_deleted || m_objectStore.isDeleted())
232         return Exception { InvalidStateError, "Failed to execute 'count' on 'IDBIndex': The index or its object store has been deleted."_s };
233
234     if (!range.isValid())
235         return Exception { DataError };
236
237     auto& transaction = m_objectStore.transaction();
238     if (!transaction.isActive())
239         return Exception { TransactionInactiveError, "Failed to execute 'count' on 'IDBIndex': The transaction is inactive or finished."_s };
240
241     return transaction.requestCount(execState, *this, range);
242 }
243
244 ExceptionOr<Ref<IDBRequest>> IDBIndex::get(ExecState& execState, IDBKeyRange* range)
245 {
246     LOG(IndexedDB, "IDBIndex::get");
247
248     return doGet(execState, IDBKeyRangeData(range));
249 }
250
251 ExceptionOr<Ref<IDBRequest>> IDBIndex::get(ExecState& execState, JSValue key)
252 {
253     LOG(IndexedDB, "IDBIndex::get");
254
255     auto idbKey = scriptValueToIDBKey(execState, key);
256     if (!idbKey->isValid())
257         return Exception { DataError, "Failed to execute 'get' on 'IDBIndex': The parameter is not a valid key."_s };
258
259     return doGet(execState, IDBKeyRangeData(idbKey.ptr()));
260 }
261
262 ExceptionOr<Ref<IDBRequest>> IDBIndex::doGet(ExecState& execState, const IDBKeyRangeData& range)
263 {
264     ASSERT(&m_objectStore.transaction().database().originThread() == &Thread::current());
265
266     if (m_deleted || m_objectStore.isDeleted())
267         return Exception { InvalidStateError, "Failed to execute 'get' on 'IDBIndex': The index or its object store has been deleted."_s };
268
269     if (range.isNull)
270         return Exception { DataError };
271
272     auto& transaction = m_objectStore.transaction();
273     if (!transaction.isActive())
274         return Exception { TransactionInactiveError, "Failed to execute 'get' on 'IDBIndex': The transaction is inactive or finished."_s };
275
276     return transaction.requestGetValue(execState, *this, range);
277 }
278
279 ExceptionOr<Ref<IDBRequest>> IDBIndex::getKey(ExecState& execState, IDBKeyRange* range)
280 {
281     LOG(IndexedDB, "IDBIndex::getKey");
282
283     return doGetKey(execState, IDBKeyRangeData(range));
284 }
285
286 ExceptionOr<Ref<IDBRequest>> IDBIndex::getKey(ExecState& execState, JSValue key)
287 {
288     LOG(IndexedDB, "IDBIndex::getKey");
289
290     auto idbKey = scriptValueToIDBKey(execState, key);
291     if (!idbKey->isValid())
292         return Exception { DataError, "Failed to execute 'getKey' on 'IDBIndex': The parameter is not a valid key."_s };
293
294     return doGetKey(execState, IDBKeyRangeData(idbKey.ptr()));
295 }
296
297 ExceptionOr<Ref<IDBRequest>> IDBIndex::doGetKey(ExecState& execState, const IDBKeyRangeData& range)
298 {
299     ASSERT(&m_objectStore.transaction().database().originThread() == &Thread::current());
300
301     if (m_deleted || m_objectStore.isDeleted())
302         return Exception { InvalidStateError, "Failed to execute 'getKey' on 'IDBIndex': The index or its object store has been deleted."_s };
303
304     if (range.isNull)
305         return Exception { DataError };
306
307     auto& transaction = m_objectStore.transaction();
308     if (!transaction.isActive())
309         return Exception { TransactionInactiveError, "Failed to execute 'getKey' on 'IDBIndex': The transaction is inactive or finished."_s };
310
311     return transaction.requestGetKey(execState, *this, range);
312 }
313
314 ExceptionOr<Ref<IDBRequest>> IDBIndex::getAll(ExecState& execState, RefPtr<IDBKeyRange> range, Optional<uint32_t> count)
315 {
316     LOG(IndexedDB, "IDBIndex::getAll");
317     ASSERT(&m_objectStore.transaction().database().originThread() == &Thread::current());
318
319     if (m_deleted || m_objectStore.isDeleted())
320         return Exception { InvalidStateError, "Failed to execute 'getAll' on 'IDBIndex': The index or its object store has been deleted."_s };
321
322     if (!m_objectStore.transaction().isActive())
323         return Exception { TransactionInactiveError, "Failed to execute 'getAll' on 'IDBIndex': The transaction is inactive or finished."_s };
324
325     return m_objectStore.transaction().requestGetAllIndexRecords(execState, *this, range.get(), IndexedDB::GetAllType::Values, count);
326 }
327
328 ExceptionOr<Ref<IDBRequest>> IDBIndex::getAll(ExecState& execState, JSValue key, Optional<uint32_t> count)
329 {
330     auto onlyResult = IDBKeyRange::only(execState, key);
331     if (onlyResult.hasException())
332         return Exception { DataError, "Failed to execute 'getAll' on 'IDBIndex': The parameter is not a valid key."_s };
333
334     return getAll(execState, onlyResult.releaseReturnValue(), count);
335 }
336
337 ExceptionOr<Ref<IDBRequest>> IDBIndex::getAllKeys(ExecState& execState, RefPtr<IDBKeyRange> range, Optional<uint32_t> count)
338 {
339     LOG(IndexedDB, "IDBIndex::getAllKeys");
340     ASSERT(&m_objectStore.transaction().database().originThread() == &Thread::current());
341
342     if (m_deleted || m_objectStore.isDeleted())
343         return Exception { InvalidStateError, "Failed to execute 'getAllKeys' on 'IDBIndex': The index or its object store has been deleted."_s };
344
345     if (!m_objectStore.transaction().isActive())
346         return Exception { TransactionInactiveError, "Failed to execute 'getAllKeys' on 'IDBIndex': The transaction is inactive or finished."_s };
347
348     return m_objectStore.transaction().requestGetAllIndexRecords(execState, *this, range.get(), IndexedDB::GetAllType::Keys, count);
349 }
350
351 ExceptionOr<Ref<IDBRequest>> IDBIndex::getAllKeys(ExecState& execState, JSValue key, Optional<uint32_t> count)
352 {
353     auto onlyResult = IDBKeyRange::only(execState, key);
354     if (onlyResult.hasException())
355         return Exception { DataError, "Failed to execute 'getAllKeys' on 'IDBIndex': The parameter is not a valid key."_s };
356
357     return getAllKeys(execState, onlyResult.releaseReturnValue(), count);
358 }
359
360 void IDBIndex::markAsDeleted()
361 {
362     ASSERT(&m_objectStore.transaction().database().originThread() == &Thread::current());
363
364     ASSERT(!m_deleted);
365     m_deleted = true;
366 }
367
368 void IDBIndex::ref()
369 {
370     m_objectStore.ref();
371 }
372
373 void IDBIndex::deref()
374 {
375     m_objectStore.deref();
376 }
377
378 } // namespace WebCore
379
380 #endif // ENABLE(INDEXED_DATABASE)