зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1754448 - Add basic testing for snapshot re-using; r=dom-storage-reviewers,webidl,asuth,jari,ipc-reviewers,mccr8
Differential Revision: https://phabricator.services.mozilla.com/D138405
This commit is contained in:
Родитель
3bf75dd4f0
Коммит
01ad68bec6
|
@ -1580,10 +1580,10 @@ class Datastore final
|
|||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Mutation Methods
|
||||
//
|
||||
// These are only called during Snapshot::RecvAsyncCheckpoint
|
||||
// These are only called during Snapshot::Checkpoint
|
||||
|
||||
/**
|
||||
* Used by Snapshot::RecvAsyncCheckpoint to set a key/value pair as part of an
|
||||
* Used by Snapshot::Checkpoint to set a key/value pair as part of an
|
||||
* explicit batch.
|
||||
*/
|
||||
void SetItem(Database* aDatabase, const nsString& aKey,
|
||||
|
@ -2023,6 +2023,11 @@ class Snapshot final : public PBackgroundLSSnapshotParent {
|
|||
// Reference counted.
|
||||
~Snapshot();
|
||||
|
||||
mozilla::ipc::IPCResult Checkpoint(nsTArray<LSWriteInfo>&& aWriteInfos);
|
||||
|
||||
mozilla::ipc::IPCResult CheckpointAndNotify(
|
||||
nsTArray<LSWriteAndNotifyInfo>&& aWriteAndNotifyInfos);
|
||||
|
||||
void Finish();
|
||||
|
||||
// IPDL methods are only called by IPDL.
|
||||
|
@ -2036,6 +2041,12 @@ class Snapshot final : public PBackgroundLSSnapshotParent {
|
|||
mozilla::ipc::IPCResult RecvAsyncCheckpointAndNotify(
|
||||
nsTArray<LSWriteAndNotifyInfo>&& aWriteAndNotifyInfos) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvSyncCheckpoint(
|
||||
nsTArray<LSWriteInfo>&& aWriteInfos) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvSyncCheckpointAndNotify(
|
||||
nsTArray<LSWriteAndNotifyInfo>&& aWriteAndNotifyInfos) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvAsyncFinish() override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvSyncFinish() override;
|
||||
|
@ -2456,6 +2467,19 @@ class PreloadedOp : public LSSimpleRequestBase {
|
|||
void GetResponse(LSSimpleRequestResponse& aResponse) override;
|
||||
};
|
||||
|
||||
class GetStateOp : public LSSimpleRequestBase {
|
||||
nsCString mOrigin;
|
||||
|
||||
public:
|
||||
GetStateOp(const LSSimpleRequestParams& aParams,
|
||||
const Maybe<ContentParentId>& aContentParentId);
|
||||
|
||||
private:
|
||||
nsresult Start() override;
|
||||
|
||||
void GetResponse(LSSimpleRequestResponse& aResponse) override;
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
* Other class declarations
|
||||
******************************************************************************/
|
||||
|
@ -3468,6 +3492,14 @@ PBackgroundLSSimpleRequestParent* AllocPBackgroundLSSimpleRequestParent(
|
|||
break;
|
||||
}
|
||||
|
||||
case LSSimpleRequestParams::TLSSimpleRequestGetStateParams: {
|
||||
RefPtr<GetStateOp> getStateOp = new GetStateOp(aParams, contentParentId);
|
||||
|
||||
actor = std::move(getStateOp);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
MOZ_CRASH("Should never get here!");
|
||||
}
|
||||
|
@ -5696,7 +5728,7 @@ mozilla::ipc::IPCResult Snapshot::RecvDeleteMe() {
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult Snapshot::RecvAsyncCheckpoint(
|
||||
mozilla::ipc::IPCResult Snapshot::Checkpoint(
|
||||
nsTArray<LSWriteInfo>&& aWriteInfos) {
|
||||
AssertIsOnBackgroundThread();
|
||||
// Don't assert `mUsage >= 0`, it can be negative when multiple snapshots are
|
||||
|
@ -5749,7 +5781,7 @@ mozilla::ipc::IPCResult Snapshot::RecvAsyncCheckpoint(
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult Snapshot::RecvAsyncCheckpointAndNotify(
|
||||
mozilla::ipc::IPCResult Snapshot::CheckpointAndNotify(
|
||||
nsTArray<LSWriteAndNotifyInfo>&& aWriteAndNotifyInfos) {
|
||||
AssertIsOnBackgroundThread();
|
||||
// Don't assert `mUsage >= 0`, it can be negative when multiple snapshots are
|
||||
|
@ -5816,6 +5848,26 @@ mozilla::ipc::IPCResult Snapshot::RecvAsyncCheckpointAndNotify(
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult Snapshot::RecvAsyncCheckpoint(
|
||||
nsTArray<LSWriteInfo>&& aWriteInfos) {
|
||||
return Checkpoint(std::move(aWriteInfos));
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult Snapshot::RecvAsyncCheckpointAndNotify(
|
||||
nsTArray<LSWriteAndNotifyInfo>&& aWriteAndNotifyInfos) {
|
||||
return CheckpointAndNotify(std::move(aWriteAndNotifyInfos));
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult Snapshot::RecvSyncCheckpoint(
|
||||
nsTArray<LSWriteInfo>&& aWriteInfos) {
|
||||
return Checkpoint(std::move(aWriteInfos));
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult Snapshot::RecvSyncCheckpointAndNotify(
|
||||
nsTArray<LSWriteAndNotifyInfo>&& aWriteAndNotifyInfos) {
|
||||
return CheckpointAndNotify(std::move(aWriteAndNotifyInfos));
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult Snapshot::RecvAsyncFinish() {
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
|
@ -7889,6 +7941,18 @@ bool LSSimpleRequestBase::VerifyRequestParams() {
|
|||
break;
|
||||
}
|
||||
|
||||
case LSSimpleRequestParams::TLSSimpleRequestGetStateParams: {
|
||||
const LSSimpleRequestGetStateParams& params =
|
||||
mParams.get_LSSimpleRequestGetStateParams();
|
||||
|
||||
if (NS_WARN_IF(!VerifyPrincipalInfo(
|
||||
params.principalInfo(), params.storagePrincipalInfo(), false))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
MOZ_CRASH("Should never get here!");
|
||||
}
|
||||
|
@ -8043,6 +8107,60 @@ void PreloadedOp::GetResponse(LSSimpleRequestResponse& aResponse) {
|
|||
aResponse = preloadedResponse;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* GetStateOp
|
||||
******************************************************************************/
|
||||
|
||||
GetStateOp::GetStateOp(const LSSimpleRequestParams& aParams,
|
||||
const Maybe<ContentParentId>& aContentParentId)
|
||||
: LSSimpleRequestBase(aParams, aContentParentId) {
|
||||
MOZ_ASSERT(aParams.type() ==
|
||||
LSSimpleRequestParams::TLSSimpleRequestGetStateParams);
|
||||
}
|
||||
|
||||
nsresult GetStateOp::Start() {
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(mState == State::StartingRequest);
|
||||
MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
|
||||
MOZ_ASSERT(MayProceed());
|
||||
|
||||
const LSSimpleRequestGetStateParams& params =
|
||||
mParams.get_LSSimpleRequestGetStateParams();
|
||||
|
||||
const PrincipalInfo& storagePrincipalInfo = params.storagePrincipalInfo();
|
||||
|
||||
MOZ_ASSERT(
|
||||
storagePrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo ||
|
||||
storagePrincipalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
|
||||
mOrigin = storagePrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo
|
||||
? nsCString{QuotaManager::GetOriginForChrome()}
|
||||
: QuotaManager::GetOriginFromValidatedPrincipalInfo(
|
||||
storagePrincipalInfo);
|
||||
|
||||
mState = State::SendingResults;
|
||||
MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void GetStateOp::GetResponse(LSSimpleRequestResponse& aResponse) {
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(mState == State::SendingResults);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(ResultCode()));
|
||||
MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
|
||||
MOZ_ASSERT(MayProceed());
|
||||
|
||||
LSSimpleRequestGetStateResponse getStateResponse;
|
||||
|
||||
if (RefPtr<Datastore> datastore = GetDatastore(mOrigin)) {
|
||||
if (!datastore->IsClosed()) {
|
||||
getStateResponse.itemInfos() = datastore->GetOrderedItems().Clone();
|
||||
}
|
||||
}
|
||||
|
||||
aResponse = getStateResponse;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* ArchivedOriginScope
|
||||
******************************************************************************/
|
||||
|
|
|
@ -300,6 +300,21 @@ nsresult LSDatabase::BeginExplicitSnapshot(LSObject* aObject) {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult LSDatabase::CheckpointExplicitSnapshot() {
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(mActor);
|
||||
MOZ_ASSERT(!mAllowedToClose);
|
||||
MOZ_ASSERT(mSnapshot);
|
||||
MOZ_ASSERT(mSnapshot->Explicit());
|
||||
|
||||
nsresult rv = mSnapshot->ExplicitCheckpoint();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult LSDatabase::EndExplicitSnapshot() {
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(mActor);
|
||||
|
@ -307,7 +322,7 @@ nsresult LSDatabase::EndExplicitSnapshot() {
|
|||
MOZ_ASSERT(mSnapshot);
|
||||
MOZ_ASSERT(mSnapshot->Explicit());
|
||||
|
||||
nsresult rv = mSnapshot->End();
|
||||
nsresult rv = mSnapshot->ExplicitEnd();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
|
|
@ -84,6 +84,8 @@ class LSDatabase final {
|
|||
|
||||
nsresult BeginExplicitSnapshot(LSObject* aObject);
|
||||
|
||||
nsresult CheckpointExplicitSnapshot();
|
||||
|
||||
nsresult EndExplicitSnapshot();
|
||||
|
||||
bool HasSnapshot() const;
|
||||
|
|
|
@ -835,6 +835,29 @@ void LSObject::BeginExplicitSnapshot(nsIPrincipal& aSubjectPrincipal,
|
|||
mInExplicitSnapshot = true;
|
||||
}
|
||||
|
||||
void LSObject::CheckpointExplicitSnapshot(nsIPrincipal& aSubjectPrincipal,
|
||||
ErrorResult& aError) {
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
if (!CanUseStorage(aSubjectPrincipal)) {
|
||||
aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mInExplicitSnapshot) {
|
||||
aError.Throw(NS_ERROR_NOT_INITIALIZED);
|
||||
return;
|
||||
}
|
||||
|
||||
AssertExplicitSnapshotInvariants(*this);
|
||||
|
||||
nsresult rv = mDatabase->CheckpointExplicitSnapshot();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aError.Throw(rv);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void LSObject::EndExplicitSnapshot(nsIPrincipal& aSubjectPrincipal,
|
||||
ErrorResult& aError) {
|
||||
AssertIsOnOwningThread();
|
||||
|
|
|
@ -179,6 +179,9 @@ class LSObject final : public Storage {
|
|||
void BeginExplicitSnapshot(nsIPrincipal& aSubjectPrincipal,
|
||||
ErrorResult& aError) override;
|
||||
|
||||
void CheckpointExplicitSnapshot(nsIPrincipal& aSubjectPrincipal,
|
||||
ErrorResult& aError) override;
|
||||
|
||||
void EndExplicitSnapshot(nsIPrincipal& aSubjectPrincipal,
|
||||
ErrorResult& aError) override;
|
||||
|
||||
|
|
|
@ -567,7 +567,24 @@ void LSSnapshot::MarkDirty() {
|
|||
}
|
||||
}
|
||||
|
||||
nsresult LSSnapshot::End() {
|
||||
nsresult LSSnapshot::ExplicitCheckpoint() {
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(mActor);
|
||||
MOZ_ASSERT(mExplicit);
|
||||
MOZ_ASSERT(!mHasPendingStableStateCallback);
|
||||
MOZ_ASSERT(!mHasPendingIdleTimerCallback);
|
||||
MOZ_ASSERT(mInitialized);
|
||||
MOZ_ASSERT(!mSentFinish);
|
||||
|
||||
nsresult rv = Checkpoint(/* aSync */ true);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult LSSnapshot::ExplicitEnd() {
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(mActor);
|
||||
MOZ_ASSERT(mExplicit);
|
||||
|
@ -920,7 +937,7 @@ nsresult LSSnapshot::UpdateUsage(int64_t aDelta) {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult LSSnapshot::Checkpoint() {
|
||||
nsresult LSSnapshot::Checkpoint(bool aSync) {
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(mActor);
|
||||
MOZ_ASSERT(mInitialized);
|
||||
|
@ -930,8 +947,13 @@ nsresult LSSnapshot::Checkpoint() {
|
|||
MOZ_ASSERT(mWriteAndNotifyInfos);
|
||||
|
||||
if (!mWriteAndNotifyInfos->IsEmpty()) {
|
||||
MOZ_ALWAYS_TRUE(
|
||||
mActor->SendAsyncCheckpointAndNotify(*mWriteAndNotifyInfos));
|
||||
if (aSync) {
|
||||
MOZ_ALWAYS_TRUE(
|
||||
mActor->SendSyncCheckpointAndNotify(*mWriteAndNotifyInfos));
|
||||
} else {
|
||||
MOZ_ALWAYS_TRUE(
|
||||
mActor->SendAsyncCheckpointAndNotify(*mWriteAndNotifyInfos));
|
||||
}
|
||||
|
||||
mWriteAndNotifyInfos->Clear();
|
||||
}
|
||||
|
@ -944,7 +966,11 @@ nsresult LSSnapshot::Checkpoint() {
|
|||
|
||||
MOZ_ASSERT(!writeInfos.IsEmpty());
|
||||
|
||||
MOZ_ALWAYS_TRUE(mActor->SendAsyncCheckpoint(writeInfos));
|
||||
if (aSync) {
|
||||
MOZ_ALWAYS_TRUE(mActor->SendSyncCheckpoint(writeInfos));
|
||||
} else {
|
||||
MOZ_ALWAYS_TRUE(mActor->SendAsyncCheckpoint(writeInfos));
|
||||
}
|
||||
|
||||
mWriteOptimizer->Reset();
|
||||
}
|
||||
|
@ -1035,7 +1061,7 @@ LSSnapshot::Run() {
|
|||
if (mDirty || mHasOtherProcessDatabases ||
|
||||
!Preferences::GetBool("dom.storage.snapshot_reusing")) {
|
||||
MOZ_ALWAYS_SUCCEEDS(Finish());
|
||||
} else if (!mExplicit) {
|
||||
} else {
|
||||
MOZ_ASSERT(mIdleTimer);
|
||||
|
||||
MOZ_ALWAYS_SUCCEEDS(mIdleTimer->InitWithNamedFuncCallback(
|
||||
|
|
|
@ -156,7 +156,9 @@ class LSSnapshot final : public nsIRunnable {
|
|||
|
||||
void MarkDirty();
|
||||
|
||||
nsresult End();
|
||||
nsresult ExplicitCheckpoint();
|
||||
|
||||
nsresult ExplicitEnd();
|
||||
|
||||
int64_t GetUsage() const;
|
||||
|
||||
|
@ -175,7 +177,7 @@ class LSSnapshot final : public nsIRunnable {
|
|||
|
||||
nsresult UpdateUsage(int64_t aDelta);
|
||||
|
||||
nsresult Checkpoint();
|
||||
nsresult Checkpoint(bool aSync = false);
|
||||
|
||||
nsresult Finish(bool aSync = false);
|
||||
|
||||
|
|
|
@ -148,6 +148,8 @@ class SimpleRequestResolver final : public LSSimpleRequestChildCallback {
|
|||
|
||||
void HandleResponse(bool aResponse);
|
||||
|
||||
void HandleResponse(const nsTArray<LSItemInfo>& aResponse);
|
||||
|
||||
// LSRequestChildCallback
|
||||
void OnResponse(const LSSimpleRequestResponse& aResponse) override;
|
||||
};
|
||||
|
@ -385,6 +387,37 @@ LocalStorageManager2::IsPreloaded(nsIPrincipal* aPrincipal, JSContext* aContext,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
LocalStorageManager2::GetState(nsIPrincipal* aPrincipal, JSContext* aContext,
|
||||
Promise** _retval) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aPrincipal);
|
||||
MOZ_ASSERT(_retval);
|
||||
|
||||
RefPtr<Promise> promise;
|
||||
nsresult rv = CreatePromise(aContext, getter_AddRefs(promise));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
LSSimpleRequestGetStateParams params;
|
||||
|
||||
rv = CheckedPrincipalToPrincipalInfo(aPrincipal, params.principalInfo());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
params.storagePrincipalInfo() = params.principalInfo();
|
||||
|
||||
rv = StartSimpleRequest(promise, params);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
promise.forget(_retval);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
LSRequestChild* LocalStorageManager2::StartRequest(
|
||||
const LSRequestParams& aParams, LSRequestChildCallback* aCallback) {
|
||||
AssertIsOnDOMFileThread();
|
||||
|
@ -565,6 +598,42 @@ void SimpleRequestResolver::HandleResponse(bool aResponse) {
|
|||
mPromise->MaybeResolve(aResponse);
|
||||
}
|
||||
|
||||
[[nodiscard]] static bool ToJSValue(JSContext* aCx,
|
||||
const nsTArray<LSItemInfo>& aArgument,
|
||||
JS::MutableHandle<JS::Value> aValue) {
|
||||
JS::RootedObject obj(aCx, JS_NewPlainObject(aCx));
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < aArgument.Length(); ++i) {
|
||||
const LSItemInfo& itemInfo = aArgument[i];
|
||||
|
||||
const nsString& key = itemInfo.key();
|
||||
|
||||
JS::RootedValue value(aCx);
|
||||
if (!ToJSValue(aCx, itemInfo.value().AsString(), &value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!JS_DefineUCProperty(aCx, obj, key.BeginReading(), key.Length(), value,
|
||||
JSPROP_ENUMERATE)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
aValue.setObject(*obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SimpleRequestResolver::HandleResponse(
|
||||
const nsTArray<LSItemInfo>& aResponse) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mPromise);
|
||||
|
||||
mPromise->MaybeResolve(aResponse);
|
||||
}
|
||||
|
||||
void SimpleRequestResolver::OnResponse(
|
||||
const LSSimpleRequestResponse& aResponse) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
@ -579,6 +648,11 @@ void SimpleRequestResolver::OnResponse(
|
|||
aResponse.get_LSSimpleRequestPreloadedResponse().preloaded());
|
||||
break;
|
||||
|
||||
case LSSimpleRequestResponse::TLSSimpleRequestGetStateResponse:
|
||||
HandleResponse(
|
||||
aResponse.get_LSSimpleRequestGetStateResponse().itemInfos());
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_CRASH("Unknown response type!");
|
||||
}
|
||||
|
|
|
@ -68,9 +68,16 @@ struct LSSimpleRequestPreloadedParams
|
|||
PrincipalInfo storagePrincipalInfo;
|
||||
};
|
||||
|
||||
struct LSSimpleRequestGetStateParams
|
||||
{
|
||||
PrincipalInfo principalInfo;
|
||||
PrincipalInfo storagePrincipalInfo;
|
||||
};
|
||||
|
||||
union LSSimpleRequestParams
|
||||
{
|
||||
LSSimpleRequestPreloadedParams;
|
||||
LSSimpleRequestGetStateParams;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
include protocol PBackground;
|
||||
|
||||
include PBackgroundLSSharedTypes;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
|
@ -16,6 +18,11 @@ struct LSSimpleRequestPreloadedResponse
|
|||
bool preloaded;
|
||||
};
|
||||
|
||||
struct LSSimpleRequestGetStateResponse
|
||||
{
|
||||
LSItemInfo[] itemInfos;
|
||||
};
|
||||
|
||||
/**
|
||||
* Discriminated union which can contain an error code (`nsresult`) or
|
||||
* particular simple request response.
|
||||
|
@ -24,6 +31,7 @@ union LSSimpleRequestResponse
|
|||
{
|
||||
nsresult;
|
||||
LSSimpleRequestPreloadedResponse;
|
||||
LSSimpleRequestGetStateResponse;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -75,6 +75,14 @@ parent:
|
|||
|
||||
async AsyncCheckpointAndNotify(LSWriteAndNotifyInfo[] writeAndNotifyInfos);
|
||||
|
||||
// A synchronous checkpoint. This should only be used by the snapshotting code
|
||||
// to checkpoint an explicit snapshot.
|
||||
sync SyncCheckpoint(LSWriteInfo[] writeInfos);
|
||||
|
||||
// A synchronous checkpoint and notify. This should only be used by the
|
||||
// snapshotting code to checkpoint and notify an explicit snapshot.
|
||||
sync SyncCheckpointAndNotify(LSWriteAndNotifyInfo[] writeAndNotifyInfos);
|
||||
|
||||
async AsyncFinish();
|
||||
|
||||
// A synchronous finish. This should only be used by the snapshotting code to
|
||||
|
|
|
@ -32,4 +32,7 @@ interface nsILocalStorageManager : nsISupports
|
|||
|
||||
[implicit_jscontext]
|
||||
Promise isPreloaded(in nsIPrincipal aPrincipal);
|
||||
|
||||
[implicit_jscontext]
|
||||
Promise getState(in nsIPrincipal aPrincipal);
|
||||
};
|
||||
|
|
|
@ -318,6 +318,16 @@ LocalStorageManager::IsPreloaded(nsIPrincipal* aPrincipal, JSContext* aContext,
|
|||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
LocalStorageManager::GetState(nsIPrincipal* aPrincipal, JSContext* aContext,
|
||||
Promise** _retval) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aPrincipal);
|
||||
MOZ_ASSERT(_retval);
|
||||
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
void LocalStorageManager::ClearCaches(uint32_t aUnloadFlags,
|
||||
const OriginAttributesPattern& aPattern,
|
||||
const nsACString& aOriginScope) {
|
||||
|
|
|
@ -118,6 +118,9 @@ class Storage : public nsISupports, public nsWrapperCache {
|
|||
virtual void BeginExplicitSnapshot(nsIPrincipal& aSubjectPrincipal,
|
||||
ErrorResult& aRv) {}
|
||||
|
||||
virtual void CheckpointExplicitSnapshot(nsIPrincipal& aSubjectPrincipal,
|
||||
ErrorResult& aRv) {}
|
||||
|
||||
virtual void EndExplicitSnapshot(nsIPrincipal& aSubjectPrincipal,
|
||||
ErrorResult& aRv) {}
|
||||
|
||||
|
|
|
@ -71,6 +71,12 @@ async function beginExplicitSnapshot(knownTab) {
|
|||
});
|
||||
}
|
||||
|
||||
async function checkpointExplicitSnapshot(knownTab) {
|
||||
await SpecialPowers.spawn(knownTab.tab.linkedBrowser, [], function() {
|
||||
return content.wrappedJSObject.checkpointExplicitSnapshot();
|
||||
});
|
||||
}
|
||||
|
||||
async function endExplicitSnapshot(knownTab) {
|
||||
await SpecialPowers.spawn(knownTab.tab.linkedBrowser, [], function() {
|
||||
return content.wrappedJSObject.endExplicitSnapshot();
|
||||
|
@ -99,6 +105,24 @@ async function verifySnapshotUsage(knownTab, expectedSnapshotUsage) {
|
|||
is(snapshotUsage, expectedSnapshotUsage, "Correct snapshot usage");
|
||||
}
|
||||
|
||||
async function verifyParentState(expectedState) {
|
||||
let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
|
||||
HELPER_PAGE_ORIGIN
|
||||
);
|
||||
|
||||
let actualState = await Services.domStorageManager.getState(principal);
|
||||
|
||||
for (let [expectedKey, expectedValue] of Object.entries(expectedState)) {
|
||||
ok(actualState.hasOwnProperty(expectedKey), "key present: " + expectedKey);
|
||||
is(actualState[expectedKey], expectedValue, "value correct");
|
||||
}
|
||||
for (let actualKey of Object.keys(actualState)) {
|
||||
if (!expectedState.hasOwnProperty(actualKey)) {
|
||||
ok(false, "actual state has key it shouldn't have: " + actualKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We spin up a ton of child processes.
|
||||
requestLongerTimeout(4);
|
||||
|
||||
|
@ -648,3 +672,84 @@ add_task(async function() {
|
|||
|
||||
clearOriginStorageEnsuringNoPreload(HELPER_PAGE_ORIGIN);
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify that datastore in the parent is correctly updated after a checkpoint.
|
||||
*/
|
||||
add_task(async function() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
// Force multiple web and webIsolated content processes so that the
|
||||
// multi-e10s logic works correctly.
|
||||
["dom.ipc.processCount", 4],
|
||||
["dom.ipc.processCount.webIsolated", 2],
|
||||
// Enable LocalStorage's testing API so we can explicitly create
|
||||
// snapshots when needed.
|
||||
["dom.storage.testing", true],
|
||||
],
|
||||
});
|
||||
|
||||
// Ensure that there is no localstorage data by forcing the origin to be
|
||||
// cleared prior to the start of our test.
|
||||
await clearOriginStorageEnsuringNoPreload(HELPER_PAGE_ORIGIN);
|
||||
|
||||
// Open tabs. Don't configure any of them yet.
|
||||
const knownTabs = new KnownTabs();
|
||||
const writerTab1 = await openTestTab(
|
||||
HELPER_PAGE_URL,
|
||||
"writer1",
|
||||
knownTabs,
|
||||
true
|
||||
);
|
||||
|
||||
await verifyParentState({});
|
||||
|
||||
// Apply the initial mutation using an explicit snapshot. The explicit
|
||||
// snapshot here ensures that the parent process have received the changes.
|
||||
await beginExplicitSnapshot(writerTab1);
|
||||
await verifyParentState({});
|
||||
await applyMutations(writerTab1, [["key", "something"]]);
|
||||
await verifyParentState({});
|
||||
await endExplicitSnapshot(writerTab1);
|
||||
|
||||
await verifyParentState({ key: "something" });
|
||||
|
||||
// Begin an explicit snapshot in writerTab1, apply the first mutation in
|
||||
// writerTab1 and checkpoint the explicit snapshot.
|
||||
await beginExplicitSnapshot(writerTab1);
|
||||
await verifyParentState({ key: "something" });
|
||||
await applyMutations(writerTab1, [["key", "somethingBigger"]]);
|
||||
await verifyParentState({ key: "something" });
|
||||
await checkpointExplicitSnapshot(writerTab1);
|
||||
|
||||
await verifyParentState({ key: "somethingBigger" });
|
||||
|
||||
// Apply the second mutation in writerTab1 and checkpoint the explicit
|
||||
// snapshot.
|
||||
await applyMutations(writerTab1, [["key", null]]);
|
||||
await verifyParentState({ key: "somethingBigger" });
|
||||
await checkpointExplicitSnapshot(writerTab1);
|
||||
|
||||
await verifyParentState({});
|
||||
|
||||
// Apply the third mutation in writerTab1 and end the explicit snapshot.
|
||||
await applyMutations(writerTab1, [["otherKey", "something"]]);
|
||||
await verifyParentState({});
|
||||
await endExplicitSnapshot(writerTab1);
|
||||
|
||||
await verifyParentState({ otherKey: "something" });
|
||||
|
||||
// Verify the final state, it should match the state after the third mutation
|
||||
// has been applied and "commited". An explicit snapshot is used.
|
||||
await beginExplicitSnapshot(writerTab1);
|
||||
await verifyParentState({ otherKey: "something" });
|
||||
await verifyState(writerTab1, { otherKey: "something" });
|
||||
await endExplicitSnapshot(writerTab1);
|
||||
|
||||
await verifyParentState({ otherKey: "something" });
|
||||
|
||||
// Clean up.
|
||||
await cleanupTabs(knownTabs);
|
||||
|
||||
clearOriginStorageEnsuringNoPreload(HELPER_PAGE_ORIGIN);
|
||||
});
|
||||
|
|
|
@ -46,6 +46,10 @@ function beginExplicitSnapshot() {
|
|||
localStorage.beginExplicitSnapshot();
|
||||
}
|
||||
|
||||
function checkpointExplicitSnapshot() {
|
||||
localStorage.checkpointExplicitSnapshot();
|
||||
}
|
||||
|
||||
function endExplicitSnapshot() {
|
||||
localStorage.endExplicitSnapshot();
|
||||
}
|
||||
|
|
|
@ -65,6 +65,15 @@ partial interface Storage {
|
|||
[Throws, NeedsSubjectPrincipal, Pref="dom.storage.testing"]
|
||||
void beginExplicitSnapshot();
|
||||
|
||||
/**
|
||||
* Checkpoints the explicitly begun snapshot. This is only useful for testing
|
||||
* of snapshot re-using when multiple checkpoints are involved. There's no
|
||||
* need to call this before `endExplicitSnapshot` because it checkpoints the
|
||||
* snapshot before it's ended.
|
||||
*/
|
||||
[Throws, NeedsSubjectPrincipal, Pref="dom.storage.testing"]
|
||||
void checkpointExplicitSnapshot();
|
||||
|
||||
/**
|
||||
* Ends the explicitly begun snapshot and retains the underlying database.
|
||||
* Compare with `close` which also drops the reference to the database.
|
||||
|
|
|
@ -854,6 +854,10 @@ description = legacy sync IPC - please add detailed description
|
|||
description = legacy sync IPC - please add detailed description
|
||||
[PBackgroundLSDatabase::PBackgroundLSSnapshot]
|
||||
description = See corresponding comment in PBackgroundLSDatabase.ipdl
|
||||
[PBackgroundLSSnapshot::SyncCheckpoint]
|
||||
description = See corresponding comment in PBackgroundLSSnapshot.ipdl
|
||||
[PBackgroundLSSnapshot::SyncCheckpointAndNotify]
|
||||
description = See corresponding comment in PBackgroundLSSnapshot.ipdl
|
||||
[PBackgroundLSSnapshot::SyncFinish]
|
||||
description = See corresponding comment in PBackgroundLSSnapshot.ipdl
|
||||
[PBackgroundLSSnapshot::LoadValueAndMoreItems]
|
||||
|
|
Загрузка…
Ссылка в новой задаче