Bug 1363012 - Implement browsingData.removePasswords WebExtension API method on android. r?grisha, mattw
MozReview-Commit-ID: 2tHGDfos8cl
--- a/mobile/android/components/extensions/ext-browsingData.js
+++ b/mobile/android/components/extensions/ext-browsingData.js
@@ -4,16 +4,41 @@
Cu.import("resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "SharedPreferences",
"resource://gre/modules/SharedPreferences.jsm");
+const YIELD_PERIOD = 10;
+
+let clearPasswords = async function(options) {
+ let loginManager = Services.logins;
+ 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) {
+ await new Promise(resolve => setTimeout(resolve, 0)); // Don't block the main thread too long.
+ }
+ }
+ }
+ } else {
+ // Remove everything.
+ loginManager.removeAllLogins();
+ }
+};
+
+
this.browsingData = class extends ExtensionAPI {
getAPI(context) {
return {
browsingData: {
settings() {
const PREF_DOMAIN = "android.not_a_preference.privacy.clear";
const PREF_KEY_PREFIX = "private.data.";
// The following prefs are the only ones in Firefox that match corresponding
@@ -49,12 +74,15 @@ this.browsingData = class extends Extens
dataToRemove[name] = dataTrue.indexOf(`${PREF_KEY_PREFIX}${item}`) > -1;
// Firefox doesn't have the same concept of dataRemovalPermitted
// as Chrome, so it will always be true.
dataRemovalPermitted[name] = true;
}
return Promise.resolve({options, dataToRemove, dataRemovalPermitted});
},
+ removePasswords(options) {
+ return clearPasswords(options);
+ },
},
};
}
};
\ No newline at end of file
--- a/mobile/android/components/extensions/schemas/browsing_data.json
+++ b/mobile/android/components/extensions/schemas/browsing_data.json
@@ -376,17 +376,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",
--- a/mobile/android/components/extensions/test/mochitest/chrome.ini
+++ b/mobile/android/components/extensions/test/mochitest/chrome.ini
@@ -1,10 +1,11 @@
[DEFAULT]
support-files =
head.js
../../../../../../toolkit/components/extensions/test/mochitest/chrome_cleanup_script.js
tags = webextensions
[test_ext_browserAction_getTitle_setTitle.html]
[test_ext_browserAction_onClicked.html]
+[test_ext_browsingData_passwords.html]
[test_ext_pageAction_show_hide.html]
[test_ext_pageAction_getPopup_setPopup.html]
new file mode 100644
--- /dev/null
+++ b/mobile/android/components/extensions/test/mochitest/test_ext_browsingData_passwords.html
@@ -0,0 +1,102 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>BrowsingData Settings test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/ExtensionTestUtils.js"></script>
+ <script type="text/javascript" src="head.js"></script>
+ <link rel="stylesheet" href="chrome://mochikit/contents/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<script type="text/javascript">
+"use strict";
+
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+
+var loginManager = Components.classes["@mozilla.org/login-manager;1"].getService(Components.interfaces.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 checkLoginExists(host, shouldExist) {
+ let count = {value: 0};
+ loginManager.findLogins(count, host, "", null);
+ is(count.value, shouldExist ? 1 : 0, `Login was ${shouldExist ? "" : "not "} found.`);
+}
+
+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);
+}
+
+async function setupPasswords() {
+ loginManager.removeAllLogins();
+ addLogin(NEW_HOST, REFERENCE_DATE);
+ addLogin(OLD_HOST, REFERENCE_DATE - 10000);
+}
+
+add_task(async function testPasswords() {
+ function background() {
+ browser.test.onMessage.addListener(async (msg, options) => {
+ await browser.browsingData.removePasswords(options);
+ 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 extension.unload();
+});
+
+</script>
+</body>
+</html>
\ No newline at end of file