mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
Bug 1914599 - Fix a potential race between OpenDatabaseOp and GetDatabasesOp; r=dom-storage-reviewers,jari a=RyanVM
Differential Revision: https://phabricator.services.mozilla.com/D224463
This commit is contained in:
parent
e9fd0f9f25
commit
8bd20bdeea
@ -3002,10 +3002,15 @@ class FactoryOp
|
||||
|
||||
// Waiting to do/doing work on the "work thread". This involves waiting for
|
||||
// the VersionChangeOp (OpenDatabaseOp and DeleteDatabaseOp each have a
|
||||
// different implementation) to do its work. Eventually the state will
|
||||
// transition to SendingResults.
|
||||
// different implementation) to do its work. If the VersionChangeOp is
|
||||
// OpenDatabaseOp and it succeeded then the next state is
|
||||
// DatabaseWorkVersionUpdate. Otherwise the next step is SendingResults.
|
||||
DatabaseWorkVersionChange,
|
||||
|
||||
// Waiting to do/doing finalization work on the QuotaManager IO thread.
|
||||
// Eventually the state will transition to SendingResults.
|
||||
DatabaseWorkVersionUpdate,
|
||||
|
||||
// Waiting to send/sending results on the PBackground thread. Next step is
|
||||
// Completed.
|
||||
SendingResults,
|
||||
@ -3127,6 +3132,8 @@ class FactoryOp
|
||||
|
||||
virtual nsresult DispatchToWorkThread() = 0;
|
||||
|
||||
virtual nsresult DoVersionUpdate() = 0;
|
||||
|
||||
// Should only be called by Run().
|
||||
virtual void SendResults() = 0;
|
||||
|
||||
@ -3206,6 +3213,8 @@ class OpenDatabaseOp final : public FactoryRequestOp {
|
||||
// cycles.
|
||||
VersionChangeOp* mVersionChangeOp;
|
||||
|
||||
MoveOnlyFunction<void()> mCompleteCallback;
|
||||
|
||||
uint32_t mTelemetryId;
|
||||
|
||||
public:
|
||||
@ -3250,6 +3259,8 @@ class OpenDatabaseOp final : public FactoryRequestOp {
|
||||
|
||||
nsresult DispatchToWorkThread() override;
|
||||
|
||||
nsresult DoVersionUpdate() override;
|
||||
|
||||
void SendResults() override;
|
||||
|
||||
static nsresult UpdateLocaleAwareIndex(mozIStorageConnection& aConnection,
|
||||
@ -3321,6 +3332,8 @@ class DeleteDatabaseOp final : public FactoryRequestOp {
|
||||
|
||||
nsresult DispatchToWorkThread() override;
|
||||
|
||||
nsresult DoVersionUpdate() override;
|
||||
|
||||
void SendResults() override;
|
||||
};
|
||||
|
||||
@ -3381,6 +3394,8 @@ class GetDatabasesOp final : public FactoryOp {
|
||||
|
||||
nsresult DispatchToWorkThread() override;
|
||||
|
||||
nsresult DoVersionUpdate() override;
|
||||
|
||||
void SendResults() override;
|
||||
};
|
||||
|
||||
@ -10815,6 +10830,7 @@ void VersionChangeTransaction::UpdateMetadata(nsresult aResult) {
|
||||
void VersionChangeTransaction::SendCompleteNotification(nsresult aResult) {
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(mOpenDatabaseOp);
|
||||
MOZ_ASSERT(!mOpenDatabaseOp->mCompleteCallback);
|
||||
MOZ_ASSERT_IF(!mActorWasAlive, mOpenDatabaseOp->HasFailed());
|
||||
MOZ_ASSERT_IF(!mActorWasAlive, mOpenDatabaseOp->mState >
|
||||
OpenDatabaseOp::State::SendingResults);
|
||||
@ -10825,21 +10841,40 @@ void VersionChangeTransaction::SendCompleteNotification(nsresult aResult) {
|
||||
return;
|
||||
}
|
||||
|
||||
openDatabaseOp->mCompleteCallback =
|
||||
[self = SafeRefPtr{this, AcquireStrongRefFromRawPtr{}}, aResult]() {
|
||||
if (!self->IsActorDestroyed()) {
|
||||
Unused << self->SendComplete(aResult);
|
||||
}
|
||||
};
|
||||
|
||||
auto handleError = [openDatabaseOp](const nsresult rv) {
|
||||
openDatabaseOp->SetFailureCodeIfUnset(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
|
||||
|
||||
openDatabaseOp->mState = OpenDatabaseOp::State::SendingResults;
|
||||
|
||||
MOZ_ALWAYS_SUCCEEDS(openDatabaseOp->Run());
|
||||
};
|
||||
|
||||
if (NS_FAILED(aResult)) {
|
||||
// 3.3.1 Opening a database:
|
||||
// "If the upgrade transaction was aborted, run the steps for closing a
|
||||
// database connection with connection, create and return a new AbortError
|
||||
// exception and abort these steps."
|
||||
openDatabaseOp->SetFailureCodeIfUnset(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
|
||||
handleError(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
openDatabaseOp->mState = OpenDatabaseOp::State::SendingResults;
|
||||
openDatabaseOp->mState = OpenDatabaseOp::State::DatabaseWorkVersionUpdate;
|
||||
|
||||
if (!IsActorDestroyed()) {
|
||||
Unused << SendComplete(aResult);
|
||||
}
|
||||
QuotaManager* const quotaManager = QuotaManager::Get();
|
||||
MOZ_ASSERT(quotaManager);
|
||||
|
||||
MOZ_ALWAYS_SUCCEEDS(openDatabaseOp->Run());
|
||||
QM_TRY(MOZ_TO_RESULT(quotaManager->IOThread()->Dispatch(openDatabaseOp,
|
||||
NS_DISPATCH_NORMAL))
|
||||
.mapErr(
|
||||
[](const auto) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; }),
|
||||
QM_VOID, handleError);
|
||||
}
|
||||
|
||||
void VersionChangeTransaction::ActorDestroy(ActorDestroyReason aWhy) {
|
||||
@ -11652,7 +11687,20 @@ DatabaseFileManager::DatabaseFileManager(
|
||||
mEnforcingQuota(aEnforcingQuota),
|
||||
mIsInPrivateBrowsingMode(aIsInPrivateBrowsingMode) {}
|
||||
|
||||
uint64_t DatabaseFileManager::DatabaseVersion() const {
|
||||
AssertIsOnIOThread();
|
||||
|
||||
return mDatabaseVersion;
|
||||
}
|
||||
|
||||
void DatabaseFileManager::UpdateDatabaseVersion(uint64_t aDatabaseVersion) {
|
||||
AssertIsOnIOThread();
|
||||
|
||||
mDatabaseVersion = aDatabaseVersion;
|
||||
}
|
||||
|
||||
nsresult DatabaseFileManager::Init(nsIFile* aDirectory,
|
||||
const uint64_t aDatabaseVersion,
|
||||
mozIStorageConnection& aConnection) {
|
||||
AssertIsOnIOThread();
|
||||
MOZ_ASSERT(aDirectory);
|
||||
@ -11687,6 +11735,8 @@ nsresult DatabaseFileManager::Init(nsIFile* aDirectory,
|
||||
mJournalDirectoryPath.init(std::move(path));
|
||||
}
|
||||
|
||||
mDatabaseVersion = aDatabaseVersion;
|
||||
|
||||
QM_TRY_INSPECT(const auto& stmt,
|
||||
MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
|
||||
nsCOMPtr<mozIStorageStatement>, aConnection,
|
||||
@ -15049,6 +15099,10 @@ FactoryOp::Run() {
|
||||
QM_WARNONLY_TRY(MOZ_TO_RESULT(DispatchToWorkThread()), handleError);
|
||||
break;
|
||||
|
||||
case State::DatabaseWorkVersionUpdate:
|
||||
QM_WARNONLY_TRY(MOZ_TO_RESULT(DoVersionUpdate()), handleError);
|
||||
break;
|
||||
|
||||
case State::SendingResults:
|
||||
SendResults();
|
||||
break;
|
||||
@ -15294,7 +15348,8 @@ nsresult OpenDatabaseOp::DoDatabaseWork() {
|
||||
NS_ERROR_DOM_INDEXEDDB_VERSION_ERR);
|
||||
|
||||
if (!fileManager->Initialized()) {
|
||||
QM_TRY(MOZ_TO_RESULT(fileManager->Init(fmDirectory, *connection)));
|
||||
QM_TRY(MOZ_TO_RESULT(fileManager->Init(
|
||||
fmDirectory, mMetadata->mCommonMetadata.version(), *connection)));
|
||||
|
||||
idm->AddFileManager(fileManager.clonePtr());
|
||||
}
|
||||
@ -15832,12 +15887,40 @@ nsresult OpenDatabaseOp::SendUpgradeNeeded() {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult OpenDatabaseOp::DoVersionUpdate() {
|
||||
AssertIsOnIOThread();
|
||||
MOZ_ASSERT(mState == State::DatabaseWorkVersionUpdate);
|
||||
MOZ_ASSERT(!HasFailed());
|
||||
|
||||
AUTO_PROFILER_LABEL("OpenDatabaseOp::DoVersionUpdate", DOM);
|
||||
|
||||
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
|
||||
!OperationMayProceed()) {
|
||||
IDB_REPORT_INTERNAL_ERR();
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
mFileManager->UpdateDatabaseVersion(mRequestedVersion);
|
||||
|
||||
mState = State::SendingResults;
|
||||
|
||||
QM_TRY(MOZ_TO_RESULT(
|
||||
DispatchThisAfterProcessingCurrentEvent(mOwningEventTarget)));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void OpenDatabaseOp::SendResults() {
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(mState == State::SendingResults);
|
||||
MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
|
||||
MOZ_ASSERT_IF(!HasFailed(), !mVersionChangeTransaction);
|
||||
|
||||
if (mCompleteCallback) {
|
||||
auto completeCallback = std::move(mCompleteCallback);
|
||||
completeCallback();
|
||||
}
|
||||
|
||||
DebugOnly<DatabaseActorInfo*> info = nullptr;
|
||||
MOZ_ASSERT_IF(mDatabaseId.isSome() && gLiveDatabaseHashtable &&
|
||||
gLiveDatabaseHashtable->Get(mDatabaseId.ref(), &info),
|
||||
@ -15860,8 +15943,6 @@ void OpenDatabaseOp::SendResults() {
|
||||
// need to update the version in our metadata.
|
||||
mMetadata->mCommonMetadata.version() = mRequestedVersion;
|
||||
|
||||
mFileManager->UpdateDatabaseVersion(mRequestedVersion);
|
||||
|
||||
nsresult rv = EnsureDatabaseActorIsAlive();
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// We successfully opened a database so use its actor as the success
|
||||
@ -16483,6 +16564,10 @@ void DeleteDatabaseOp::SendBlockedNotification() {
|
||||
}
|
||||
}
|
||||
|
||||
nsresult DeleteDatabaseOp::DoVersionUpdate() {
|
||||
MOZ_CRASH("Not implemented because this should be unreachable.");
|
||||
}
|
||||
|
||||
void DeleteDatabaseOp::SendResults() {
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(mState == State::SendingResults);
|
||||
@ -16853,6 +16938,10 @@ nsresult GetDatabasesOp::DispatchToWorkThread() {
|
||||
MOZ_CRASH("Not implemented because this should be unreachable.");
|
||||
}
|
||||
|
||||
nsresult GetDatabasesOp::DoVersionUpdate() {
|
||||
MOZ_CRASH("Not implemented because this should be unreachable.");
|
||||
}
|
||||
|
||||
void GetDatabasesOp::SendResults() {
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(mState == State::SendingResults);
|
||||
|
@ -89,11 +89,9 @@ class DatabaseFileManager final
|
||||
|
||||
const nsAString& DatabaseFilePath() const { return mDatabaseFilePath; }
|
||||
|
||||
uint64_t DatabaseVersion() const { return mDatabaseVersion; }
|
||||
uint64_t DatabaseVersion() const;
|
||||
|
||||
void UpdateDatabaseVersion(uint64_t aDatabaseVersion) {
|
||||
mDatabaseVersion = aDatabaseVersion;
|
||||
}
|
||||
void UpdateDatabaseVersion(uint64_t aDatabaseVersion);
|
||||
|
||||
IndexedDBCipherKeyManager& MutableCipherKeyManagerRef() const {
|
||||
MOZ_ASSERT(mIsInPrivateBrowsingMode);
|
||||
@ -108,7 +106,8 @@ class DatabaseFileManager final
|
||||
|
||||
bool Initialized() const { return mInitialized; }
|
||||
|
||||
nsresult Init(nsIFile* aDirectory, mozIStorageConnection& aConnection);
|
||||
nsresult Init(nsIFile* aDirectory, const uint64_t aDatabaseVersion,
|
||||
mozIStorageConnection& aConnection);
|
||||
|
||||
[[nodiscard]] nsCOMPtr<nsIFile> GetDirectory();
|
||||
|
||||
|
@ -2866,7 +2866,8 @@ nsresult UpgradeFileIdsFunction::Init(nsIFile* aFMDirectory,
|
||||
/* aDatabaseFilePath */ u""_ns, /* aEnforcingQuota */ false,
|
||||
/* aIsInPrivateBrowsingMode */ false);
|
||||
|
||||
nsresult rv = fileManager->Init(aFMDirectory, aConnection);
|
||||
nsresult rv =
|
||||
fileManager->Init(aFMDirectory, /* aDatabaseVersion */ 0, aConnection);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user