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