From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- dom/indexedDB/IDBIndex.cpp | 637 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 637 insertions(+) create mode 100644 dom/indexedDB/IDBIndex.cpp (limited to 'dom/indexedDB/IDBIndex.cpp') diff --git a/dom/indexedDB/IDBIndex.cpp b/dom/indexedDB/IDBIndex.cpp new file mode 100644 index 0000000000..2f77a1518c --- /dev/null +++ b/dom/indexedDB/IDBIndex.cpp @@ -0,0 +1,637 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "IDBIndex.h" + +#include "IDBCursorType.h" +#include "IDBDatabase.h" +#include "IDBEvents.h" +#include "IDBKeyRange.h" +#include "IDBObjectStore.h" +#include "IDBRequest.h" +#include "IDBTransaction.h" +#include "IndexedDatabase.h" +#include "IndexedDatabaseInlines.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h" +#include "ProfilerHelpers.h" +#include "ReportInternalError.h" + +// Include this last to avoid path problems on Windows. +#include "ActorsChild.h" + +namespace mozilla::dom { + +using namespace mozilla::dom::indexedDB; + +namespace { + +MovingNotNull> GenerateRequest(JSContext* aCx, + IDBIndex* aIndex) { + MOZ_ASSERT(aIndex); + aIndex->AssertIsOnOwningThread(); + + auto transaction = aIndex->ObjectStore()->AcquireTransaction(); + auto* const database = transaction->Database(); + + return IDBRequest::Create(aCx, aIndex, database, std::move(transaction)); +} + +} // namespace + +IDBIndex::IDBIndex(IDBObjectStore* aObjectStore, const IndexMetadata* aMetadata) + : mObjectStore(aObjectStore), + mCachedKeyPath(JS::UndefinedValue()), + mMetadata(aMetadata), + mId(aMetadata->id()), + mRooted(false) { + MOZ_ASSERT(aObjectStore); + aObjectStore->AssertIsOnOwningThread(); + MOZ_ASSERT(aMetadata); +} + +IDBIndex::~IDBIndex() { + AssertIsOnOwningThread(); + + if (mRooted) { + mozilla::DropJSObjects(this); + } +} + +RefPtr IDBIndex::Create(IDBObjectStore* aObjectStore, + const IndexMetadata& aMetadata) { + MOZ_ASSERT(aObjectStore); + aObjectStore->AssertIsOnOwningThread(); + + return new IDBIndex(aObjectStore, &aMetadata); +} + +#ifdef DEBUG + +void IDBIndex::AssertIsOnOwningThread() const { + MOZ_ASSERT(mObjectStore); + mObjectStore->AssertIsOnOwningThread(); +} + +#endif // DEBUG + +RefPtr IDBIndex::OpenCursor(JSContext* aCx, + JS::Handle aRange, + IDBCursorDirection aDirection, + ErrorResult& aRv) { + AssertIsOnOwningThread(); + + return OpenCursorInternal(/* aKeysOnly */ false, aCx, aRange, aDirection, + aRv); +} + +RefPtr IDBIndex::OpenKeyCursor(JSContext* aCx, + JS::Handle aRange, + IDBCursorDirection aDirection, + ErrorResult& aRv) { + AssertIsOnOwningThread(); + + return OpenCursorInternal(/* aKeysOnly */ true, aCx, aRange, aDirection, aRv); +} + +RefPtr IDBIndex::Get(JSContext* aCx, JS::Handle aKey, + ErrorResult& aRv) { + AssertIsOnOwningThread(); + + return GetInternal(/* aKeyOnly */ false, aCx, aKey, aRv); +} + +RefPtr IDBIndex::GetKey(JSContext* aCx, JS::Handle aKey, + ErrorResult& aRv) { + AssertIsOnOwningThread(); + + return GetInternal(/* aKeyOnly */ true, aCx, aKey, aRv); +} + +RefPtr IDBIndex::GetAll(JSContext* aCx, JS::Handle aKey, + const Optional& aLimit, + ErrorResult& aRv) { + AssertIsOnOwningThread(); + + return GetAllInternal(/* aKeysOnly */ false, aCx, aKey, aLimit, aRv); +} + +RefPtr IDBIndex::GetAllKeys(JSContext* aCx, + JS::Handle aKey, + const Optional& aLimit, + ErrorResult& aRv) { + AssertIsOnOwningThread(); + + return GetAllInternal(/* aKeysOnly */ true, aCx, aKey, aLimit, aRv); +} + +void IDBIndex::RefreshMetadata(bool aMayDelete) { + AssertIsOnOwningThread(); + MOZ_ASSERT_IF(mDeletedMetadata, mMetadata == mDeletedMetadata.get()); + + const auto& indexes = mObjectStore->Spec().indexes(); + const auto foundIt = std::find_if( + indexes.cbegin(), indexes.cend(), + [id = Id()](const auto& metadata) { return metadata.id() == id; }); + const bool found = foundIt != indexes.cend(); + + MOZ_ASSERT_IF(!aMayDelete && !mDeletedMetadata, found); + + if (found) { + mMetadata = &*foundIt; + MOZ_ASSERT(mMetadata != mDeletedMetadata.get()); + mDeletedMetadata = nullptr; + } else { + NoteDeletion(); + } +} + +void IDBIndex::NoteDeletion() { + AssertIsOnOwningThread(); + MOZ_ASSERT(mMetadata); + MOZ_ASSERT(Id() == mMetadata->id()); + + if (mDeletedMetadata) { + MOZ_ASSERT(mMetadata == mDeletedMetadata.get()); + return; + } + + mDeletedMetadata = MakeUnique(*mMetadata); + + mMetadata = mDeletedMetadata.get(); +} + +const nsString& IDBIndex::Name() const { + AssertIsOnOwningThread(); + MOZ_ASSERT(mMetadata); + + return mMetadata->name(); +} + +void IDBIndex::SetName(const nsAString& aName, ErrorResult& aRv) { + AssertIsOnOwningThread(); + + const auto& transaction = mObjectStore->TransactionRef(); + + if (transaction.GetMode() != IDBTransaction::Mode::VersionChange || + mDeletedMetadata) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return; + } + + if (!transaction.IsActive()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return; + } + + if (aName == mMetadata->name()) { + return; + } + + // Cache logging string of this index before renaming. + const LoggingString loggingOldIndex(this); + + const int64_t indexId = Id(); + + nsresult rv = + transaction.Database()->RenameIndex(mObjectStore->Id(), indexId, aName); + + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return; + } + + // Don't do this in the macro because we always need to increment the serial + // number to keep in sync with the parent. + const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber(); + + IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( + "database(%s).transaction(%s).objectStore(%s).index(%s)." + "rename(%s)", + "IDBIndex.rename(%.0s%.0s%.0s%.0s%.0s)", + transaction.LoggingSerialNumber(), requestSerialNumber, + IDB_LOG_STRINGIFY(transaction.Database()), IDB_LOG_STRINGIFY(transaction), + IDB_LOG_STRINGIFY(mObjectStore), loggingOldIndex.get(), + IDB_LOG_STRINGIFY(this)); + + mObjectStore->MutableTransactionRef().RenameIndex(mObjectStore, indexId, + aName); +} + +bool IDBIndex::Unique() const { + AssertIsOnOwningThread(); + MOZ_ASSERT(mMetadata); + + return mMetadata->unique(); +} + +bool IDBIndex::MultiEntry() const { + AssertIsOnOwningThread(); + MOZ_ASSERT(mMetadata); + + return mMetadata->multiEntry(); +} + +bool IDBIndex::LocaleAware() const { + AssertIsOnOwningThread(); + MOZ_ASSERT(mMetadata); + + return mMetadata->locale().IsEmpty(); +} + +const indexedDB::KeyPath& IDBIndex::GetKeyPath() const { + AssertIsOnOwningThread(); + MOZ_ASSERT(mMetadata); + + return mMetadata->keyPath(); +} + +void IDBIndex::GetLocale(nsString& aLocale) const { + AssertIsOnOwningThread(); + MOZ_ASSERT(mMetadata); + + if (mMetadata->locale().IsEmpty()) { + SetDOMStringToNull(aLocale); + } else { + CopyASCIItoUTF16(mMetadata->locale(), aLocale); + } +} + +const nsCString& IDBIndex::Locale() const { + AssertIsOnOwningThread(); + MOZ_ASSERT(mMetadata); + + return mMetadata->locale(); +} + +bool IDBIndex::IsAutoLocale() const { + AssertIsOnOwningThread(); + MOZ_ASSERT(mMetadata); + + return mMetadata->autoLocale(); +} + +nsIGlobalObject* IDBIndex::GetParentObject() const { + AssertIsOnOwningThread(); + + return mObjectStore->GetParentObject(); +} + +void IDBIndex::GetKeyPath(JSContext* aCx, JS::MutableHandle aResult, + ErrorResult& aRv) { + AssertIsOnOwningThread(); + + if (!mCachedKeyPath.isUndefined()) { + MOZ_ASSERT(mRooted); + aResult.set(mCachedKeyPath); + return; + } + + MOZ_ASSERT(!mRooted); + + aRv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + if (mCachedKeyPath.isGCThing()) { + mozilla::HoldJSObjects(this); + mRooted = true; + } + + aResult.set(mCachedKeyPath); +} + +RefPtr IDBIndex::GetInternal(bool aKeyOnly, JSContext* aCx, + JS::Handle aKey, + ErrorResult& aRv) { + AssertIsOnOwningThread(); + + if (mDeletedMetadata) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); + return nullptr; + } + + const auto& transaction = mObjectStore->TransactionRef(); + if (!transaction.IsActive()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return nullptr; + } + + RefPtr keyRange; + IDBKeyRange::FromJSVal(aCx, aKey, &keyRange, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + if (!keyRange) { + // Must specify a key or keyRange for get() and getKey(). + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_KEY_ERR); + return nullptr; + } + + const int64_t objectStoreId = mObjectStore->Id(); + const int64_t indexId = Id(); + + SerializedKeyRange serializedKeyRange; + keyRange->ToSerialized(serializedKeyRange); + + RequestParams params; + + if (aKeyOnly) { + params = IndexGetKeyParams(objectStoreId, indexId, serializedKeyRange); + } else { + params = IndexGetParams(objectStoreId, indexId, serializedKeyRange); + } + + auto request = GenerateRequest(aCx, this).unwrap(); + + if (aKeyOnly) { + IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( + "database(%s).transaction(%s).objectStore(%s).index(%s)." + "getKey(%s)", + "IDBIndex.getKey(%.0s%.0s%.0s%.0s%.0s)", + transaction.LoggingSerialNumber(), request->LoggingSerialNumber(), + IDB_LOG_STRINGIFY(transaction.Database()), + IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore), + IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange)); + } else { + IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( + "database(%s).transaction(%s).objectStore(%s).index(%s)." + "get(%s)", + "IDBIndex.get(%.0s%.0s%.0s%.0s%.0s)", transaction.LoggingSerialNumber(), + request->LoggingSerialNumber(), + IDB_LOG_STRINGIFY(transaction.Database()), + IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore), + IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange)); + } + + auto& mutableTransaction = mObjectStore->MutableTransactionRef(); + + // TODO: This is necessary to preserve request ordering only. Proper + // sequencing of requests should be done in a more sophisticated manner that + // doesn't require invalidating cursor caches (Bug 1580499). + mutableTransaction.InvalidateCursorCaches(); + + mutableTransaction.StartRequest(request, params); + + return request; +} + +RefPtr IDBIndex::GetAllInternal(bool aKeysOnly, JSContext* aCx, + JS::Handle aKey, + const Optional& aLimit, + ErrorResult& aRv) { + AssertIsOnOwningThread(); + + if (mDeletedMetadata) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); + return nullptr; + } + + const auto& transaction = mObjectStore->TransactionRef(); + if (!transaction.IsActive()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return nullptr; + } + + RefPtr keyRange; + IDBKeyRange::FromJSVal(aCx, aKey, &keyRange, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + const int64_t objectStoreId = mObjectStore->Id(); + const int64_t indexId = Id(); + + Maybe optionalKeyRange; + if (keyRange) { + SerializedKeyRange serializedKeyRange; + keyRange->ToSerialized(serializedKeyRange); + optionalKeyRange.emplace(serializedKeyRange); + } + + const uint32_t limit = aLimit.WasPassed() ? aLimit.Value() : 0; + + const auto& params = + aKeysOnly ? RequestParams{IndexGetAllKeysParams(objectStoreId, indexId, + optionalKeyRange, limit)} + : RequestParams{IndexGetAllParams(objectStoreId, indexId, + optionalKeyRange, limit)}; + + auto request = GenerateRequest(aCx, this).unwrap(); + + if (aKeysOnly) { + IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( + "database(%s).transaction(%s).objectStore(%s).index(%s)." + "getAllKeys(%s, %s)", + "IDBIndex.getAllKeys(%.0s%.0s%.0s%.0s%.0s%.0s)", + transaction.LoggingSerialNumber(), request->LoggingSerialNumber(), + IDB_LOG_STRINGIFY(transaction.Database()), + IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore), + IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange), + IDB_LOG_STRINGIFY(aLimit)); + } else { + IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( + "database(%s).transaction(%s).objectStore(%s).index(%s)." + "getAll(%s, %s)", + "IDBIndex.getAll(%.0s%.0s%.0s%.0s%.0s%.0s)", + transaction.LoggingSerialNumber(), request->LoggingSerialNumber(), + IDB_LOG_STRINGIFY(transaction.Database()), + IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore), + IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange), + IDB_LOG_STRINGIFY(aLimit)); + } + + auto& mutableTransaction = mObjectStore->MutableTransactionRef(); + + // TODO: This is necessary to preserve request ordering only. Proper + // sequencing of requests should be done in a more sophisticated manner that + // doesn't require invalidating cursor caches (Bug 1580499). + mutableTransaction.InvalidateCursorCaches(); + + mutableTransaction.StartRequest(request, params); + + return request; +} + +RefPtr IDBIndex::OpenCursorInternal(bool aKeysOnly, JSContext* aCx, + JS::Handle aRange, + IDBCursorDirection aDirection, + ErrorResult& aRv) { + AssertIsOnOwningThread(); + + if (mDeletedMetadata) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); + return nullptr; + } + + const auto& transaction = mObjectStore->TransactionRef(); + if (!transaction.IsActive()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return nullptr; + } + + RefPtr keyRange; + IDBKeyRange::FromJSVal(aCx, aRange, &keyRange, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + const int64_t objectStoreId = mObjectStore->Id(); + const int64_t indexId = Id(); + + Maybe optionalKeyRange; + + if (keyRange) { + SerializedKeyRange serializedKeyRange; + keyRange->ToSerialized(serializedKeyRange); + + optionalKeyRange.emplace(std::move(serializedKeyRange)); + } + + const CommonIndexOpenCursorParams commonIndexParams = { + {objectStoreId, std::move(optionalKeyRange), aDirection}, indexId}; + + const auto params = + aKeysOnly ? OpenCursorParams{IndexOpenKeyCursorParams{commonIndexParams}} + : OpenCursorParams{IndexOpenCursorParams{commonIndexParams}}; + + auto request = GenerateRequest(aCx, this).unwrap(); + + if (aKeysOnly) { + IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( + "database(%s).transaction(%s).objectStore(%s).index(%s)." + "openKeyCursor(%s, %s)", + "IDBIndex.openKeyCursor(%.0s%.0s%.0s%.0s%.0s%.0s)", + transaction.LoggingSerialNumber(), request->LoggingSerialNumber(), + IDB_LOG_STRINGIFY(transaction.Database()), + IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore), + IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange), + IDB_LOG_STRINGIFY(aDirection)); + } else { + IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( + "database(%s).transaction(%s).objectStore(%s).index(%s)." + "openCursor(%s, %s)", + "IDBIndex.openCursor(%.0s%.0s%.0s%.0s%.0s%.0s)", + transaction.LoggingSerialNumber(), request->LoggingSerialNumber(), + IDB_LOG_STRINGIFY(transaction.Database()), + IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore), + IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange), + IDB_LOG_STRINGIFY(aDirection)); + } + + const auto actor = + aKeysOnly + ? static_cast>( + MakeSafeRefPtr>( + request, this, aDirection)) + : MakeSafeRefPtr>( + request, this, aDirection); + + auto& mutableTransaction = mObjectStore->MutableTransactionRef(); + + // TODO: This is necessary to preserve request ordering only. Proper + // sequencing of requests should be done in a more sophisticated manner that + // doesn't require invalidating cursor caches (Bug 1580499). + mutableTransaction.InvalidateCursorCaches(); + + mutableTransaction.OpenCursor(*actor, params); + + return request; +} + +RefPtr IDBIndex::Count(JSContext* aCx, JS::Handle aKey, + ErrorResult& aRv) { + AssertIsOnOwningThread(); + + if (mDeletedMetadata) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); + return nullptr; + } + + const auto& transaction = mObjectStore->TransactionRef(); + if (!transaction.IsActive()) { + aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR); + return nullptr; + } + + RefPtr keyRange; + IDBKeyRange::FromJSVal(aCx, aKey, &keyRange, aRv); + if (aRv.Failed()) { + return nullptr; + } + + IndexCountParams params; + params.objectStoreId() = mObjectStore->Id(); + params.indexId() = Id(); + + if (keyRange) { + SerializedKeyRange serializedKeyRange; + keyRange->ToSerialized(serializedKeyRange); + params.optionalKeyRange().emplace(serializedKeyRange); + } + + auto request = GenerateRequest(aCx, this).unwrap(); + + IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( + "database(%s).transaction(%s).objectStore(%s).index(%s)." + "count(%s)", + "IDBIndex.count(%.0s%.0s%.0s%.0s%.0s)", transaction.LoggingSerialNumber(), + request->LoggingSerialNumber(), IDB_LOG_STRINGIFY(transaction.Database()), + IDB_LOG_STRINGIFY(transaction), IDB_LOG_STRINGIFY(mObjectStore), + IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(keyRange)); + + auto& mutableTransaction = mObjectStore->MutableTransactionRef(); + + // TODO: This is necessary to preserve request ordering only. Proper + // sequencing of requests should be done in a more sophisticated manner that + // doesn't require invalidating cursor caches (Bug 1580499). + mutableTransaction.InvalidateCursorCaches(); + + mutableTransaction.StartRequest(request, params); + + return request; +} + +NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBIndex) +NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBIndex) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBIndex) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTION_CLASS(IDBIndex) + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBIndex) + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedKeyPath) +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBIndex) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObjectStore) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBIndex) + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER + + // Don't unlink mObjectStore! + + tmp->mCachedKeyPath.setUndefined(); + + if (tmp->mRooted) { + mozilla::DropJSObjects(tmp); + tmp->mRooted = false; + } +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +JSObject* IDBIndex::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) { + return IDBIndex_Binding::Wrap(aCx, this, aGivenProto); +} + +} // namespace mozilla::dom -- cgit v1.2.3