Files
tubestation/toolkit/components/url-classifier/tests/mochitest/classifierHelper.js
DimiL da8ff508ef Bug 1365877 - resetDatabase in SafeBrowsing test case should also reset cache. r=francois
resetDatabase() is used to clear out the Safe Browsing database and cache in tests.
Since bug 1333328 however we can no longer rely on updates clearing the cache.

There are two solutions to address this issue:
1. resetDatabase() calls another test-only function: reloadDatabase(). Since the cache
   is in memory, reloading the URL classifier will automatically clear the cache.
2. During an update, remove cache entries which are not in the database.

I prefer taking the first one because if we implement the second
approach, an update will take longer since we need to check if each prefix
in the cache can also be found in the database. I think this is not necessary
because prefixes not in the database will eventually be removed when they
are expired.

MozReview-Commit-ID: BjsDKDMr205
2017-05-18 17:21:00 +08:00

210 lines
6.4 KiB
JavaScript

if (typeof(classifierHelper) == "undefined") {
var classifierHelper = {};
}
const CLASSIFIER_COMMON_URL = SimpleTest.getTestFileURL("classifierCommon.js");
var gScript = SpecialPowers.loadChromeScript(CLASSIFIER_COMMON_URL);
const ADD_CHUNKNUM = 524;
const SUB_CHUNKNUM = 523;
const HASHLEN = 32;
const PREFS = {
PROVIDER_LISTS : "browser.safebrowsing.provider.mozilla.lists",
DISALLOW_COMPLETIONS : "urlclassifier.disallow_completions",
PROVIDER_GETHASHURL : "browser.safebrowsing.provider.mozilla.gethashURL"
};
// addUrlToDB & removeUrlFromDB are asynchronous, queue the task to ensure
// the callback follow correct order.
classifierHelper._updates = [];
// Keep urls added to database, those urls should be automatically
// removed after test complete.
classifierHelper._updatesToCleanup = [];
classifierHelper._initsCB = [];
// This function return a Promise, promise is resolved when SafeBrowsing.jsm
// is initialized.
classifierHelper.waitForInit = function() {
return new Promise(function(resolve, reject) {
classifierHelper._initsCB.push(resolve);
gScript.sendAsyncMessage("waitForInit");
});
}
// This function is used to allow completion for specific "list",
// some lists like "test-malware-simple" is default disabled to ask for complete.
// "list" is the db we would like to allow it
// "url" is the completion server
classifierHelper.allowCompletion = function(lists, url) {
for (var list of lists) {
// Add test db to provider
var pref = SpecialPowers.getCharPref(PREFS.PROVIDER_LISTS);
pref += "," + list;
SpecialPowers.setCharPref(PREFS.PROVIDER_LISTS, pref);
// Rename test db so we will not disallow it from completions
pref = SpecialPowers.getCharPref(PREFS.DISALLOW_COMPLETIONS);
pref = pref.replace(list, list + "-backup");
SpecialPowers.setCharPref(PREFS.DISALLOW_COMPLETIONS, pref);
}
// Set get hash url
SpecialPowers.setCharPref(PREFS.PROVIDER_GETHASHURL, url);
}
// Pass { url: ..., db: ... } to add url to database,
// onsuccess/onerror will be called when update complete.
classifierHelper.addUrlToDB = function(updateData) {
return new Promise(function(resolve, reject) {
var testUpdate = "";
for (var update of updateData) {
var LISTNAME = update.db;
var CHUNKDATA = update.url;
var CHUNKLEN = CHUNKDATA.length;
var HASHLEN = update.len ? update.len : 32;
classifierHelper._updatesToCleanup.push(update);
testUpdate +=
"n:1000\n" +
"i:" + LISTNAME + "\n" +
"ad:1\n" +
"a:" + ADD_CHUNKNUM + ":" + HASHLEN + ":" + CHUNKLEN + "\n" +
CHUNKDATA;
}
classifierHelper._update(testUpdate, resolve, reject);
});
}
// Pass { url: ..., db: ... } to remove url from database,
// onsuccess/onerror will be called when update complete.
classifierHelper.removeUrlFromDB = function(updateData) {
return new Promise(function(resolve, reject) {
var testUpdate = "";
for (var update of updateData) {
var LISTNAME = update.db;
var CHUNKDATA = ADD_CHUNKNUM + ":" + update.url;
var CHUNKLEN = CHUNKDATA.length;
var HASHLEN = update.len ? update.len : 32;
testUpdate +=
"n:1000\n" +
"i:" + LISTNAME + "\n" +
"s:" + SUB_CHUNKNUM + ":" + HASHLEN + ":" + CHUNKLEN + "\n" +
CHUNKDATA;
}
classifierHelper._updatesToCleanup =
classifierHelper._updatesToCleanup.filter((v) => {
return updateData.indexOf(v) == -1;
});
classifierHelper._update(testUpdate, resolve, reject);
});
};
// This API is used to expire all add/sub chunks we have updated
// by using addUrlToDB and removeUrlFromDB.
classifierHelper.resetDatabase = function() {
function removeDatabase() {
return new Promise(function(resolve, reject) {
var testUpdate = "";
for (var update of classifierHelper._updatesToCleanup) {
if (testUpdate.includes(update.db))
continue;
testUpdate +=
"n:1000\n" +
"i:" + update.db + "\n" +
"ad:" + ADD_CHUNKNUM + "\n" +
"sd:" + SUB_CHUNKNUM + "\n"
}
classifierHelper._update(testUpdate, resolve, reject);
});
}
// Remove and then reload will ensure both database and cache will
// be cleared.
return Promise.resolve()
.then(removeDatabase)
.then(classifierHelper.reloadDatabase);
};
classifierHelper.reloadDatabase = function() {
return new Promise(function(resolve, reject) {
gScript.addMessageListener("reloadSuccess", function handler() {
gScript.removeMessageListener('reloadSuccess', handler);
resolve();
});
gScript.sendAsyncMessage("doReload");
});
}
classifierHelper._update = function(testUpdate, onsuccess, onerror) {
// Queue the task if there is still an on-going update
classifierHelper._updates.push({"data": testUpdate,
"onsuccess": onsuccess,
"onerror": onerror});
if (classifierHelper._updates.length != 1) {
return;
}
gScript.sendAsyncMessage("doUpdate", { testUpdate });
};
classifierHelper._updateSuccess = function() {
var update = classifierHelper._updates.shift();
update.onsuccess();
if (classifierHelper._updates.length) {
var testUpdate = classifierHelper._updates[0].data;
gScript.sendAsyncMessage("doUpdate", { testUpdate });
}
};
classifierHelper._updateError = function(errorCode) {
var update = classifierHelper._updates.shift();
update.onerror(errorCode);
if (classifierHelper._updates.length) {
var testUpdate = classifierHelper._updates[0].data;
gScript.sendAsyncMessage("doUpdate", { testUpdate });
}
};
classifierHelper._inited = function() {
classifierHelper._initsCB.forEach(function (cb) {
cb();
});
classifierHelper._initsCB = [];
};
classifierHelper._setup = function() {
gScript.addMessageListener("updateSuccess", classifierHelper._updateSuccess);
gScript.addMessageListener("updateError", classifierHelper._updateError);
gScript.addMessageListener("safeBrowsingInited", classifierHelper._inited);
// cleanup will be called at end of each testcase to remove all the urls added to database.
SimpleTest.registerCleanupFunction(classifierHelper._cleanup);
};
classifierHelper._cleanup = function() {
// clean all the preferences may touch by helper
for (var pref in PREFS) {
SpecialPowers.clearUserPref(pref);
}
if (!classifierHelper._updatesToCleanup) {
return Promise.resolve();
}
return classifierHelper.resetDatabase();
};
classifierHelper._setup();