4a7f66e85820aa4922dcb283c5cac0cc6ada3a53
[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 true;
68 }
69
70 bool IDBIndex::hasPendingActivity() const
71 {
72     return m_objectStore.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::doOpenCursor(ExecState& execState, IDBCursorDirection direction, WTF::Function<ExceptionOr<RefPtr<IDBKeyRange>>()>&& function)
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     auto keyRange = function();
163     if (keyRange.hasException())
164         return keyRange.releaseException();
165
166     IDBKeyRangeData rangeData = keyRange.returnValue() ? keyRange.releaseReturnValue().get() : nullptr;
167     if (rangeData.lowerKey.isNull())
168         rangeData.lowerKey = IDBKeyData::minimum();
169     if (rangeData.upperKey.isNull())
170         rangeData.upperKey = IDBKeyData::maximum();
171
172     auto info = IDBCursorInfo::indexCursor(m_objectStore.transaction(), m_objectStore.info().identifier(), m_info.identifier(), rangeData, direction, IndexedDB::CursorType::KeyAndValue);
173     return m_objectStore.transaction().requestOpenCursor(execState, *this, info);
174 }
175
176 ExceptionOr<Ref<IDBRequest>> IDBIndex::openCursor(ExecState& execState, RefPtr<IDBKeyRange>&& range, IDBCursorDirection direction)
177 {
178     return doOpenCursor(execState, direction, [range=WTFMove(range)]() {
179         return range;
180     });
181 }
182
183 ExceptionOr<Ref<IDBRequest>> IDBIndex::openCursor(ExecState& execState, JSValue key, IDBCursorDirection direction)
184 {
185     return doOpenCursor(execState, direction, [state=&execState, key]() {
186         auto onlyResult = IDBKeyRange::only(*state, key);
187         if (onlyResult.hasException())
188             return ExceptionOr<RefPtr<IDBKeyRange>>{ Exception(DataError, "Failed to execute 'openCursor' on 'IDBIndex': The parameter is not a valid key."_s) };
189
190         return ExceptionOr<RefPtr<IDBKeyRange>> { onlyResult.releaseReturnValue() };
191     });
192 }
193
194 ExceptionOr<Ref<IDBRequest>> IDBIndex::doOpenKeyCursor(ExecState& execState, IDBCursorDirection direction, WTF::Function<ExceptionOr<RefPtr<IDBKeyRange>>()>&& function)
195 {
196     LOG(IndexedDB, "IDBIndex::openKeyCursor");
197     ASSERT(&m_objectStore.transaction().database().originThread() == &Thread::current());
198
199     if (m_deleted || m_objectStore.isDeleted())
200         return Exception { InvalidStateError, "Failed to execute 'openKeyCursor' on 'IDBIndex': The index or its object store has been deleted."_s };
201
202     if (!m_objectStore.transaction().isActive())
203         return Exception { TransactionInactiveError, "Failed to execute 'openKeyCursor' on 'IDBIndex': The transaction is inactive or finished."_s };
204
205     auto keyRange = function();
206     if (keyRange.hasException())
207         return keyRange.releaseException();
208
209     auto* keyRangePointer = keyRange.returnValue() ? keyRange.releaseReturnValue().get() : nullptr;
210     auto info = IDBCursorInfo::indexCursor(m_objectStore.transaction(), m_objectStore.info().identifier(), m_info.identifier(), keyRangePointer, direction, IndexedDB::CursorType::KeyOnly);
211     return m_objectStore.transaction().requestOpenCursor(execState, *this, info);
212 }
213
214 ExceptionOr<Ref<IDBRequest>> IDBIndex::openKeyCursor(ExecState& execState, RefPtr<IDBKeyRange>&& range, IDBCursorDirection direction)
215 {
216     return doOpenKeyCursor(execState, direction, [range=WTFMove(range)]() {
217         return range;
218     });
219 }
220
221 ExceptionOr<Ref<IDBRequest>> IDBIndex::openKeyCursor(ExecState& execState, JSValue key, IDBCursorDirection direction)
222 {
223     return doOpenKeyCursor(execState, direction, [state=&execState, key]() {
224         auto onlyResult = IDBKeyRange::only(*state, key);
225         if (onlyResult.hasException())
226             return ExceptionOr<RefPtr<IDBKeyRange>>{ Exception(DataError, "Failed to execute 'openKeyCursor' on 'IDBIndex': The parameter is not a valid key."_s) };
227
228         return ExceptionOr<RefPtr<IDBKeyRange>> { onlyResult.releaseReturnValue() };
229     });
230 }
231
232 ExceptionOr<Ref<IDBRequest>> IDBIndex::count(ExecState& execState, IDBKeyRange* range)
233 {
234     LOG(IndexedDB, "IDBIndex::count");
235
236     return doCount(execState, range ? IDBKeyRangeData(range) : IDBKeyRangeData::allKeys());
237 }
238
239 ExceptionOr<Ref<IDBRequest>> IDBIndex::count(ExecState& execState, JSValue key)
240 {
241     LOG(IndexedDB, "IDBIndex::count");
242
243     auto idbKey = scriptValueToIDBKey(execState, key);
244     auto* idbKeyPointer = idbKey->isValid() ? idbKey.ptr() : nullptr;
245
246     return doCount(execState, IDBKeyRangeData(idbKeyPointer));
247 }
248
249 ExceptionOr<Ref<IDBRequest>> IDBIndex::doCount(ExecState& execState, const IDBKeyRangeData& range)
250 {
251     ASSERT(&m_objectStore.transaction().database().originThread() == &Thread::current());
252
253     if (m_deleted || m_objectStore.isDeleted())
254         return Exception { InvalidStateError, "Failed to execute 'count' on 'IDBIndex': The index or its object store has been deleted."_s };
255
256     auto& transaction = m_objectStore.transaction();
257     if (!transaction.isActive())
258         return Exception { TransactionInactiveError, "Failed to execute 'count' on 'IDBIndex': The transaction is inactive or finished."_s };
259
260     if (!range.isValid())
261         return Exception { DataError, "Failed to execute 'count' on 'IDBIndex': The parameter is not a valid key."_s };
262
263     return transaction.requestCount(execState, *this, range);
264 }
265
266 ExceptionOr<Ref<IDBRequest>> IDBIndex::get(ExecState& execState, IDBKeyRange* range)
267 {
268     LOG(IndexedDB, "IDBIndex::get");
269
270     return doGet(execState, IDBKeyRangeData(range));
271 }
272
273 ExceptionOr<Ref<IDBRequest>> IDBIndex::get(ExecState& execState, JSValue key)
274 {
275     LOG(IndexedDB, "IDBIndex::get");
276
277     auto idbKey = scriptValueToIDBKey(execState, key);
278     if (!idbKey->isValid())
279         return doGet(execState, Exception(DataError, "Failed to execute 'get' on 'IDBIndex': The parameter is not a valid key."_s));
280
281     return doGet(execState, IDBKeyRangeData(idbKey.ptr()));
282 }
283
284 ExceptionOr<Ref<IDBRequest>> IDBIndex::doGet(ExecState& execState, ExceptionOr<IDBKeyRangeData> range)
285 {
286     ASSERT(&m_objectStore.transaction().database().originThread() == &Thread::current());
287
288     if (m_deleted || m_objectStore.isDeleted())
289         return Exception { InvalidStateError, "Failed to execute 'get' on 'IDBIndex': The index or its object store has been deleted."_s };
290
291     auto& transaction = m_objectStore.transaction();
292     if (!transaction.isActive())
293         return Exception { TransactionInactiveError, "Failed to execute 'get' on 'IDBIndex': The transaction is inactive or finished."_s };
294
295     if (range.hasException())
296         return range.releaseException();
297     auto keyRange = range.releaseReturnValue();
298
299     if (keyRange.isNull)
300         return Exception { DataError };
301
302     return transaction.requestGetValue(execState, *this, keyRange);
303 }
304
305 ExceptionOr<Ref<IDBRequest>> IDBIndex::getKey(ExecState& execState, IDBKeyRange* range)
306 {
307     LOG(IndexedDB, "IDBIndex::getKey");
308
309     return doGetKey(execState, IDBKeyRangeData(range));
310 }
311
312 ExceptionOr<Ref<IDBRequest>> IDBIndex::getKey(ExecState& execState, JSValue key)
313 {
314     LOG(IndexedDB, "IDBIndex::getKey");
315
316     auto idbKey = scriptValueToIDBKey(execState, key);
317     if (!idbKey->isValid())
318         return doGetKey(execState, Exception(DataError, "Failed to execute 'getKey' on 'IDBIndex': The parameter is not a valid key."_s));
319
320     return doGetKey(execState, IDBKeyRangeData(idbKey.ptr()));
321 }
322
323 ExceptionOr<Ref<IDBRequest>> IDBIndex::doGetKey(ExecState& execState, ExceptionOr<IDBKeyRangeData> range)
324 {
325     ASSERT(&m_objectStore.transaction().database().originThread() == &Thread::current());
326
327     if (m_deleted || m_objectStore.isDeleted())
328         return Exception { InvalidStateError, "Failed to execute 'getKey' on 'IDBIndex': The index or its object store has been deleted."_s };
329
330     auto& transaction = m_objectStore.transaction();
331     if (!transaction.isActive())
332         return Exception { TransactionInactiveError, "Failed to execute 'getKey' on 'IDBIndex': The transaction is inactive or finished."_s };
333
334     if (range.hasException())
335         return range.releaseException();
336     auto keyRange = range.releaseReturnValue();
337     
338     if (keyRange.isNull)
339         return Exception { DataError };
340
341     return transaction.requestGetKey(execState, *this, keyRange);
342 }
343
344 ExceptionOr<Ref<IDBRequest>> IDBIndex::doGetAll(ExecState& execState, Optional<uint32_t> count, WTF::Function<ExceptionOr<RefPtr<IDBKeyRange>>()>&& function)
345 {
346     LOG(IndexedDB, "IDBIndex::getAll");
347     ASSERT(&m_objectStore.transaction().database().originThread() == &Thread::current());
348
349     if (m_deleted || m_objectStore.isDeleted())
350         return Exception { InvalidStateError, "Failed to execute 'getAll' on 'IDBIndex': The index or its object store has been deleted."_s };
351
352     if (!m_objectStore.transaction().isActive())
353         return Exception { TransactionInactiveError, "Failed to execute 'getAll' on 'IDBIndex': The transaction is inactive or finished."_s };
354
355     auto keyRange = function();
356     if (keyRange.hasException())
357         return keyRange.releaseException();
358
359     auto* keyRangePointer = keyRange.returnValue() ? keyRange.releaseReturnValue().get() : nullptr;
360     return m_objectStore.transaction().requestGetAllIndexRecords(execState, *this, keyRangePointer, IndexedDB::GetAllType::Values, count);
361 }
362
363 ExceptionOr<Ref<IDBRequest>> IDBIndex::getAll(ExecState& execState, RefPtr<IDBKeyRange>&& range, Optional<uint32_t> count)
364 {
365     return doGetAll(execState, count, [range = WTFMove(range)]() {
366         return range;
367     });
368 }
369
370 ExceptionOr<Ref<IDBRequest>> IDBIndex::getAll(ExecState& execState, JSValue key, Optional<uint32_t> count)
371 {
372     return doGetAll(execState, count, [state=&execState, key]() {
373         auto onlyResult = IDBKeyRange::only(*state, key);
374         if (onlyResult.hasException())
375             return ExceptionOr<RefPtr<IDBKeyRange>>{ Exception(DataError, "Failed to execute 'getAll' on 'IDBIndex': The parameter is not a valid key."_s) };
376
377         return ExceptionOr<RefPtr<IDBKeyRange>> { onlyResult.releaseReturnValue() };
378     });
379 }
380
381 ExceptionOr<Ref<IDBRequest>> IDBIndex::doGetAllKeys(ExecState& execState, Optional<uint32_t> count, WTF::Function<ExceptionOr<RefPtr<IDBKeyRange>>()>&& function)
382 {
383     LOG(IndexedDB, "IDBIndex::getAllKeys");
384     ASSERT(&m_objectStore.transaction().database().originThread() == &Thread::current());
385
386     if (m_deleted || m_objectStore.isDeleted())
387         return Exception { InvalidStateError, "Failed to execute 'getAllKeys' on 'IDBIndex': The index or its object store has been deleted."_s };
388
389     if (!m_objectStore.transaction().isActive())
390         return Exception { TransactionInactiveError, "Failed to execute 'getAllKeys' on 'IDBIndex': The transaction is inactive or finished."_s };
391
392     auto keyRange = function();
393     if (keyRange.hasException())
394         return keyRange.releaseException();
395
396     auto* keyRangePointer = keyRange.returnValue() ? keyRange.releaseReturnValue().get() : nullptr;
397     return m_objectStore.transaction().requestGetAllIndexRecords(execState, *this, keyRangePointer, IndexedDB::GetAllType::Keys, count);
398 }
399
400 ExceptionOr<Ref<IDBRequest>> IDBIndex::getAllKeys(ExecState& execState, RefPtr<IDBKeyRange>&& range, Optional<uint32_t> count)
401 {
402     return doGetAllKeys(execState, count, [range = WTFMove(range)]() {
403         return range;
404     });
405 }
406
407 ExceptionOr<Ref<IDBRequest>> IDBIndex::getAllKeys(ExecState& execState, JSValue key, Optional<uint32_t> count)
408 {
409     return doGetAllKeys(execState, count, [state=&execState, key]() {
410         auto onlyResult = IDBKeyRange::only(*state, key);
411         if (onlyResult.hasException())
412             return ExceptionOr<RefPtr<IDBKeyRange>>{ Exception(DataError, "Failed to execute 'getAllKeys' on 'IDBIndex': The parameter is not a valid key."_s) };
413
414         return ExceptionOr<RefPtr<IDBKeyRange>> { onlyResult.releaseReturnValue() };
415     });
416 }
417
418 void IDBIndex::markAsDeleted()
419 {
420     ASSERT(&m_objectStore.transaction().database().originThread() == &Thread::current());
421
422     ASSERT(!m_deleted);
423     m_deleted = true;
424 }
425
426 void IDBIndex::ref()
427 {
428     m_objectStore.ref();
429 }
430
431 void IDBIndex::deref()
432 {
433     m_objectStore.deref();
434 }
435
436 } // namespace WebCore
437
438 #endif // ENABLE(INDEXED_DATABASE)