Bug 1321303 - Part 7: Implement browsingData.removePasswords, r?aswan
MozReview-Commit-ID: E23EYrBs3Ze
--- a/browser/components/extensions/ext-browsingData.js
+++ b/browser/components/extensions/ext-browsingData.js
@@ -13,16 +13,19 @@ XPCOMUtils.defineLazyModuleGetter(this,
XPCOMUtils.defineLazyModuleGetter(this, "setTimeout",
"resource://gre/modules/Timer.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch",
"resource://gre/modules/TelemetryStopwatch.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "cookieMgr",
"@mozilla.org/cookiemanager;1",
"nsICookieManager");
+XPCOMUtils.defineLazyServiceGetter(this, "loginManager",
+ "@mozilla.org/login-manager;1",
+ "nsILoginManager");
/**
* A number of iterations after which to yield time back
* to the system.
*/
const YIELD_PERIOD = 10;
const PREF_DOMAIN = "privacy.cpd.";
@@ -77,16 +80,37 @@ function clearDownloads(options) {
function clearFormData(options) {
return sanitizer.items.formdata.clear(makeRange(options));
}
function clearHistory(options) {
return sanitizer.items.history.clear(makeRange(options));
}
+let clearPasswords = Task.async(function* (options) {
+ let yieldCounter = 0;
+
+ if (options.since) {
+ // Iterate through the logins and delete any updated after our cutoff.
+ let logins = loginManager.getAllLogins();
+ for (let login of logins) {
+ login.QueryInterface(Ci.nsILoginMetaInfo);
+ if (login.timePasswordChanged > options.since) {
+ loginManager.removeLogin(login);
+ if (++yieldCounter % YIELD_PERIOD == 0) {
+ yield new Promise(resolve => setTimeout(resolve, 0)); // Don't block the main thread too long.
+ }
+ }
+ }
+ } else {
+ // Remove everything.
+ loginManager.removeAllLogins();
+ }
+});
+
function clearPluginData(options) {
return sanitizer.items.pluginData.clear(makeRange(options));
}
function doRemoval(options, dataToRemove, extension) {
if (options.originTypes &&
(options.originTypes.protectedWeb || options.originTypes.extension)) {
return Promise.reject(
@@ -108,16 +132,19 @@ function doRemoval(options, dataToRemove
removalPromises.push(clearDownloads(options));
break;
case "formData":
removalPromises.push(clearFormData(options));
break;
case "history":
removalPromises.push(clearHistory(options));
break;
+ case "passwords":
+ removalPromises.push(clearPasswords(options));
+ break;
case "pluginData":
removalPromises.push(clearPluginData(options));
break;
default:
invalidDataTypes.push(dataType);
}
}
}
@@ -171,14 +198,17 @@ extensions.registerSchemaAPI("browsingDa
return doRemoval(options, {downloads: true});
},
removeFormData(options) {
return doRemoval(options, {formData: true});
},
removeHistory(options) {
return doRemoval(options, {history: true});
},
+ removePasswords(options) {
+ return doRemoval(options, {passwords: true});
+ },
removePluginData(options) {
return doRemoval(options, {pluginData: true});
},
},
};
});
--- a/browser/components/extensions/schemas/browsing_data.json
+++ b/browser/components/extensions/schemas/browsing_data.json
@@ -369,17 +369,16 @@
}
]
},
{
"name": "removePasswords",
"description": "Clears the browser's stored passwords.",
"type": "function",
"async": "callback",
- "unsupported": true,
"parameters": [
{
"$ref": "RemovalOptions",
"name": "options"
},
{
"name": "callback",
"type": "function",
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/xpcshell/test_ext_browsingData_passwords.js
@@ -0,0 +1,92 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+XPCOMUtils.defineLazyServiceGetter(this, "loginManager",
+ "@mozilla.org/login-manager;1",
+ "nsILoginManager");
+
+const REFERENCE_DATE = Date.now();
+const LOGIN_USERNAME = "username";
+const LOGIN_PASSWORD = "password";
+const LOGIN_USERNAME_FIELD = "username_field";
+const LOGIN_PASSWORD_FIELD = "password_field";
+const OLD_HOST = "http://mozilla.org";
+const NEW_HOST = "http://mozilla.com";
+
+function addLogin(host, timestamp) {
+ checkLoginExists(host, false);
+ let login = Cc["@mozilla.org/login-manager/loginInfo;1"].
+ createInstance(Ci.nsILoginInfo);
+ login.init(host, "", null, LOGIN_USERNAME, LOGIN_PASSWORD,
+ LOGIN_USERNAME_FIELD, LOGIN_PASSWORD_FIELD);
+ login.QueryInterface(Ci.nsILoginMetaInfo);
+ login.timePasswordChanged = timestamp;
+ loginManager.addLogin(login);
+ checkLoginExists(host, true);
+}
+
+function checkLoginExists(host, shouldExist) {
+ let count = { value: 0 };
+ loginManager.findLogins(count, host, "", null);
+ equal(count.value, shouldExist ? 1 : 0, `Login was ${shouldExist ? "" : "not "} found.`);
+}
+
+async function setupPasswords() {
+ loginManager.removeAllLogins();
+ addLogin(NEW_HOST, REFERENCE_DATE);
+ addLogin(OLD_HOST, REFERENCE_DATE - 10000);
+}
+
+add_task(async function testHistory() {
+ function background() {
+ browser.test.onMessage.addListener(async (msg, options) => {
+ if (msg == "removeHistory") {
+ await browser.browsingData.removePasswords(options);
+ } else {
+ await browser.browsingData.remove(options, {passwords: true});
+ }
+ browser.test.sendMessage("passwordsRemoved");
+ });
+ }
+
+ let extension = ExtensionTestUtils.loadExtension({
+ background,
+ manifest: {
+ permissions: ["browsingData"],
+ },
+ });
+
+ async function testRemovalMethod(method) {
+ // Clear passwords with no since value.
+ await setupPasswords();
+ extension.sendMessage(method, {});
+ await extension.awaitMessage("passwordsRemoved");
+
+ checkLoginExists(OLD_HOST, false);
+ checkLoginExists(NEW_HOST, false);
+
+ // Clear passwords with recent since value.
+ await setupPasswords();
+ extension.sendMessage(method, {since: REFERENCE_DATE - 1000});
+ await extension.awaitMessage("passwordsRemoved");
+
+ checkLoginExists(OLD_HOST, true);
+ checkLoginExists(NEW_HOST, false);
+
+ // Clear passwords with old since value.
+ await setupPasswords();
+ extension.sendMessage(method, {since: REFERENCE_DATE - 20000});
+ await extension.awaitMessage("passwordsRemoved");
+
+ checkLoginExists(OLD_HOST, false);
+ checkLoginExists(NEW_HOST, false);
+ }
+
+ await extension.startup();
+
+ await testRemovalMethod("removePasswords");
+ await testRemovalMethod("remove");
+
+ await extension.unload();
+});
--- a/browser/components/extensions/test/xpcshell/xpcshell.ini
+++ b/browser/components/extensions/test/xpcshell/xpcshell.ini
@@ -5,13 +5,14 @@ firefox-appdir = browser
tags = webextensions
[test_ext_bookmarks.js]
[test_ext_browsingData.js]
[test_ext_browsingData_cookies_cache.js]
[test_ext_browsingData_downloads.js]
[test_ext_browsingData_formdata.js]
[test_ext_browsingData_history.js]
+[test_ext_browsingData_passwords.js]
[test_ext_browsingData_settings.js]
[test_ext_history.js]
[test_ext_manifest_commands.js]
[test_ext_manifest_omnibox.js]
[test_ext_manifest_permissions.js]