Bug 1252998 - Fix sanitize-offlineData test failures, move SW utility functions to SiteDataTestUtils.jsm. r=baku

This patch fixes a bunch of intermittent/perma failures in sanitize-offlineData.js by:

- Ignoring localStorage for now. LocalStorage is cleared by sending an
  observer notification. The flush often happens after several seconds, heavily
  interfering with our own test or with subsequent tests. We can not reliably wait
  on the operation to finish. Waiting for "domstorage-test-flushed" after calling
  Sanitizer.sanitize() fixes the problem, but that notification is intermittently
  not triggered for other unknown reasons, which makes it not really viable to use.

- Creating and checking indexedDB data in the chrome process (using SiteDataTestUtils).

- Cleaning up after running the test.

- Ignoring a stray NS_ERROR_ABORT that's hard to track down and doesn't seem to
  do any damage right now.

I've also moved the ServiceWorker utility functions into SiteDataTestUtils,
which we're planning to use in all other browser tests that handle site data.
This commit is contained in:
Johann Hofmann 2018-04-11 16:53:50 +02:00
parent dade560913
commit 035f3a408e
4 changed files with 113 additions and 124 deletions

View File

@ -4,9 +4,17 @@ var EXPORTED_SYMBOLS = [
"SiteDataTestUtils",
];
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://testing-common/ContentTask.jsm");
ChromeUtils.import("resource://testing-common/BrowserTestUtils.jsm");
const {Sanitizer} = ChromeUtils.import("resource:///modules/Sanitizer.jsm", {});
XPCOMUtils.defineLazyServiceGetter(this, "swm",
"@mozilla.org/serviceworkers/manager;1",
"nsIServiceWorkerManager");
/**
* This module assists with tasks around testing functionality that shows
* or clears site data.
@ -20,10 +28,12 @@ var SiteDataTestUtils = {
* Adds a new entry to a dummy indexedDB database for the specified origin.
*
* @param {String} origin - the origin of the site to add test data for
* @param {String} name [optional] - the entry key
* @param {String} value [optional] - the entry value
*
* @returns a Promise that resolves when the data was added successfully.
*/
addToIndexedDB(origin) {
addToIndexedDB(origin, key = "foo", value = "bar") {
return new Promise(resolve => {
let uri = Services.io.newURI(origin);
let principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
@ -37,7 +47,7 @@ var SiteDataTestUtils = {
let tx = db.transaction("TestStore", "readwrite");
let store = tx.objectStore("TestStore");
tx.oncomplete = resolve;
store.put({ id: performance.now().toString(), description: "IndexedDB Test"});
store.put({ id: key, description: value});
};
});
},
@ -46,15 +56,60 @@ var SiteDataTestUtils = {
* Adds a new cookie for the specified origin, with the specified contents.
* The cookie will be valid for one day.
*
* @param {String} name - the cookie name
* @param {String} value - the cookie value
* @param {String} origin - the origin of the site to add test data for
* @param {String} name [optional] - the cookie name
* @param {String} value [optional] - the cookie value
*/
addToCookies(origin, name, value) {
addToCookies(origin, name = "foo", value = "bar") {
let uri = Services.io.newURI(origin);
Services.cookies.add(uri.host, uri.pathQueryRef, name, value,
false, false, false, Date.now() + 24000 * 60 * 60);
},
/**
* Adds a new serviceworker with the specified path. Note that this
* method will open a new tab at the domain of the SW path to that effect.
*
* @param {String} path - the path to the service worker to add.
*
* @returns a Promise that resolves when the service worker was registered
*/
addServiceWorker(path) {
let uri = Services.io.newURI(path);
// Register a dummy ServiceWorker.
return BrowserTestUtils.withNewTab(uri.prePath, async function(browser) {
return ContentTask.spawn(browser, {path}, async ({path: p}) => {
let r = await content.navigator.serviceWorker.register(p);
return new Promise(resolve => {
let worker = r.installing;
worker.addEventListener("statechange", () => {
if (worker.state === "installed") {
resolve();
}
});
});
});
});
},
/**
* Checks whether the specified origin has registered ServiceWorkers.
*
* @param {String} origin - the origin of the site to check
*
* @returns {Boolean} whether or not the site has ServiceWorkers.
*/
hasServiceWorkers(origin) {
let serviceWorkers = swm.getAllRegistrations();
for (let i = 0; i < serviceWorkers.length; i++) {
let sw = serviceWorkers.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);
if (sw.principal.origin == origin) {
return true;
}
}
return false;
},
/**
* Gets the current quota usage for the specified origin.
*

View File

@ -3,7 +3,6 @@ support-files=
head.js
dummy.js
dummy_page.html
sanitize.html
[browser_purgehistory_clears_sh.js]
[browser_sanitize-formhistory.js]

View File

@ -2,6 +2,8 @@
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
const {Sanitizer} = ChromeUtils.import("resource:///modules/Sanitizer.jsm", {});
const {SiteDataTestUtils} = ChromeUtils.import("resource://testing-common/SiteDataTestUtils.jsm", {});
const {PromiseTestUtils} = ChromeUtils.import("resource://testing-common/PromiseTestUtils.jsm", {});
XPCOMUtils.defineLazyServiceGetter(this, "sas",
"@mozilla.org/storage/activity-service;1",
@ -9,15 +11,27 @@ XPCOMUtils.defineLazyServiceGetter(this, "sas",
XPCOMUtils.defineLazyServiceGetter(this, "swm",
"@mozilla.org/serviceworkers/manager;1",
"nsIServiceWorkerManager");
XPCOMUtils.defineLazyServiceGetter(this, "quotaManagerService",
"@mozilla.org/dom/quota-manager-service;1",
"nsIQuotaManagerService");
const oneHour = 3600000000;
const fiveHours = oneHour * 5;
const itemsToClear = [ "cookies", "offlineApps" ];
function hasIndexedDB(origin) {
return new Promise(resolve => {
let hasData = true;
let uri = Services.io.newURI(origin);
let principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
let request = indexedDB.openForPrincipal(principal, "TestDatabase", 1);
request.onupgradeneeded = function(e) {
hasData = false;
};
request.onsuccess = function(e) {
resolve(hasData);
};
});
}
function waitForUnregister(host) {
return new Promise(resolve => {
let listener = {
@ -36,20 +50,11 @@ function waitForUnregister(host) {
}
async function createData(host) {
let pageURL = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://" + host) + "sanitize.html";
let origin = "https://" + host;
let dummySWURL = getRootDirectory(gTestPath).replace("chrome://mochitests/content", origin) + "dummy.js";
return BrowserTestUtils.withNewTab(pageURL, async function(browser) {
await ContentTask.spawn(browser, null, () => {
return new content.window.Promise(resolve => {
let id = content.window.setInterval(() => {
if ("foobar" in content.window.localStorage) {
content.window.clearInterval(id);
resolve(true);
}
}, 1000);
});
});
});
await SiteDataTestUtils.addToIndexedDB(origin);
await SiteDataTestUtils.addServiceWorker(dummySWURL);
}
function moveOriginInTime(principals, endDate, host) {
@ -63,44 +68,12 @@ function moveOriginInTime(principals, endDate, host) {
return false;
}
async function getData(host) {
let dummyURL = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://" + host) + "dummy_page.html";
// LocalStorage + IndexedDB
let data = await BrowserTestUtils.withNewTab(dummyURL, async function(browser) {
return ContentTask.spawn(browser, null, () => {
return new content.window.Promise(resolve => {
let obj = {
localStorage: "foobar" in content.window.localStorage,
indexedDB: true,
serviceWorker: false,
};
let request = content.window.indexedDB.open("sanitizer_test", 1);
request.onupgradeneeded = event => {
obj.indexedDB = false;
};
request.onsuccess = event => {
resolve(obj);
};
});
});
});
// ServiceWorkers
let serviceWorkers = swm.getAllRegistrations();
for (let i = 0; i < serviceWorkers.length; i++) {
let sw = serviceWorkers.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);
if (sw.principal.URI.host == host) {
data.serviceWorker = true;
break;
}
}
return data;
}
add_task(async function testWithRange() {
// We have intermittent occurrences of NS_ERROR_ABORT being
// thrown at closing database instances when using Santizer.sanitize().
// This does not seem to impact cleanup, since our tests run fine anyway.
PromiseTestUtils.whitelistRejectionsGlobally(/NS_ERROR_ABORT/);
await SpecialPowers.pushPrefEnv({"set": [
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.exemptFromPerDomainMax", true],
@ -137,15 +110,15 @@ add_task(async function testWithRange() {
is(found, 2, "Our origins are active.");
let dataPre = await getData("example.org");
ok(dataPre.localStorage, "We have localStorage data");
ok(dataPre.indexedDB, "We have indexedDB data");
ok(dataPre.serviceWorker, "We have serviceWorker data");
ok(await hasIndexedDB("https://example.org"),
"We have indexedDB data for example.org");
ok(SiteDataTestUtils.hasServiceWorkers("https://example.org"),
"We have serviceWorker data for example.org");
dataPre = await getData("example.com");
ok(dataPre.localStorage, "We have localStorage data");
ok(dataPre.indexedDB, "We have indexedDB data");
ok(dataPre.serviceWorker, "We have serviceWorker data");
ok(await hasIndexedDB("https://example.com"),
"We have indexedDB data for example.com");
ok(SiteDataTestUtils.hasServiceWorkers("https://example.com"),
"We have serviceWorker data for example.com");
// Let's move example.com in the past.
ok(moveOriginInTime(principals, endDate, "example.com"), "Operation completed!");
@ -157,15 +130,15 @@ add_task(async function testWithRange() {
await Sanitizer.sanitize(itemsToClear, {ignoreTimespan: false});
await p;
let dataPost = await getData("example.org");
ok(!dataPost.localStorage, "We don't have localStorage data");
ok(!dataPost.indexedDB, "We don't have indexedDB data");
ok(!dataPost.serviceWorker, "We don't have serviceWorker data");
ok(!(await hasIndexedDB("https://example.org")),
"We don't have indexedDB data for example.org");
ok(!SiteDataTestUtils.hasServiceWorkers("https://example.org"),
"We don't have serviceWorker data for example.org");
dataPost = await getData("example.com");
ok(dataPost.localStorage, "We still have localStorage data");
ok(dataPost.indexedDB, "We still have indexedDB data");
ok(dataPost.serviceWorker, "We still have serviceWorker data");
ok(await hasIndexedDB("https://example.com"),
"We still have indexedDB data for example.com");
ok(SiteDataTestUtils.hasServiceWorkers("https://example.com"),
"We still have serviceWorker data for example.com");
// We have to move example.com in the past because how we check IDB triggers
// a storage activity.
@ -175,15 +148,18 @@ add_task(async function testWithRange() {
info("sanitize again to ensure clearing doesn't expand the activity scope");
await Sanitizer.sanitize(itemsToClear, {ignoreTimespan: false});
dataPost = await getData("example.com");
ok(dataPost.localStorage, "We still have localStorage data");
ok(dataPost.indexedDB, "We still have indexedDB data");
ok(dataPost.serviceWorker, "We still have serviceWorker data");
ok(await hasIndexedDB("https://example.com"),
"We still have indexedDB data for example.com");
ok(SiteDataTestUtils.hasServiceWorkers("https://example.com"),
"We still have serviceWorker data for example.com");
dataPost = await getData("example.org");
ok(!dataPost.localStorage, "We don't have localStorage data");
ok(!dataPost.indexedDB, "We don't have indexedDB data");
ok(!dataPost.serviceWorker, "We don't have serviceWorker data");
ok(!(await hasIndexedDB("https://example.org")),
"We don't have indexedDB data for example.org");
ok(!SiteDataTestUtils.hasServiceWorkers("https://example.org"),
"We don't have serviceWorker data for example.org");
sas.testOnlyReset();
// Clean up.
await Sanitizer.sanitize(itemsToClear);
});

View File

@ -1,41 +0,0 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"></meta>
</head>
<body>
<script>
// indexedDB
let p = new Promise(resolve => {
let request = indexedDB.open("sanitizer_test", 1);
request.onupgradeneeded = event => {
let db = event.target.result;
event.target.onsuccess = resolve;
db.createObjectStore("foo", { autoIncrement: true });
db.createObjectStore("bar", { autoIncrement: true });
};
});
// ServiceWorker
p.then(() => {
return navigator.serviceWorker.register("dummy.js")
.then(r => {
return new Promise(resolve => {
let worker = r.installing;
worker.addEventListener("statechange", () => {
if (worker.state === "installed") {
resolve(true);
}
});
});
});
})
// localStorage
.then(() => {
localStorage.foobar = "hello world!";
});
</script>
</body>
</html>