Bug 1722502 - P4 Update the test for quota usage mitigation. r=dom-worker-reviewers,asuth

Depends on D122189

Differential Revision: https://phabricator.services.mozilla.com/D122190
This commit is contained in:
Eden Chuang 2021-08-30 09:24:32 +00:00
Родитель 432ced9589
Коммит 902453ab34
3 изменённых файлов: 71 добавлений и 9 удалений

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

@ -25,6 +25,11 @@ const SWM = Cc["@mozilla.org/serviceworkers/manager;1"].getService(
Ci.nsIServiceWorkerManager
);
// The expected minimum usage for an origin that has any Cache API storage in
// use. Currently, the DB uses a page size of 4k and a minimum growth size of
// 32k and has enough tables/indices for this to round up to 64k.
const kMinimumOriginUsageBytes = 65536;
function getPrincipal(url, attrs) {
const uri = Services.io.newURI(url);
if (!attrs) {
@ -47,6 +52,13 @@ async function _qm_requestFinished(request) {
return request.result;
}
async function qm_reset_storage() {
return new Promise(resolve => {
let request = Services.qms.reset();
request.callback = resolve;
});
}
async function get_qm_origin_usage(origin) {
return new Promise(resolve => {
const principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
@ -70,7 +82,7 @@ async function clear_qm_origin_group_via_clearData(origin) {
// Initiate group clearing and wait for it.
await new Promise((resolve, reject) => {
Services.clearData.deleteDataFromHost(
Services.clearData.deleteDataFromBaseDomain(
baseDomain,
false,
Services.clearData.CLEAR_DOM_QUOTA,
@ -165,6 +177,12 @@ async function consume_storage(origin, storageDesc) {
);
}
// Check if the origin is effectively empty, but allowing for the minimum size
// Cache API database to be present.
function is_minimum_origin_usage(originUsageBytes) {
return originUsageBytes <= kMinimumOriginUsageBytes;
}
/**
* Perform a navigation, waiting until the navigation stops, then returning
* the `textContent` of the body node. The expectation is this will be used
@ -253,6 +271,23 @@ function waitForUnregister(scope) {
});
}
// Be careful using this helper function, please make sure QuotaUsageCheck must
// happen, otherwise test would be stucked in this function.
function waitForQuotaUsageCheckFinish(scope) {
return new Promise(function(resolve) {
let listener = {
onQuotaUsageCheckFinish(registration) {
if (registration.scope !== scope) {
return;
}
SWM.removeListener(listener);
resolve(registration);
},
};
SWM.addListener(listener);
});
}
function waitForServiceWorkerRegistrationChange(registration, callback) {
return new Promise(function(resolve) {
let listener = {

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

@ -104,8 +104,8 @@ const TEST_SW_SETUP = {
};
const TEST_STORAGE_SETUP = {
cacheBytes: 4 * 1024 * 1024,
idbBytes: 4 * 1024 * 1024,
cacheBytes: 4 * 1024 * 1024, // 4 MiB
idbBytes: 4 * 1024 * 1024, // 4 MiB
};
const FAULTS_BEFORE_MITIGATION = 3;
@ -139,7 +139,7 @@ async function do_fault_injection_test({
// ## Generate quota usage if appropriate
if (consumeQuotaOrigin) {
await consume_storage(consumeQuotaOrigin, TEST_SW_SETUP);
await consume_storage(consumeQuotaOrigin, TEST_STORAGE_SETUP);
}
// ## Verify normal navigation is served by the SW.
@ -164,13 +164,17 @@ async function do_fault_injection_test({
// we expect it happens after navigation fault threshold reached.
const unregisteredPromise = waitForUnregister(reg.scope);
// Make sure the test is listening on the finish of quota checking, since we
// expect it happens after navigation fault threshold reached.
const quotaUsageCheckFinishPromise = waitForQuotaUsageCheckFinish(reg.scope);
// ## Inject faults in a loop until expected mitigation.
sw.testingInjectCancellation = useError;
for (let iFault = 0; iFault < FAULTS_BEFORE_MITIGATION; iFault++) {
info(`## Testing with injected fault number ${iFault + 1}`);
// We should never have triggered an origin quota usage check before the
// final fault injection.
is(reg.quotaUsageCheckCount, 0, "No quota usage check yet.");
is(reg.quotaUsageCheckCount, 0, "No quota usage check yet");
// Make sure our loads encode the specific
const debugTag = `err=${name}&fault=${iFault + 1}`;
@ -194,17 +198,23 @@ async function do_fault_injection_test({
}
await unregisteredPromise;
is(reg.unregistered, true, "registration should not exist.");
is(reg.unregistered, true, "registration should be unregistered");
//is(reg.quotaUsageCheckCount, 1, "Quota usage check must be started");
await quotaUsageCheckFinishPromise;
if (consumeQuotaOrigin) {
// Check that there is no longer any storage usaged by the origin in this
// case.
const originUsage = await get_qm_origin_usage(TEST_ORIGIN);
ok(originUsage > 0, "origin should still have usage until mitigated");
ok(
is_minimum_origin_usage(originUsage),
"origin usage should be mitigated"
);
if (consumeQuotaOrigin === SAME_GROUP_ORIGIN) {
const sameGroupUsage = await get_qm_origin_usage(SAME_GROUP_ORIGIN);
ok(sameGroupUsage > 0, "same group should still have usage for now");
ok(sameGroupUsage === 0, "same group usage should be mitigated");
}
}
}
@ -216,6 +226,8 @@ add_task(async function test_navigation_fetch_fault_handling() {
["dom.serviceWorkers.exemptFromPerDomainMax", true],
["dom.serviceWorkers.testing.enabled", true],
["dom.serviceWorkers.mitigations.bypass_on_fault", true],
["dom.serviceWorkers.mitigations.group_usage_headroom_kb", 5 * 1024],
["dom.quotaManager.testing", true],
// We want the temporary global limit to be 10 MiB (the pref is in KiB).
// This will result in the group limit also being 10 MiB because on small
// disks we provide a group limit value of min(10 MiB, global limit).
@ -223,6 +235,10 @@ add_task(async function test_navigation_fetch_fault_handling() {
],
});
// Need to reset the storages to make dom.quotaManager.temporaryStorage.fixedLimit
// works.
await qm_reset_storage();
const quotaOriginVariations = [
// Don't put us near the storage limit.
undefined,

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

@ -94,7 +94,18 @@ async function unregisterAll() {
*/
function makeRandomBlob(size) {
const arr = new Uint8Array(size);
window.crypto.getRandomValues(arr);
let offset = 0;
/**
* getRandomValues will only provide a maximum of 64k of data at a time and
* will error if we ask for more, so using a while loop for get a random value
* which much larger than 64k.
* https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues#exceptions
*/
while (offset < size) {
const nextSize = Math.min(size - offset, 65536);
window.crypto.getRandomValues(new Uint8Array(arr.buffer, offset, nextSize));
offset += nextSize;
}
return new Blob([arr], { type: "application/octet-stream" });
}