Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test gets skipped with pattern: inc_origin_init
- This test runs only with pattern: !condprof
- Manifest: dom/quota/test/xpcshell/xpcshell.toml
/**
* Any copyright is dedicated to the Public Domain.
*/
const { PrefUtils } = ChromeUtils.importESModule(
);
const { PrincipalUtils } = ChromeUtils.importESModule(
);
const { QuotaUtils } = ChromeUtils.importESModule(
);
const { SimpleDBUtils } = ChromeUtils.importESModule(
);
/**
* This test exercises the clearing of non-persisted zero-usage origins during
* temporary storage initialization. It verifies that:
*
* - When the pref
* `dom.quotaManager.temporaryStorage.clearNonPersistedZeroUsageOrigins`
* is enabled, origins that are:
* - non-persisted,
* - have zero quota-charged usage,
* - and have not been used recently (older than one week)
* are cleared in batches.
*
* - When the pref is disabled, no such origins are cleared and all remain
* present on disk.
*
* The test creates a set of origins with different combinations of:
* persisted / non-persisted,
* zero / non-zero,
* recent / old access time.
*
* After initializing and shutting down storage multiple times, the test checks
* that only the expected origins remain. Batch size is controlled via
* `dom.quotaManager.temporaryStorage.maxOriginsToClearDuringCleanup`.
*/
/**
* Runs a callback while temporarily setting the initial access time offset.
* This allows simulation of "older" origins without waiting in real time.
*/
async function withArtificialAccessTime(offset, callback) {
const prefs = [
[
"dom.quotaManager.temporaryStorage.initialOriginAccessTimeOffsetSec",
offset,
],
// Disable updates so the artificial time persists.
["dom.quotaManager.temporaryStorage.updateOriginAccessTime", false],
];
const originalPrefs = PrefUtils.getPrefs(prefs);
try {
PrefUtils.setPrefs(prefs);
await callback();
} finally {
PrefUtils.setPrefs(originalPrefs);
}
}
/**
* Main test function. Creates a set of origins with varying attributes
* (persisted, zero, recent), then verifies whether cleanup during temporary
* storage initialization behaves as expected depending on the
* `clearingEnabled` flag and `maxOriginsToClear` parameter.
*/
async function testNonPersistedZeroUsageOriginsClearing(
clearingEnabled,
maxOriginsToClear
) {
/* prettier-ignore */
const originInfos = [
{ url: "https://alpha.io", persisted: false, zero: false, recent: false },
{ url: "https://beta.io", persisted: false, zero: false, recent: true },
{ url: "https://gamma.io", persisted: false, zero: true, recent: true },
{ url: "https://delta.io", persisted: true, zero: false, recent: false },
{ url: "https://epsilon.io", persisted: true, zero: false, recent: true },
{ url: "https://zeta.io", persisted: true, zero: true, recent: false },
{ url: "https://eta.io", persisted: true, zero: true, recent: true },
// Three non-persisted, zero-usage, non-recent origins, candidates for
// cleanup.
{ url: "https://omega1.io", persisted: false, zero: true, recent: false },
{ url: "https://omega2.io", persisted: false, zero: true, recent: false },
{ url: "https://omega3.io", persisted: false, zero: true, recent: false },
];
// If clearing is enabled, only persisted, non-zero-usage, or recently used
// origins should remain after cleanup.
// If disabled, all origins are expected to remain.
const expectedOriginInfos = clearingEnabled
? originInfos.filter(info => info.persisted || !info.zero || info.recent)
: originInfos;
const expectedOrigins = expectedOriginInfos.map(info => info.url);
// Threshold in seconds used to distinguish recent from non-recent origins.
// Set to 8 days to ensure origins are safely beyond the 7-day cutoff used
// by quota manager for temporary storage cleanup.
const nonRecentThresholdSec = 8 * 86400;
const name = "test_testNonPersistedZeroUsageOriginsClearing";
info("Initializing storage");
{
const request = Services.qms.init();
await QuotaUtils.requestFinished(request);
}
info("Initializing temporary storage");
{
const request = Services.qms.initTemporaryStorage();
await QuotaUtils.requestFinished(request);
}
// Create origins with the desired persisted, usage, and recency attributes.
for (const originInfo of originInfos) {
const principal = PrincipalUtils.createPrincipal(originInfo.url);
// Simulate access time: 0s offset = recent, 8 days offset = non-recent.
const offset = originInfo.recent ? 0 : nonRecentThresholdSec;
await withArtificialAccessTime(offset, async function () {
info("Initializing temporary origin");
{
const request = Services.qms.initializeTemporaryOrigin(
"default",
principal,
/* aCreateIfNonExistent */ true
);
await QuotaUtils.requestFinished(request);
if (originInfo.persisted) {
const request = Services.qms.persist(principal);
await QuotaUtils.requestFinished(request);
}
}
info("Creating database");
{
const connection = SimpleDBUtils.createConnection(principal);
const openRequest = connection.open(name);
await SimpleDBUtils.requestFinished(openRequest);
if (!originInfo.zero) {
const writeRequest = connection.write(new ArrayBuffer(1));
await SimpleDBUtils.requestFinished(writeRequest);
}
}
info("Shutting down temporary origin");
{
const request = Services.qms.resetStoragesForPrincipal(
principal,
"default"
);
await QuotaUtils.requestFinished(request);
}
});
}
info("Shutting down storage");
{
const request = Services.qms.reset();
await QuotaUtils.requestFinished(request);
}
info("Listing origins");
let lastOrigins = await (async function () {
const request = Services.qms.listOrigins();
return QuotaUtils.requestFinished(request);
})();
let lastDelta;
// Repeatedly initialize temporary storage to trigger cleanup batches.
for (let index = 0; index < originInfos.length; index++) {
info("Initializing storage");
{
const request = Services.qms.init();
await QuotaUtils.requestFinished(request);
}
info("Initializing temporary storage");
{
const request = Services.qms.initTemporaryStorage();
await QuotaUtils.requestFinished(request);
}
info("Shutting down storage");
{
const request = Services.qms.reset();
await QuotaUtils.requestFinished(request);
}
info("Listing origins");
const origins = await (async function () {
const request = Services.qms.listOrigins();
return QuotaUtils.requestFinished(request);
})();
info("Verifying number of origins");
Assert.lessOrEqual(origins.length, lastOrigins.length);
const delta = lastOrigins.length - origins.length;
if (lastDelta === 0) {
// Once no more origins are cleared, cleanup must stay stable.
Assert.equal(delta, 0, "Correct delta");
} else {
// Allow either no change or a batch of maxOriginsToClear cleared.
Assert.greaterOrEqual(delta, 0, "Correct delta");
Assert.lessOrEqual(delta, maxOriginsToClear, "Correct delta");
}
lastOrigins = origins;
lastDelta = delta;
}
info("Verifying origins");
Assert.equal(
lastOrigins.length,
expectedOrigins.length,
"Correct number of origins"
);
lastOrigins.sort();
expectedOrigins.sort();
for (let index = 0; index < lastOrigins.length; index++) {
Assert.equal(
lastOrigins[index],
expectedOrigins[index],
`Origin at index ${index} matches`
);
}
}
/**
* Three runs:
* 1. With clearing enabled and batch size = 2: up to two non-persisted,
* zero-usage, non-recent origins are cleared per initialization.
* 2. With clearing enabled and batch size = 1: up to one non-persisted,
* zero-usage, non-recent origin is cleared per initialization.
* 3. With clearing disabled: all origins remain intact to ensure no
* unintended removals occur.
*/
async function testSteps() {
add_task(
{
pref_set: [
[
"dom.quotaManager.temporaryStorage.clearNonPersistedZeroUsageOrigins",
true,
],
["dom.quotaManager.temporaryStorage.maxOriginsToClearDuringCleanup", 2],
],
},
async function () {
await testNonPersistedZeroUsageOriginsClearing(
/* clearingEnabled */ true,
/* maxOriginsToClear */ 2
);
}
);
add_task(
{
pref_set: [
[
"dom.quotaManager.temporaryStorage.clearNonPersistedZeroUsageOrigins",
true,
],
["dom.quotaManager.temporaryStorage.maxOriginsToClearDuringCleanup", 1],
],
},
async function () {
await testNonPersistedZeroUsageOriginsClearing(
/* clearingEnabled */ true,
/* maxOriginsToClear */ 1
);
}
);
add_task(
{
pref_set: [
[
"dom.quotaManager.temporaryStorage.clearNonPersistedZeroUsageOrigins",
false,
],
],
},
async function () {
await testNonPersistedZeroUsageOriginsClearing(
/* clearingEnabled */ false,
/* maxOriginsToClear */ 0
);
}
);
}