2 * Copyright (C) 2015 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "IDBObjectStore.h"
29 #if ENABLE(INDEXED_DATABASE)
31 #include "DOMStringList.h"
33 #include "IDBBindingUtilities.h"
34 #include "IDBCursor.h"
35 #include "IDBDatabase.h"
37 #include "IDBGetRecordData.h"
40 #include "IDBKeyRangeData.h"
41 #include "IDBRequest.h"
42 #include "IDBTransaction.h"
43 #include "IndexedDB.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>
57 IDBObjectStore::IDBObjectStore(ScriptExecutionContext& context, const IDBObjectStoreInfo& info, IDBTransaction& transaction)
58 : ActiveDOMObject(&context)
60 , m_originalInfo(info)
61 , m_transaction(transaction)
63 ASSERT(&m_transaction.database().originThread() == &Thread::current());
68 IDBObjectStore::~IDBObjectStore()
70 ASSERT(&m_transaction.database().originThread() == &Thread::current());
73 const char* IDBObjectStore::activeDOMObjectName() const
75 return "IDBObjectStore";
78 bool IDBObjectStore::canSuspendForDocumentSuspension() const
83 bool IDBObjectStore::hasPendingActivity() const
85 return m_transaction.hasPendingActivity();
88 const String& IDBObjectStore::name() const
90 ASSERT(&m_transaction.database().originThread() == &Thread::current());
94 ExceptionOr<void> IDBObjectStore::setName(const String& name)
96 ASSERT(&m_transaction.database().originThread() == &Thread::current());
99 return Exception { InvalidStateError, "Failed set property 'name' on 'IDBObjectStore': The object store has been deleted."_s };
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 };
104 if (!m_transaction.isActive())
105 return Exception { TransactionInactiveError, "Failed set property 'name' on 'IDBObjectStore': The object store's transaction is not active."_s };
107 if (m_info.name() == name)
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, "'.") };
113 m_transaction.database().renameObjectStore(*this, name);
119 const Optional<IDBKeyPath>& IDBObjectStore::keyPath() const
121 ASSERT(&m_transaction.database().originThread() == &Thread::current());
122 return m_info.keyPath();
125 Ref<DOMStringList> IDBObjectStore::indexNames() const
127 ASSERT(&m_transaction.database().originThread() == &Thread::current());
129 auto indexNames = DOMStringList::create();
132 for (auto& name : m_info.indexNames())
133 indexNames->append(name);
140 IDBTransaction& IDBObjectStore::transaction()
142 ASSERT(&m_transaction.database().originThread() == &Thread::current());
143 return m_transaction;
146 bool IDBObjectStore::autoIncrement() const
148 ASSERT(&m_transaction.database().originThread() == &Thread::current());
149 return m_info.autoIncrement();
152 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::openCursor(ExecState& execState, RefPtr<IDBKeyRange> range, IDBCursorDirection direction)
154 LOG(IndexedDB, "IDBObjectStore::openCursor");
155 ASSERT(&m_transaction.database().originThread() == &Thread::current());
158 return Exception { InvalidStateError, "Failed to execute 'openCursor' on 'IDBObjectStore': The object store has been deleted."_s };
160 if (!m_transaction.isActive())
161 return Exception { TransactionInactiveError, "Failed to execute 'openCursor' on 'IDBObjectStore': The transaction is inactive or finished."_s };
163 auto info = IDBCursorInfo::objectStoreCursor(m_transaction, m_info.identifier(), range.get(), direction, IndexedDB::CursorType::KeyAndValue);
164 return m_transaction.requestOpenCursor(execState, *this, info);
167 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::openCursor(ExecState& execState, JSValue key, IDBCursorDirection direction)
169 auto onlyResult = IDBKeyRange::only(execState, key);
170 if (onlyResult.hasException())
171 return Exception { DataError, "Failed to execute 'openCursor' on 'IDBObjectStore': The parameter is not a valid key."_s };
173 return openCursor(execState, onlyResult.releaseReturnValue(), direction);
176 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::openKeyCursor(ExecState& execState, RefPtr<IDBKeyRange> range, IDBCursorDirection direction)
178 LOG(IndexedDB, "IDBObjectStore::openCursor");
179 ASSERT(&m_transaction.database().originThread() == &Thread::current());
182 return Exception { InvalidStateError, "Failed to execute 'openKeyCursor' on 'IDBObjectStore': The object store has been deleted."_s };
184 if (!m_transaction.isActive())
185 return Exception { TransactionInactiveError, "Failed to execute 'openKeyCursor' on 'IDBObjectStore': The transaction is inactive or finished."_s };
187 auto info = IDBCursorInfo::objectStoreCursor(m_transaction, m_info.identifier(), range.get(), direction, IndexedDB::CursorType::KeyOnly);
188 return m_transaction.requestOpenCursor(execState, *this, info);
191 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::openKeyCursor(ExecState& execState, JSValue key, IDBCursorDirection direction)
193 auto onlyResult = IDBKeyRange::only(execState, key);
194 if (onlyResult.hasException())
195 return Exception { DataError, "Failed to execute 'openKeyCursor' on 'IDBObjectStore': The parameter is not a valid key or key range."_s };
197 return openKeyCursor(execState, onlyResult.releaseReturnValue(), direction);
200 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::get(ExecState& execState, JSValue key)
202 LOG(IndexedDB, "IDBObjectStore::get");
203 ASSERT(&m_transaction.database().originThread() == &Thread::current());
206 return Exception { InvalidStateError, "Failed to execute 'get' on 'IDBObjectStore': The object store has been deleted."_s };
208 if (!m_transaction.isActive())
209 return Exception { TransactionInactiveError, "Failed to execute 'get' on 'IDBObjectStore': The transaction is inactive or finished."_s };
211 auto idbKey = scriptValueToIDBKey(execState, key);
212 if (!idbKey->isValid())
213 return Exception { DataError, "Failed to execute 'get' on 'IDBObjectStore': The parameter is not a valid key."_s };
215 return m_transaction.requestGetRecord(execState, *this, { idbKey.ptr(), IDBGetRecordDataType::KeyAndValue });
218 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::get(ExecState& execState, IDBKeyRange* keyRange)
220 LOG(IndexedDB, "IDBObjectStore::get");
221 ASSERT(&m_transaction.database().originThread() == &Thread::current());
224 return Exception { InvalidStateError, "Failed to execute 'get' on 'IDBObjectStore': The object store has been deleted."_s };
226 if (!m_transaction.isActive())
227 return Exception { TransactionInactiveError };
229 IDBKeyRangeData keyRangeData(keyRange);
230 if (!keyRangeData.isValid())
231 return Exception { DataError };
233 return m_transaction.requestGetRecord(execState, *this, { keyRangeData, IDBGetRecordDataType::KeyAndValue });
236 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::getKey(ExecState& execState, JSValue key)
238 LOG(IndexedDB, "IDBObjectStore::getKey");
239 ASSERT(&m_transaction.database().originThread() == &Thread::current());
242 return Exception { InvalidStateError, "Failed to execute 'getKey' on 'IDBObjectStore': The object store has been deleted."_s };
244 if (!m_transaction.isActive())
245 return Exception { TransactionInactiveError, "Failed to execute 'getKey' on 'IDBObjectStore': The transaction is inactive or finished."_s };
247 auto idbKey = scriptValueToIDBKey(execState, key);
248 if (!idbKey->isValid())
249 return Exception { DataError, "Failed to execute 'getKey' on 'IDBObjectStore': The parameter is not a valid key."_s };
251 return m_transaction.requestGetRecord(execState, *this, { idbKey.ptr(), IDBGetRecordDataType::KeyOnly });
254 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::getKey(ExecState& execState, IDBKeyRange* keyRange)
256 LOG(IndexedDB, "IDBObjectStore::getKey");
257 ASSERT(&m_transaction.database().originThread() == &Thread::current());
260 return Exception { InvalidStateError, "Failed to execute 'getKey' on 'IDBObjectStore': The object store has been deleted."_s };
262 if (!m_transaction.isActive())
263 return Exception { TransactionInactiveError, "Failed to execute 'getKey' on 'IDBObjectStore': The transaction is inactive or finished."_s };
265 IDBKeyRangeData keyRangeData(keyRange);
266 if (!keyRangeData.isValid())
267 return Exception { DataError, "Failed to execute 'getKey' on 'IDBObjectStore': The parameter is not a valid key range."_s };
269 return m_transaction.requestGetRecord(execState, *this, { keyRangeData, IDBGetRecordDataType::KeyOnly });
272 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::add(ExecState& execState, JSValue value, JSValue key)
274 RefPtr<IDBKey> idbKey;
275 if (!key.isUndefined())
276 idbKey = scriptValueToIDBKey(execState, key);
277 return putOrAdd(execState, value, idbKey, IndexedDB::ObjectStoreOverwriteMode::NoOverwrite, InlineKeyCheck::Perform);
280 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::put(ExecState& execState, JSValue value, JSValue key)
282 RefPtr<IDBKey> idbKey;
283 if (!key.isUndefined())
284 idbKey = scriptValueToIDBKey(execState, key);
285 return putOrAdd(execState, value, idbKey, IndexedDB::ObjectStoreOverwriteMode::Overwrite, InlineKeyCheck::Perform);
288 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::putForCursorUpdate(ExecState& state, JSValue value, RefPtr<IDBKey> key)
290 return putOrAdd(state, value, WTFMove(key), IndexedDB::ObjectStoreOverwriteMode::OverwriteForCursor, InlineKeyCheck::DoNotPerform);
293 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::putOrAdd(ExecState& state, JSValue value, RefPtr<IDBKey> key, IndexedDB::ObjectStoreOverwriteMode overwriteMode, InlineKeyCheck inlineKeyCheck)
296 auto scope = DECLARE_CATCH_SCOPE(vm);
298 LOG(IndexedDB, "IDBObjectStore::putOrAdd");
299 ASSERT(&m_transaction.database().originThread() == &Thread::current());
301 auto context = scriptExecutionContextFromExecState(&state);
303 return Exception { UnknownError, "Unable to store record in object store because it does not have a valid script execution context"_s };
306 return Exception { InvalidStateError, "Failed to store record in an IDBObjectStore: The object store has been deleted."_s };
308 if (!m_transaction.isActive())
309 return Exception { TransactionInactiveError, "Failed to store record in an IDBObjectStore: The transaction is inactive or finished."_s };
311 if (m_transaction.isReadOnly())
312 return Exception { ReadonlyError, "Failed to store record in an IDBObjectStore: The transaction is read-only."_s };
314 auto serializedValue = SerializedScriptValue::create(state, value);
315 if (UNLIKELY(scope.exception())) {
316 // Clear the DOM exception from the serializer so we can give a more targeted exception.
317 scope.clearException();
319 return Exception { DataCloneError, "Failed to store record in an IDBObjectStore: An object could not be cloned."_s };
322 bool privateBrowsingEnabled = false;
323 if (is<Document>(*context)) {
324 if (auto* page = downcast<Document>(*context).page())
325 privateBrowsingEnabled = page->sessionID().isEphemeral();
328 if (serializedValue->hasBlobURLs() && privateBrowsingEnabled) {
329 // https://bugs.webkit.org/show_bug.cgi?id=156347 - Support Blobs in private browsing.
330 return Exception { DataCloneError, "Failed to store record in an IDBObjectStore: BlobURLs are not yet supported."_s };
333 if (key && !key->isValid())
334 return Exception { DataError, "Failed to store record in an IDBObjectStore: The parameter is not a valid key."_s };
336 bool usesInlineKeys = !!m_info.keyPath();
337 bool usesKeyGenerator = autoIncrement();
338 if (usesInlineKeys && inlineKeyCheck == InlineKeyCheck::Perform) {
340 return Exception { DataError, "Failed to store record in an IDBObjectStore: The object store uses in-line keys and the key parameter was provided."_s };
342 RefPtr<IDBKey> keyPathKey = maybeCreateIDBKeyFromScriptValueAndKeyPath(state, value, m_info.keyPath().value());
343 if (keyPathKey && !keyPathKey->isValid())
344 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 };
347 if (!usesKeyGenerator)
348 return Exception { DataError, "Failed to store record in an IDBObjectStore: Evaluating the object store's key path did not yield a value."_s };
349 if (!canInjectIDBKeyIntoScriptValue(state, value, m_info.keyPath().value()))
350 return Exception { DataError };
357 } else if (!usesKeyGenerator && !key)
358 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 };
360 return m_transaction.requestPutOrAdd(state, *this, WTFMove(key), *serializedValue, overwriteMode);
363 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::deleteFunction(ExecState& execState, IDBKeyRange* keyRange)
365 return doDelete(execState, keyRange);
368 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::doDelete(ExecState& execState, IDBKeyRange* keyRange)
370 LOG(IndexedDB, "IDBObjectStore::deleteFunction");
371 ASSERT(&m_transaction.database().originThread() == &Thread::current());
373 // The IDB spec for several IDBObjectStore methods states that transaction related exceptions should fire before
374 // the exception for an object store being deleted.
375 // However, a handful of W3C IDB tests expect the deleted exception even though the transaction inactive exception also applies.
376 // Additionally, Chrome and Edge agree with the test, as does Legacy IDB in WebKit.
377 // Until this is sorted out, we'll agree with the test and the majority share browsers.
379 return Exception { InvalidStateError, "Failed to execute 'delete' on 'IDBObjectStore': The object store has been deleted."_s };
381 if (!m_transaction.isActive())
382 return Exception { TransactionInactiveError, "Failed to execute 'delete' on 'IDBObjectStore': The transaction is inactive or finished."_s };
384 if (m_transaction.isReadOnly())
385 return Exception { ReadonlyError, "Failed to execute 'delete' on 'IDBObjectStore': The transaction is read-only."_s };
387 IDBKeyRangeData keyRangeData(keyRange);
388 if (!keyRangeData.isValid())
389 return Exception { DataError, "Failed to execute 'delete' on 'IDBObjectStore': The parameter is not a valid key range."_s };
391 return m_transaction.requestDeleteRecord(execState, *this, keyRangeData);
394 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::deleteFunction(ExecState& execState, JSValue key)
396 Ref<IDBKey> idbKey = scriptValueToIDBKey(execState, key);
397 if (!idbKey->isValid())
398 return Exception { DataError, "Failed to execute 'delete' on 'IDBObjectStore': The parameter is not a valid key."_s };
399 return doDelete(execState, IDBKeyRange::create(WTFMove(idbKey)).ptr());
402 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::clear(ExecState& execState)
404 LOG(IndexedDB, "IDBObjectStore::clear");
405 ASSERT(&m_transaction.database().originThread() == &Thread::current());
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.
413 return Exception { InvalidStateError, "Failed to execute 'clear' on 'IDBObjectStore': The object store has been deleted."_s };
415 if (!m_transaction.isActive())
416 return Exception { TransactionInactiveError, "Failed to execute 'clear' on 'IDBObjectStore': The transaction is inactive or finished."_s };
418 if (m_transaction.isReadOnly())
419 return Exception { ReadonlyError, "Failed to execute 'clear' on 'IDBObjectStore': The transaction is read-only."_s };
421 return m_transaction.requestClearObjectStore(execState, *this);
424 ExceptionOr<Ref<IDBIndex>> IDBObjectStore::createIndex(ExecState&, const String& name, IDBKeyPath&& keyPath, const IndexParameters& parameters)
426 LOG(IndexedDB, "IDBObjectStore::createIndex %s (keyPath: %s, unique: %i, multiEntry: %i)", name.utf8().data(), loggingString(keyPath).utf8().data(), parameters.unique, parameters.multiEntry);
427 ASSERT(&m_transaction.database().originThread() == &Thread::current());
429 if (!m_transaction.isVersionChange())
430 return Exception { InvalidStateError, "Failed to execute 'createIndex' on 'IDBObjectStore': The database is not running a version change transaction."_s };
433 return Exception { InvalidStateError, "Failed to execute 'createIndex' on 'IDBObjectStore': The object store has been deleted."_s };
435 if (!m_transaction.isActive())
436 return Exception { TransactionInactiveError, "Failed to execute 'createIndex' on 'IDBObjectStore': The transaction is inactive."_s};
438 if (m_info.hasIndex(name))
439 return Exception { ConstraintError, "Failed to execute 'createIndex' on 'IDBObjectStore': An index with the specified name already exists."_s };
441 if (!isIDBKeyPathValid(keyPath))
442 return Exception { SyntaxError, "Failed to execute 'createIndex' on 'IDBObjectStore': The keyPath argument contains an invalid key path."_s };
445 return Exception { TypeError };
447 if (parameters.multiEntry && WTF::holds_alternative<Vector<String>>(keyPath))
448 return Exception { InvalidAccessError, "Failed to execute 'createIndex' on 'IDBObjectStore': The keyPath argument was an array and the multiEntry option is true."_s };
450 // Install the new Index into the ObjectStore's info.
451 IDBIndexInfo info = m_info.createNewIndex(name, WTFMove(keyPath), parameters.unique, parameters.multiEntry);
452 m_transaction.database().didCreateIndexInfo(info);
454 // Create the actual IDBObjectStore from the transaction, which also schedules the operation server side.
455 auto index = m_transaction.createIndex(*this, info);
457 Ref<IDBIndex> referencedIndex { *index };
459 Locker<Lock> locker(m_referencedIndexLock);
460 m_referencedIndexes.set(name, WTFMove(index));
462 return WTFMove(referencedIndex);
465 ExceptionOr<Ref<IDBIndex>> IDBObjectStore::index(const String& indexName)
467 LOG(IndexedDB, "IDBObjectStore::index");
468 ASSERT(&m_transaction.database().originThread() == &Thread::current());
470 if (!scriptExecutionContext())
471 return Exception { InvalidStateError }; // FIXME: Is this code tested? Is iteven reachable?
474 return Exception { InvalidStateError, "Failed to execute 'index' on 'IDBObjectStore': The object store has been deleted."_s };
476 if (m_transaction.isFinishedOrFinishing())
477 return Exception { InvalidStateError, "Failed to execute 'index' on 'IDBObjectStore': The transaction is finished."_s };
479 Locker<Lock> locker(m_referencedIndexLock);
480 auto iterator = m_referencedIndexes.find(indexName);
481 if (iterator != m_referencedIndexes.end())
482 return Ref<IDBIndex> { *iterator->value };
484 auto* info = m_info.infoForExistingIndex(indexName);
486 return Exception { NotFoundError, "Failed to execute 'index' on 'IDBObjectStore': The specified index was not found."_s };
488 auto index = std::make_unique<IDBIndex>(*scriptExecutionContext(), *info, *this);
490 Ref<IDBIndex> referencedIndex { *index };
492 m_referencedIndexes.set(indexName, WTFMove(index));
494 return WTFMove(referencedIndex);
497 ExceptionOr<void> IDBObjectStore::deleteIndex(const String& name)
499 LOG(IndexedDB, "IDBObjectStore::deleteIndex %s", name.utf8().data());
500 ASSERT(&m_transaction.database().originThread() == &Thread::current());
503 return Exception { InvalidStateError, "Failed to execute 'deleteIndex' on 'IDBObjectStore': The object store has been deleted."_s };
505 if (!m_transaction.isVersionChange())
506 return Exception { InvalidStateError, "Failed to execute 'deleteIndex' on 'IDBObjectStore': The database is not running a version change transaction."_s };
508 if (!m_transaction.isActive())
509 return Exception { TransactionInactiveError, "Failed to execute 'deleteIndex' on 'IDBObjectStore': The transaction is inactive or finished."_s };
511 if (!m_info.hasIndex(name))
512 return Exception { NotFoundError, "Failed to execute 'deleteIndex' on 'IDBObjectStore': The specified index was not found."_s };
514 auto* info = m_info.infoForExistingIndex(name);
516 m_transaction.database().didDeleteIndexInfo(*info);
518 m_info.deleteIndex(name);
521 Locker<Lock> locker(m_referencedIndexLock);
522 if (auto index = m_referencedIndexes.take(name)) {
523 index->markAsDeleted();
524 auto identifier = index->info().identifier();
525 m_deletedIndexes.add(identifier, WTFMove(index));
529 m_transaction.deleteIndex(m_info.identifier(), name);
534 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::count(ExecState& execState, JSValue key)
536 LOG(IndexedDB, "IDBObjectStore::count");
538 Ref<IDBKey> idbKey = scriptValueToIDBKey(execState, key);
539 if (!idbKey->isValid())
540 return Exception { DataError, "Failed to execute 'count' on 'IDBObjectStore': The parameter is not a valid key."_s };
542 return doCount(execState, IDBKeyRangeData(idbKey.ptr()));
545 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::count(ExecState& execState, IDBKeyRange* range)
547 LOG(IndexedDB, "IDBObjectStore::count");
549 return doCount(execState, range ? IDBKeyRangeData(range) : IDBKeyRangeData::allKeys());
552 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::doCount(ExecState& execState, const IDBKeyRangeData& range)
554 ASSERT(&m_transaction.database().originThread() == &Thread::current());
556 // The IDB spec for several IDBObjectStore methods states that transaction related exceptions should fire before
557 // the exception for an object store being deleted.
558 // However, a handful of W3C IDB tests expect the deleted exception even though the transaction inactive exception also applies.
559 // Additionally, Chrome and Edge agree with the test, as does Legacy IDB in WebKit.
560 // Until this is sorted out, we'll agree with the test and the majority share browsers.
562 return Exception { InvalidStateError, "Failed to execute 'count' on 'IDBObjectStore': The object store has been deleted."_s };
564 if (!m_transaction.isActive())
565 return Exception { TransactionInactiveError, "Failed to execute 'count' on 'IDBObjectStore': The transaction is inactive or finished."_s };
567 if (!range.isValid())
568 return Exception { DataError };
570 return m_transaction.requestCount(execState, *this, range);
573 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::getAll(ExecState& execState, RefPtr<IDBKeyRange> range, Optional<uint32_t> count)
575 LOG(IndexedDB, "IDBObjectStore::getAll");
576 ASSERT(&m_transaction.database().originThread() == &Thread::current());
579 return Exception { InvalidStateError, "Failed to execute 'getAll' on 'IDBObjectStore': The object store has been deleted."_s };
581 if (!m_transaction.isActive())
582 return Exception { TransactionInactiveError, "Failed to execute 'getAll' on 'IDBObjectStore': The transaction is inactive or finished."_s };
584 return m_transaction.requestGetAllObjectStoreRecords(execState, *this, range.get(), IndexedDB::GetAllType::Values, count);
587 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::getAll(ExecState& execState, JSValue key, Optional<uint32_t> count)
589 auto onlyResult = IDBKeyRange::only(execState, key);
590 if (onlyResult.hasException())
591 return Exception { DataError, "Failed to execute 'getAll' on 'IDBObjectStore': The parameter is not a valid key."_s };
593 return getAll(execState, onlyResult.releaseReturnValue(), count);
596 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::getAllKeys(ExecState& execState, RefPtr<IDBKeyRange> range, Optional<uint32_t> count)
598 LOG(IndexedDB, "IDBObjectStore::getAllKeys");
599 ASSERT(&m_transaction.database().originThread() == &Thread::current());
602 return Exception { InvalidStateError, "Failed to execute 'getAllKeys' on 'IDBObjectStore': The object store has been deleted."_s };
604 if (!m_transaction.isActive())
605 return Exception { TransactionInactiveError, "Failed to execute 'getAllKeys' on 'IDBObjectStore': The transaction is inactive or finished."_s };
607 return m_transaction.requestGetAllObjectStoreRecords(execState, *this, range.get(), IndexedDB::GetAllType::Keys, count);
610 ExceptionOr<Ref<IDBRequest>> IDBObjectStore::getAllKeys(ExecState& execState, JSValue key, Optional<uint32_t> count)
612 auto onlyResult = IDBKeyRange::only(execState, key);
613 if (onlyResult.hasException())
614 return Exception { DataError, "Failed to execute 'getAllKeys' on 'IDBObjectStore': The parameter is not a valid key."_s };
616 return getAllKeys(execState, onlyResult.releaseReturnValue(), count);
619 void IDBObjectStore::markAsDeleted()
621 ASSERT(&m_transaction.database().originThread() == &Thread::current());
625 void IDBObjectStore::rollbackForVersionChangeAbort()
627 ASSERT(&m_transaction.database().originThread() == &Thread::current());
629 String currentName = m_info.name();
630 m_info = m_originalInfo;
632 auto& databaseInfo = transaction().database().info();
633 auto* objectStoreInfo = databaseInfo.infoForExistingObjectStore(m_info.identifier());
634 if (!objectStoreInfo) {
635 m_info.rename(currentName);
640 HashSet<uint64_t> indexesToRemove;
641 for (auto indexIdentifier : objectStoreInfo->indexMap().keys()) {
642 if (!objectStoreInfo->hasIndex(indexIdentifier))
643 indexesToRemove.add(indexIdentifier);
646 for (auto indexIdentifier : indexesToRemove)
647 m_info.deleteIndex(indexIdentifier);
650 Locker<Lock> locker(m_referencedIndexLock);
652 Vector<uint64_t> identifiersToRemove;
653 for (auto& iterator : m_deletedIndexes) {
654 if (m_info.hasIndex(iterator.key)) {
655 auto name = iterator.value->info().name();
656 m_referencedIndexes.set(name, WTFMove(iterator.value));
657 identifiersToRemove.append(iterator.key);
661 for (auto identifier : identifiersToRemove)
662 m_deletedIndexes.remove(identifier);
664 for (auto& index : m_referencedIndexes.values())
665 index->rollbackInfoForVersionChangeAbort();
668 void IDBObjectStore::visitReferencedIndexes(SlotVisitor& visitor) const
670 Locker<Lock> locker(m_referencedIndexLock);
671 for (auto& index : m_referencedIndexes.values())
672 visitor.addOpaqueRoot(index.get());
673 for (auto& index : m_deletedIndexes.values())
674 visitor.addOpaqueRoot(index.get());
677 void IDBObjectStore::renameReferencedIndex(IDBIndex& index, const String& newName)
679 LOG(IndexedDB, "IDBObjectStore::renameReferencedIndex");
681 auto* indexInfo = m_info.infoForExistingIndex(index.info().identifier());
683 indexInfo->rename(newName);
685 ASSERT(m_referencedIndexes.contains(index.info().name()));
686 ASSERT(!m_referencedIndexes.contains(newName));
687 ASSERT(m_referencedIndexes.get(index.info().name()) == &index);
689 m_referencedIndexes.set(newName, m_referencedIndexes.take(index.info().name()));
692 void IDBObjectStore::ref()
697 void IDBObjectStore::deref()
699 m_transaction.deref();
702 } // namespace WebCore
704 #endif // ENABLE(INDEXED_DATABASE)