a597ac19b63b888c122f9283a7dca2ef974fee20
[WebKit-https.git] / Source / WebCore / Modules / indexeddb / IDBObjectStore.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 "IDBObjectStore.h"
28
29 #if ENABLE(INDEXED_DATABASE)
30
31 #include "DOMStringList.h"
32 #include "Document.h"
33 #include "IDBBindingUtilities.h"
34 #include "IDBCursor.h"
35 #include "IDBDatabase.h"
36 #include "IDBError.h"
37 #include "IDBGetRecordData.h"
38 #include "IDBIndex.h"
39 #include "IDBKey.h"
40 #include "IDBKeyRangeData.h"
41 #include "IDBRequest.h"
42 #include "IDBTransaction.h"
43 #include "IndexedDB.h"
44 #include "Logging.h"
45 #include "Page.h"
46 #include "ScriptExecutionContext.h"
47 #include "ScriptState.h"
48 #include "SerializedScriptValue.h"
49 #include <JavaScriptCore/CatchScope.h>
50 #include <JavaScriptCore/HeapInlines.h>
51 #include <JavaScriptCore/JSCJSValueInlines.h>
52 #include <wtf/Locker.h>
53
54 namespace WebCore {
55 using namespace JSC;
56
57 IDBObjectStore::IDBObjectStore(ScriptExecutionContext& context, const IDBObjectStoreInfo& info, IDBTransaction& transaction)
58     : ActiveDOMObject(&context)
59     , m_info(info)
60     , m_originalInfo(info)
61     , m_transaction(transaction)
62 {
63     ASSERT(&m_transaction.database().originThread() == &Thread::current());
64
65     suspendIfNeeded();
66 }
67
68 IDBObjectStore::~IDBObjectStore()
69 {
70     ASSERT(&m_transaction.database().originThread() == &Thread::current());
71 }
72
73 const char* IDBObjectStore::activeDOMObjectName() const
74 {
75     return "IDBObjectStore";
76 }
77
78 bool IDBObjectStore::canSuspendForDocumentSuspension() const
79 {
80     return true;
81 }
82
83 bool IDBObjectStore::hasPendingActivity() const
84 {
85     return m_transaction.hasPendingActivity();
86 }
87
88 const String& IDBObjectStore::name() const
89 {
90     ASSERT(&m_transaction.database().originThread() == &Thread::current());
91     return m_info.name();
92 }
93
94 ExceptionOr<void> IDBObjectStore::setName(const String& name)
95 {
96     ASSERT(&m_transaction.database().originThread() == &Thread::current());
97
98     if (m_deleted)
99         return Exception { InvalidStateError, "Failed set property 'name' on 'IDBObjectStore': The object store has been deleted."_s };
100
101     if (!m_transaction.isVersionChange())
102         return Exception { InvalidStateError, "Failed set property 'name' on 'IDBObjectStore': The object store's transaction is not a version change transaction."_s };
103
104     if (!m_transaction.isActive())
105         return Exception { TransactionInactiveError, "Failed set property 'name' on 'IDBObjectStore': The object store's transaction is not active."_s };
106
107     if (m_info.name() == name)
108         return { };
109
110     if (m_transaction.database().info().hasObjectStore(name))
111         return Exception { ConstraintError, makeString("Failed set property 'name' on 'IDBObjectStore': The database already has an object store named '", name, "'.") };
112
113     m_transaction.database().renameObjectStore(*this, name);
114     m_info.rename(name);
115
116     return { };
117 }
118
119 const Optional<IDBKeyPath>& IDBObjectStore::keyPath() const
120 {
121     ASSERT(&m_transaction.database().originThread() == &Thread::current());
122     return m_info.keyPath();
123 }
124
125 Ref<DOMStringList> IDBObjectStore::indexNames() const
126 {
127     ASSERT(&m_transaction.database().originThread() == &Thread::current());
128
129     auto indexNames = DOMStringList::create();
130
131     if (!m_deleted) {
132         for (auto& name : m_info.indexNames())
133             indexNames->append(name);
134         indexNames->sort();
135     }
136
137     return indexNames;
138 }
139
140 IDBTransaction& IDBObjectStore::transaction()
141 {
142     ASSERT(&m_transaction.database().originThread() == &Thread::current());
143     return m_transaction;
144 }
145
146 bool IDBObjectStore::autoIncrement() const
147 {
148     ASSERT(&m_transaction.database().originThread() == &Thread::current());
149     return m_info.autoIncrement();
150 }
151
152 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::doOpenCursor(ExecState& execState, IDBCursorDirection direction, WTF::Function<ExceptionOr<RefPtr<IDBKeyRange>>()>&& function)
153 {
154     LOG(IndexedDB, "IDBObjectStore::openCursor");
155     ASSERT(&m_transaction.database().originThread() == &Thread::current());
156
157     if (m_deleted)
158         return Exception { InvalidStateError, "Failed to execute 'openCursor' on 'IDBObjectStore': The object store has been deleted."_s };
159
160     if (!m_transaction.isActive())
161         return Exception { TransactionInactiveError, "Failed to execute 'openCursor' on 'IDBObjectStore': The transaction is inactive or finished."_s };
162
163     auto keyRange = function();
164     if (keyRange.hasException())
165         return keyRange.releaseException();
166     auto* keyRangePointer = keyRange.returnValue() ? keyRange.releaseReturnValue().get() : nullptr;
167
168     auto info = IDBCursorInfo::objectStoreCursor(m_transaction, m_info.identifier(), keyRangePointer, direction, IndexedDB::CursorType::KeyAndValue);
169     return m_transaction.requestOpenCursor(execState, *this, info);
170 }
171
172 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::openCursor(ExecState& execState, RefPtr<IDBKeyRange>&& range, IDBCursorDirection direction)
173 {
174     return doOpenCursor(execState, direction, [range = WTFMove(range)]() {
175         return range;
176     });
177 }
178
179 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::openCursor(ExecState& execState, JSValue key, IDBCursorDirection direction)
180 {
181     return doOpenCursor(execState, direction, [state=&execState, key]() {
182         auto onlyResult = IDBKeyRange::only(*state, key);
183         if (onlyResult.hasException())
184             return ExceptionOr<RefPtr<IDBKeyRange>>{ Exception(DataError, "Failed to execute 'openCursor' on 'IDBObjectStore': The parameter is not a valid key."_s) };
185
186         return ExceptionOr<RefPtr<IDBKeyRange>> { onlyResult.releaseReturnValue() };
187     });
188 }
189
190 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::doOpenKeyCursor(ExecState& execState, IDBCursorDirection direction, WTF::Function<ExceptionOr<RefPtr<IDBKeyRange>>()>&& function)
191 {
192     LOG(IndexedDB, "IDBObjectStore::openKeyCursor");
193     ASSERT(&m_transaction.database().originThread() == &Thread::current());
194
195     if (m_deleted)
196         return Exception { InvalidStateError, "Failed to execute 'openKeyCursor' on 'IDBObjectStore': The object store has been deleted."_s };
197
198     if (!m_transaction.isActive())
199         return Exception { TransactionInactiveError, "Failed to execute 'openKeyCursor' on 'IDBObjectStore': The transaction is inactive or finished."_s };
200
201     auto keyRange = function();
202     if (keyRange.hasException())
203         return keyRange.releaseException();
204
205     auto* keyRangePointer = keyRange.returnValue() ? keyRange.releaseReturnValue().get() : nullptr;
206     auto info = IDBCursorInfo::objectStoreCursor(m_transaction, m_info.identifier(), keyRangePointer, direction, IndexedDB::CursorType::KeyOnly);
207     return m_transaction.requestOpenCursor(execState, *this, info);
208 }
209
210 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::openKeyCursor(ExecState& execState, RefPtr<IDBKeyRange>&& range, IDBCursorDirection direction)
211 {
212     return doOpenKeyCursor(execState, direction, [range = WTFMove(range)]() {
213         return range;
214     });
215 }
216
217 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::openKeyCursor(ExecState& execState, JSValue key, IDBCursorDirection direction)
218 {
219     return doOpenCursor(execState, direction, [state=&execState, key]() {
220         auto onlyResult = IDBKeyRange::only(*state, key);
221         if (onlyResult.hasException())
222             return ExceptionOr<RefPtr<IDBKeyRange>>{ Exception(DataError, "Failed to execute 'openKeyCursor' on 'IDBObjectStore': The parameter is not a valid key."_s) };
223
224         return ExceptionOr<RefPtr<IDBKeyRange>> { onlyResult.releaseReturnValue() };
225     });
226 }
227
228 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::get(ExecState& execState, JSValue key)
229 {
230     LOG(IndexedDB, "IDBObjectStore::get");
231     ASSERT(&m_transaction.database().originThread() == &Thread::current());
232
233     if (m_deleted)
234         return Exception { InvalidStateError, "Failed to execute 'get' on 'IDBObjectStore': The object store has been deleted."_s };
235
236     if (!m_transaction.isActive())
237         return Exception { TransactionInactiveError, "Failed to execute 'get' on 'IDBObjectStore': The transaction is inactive or finished."_s };
238
239     auto idbKey = scriptValueToIDBKey(execState, key);
240     if (!idbKey->isValid())
241         return Exception { DataError, "Failed to execute 'get' on 'IDBObjectStore': The parameter is not a valid key."_s };
242
243     return m_transaction.requestGetRecord(execState, *this, { idbKey.ptr(), IDBGetRecordDataType::KeyAndValue });
244 }
245
246 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::get(ExecState& execState, IDBKeyRange* keyRange)
247 {
248     LOG(IndexedDB, "IDBObjectStore::get");
249     ASSERT(&m_transaction.database().originThread() == &Thread::current());
250
251     if (m_deleted)
252         return Exception { InvalidStateError, "Failed to execute 'get' on 'IDBObjectStore': The object store has been deleted."_s };
253
254     if (!m_transaction.isActive())
255         return Exception { TransactionInactiveError };
256
257     IDBKeyRangeData keyRangeData(keyRange);
258     if (!keyRangeData.isValid())
259         return Exception { DataError };
260
261     return m_transaction.requestGetRecord(execState, *this, { keyRangeData, IDBGetRecordDataType::KeyAndValue });
262 }
263
264 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::getKey(ExecState& execState, JSValue key)
265 {
266     LOG(IndexedDB, "IDBObjectStore::getKey");
267     ASSERT(&m_transaction.database().originThread() == &Thread::current());
268
269     if (m_deleted)
270         return Exception { InvalidStateError, "Failed to execute 'getKey' on 'IDBObjectStore': The object store has been deleted."_s };
271
272     if (!m_transaction.isActive())
273         return Exception { TransactionInactiveError, "Failed to execute 'getKey' on 'IDBObjectStore': The transaction is inactive or finished."_s };
274
275     auto idbKey = scriptValueToIDBKey(execState, key);
276     if (!idbKey->isValid())
277         return Exception { DataError, "Failed to execute 'getKey' on 'IDBObjectStore': The parameter is not a valid key."_s };
278
279     return m_transaction.requestGetRecord(execState, *this, { idbKey.ptr(), IDBGetRecordDataType::KeyOnly });
280 }
281
282 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::getKey(ExecState& execState, IDBKeyRange* keyRange)
283 {
284     LOG(IndexedDB, "IDBObjectStore::getKey");
285     ASSERT(&m_transaction.database().originThread() == &Thread::current());
286
287     if (m_deleted)
288         return Exception { InvalidStateError, "Failed to execute 'getKey' on 'IDBObjectStore': The object store has been deleted."_s };
289
290     if (!m_transaction.isActive())
291         return Exception { TransactionInactiveError, "Failed to execute 'getKey' on 'IDBObjectStore': The transaction is inactive or finished."_s };
292
293     IDBKeyRangeData keyRangeData(keyRange);
294     if (!keyRangeData.isValid())
295         return Exception { DataError, "Failed to execute 'getKey' on 'IDBObjectStore': The parameter is not a valid key range."_s };
296
297     return m_transaction.requestGetRecord(execState, *this, { keyRangeData, IDBGetRecordDataType::KeyOnly });
298 }
299
300 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::add(ExecState& execState, JSValue value, JSValue key)
301 {
302     RefPtr<IDBKey> idbKey;
303     if (!key.isUndefined())
304         idbKey = scriptValueToIDBKey(execState, key);
305     return putOrAdd(execState, value, idbKey, IndexedDB::ObjectStoreOverwriteMode::NoOverwrite, InlineKeyCheck::Perform);
306 }
307
308 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::put(ExecState& execState, JSValue value, JSValue key)
309 {
310     RefPtr<IDBKey> idbKey;
311     if (!key.isUndefined())
312         idbKey = scriptValueToIDBKey(execState, key);
313     return putOrAdd(execState, value, idbKey, IndexedDB::ObjectStoreOverwriteMode::Overwrite, InlineKeyCheck::Perform);
314 }
315
316 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::putForCursorUpdate(ExecState& state, JSValue value, RefPtr<IDBKey> key)
317 {
318     return putOrAdd(state, value, WTFMove(key), IndexedDB::ObjectStoreOverwriteMode::OverwriteForCursor, InlineKeyCheck::DoNotPerform);
319 }
320
321 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::putOrAdd(ExecState& state, JSValue value, RefPtr<IDBKey> key, IndexedDB::ObjectStoreOverwriteMode overwriteMode, InlineKeyCheck inlineKeyCheck)
322 {
323     VM& vm = state.vm();
324     auto scope = DECLARE_CATCH_SCOPE(vm);
325
326     LOG(IndexedDB, "IDBObjectStore::putOrAdd");
327     ASSERT(&m_transaction.database().originThread() == &Thread::current());
328
329     auto context = scriptExecutionContextFromExecState(&state);
330     if (!context)
331         return Exception { UnknownError, "Unable to store record in object store because it does not have a valid script execution context"_s };
332
333     if (m_deleted)
334         return Exception { InvalidStateError, "Failed to store record in an IDBObjectStore: The object store has been deleted."_s };
335
336     if (!m_transaction.isActive())
337         return Exception { TransactionInactiveError, "Failed to store record in an IDBObjectStore: The transaction is inactive or finished."_s };
338
339     if (m_transaction.isReadOnly())
340         return Exception { ReadonlyError, "Failed to store record in an IDBObjectStore: The transaction is read-only."_s };
341
342     auto serializedValue = SerializedScriptValue::create(state, value);
343     if (UNLIKELY(scope.exception()))
344         return Exception { DataCloneError, "Failed to store record in an IDBObjectStore: An object could not be cloned."_s };
345
346     bool privateBrowsingEnabled = false;
347     if (is<Document>(*context)) {
348         if (auto* page = downcast<Document>(*context).page())
349             privateBrowsingEnabled = page->sessionID().isEphemeral();
350     }
351
352     if (serializedValue->hasBlobURLs() && privateBrowsingEnabled) {
353         // https://bugs.webkit.org/show_bug.cgi?id=156347 - Support Blobs in private browsing.
354         return Exception { DataCloneError, "Failed to store record in an IDBObjectStore: BlobURLs are not yet supported."_s };
355     }
356
357     if (key && !key->isValid())
358         return Exception { DataError, "Failed to store record in an IDBObjectStore: The parameter is not a valid key."_s };
359
360     bool usesInlineKeys = !!m_info.keyPath();
361     bool usesKeyGenerator = autoIncrement();
362     if (usesInlineKeys && inlineKeyCheck == InlineKeyCheck::Perform) {
363         if (key)
364             return Exception { DataError, "Failed to store record in an IDBObjectStore: The object store uses in-line keys and the key parameter was provided."_s };
365
366         RefPtr<IDBKey> keyPathKey = maybeCreateIDBKeyFromScriptValueAndKeyPath(state, value, m_info.keyPath().value());
367         if (keyPathKey && !keyPathKey->isValid())
368             return Exception { DataError, "Failed to store record in an IDBObjectStore: Evaluating the object store's key path yielded a value that is not a valid key."_s };
369
370         if (!keyPathKey) {
371             if (!usesKeyGenerator)
372                 return Exception { DataError, "Failed to store record in an IDBObjectStore: Evaluating the object store's key path did not yield a value."_s };
373             if (!canInjectIDBKeyIntoScriptValue(state, value, m_info.keyPath().value()))
374                 return Exception { DataError };
375         }
376
377         if (keyPathKey) {
378             ASSERT(!key);
379             key = keyPathKey;
380         }
381     } else if (!usesKeyGenerator && !key)
382         return Exception { DataError, "Failed to store record in an IDBObjectStore: The object store uses out-of-line keys and has no key generator and the key parameter was not provided."_s };
383
384     return m_transaction.requestPutOrAdd(state, *this, WTFMove(key), *serializedValue, overwriteMode);
385 }
386
387 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::deleteFunction(ExecState& execState, IDBKeyRange* keyRange)
388 {
389     return doDelete(execState, [keyRange]() {
390         return makeRefPtr(keyRange);
391     });
392 }
393
394 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::doDelete(ExecState& execState, WTF::Function<ExceptionOr<RefPtr<IDBKeyRange>>()>&& function)
395 {
396     LOG(IndexedDB, "IDBObjectStore::deleteFunction");
397     ASSERT(&m_transaction.database().originThread() == &Thread::current());
398
399     // The IDB spec for several IDBObjectStore methods states that transaction related exceptions should fire before
400     // the exception for an object store being deleted.
401     // However, a handful of W3C IDB tests expect the deleted exception even though the transaction inactive exception also applies.
402     // Additionally, Chrome and Edge agree with the test, as does Legacy IDB in WebKit.
403     // Until this is sorted out, we'll agree with the test and the majority share browsers.
404     if (m_deleted)
405         return Exception { InvalidStateError, "Failed to execute 'delete' on 'IDBObjectStore': The object store has been deleted."_s };
406
407     if (!m_transaction.isActive())
408         return Exception { TransactionInactiveError, "Failed to execute 'delete' on 'IDBObjectStore': The transaction is inactive or finished."_s };
409
410     if (m_transaction.isReadOnly())
411         return Exception { ReadonlyError, "Failed to execute 'delete' on 'IDBObjectStore': The transaction is read-only."_s };
412
413     auto keyRange = function();
414     if (keyRange.hasException())
415         return keyRange.releaseException();
416
417     IDBKeyRangeData keyRangeData = keyRange.returnValue() ? keyRange.releaseReturnValue().get() : nullptr;
418     if (!keyRangeData.isValid())
419         return Exception { DataError, "Failed to execute 'delete' on 'IDBObjectStore': The parameter is not a valid key range."_s };
420
421     return m_transaction.requestDeleteRecord(execState, *this, keyRangeData);
422 }
423
424 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::deleteFunction(ExecState& execState, JSValue key)
425 {
426     return doDelete(execState, [state=&execState, key]() {
427         auto idbKey = scriptValueToIDBKey(*state, key);
428         if (!idbKey->isValid())
429             return ExceptionOr<RefPtr<IDBKeyRange>>{ Exception(DataError, "Failed to execute 'delete' on 'IDBObjectStore': The parameter is not a valid key."_s) };
430         return ExceptionOr<RefPtr<IDBKeyRange>> { (IDBKeyRange::create(WTFMove(idbKey))).ptr() };
431     });
432 }
433
434 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::clear(ExecState& execState)
435 {
436     LOG(IndexedDB, "IDBObjectStore::clear");
437     ASSERT(&m_transaction.database().originThread() == &Thread::current());
438
439     // The IDB spec for several IDBObjectStore methods states that transaction related exceptions should fire before
440     // the exception for an object store being deleted.
441     // However, a handful of W3C IDB tests expect the deleted exception even though the transaction inactive exception also applies.
442     // Additionally, Chrome and Edge agree with the test, as does Legacy IDB in WebKit.
443     // Until this is sorted out, we'll agree with the test and the majority share browsers.
444     if (m_deleted)
445         return Exception { InvalidStateError, "Failed to execute 'clear' on 'IDBObjectStore': The object store has been deleted."_s };
446
447     if (!m_transaction.isActive())
448         return Exception { TransactionInactiveError, "Failed to execute 'clear' on 'IDBObjectStore': The transaction is inactive or finished."_s };
449
450     if (m_transaction.isReadOnly())
451         return Exception { ReadonlyError, "Failed to execute 'clear' on 'IDBObjectStore': The transaction is read-only."_s };
452
453     return m_transaction.requestClearObjectStore(execState, *this);
454 }
455
456 ExceptionOr<Ref<IDBIndex>> IDBObjectStore::createIndex(ExecState&, const String& name, IDBKeyPath&& keyPath, const IndexParameters& parameters)
457 {
458     LOG(IndexedDB, "IDBObjectStore::createIndex %s (keyPath: %s, unique: %i, multiEntry: %i)", name.utf8().data(), loggingString(keyPath).utf8().data(), parameters.unique, parameters.multiEntry);
459     ASSERT(&m_transaction.database().originThread() == &Thread::current());
460
461     if (!m_transaction.isVersionChange())
462         return Exception { InvalidStateError, "Failed to execute 'createIndex' on 'IDBObjectStore': The database is not running a version change transaction."_s };
463
464     if (m_deleted)
465         return Exception { InvalidStateError, "Failed to execute 'createIndex' on 'IDBObjectStore': The object store has been deleted."_s };
466
467     if (!m_transaction.isActive())
468         return Exception { TransactionInactiveError, "Failed to execute 'createIndex' on 'IDBObjectStore': The transaction is inactive."_s};
469
470     if (m_info.hasIndex(name))
471         return Exception { ConstraintError, "Failed to execute 'createIndex' on 'IDBObjectStore': An index with the specified name already exists."_s };
472
473     if (!isIDBKeyPathValid(keyPath))
474         return Exception { SyntaxError, "Failed to execute 'createIndex' on 'IDBObjectStore': The keyPath argument contains an invalid key path."_s };
475
476     if (name.isNull())
477         return Exception { TypeError };
478
479     if (parameters.multiEntry && WTF::holds_alternative<Vector<String>>(keyPath))
480         return Exception { InvalidAccessError, "Failed to execute 'createIndex' on 'IDBObjectStore': The keyPath argument was an array and the multiEntry option is true."_s };
481
482     // Install the new Index into the ObjectStore's info.
483     IDBIndexInfo info = m_info.createNewIndex(name, WTFMove(keyPath), parameters.unique, parameters.multiEntry);
484     m_transaction.database().didCreateIndexInfo(info);
485
486     // Create the actual IDBObjectStore from the transaction, which also schedules the operation server side.
487     auto index = m_transaction.createIndex(*this, info);
488
489     Ref<IDBIndex> referencedIndex { *index };
490
491     Locker<Lock> locker(m_referencedIndexLock);
492     m_referencedIndexes.set(name, WTFMove(index));
493
494     return referencedIndex;
495 }
496
497 ExceptionOr<Ref<IDBIndex>> IDBObjectStore::index(const String& indexName)
498 {
499     LOG(IndexedDB, "IDBObjectStore::index");
500     ASSERT(&m_transaction.database().originThread() == &Thread::current());
501
502     if (!scriptExecutionContext())
503         return Exception { InvalidStateError }; // FIXME: Is this code tested? Is iteven reachable?
504
505     if (m_deleted)
506         return Exception { InvalidStateError, "Failed to execute 'index' on 'IDBObjectStore': The object store has been deleted."_s };
507
508     if (m_transaction.isFinishedOrFinishing())
509         return Exception { InvalidStateError, "Failed to execute 'index' on 'IDBObjectStore': The transaction is finished."_s };
510
511     Locker<Lock> locker(m_referencedIndexLock);
512     auto iterator = m_referencedIndexes.find(indexName);
513     if (iterator != m_referencedIndexes.end())
514         return Ref<IDBIndex> { *iterator->value };
515
516     auto* info = m_info.infoForExistingIndex(indexName);
517     if (!info)
518         return Exception { NotFoundError, "Failed to execute 'index' on 'IDBObjectStore': The specified index was not found."_s };
519
520     auto index = makeUnique<IDBIndex>(*scriptExecutionContext(), *info, *this);
521
522     Ref<IDBIndex> referencedIndex { *index };
523
524     m_referencedIndexes.set(indexName, WTFMove(index));
525
526     return referencedIndex;
527 }
528
529 ExceptionOr<void> IDBObjectStore::deleteIndex(const String& name)
530 {
531     LOG(IndexedDB, "IDBObjectStore::deleteIndex %s", name.utf8().data());
532     ASSERT(&m_transaction.database().originThread() == &Thread::current());
533
534     if (m_deleted)
535         return Exception { InvalidStateError, "Failed to execute 'deleteIndex' on 'IDBObjectStore': The object store has been deleted."_s };
536
537     if (!m_transaction.isVersionChange())
538         return Exception { InvalidStateError, "Failed to execute 'deleteIndex' on 'IDBObjectStore': The database is not running a version change transaction."_s };
539
540     if (!m_transaction.isActive())
541         return Exception { TransactionInactiveError,  "Failed to execute 'deleteIndex' on 'IDBObjectStore': The transaction is inactive or finished."_s };
542
543     if (!m_info.hasIndex(name))
544         return Exception { NotFoundError, "Failed to execute 'deleteIndex' on 'IDBObjectStore': The specified index was not found."_s };
545
546     auto* info = m_info.infoForExistingIndex(name);
547     ASSERT(info);
548     m_transaction.database().didDeleteIndexInfo(*info);
549
550     m_info.deleteIndex(name);
551
552     {
553         Locker<Lock> locker(m_referencedIndexLock);
554         if (auto index = m_referencedIndexes.take(name)) {
555             index->markAsDeleted();
556             auto identifier = index->info().identifier();
557             m_deletedIndexes.add(identifier, WTFMove(index));
558         }
559     }
560
561     m_transaction.deleteIndex(m_info.identifier(), name);
562
563     return { };
564 }
565
566 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::count(ExecState& execState, JSValue key)
567 {
568     LOG(IndexedDB, "IDBObjectStore::count");
569
570     auto idbKey = scriptValueToIDBKey(execState, key);
571
572     return doCount(execState, IDBKeyRangeData(idbKey->isValid() ? idbKey.ptr() : nullptr));
573 }
574
575 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::count(ExecState& execState, IDBKeyRange* range)
576 {
577     LOG(IndexedDB, "IDBObjectStore::count");
578
579     return doCount(execState, range ? IDBKeyRangeData(range) : IDBKeyRangeData::allKeys());
580 }
581
582 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::doCount(ExecState& execState, const IDBKeyRangeData& range)
583 {
584     ASSERT(&m_transaction.database().originThread() == &Thread::current());
585
586     // The IDB spec for several IDBObjectStore methods states that transaction related exceptions should fire before
587     // the exception for an object store being deleted.
588     // However, a handful of W3C IDB tests expect the deleted exception even though the transaction inactive exception also applies.
589     // Additionally, Chrome and Edge agree with the test, as does Legacy IDB in WebKit.
590     // Until this is sorted out, we'll agree with the test and the majority share browsers.
591     if (m_deleted)
592         return Exception { InvalidStateError, "Failed to execute 'count' on 'IDBObjectStore': The object store has been deleted."_s };
593
594     if (!m_transaction.isActive())
595         return Exception { TransactionInactiveError, "Failed to execute 'count' on 'IDBObjectStore': The transaction is inactive or finished."_s };
596
597     if (!range.isValid())
598         return Exception { DataError, "Failed to execute 'count' on 'IDBObjectStore': The parameter is not a valid key."_s };
599
600     return m_transaction.requestCount(execState, *this, range);
601 }
602
603 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::doGetAll(ExecState& execState, Optional<uint32_t> count, WTF::Function<ExceptionOr<RefPtr<IDBKeyRange>>()>&& function)
604 {
605     LOG(IndexedDB, "IDBObjectStore::getAll");
606     ASSERT(&m_transaction.database().originThread() == &Thread::current());
607
608     if (m_deleted)
609         return Exception { InvalidStateError, "Failed to execute 'getAll' on 'IDBObjectStore': The object store has been deleted."_s };
610
611     if (!m_transaction.isActive())
612         return Exception { TransactionInactiveError, "Failed to execute 'getAll' on 'IDBObjectStore': The transaction is inactive or finished."_s };
613
614     auto keyRange = function();
615     if (keyRange.hasException())
616         return keyRange.releaseException();
617
618     auto* keyRangePointer = keyRange.returnValue() ? keyRange.releaseReturnValue().get() : nullptr;
619     return m_transaction.requestGetAllObjectStoreRecords(execState, *this, keyRangePointer, IndexedDB::GetAllType::Values, count);
620 }
621
622 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::getAll(ExecState& execState, RefPtr<IDBKeyRange>&& range, Optional<uint32_t> count)
623 {
624     return doGetAll(execState, count, [range = WTFMove(range)]() {
625         return range;
626     });
627 }
628
629 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::getAll(ExecState& execState, JSValue key, Optional<uint32_t> count)
630 {
631     return doGetAll(execState, count, [state=&execState, key]() {
632         auto onlyResult = IDBKeyRange::only(*state, key);
633         if (onlyResult.hasException())
634             return ExceptionOr<RefPtr<IDBKeyRange>>{ Exception(DataError, "Failed to execute 'getAll' on 'IDBObjectStore': The parameter is not a valid key."_s) };
635
636         return ExceptionOr<RefPtr<IDBKeyRange>> { onlyResult.releaseReturnValue() };
637     });
638 }
639
640 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::doGetAllKeys(ExecState& execState, Optional<uint32_t> count, WTF::Function<ExceptionOr<RefPtr<IDBKeyRange>>()>&& function)
641 {
642     LOG(IndexedDB, "IDBObjectStore::getAllKeys");
643     ASSERT(&m_transaction.database().originThread() == &Thread::current());
644
645     if (m_deleted)
646         return Exception { InvalidStateError, "Failed to execute 'getAllKeys' on 'IDBObjectStore': The object store has been deleted."_s };
647
648     if (!m_transaction.isActive())
649         return Exception { TransactionInactiveError, "Failed to execute 'getAllKeys' on 'IDBObjectStore': The transaction is inactive or finished."_s };
650
651     auto keyRange = function();
652     if (keyRange.hasException())
653         return keyRange.releaseException();
654
655     auto* keyRangePointer = keyRange.returnValue() ? keyRange.releaseReturnValue().get() : nullptr;
656     return m_transaction.requestGetAllObjectStoreRecords(execState, *this, keyRangePointer, IndexedDB::GetAllType::Keys, count);
657 }
658
659 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::getAllKeys(ExecState& execState, RefPtr<IDBKeyRange>&& range, Optional<uint32_t> count)
660 {
661     return doGetAllKeys(execState, count, [range = WTFMove(range)]() {
662         return range;
663     });
664 }
665
666 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::getAllKeys(ExecState& execState, JSValue key, Optional<uint32_t> count)
667 {
668     return doGetAllKeys(execState, count, [state=&execState, key]() {
669         auto onlyResult = IDBKeyRange::only(*state, key);
670         if (onlyResult.hasException())
671             return ExceptionOr<RefPtr<IDBKeyRange>>{ Exception(DataError, "Failed to execute 'getAllKeys' on 'IDBObjectStore': The parameter is not a valid key."_s) };
672
673         return ExceptionOr<RefPtr<IDBKeyRange>> { onlyResult.releaseReturnValue() };
674     });
675 }
676
677 void IDBObjectStore::markAsDeleted()
678 {
679     ASSERT(&m_transaction.database().originThread() == &Thread::current());
680     m_deleted = true;
681 }
682
683 void IDBObjectStore::rollbackForVersionChangeAbort()
684 {
685     ASSERT(&m_transaction.database().originThread() == &Thread::current());
686
687     String currentName = m_info.name();
688     m_info = m_originalInfo;
689
690     auto& databaseInfo = transaction().database().info();
691     auto* objectStoreInfo = databaseInfo.infoForExistingObjectStore(m_info.identifier());
692     if (!objectStoreInfo) {
693         m_info.rename(currentName);
694         m_deleted = true;
695     } else {
696         m_deleted = false;
697         
698         HashSet<uint64_t> indexesToRemove;
699         for (auto indexIdentifier : objectStoreInfo->indexMap().keys()) {
700             if (!objectStoreInfo->hasIndex(indexIdentifier))
701                 indexesToRemove.add(indexIdentifier);
702         }
703
704         for (auto indexIdentifier : indexesToRemove)
705             m_info.deleteIndex(indexIdentifier);
706     }
707
708     Locker<Lock> locker(m_referencedIndexLock);
709
710     Vector<uint64_t> identifiersToRemove;
711     for (auto& iterator : m_deletedIndexes) {
712         if (m_info.hasIndex(iterator.key)) {
713             auto name = iterator.value->info().name();
714             m_referencedIndexes.set(name, WTFMove(iterator.value));
715             identifiersToRemove.append(iterator.key);
716         }
717     }
718
719     for (auto identifier : identifiersToRemove)
720         m_deletedIndexes.remove(identifier);
721
722     for (auto& index : m_referencedIndexes.values())
723         index->rollbackInfoForVersionChangeAbort();
724 }
725
726 void IDBObjectStore::visitReferencedIndexes(SlotVisitor& visitor) const
727 {
728     Locker<Lock> locker(m_referencedIndexLock);
729     for (auto& index : m_referencedIndexes.values())
730         visitor.addOpaqueRoot(index.get());
731     for (auto& index : m_deletedIndexes.values())
732         visitor.addOpaqueRoot(index.get());
733 }
734
735 void IDBObjectStore::renameReferencedIndex(IDBIndex& index, const String& newName)
736 {
737     LOG(IndexedDB, "IDBObjectStore::renameReferencedIndex");
738
739     auto* indexInfo = m_info.infoForExistingIndex(index.info().identifier());
740     ASSERT(indexInfo);
741     indexInfo->rename(newName);
742
743     ASSERT(m_referencedIndexes.contains(index.info().name()));
744     ASSERT(!m_referencedIndexes.contains(newName));
745     ASSERT(m_referencedIndexes.get(index.info().name()) == &index);
746
747     m_referencedIndexes.set(newName, m_referencedIndexes.take(index.info().name()));
748 }
749
750 void IDBObjectStore::ref()
751 {
752     m_transaction.ref();
753 }
754
755 void IDBObjectStore::deref()
756 {
757     m_transaction.deref();
758 }
759
760 } // namespace WebCore
761
762 #endif // ENABLE(INDEXED_DATABASE)