Bug 1362998 - Implement browsingData.removeCookies WebExtension API method on android. r?bsilverberg
MozReview-Commit-ID: 9h5YegFelhe
--- a/mobile/android/components/extensions/ext-browsingData.js
+++ b/mobile/android/components/extensions/ext-browsingData.js
@@ -4,16 +4,51 @@
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");
+let clearCookies = async function(options) {
+ if (options.originTypes &&
+ (options.originTypes.protectedWeb || options.originTypes.extension)) {
+ return Promise.reject(
+ {message: "Firefox does not support protectedWeb or extension as originTypes."});
+ }
+
+ let cookieMgr = Services.cookies;
+ let yieldCounter = 0;
+ const YIELD_PERIOD = 10;
+
+ if (options.since) {
+ // Convert it to microseconds
+ let since = options.since*1000;
+ // Iterate through the cookies and delete any created after our cutoff.
+ let cookiesEnum = cookieMgr.enumerator;
+ while (cookiesEnum.hasMoreElements()) {
+ let cookie = cookiesEnum.getNext().QueryInterface(Ci.nsICookie2);
+
+ if (cookie.creationTime >= since) {
+ // This cookie was created after our cutoff, clear it.
+ cookieMgr.remove(cookie.host, cookie.name, cookie.path,
+ false, cookie.originAttributes);
+
+ if (++yieldCounter % YIELD_PERIOD == 0) {
+ await new Promise(resolve => setTimeout(resolve, 0)); // Don't block the main thread too long.
+ }
+ }
+ }
+ } else {
+ // Remove everything.
+ cookieMgr.removeAll();
+ }
+};
+
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
@@ -46,12 +81,15 @@ this.browsingData = class extends Extens
// Firefox doesn't have the same concept of dataRemovalPermitted
// as Chrome, so it will always be true.
dataRemovalPermitted[name] = true;
}
// We do not provide option to delete history by time
// so, since value is given 0, which means Everything
return Promise.resolve({options: {since: 0}, dataToRemove, dataRemovalPermitted});
},
+ removeCookies(options) {
+ return clearCookies(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
@@ -222,17 +222,16 @@
}
]
},
{
"name": "removeCookies",
"description": "Clears the browser's cookies and server-bound certificates modified within a particular timeframe.",
"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,12 +1,13 @@
[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_cookies.html]
[test_ext_browsingData_settings.html]
[test_ext_pageAction_show_hide.html]
[test_ext_pageAction_getPopup_setPopup.html]
skip-if = os == 'android' # bug 1373170
new file mode 100644
--- /dev/null
+++ b/mobile/android/components/extensions/test/mochitest/test_ext_browsingData_cookies.html
@@ -0,0 +1,98 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>BrowsingData Cookies 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;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+const COOKIE = {
+ host: "example.com",
+ name: "test_cookie",
+ path: "/",
+};
+let since, oldCookie;
+
+function addCookie(cookie) {
+ let expiry = Date.now() / 1000 + 10000;
+ Services.cookies.add(cookie.host, cookie.path, cookie.name, "test", false, false, false, expiry);
+ ok(Services.cookies.cookieExists(cookie), `Cookie ${cookie.name} was created.`);
+}
+
+async function setUpCookies() {
+ // Add a cookie which will end up with an older creationTime.
+ oldCookie = Object.assign({}, COOKIE, {name: Date.now()});
+ addCookie(oldCookie);
+ await new Promise(resolve => setTimeout(resolve, 20));
+ since = Date.now();
+ await new Promise(resolve => setTimeout(resolve, 10));
+
+ // Add a cookie which will end up with a more recent creationTime.
+ addCookie(COOKIE);
+}
+
+add_task(async function testCookies() {
+ function background() {
+ browser.test.onMessage.addListener(async (msg, options) => {
+ await browser.browsingData.removeCookies(options);
+ browser.test.sendMessage("cookiesRemoved");
+ });
+ }
+
+ let extension = ExtensionTestUtils.loadExtension({
+ background,
+ manifest: {
+ permissions: ["browsingData"],
+ },
+ });
+
+ async function testRemovalMethod(method) {
+ // Clear cookies with a recent since value.
+ await setUpCookies();
+ extension.sendMessage(method, {since});
+ await extension.awaitMessage("cookiesRemoved");
+
+ ok(Services.cookies.cookieExists(oldCookie), "Old cookie was not removed.");
+ ok(!Services.cookies.cookieExists(COOKIE), "Recent cookie was removed.");
+
+ // Clear cookies with an old since value.
+ await setUpCookies();
+ addCookie(COOKIE);
+ extension.sendMessage(method, {since: since - 100000});
+ await extension.awaitMessage("cookiesRemoved");
+
+ ok(!Services.cookies.cookieExists(oldCookie), "Old cookie was removed.");
+ ok(!Services.cookies.cookieExists(COOKIE), "Recent cookie was removed.");
+
+ // Clear cookies with no since value and valid originTypes.
+ await setUpCookies();
+ extension.sendMessage(
+ method,
+ {originTypes: {unprotectedWeb: true, protectedWeb: false}});
+ await extension.awaitMessage("cookiesRemoved");
+
+ ok(!Services.cookies.cookieExists(COOKIE), `Cookie ${COOKIE.name} was removed.`);
+ ok(!Services.cookies.cookieExists(oldCookie), `Cookie ${oldCookie.name} was removed.`);
+ }
+
+ await extension.startup();
+
+ await testRemovalMethod("removeCookies");
+
+ await extension.unload();
+});
+</script>
+
+</body>
+</html>
\ No newline at end of file