/* 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/. */ var { PlacesUtils } = ChromeUtils.importESModule( "resource://gre/modules/PlacesUtils.sys.mjs" ); var { AppConstants } = ChromeUtils.importESModule( "resource://gre/modules/AppConstants.sys.mjs" ); function Sanitizer() {} Sanitizer.prototype = { // warning to the caller: this one may raise an exception (e.g. bug #265028) clearItem(aItemName) { if (this.items[aItemName].canClear) { this.items[aItemName].clear(); } }, canClearItem(aItemName) { return this.items[aItemName].canClear; }, prefDomain: "", getNameFromPreference(aPreferenceName) { return aPreferenceName.substr(this.prefDomain.length); }, /** * Deletes privacy sensitive data in a batch, according to user preferences * * @returns null if everything's fine; an object in the form * { itemName: error, ... } on (partial) failure */ sanitize() { var branch = Services.prefs.getBranch(this.prefDomain); var errors = null; // 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(); } for (var itemName in this.items) { var item = this.items[itemName]; item.range = range; if ("clear" in item && item.canClear && branch.getBoolPref(itemName)) { // 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 try { item.clear(); } catch (er) { if (!errors) { errors = {}; } errors[itemName] = er; dump("Error sanitizing " + itemName + ": " + er + "\n"); } } } return errors; }, // 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() { try { // Cache doesn't consult timespan, nor does it have the // facility for timespan-based eviction. Wipe it. Services.cache2.clear(); } catch (ex) {} }, get canClear() { return true; }, }, cookies: { clear() { if (this.range) { // Iterate through the cookies and delete any created after our cutoff. for (let cookie of Services.cookies.cookies) { if (cookie.creationTime > this.range[0]) { // This cookie was created after our cutoff, clear it Services.cookies.remove( cookie.host, cookie.name, cookie.path, cookie.originAttributes ); } } } else { // Remove everything Services.cookies.removeAll(); } }, get canClear() { return true; }, }, history: { clear() { if (this.range) { PlacesUtils.history.removeVisitsByFilter({ beginDate: new Date(this.range[0]), endDate: new Date(this.range[1]), }); } else { PlacesUtils.history.clear(); } 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; }, }, }, }; // "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; // 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 new Error("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 = Services.prefs.getBranch(Sanitizer.prefDomain)); }); // Shows sanitization UI Sanitizer.showUI = function (aParentWindow) { Services.ww.openWindow( AppConstants.platform == "macosx" ? null : aParentWindow, "chrome://messenger/content/sanitize.xhtml", "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); }; // this is called on startup and shutdown, to perform pending sanitizations Sanitizer._checkAndSanitize = function () { 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.sanitize() || prefs.setBoolPref(Sanitizer.prefDidShutdown, true); // sanitize() returns null on full success } };