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