Layout tests imported/w3c/web-platform-tests/IndexedDB/*-exception-order.html are...
[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 false;
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         // Clear the DOM exception from the serializer so we can give a more targeted exception.
345         scope.clearException();
346
347         return Exception { DataCloneError, "Failed to store record in an IDBObjectStore: An object could not be cloned."_s };
348     }
349
350     bool privateBrowsingEnabled = false;
351     if (is<Document>(*context)) {
352         if (auto* page = downcast<Document>(*context).page())
353             privateBrowsingEnabled = page->sessionID().isEphemeral();
354     }
355
356     if (serializedValue->hasBlobURLs() && privateBrowsingEnabled) {
357         // https://bugs.webkit.org/show_bug.cgi?id=156347 - Support Blobs in private browsing.
358         return Exception { DataCloneError, "Failed to store record in an IDBObjectStore: BlobURLs are not yet supported."_s };
359     }
360
361     if (key && !key->isValid())
362         return Exception { DataError, "Failed to store record in an IDBObjectStore: The parameter is not a valid key."_s };
363
364     bool usesInlineKeys = !!m_info.keyPath();
365     bool usesKeyGenerator = autoIncrement();
366     if (usesInlineKeys && inlineKeyCheck == InlineKeyCheck::Perform) {
367         if (key)
368             return Exception { DataError, "Failed to store record in an IDBObjectStore: The object store uses in-line keys and the key parameter was provided."_s };
369
370         RefPtr<IDBKey> keyPathKey = maybeCreateIDBKeyFromScriptValueAndKeyPath(state, value, m_info.keyPath().value());
371         if (keyPathKey && !keyPathKey->isValid())
372             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 };
373
374         if (!keyPathKey) {
375             if (!usesKeyGenerator)
376                 return Exception { DataError, "Failed to store record in an IDBObjectStore: Evaluating the object store's key path did not yield a value."_s };
377             if (!canInjectIDBKeyIntoScriptValue(state, value, m_info.keyPath().value()))
378                 return Exception { DataError };
379         }
380
381         if (keyPathKey) {
382             ASSERT(!key);
383             key = keyPathKey;
384         }
385     } else if (!usesKeyGenerator && !key)
386         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 };
387
388     return m_transaction.requestPutOrAdd(state, *this, WTFMove(key), *serializedValue, overwriteMode);
389 }
390
391 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::deleteFunction(ExecState& execState, IDBKeyRange* keyRange)
392 {
393     return doDelete(execState, [keyRange]() {
394         return makeRefPtr(keyRange);
395     });
396 }
397
398 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::doDelete(ExecState& execState, WTF::Function<ExceptionOr<RefPtr<IDBKeyRange>>()>&& function)
399 {
400     LOG(IndexedDB, "IDBObjectStore::deleteFunction");
401     ASSERT(&m_transaction.database().originThread() == &Thread::current());
402
403     // The IDB spec for several IDBObjectStore methods states that transaction related exceptions should fire before
404     // the exception for an object store being deleted.
405     // However, a handful of W3C IDB tests expect the deleted exception even though the transaction inactive exception also applies.
406     // Additionally, Chrome and Edge agree with the test, as does Legacy IDB in WebKit.
407     // Until this is sorted out, we'll agree with the test and the majority share browsers.
408     if (m_deleted)
409         return Exception { InvalidStateError, "Failed to execute 'delete' on 'IDBObjectStore': The object store has been deleted."_s };
410
411     if (!m_transaction.isActive())
412         return Exception { TransactionInactiveError, "Failed to execute 'delete' on 'IDBObjectStore': The transaction is inactive or finished."_s };
413
414     if (m_transaction.isReadOnly())
415         return Exception { ReadonlyError, "Failed to execute 'delete' on 'IDBObjectStore': The transaction is read-only."_s };
416
417     auto keyRange = function();
418     if (keyRange.hasException())
419         return keyRange.releaseException();
420
421     IDBKeyRangeData keyRangeData = keyRange.returnValue() ? keyRange.releaseReturnValue().get() : nullptr;
422     if (!keyRangeData.isValid())
423         return Exception { DataError, "Failed to execute 'delete' on 'IDBObjectStore': The parameter is not a valid key range."_s };
424
425     return m_transaction.requestDeleteRecord(execState, *this, keyRangeData);
426 }
427
428 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::deleteFunction(ExecState& execState, JSValue key)
429 {
430     return doDelete(execState, [state=&execState, key]() {
431         auto idbKey = scriptValueToIDBKey(*state, key);
432         if (!idbKey->isValid())
433             return ExceptionOr<RefPtr<IDBKeyRange>>{ Exception(DataError, "Failed to execute 'delete' on 'IDBObjectStore': The parameter is not a valid key."_s) };
434         return ExceptionOr<RefPtr<IDBKeyRange>> { (IDBKeyRange::create(WTFMove(idbKey))).ptr() };
435     });
436 }
437
438 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::clear(ExecState& execState)
439 {
440     LOG(IndexedDB, "IDBObjectStore::clear");
441     ASSERT(&m_transaction.database().originThread() == &Thread::current());
442
443     // The IDB spec for several IDBObjectStore methods states that transaction related exceptions should fire before
444     // the exception for an object store being deleted.
445     // However, a handful of W3C IDB tests expect the deleted exception even though the transaction inactive exception also applies.
446     // Additionally, Chrome and Edge agree with the test, as does Legacy IDB in WebKit.
447     // Until this is sorted out, we'll agree with the test and the majority share browsers.
448     if (m_deleted)
449         return Exception { InvalidStateError, "Failed to execute 'clear' on 'IDBObjectStore': The object store has been deleted."_s };
450
451     if (!m_transaction.isActive())
452         return Exception { TransactionInactiveError, "Failed to execute 'clear' on 'IDBObjectStore': The transaction is inactive or finished."_s };
453
454     if (m_transaction.isReadOnly())
455         return Exception { ReadonlyError, "Failed to execute 'clear' on 'IDBObjectStore': The transaction is read-only."_s };
456
457     return m_transaction.requestClearObjectStore(execState, *this);
458 }
459
460 ExceptionOr<Ref<IDBIndex>> IDBObjectStore::createIndex(ExecState&, const String& name, IDBKeyPath&& keyPath, const IndexParameters& parameters)
461 {
462     LOG(IndexedDB, "IDBObjectStore::createIndex %s (keyPath: %s, unique: %i, multiEntry: %i)", name.utf8().data(), loggingString(keyPath).utf8().data(), parameters.unique, parameters.multiEntry);
463     ASSERT(&m_transaction.database().originThread() == &Thread::current());
464
465     if (!m_transaction.isVersionChange())
466         return Exception { InvalidStateError, "Failed to execute 'createIndex' on 'IDBObjectStore': The database is not running a version change transaction."_s };
467
468     if (m_deleted)
469         return Exception { InvalidStateError, "Failed to execute 'createIndex' on 'IDBObjectStore': The object store has been deleted."_s };
470
471     if (!m_transaction.isActive())
472         return Exception { TransactionInactiveError, "Failed to execute 'createIndex' on 'IDBObjectStore': The transaction is inactive."_s};
473
474     if (m_info.hasIndex(name))
475         return Exception { ConstraintError, "Failed to execute 'createIndex' on 'IDBObjectStore': An index with the specified name already exists."_s };
476
477     if (!isIDBKeyPathValid(keyPath))
478         return Exception { SyntaxError, "Failed to execute 'createIndex' on 'IDBObjectStore': The keyPath argument contains an invalid key path."_s };
479
480     if (name.isNull())
481         return Exception { TypeError };
482
483     if (parameters.multiEntry && WTF::holds_alternative<Vector<String>>(keyPath))
484         return Exception { InvalidAccessError, "Failed to execute 'createIndex' on 'IDBObjectStore': The keyPath argument was an array and the multiEntry option is true."_s };
485
486     // Install the new Index into the ObjectStore's info.
487     IDBIndexInfo info = m_info.createNewIndex(name, WTFMove(keyPath), parameters.unique, parameters.multiEntry);
488     m_transaction.database().didCreateIndexInfo(info);
489
490     // Create the actual IDBObjectStore from the transaction, which also schedules the operation server side.
491     auto index = m_transaction.createIndex(*this, info);
492
493     Ref<IDBIndex> referencedIndex { *index };
494
495     Locker<Lock> locker(m_referencedIndexLock);
496     m_referencedIndexes.set(name, WTFMove(index));
497
498     return WTFMove(referencedIndex);
499 }
500
501 ExceptionOr<Ref<IDBIndex>> IDBObjectStore::index(const String& indexName)
502 {
503     LOG(IndexedDB, "IDBObjectStore::index");
504     ASSERT(&m_transaction.database().originThread() == &Thread::current());
505
506     if (!scriptExecutionContext())
507         return Exception { InvalidStateError }; // FIXME: Is this code tested? Is iteven reachable?
508
509     if (m_deleted)
510         return Exception { InvalidStateError, "Failed to execute 'index' on 'IDBObjectStore': The object store has been deleted."_s };
511
512     if (m_transaction.isFinishedOrFinishing())
513         return Exception { InvalidStateError, "Failed to execute 'index' on 'IDBObjectStore': The transaction is finished."_s };
514
515     Locker<Lock> locker(m_referencedIndexLock);
516     auto iterator = m_referencedIndexes.find(indexName);
517     if (iterator != m_referencedIndexes.end())
518         return Ref<IDBIndex> { *iterator->value };
519
520     auto* info = m_info.infoForExistingIndex(indexName);
521     if (!info)
522         return Exception { NotFoundError, "Failed to execute 'index' on 'IDBObjectStore': The specified index was not found."_s };
523
524     auto index = std::make_unique<IDBIndex>(*scriptExecutionContext(), *info, *this);
525
526     Ref<IDBIndex> referencedIndex { *index };
527
528     m_referencedIndexes.set(indexName, WTFMove(index));
529
530     return WTFMove(referencedIndex);
531 }
532
533 ExceptionOr<void> IDBObjectStore::deleteIndex(const String& name)
534 {
535     LOG(IndexedDB, "IDBObjectStore::deleteIndex %s", name.utf8().data());
536     ASSERT(&m_transaction.database().originThread() == &Thread::current());
537
538     if (m_deleted)
539         return Exception { InvalidStateError, "Failed to execute 'deleteIndex' on 'IDBObjectStore': The object store has been deleted."_s };
540
541     if (!m_transaction.isVersionChange())
542         return Exception { InvalidStateError, "Failed to execute 'deleteIndex' on 'IDBObjectStore': The database is not running a version change transaction."_s };
543
544     if (!m_transaction.isActive())
545         return Exception { TransactionInactiveError,  "Failed to execute 'deleteIndex' on 'IDBObjectStore': The transaction is inactive or finished."_s };
546
547     if (!m_info.hasIndex(name))
548         return Exception { NotFoundError, "Failed to execute 'deleteIndex' on 'IDBObjectStore': The specified index was not found."_s };
549
550     auto* info = m_info.infoForExistingIndex(name);
551     ASSERT(info);
552     m_transaction.database().didDeleteIndexInfo(*info);
553
554     m_info.deleteIndex(name);
555
556     {
557         Locker<Lock> locker(m_referencedIndexLock);
558         if (auto index = m_referencedIndexes.take(name)) {
559             index->markAsDeleted();
560             auto identifier = index->info().identifier();
561             m_deletedIndexes.add(identifier, WTFMove(index));
562         }
563     }
564
565     m_transaction.deleteIndex(m_info.identifier(), name);
566
567     return { };
568 }
569
570 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::count(ExecState& execState, JSValue key)
571 {
572     LOG(IndexedDB, "IDBObjectStore::count");
573
574     auto idbKey = scriptValueToIDBKey(execState, key);
575
576     return doCount(execState, IDBKeyRangeData(idbKey->isValid() ? idbKey.ptr() : nullptr));
577 }
578
579 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::count(ExecState& execState, IDBKeyRange* range)
580 {
581     LOG(IndexedDB, "IDBObjectStore::count");
582
583     return doCount(execState, range ? IDBKeyRangeData(range) : IDBKeyRangeData::allKeys());
584 }
585
586 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::doCount(ExecState& execState, const IDBKeyRangeData& range)
587 {
588     ASSERT(&m_transaction.database().originThread() == &Thread::current());
589
590     // The IDB spec for several IDBObjectStore methods states that transaction related exceptions should fire before
591     // the exception for an object store being deleted.
592     // However, a handful of W3C IDB tests expect the deleted exception even though the transaction inactive exception also applies.
593     // Additionally, Chrome and Edge agree with the test, as does Legacy IDB in WebKit.
594     // Until this is sorted out, we'll agree with the test and the majority share browsers.
595     if (m_deleted)
596         return Exception { InvalidStateError, "Failed to execute 'count' on 'IDBObjectStore': The object store has been deleted."_s };
597
598     if (!m_transaction.isActive())
599         return Exception { TransactionInactiveError, "Failed to execute 'count' on 'IDBObjectStore': The transaction is inactive or finished."_s };
600
601     if (!range.isValid())
602         return Exception { DataError, "Failed to execute 'count' on 'IDBObjectStore': The parameter is not a valid key."_s };
603
604     return m_transaction.requestCount(execState, *this, range);
605 }
606
607 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::doGetAll(ExecState& execState, Optional<uint32_t> count, WTF::Function<ExceptionOr<RefPtr<IDBKeyRange>>()>&& function)
608 {
609     LOG(IndexedDB, "IDBObjectStore::getAll");
610     ASSERT(&m_transaction.database().originThread() == &Thread::current());
611
612     if (m_deleted)
613         return Exception { InvalidStateError, "Failed to execute 'getAll' on 'IDBObjectStore': The object store has been deleted."_s };
614
615     if (!m_transaction.isActive())
616         return Exception { TransactionInactiveError, "Failed to execute 'getAll' on 'IDBObjectStore': The transaction is inactive or finished."_s };
617
618     auto keyRange = function();
619     if (keyRange.hasException())
620         return keyRange.releaseException();
621
622     auto* keyRangePointer = keyRange.returnValue() ? keyRange.releaseReturnValue().get() : nullptr;
623     return m_transaction.requestGetAllObjectStoreRecords(execState, *this, keyRangePointer, IndexedDB::GetAllType::Values, count);
624 }
625
626 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::getAll(ExecState& execState, RefPtr<IDBKeyRange>&& range, Optional<uint32_t> count)
627 {
628     return doGetAll(execState, count, [range = WTFMove(range)]() {
629         return range;
630     });
631 }
632
633 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::getAll(ExecState& execState, JSValue key, Optional<uint32_t> count)
634 {
635     return doGetAll(execState, count, [state=&execState, key]() {
636         auto onlyResult = IDBKeyRange::only(*state, key);
637         if (onlyResult.hasException())
638             return ExceptionOr<RefPtr<IDBKeyRange>>{ Exception(DataError, "Failed to execute 'getAll' on 'IDBObjectStore': The parameter is not a valid key."_s) };
639
640         return ExceptionOr<RefPtr<IDBKeyRange>> { onlyResult.releaseReturnValue() };
641     });
642 }
643
644 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::doGetAllKeys(ExecState& execState, Optional<uint32_t> count, WTF::Function<ExceptionOr<RefPtr<IDBKeyRange>>()>&& function)
645 {
646     LOG(IndexedDB, "IDBObjectStore::getAllKeys");
647     ASSERT(&m_transaction.database().originThread() == &Thread::current());
648
649     if (m_deleted)
650         return Exception { InvalidStateError, "Failed to execute 'getAllKeys' on 'IDBObjectStore': The object store has been deleted."_s };
651
652     if (!m_transaction.isActive())
653         return Exception { TransactionInactiveError, "Failed to execute 'getAllKeys' on 'IDBObjectStore': The transaction is inactive or finished."_s };
654
655     auto keyRange = function();
656     if (keyRange.hasException())
657         return keyRange.releaseException();
658
659     auto* keyRangePointer = keyRange.returnValue() ? keyRange.releaseReturnValue().get() : nullptr;
660     return m_transaction.requestGetAllObjectStoreRecords(execState, *this, keyRangePointer, IndexedDB::GetAllType::Keys, count);
661 }
662
663 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::getAllKeys(ExecState& execState, RefPtr<IDBKeyRange>&& range, Optional<uint32_t> count)
664 {
665     return doGetAllKeys(execState, count, [range = WTFMove(range)]() {
666         return range;
667     });
668 }
669
670 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::getAllKeys(ExecState& execState, JSValue key, Optional<uint32_t> count)
671 {
672     return doGetAllKeys(execState, count, [state=&execState, key]() {
673         auto onlyResult = IDBKeyRange::only(*state, key);
674         if (onlyResult.hasException())
675             return ExceptionOr<RefPtr<IDBKeyRange>>{ Exception(DataError, "Failed to execute 'getAllKeys' on 'IDBObjectStore': The parameter is not a valid key."_s) };
676
677         return ExceptionOr<RefPtr<IDBKeyRange>> { onlyResult.releaseReturnValue() };
678     });
679 }
680
681 void IDBObjectStore::markAsDeleted()
682 {
683     ASSERT(&m_transaction.database().originThread() == &Thread::current());
684     m_deleted = true;
685 }
686
687 void IDBObjectStore::rollbackForVersionChangeAbort()
688 {
689     ASSERT(&m_transaction.database().originThread() == &Thread::current());
690
691     String currentName = m_info.name();
692     m_info = m_originalInfo;
693
694     auto& databaseInfo = transaction().database().info();
695     auto* objectStoreInfo = databaseInfo.infoForExistingObjectStore(m_info.identifier());
696     if (!objectStoreInfo) {
697         m_info.rename(currentName);
698         m_deleted = true;
699     } else {
700         m_deleted = false;
701         
702         HashSet<uint64_t> indexesToRemove;
703         for (auto indexIdentifier : objectStoreInfo->indexMap().keys()) {
704             if (!objectStoreInfo->hasIndex(indexIdentifier))
705                 indexesToRemove.add(indexIdentifier);
706         }
707
708         for (auto indexIdentifier : indexesToRemove)
709             m_info.deleteIndex(indexIdentifier);
710     }
711
712     Locker<Lock> locker(m_referencedIndexLock);
713
714     Vector<uint64_t> identifiersToRemove;
715     for (auto& iterator : m_deletedIndexes) {
716         if (m_info.hasIndex(iterator.key)) {
717             auto name = iterator.value->info().name();
718             m_referencedIndexes.set(name, WTFMove(iterator.value));
719             identifiersToRemove.append(iterator.key);
720         }
721     }
722
723     for (auto identifier : identifiersToRemove)
724         m_deletedIndexes.remove(identifier);
725
726     for (auto& index : m_referencedIndexes.values())
727         index->rollbackInfoForVersionChangeAbort();
728 }
729
730 void IDBObjectStore::visitReferencedIndexes(SlotVisitor& visitor) const
731 {
732     Locker<Lock> locker(m_referencedIndexLock);
733     for (auto& index : m_referencedIndexes.values())
734         visitor.addOpaqueRoot(index.get());
735     for (auto& index : m_deletedIndexes.values())
736         visitor.addOpaqueRoot(index.get());
737 }
738
739 void IDBObjectStore::renameReferencedIndex(IDBIndex& index, const String& newName)
740 {
741     LOG(IndexedDB, "IDBObjectStore::renameReferencedIndex");
742
743     auto* indexInfo = m_info.infoForExistingIndex(index.info().identifier());
744     ASSERT(indexInfo);
745     indexInfo->rename(newName);
746
747     ASSERT(m_referencedIndexes.contains(index.info().name()));
748     ASSERT(!m_referencedIndexes.contains(newName));
749     ASSERT(m_referencedIndexes.get(index.info().name()) == &index);
750
751     m_referencedIndexes.set(newName, m_referencedIndexes.take(index.info().name()));
752 }
753
754 void IDBObjectStore::ref()
755 {
756     m_transaction.ref();
757 }
758
759 void IDBObjectStore::deref()
760 {
761     m_transaction.deref();
762 }
763
764 } // namespace WebCore
765
766 #endif // ENABLE(INDEXED_DATABASE)