123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518 |
- // -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- // 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/.
- Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
- XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
- "resource://gre/modules/PlacesUtils.jsm");
- XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
- "resource://gre/modules/FormHistory.jsm");
- XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
- "resource://gre/modules/Downloads.jsm");
- XPCOMUtils.defineLazyModuleGetter(this, "Promise",
- "resource://gre/modules/Promise.jsm");
- XPCOMUtils.defineLazyModuleGetter(this, "Task",
- "resource://gre/modules/Task.jsm");
- XPCOMUtils.defineLazyModuleGetter(this, "console",
- "resource://gre/modules/Console.jsm");
- function Sanitizer() {}
- Sanitizer.prototype = {
- // warning to the caller: this one may raise an exception (e.g. bug #265028)
- clearItem: function (aItemName) {
- if (this.items[aItemName].canClear) {
- this.items[aItemName].clear();
- }
- },
- canClearItem: function (aItemName, aCallback, aArg) {
- let canClear = this.items[aItemName].canClear;
- if (typeof canClear == "function") {
- canClear(aCallback, aArg);
- return false;
- }
- aCallback(aItemName, canClear, aArg);
- return canClear;
- },
-
- prefDomain: "",
- isShutDown: false,
-
- getNameFromPreference: function (aPreferenceName) {
- return aPreferenceName.substr(this.prefDomain.length);
- },
-
- /**
- * Deletes privacy sensitive data in a batch, according to user preferences.
- * Returns a promise which is resolved if no errors occurred. If an error
- * occurs, a message is reported to the console and all other items are still
- * cleared before the promise is finally rejected.
- */
- sanitize: function () {
- var deferred = Promise.defer();
- var psvc = Components.classes["@mozilla.org/preferences-service;1"]
- .getService(Components.interfaces.nsIPrefService);
- var branch = psvc.getBranch(this.prefDomain);
- var seenError = false;
- // Cache the range of times to clear
- if (this.ignoreTimespan) {
- // If we ignore timespan, clear everything
- var range = null;
- } else {
- range = this.range || Sanitizer.getClearRange();
- }
- let itemCount = Object.keys(this.items).length;
- let onItemComplete = function() {
- if (!--itemCount) {
- seenError ? deferred.reject() : deferred.resolve();
- }
- };
- for (var itemName in this.items) {
- let item = this.items[itemName];
- item.range = range;
- item.isShutDown = this.isShutDown;
- if ("clear" in item && branch.getBoolPref(itemName)) {
- let clearCallback = (itemName, aCanClear) => {
- // Some of these clear() may raise exceptions (see bug #265028)
- // to sanitize as much as possible, we catch and store them,
- // rather than fail fast.
- // Callers should check returned errors and give user feedback
- // about items that could not be sanitized
- let item = this.items[itemName];
- try {
- if (aCanClear)
- item.clear();
- } catch(er) {
- seenError = true;
- console.error("Error sanitizing " + itemName + ": " + er + "\n");
- }
- onItemComplete();
- };
- this.canClearItem(itemName, clearCallback);
- } else {
- onItemComplete();
- }
- }
- return deferred.promise;
- },
-
- // Time span only makes sense in certain cases. Consumers who want
- // to only clear some private data can opt in by setting this to false,
- // and can optionally specify a specific range. If timespan is not ignored,
- // and range is not set, sanitize() will use the value of the timespan
- // pref to determine a range
- ignoreTimespan : true,
- range : null,
- items: {
- cache: {
- clear: function() {
- var cache = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
- .getService(Ci.nsICacheStorageService);
- try {
- // Cache doesn't consult timespan, nor does it have the
- // facility for timespan-based eviction. Wipe it.
- cache.clear();
- } catch(er) {}
- var imageCache = Cc["@mozilla.org/image/tools;1"].
- getService(Ci.imgITools).getImgCacheForDocument(null);
- try {
- imageCache.clearCache(false); // true=chrome, false=content
- } catch(er) {}
- },
- get canClear() {
- return true;
- }
- },
- cookies: {
- clear: function () {
- var cookieMgr = Components.classes["@mozilla.org/cookiemanager;1"]
- .getService(Ci.nsICookieManager);
- if (this.range) {
- // Iterate through the cookies and delete any created after our cutoff.
- var cookiesEnum = cookieMgr.enumerator;
- while (cookiesEnum.hasMoreElements()) {
- var cookie = cookiesEnum.getNext().QueryInterface(Ci.nsICookie2);
- if (cookie.creationTime > this.range[0]) {
- // This cookie was created after our cutoff, clear it
- cookieMgr.remove(cookie.host, cookie.name, cookie.path,
- false, cookie.originAttributes);
- }
- }
- } else {
- // Remove everything
- cookieMgr.removeAll();
- }
- // Clear plugin data.
- const phInterface = Ci.nsIPluginHost;
- const FLAG_CLEAR_ALL = phInterface.FLAG_CLEAR_ALL;
- let ph = Cc["@mozilla.org/plugin/host;1"].getService(phInterface);
- // Determine age range in seconds. (-1 means clear all.) We don't know
- // that this.range[1] is actually now, so we compute age range based
- // on the lower bound. If this.range results in a negative age, do
- // nothing.
- let age = this.range ?
- (Date.now() / 1000 - this.range[0] / 1000000) :
- -1;
- if (!this.range || age >= 0) {
- let tags = ph.getPluginTags();
- for (let i = 0; i < tags.length; i++) {
- try {
- ph.clearSiteData(tags[i], null, FLAG_CLEAR_ALL, age);
- } catch(e) {
- // If the plugin doesn't support clearing by age, clear everything.
- if (e.result == Components.results.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED) {
- try {
- ph.clearSiteData(tags[i], null, FLAG_CLEAR_ALL, -1);
- } catch(e) {
- // Ignore errors from the plugin
- }
- }
- }
- }
- }
- },
- get canClear() {
- return true;
- }
- },
- offlineApps: {
- clear: function () {
- Components.utils.import("resource:///modules/offlineAppCache.jsm");
- OfflineAppCacheHelper.clear();
- if (!this.range || this.isShutDown) {
- Components.utils.import("resource:///modules/QuotaManager.jsm");
- QuotaManagerHelper.clear(this.isShutDown);
- }
- },
- get canClear() {
- return true;
- }
- },
- history: {
- clear: function () {
- if (this.range) {
- PlacesUtils.history.removeVisitsByFilter({
- beginDate: new Date(this.range[0] / 1000),
- endDate: new Date(this.range[1] / 1000)
- }).catch(Components.utils.reportError);;
- } else {
- // Remove everything.
- PlacesUtils.history.clear()
- .catch(Components.utils.reportError);
- }
- try {
- var os = Components.classes["@mozilla.org/observer-service;1"]
- .getService(Components.interfaces.nsIObserverService);
- os.notifyObservers(null, "browser:purge-session-history", "");
- } catch(e) {}
- // Clear last URL of the Open Web Location dialog
- var prefs = Components.classes["@mozilla.org/preferences-service;1"]
- .getService(Components.interfaces.nsIPrefBranch);
- try {
- prefs.clearUserPref("general.open_location.last_url");
- } 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;
- }
- },
- formdata: {
- clear: function () {
- // Clear undo history of all searchBars
- var windowManager = Components.classes['@mozilla.org/appshell/window-mediator;1']
- .getService(Components.interfaces.nsIWindowMediator);
- var windows = windowManager.getEnumerator("navigator:browser");
- while (windows.hasMoreElements()) {
- let currentDocument = windows.getNext().document;
- let searchBar = currentDocument.getElementById("searchbar");
- if (searchBar) {
- searchBar.textbox.reset();
- }
- let findBar = currentDocument.getElementById("FindToolbar");
- if (findBar) {
- findBar.clear();
- }
- }
- let change = { op: "remove" };
- if (this.range) {
- [ change.firstUsedStart, change.firstUsedEnd ] = this.range;
- }
- FormHistory.update(change);
- },
- canClear: function(aCallback, aArg) {
- var windowManager = Components.classes['@mozilla.org/appshell/window-mediator;1']
- .getService(Components.interfaces.nsIWindowMediator);
- var windows = windowManager.getEnumerator("navigator:browser");
- while (windows.hasMoreElements()) {
- let currentDocument = windows.getNext().document;
- let searchBar = currentDocument.getElementById("searchbar");
- if (searchBar) {
- let transactionMgr = searchBar.textbox.editor.transactionManager;
- if (searchBar.value ||
- transactionMgr.numberOfUndoItems ||
- transactionMgr.numberOfRedoItems) {
- aCallback("formdata", true, aArg);
- return false;
- }
- }
- let findBar = currentDocument.getElementById("FindToolbar");
- if (findBar && findBar.canClear) {
- aCallback("formdata", true, aArg);
- return false;
- }
- }
- let count = 0;
- let countDone = {
- handleResult: function(aResult) {
- count = aResult;
- },
- handleError: function(aError) {
- Components.utils.reportError(aError);
- },
- handleCompletion: function(aReason) {
- aCallback("formdata", aReason == 0 && count > 0, aArg);
- }
- };
- FormHistory.count({}, countDone);
- return false;
- }
- },
- downloads: {
- clear: Task.async(function* (range) {
- let refObj = {};
- try {
- let filterByTime = null;
- if (range) {
- // Convert microseconds back to milliseconds for date comparisons.
- let rangeBeginMs = range[0] / 1000;
- let rangeEndMs = range[1] / 1000;
- filterByTime = download => {
- return download.startTime >= rangeBeginMs &&
- download.startTime <= rangeEndMs;
- }
- }
- // Clear all completed/cancelled downloads
- let list = yield Downloads.getList(Downloads.ALL);
- list.removeFinished(filterByTime);
- } finally {}
- }),
- get canClear() {
- //Clearing is always possible with JSTransfers
- return true;
- }
- },
- passwords: {
- clear: function () {
- var pwmgr = Components.classes["@mozilla.org/login-manager;1"]
- .getService(Components.interfaces.nsILoginManager);
- // Passwords are timeless, and don't respect the timeSpan setting
- pwmgr.removeAllLogins();
- },
- get canClear() {
- var pwmgr = Components.classes["@mozilla.org/login-manager;1"]
- .getService(Components.interfaces.nsILoginManager);
- // count all logins
- var count = pwmgr.countLogins("", "", "");
- return (count > 0);
- }
- },
- sessions: {
- clear: function () {
- // clear all auth tokens
- var sdr = Components.classes["@mozilla.org/security/sdr;1"]
- .getService(Components.interfaces.nsISecretDecoderRing);
- sdr.logoutAndTeardown();
- // clear FTP and plain HTTP auth sessions
- var os = Components.classes["@mozilla.org/observer-service;1"]
- .getService(Components.interfaces.nsIObserverService);
- os.notifyObservers(null, "net:clear-active-logins", null);
- },
- get canClear() {
- return true;
- }
- },
- siteSettings: {
- clear: function () {
- // Clear site-specific permissions like "Allow this site to open popups"
- var pm = Components.classes["@mozilla.org/permissionmanager;1"]
- .getService(Components.interfaces.nsIPermissionManager);
- pm.removeAll();
- // Clear site-specific settings like page-zoom level
- var cps = Components.classes["@mozilla.org/content-pref/service;1"]
- .getService(Components.interfaces.nsIContentPrefService2);
- cps.removeAllDomains(null);
- // Clear "Never remember passwords for this site", which is not handled by
- // the permission manager
- var pwmgr = Components.classes["@mozilla.org/login-manager;1"]
- .getService(Components.interfaces.nsILoginManager);
- var hosts = pwmgr.getAllDisabledHosts();
- for each (var host in hosts) {
- pwmgr.setLoginSavingEnabled(host, true);
- }
- },
- get canClear() {
- return true;
- }
- },
- connectivityData: {
- clear: function () {
- // Clear site security settings
- var sss = Components.classes["@mozilla.org/ssservice;1"]
- .getService(Components.interfaces.nsISiteSecurityService);
- sss.clearAll();
- },
- get canClear() {
- return true;
- }
- }
- }
- };
- // "Static" members
- Sanitizer.prefDomain = "privacy.sanitize.";
- Sanitizer.prefShutdown = "sanitizeOnShutdown";
- Sanitizer.prefDidShutdown = "didShutdownSanitize";
- // Time span constants corresponding to values of the privacy.sanitize.timeSpan
- // pref. Used to determine how much history to clear, for various items
- Sanitizer.TIMESPAN_EVERYTHING = 0;
- Sanitizer.TIMESPAN_HOUR = 1;
- Sanitizer.TIMESPAN_2HOURS = 2;
- Sanitizer.TIMESPAN_4HOURS = 3;
- Sanitizer.TIMESPAN_TODAY = 4;
- Sanitizer.IS_SHUTDOWN = true;
- // Return a 2 element array representing the start and end times,
- // in the uSec-since-epoch format that PRTime likes. If we should
- // clear everything, return null. Use ts if it is defined; otherwise
- // use the timeSpan pref.
- Sanitizer.getClearRange = function(ts) {
- if (ts === undefined) {
- ts = Sanitizer.prefs.getIntPref("timeSpan");
- }
- if (ts === Sanitizer.TIMESPAN_EVERYTHING) {
- return null;
- }
-
- // PRTime is microseconds while JS time is milliseconds
- var endDate = Date.now() * 1000;
- switch (ts) {
- case Sanitizer.TIMESPAN_HOUR :
- var startDate = endDate - 3600000000; // 1*60*60*1000000
- break;
- case Sanitizer.TIMESPAN_2HOURS :
- startDate = endDate - 7200000000; // 2*60*60*1000000
- break;
- case Sanitizer.TIMESPAN_4HOURS :
- startDate = endDate - 14400000000; // 4*60*60*1000000
- break;
- case Sanitizer.TIMESPAN_TODAY :
- var d = new Date(); // Start with today
- d.setHours(0); // zero us back to midnight...
- d.setMinutes(0);
- d.setSeconds(0);
- startDate = d.valueOf() * 1000; // convert to epoch usec
- break;
- default:
- throw "Invalid time span for clear private data: " + ts;
- }
- return [startDate, endDate];
- };
- Sanitizer._prefs = null;
- Sanitizer.__defineGetter__("prefs", function() {
- return Sanitizer._prefs ?
- Sanitizer._prefs :
- Sanitizer._prefs = Components.classes["@mozilla.org/preferences-service;1"]
- .getService(Components.interfaces.nsIPrefService)
- .getBranch(Sanitizer.prefDomain);
- });
- // Shows sanitization UI
- Sanitizer.showUI = function(aParentWindow) {
- var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
- .getService(Components.interfaces.nsIWindowWatcher);
- ww.openWindow(aParentWindow,
- "chrome://browser/content/sanitize.xul",
- "Sanitize",
- "chrome,titlebar,dialog,centerscreen,modal",
- null);
- };
- /**
- * Deletes privacy sensitive data in a batch, optionally showing the
- * sanitize UI, according to user preferences
- */
- Sanitizer.sanitize = function(aParentWindow) {
- Sanitizer.showUI(aParentWindow);
- };
- Sanitizer.onStartup = function() {
- // we check for unclean exit with pending sanitization
- Sanitizer._checkAndSanitize();
- };
- Sanitizer.onShutdown = function() {
- // we check if sanitization is needed and perform it
- Sanitizer._checkAndSanitize(Sanitizer.IS_SHUTDOWN);
- };
- // this is called on startup and shutdown, to perform pending sanitizations
- Sanitizer._checkAndSanitize = function(isShutDown) {
- const prefs = Sanitizer.prefs;
- if (prefs.getBoolPref(Sanitizer.prefShutdown) &&
- !prefs.prefHasUserValue(Sanitizer.prefDidShutdown)) {
- // this is a shutdown or a startup after an unclean exit
- var s = new Sanitizer();
- s.prefDomain = "privacy.clearOnShutdown.";
- s.isShutDown = isShutDown;
- s.sanitize().then(function() {
- prefs.setBoolPref(Sanitizer.prefDidShutdown, true);
- });
- }
- };
|