Refactor data passed along for a "GetRecord" request.
[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 "IDBDatabaseException.h"
37 #include "IDBError.h"
38 #include "IDBGetRecordData.h"
39 #include "IDBIndex.h"
40 #include "IDBKey.h"
41 #include "IDBKeyRangeData.h"
42 #include "IDBRequest.h"
43 #include "IDBTransaction.h"
44 #include "IndexedDB.h"
45 #include "Logging.h"
46 #include "Page.h"
47 #include "ScriptExecutionContext.h"
48 #include "ScriptState.h"
49 #include "SerializedScriptValue.h"
50 #include <wtf/Locker.h>
51
52 using namespace JSC;
53
54 namespace WebCore {
55
56 Ref<IDBObjectStore> IDBObjectStore::create(ScriptExecutionContext& context, const IDBObjectStoreInfo& info, IDBTransaction& transaction)
57 {
58     return adoptRef(*new IDBObjectStore(context, info, transaction));
59 }
60
61 IDBObjectStore::IDBObjectStore(ScriptExecutionContext& context, const IDBObjectStoreInfo& info, IDBTransaction& transaction)
62     : ActiveDOMObject(&context)
63     , m_info(info)
64     , m_originalInfo(info)
65     , m_transaction(transaction)
66 {
67     ASSERT(currentThread() == m_transaction->database().originThreadID());
68
69     suspendIfNeeded();
70 }
71
72 IDBObjectStore::~IDBObjectStore()
73 {
74     ASSERT(currentThread() == m_transaction->database().originThreadID());
75 }
76
77 const char* IDBObjectStore::activeDOMObjectName() const
78 {
79     return "IDBObjectStore";
80 }
81
82 bool IDBObjectStore::canSuspendForDocumentSuspension() const
83 {
84     return false;
85 }
86
87 bool IDBObjectStore::hasPendingActivity() const
88 {
89     return !m_transaction->isFinished();
90 }
91
92 const String& IDBObjectStore::name() const
93 {
94     ASSERT(currentThread() == m_transaction->database().originThreadID());
95     return m_info.name();
96 }
97
98 const IDBKeyPath& IDBObjectStore::keyPath() const
99 {
100     ASSERT(currentThread() == m_transaction->database().originThreadID());
101     return m_info.keyPath();
102 }
103
104 RefPtr<DOMStringList> IDBObjectStore::indexNames() const
105 {
106     ASSERT(currentThread() == m_transaction->database().originThreadID());
107
108     RefPtr<DOMStringList> indexNames = DOMStringList::create();
109     for (auto& name : m_info.indexNames())
110         indexNames->append(name);
111     indexNames->sort();
112
113     return indexNames;
114 }
115
116 IDBTransaction& IDBObjectStore::transaction()
117 {
118     ASSERT(currentThread() == m_transaction->database().originThreadID());
119     return m_transaction.get();
120 }
121
122 bool IDBObjectStore::autoIncrement() const
123 {
124     ASSERT(currentThread() == m_transaction->database().originThreadID());
125     return m_info.autoIncrement();
126 }
127
128 RefPtr<IDBRequest> IDBObjectStore::openCursor(ExecState& execState, IDBKeyRange* range, const String& directionString, ExceptionCodeWithMessage& ec)
129 {
130     LOG(IndexedDB, "IDBObjectStore::openCursor");
131     ASSERT(currentThread() == m_transaction->database().originThreadID());
132
133     if (m_deleted) {
134         ec.code = IDBDatabaseException::InvalidStateError;
135         ec.message = ASCIILiteral("Failed to execute 'openCursor' on 'IDBObjectStore': The object store has been deleted.");
136         return nullptr;
137     }
138
139     if (!m_transaction->isActive()) {
140         ec.code = IDBDatabaseException::TransactionInactiveError;
141         ec.message = ASCIILiteral("Failed to execute 'openCursor' on 'IDBObjectStore': The transaction is inactive or finished.");
142         return nullptr;
143     }
144
145     IndexedDB::CursorDirection direction = IDBCursor::stringToDirection(directionString, ec.code);
146     if (ec.code)
147         return nullptr;
148
149     auto info = IDBCursorInfo::objectStoreCursor(m_transaction.get(), m_info.identifier(), range, direction);
150     return m_transaction->requestOpenCursor(execState, *this, info);
151 }
152
153 RefPtr<IDBRequest> IDBObjectStore::openCursor(ExecState& execState, JSValue key, const String& direction, ExceptionCodeWithMessage& ec)
154 {
155     RefPtr<IDBKeyRange> keyRange = IDBKeyRange::only(execState, key, ec.code);
156     if (ec.code) {
157         ec.message = ASCIILiteral("Failed to execute 'openCursor' on 'IDBObjectStore': The parameter is not a valid key.");
158         return 0;
159     }
160
161     return openCursor(execState, keyRange.get(), direction, ec);
162 }
163
164 RefPtr<IDBRequest> IDBObjectStore::get(ExecState& execState, JSValue key, ExceptionCodeWithMessage& ec)
165 {
166     LOG(IndexedDB, "IDBObjectStore::get");
167     ASSERT(currentThread() == m_transaction->database().originThreadID());
168
169     if (!m_transaction->isActive()) {
170         ec.code = IDBDatabaseException::TransactionInactiveError;
171         ec.message = ASCIILiteral("Failed to execute 'get' on 'IDBObjectStore': The transaction is inactive or finished.");
172         return nullptr;
173     }
174
175     if (m_deleted) {
176         ec.code = IDBDatabaseException::InvalidStateError;
177         ec.message = ASCIILiteral("Failed to execute 'get' on 'IDBObjectStore': The object store has been deleted.");
178         return nullptr;
179     }
180
181     Ref<IDBKey> idbKey = scriptValueToIDBKey(execState, key);
182     if (!idbKey->isValid()) {
183         ec.code = IDBDatabaseException::DataError;
184         ec.message = ASCIILiteral("Failed to execute 'get' on 'IDBObjectStore': The parameter is not a valid key.");
185         return nullptr;
186     }
187
188     return m_transaction->requestGetRecord(execState, *this, { idbKey.ptr() });
189 }
190
191 RefPtr<IDBRequest> IDBObjectStore::get(ExecState& execState, IDBKeyRange* keyRange, ExceptionCodeWithMessage& ec)
192 {
193     LOG(IndexedDB, "IDBObjectStore::get");
194     ASSERT(currentThread() == m_transaction->database().originThreadID());
195
196     if (!m_transaction->isActive()) {
197         ec.code = IDBDatabaseException::TransactionInactiveError;
198         return nullptr;
199     }
200
201     if (m_deleted) {
202         ec.code = IDBDatabaseException::InvalidStateError;
203         ec.message = ASCIILiteral("Failed to execute 'get' on 'IDBObjectStore': The object store has been deleted.");
204         return nullptr;
205     }
206
207     IDBKeyRangeData keyRangeData(keyRange);
208     if (!keyRangeData.isValid()) {
209         ec.code = IDBDatabaseException::DataError;
210         return nullptr;
211     }
212
213     return m_transaction->requestGetRecord(execState, *this, { keyRangeData });
214 }
215
216 RefPtr<IDBRequest> IDBObjectStore::add(ExecState& execState, JSValue value, JSValue key, ExceptionCodeWithMessage& ec)
217 {
218     RefPtr<IDBKey> idbKey;
219     if (!key.isUndefined())
220         idbKey = scriptValueToIDBKey(execState, key);
221     return putOrAdd(execState, value, idbKey, IndexedDB::ObjectStoreOverwriteMode::NoOverwrite, InlineKeyCheck::Perform, ec);
222 }
223
224 RefPtr<IDBRequest> IDBObjectStore::put(ExecState& execState, JSValue value, JSValue key, ExceptionCodeWithMessage& ec)
225 {
226     RefPtr<IDBKey> idbKey;
227     if (!key.isUndefined())
228         idbKey = scriptValueToIDBKey(execState, key);
229     return putOrAdd(execState, value, idbKey, IndexedDB::ObjectStoreOverwriteMode::Overwrite, InlineKeyCheck::Perform, ec);
230 }
231
232 RefPtr<IDBRequest> IDBObjectStore::putForCursorUpdate(ExecState& state, JSValue value, JSValue key, ExceptionCodeWithMessage& ec)
233 {
234     return putOrAdd(state, value, scriptValueToIDBKey(state, key), IndexedDB::ObjectStoreOverwriteMode::OverwriteForCursor, InlineKeyCheck::DoNotPerform, ec);
235 }
236
237 RefPtr<IDBRequest> IDBObjectStore::putOrAdd(ExecState& state, JSValue value, RefPtr<IDBKey> key, IndexedDB::ObjectStoreOverwriteMode overwriteMode, InlineKeyCheck inlineKeyCheck, ExceptionCodeWithMessage& ec)
238 {
239     LOG(IndexedDB, "IDBObjectStore::putOrAdd");
240     ASSERT(currentThread() == m_transaction->database().originThreadID());
241
242     auto context = scriptExecutionContextFromExecState(&state);
243     if (!context) {
244         ec.code = IDBDatabaseException::UnknownError;
245         ec.message = ASCIILiteral("Unable to store record in object store because it does not have a valid script execution context");
246         return nullptr;
247     }
248
249     // The IDB spec for several IDBObjectStore methods states that transaction related exceptions should fire before
250     // the exception for an object store being deleted.
251     // However, a handful of W3C IDB tests expect the deleted exception even though the transaction inactive exception also applies.
252     // Additionally, Chrome and Edge agree with the test, as does Legacy IDB in WebKit.
253     // Until this is sorted out, we'll agree with the test and the majority share browsers.
254     if (m_deleted) {
255         ec.code = IDBDatabaseException::InvalidStateError;
256         ec.message = ASCIILiteral("Failed to store record in an IDBObjectStore: The object store has been deleted.");
257         return nullptr;
258     }
259
260     if (!m_transaction->isActive()) {
261         ec.code = IDBDatabaseException::TransactionInactiveError;
262         ec.message = ASCIILiteral("Failed to store record in an IDBObjectStore: The transaction is inactive or finished.");
263         return nullptr;
264     }
265
266     if (m_transaction->isReadOnly()) {
267         ec.code = IDBDatabaseException::ReadOnlyError;
268         ec.message = ASCIILiteral("Failed to store record in an IDBObjectStore: The transaction is read-only.");
269         return nullptr;
270     }
271
272     RefPtr<SerializedScriptValue> serializedValue = SerializedScriptValue::create(&state, value, nullptr, nullptr);
273     if (state.hadException()) {
274         // Clear the DOM exception from the serializer so we can give a more targeted exception.
275         state.clearException();
276
277         ec.code = IDBDatabaseException::DataCloneError;
278         ec.message = ASCIILiteral("Failed to store record in an IDBObjectStore: An object could not be cloned.");
279         return nullptr;
280     }
281
282     bool privateBrowsingEnabled = false;
283     if (context->isDocument()) {
284         if (auto* page = static_cast<Document*>(context)->page())
285             privateBrowsingEnabled = page->sessionID().isEphemeral();
286     }
287
288     if (serializedValue->hasBlobURLs() && privateBrowsingEnabled) {
289         // https://bugs.webkit.org/show_bug.cgi?id=156347 - Support Blobs in private browsing.
290         ec.code = IDBDatabaseException::DataCloneError;
291         ec.message = ASCIILiteral("Failed to store record in an IDBObjectStore: BlobURLs are not yet supported.");
292         return nullptr;
293     }
294
295     if (key && !key->isValid()) {
296         ec.code = IDBDatabaseException::DataError;
297         ec.message = ASCIILiteral("Failed to store record in an IDBObjectStore: The parameter is not a valid key.");
298         return nullptr;
299     }
300
301     bool usesInlineKeys = !m_info.keyPath().isNull();
302     bool usesKeyGenerator = autoIncrement();
303     if (usesInlineKeys && inlineKeyCheck == InlineKeyCheck::Perform) {
304         if (key) {
305             ec.code = IDBDatabaseException::DataError;
306             ec.message = ASCIILiteral("Failed to store record in an IDBObjectStore: The object store uses in-line keys and the key parameter was provided.");
307             return nullptr;
308         }
309
310         RefPtr<IDBKey> keyPathKey = maybeCreateIDBKeyFromScriptValueAndKeyPath(state, value, m_info.keyPath());
311         if (keyPathKey && !keyPathKey->isValid()) {
312             ec.code = IDBDatabaseException::DataError;
313             ec.message = ASCIILiteral("Failed to store record in an IDBObjectStore: Evaluating the object store's key path yielded a value that is not a valid key.");
314             return nullptr;
315         }
316
317         if (!keyPathKey) {
318             if (usesKeyGenerator) {
319                 if (!canInjectIDBKeyIntoScriptValue(state, value, m_info.keyPath())) {
320                     ec.code = IDBDatabaseException::DataError;
321                     return nullptr;
322                 }
323             } else {
324                 ec.code = IDBDatabaseException::DataError;
325                 ec.message = ASCIILiteral("Failed to store record in an IDBObjectStore: Evaluating the object store's key path did not yield a value.");
326                 return nullptr;
327             }
328         }
329
330         if (keyPathKey) {
331             ASSERT(!key);
332             key = keyPathKey;
333         }
334     } else if (!usesKeyGenerator && !key) {
335         ec.code = IDBDatabaseException::DataError;
336         ec.message = ASCIILiteral("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.");
337         return nullptr;
338     }
339
340     return m_transaction->requestPutOrAdd(state, *this, key.get(), *serializedValue, overwriteMode);
341 }
342
343 RefPtr<IDBRequest> IDBObjectStore::deleteFunction(ExecState& execState, IDBKeyRange* keyRange, ExceptionCodeWithMessage& ec)
344 {
345     return doDelete(execState, keyRange, ec);
346 }
347
348 RefPtr<IDBRequest> IDBObjectStore::doDelete(ExecState& execState, IDBKeyRange* keyRange, ExceptionCodeWithMessage& ec)
349 {
350     LOG(IndexedDB, "IDBObjectStore::deleteFunction");
351     ASSERT(currentThread() == m_transaction->database().originThreadID());
352
353     // The IDB spec for several IDBObjectStore methods states that transaction related exceptions should fire before
354     // the exception for an object store being deleted.
355     // However, a handful of W3C IDB tests expect the deleted exception even though the transaction inactive exception also applies.
356     // Additionally, Chrome and Edge agree with the test, as does Legacy IDB in WebKit.
357     // Until this is sorted out, we'll agree with the test and the majority share browsers.
358     if (m_deleted) {
359         ec.code = IDBDatabaseException::InvalidStateError;
360         ec.message = ASCIILiteral("Failed to execute 'delete' on 'IDBObjectStore': The object store has been deleted.");
361         return nullptr;
362     }
363
364     if (!m_transaction->isActive()) {
365         ec.code = IDBDatabaseException::TransactionInactiveError;
366         ec.message = ASCIILiteral("Failed to execute 'delete' on 'IDBObjectStore': The transaction is inactive or finished.");
367         return nullptr;
368     }
369
370     if (m_transaction->isReadOnly()) {
371         ec.code = IDBDatabaseException::ReadOnlyError;
372         ec.message = ASCIILiteral("Failed to execute 'delete' on 'IDBObjectStore': The transaction is read-only.");
373         return nullptr;
374     }
375
376     IDBKeyRangeData keyRangeData(keyRange);
377     if (!keyRangeData.isValid()) {
378         ec.code = IDBDatabaseException::DataError;
379         ec.message = ASCIILiteral("Failed to execute 'delete' on 'IDBObjectStore': The parameter is not a valid key range.");
380         return nullptr;
381     }
382
383     return m_transaction->requestDeleteRecord(execState, *this, keyRangeData);
384 }
385
386 RefPtr<IDBRequest> IDBObjectStore::deleteFunction(ExecState& execState, JSValue key, ExceptionCodeWithMessage& ec)
387 {
388     Ref<IDBKey> idbKey = scriptValueToIDBKey(execState, key);
389     if (!idbKey->isValid()) {
390         ec.code = IDBDatabaseException::DataError;
391         ec.message = ASCIILiteral("Failed to execute 'delete' on 'IDBObjectStore': The parameter is not a valid key.");
392         return nullptr;
393     }
394
395     return doDelete(execState, &IDBKeyRange::create(WTFMove(idbKey)).get(), ec);
396 }
397
398 RefPtr<IDBRequest> IDBObjectStore::clear(ExecState& execState, ExceptionCodeWithMessage& ec)
399 {
400     LOG(IndexedDB, "IDBObjectStore::clear");
401     ASSERT(currentThread() == m_transaction->database().originThreadID());
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         ec.code = IDBDatabaseException::InvalidStateError;
410         ec.message = ASCIILiteral("Failed to execute 'clear' on 'IDBObjectStore': The object store has been deleted.");
411         return nullptr;
412     }
413
414     if (!m_transaction->isActive()) {
415         ec.code = IDBDatabaseException::TransactionInactiveError;
416         ec.message = ASCIILiteral("Failed to execute 'clear' on 'IDBObjectStore': The transaction is inactive or finished.");
417         return nullptr;
418     }
419
420     if (m_transaction->isReadOnly()) {
421         ec.code = IDBDatabaseException::ReadOnlyError;
422         ec.message = ASCIILiteral("Failed to execute 'clear' on 'IDBObjectStore': The transaction is read-only.");
423         return nullptr;
424     }
425
426     Ref<IDBRequest> request = m_transaction->requestClearObjectStore(execState, *this);
427     return adoptRef(request.leakRef());
428 }
429
430 RefPtr<IDBIndex> IDBObjectStore::createIndex(ExecState&, const String& name, const IDBKeyPath& keyPath, const IndexParameters& parameters, ExceptionCodeWithMessage& ec)
431 {
432     LOG(IndexedDB, "IDBObjectStore::createIndex %s", name.utf8().data());
433     ASSERT(currentThread() == m_transaction->database().originThreadID());
434
435     if (m_deleted) {
436         ec.code = IDBDatabaseException::InvalidStateError;
437         ec.message = ASCIILiteral("Failed to execute 'createIndex' on 'IDBObjectStore': The object store has been deleted.");
438         return nullptr;
439     }
440
441     if (!m_transaction->isVersionChange()) {
442         ec.code = IDBDatabaseException::InvalidStateError;
443         ec.message = ASCIILiteral("Failed to execute 'createIndex' on 'IDBObjectStore': The database is not running a version change transaction.");
444         return nullptr;
445     }
446
447     if (!m_transaction->isActive()) {
448         ec.code = IDBDatabaseException::TransactionInactiveError;
449         return nullptr;
450     }
451
452     if (!keyPath.isValid()) {
453         ec.code = IDBDatabaseException::SyntaxError;
454         ec.message = ASCIILiteral("Failed to execute 'createIndex' on 'IDBObjectStore': The keyPath argument contains an invalid key path.");
455         return nullptr;
456     }
457
458     if (name.isNull()) {
459         ec.code = TypeError;
460         return nullptr;
461     }
462
463     if (m_info.hasIndex(name)) {
464         ec.code = IDBDatabaseException::ConstraintError;
465         ec.message = ASCIILiteral("Failed to execute 'createIndex' on 'IDBObjectStore': An index with the specified name already exists.");
466         return nullptr;
467     }
468
469     if (keyPath.type() == IDBKeyPath::Type::Array && parameters.multiEntry) {
470         ec.code = IDBDatabaseException::InvalidAccessError;
471         ec.message = ASCIILiteral("Failed to execute 'createIndex' on 'IDBObjectStore': The keyPath argument was an array and the multiEntry option is true.");
472         return nullptr;
473     }
474
475     // Install the new Index into the ObjectStore's info.
476     IDBIndexInfo info = m_info.createNewIndex(name, keyPath, parameters.unique, parameters.multiEntry);
477     m_transaction->database().didCreateIndexInfo(info);
478
479     // Create the actual IDBObjectStore from the transaction, which also schedules the operation server side.
480     auto index = m_transaction->createIndex(*this, info);
481     RefPtr<IDBIndex> refIndex = index.get();
482
483     Locker<Lock> locker(m_referencedIndexLock);
484     m_referencedIndexes.set(name, WTFMove(index));
485
486     return refIndex;
487 }
488
489 RefPtr<IDBIndex> IDBObjectStore::index(const String& indexName, ExceptionCodeWithMessage& ec)
490 {
491     LOG(IndexedDB, "IDBObjectStore::index");
492     ASSERT(currentThread() == m_transaction->database().originThreadID());
493
494     if (!scriptExecutionContext())
495         return nullptr;
496
497     if (m_deleted) {
498         ec.code = IDBDatabaseException::InvalidStateError;
499         ec.message = ASCIILiteral("Failed to execute 'index' on 'IDBObjectStore': The object store has been deleted.");
500         return nullptr;
501     }
502
503     if (m_transaction->isFinishedOrFinishing()) {
504         ec.code = IDBDatabaseException::InvalidStateError;
505         ec.message = ASCIILiteral("Failed to execute 'index' on 'IDBObjectStore': The transaction is finished.");
506         return nullptr;
507     }
508
509     Locker<Lock> locker(m_referencedIndexLock);
510     auto iterator = m_referencedIndexes.find(indexName);
511     if (iterator != m_referencedIndexes.end())
512         return iterator->value.get();
513
514     auto* info = m_info.infoForExistingIndex(indexName);
515     if (!info) {
516         ec.code = IDBDatabaseException::NotFoundError;
517         ec.message = ASCIILiteral("Failed to execute 'index' on 'IDBObjectStore': The specified index was not found.");
518         return nullptr;
519     }
520
521     auto index = std::make_unique<IDBIndex>(*scriptExecutionContext(), *info, *this);
522     RefPtr<IDBIndex> refIndex = index.get();
523     m_referencedIndexes.set(indexName, WTFMove(index));
524
525     return refIndex;
526 }
527
528 void IDBObjectStore::deleteIndex(const String& name, ExceptionCodeWithMessage& ec)
529 {
530     LOG(IndexedDB, "IDBObjectStore::deleteIndex %s", name.utf8().data());
531     ASSERT(currentThread() == m_transaction->database().originThreadID());
532
533     if (m_deleted) {
534         ec.code = IDBDatabaseException::InvalidStateError;
535         ec.message = ASCIILiteral("Failed to execute 'deleteIndex' on 'IDBObjectStore': The object store has been deleted.");
536         return;
537     }
538
539     if (!m_transaction->isVersionChange()) {
540         ec.code = IDBDatabaseException::InvalidStateError;
541         ec.message = ASCIILiteral("Failed to execute 'deleteIndex' on 'IDBObjectStore': The database is not running a version change transaction.");
542         return;
543     }
544
545     if (!m_transaction->isActive()) {
546         ec.code = IDBDatabaseException::TransactionInactiveError;
547         ec.message = ASCIILiteral("Failed to execute 'deleteIndex' on 'IDBObjectStore': The transaction is inactive or finished.");
548         return;
549     }
550
551     if (!m_info.hasIndex(name)) {
552         ec.code = IDBDatabaseException::NotFoundError;
553         ec.message = ASCIILiteral("Failed to execute 'deleteIndex' on 'IDBObjectStore': The specified index was not found.");
554         return;
555     }
556
557     auto* info = m_info.infoForExistingIndex(name);
558     ASSERT(info);
559     m_transaction->database().didDeleteIndexInfo(*info);
560
561     m_info.deleteIndex(name);
562
563     {
564         Locker<Lock> locker(m_referencedIndexLock);
565         if (auto index = m_referencedIndexes.take(name)) {
566             index->markAsDeleted();
567             m_deletedIndexes.add(WTFMove(index));
568         }
569
570     }
571
572     m_transaction->deleteIndex(m_info.identifier(), name);
573 }
574
575 RefPtr<IDBRequest> IDBObjectStore::count(ExecState& execState, JSValue key, ExceptionCodeWithMessage& ec)
576 {
577     LOG(IndexedDB, "IDBObjectStore::count");
578
579     Ref<IDBKey> idbKey = scriptValueToIDBKey(execState, key);
580     if (!idbKey->isValid()) {
581         ec.code = IDBDatabaseException::DataError;
582         ec.message = ASCIILiteral("Failed to execute 'count' on 'IDBObjectStore': The parameter is not a valid key.");
583         return nullptr;
584     }
585
586     return doCount(execState, IDBKeyRangeData(idbKey.ptr()), ec);
587 }
588
589 RefPtr<IDBRequest> IDBObjectStore::count(ExecState& execState, IDBKeyRange* range, ExceptionCodeWithMessage& ec)
590 {
591     LOG(IndexedDB, "IDBObjectStore::count");
592
593     return doCount(execState, range ? IDBKeyRangeData(range) : IDBKeyRangeData::allKeys(), ec);
594 }
595
596 RefPtr<IDBRequest> IDBObjectStore::doCount(ExecState& execState, const IDBKeyRangeData& range, ExceptionCodeWithMessage& ec)
597 {
598     ASSERT(currentThread() == m_transaction->database().originThreadID());
599
600     // The IDB spec for several IDBObjectStore methods states that transaction related exceptions should fire before
601     // the exception for an object store being deleted.
602     // However, a handful of W3C IDB tests expect the deleted exception even though the transaction inactive exception also applies.
603     // Additionally, Chrome and Edge agree with the test, as does Legacy IDB in WebKit.
604     // Until this is sorted out, we'll agree with the test and the majority share browsers.
605     if (m_deleted) {
606         ec.code = IDBDatabaseException::InvalidStateError;
607         ec.message = ASCIILiteral("Failed to execute 'count' on 'IDBObjectStore': The object store has been deleted.");
608         return nullptr;
609     }
610
611     if (!m_transaction->isActive()) {
612         ec.code = IDBDatabaseException::TransactionInactiveError;
613         ec.message = ASCIILiteral("Failed to execute 'count' on 'IDBObjectStore': The transaction is inactive or finished.");
614         return nullptr;
615     }
616
617     if (!range.isValid()) {
618         ec.code = IDBDatabaseException::DataError;
619         return nullptr;
620     }
621
622     return m_transaction->requestCount(execState, *this, range);
623 }
624
625 void IDBObjectStore::markAsDeleted()
626 {
627     ASSERT(currentThread() == m_transaction->database().originThreadID());
628     m_deleted = true;
629 }
630
631 void IDBObjectStore::rollbackInfoForVersionChangeAbort()
632 {
633     ASSERT(currentThread() == m_transaction->database().originThreadID());
634     m_info = m_originalInfo;
635 }
636
637 void IDBObjectStore::visitReferencedIndexes(SlotVisitor& visitor) const
638 {
639     Locker<Lock> locker(m_referencedIndexLock);
640     for (auto& index : m_referencedIndexes.values())
641         visitor.addOpaqueRoot(index.get());
642 }
643
644 } // namespace WebCore
645
646 #endif // ENABLE(INDEXED_DATABASE)