From 40a355a42d4a9444dc753c04c6608dade2f06a23 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 03:13:27 +0200 Subject: Adding upstream version 125.0.1. Signed-off-by: Daniel Baumann --- dom/indexedDB/ActorsParent.cpp | 441 ++++++++++----------- dom/indexedDB/ActorsParent.h | 5 +- dom/indexedDB/ActorsParentCommon.cpp | 2 +- dom/indexedDB/IDBFactory.cpp | 35 +- dom/indexedDB/IndexedDatabaseManager.cpp | 45 ++- dom/indexedDB/IndexedDatabaseManager.h | 3 + dom/indexedDB/SchemaUpgrades.cpp | 6 +- dom/indexedDB/SerializationHelpers.h | 7 +- dom/indexedDB/test/abort_on_reload.html | 6 +- dom/indexedDB/test/bfcache_page1.html | 2 +- dom/indexedDB/test/blob_worker_crash_iframe.html | 2 +- dom/indexedDB/test/browser_private_idb.js | 4 +- dom/indexedDB/test/bug839193.js | 2 +- .../error_events_abort_transactions_iframe.html | 6 +- dom/indexedDB/test/event_propagation_iframe.html | 4 +- .../test/exceptions_in_events_iframe.html | 4 +- dom/indexedDB/test/head.js | 2 +- dom/indexedDB/test/helpers.js | 4 +- dom/indexedDB/test/leaving_page_iframe.html | 2 +- dom/indexedDB/test/mochitest-common.toml | 6 +- dom/indexedDB/test/test_abort_on_reload.html | 2 +- dom/indexedDB/test/test_event_listener_leaks.html | 2 +- dom/indexedDB/test/test_open_for_principal.html | 1 + dom/indexedDB/test/third_party_window.html | 2 +- dom/indexedDB/test/unit/test_blocked_order.js | 8 +- .../test/unit/test_cleanup_transaction.js | 4 +- dom/indexedDB/test/unit/test_clear.js | 2 +- .../test/unit/test_connection_idle_maintenance.js | 97 +++++ .../unit/test_connection_idle_maintenance_stop.js | 204 ++++++++++ dom/indexedDB/test/unit/test_cursor_cycle.js | 2 +- dom/indexedDB/test/unit/test_cursor_mutation.js | 4 +- dom/indexedDB/test/unit/test_cursors.js | 4 +- dom/indexedDB/test/unit/test_database_onclose.js | 4 +- dom/indexedDB/test/unit/test_getAll.js | 2 +- .../test/unit/test_index_object_cursors.js | 4 +- .../test/unit/test_index_update_delete.js | 4 +- dom/indexedDB/test/unit/test_indexes_bad_values.js | 2 +- dom/indexedDB/test/unit/test_invalid_version.js | 1 + .../test/unit/test_quotaExceeded_recovery.js | 4 +- dom/indexedDB/test/unit/test_remove_objectStore.js | 2 +- dom/indexedDB/test/unit/test_setVersion_throw.js | 4 +- dom/indexedDB/test/unit/test_table_locks.js | 4 +- dom/indexedDB/test/unit/test_transaction_abort.js | 17 +- .../test/unit/test_transaction_abort_hang.js | 4 +- .../test/unit/xpcshell-head-parent-process.js | 14 +- .../test/unit/xpcshell-parent-process.toml | 4 + 46 files changed, 657 insertions(+), 333 deletions(-) create mode 100644 dom/indexedDB/test/unit/test_connection_idle_maintenance.js create mode 100644 dom/indexedDB/test/unit/test_connection_idle_maintenance_stop.js (limited to 'dom/indexedDB') diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp index bc0baf1157..47360094ca 100644 --- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -291,7 +291,8 @@ static_assert(kSQLiteGrowthIncrement >= 0 && "Must be 0 (disabled) or a positive multiple of the page size!"); // The maximum number of threads that can be used for database activity at a -// single time. +// single time. Please keep in sync with the constants in +// test_connection_idle_maintenance*.js tests const uint32_t kMaxConnectionThreadCount = 20; static_assert(kMaxConnectionThreadCount, "Must have at least one thread!"); @@ -304,7 +305,8 @@ static_assert(kMaxConnectionThreadCount >= kMaxIdleConnectionThreadCount, "Idle thread limit must be less than total thread limit!"); // The length of time that database connections will be held open after all -// transactions have completed before doing idle maintenance. +// transactions have completed before doing idle maintenance. Please keep in +// sync with the timeouts in test_connection_idle_maintenance*.js tests const uint32_t kConnectionIdleMaintenanceMS = 2 * 1000; // 2 seconds // The length of time that database connections will be held open after all @@ -1162,7 +1164,8 @@ class DatabaseConnection final : public CachingDatabaseConnection { return CheckpointInternal(CheckpointMode::Full); } - void DoIdleProcessing(bool aNeedsCheckpoint); + void DoIdleProcessing(bool aNeedsCheckpoint, + const Atomic& aInterrupted); void Close(); @@ -1189,7 +1192,8 @@ class DatabaseConnection final : public CachingDatabaseConnection { */ Result ReclaimFreePagesWhileIdle( CachedStatement& aFreelistStatement, CachedStatement& aRollbackStatement, - uint32_t aFreelistCount, bool aNeedsCheckpoint); + uint32_t aFreelistCount, bool aNeedsCheckpoint, + const Atomic& aInterrupted); Result GetFileSize(const nsAString& aPath); }; @@ -1362,6 +1366,34 @@ class ConnectionPool final { } }; + struct PerformingIdleMaintenanceDatabaseInfo { + const NotNull mDatabaseInfo; + RefPtr mIdleConnectionRunnable; + + PerformingIdleMaintenanceDatabaseInfo( + DatabaseInfo& aDatabaseInfo, + RefPtr aIdleConnectionRunnable); + + PerformingIdleMaintenanceDatabaseInfo( + const PerformingIdleMaintenanceDatabaseInfo& aOther) = delete; + PerformingIdleMaintenanceDatabaseInfo( + PerformingIdleMaintenanceDatabaseInfo&& aOther) noexcept + : mDatabaseInfo{aOther.mDatabaseInfo}, + mIdleConnectionRunnable{std::move(aOther.mIdleConnectionRunnable)} { + MOZ_COUNT_CTOR(ConnectionPool::PerformingIdleMaintenanceDatabaseInfo); + } + PerformingIdleMaintenanceDatabaseInfo& operator=( + const PerformingIdleMaintenanceDatabaseInfo& aOther) = delete; + PerformingIdleMaintenanceDatabaseInfo& operator=( + PerformingIdleMaintenanceDatabaseInfo&& aOther) = delete; + + ~PerformingIdleMaintenanceDatabaseInfo(); + + bool operator==(const DatabaseInfo* aDatabaseInfo) const { + return mDatabaseInfo == aDatabaseInfo; + } + }; + class ThreadInfo { public: ThreadInfo(); @@ -1456,7 +1488,8 @@ class ConnectionPool final { nsCOMPtr mIOTarget; nsTArray mIdleThreads; nsTArray mIdleDatabases; - nsTArray> mDatabasesPerformingIdleMaintenance; + nsTArray + mDatabasesPerformingIdleMaintenance; nsCOMPtr mIdleTimer; TimeStamp mTargetIdleTime; @@ -1559,6 +1592,7 @@ class ConnectionPool::ConnectionRunnable : public Runnable { class ConnectionPool::IdleConnectionRunnable final : public ConnectionRunnable { const bool mNeedsCheckpoint; + Atomic mInterrupted; public: IdleConnectionRunnable(DatabaseInfo& aDatabaseInfo, bool aNeedsCheckpoint) @@ -1567,6 +1601,8 @@ class ConnectionPool::IdleConnectionRunnable final : public ConnectionRunnable { NS_INLINE_DECL_REFCOUNTING_INHERITED(IdleConnectionRunnable, ConnectionRunnable) + void Interrupt() { mInterrupted = true; } + private: ~IdleConnectionRunnable() override = default; @@ -2061,6 +2097,7 @@ class TransactionDatabaseOperationBase : public DatabaseOperationBase { class Factory final : public PBackgroundIDBFactoryParent, public AtomicSafeRefCounted { + nsCString mSystemLocale; RefPtr mLoggingInfo; #ifdef DEBUG @@ -2072,7 +2109,7 @@ class Factory final : public PBackgroundIDBFactoryParent, public: [[nodiscard]] static SafeRefPtr Create( - const LoggingInfo& aLoggingInfo); + const LoggingInfo& aLoggingInfo, const nsACString& aSystemLocale); DatabaseLoggingInfo* GetLoggingInfo() const { AssertIsOnBackgroundThread(); @@ -2081,11 +2118,14 @@ class Factory final : public PBackgroundIDBFactoryParent, return mLoggingInfo; } + const nsCString& GetSystemLocale() const { return mSystemLocale; } + MOZ_DECLARE_REFCOUNTED_TYPENAME(mozilla::dom::indexedDB::Factory) MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED(Factory, AtomicSafeRefCounted) // Only constructed in Create(). - explicit Factory(RefPtr aLoggingInfo); + Factory(RefPtr aLoggingInfo, + const nsACString& aSystemLocale); // IPDL methods are only called by IPDL. void ActorDestroy(ActorDestroyReason aWhy) override; @@ -2961,17 +3001,11 @@ class FactoryOp protected: enum class State { - // Just created on the PBackground thread, dispatched to the main thread. - // Next step is either SendingResults if permission is denied, - // PermissionChallenge if the permission is unknown, or FinishOpen - // if permission is granted. + // Just created on the PBackground thread, dispatched to the current thread. + // Next step is either SendingResults if opening initialization failed, or + // DirectoryOpenPending if the opening initialization succeeded. Initial, - // Ensuring quota manager is created and opening directory on the - // PBackground thread. Next step is either SendingResults if quota manager - // is not available or DirectoryOpenPending if quota manager is available. - FinishOpen, - // Waiting for directory open allowed on the PBackground thread. The next // step is either SendingResults if directory lock failed to acquire, or // DatabaseOpenPending if directory lock is acquired. @@ -3129,10 +3163,6 @@ class FactoryOp virtual void SendBlockedNotification() = 0; private: - mozilla::Result CheckPermission(); - - nsresult FinishOpen(); - // Test whether this FactoryOp needs to wait for the given op. bool MustWaitFor(const FactoryOp& aExistingOp); }; @@ -6517,7 +6547,7 @@ already_AddRefed MakeConnectionIOTarget() { ******************************************************************************/ already_AddRefed AllocPBackgroundIDBFactoryParent( - const LoggingInfo& aLoggingInfo) { + const LoggingInfo& aLoggingInfo, const nsACString& aSystemLocale) { AssertIsOnBackgroundThread(); if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) { @@ -6531,15 +6561,15 @@ already_AddRefed AllocPBackgroundIDBFactoryParent( return nullptr; } - SafeRefPtr actor = Factory::Create(aLoggingInfo); + SafeRefPtr actor = Factory::Create(aLoggingInfo, aSystemLocale); MOZ_ASSERT(actor); return actor.forget(); } bool RecvPBackgroundIDBFactoryConstructor( - PBackgroundIDBFactoryParent* aActor, - const LoggingInfo& /* aLoggingInfo */) { + PBackgroundIDBFactoryParent* aActor, const LoggingInfo& /* aLoggingInfo */, + const nsACString& /* aSystemLocale */) { AssertIsOnBackgroundThread(); MOZ_ASSERT(aActor); MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread()); @@ -6862,7 +6892,8 @@ nsresult DatabaseConnection::CheckpointInternal(CheckpointMode aMode) { return NS_OK; } -void DatabaseConnection::DoIdleProcessing(bool aNeedsCheckpoint) { +void DatabaseConnection::DoIdleProcessing(bool aNeedsCheckpoint, + const Atomic& aInterrupted) { AssertIsOnConnectionThread(); MOZ_ASSERT(mInReadTransaction); MOZ_ASSERT(!mInWriteTransaction); @@ -6887,22 +6918,23 @@ void DatabaseConnection::DoIdleProcessing(bool aNeedsCheckpoint) { mInReadTransaction = false; } - const bool freedSomePages = freelistCount && [this, &freelistStmt, - &rollbackStmt, freelistCount, - aNeedsCheckpoint] { - // Warn in case of an error, but do not propagate it. Just indicate we - // didn't free any pages. - QM_TRY_INSPECT(const bool& res, - ReclaimFreePagesWhileIdle(freelistStmt, rollbackStmt, - freelistCount, aNeedsCheckpoint), - false); + const bool freedSomePages = + freelistCount && [this, &freelistStmt, &rollbackStmt, freelistCount, + aNeedsCheckpoint, &aInterrupted] { + // Warn in case of an error, but do not propagate it. Just indicate we + // didn't free any pages. + QM_TRY_INSPECT( + const bool& res, + ReclaimFreePagesWhileIdle(freelistStmt, rollbackStmt, freelistCount, + aNeedsCheckpoint, aInterrupted), + false); - // Make sure we didn't leave a transaction running. - MOZ_ASSERT(!mInReadTransaction); - MOZ_ASSERT(!mInWriteTransaction); + // Make sure we didn't leave a transaction running. + MOZ_ASSERT(!mInReadTransaction); + MOZ_ASSERT(!mInWriteTransaction); - return res; - }(); + return res; + }(); // Truncate the WAL if we were asked to or if we managed to free some space. if (aNeedsCheckpoint || freedSomePages) { @@ -6922,7 +6954,8 @@ void DatabaseConnection::DoIdleProcessing(bool aNeedsCheckpoint) { Result DatabaseConnection::ReclaimFreePagesWhileIdle( CachedStatement& aFreelistStatement, CachedStatement& aRollbackStatement, - uint32_t aFreelistCount, bool aNeedsCheckpoint) { + uint32_t aFreelistCount, bool aNeedsCheckpoint, + const Atomic& aInterrupted) { AssertIsOnConnectionThread(); MOZ_ASSERT(aFreelistStatement); MOZ_ASSERT(aRollbackStatement); @@ -6932,11 +6965,14 @@ Result DatabaseConnection::ReclaimFreePagesWhileIdle( AUTO_PROFILER_LABEL("DatabaseConnection::ReclaimFreePagesWhileIdle", DOM); - // Make sure we don't keep working if anything else needs this thread. - nsIThread* currentThread = NS_GetCurrentThread(); - MOZ_ASSERT(currentThread); + uint32_t pauseOnConnectionThreadMs = StaticPrefs:: + dom_indexedDB_connectionIdleMaintenance_pauseOnConnectionThreadMs(); + if (pauseOnConnectionThreadMs > 0) { + PR_Sleep(PR_MillisecondsToInterval(pauseOnConnectionThreadMs)); + } - if (NS_HasPendingEvents(currentThread)) { + // Make sure we don't keep working if anything else needs this thread. + if (aInterrupted) { return false; } @@ -6968,7 +7004,7 @@ Result DatabaseConnection::ReclaimFreePagesWhileIdle( mInWriteTransaction = true; - bool freedSomePages = false, interrupted = false; + bool freedSomePages = false; const auto rollback = [&aRollbackStatement, this](const auto&) { MOZ_ASSERT(mInWriteTransaction); @@ -6984,14 +7020,12 @@ Result DatabaseConnection::ReclaimFreePagesWhileIdle( uint64_t previousFreelistCount = (uint64_t)aFreelistCount + 1; QM_TRY(CollectWhile( - [&aFreelistCount, &previousFreelistCount, &interrupted, - currentThread]() -> Result { - if (NS_HasPendingEvents(currentThread)) { - // Abort if something else wants to use the thread, and - // roll back this transaction. It's ok if we never make - // progress here because the idle service should - // eventually reclaim this space. - interrupted = true; + [&aFreelistCount, &previousFreelistCount, + &aInterrupted]() -> Result { + if (aInterrupted) { + // On interrupt, abort and roll back this transaction. It's ok + // if we never make progress here because the idle service + // should eventually reclaim this space. return false; } // If we were not able to free anything, we might either see @@ -7001,7 +7035,7 @@ Result DatabaseConnection::ReclaimFreePagesWhileIdle( bool madeProgress = previousFreelistCount != aFreelistCount; previousFreelistCount = aFreelistCount; MOZ_ASSERT(madeProgress); - QM_WARNONLY_TRY(MOZ_TO_RESULT(!madeProgress)); + QM_WARNONLY_TRY(MOZ_TO_RESULT(madeProgress)); return madeProgress && (aFreelistCount != 0); }, [&aFreelistStatement, &aFreelistCount, &incrementalVacuumStmt, @@ -7015,9 +7049,9 @@ Result DatabaseConnection::ReclaimFreePagesWhileIdle( return Ok{}; }) - .andThen([&commitStmt, &freedSomePages, &interrupted, &rollback, + .andThen([&commitStmt, &freedSomePages, &aInterrupted, &rollback, this](Ok) -> Result { - if (interrupted) { + if (aInterrupted) { rollback(Ok{}); freedSomePages = false; } @@ -7982,8 +8016,9 @@ void ConnectionPool::CloseIdleDatabases() { } if (!mDatabasesPerformingIdleMaintenance.IsEmpty()) { - for (const auto dbInfo : mDatabasesPerformingIdleMaintenance) { - CloseDatabase(*dbInfo); + for (PerformingIdleMaintenanceDatabaseInfo& performingIdleMaintenanceInfo : + mDatabasesPerformingIdleMaintenance) { + CloseDatabase(*performingIdleMaintenanceInfo.mDatabaseInfo); } mDatabasesPerformingIdleMaintenance.Clear(); } @@ -8059,16 +8094,13 @@ bool ConnectionPool::ScheduleTransaction(TransactionInfo& aTransactionInfo, // deliberately const to prevent the attempt to wrongly optimize the // refcounting by passing runnable.forget() to the Dispatch method, see // bug 1598559. - const nsCOMPtr runnable = - new Runnable("IndexedDBDummyRunnable"); for (uint32_t index = mDatabasesPerformingIdleMaintenance.Length(); index > 0; index--) { - const auto dbInfo = mDatabasesPerformingIdleMaintenance[index - 1]; - dbInfo->mThreadInfo.AssertValid(); + const auto& performingIdleMaintenanceInfo = + mDatabasesPerformingIdleMaintenance[index - 1]; - MOZ_ALWAYS_SUCCEEDS(dbInfo->mThreadInfo.ThreadRef().Dispatch( - runnable, NS_DISPATCH_NORMAL)); + performingIdleMaintenanceInfo.mIdleConnectionRunnable->Interrupt(); } } @@ -8369,12 +8401,15 @@ void ConnectionPool::PerformIdleDatabaseMaintenance( aDatabaseInfo.mNeedsCheckpoint = false; aDatabaseInfo.mIdle = false; + auto idleConnectionRunnable = + MakeRefPtr(aDatabaseInfo, neededCheckpoint); + mDatabasesPerformingIdleMaintenance.AppendElement( - WrapNotNullUnchecked(&aDatabaseInfo)); + PerformingIdleMaintenanceDatabaseInfo{aDatabaseInfo, + idleConnectionRunnable}); MOZ_ALWAYS_SUCCEEDS(aDatabaseInfo.mThreadInfo.ThreadRef().Dispatch( - MakeAndAddRef(aDatabaseInfo, neededCheckpoint), - NS_DISPATCH_NORMAL)); + idleConnectionRunnable.forget(), NS_DISPATCH_NORMAL)); } void ConnectionPool::CloseDatabase(DatabaseInfo& aDatabaseInfo) const { @@ -8437,7 +8472,8 @@ ConnectionPool::IdleConnectionRunnable::Run() { // The connection could be null if EnsureConnection() didn't run or was not // successful in TransactionDatabaseOperationBase::RunOnConnectionThread(). if (mDatabaseInfo.mConnection) { - mDatabaseInfo.mConnection->DoIdleProcessing(mNeedsCheckpoint); + mDatabaseInfo.mConnection->DoIdleProcessing(mNeedsCheckpoint, + mInterrupted); } MOZ_ALWAYS_SUCCEEDS(owningThread->Dispatch(this, NS_DISPATCH_NORMAL)); @@ -8733,6 +8769,25 @@ ConnectionPool::IdleDatabaseInfo::~IdleDatabaseInfo() { MOZ_COUNT_DTOR(ConnectionPool::IdleDatabaseInfo); } +ConnectionPool::PerformingIdleMaintenanceDatabaseInfo:: + PerformingIdleMaintenanceDatabaseInfo( + DatabaseInfo& aDatabaseInfo, + RefPtr aIdleConnectionRunnable) + : mDatabaseInfo(WrapNotNullUnchecked(&aDatabaseInfo)), + mIdleConnectionRunnable(std::move(aIdleConnectionRunnable)) { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mIdleConnectionRunnable); + + MOZ_COUNT_CTOR(ConnectionPool::PerformingIdleMaintenanceDatabaseInfo); +} + +ConnectionPool::PerformingIdleMaintenanceDatabaseInfo:: + ~PerformingIdleMaintenanceDatabaseInfo() { + AssertIsOnBackgroundThread(); + + MOZ_COUNT_DTOR(ConnectionPool::PerformingIdleMaintenanceDatabaseInfo); +} + ConnectionPool::IdleThreadInfo::IdleThreadInfo(ThreadInfo aThreadInfo) : IdleResource(TimeStamp::NowLoRes() + TimeDuration::FromMilliseconds(kConnectionThreadIdleMS)), @@ -8915,8 +8970,10 @@ DatabaseLoggingInfo::~DatabaseLoggingInfo() { * Factory ******************************************************************************/ -Factory::Factory(RefPtr aLoggingInfo) - : mLoggingInfo(std::move(aLoggingInfo)) +Factory::Factory(RefPtr aLoggingInfo, + const nsACString& aSystemLocale) + : mSystemLocale(aSystemLocale), + mLoggingInfo(std::move(aLoggingInfo)) #ifdef DEBUG , mActorDestroyed(false) @@ -8929,7 +8986,8 @@ Factory::Factory(RefPtr aLoggingInfo) Factory::~Factory() { MOZ_ASSERT(mActorDestroyed); } // static -SafeRefPtr Factory::Create(const LoggingInfo& aLoggingInfo) { +SafeRefPtr Factory::Create(const LoggingInfo& aLoggingInfo, + const nsACString& aSystemLocale) { AssertIsOnBackgroundThread(); MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread()); @@ -8966,7 +9024,7 @@ SafeRefPtr Factory::Create(const LoggingInfo& aLoggingInfo) { return do_AddRef(entry.Data()); }); - return MakeSafeRefPtr(std::move(loggingInfo)); + return MakeSafeRefPtr(std::move(loggingInfo), aSystemLocale); } void Factory::ActorDestroy(ActorDestroyReason aWhy) { @@ -9045,6 +9103,14 @@ Factory::AllocPBackgroundIDBFactoryRequestParent( return nullptr; } + if (NS_AUUF_OR_WARN_IF( + principalInfo.type() == PrincipalInfo::TContentPrincipalInfo && + QuotaManager::IsOriginInternal( + principalInfo.get_ContentPrincipalInfo().originNoSuffix()) && + metadata.persistenceType() != PERSISTENCE_TYPE_PERSISTENT)) { + return nullptr; + } + Maybe contentParentId; uint64_t childID = BackgroundParent::GetChildID(Manager()); @@ -9087,7 +9153,7 @@ mozilla::ipc::IPCResult Factory::RecvPBackgroundIDBFactoryRequestConstructor( auto* op = static_cast(aActor); - MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(op)); + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(op)); return IPC_OK(); } @@ -12247,8 +12313,9 @@ nsresult QuotaClient::GetUsageForOriginInternal( // If this fails, it probably means we are in a serious situation. // e.g. Filesystem corruption. Will handle this in bug 1521541. QM_TRY(MOZ_TO_RESULT(RemoveDatabaseFilesAndDirectory( - *directory, subdirNameBase, nullptr, aPersistenceType, - aOriginMetadata, u""_ns)), + *directory, subdirNameBase, /* aQuotaManager */ nullptr, + aPersistenceType, aOriginMetadata, + /* aDatabaseName */ u""_ns)), Err(NS_ERROR_UNEXPECTED)); databaseFilenames.Remove(subdirNameBase); @@ -14476,10 +14543,6 @@ void FactoryOp::StringifyState(nsACString& aResult) const { aResult.AppendLiteral("Initial"); return; - case State::FinishOpen: - aResult.AppendLiteral("FinishOpen"); - return; - case State::DirectoryOpenPending: aResult.AppendLiteral("DirectoryOpenPending"); return; @@ -14538,23 +14601,50 @@ void FactoryOp::Stringify(nsACString& aResult) const { } nsresult FactoryOp::Open() { - AssertIsOnMainThread(); + AssertIsOnOwningThread(); MOZ_ASSERT(mState == State::Initial); + MOZ_ASSERT(mOriginMetadata.mOrigin.IsEmpty()); + MOZ_ASSERT(!mDirectoryLock); - if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) || - !OperationMayProceed()) { + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) || + IsActorDestroyed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } + QM_TRY(QuotaManager::EnsureCreated()); + + QuotaManager* const quotaManager = QuotaManager::Get(); + MOZ_ASSERT(quotaManager); + + const DatabaseMetadata& metadata = mCommonParams.metadata(); + + const PersistenceType persistenceType = metadata.persistenceType(); + const PrincipalInfo& principalInfo = mCommonParams.principalInfo(); + + QM_TRY_UNWRAP(auto principalMetadata, + quotaManager->GetInfoFromValidatedPrincipalInfo(principalInfo)); + + mOriginMetadata = {std::move(principalMetadata), persistenceType}; + if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) { MOZ_ASSERT(mCommonParams.metadata().persistenceType() == PERSISTENCE_TYPE_PERSISTENT); + + mEnforcingQuota = false; } else if (principalInfo.type() == PrincipalInfo::TContentPrincipalInfo) { const ContentPrincipalInfo& contentPrincipalInfo = principalInfo.get_ContentPrincipalInfo(); - if (contentPrincipalInfo.attrs().mPrivateBrowsingId != 0) { + + MOZ_ASSERT_IF( + QuotaManager::IsOriginInternal(contentPrincipalInfo.originNoSuffix()), + mCommonParams.metadata().persistenceType() == + PERSISTENCE_TYPE_PERSISTENT); + + mEnforcingQuota = persistenceType != PERSISTENCE_TYPE_PERSISTENT; + + if (mOriginMetadata.mIsPrivate) { if (StaticPrefs::dom_indexedDB_privateBrowsing_enabled()) { // Explicitly disallow moz-extension urls from using the encrypted // indexedDB storage mode when the caller is an extension (see Bug @@ -14573,35 +14663,46 @@ nsresult FactoryOp::Open() { MOZ_ASSERT(false); } - QM_TRY_INSPECT(const auto& permission, CheckPermission()); + QuotaManager::GetStorageId(persistenceType, mOriginMetadata.mOrigin, + Client::IDB, mDatabaseId); - MOZ_ASSERT(permission == PermissionValue::kPermissionAllowed || - permission == PermissionValue::kPermissionDenied); + mDatabaseId.Append('*'); + mDatabaseId.Append(NS_ConvertUTF16toUTF8(metadata.name())); - if (permission == PermissionValue::kPermissionDenied) { - return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR; - } + // Need to get database file path before opening the directory. + // XXX: For what reason? + QM_TRY_UNWRAP( + mDatabaseFilePath, + ([this, metadata, quotaManager]() -> mozilla::Result { + QM_TRY_INSPECT(const auto& dbFile, + quotaManager->GetOriginDirectory(mOriginMetadata)); - { - // These services have to be started on the main thread currently. + QM_TRY(MOZ_TO_RESULT(dbFile->Append( + NS_LITERAL_STRING_FROM_CSTRING(IDB_DIRECTORY_NAME)))); - IndexedDatabaseManager* mgr; - if (NS_WARN_IF(!(mgr = IndexedDatabaseManager::GetOrCreate()))) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } + QM_TRY(MOZ_TO_RESULT( + dbFile->Append(GetDatabaseFilenameBase(metadata.name(), + mOriginMetadata.mIsPrivate) + + kSQLiteSuffix))); - nsCOMPtr ss; - if (NS_WARN_IF(!(ss = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID)))) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - } + QM_TRY_RETURN( + MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString, dbFile, GetPath)); + }())); - MOZ_ASSERT(permission == PermissionValue::kPermissionAllowed); + // Open directory + mState = State::DirectoryOpenPending; - mState = State::FinishOpen; - MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL)); + quotaManager->OpenClientDirectory({mOriginMetadata, Client::IDB}) + ->Then( + GetCurrentSerialEventTarget(), __func__, + [self = RefPtr(this)]( + const ClientDirectoryLockPromise::ResolveOrRejectValue& aValue) { + if (aValue.IsResolve()) { + self->DirectoryLockAcquired(aValue.ResolveValue()); + } else { + self->DirectoryLockFailed(); + } + }); return NS_OK; } @@ -14724,37 +14825,6 @@ void FactoryOp::FinishSendResults() { mFactory = nullptr; } -Result FactoryOp::CheckPermission() { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mState == State::Initial); - - const PrincipalInfo& principalInfo = mCommonParams.principalInfo(); - MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo || - principalInfo.type() == PrincipalInfo::TContentPrincipalInfo); - - if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) { - MOZ_ASSERT(mState == State::Initial); - - return PermissionValue::kPermissionAllowed; - } - - QM_TRY_INSPECT( - const auto& permission, - ([persistenceType = mCommonParams.metadata().persistenceType(), - origin = QuotaManager::GetOriginFromValidatedPrincipalInfo( - principalInfo)]() -> mozilla::Result { - if (persistenceType == PERSISTENCE_TYPE_PERSISTENT) { - if (QuotaManager::IsOriginInternal(origin)) { - return PermissionValue::kPermissionAllowed; - } - return Err(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); - } - return PermissionValue::kPermissionAllowed; - })()); - - return permission; -} - nsresult FactoryOp::SendVersionChangeMessages( DatabaseActorInfo* aDatabaseActorInfo, Maybe aOpeningDatabase, uint64_t aOldVersion, const Maybe& aNewVersion) { @@ -14795,91 +14865,6 @@ nsresult FactoryOp::SendVersionChangeMessages( return NS_OK; } // namespace indexedDB -nsresult FactoryOp::FinishOpen() { - AssertIsOnOwningThread(); - MOZ_ASSERT(mState == State::FinishOpen); - MOZ_ASSERT(mOriginMetadata.mOrigin.IsEmpty()); - MOZ_ASSERT(!mDirectoryLock); - - if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) || - IsActorDestroyed()) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - QM_TRY(QuotaManager::EnsureCreated()); - - QuotaManager* const quotaManager = QuotaManager::Get(); - MOZ_ASSERT(quotaManager); - - const PrincipalInfo& principalInfo = mCommonParams.principalInfo(); - - const DatabaseMetadata& metadata = mCommonParams.metadata(); - - const PersistenceType persistenceType = metadata.persistenceType(); - - if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) { - mOriginMetadata = {QuotaManager::GetInfoForChrome(), persistenceType}; - - MOZ_ASSERT(QuotaManager::IsOriginInternal(mOriginMetadata.mOrigin)); - - mEnforcingQuota = false; - } else { - MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo); - - QM_TRY_UNWRAP( - auto principalMetadata, - quotaManager->GetInfoFromValidatedPrincipalInfo(principalInfo)); - - mOriginMetadata = {std::move(principalMetadata), persistenceType}; - - mEnforcingQuota = persistenceType != PERSISTENCE_TYPE_PERSISTENT; - } - - QuotaManager::GetStorageId(persistenceType, mOriginMetadata.mOrigin, - Client::IDB, mDatabaseId); - - mDatabaseId.Append('*'); - mDatabaseId.Append(NS_ConvertUTF16toUTF8(metadata.name())); - - // Need to get database file path before opening the directory. - // XXX: For what reason? - QM_TRY_UNWRAP( - mDatabaseFilePath, - ([this, metadata, quotaManager]() -> mozilla::Result { - QM_TRY_INSPECT(const auto& dbFile, - quotaManager->GetOriginDirectory(mOriginMetadata)); - - QM_TRY(MOZ_TO_RESULT(dbFile->Append( - NS_LITERAL_STRING_FROM_CSTRING(IDB_DIRECTORY_NAME)))); - - QM_TRY(MOZ_TO_RESULT( - dbFile->Append(GetDatabaseFilenameBase(metadata.name(), - mOriginMetadata.mIsPrivate) + - kSQLiteSuffix))); - - QM_TRY_RETURN( - MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString, dbFile, GetPath)); - }())); - - // Open directory - mState = State::DirectoryOpenPending; - - quotaManager->OpenClientDirectory({mOriginMetadata, Client::IDB}) - ->Then( - GetCurrentSerialEventTarget(), __func__, - [self = RefPtr(this)]( - const ClientDirectoryLockPromise::ResolveOrRejectValue& aValue) { - if (aValue.IsResolve()) { - self->DirectoryLockAcquired(aValue.ResolveValue()); - } else { - self->DirectoryLockFailed(); - } - }); - - return NS_OK; -} - bool FactoryOp::MustWaitFor(const FactoryOp& aExistingOp) { AssertIsOnOwningThread(); @@ -14920,10 +14905,6 @@ FactoryOp::Run() { QM_WARNONLY_TRY(MOZ_TO_RESULT(Open()), handleError); break; - case State::FinishOpen: - QM_WARNONLY_TRY(MOZ_TO_RESULT(FinishOpen()), handleError); - break; - case State::DatabaseOpenPending: QM_WARNONLY_TRY(MOZ_TO_RESULT(DatabaseOpen()), handleError); break; @@ -15328,7 +15309,7 @@ nsresult OpenDatabaseOp::LoadDatabaseInformation( QM_TRY_INSPECT( const auto& lastIndexId, - ([&aConnection, + ([this, &aConnection, &objectStores]() -> mozilla::Result { // Load index information QM_TRY_INSPECT( @@ -15345,7 +15326,7 @@ nsresult OpenDatabaseOp::LoadDatabaseInformation( QM_TRY(CollectWhileHasResult( *stmt, - [&lastIndexId, &objectStores, &aConnection, + [this, &lastIndexId, &objectStores, &aConnection, usedIds = Maybe>{}, usedNames = Maybe>{}]( auto& stmt) mutable -> mozilla::Result { @@ -15436,8 +15417,7 @@ nsresult OpenDatabaseOp::LoadDatabaseInformation( indexMetadata->mCommonMetadata.locale(); const bool& isAutoLocale = indexMetadata->mCommonMetadata.autoLocale(); - const nsCString& systemLocale = - IndexedDatabaseManager::GetLocale(); + const nsCString& systemLocale = mFactory->GetSystemLocale(); if (!systemLocale.IsEmpty() && isAutoLocale && !indexedLocale.Equals(systemLocale)) { QM_TRY(MOZ_TO_RESULT(UpdateLocaleAwareIndex( @@ -16985,7 +16965,8 @@ TransactionBase::CommitOp::Run() { connection->FinishWriteTransaction(); if (mTransaction->GetMode() == IDBTransaction::Mode::Cleanup) { - connection->DoIdleProcessing(/* aNeedsCheckpoint */ true); + connection->DoIdleProcessing(/* aNeedsCheckpoint */ true, + /* aInterrupted */ Atomic(false)); connection->EnableQuotaChecks(); } diff --git a/dom/indexedDB/ActorsParent.h b/dom/indexedDB/ActorsParent.h index ab6c6c142f..45c0bf8688 100644 --- a/dom/indexedDB/ActorsParent.h +++ b/dom/indexedDB/ActorsParent.h @@ -38,10 +38,11 @@ class PBackgroundIDBFactoryParent; class PBackgroundIndexedDBUtilsParent; already_AddRefed AllocPBackgroundIDBFactoryParent( - const LoggingInfo& aLoggingInfo); + const LoggingInfo& aLoggingInfo, const nsACString& aSystemLocale); bool RecvPBackgroundIDBFactoryConstructor(PBackgroundIDBFactoryParent* aActor, - const LoggingInfo& aLoggingInfo); + const LoggingInfo& aLoggingInfo, + const nsACString& aSystemLocale); bool DeallocPBackgroundIDBFactoryParent(PBackgroundIDBFactoryParent* aActor); diff --git a/dom/indexedDB/ActorsParentCommon.cpp b/dom/indexedDB/ActorsParentCommon.cpp index 637906b6d3..1b92e15dad 100644 --- a/dom/indexedDB/ActorsParentCommon.cpp +++ b/dom/indexedDB/ActorsParentCommon.cpp @@ -728,7 +728,7 @@ nsresult ExecuteSimpleSQLSequence(mozIStorageConnection& aConnection, Span aSQLCommands) { for (const auto& aSQLCommand : aSQLCommands) { const auto extraInfo = quota::ScopedLogExtraInfo{ - quota::ScopedLogExtraInfo::kTagQuery, aSQLCommand}; + quota::ScopedLogExtraInfo::kTagQueryTainted, aSQLCommand}; QM_TRY(MOZ_TO_RESULT(aConnection.ExecuteSimpleSQL(aSQLCommand))); } diff --git a/dom/indexedDB/IDBFactory.cpp b/dom/indexedDB/IDBFactory.cpp index be34e1914d..81dce07d7e 100644 --- a/dom/indexedDB/IDBFactory.cpp +++ b/dom/indexedDB/IDBFactory.cpp @@ -242,6 +242,12 @@ Result, nsresult> IDBFactory::CreateForMainThreadJSInternal( return Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); } + nsresult rv = mgr->EnsureLocale(); + if (NS_WARN_IF(NS_FAILED(rv))) { + IDB_REPORT_INTERNAL_ERR(); + return Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + }; + return CreateInternal(aGlobal, std::move(aPrincipalInfo), /* aInnerWindowID */ 0); } @@ -283,10 +289,17 @@ nsresult IDBFactory::AllowedForWindowInternal( MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aWindow); - if (NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate())) { + IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetOrCreate(); + if (NS_WARN_IF(!mgr)) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } + nsresult rv = mgr->EnsureLocale(); + if (NS_WARN_IF(NS_FAILED(rv))) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + }; + StorageAccess access = StorageAllowedForWindow(aWindow); // the factory callsite records whether the browser is in private browsing. @@ -341,10 +354,16 @@ bool IDBFactory::AllowedForPrincipal(nsIPrincipal* aPrincipal, MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aPrincipal); - if (NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate())) { + IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetOrCreate(); + if (NS_WARN_IF(!mgr)) { return false; } + nsresult rv = mgr->EnsureLocale(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + }; + if (aPrincipal->IsSystemPrincipal()) { if (aIsSystemPrincipal) { *aIsSystemPrincipal = true; @@ -573,19 +592,20 @@ RefPtr IDBFactory::OpenInternal( PersistenceType persistenceType; - bool isInternal = principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo; - if (!isInternal && + bool isPersistent = + principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo; + if (!isPersistent && principalInfo.type() == PrincipalInfo::TContentPrincipalInfo) { nsCString origin = principalInfo.get_ContentPrincipalInfo().originNoSuffix(); - isInternal = QuotaManager::IsOriginInternal(origin); + isPersistent = QuotaManager::IsOriginInternal(origin); } const bool isPrivate = principalInfo.type() == PrincipalInfo::TContentPrincipalInfo && principalInfo.get_ContentPrincipalInfo().attrs().mPrivateBrowsingId > 0; - if (isInternal) { + if (isPersistent) { // Chrome privilege and internal origins always get persistent storage. persistenceType = PERSISTENCE_TYPE_PERSISTENT; } else if (isPrivate) { @@ -641,7 +661,8 @@ RefPtr IDBFactory::OpenInternal( mBackgroundActor = static_cast( backgroundActor->SendPBackgroundIDBFactoryConstructor( - actor, idbThreadLocal->GetLoggingInfo())); + actor, idbThreadLocal->GetLoggingInfo(), + IndexedDatabaseManager::GetLocale())); if (NS_WARN_IF(!mBackgroundActor)) { mBackgroundActorFailed = true; diff --git a/dom/indexedDB/IndexedDatabaseManager.cpp b/dom/indexedDB/IndexedDatabaseManager.cpp index 5ee16ec5e9..0558c38826 100644 --- a/dom/indexedDB/IndexedDatabaseManager.cpp +++ b/dom/indexedDB/IndexedDatabaseManager.cpp @@ -296,25 +296,6 @@ nsresult IndexedDatabaseManager::Init() { Preferences::RegisterCallbackAndCall(MaxPreloadExtraRecordsPrefChangeCallback, kPrefMaxPreloadExtraRecords); - nsAutoCString acceptLang; - Preferences::GetLocalizedCString("intl.accept_languages", acceptLang); - - // Split values on commas. - for (const auto& lang : - nsCCharSeparatedTokenizer(acceptLang, ',').ToRange()) { - mozilla::intl::LocaleCanonicalizer::Vector asciiString{}; - auto result = mozilla::intl::LocaleCanonicalizer::CanonicalizeICULevel1( - PromiseFlatCString(lang).get(), asciiString); - if (result.isOk()) { - mLocale.AssignASCII(asciiString); - break; - } - } - - if (mLocale.IsEmpty()) { - mLocale.AssignLiteral("en_US"); - } - return NS_OK; } @@ -652,11 +633,37 @@ void IndexedDatabaseManager::LoggingModePrefChangedCallback( } } +nsresult IndexedDatabaseManager::EnsureLocale() { + MOZ_ASSERT(NS_IsMainThread()); + + nsAutoCString acceptLang; + Preferences::GetLocalizedCString("intl.accept_languages", acceptLang); + + // Split values on commas. + for (const auto& lang : + nsCCharSeparatedTokenizer(acceptLang, ',').ToRange()) { + mozilla::intl::LocaleCanonicalizer::Vector asciiString{}; + auto result = mozilla::intl::LocaleCanonicalizer::CanonicalizeICULevel1( + PromiseFlatCString(lang).get(), asciiString); + if (result.isOk()) { + mLocale.AssignASCII(asciiString); + break; + } + } + + if (mLocale.IsEmpty()) { + mLocale.AssignLiteral("en_US"); + } + + return NS_OK; +} + // static const nsCString& IndexedDatabaseManager::GetLocale() { IndexedDatabaseManager* idbManager = Get(); MOZ_ASSERT(idbManager, "IDBManager is not ready!"); + MOZ_ASSERT(!idbManager->mLocale.IsEmpty()); return idbManager->mLocale; } diff --git a/dom/indexedDB/IndexedDatabaseManager.h b/dom/indexedDB/IndexedDatabaseManager.h index 5bf6485aff..0766143dd6 100644 --- a/dom/indexedDB/IndexedDatabaseManager.h +++ b/dom/indexedDB/IndexedDatabaseManager.h @@ -123,6 +123,9 @@ class IndexedDatabaseManager final { nsresult FlushPendingFileDeletions(); + // XXX This extra explicit initialization should go away with bug 1730706. + nsresult EnsureLocale(); + static const nsCString& GetLocale(); static bool ResolveSandboxBinding(JSContext* aCx); diff --git a/dom/indexedDB/SchemaUpgrades.cpp b/dom/indexedDB/SchemaUpgrades.cpp index 6066390740..234c4c9f04 100644 --- a/dom/indexedDB/SchemaUpgrades.cpp +++ b/dom/indexedDB/SchemaUpgrades.cpp @@ -2861,8 +2861,10 @@ nsresult UpgradeFileIdsFunction::Init(nsIFile* aFMDirectory, // purpose is to store file ids without adding more complexity or code // duplication. auto fileManager = MakeSafeRefPtr( - PERSISTENCE_TYPE_INVALID, quota::OriginMetadata{}, u""_ns, ""_ns, false, - false); + PERSISTENCE_TYPE_INVALID, quota::OriginMetadata{}, + /* aDatabaseName */ u""_ns, /* aDatabaseID */ ""_ns, + /* aEnforcingQuota */ false, + /* aIsInPrivateBrowsingMode */ false); nsresult rv = fileManager->Init(aFMDirectory, aConnection); if (NS_WARN_IF(NS_FAILED(rv))) { diff --git a/dom/indexedDB/SerializationHelpers.h b/dom/indexedDB/SerializationHelpers.h index 7bc05720d4..488c1eb044 100644 --- a/dom/indexedDB/SerializationHelpers.h +++ b/dom/indexedDB/SerializationHelpers.h @@ -10,6 +10,7 @@ #include "ipc/EnumSerializer.h" #include "ipc/IPCMessageUtilsSpecializations.h" +#include "mozilla/dom/BindingIPCUtils.h" #include "mozilla/dom/indexedDB/Key.h" #include "mozilla/dom/indexedDB/KeyPath.h" #include "mozilla/dom/IDBCursor.h" @@ -61,10 +62,8 @@ struct ParamTraits { template <> struct ParamTraits - : public ContiguousEnumSerializer< - mozilla::dom::IDBCursor::Direction, - mozilla::dom::IDBCursor::Direction::Next, - mozilla::dom::IDBCursor::Direction::EndGuard_> {}; + : public mozilla::dom::WebIDLEnumSerializer< + mozilla::dom::IDBCursor::Direction> {}; template <> struct ParamTraits diff --git a/dom/indexedDB/test/abort_on_reload.html b/dom/indexedDB/test/abort_on_reload.html index 4e4fe3a339..7b3cba6d95 100644 --- a/dom/indexedDB/test/abort_on_reload.html +++ b/dom/indexedDB/test/abort_on_reload.html @@ -3,7 +3,7 @@