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:
Jan Varga 2022-03-09 09:43:09 +00:00
Родитель 3bf75dd4f0
Коммит 01ad68bec6
18 изменённых файлов: 437 добавлений и 13 удалений

Просмотреть файл

@ -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]