зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1610641 - Remove Fennec's ext-browsingData. r=mixedpuppy
Differential Revision: https://phabricator.services.mozilla.com/D60580 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
a6d7032a66
Коммит
9b30266671
|
@ -77,13 +77,6 @@ extensions.registerModules({
|
|||
manifest: ["browser_action"],
|
||||
paths: [["browserAction"]],
|
||||
},
|
||||
browsingData: {
|
||||
url: "chrome://geckoview/content/ext-browsingData.js",
|
||||
schema: "chrome://geckoview/content/schemas/browsing_data.json",
|
||||
scopes: ["addon_parent"],
|
||||
manifest: ["browsing_data"],
|
||||
paths: [["browsingData"]],
|
||||
},
|
||||
pageAction: {
|
||||
url: "chrome://geckoview/content/ext-pageAction.js",
|
||||
schema: "chrome://extensions/content/schemas/page_action.json",
|
||||
|
|
|
@ -1,176 +0,0 @@
|
|||
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set sts=2 sw=2 et tw=80: */
|
||||
"use strict";
|
||||
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"Sanitizer",
|
||||
"resource://gre/modules/Sanitizer.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"Services",
|
||||
"resource://gre/modules/Services.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"SharedPreferences",
|
||||
"resource://gre/modules/SharedPreferences.jsm"
|
||||
);
|
||||
|
||||
const clearCache = () => {
|
||||
// Clearing the cache does not support timestamps.
|
||||
return Sanitizer.clearItem("cache");
|
||||
};
|
||||
|
||||
const clearCookies = async function(options) {
|
||||
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.
|
||||
for (let cookie of cookieMgr.cookies) {
|
||||
if (cookie.creationTime >= since) {
|
||||
// This cookie was created after our cutoff, clear it.
|
||||
cookieMgr.remove(
|
||||
cookie.host,
|
||||
cookie.name,
|
||||
cookie.path,
|
||||
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();
|
||||
}
|
||||
};
|
||||
|
||||
const clearDownloads = options => {
|
||||
return Sanitizer.clearItem("downloadHistory", options.since);
|
||||
};
|
||||
|
||||
const clearFormData = options => {
|
||||
return Sanitizer.clearItem("formdata", options.since);
|
||||
};
|
||||
|
||||
const doRemoval = (options, dataToRemove, extension) => {
|
||||
if (
|
||||
options.originTypes &&
|
||||
(options.originTypes.protectedWeb || options.originTypes.extension)
|
||||
) {
|
||||
return Promise.reject({
|
||||
message:
|
||||
"Firefox does not support protectedWeb or extension as originTypes.",
|
||||
});
|
||||
}
|
||||
|
||||
let removalPromises = [];
|
||||
let invalidDataTypes = [];
|
||||
for (let dataType in dataToRemove) {
|
||||
if (dataToRemove[dataType]) {
|
||||
switch (dataType) {
|
||||
case "cache":
|
||||
removalPromises.push(clearCache());
|
||||
break;
|
||||
case "cookies":
|
||||
removalPromises.push(clearCookies(options));
|
||||
break;
|
||||
case "downloads":
|
||||
removalPromises.push(clearDownloads(options));
|
||||
break;
|
||||
case "formData":
|
||||
removalPromises.push(clearFormData(options));
|
||||
break;
|
||||
default:
|
||||
invalidDataTypes.push(dataType);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (extension && invalidDataTypes.length) {
|
||||
extension.logger.warn(
|
||||
`Firefox does not support dataTypes: ${invalidDataTypes.toString()}.`
|
||||
);
|
||||
}
|
||||
return Promise.all(removalPromises);
|
||||
};
|
||||
|
||||
this.browsingData = class extends ExtensionAPI {
|
||||
getAPI(context) {
|
||||
let { extension } = 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
|
||||
// values used by Chrome when returning settings.
|
||||
const PREF_LIST = [
|
||||
"cache",
|
||||
"history",
|
||||
"formdata",
|
||||
"cookies_sessions",
|
||||
"downloadFiles",
|
||||
];
|
||||
|
||||
let dataTrue = SharedPreferences.forProfile().getSetPref(PREF_DOMAIN);
|
||||
let name;
|
||||
|
||||
let dataToRemove = {};
|
||||
let dataRemovalPermitted = {};
|
||||
|
||||
for (let item of PREF_LIST) {
|
||||
// The property formData needs a different case than the
|
||||
// formdata preference.
|
||||
switch (item) {
|
||||
case "formdata":
|
||||
name = "formData";
|
||||
break;
|
||||
case "cookies_sessions":
|
||||
name = "cookies";
|
||||
break;
|
||||
case "downloadFiles":
|
||||
name = "downloads";
|
||||
break;
|
||||
default:
|
||||
name = item;
|
||||
}
|
||||
dataToRemove[name] = dataTrue.includes(`${PREF_KEY_PREFIX}${item}`);
|
||||
// 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,
|
||||
});
|
||||
},
|
||||
remove(options, dataToRemove) {
|
||||
return doRemoval(options, dataToRemove, extension);
|
||||
},
|
||||
removeCache(options) {
|
||||
return doRemoval(options, { cache: true });
|
||||
},
|
||||
removeCookies(options) {
|
||||
return doRemoval(options, { cookies: true });
|
||||
},
|
||||
removeDownloads(options) {
|
||||
return doRemoval(options, { downloads: true });
|
||||
},
|
||||
removeFormData(options) {
|
||||
return doRemoval(options, { formData: true });
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
|
@ -7,7 +7,6 @@ geckoview.jar:
|
|||
content/ext-c-android.js
|
||||
content/ext-c-tabs.js
|
||||
content/ext-browserAction.js
|
||||
content/ext-browsingData.js
|
||||
content/ext-pageAction.js
|
||||
content/ext-tabs.js
|
||||
content/ext-utils.js
|
||||
|
|
|
@ -1,421 +0,0 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
[
|
||||
{
|
||||
"namespace": "manifest",
|
||||
"types": [
|
||||
{
|
||||
"$extend": "Permission",
|
||||
"choices": [{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"browsingData"
|
||||
]
|
||||
}]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"namespace": "browsingData",
|
||||
"description": "Use the <code>chrome.browsingData</code> API to remove browsing data from a user's local profile.",
|
||||
"permissions": ["browsingData"],
|
||||
"types": [
|
||||
{
|
||||
"id": "RemovalOptions",
|
||||
"type": "object",
|
||||
"description": "Options that determine exactly what data will be removed.",
|
||||
"properties": {
|
||||
"since": {
|
||||
"$ref": "extensionTypes.Date",
|
||||
"optional": true,
|
||||
"description": "Remove data accumulated on or after this date, represented in milliseconds since the epoch (accessible via the <code>getTime</code> method of the JavaScript <code>Date</code> object). If absent, defaults to 0 (which would remove all browsing data)."
|
||||
},
|
||||
"hostnames": {
|
||||
"type": "array",
|
||||
"items": {"type": "string", "format": "hostname"},
|
||||
"optional": true,
|
||||
"description": "Only remove data associated with these hostnames (only applies to cookies)."
|
||||
},
|
||||
"originTypes": {
|
||||
"type": "object",
|
||||
"optional": true,
|
||||
"description": "An object whose properties specify which origin types ought to be cleared. If this object isn't specified, it defaults to clearing only \"unprotected\" origins. Please ensure that you <em>really</em> want to remove application data before adding 'protectedWeb' or 'extensions'.",
|
||||
"properties": {
|
||||
"unprotectedWeb": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "Normal websites."
|
||||
},
|
||||
"protectedWeb": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "Websites that have been installed as hosted applications (be careful!)."
|
||||
},
|
||||
"extension": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "Extensions and packaged applications a user has installed (be _really_ careful!)."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "DataTypeSet",
|
||||
"type": "object",
|
||||
"description": "A set of data types. Missing data types are interpreted as <code>false</code>.",
|
||||
"properties": {
|
||||
"cache": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "The browser's cache. Note: when removing data, this clears the <em>entire</em> cache: it is not limited to the range you specify."
|
||||
},
|
||||
"cookies": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "The browser's cookies."
|
||||
},
|
||||
"downloads": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "The browser's download list."
|
||||
},
|
||||
"formData": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "The browser's stored form data."
|
||||
},
|
||||
"history": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "The browser's history."
|
||||
},
|
||||
"indexedDB": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "Websites' IndexedDB data."
|
||||
},
|
||||
"localStorage": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "Websites' local storage data."
|
||||
},
|
||||
"serverBoundCertificates": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "Server-bound certificates."
|
||||
},
|
||||
"passwords": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "Stored passwords."
|
||||
},
|
||||
"pluginData": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "Plugins' data."
|
||||
},
|
||||
"serviceWorkers": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "Service Workers."
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"functions": [
|
||||
{
|
||||
"name": "settings",
|
||||
"description": "Reports which types of data are currently selected in the 'Clear browsing data' settings UI. Note: some of the data types included in this API are not available in the settings UI, and some UI settings control more than one data type listed here.",
|
||||
"type": "function",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "callback",
|
||||
"type": "function",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "result",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"options": {
|
||||
"$ref": "RemovalOptions"
|
||||
},
|
||||
"dataToRemove": {
|
||||
"$ref": "DataTypeSet",
|
||||
"description": "All of the types will be present in the result, with values of <code>true</code> if they are both selected to be removed and permitted to be removed, otherwise <code>false</code>."
|
||||
},
|
||||
"dataRemovalPermitted": {
|
||||
"$ref": "DataTypeSet",
|
||||
"description": "All of the types will be present in the result, with values of <code>true</code> if they are permitted to be removed (e.g., by enterprise policy) and <code>false</code> if not."
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "remove",
|
||||
"description": "Clears various types of browsing data stored in a user's profile.",
|
||||
"type": "function",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "RemovalOptions",
|
||||
"name": "options"
|
||||
},
|
||||
{
|
||||
"name": "dataToRemove",
|
||||
"$ref": "DataTypeSet",
|
||||
"description": "The set of data types to remove."
|
||||
},
|
||||
{
|
||||
"name": "callback",
|
||||
"type": "function",
|
||||
"description": "Called when deletion has completed.",
|
||||
"optional": true,
|
||||
"parameters": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "removeAppcache",
|
||||
"description": "Clears websites' appcache data.",
|
||||
"type": "function",
|
||||
"async": "callback",
|
||||
"unsupported": true,
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "RemovalOptions",
|
||||
"name": "options"
|
||||
},
|
||||
{
|
||||
"name": "callback",
|
||||
"type": "function",
|
||||
"description": "Called when websites' appcache data has been cleared.",
|
||||
"optional": true,
|
||||
"parameters": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "removeCache",
|
||||
"description": "Clears the browser's cache.",
|
||||
"type": "function",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "RemovalOptions",
|
||||
"name": "options"
|
||||
},
|
||||
{
|
||||
"name": "callback",
|
||||
"type": "function",
|
||||
"description": "Called when the browser's cache has been cleared.",
|
||||
"optional": true,
|
||||
"parameters": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "removeCookies",
|
||||
"description": "Clears the browser's cookies and server-bound certificates modified within a particular timeframe.",
|
||||
"type": "function",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "RemovalOptions",
|
||||
"name": "options"
|
||||
},
|
||||
{
|
||||
"name": "callback",
|
||||
"type": "function",
|
||||
"description": "Called when the browser's cookies and server-bound certificates have been cleared.",
|
||||
"optional": true,
|
||||
"parameters": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "removeDownloads",
|
||||
"description": "Clears the browser's list of downloaded files (<em>not</em> the downloaded files themselves).",
|
||||
"type": "function",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "RemovalOptions",
|
||||
"name": "options"
|
||||
},
|
||||
{
|
||||
"name": "callback",
|
||||
"type": "function",
|
||||
"description": "Called when the browser's list of downloaded files has been cleared.",
|
||||
"optional": true,
|
||||
"parameters": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "removeFileSystems",
|
||||
"description": "Clears websites' file system data.",
|
||||
"type": "function",
|
||||
"async": "callback",
|
||||
"unsupported": true,
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "RemovalOptions",
|
||||
"name": "options"
|
||||
},
|
||||
{
|
||||
"name": "callback",
|
||||
"type": "function",
|
||||
"description": "Called when websites' file systems have been cleared.",
|
||||
"optional": true,
|
||||
"parameters": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "removeFormData",
|
||||
"description": "Clears the browser's stored form data (autofill).",
|
||||
"type": "function",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "RemovalOptions",
|
||||
"name": "options"
|
||||
},
|
||||
{
|
||||
"name": "callback",
|
||||
"type": "function",
|
||||
"description": "Called when the browser's form data has been cleared.",
|
||||
"optional": true,
|
||||
"parameters": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "removeHistory",
|
||||
"description": "Clears the browser's history.",
|
||||
"type": "function",
|
||||
"async": "callback",
|
||||
"unsupported": true,
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "RemovalOptions",
|
||||
"name": "options"
|
||||
},
|
||||
{
|
||||
"name": "callback",
|
||||
"type": "function",
|
||||
"description": "Called when the browser's history has cleared.",
|
||||
"optional": true,
|
||||
"parameters": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "removeIndexedDB",
|
||||
"description": "Clears websites' IndexedDB data.",
|
||||
"type": "function",
|
||||
"async": "callback",
|
||||
"unsupported": true,
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "RemovalOptions",
|
||||
"name": "options"
|
||||
},
|
||||
{
|
||||
"name": "callback",
|
||||
"type": "function",
|
||||
"description": "Called when websites' IndexedDB data has been cleared.",
|
||||
"optional": true,
|
||||
"parameters": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "removeLocalStorage",
|
||||
"description": "Clears websites' local storage data.",
|
||||
"type": "function",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "RemovalOptions",
|
||||
"name": "options"
|
||||
},
|
||||
{
|
||||
"name": "callback",
|
||||
"type": "function",
|
||||
"description": "Called when websites' local storage has been cleared.",
|
||||
"optional": true,
|
||||
"parameters": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "removePluginData",
|
||||
"description": "Clears plugins' data.",
|
||||
"type": "function",
|
||||
"async": "callback",
|
||||
"unsupported": true,
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "RemovalOptions",
|
||||
"name": "options"
|
||||
},
|
||||
{
|
||||
"name": "callback",
|
||||
"type": "function",
|
||||
"description": "Called when plugins' data has been cleared.",
|
||||
"optional": true,
|
||||
"parameters": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "removePasswords",
|
||||
"description": "Clears the browser's stored passwords.",
|
||||
"type": "function",
|
||||
"async": "callback",
|
||||
"unsupported": true,
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "RemovalOptions",
|
||||
"name": "options"
|
||||
},
|
||||
{
|
||||
"name": "callback",
|
||||
"type": "function",
|
||||
"description": "Called when the browser's passwords have been cleared.",
|
||||
"optional": true,
|
||||
"parameters": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "removeWebSQL",
|
||||
"description": "Clears websites' WebSQL data.",
|
||||
"type": "function",
|
||||
"async": "callback",
|
||||
"unsupported": true,
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "RemovalOptions",
|
||||
"name": "options"
|
||||
},
|
||||
{
|
||||
"name": "callback",
|
||||
"type": "function",
|
||||
"description": "Called when websites' WebSQL databases have been cleared.",
|
||||
"optional": true,
|
||||
"parameters": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
|
@ -3,6 +3,5 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
geckoview.jar:
|
||||
content/schemas/browsing_data.json
|
||||
content/schemas/gecko_view_addons.json
|
||||
content/schemas/tabs.json
|
||||
|
|
|
@ -4,8 +4,4 @@ support-files =
|
|||
../../../../../../toolkit/components/extensions/test/mochitest/chrome_cleanup_script.js
|
||||
tags = webextensions
|
||||
|
||||
[test_ext_browsingData_cookies_cache.html]
|
||||
[test_ext_browsingData_downloads.html]
|
||||
[test_ext_browsingData_formdata.html]
|
||||
[test_ext_browsingData_settings.html]
|
||||
[test_ext_options_ui.html]
|
||||
|
|
|
@ -1,147 +0,0 @@
|
|||
<!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/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 {Services} = ChromeUtils.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, {}, Ci.nsICookie.SAMESITE_NONE);
|
||||
ok(Services.cookies.cookieExists(cookie.host, cookie.path, cookie.name, {}), `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) => {
|
||||
if (msg == "removeCookies") {
|
||||
await browser.browsingData.removeCookies(options);
|
||||
} else {
|
||||
await browser.browsingData.remove(options, {cookies: true});
|
||||
}
|
||||
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.host, oldCookie.path, oldCookie.name, {}), "Old cookie was not removed.");
|
||||
ok(!Services.cookies.cookieExists(COOKIE.host, COOKIE.path, COOKIE.name, {}), "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.host, oldCookie.path, oldCookie.name, {}), "Old cookie was removed.");
|
||||
ok(!Services.cookies.cookieExists(COOKIE.host, COOKIE.path, COOKIE.name, {}), "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.host, COOKIE.path, COOKIE.name, {}), `Cookie ${COOKIE.name} was removed.`);
|
||||
ok(!Services.cookies.cookieExists(oldCookie.host, oldCookie.path, oldCookie.name, {}), `Cookie ${oldCookie.name} was removed.`);
|
||||
}
|
||||
|
||||
await extension.startup();
|
||||
|
||||
await testRemovalMethod("removeCookies");
|
||||
await testRemovalMethod("remove");
|
||||
|
||||
await extension.unload();
|
||||
});
|
||||
|
||||
add_task(async function testCache() {
|
||||
function background() {
|
||||
browser.test.onMessage.addListener(async msg => {
|
||||
if (msg == "removeCache") {
|
||||
await browser.browsingData.removeCache({});
|
||||
} else {
|
||||
await browser.browsingData.remove({}, {cache: true});
|
||||
}
|
||||
browser.test.sendMessage("cacheRemoved");
|
||||
});
|
||||
}
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
background,
|
||||
manifest: {
|
||||
permissions: ["browsingData"],
|
||||
},
|
||||
});
|
||||
|
||||
// Returns a promise when 'cacheservice:empty-cache' event is fired
|
||||
function topicObserved() {
|
||||
return new Promise(resolve => {
|
||||
let observe = (subject, topic, data) => {
|
||||
Services.obs.removeObserver(observe, "cacheservice:empty-cache");
|
||||
resolve(data);
|
||||
};
|
||||
Services.obs.addObserver(observe, "cacheservice:empty-cache");
|
||||
});
|
||||
}
|
||||
|
||||
async function testRemovalMethod(method) {
|
||||
// We can assume the notification works properly, so we only need to observe
|
||||
// the notification to know the cache was cleared.
|
||||
let awaitNotification = topicObserved();
|
||||
extension.sendMessage(method);
|
||||
await awaitNotification;
|
||||
await extension.awaitMessage("cacheRemoved");
|
||||
}
|
||||
|
||||
await extension.startup();
|
||||
|
||||
await testRemovalMethod("removeCache");
|
||||
await testRemovalMethod("remove");
|
||||
|
||||
await extension.unload();
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,123 +0,0 @@
|
|||
<!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/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";
|
||||
|
||||
var {Downloads} = ChromeUtils.import("resource://gre/modules/Downloads.jsm");
|
||||
|
||||
const OLD_NAMES = {[Downloads.PUBLIC]: "old-public", [Downloads.PRIVATE]: "old-private"};
|
||||
const RECENT_NAMES = {[Downloads.PUBLIC]: "recent-public", [Downloads.PRIVATE]: "recent-private"};
|
||||
const REFERENCE_DATE = new Date();
|
||||
const OLD_DATE = new Date(Number(REFERENCE_DATE) - 10000);
|
||||
|
||||
async function downloadExists(list, path) {
|
||||
let listArray = await list.getAll();
|
||||
return listArray.some(i => i.target.path == path);
|
||||
}
|
||||
|
||||
async function checkDownloads(expectOldExists = true, expectRecentExists = true) {
|
||||
for (let listType of [Downloads.PUBLIC, Downloads.PRIVATE]) {
|
||||
let downloadsList = await Downloads.getList(listType);
|
||||
is(
|
||||
(await downloadExists(downloadsList, OLD_NAMES[listType])),
|
||||
expectOldExists,
|
||||
`Fake old download ${(expectOldExists) ? "was found" : "was removed"}.`);
|
||||
is(
|
||||
(await downloadExists(downloadsList, RECENT_NAMES[listType])),
|
||||
expectRecentExists,
|
||||
`Fake recent download ${(expectRecentExists) ? "was found" : "was removed"}.`);
|
||||
}
|
||||
}
|
||||
|
||||
async function setupDownloads() {
|
||||
let downloadsList = await Downloads.getList(Downloads.ALL);
|
||||
await downloadsList.removeFinished();
|
||||
|
||||
for (let listType of [Downloads.PUBLIC, Downloads.PRIVATE]) {
|
||||
downloadsList = await Downloads.getList(listType);
|
||||
let download = await Downloads.createDownload({
|
||||
source: {
|
||||
url: "https://bugzilla.mozilla.org/show_bug.cgi?id=1363001",
|
||||
isPrivate: listType == Downloads.PRIVATE},
|
||||
target: OLD_NAMES[listType],
|
||||
});
|
||||
download.startTime = OLD_DATE;
|
||||
download.canceled = true;
|
||||
await downloadsList.add(download);
|
||||
|
||||
download = await Downloads.createDownload({
|
||||
source: {
|
||||
url: "https://bugzilla.mozilla.org/show_bug.cgi?id=1363001",
|
||||
isPrivate: listType == Downloads.PRIVATE},
|
||||
target: RECENT_NAMES[listType],
|
||||
});
|
||||
download.startTime = REFERENCE_DATE;
|
||||
download.canceled = true;
|
||||
await downloadsList.add(download);
|
||||
}
|
||||
|
||||
// Confirm everything worked.
|
||||
downloadsList = await Downloads.getList(Downloads.ALL);
|
||||
is((await downloadsList.getAll()).length, 4, "4 fake downloads added.");
|
||||
checkDownloads();
|
||||
}
|
||||
|
||||
add_task(async function testDownloads() {
|
||||
function background() {
|
||||
browser.test.onMessage.addListener(async (msg, options) => {
|
||||
if (msg == "removeDownloads") {
|
||||
await browser.browsingData.removeDownloads(options);
|
||||
} else {
|
||||
await browser.browsingData.remove(options, {downloads: true});
|
||||
}
|
||||
browser.test.sendMessage("downloadsRemoved");
|
||||
});
|
||||
}
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
background,
|
||||
manifest: {
|
||||
permissions: ["browsingData"],
|
||||
},
|
||||
});
|
||||
|
||||
async function testRemovalMethod(method) {
|
||||
// Clear downloads with no since value.
|
||||
await setupDownloads();
|
||||
extension.sendMessage(method, {});
|
||||
await extension.awaitMessage("downloadsRemoved");
|
||||
await checkDownloads(false, false);
|
||||
|
||||
// Clear downloads with recent since value.
|
||||
await setupDownloads();
|
||||
extension.sendMessage(method, {since: REFERENCE_DATE});
|
||||
await extension.awaitMessage("downloadsRemoved");
|
||||
await checkDownloads(true, false);
|
||||
|
||||
// Clear downloads with old since value.
|
||||
await setupDownloads();
|
||||
extension.sendMessage(method, {since: REFERENCE_DATE - 100000});
|
||||
await extension.awaitMessage("downloadsRemoved");
|
||||
await checkDownloads(false, false);
|
||||
}
|
||||
|
||||
await extension.startup();
|
||||
|
||||
await testRemovalMethod("removeDownloads");
|
||||
await testRemovalMethod("remove");
|
||||
|
||||
await extension.unload();
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,149 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>BrowsingData FormData test</title>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.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";
|
||||
|
||||
var {FormHistory} = ChromeUtils.import("resource://gre/modules/FormHistory.jsm");
|
||||
|
||||
const REFERENCE_DATE = Date.now();
|
||||
|
||||
function countEntries(fieldname, message, expected) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let callback = {
|
||||
handleResult: result => {
|
||||
is(result, expected, message);
|
||||
resolve();
|
||||
},
|
||||
handleError: reject,
|
||||
};
|
||||
|
||||
FormHistory.count({fieldname}, callback);
|
||||
});
|
||||
}
|
||||
|
||||
async function setupFormHistory() {
|
||||
function searchEntries(terms, params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let callback = {
|
||||
handleResult: resolve,
|
||||
handleError: reject,
|
||||
};
|
||||
|
||||
FormHistory.search(terms, params, callback);
|
||||
});
|
||||
}
|
||||
|
||||
function update(changes) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let callback = {
|
||||
handleError: reject,
|
||||
handleCompletion: resolve,
|
||||
};
|
||||
FormHistory.update(changes, callback);
|
||||
});
|
||||
}
|
||||
|
||||
// Make sure we've got a clean DB to start with, then add the entries we'll be testing.
|
||||
await update([
|
||||
{op: "remove"},
|
||||
{
|
||||
op: "add",
|
||||
fieldname: "reference",
|
||||
value: "reference",
|
||||
}, {
|
||||
op: "add",
|
||||
fieldname: "10secondsAgo",
|
||||
value: "10s",
|
||||
}, {
|
||||
op: "add",
|
||||
fieldname: "10minutesAgo",
|
||||
value: "10m",
|
||||
}]);
|
||||
|
||||
// Age the entries to the proper vintage.
|
||||
let timestamp = REFERENCE_DATE * 1000;
|
||||
let result = await searchEntries(["guid"], {fieldname: "reference"});
|
||||
await update({op: "update", firstUsed: timestamp, guid: result.guid});
|
||||
|
||||
timestamp = (REFERENCE_DATE - 10000) * 1000;
|
||||
result = await searchEntries(["guid"], {fieldname: "10secondsAgo"});
|
||||
await update({op: "update", firstUsed: timestamp, guid: result.guid});
|
||||
|
||||
timestamp = (REFERENCE_DATE - 10000 * 60) * 1000;
|
||||
result = await searchEntries(["guid"], {fieldname: "10minutesAgo"});
|
||||
await update({op: "update", firstUsed: timestamp, guid: result.guid});
|
||||
|
||||
// Sanity check.
|
||||
await countEntries("reference", "Checking for 10minutes form history entry creation", 1);
|
||||
await countEntries("10secondsAgo", "Checking for 1hour form history entry creation", 1);
|
||||
await countEntries("10minutesAgo", "Checking for 1hour10minutes form history entry creation", 1);
|
||||
}
|
||||
|
||||
add_task(async function testFormData() {
|
||||
function background() {
|
||||
browser.test.onMessage.addListener(async (msg, options) => {
|
||||
if (msg == "removeFormData") {
|
||||
await browser.browsingData.removeFormData(options);
|
||||
} else {
|
||||
await browser.browsingData.remove(options, {formData: true});
|
||||
}
|
||||
browser.test.sendMessage("formDataRemoved");
|
||||
});
|
||||
}
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
background,
|
||||
manifest: {
|
||||
permissions: ["browsingData"],
|
||||
},
|
||||
});
|
||||
|
||||
async function testRemovalMethod(method) {
|
||||
// Clear form data with no since value.
|
||||
await setupFormHistory();
|
||||
extension.sendMessage(method, {});
|
||||
await extension.awaitMessage("formDataRemoved");
|
||||
|
||||
await countEntries("reference", "reference form entry should be deleted.", 0);
|
||||
await countEntries("10secondsAgo", "10secondsAgo form entry should be deleted.", 0);
|
||||
await countEntries("10minutesAgo", "10minutesAgo form entry should be deleted.", 0);
|
||||
|
||||
// Clear form data with recent since value.
|
||||
await setupFormHistory();
|
||||
extension.sendMessage(method, {since: REFERENCE_DATE});
|
||||
await extension.awaitMessage("formDataRemoved");
|
||||
|
||||
await countEntries("reference", "reference form entry should be deleted.", 0);
|
||||
await countEntries("10secondsAgo", "10secondsAgo form entry should still exist.", 1);
|
||||
await countEntries("10minutesAgo", "10minutesAgo form entry should still exist.", 1);
|
||||
|
||||
// Clear form data with old since value.
|
||||
await setupFormHistory();
|
||||
extension.sendMessage(method, {since: REFERENCE_DATE - 1000000});
|
||||
await extension.awaitMessage("formDataRemoved");
|
||||
|
||||
await countEntries("reference", "reference form entry should be deleted.", 0);
|
||||
await countEntries("10secondsAgo", "10secondsAgo form entry should be deleted.", 0);
|
||||
await countEntries("10minutesAgo", "10minutesAgo form entry should be deleted.", 0);
|
||||
}
|
||||
|
||||
await extension.startup();
|
||||
|
||||
await testRemovalMethod("removeFormData");
|
||||
await testRemovalMethod("remove");
|
||||
|
||||
await extension.unload();
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,98 +0,0 @@
|
|||
<!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/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";
|
||||
|
||||
var {SharedPreferences} = ChromeUtils.import("resource://gre/modules/SharedPreferences.jsm");
|
||||
|
||||
const PREF_DOMAIN = "android.not_a_preference.privacy.clear";
|
||||
const PREF_KEY_PREFIX = "private.data.";
|
||||
const SETTINGS_LIST = ["cache", "cookies", "history", "formData", "downloads"];
|
||||
|
||||
function checkPrefs(key, actualValue, prefs, prefSuffix) {
|
||||
let prefValue = prefs.includes(`${PREF_KEY_PREFIX}${prefSuffix}`);
|
||||
is(actualValue, prefValue, `${key} property of dataToRemove matches the expected pref.`);
|
||||
}
|
||||
|
||||
function testSettingsPreferences(dataToRemove) {
|
||||
let prefs = SharedPreferences.forProfile().getSetPref(PREF_DOMAIN);
|
||||
|
||||
for (let key of Object.keys(dataToRemove)) {
|
||||
switch (key) {
|
||||
case "formData":
|
||||
checkPrefs(key, dataToRemove[key], prefs, "formdata");
|
||||
break;
|
||||
case "cookies":
|
||||
checkPrefs(key, dataToRemove[key], prefs, "cookies_sessions");
|
||||
break;
|
||||
case "downloads":
|
||||
checkPrefs(key, dataToRemove[key], prefs, "downloadFiles");
|
||||
break;
|
||||
default:
|
||||
checkPrefs(key, dataToRemove[key], prefs, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add_task(async function testSettings() {
|
||||
function background() {
|
||||
browser.test.onMessage.addListener(async (msg) => {
|
||||
if (msg == "retrieve-settings") {
|
||||
let settings = await browser.browsingData.settings();
|
||||
browser.test.sendMessage("settings", settings);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
background,
|
||||
manifest: {
|
||||
"permissions": ["browsingData"],
|
||||
},
|
||||
});
|
||||
|
||||
await extension.startup();
|
||||
|
||||
extension.sendMessage("retrieve-settings");
|
||||
let {options, dataToRemove, dataRemovalPermitted} = await extension.awaitMessage("settings");
|
||||
|
||||
// Verify that we get the keys we expect.
|
||||
is(SETTINGS_LIST.length, Object.keys(dataToRemove).length, `dataToRemove contains expected no of keys`);
|
||||
is(SETTINGS_LIST.length, Object.keys(dataRemovalPermitted).length, `dataRemovalPermitted contains expected no of keys`);
|
||||
for (let key of SETTINGS_LIST) {
|
||||
is(true, dataRemovalPermitted[key],
|
||||
`${key} property of dataRemovalPermitted matches the expected value.`);
|
||||
}
|
||||
|
||||
// Verify values of dataToRemove keys are as expected.
|
||||
testSettingsPreferences(dataToRemove);
|
||||
|
||||
// Verify object options returned as expected.
|
||||
// For now, We do not provide option to delete history by time, so,
|
||||
// since value is given 0, which means Everything.
|
||||
is(options.since, 0, `options contains expected value.`);
|
||||
|
||||
// Explicitly set some prefs to true
|
||||
const NEW_PREFS = ["private.data.cache", "private.data.cookies_sessions"];
|
||||
|
||||
SharedPreferences.forProfile().setSetPref(PREF_DOMAIN, NEW_PREFS);
|
||||
|
||||
extension.sendMessage("retrieve-settings");
|
||||
let settings = await extension.awaitMessage("settings");
|
||||
testSettingsPreferences(settings.dataToRemove);
|
||||
|
||||
await extension.unload();
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,503 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const { XPCOMUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
const { Integration } = ChromeUtils.import(
|
||||
"resource://gre/modules/Integration.jsm"
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
Accounts: "resource://gre/modules/Accounts.jsm",
|
||||
Downloads: "resource://gre/modules/Downloads.jsm",
|
||||
EventDispatcher: "resource://gre/modules/Messaging.jsm",
|
||||
FormHistory: "resource://gre/modules/FormHistory.jsm",
|
||||
OfflineAppCacheHelper: "resource://gre/modules/offlineAppCache.jsm",
|
||||
OS: "resource://gre/modules/osfile.jsm",
|
||||
ServiceWorkerCleanUp: "resource://gre/modules/ServiceWorkerCleanUp.jsm",
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetters(this, {
|
||||
quotaManagerService: [
|
||||
"@mozilla.org/dom/quota-manager-service;1",
|
||||
"nsIQuotaManagerService",
|
||||
],
|
||||
});
|
||||
|
||||
/* global DownloadIntegration */
|
||||
Integration.downloads.defineModuleGetter(
|
||||
this,
|
||||
"DownloadIntegration",
|
||||
"resource://gre/modules/DownloadIntegration.jsm"
|
||||
);
|
||||
|
||||
var EXPORTED_SYMBOLS = ["Sanitizer"];
|
||||
|
||||
function Sanitizer() {}
|
||||
Sanitizer.prototype = {
|
||||
clearItem: function(aItemName, startTime, clearUnfinishedDownloads) {
|
||||
// Only a subset of items support deletion with startTime.
|
||||
// Those who do not will be rejected with error message.
|
||||
if (typeof startTime != "undefined") {
|
||||
switch (aItemName) {
|
||||
// Normal call to DownloadFiles remove actual data from storage, but our web-extension consumer
|
||||
// deletes only download history. So, for this reason we are passing a flag 'deleteFiles'.
|
||||
case "downloadHistory":
|
||||
return this._clear("downloadFiles", {
|
||||
startTime,
|
||||
deleteFiles: false,
|
||||
});
|
||||
case "formdata":
|
||||
return this._clear(aItemName, { startTime });
|
||||
default:
|
||||
return Promise.reject({
|
||||
message: `Invalid argument: ${aItemName} does not support startTime argument.`,
|
||||
});
|
||||
}
|
||||
} else if (
|
||||
aItemName === "downloadFiles" &&
|
||||
typeof clearUnfinishedDownloads != "undefined"
|
||||
) {
|
||||
return this._clear(aItemName, { clearUnfinishedDownloads });
|
||||
} else {
|
||||
return this._clear(aItemName);
|
||||
}
|
||||
},
|
||||
|
||||
_clear: function(aItemName, options) {
|
||||
let item = this.items[aItemName];
|
||||
let canClear = item.canClear;
|
||||
if (typeof canClear == "function") {
|
||||
let maybeDoClear = async () => {
|
||||
let canClearResult = await new Promise(resolve => {
|
||||
canClear(resolve);
|
||||
});
|
||||
|
||||
if (canClearResult) {
|
||||
return item.clear(options);
|
||||
}
|
||||
};
|
||||
return maybeDoClear();
|
||||
} else if (canClear) {
|
||||
return item.clear(options);
|
||||
}
|
||||
},
|
||||
|
||||
// This code is mostly based on the Sanitizer code for desktop Firefox
|
||||
// (browser/modules/Sanitzer.jsm), however over the course of time some
|
||||
// general differences have evolved:
|
||||
// - async shutdown (and seenException handling) isn't implemented in Fennec
|
||||
// - currently there is only limited support for range-based clearing of data
|
||||
|
||||
// Any further specific differences caused by architectural differences between
|
||||
// Fennec and desktop Firefox are documented below for each item.
|
||||
items: {
|
||||
// The difference is specifically the Sanitize:Cache message,
|
||||
// so that the Android front-end can clear its caches as well,
|
||||
// while everything else is unchanged.
|
||||
cache: {
|
||||
clear: function() {
|
||||
let refObj = {};
|
||||
TelemetryStopwatch.start("FX_SANITIZE_CACHE", refObj);
|
||||
|
||||
try {
|
||||
Services.cache2.clear();
|
||||
} catch (er) {}
|
||||
|
||||
let imageCache = Cc["@mozilla.org/image/tools;1"]
|
||||
.getService(Ci.imgITools)
|
||||
.getImgCacheForDocument(null);
|
||||
try {
|
||||
imageCache.clearCache(false); // true=chrome, false=content
|
||||
} catch (er) {}
|
||||
|
||||
return EventDispatcher.instance
|
||||
.sendRequestForResult({ type: "Sanitize:Cache" })
|
||||
.catch(err => {
|
||||
Cu.reportError(
|
||||
`Java-side cache clearing failed with error: ${err}`
|
||||
);
|
||||
})
|
||||
.then(() => {
|
||||
TelemetryStopwatch.finish("FX_SANITIZE_CACHE", refObj);
|
||||
});
|
||||
},
|
||||
|
||||
get canClear() {
|
||||
return true;
|
||||
},
|
||||
},
|
||||
|
||||
// Compared to desktop, we don't clear plugin data, as plugins
|
||||
// aren't supported on Android.
|
||||
cookies: {
|
||||
clear: function() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
let refObj = {};
|
||||
TelemetryStopwatch.start("FX_SANITIZE_COOKIES_2", refObj);
|
||||
|
||||
Services.cookies.removeAll();
|
||||
|
||||
TelemetryStopwatch.finish("FX_SANITIZE_COOKIES_2", refObj);
|
||||
|
||||
// Clear deviceIds. Done asynchronously (returns before complete).
|
||||
try {
|
||||
let mediaMgr = Cc["@mozilla.org/mediaManagerService;1"].getService(
|
||||
Ci.nsIMediaManagerService
|
||||
);
|
||||
mediaMgr.sanitizeDeviceIds(0);
|
||||
} catch (er) {}
|
||||
|
||||
resolve();
|
||||
});
|
||||
},
|
||||
|
||||
get canClear() {
|
||||
return true;
|
||||
},
|
||||
},
|
||||
|
||||
// Same as desktop Firefox.
|
||||
siteSettings: {
|
||||
async clear() {
|
||||
let refObj = {};
|
||||
TelemetryStopwatch.start("FX_SANITIZE_SITESETTINGS", refObj);
|
||||
|
||||
// Clear site-specific permissions like "Allow this site to open popups"
|
||||
Services.perms.removeAll();
|
||||
|
||||
// Clear site-specific settings like page-zoom level
|
||||
Cc["@mozilla.org/content-pref/service;1"]
|
||||
.getService(Ci.nsIContentPrefService2)
|
||||
.removeAllDomains(null);
|
||||
|
||||
// Clear site security settings
|
||||
var sss = Cc["@mozilla.org/ssservice;1"].getService(
|
||||
Ci.nsISiteSecurityService
|
||||
);
|
||||
sss.clearAll();
|
||||
|
||||
// Clear push subscriptions
|
||||
await new Promise((resolve, reject) => {
|
||||
let push = Cc["@mozilla.org/push/Service;1"].getService(
|
||||
Ci.nsIPushService
|
||||
);
|
||||
push.clearForDomain("*", status => {
|
||||
if (Components.isSuccessCode(status)) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error("Error clearing push subscriptions: " + status));
|
||||
}
|
||||
});
|
||||
});
|
||||
TelemetryStopwatch.finish("FX_SANITIZE_SITESETTINGS", refObj);
|
||||
},
|
||||
|
||||
get canClear() {
|
||||
return true;
|
||||
},
|
||||
},
|
||||
|
||||
// Same as desktop Firefox.
|
||||
offlineApps: {
|
||||
async clear() {
|
||||
// AppCache
|
||||
// This doesn't wait for the cleanup to be complete.
|
||||
OfflineAppCacheHelper.clear();
|
||||
|
||||
// LocalStorage
|
||||
Services.obs.notifyObservers(null, "extension:purge-localStorage");
|
||||
|
||||
// ServiceWorkers
|
||||
await ServiceWorkerCleanUp.removeAll();
|
||||
|
||||
// QuotaManager
|
||||
let promises = [];
|
||||
await new Promise(resolve => {
|
||||
quotaManagerService.getUsage(request => {
|
||||
if (request.resultCode != Cr.NS_OK) {
|
||||
// We are probably shutting down. We don't want to propagate the
|
||||
// error, rejecting the promise.
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
for (let item of request.result) {
|
||||
let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
|
||||
item.origin
|
||||
);
|
||||
let uri = principal.URI;
|
||||
if (
|
||||
uri.scheme == "http" ||
|
||||
uri.scheme == "https" ||
|
||||
uri.scheme == "file"
|
||||
) {
|
||||
promises.push(
|
||||
new Promise(r => {
|
||||
let req = quotaManagerService.clearStoragesForPrincipal(
|
||||
principal
|
||||
);
|
||||
req.callback = () => {
|
||||
r();
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
},
|
||||
|
||||
get canClear() {
|
||||
return true;
|
||||
},
|
||||
},
|
||||
|
||||
// History on Android is implemented by the Java frontend and requires
|
||||
// different handling. Everything else is the same as for desktop Firefox.
|
||||
history: {
|
||||
clear: function() {
|
||||
let refObj = {};
|
||||
TelemetryStopwatch.start("FX_SANITIZE_HISTORY", refObj);
|
||||
|
||||
return EventDispatcher.instance
|
||||
.sendRequestForResult({ type: "Sanitize:ClearHistory" })
|
||||
.catch(e => Cu.reportError("Java-side history clearing failed: " + e))
|
||||
.then(function() {
|
||||
TelemetryStopwatch.finish("FX_SANITIZE_HISTORY", refObj);
|
||||
try {
|
||||
Services.obs.notifyObservers(
|
||||
null,
|
||||
"browser:purge-session-history"
|
||||
);
|
||||
} catch (e) {}
|
||||
|
||||
try {
|
||||
var predictor = Cc["@mozilla.org/network/predictor;1"].getService(
|
||||
Ci.nsINetworkPredictor
|
||||
);
|
||||
predictor.reset();
|
||||
} catch (e) {}
|
||||
});
|
||||
},
|
||||
|
||||
get canClear() {
|
||||
// bug 347231: Always allow clearing history due to dependencies on
|
||||
// the browser:purge-session-history notification. (like error console)
|
||||
return true;
|
||||
},
|
||||
},
|
||||
|
||||
// Equivalent to openWindows on desktop, but specific to Fennec's implementation
|
||||
// of tabbed browsing and the session store.
|
||||
openTabs: {
|
||||
clear: function() {
|
||||
let refObj = {};
|
||||
TelemetryStopwatch.start("FX_SANITIZE_OPENWINDOWS", refObj);
|
||||
|
||||
return EventDispatcher.instance
|
||||
.sendRequestForResult({ type: "Sanitize:OpenTabs" })
|
||||
.catch(e => Cu.reportError("Java-side tab clearing failed: " + e))
|
||||
.then(function() {
|
||||
try {
|
||||
// clear "Recently Closed" tabs in Android App
|
||||
Services.obs.notifyObservers(null, "browser:purge-session-tabs");
|
||||
} catch (e) {}
|
||||
TelemetryStopwatch.finish("FX_SANITIZE_OPENWINDOWS", refObj);
|
||||
});
|
||||
},
|
||||
|
||||
get canClear() {
|
||||
return true;
|
||||
},
|
||||
},
|
||||
|
||||
// Specific to Fennec.
|
||||
searchHistory: {
|
||||
clear: function() {
|
||||
return EventDispatcher.instance
|
||||
.sendRequestForResult({
|
||||
type: "Sanitize:ClearHistory",
|
||||
clearSearchHistory: true,
|
||||
})
|
||||
.catch(e =>
|
||||
Cu.reportError("Java-side search history clearing failed: " + e)
|
||||
);
|
||||
},
|
||||
|
||||
get canClear() {
|
||||
return true;
|
||||
},
|
||||
},
|
||||
|
||||
// Browser search is handled by searchHistory above and the find bar doesn't
|
||||
// require extra handling. FormHistory itself is cleared like on desktop.
|
||||
formdata: {
|
||||
clear: function({ startTime = 0 } = {}) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
let refObj = {};
|
||||
TelemetryStopwatch.start("FX_SANITIZE_FORMDATA", refObj);
|
||||
|
||||
// Conver time to microseconds
|
||||
let time = startTime * 1000;
|
||||
FormHistory.update(
|
||||
{
|
||||
op: "remove",
|
||||
firstUsedStart: time,
|
||||
},
|
||||
{
|
||||
handleCompletion() {
|
||||
TelemetryStopwatch.finish("FX_SANITIZE_FORMDATA", refObj);
|
||||
resolve();
|
||||
},
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
canClear: function(aCallback) {
|
||||
let count = 0;
|
||||
let countDone = {
|
||||
handleResult: function(aResult) {
|
||||
count = aResult;
|
||||
},
|
||||
handleError: function(aError) {
|
||||
Cu.reportError(aError);
|
||||
},
|
||||
handleCompletion: function(aReason) {
|
||||
aCallback(aReason == 0 && count > 0);
|
||||
},
|
||||
};
|
||||
FormHistory.count({}, countDone);
|
||||
},
|
||||
},
|
||||
|
||||
// Adapted from desktop, but heavily modified - see comments below.
|
||||
downloadFiles: {
|
||||
async clear({
|
||||
startTime = 0,
|
||||
deleteFiles = true,
|
||||
clearUnfinishedDownloads = false,
|
||||
} = {}) {
|
||||
let refObj = {};
|
||||
TelemetryStopwatch.start("FX_SANITIZE_DOWNLOADS", refObj);
|
||||
|
||||
let list = await Downloads.getList(Downloads.ALL);
|
||||
let downloads = await list.getAll();
|
||||
var finalizePromises = [];
|
||||
|
||||
// Logic copied from DownloadList.removeFinished. Ideally, we would
|
||||
// just use that method directly, but we want to be able to remove the
|
||||
// downloaded files as well.
|
||||
for (let download of downloads) {
|
||||
let downloadFinished =
|
||||
download.stopped && (!download.hasPartialData || download.error);
|
||||
if (
|
||||
(downloadFinished || clearUnfinishedDownloads) &&
|
||||
download.startTime.getTime() >= startTime
|
||||
) {
|
||||
// Remove the download first, so that the views don't get the change
|
||||
// notifications that may occur during finalization.
|
||||
await list.remove(download);
|
||||
// Ensure that the download is stopped and no partial data is kept.
|
||||
// This works even if the download state has changed meanwhile. We
|
||||
// don't need to wait for the procedure to be complete before
|
||||
// processing the other downloads in the list.
|
||||
finalizePromises.push(
|
||||
download.finalize(true).then(() => null, Cu.reportError)
|
||||
);
|
||||
|
||||
if (deleteFiles) {
|
||||
// Delete the downloaded files themselves.
|
||||
OS.File.remove(download.target.path).then(
|
||||
() => null,
|
||||
ex => {
|
||||
if (!(ex instanceof OS.File.Error && ex.becauseNoSuchFile)) {
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(finalizePromises);
|
||||
await DownloadIntegration.forceSave();
|
||||
TelemetryStopwatch.finish("FX_SANITIZE_DOWNLOADS", refObj);
|
||||
},
|
||||
|
||||
get canClear() {
|
||||
return true;
|
||||
},
|
||||
},
|
||||
|
||||
// Specific to Fennec.
|
||||
passwords: {
|
||||
clear: function() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
Services.logins.removeAllLogins();
|
||||
resolve();
|
||||
});
|
||||
},
|
||||
|
||||
get canClear() {
|
||||
let count = Services.logins.countLogins("", "", ""); // count all logins
|
||||
return count > 0;
|
||||
},
|
||||
},
|
||||
|
||||
// Same as desktop Firefox.
|
||||
sessions: {
|
||||
clear: function() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
let refObj = {};
|
||||
TelemetryStopwatch.start("FX_SANITIZE_SESSIONS", refObj);
|
||||
|
||||
// clear all auth tokens
|
||||
var sdr = Cc["@mozilla.org/security/sdr;1"].getService(
|
||||
Ci.nsISecretDecoderRing
|
||||
);
|
||||
sdr.logoutAndTeardown();
|
||||
|
||||
// clear FTP and plain HTTP auth sessions
|
||||
Services.obs.notifyObservers(null, "net:clear-active-logins");
|
||||
|
||||
TelemetryStopwatch.finish("FX_SANITIZE_SESSIONS", refObj);
|
||||
resolve();
|
||||
});
|
||||
},
|
||||
|
||||
get canClear() {
|
||||
return true;
|
||||
},
|
||||
},
|
||||
|
||||
// Specific to Fennec.
|
||||
syncedTabs: {
|
||||
clear: function() {
|
||||
return EventDispatcher.instance
|
||||
.sendRequestForResult({ type: "Sanitize:ClearSyncedTabs" })
|
||||
.catch(e =>
|
||||
Cu.reportError("Java-side synced tabs clearing failed: " + e)
|
||||
);
|
||||
},
|
||||
|
||||
canClear: function(aCallback) {
|
||||
Accounts.anySyncAccountsExist()
|
||||
.then(aCallback)
|
||||
.catch(function(err) {
|
||||
Cu.reportError("Java-side synced tabs clearing failed: " + err);
|
||||
aCallback(false);
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
var Sanitizer = new Sanitizer();
|
|
@ -31,7 +31,6 @@ EXTRA_JS_MODULES += [
|
|||
'MediaPlayerApp.jsm',
|
||||
'Notifications.jsm',
|
||||
'RuntimePermissions.jsm',
|
||||
'Sanitizer.jsm',
|
||||
'SharedPreferences.jsm',
|
||||
'Snackbars.jsm',
|
||||
'WebsiteMetadata.jsm'
|
||||
|
|
Загрузка…
Ссылка в новой задаче