summaryrefslogtreecommitdiffstats
path: root/application/basilisk/components/extensions/ext-browsingData.js
diff options
context:
space:
mode:
Diffstat (limited to 'application/basilisk/components/extensions/ext-browsingData.js')
-rw-r--r--application/basilisk/components/extensions/ext-browsingData.js237
1 files changed, 237 insertions, 0 deletions
diff --git a/application/basilisk/components/extensions/ext-browsingData.js b/application/basilisk/components/extensions/ext-browsingData.js
new file mode 100644
index 000000000..a927b4268
--- /dev/null
+++ b/application/basilisk/components/extensions/ext-browsingData.js
@@ -0,0 +1,237 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import("resource://gre/modules/Task.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
+ "resource://gre/modules/PlacesUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
+ "resource://gre/modules/Preferences.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Sanitizer",
+ "resource:///modules/Sanitizer.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Services",
+ "resource://gre/modules/Services.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "setTimeout",
+ "resource://gre/modules/Timer.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "serviceWorkerManager",
+ "@mozilla.org/serviceworkers/manager;1",
+ "nsIServiceWorkerManager");
+
+/**
+* A number of iterations after which to yield time back
+* to the system.
+*/
+const YIELD_PERIOD = 10;
+
+const PREF_DOMAIN = "privacy.cpd.";
+
+XPCOMUtils.defineLazyGetter(this, "sanitizer", () => {
+ let sanitizer = new Sanitizer();
+ sanitizer.prefDomain = PREF_DOMAIN;
+ return sanitizer;
+});
+
+function makeRange(options) {
+ return (options.since == null) ?
+ null :
+ [PlacesUtils.toPRTime(options.since), PlacesUtils.toPRTime(Date.now())];
+}
+
+function clearCache() {
+ // Clearing the cache does not support timestamps.
+ return sanitizer.items.cache.clear();
+}
+
+let clearCookies = Task.async(function* (options) {
+ let cookieMgr = Services.cookies;
+ // This code has been borrowed from sanitize.js.
+ let yieldCounter = 0;
+
+ if (options.since) {
+ // 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 >= PlacesUtils.toPRTime(options.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) {
+ yield new Promise(resolve => setTimeout(resolve, 0)); // Don't block the main thread too long.
+ }
+ }
+ }
+ } else {
+ // Remove everything.
+ cookieMgr.removeAll();
+ }
+});
+
+function clearDownloads(options) {
+ return sanitizer.items.downloads.clear(makeRange(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 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) {
+ 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));
+}
+
+let clearServiceWorkers = Task.async(function* () {
+ // Clearing service workers does not support timestamps.
+ let yieldCounter = 0;
+
+ // Iterate through the service workers and remove them.
+ let serviceWorkers = serviceWorkerManager.getAllRegistrations();
+ for (let i = 0; i < serviceWorkers.length; i++) {
+ let sw = serviceWorkers.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);
+ let host = sw.principal.URI.host;
+ serviceWorkerManager.removeAndPropagate(host);
+ if (++yieldCounter % YIELD_PERIOD == 0) {
+ yield new Promise(resolve => setTimeout(resolve, 0)); // Don't block the main thread too long.
+ }
+ }
+});
+
+function 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;
+ case "history":
+ removalPromises.push(clearHistory(options));
+ break;
+ case "passwords":
+ removalPromises.push(clearPasswords(options));
+ break;
+ case "pluginData":
+ removalPromises.push(clearPluginData(options));
+ break;
+ case "serviceWorkers":
+ removalPromises.push(clearServiceWorkers());
+ break;
+ default:
+ invalidDataTypes.push(dataType);
+ }
+ }
+ }
+ if (extension && invalidDataTypes.length) {
+ extension.logger.warn(
+ `Firefox does not support dataTypes: ${invalidDataTypes.toString()}.`);
+ }
+ return Promise.all(removalPromises);
+}
+
+extensions.registerSchemaAPI("browsingData", "addon_parent", context => {
+ let {extension} = context;
+ return {
+ browsingData: {
+ settings() {
+ const PREF_DOMAIN = "privacy.cpd.";
+ // The following prefs are the only ones in Firefox that match corresponding
+ // values used by Chrome when rerturning settings.
+ const PREF_LIST = ["cache", "cookies", "history", "formdata", "downloads"];
+
+ // since will be the start of what is returned by Sanitizer.getClearRange
+ // divided by 1000 to convert to ms.
+ // If Sanitizer.getClearRange returns undefined that means the range is
+ // currently "Everything", so we should set since to 0.
+ let clearRange = Sanitizer.getClearRange();
+ let since = clearRange ? clearRange[0] / 1000 : 0;
+ let options = {since};
+
+ let dataToRemove = {};
+ let dataRemovalPermitted = {};
+
+ for (let item of PREF_LIST) {
+ // The property formData needs a different case than the
+ // formdata preference.
+ const name = item === "formdata" ? "formData" : item;
+ dataToRemove[name] = Preferences.get(`${PREF_DOMAIN}${item}`);
+ // 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});
+ },
+ 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});
+ },
+ removeHistory(options) {
+ return doRemoval(options, {history: true});
+ },
+ removePasswords(options) {
+ return doRemoval(options, {passwords: true});
+ },
+ removePluginData(options) {
+ return doRemoval(options, {pluginData: true});
+ },
+ },
+ };
+});