Created
October 30, 2013 19:21
-
-
Save oogali/7238535 to your computer and use it in GitHub Desktop.
chrome://browser/content/sanitize.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//@line 5 "/builds/slave/rel-m-rel-osx64_bld-0000000000/build/browser/base/content/sanitize.js" | |
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, "Promise", | |
"resource://gre/modules/commonjs/sdk/core/promise.js"); | |
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: "", | |
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) | |
var range = null; // If we ignore timespan, clear everything | |
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; | |
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; | |
Cu.reportError("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 cacheService = Cc["@mozilla.org/network/cache-service;1"]. | |
getService(Ci.nsICacheService); | |
try { | |
// Cache doesn't consult timespan, nor does it have the | |
// facility for timespan-based eviction. Wipe it. | |
cacheService.evictEntries(Ci.nsICache.STORE_ANYWHERE); | |
} 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); | |
} | |
} | |
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(); | |
}, | |
get canClear() | |
{ | |
return true; | |
} | |
}, | |
history: { | |
clear: function () | |
{ | |
if (this.range) | |
PlacesUtils.history.removeVisitsByTimeframe(this.range[0], this.range[1]); | |
else | |
PlacesUtils.history.removeAllPages(); | |
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: function () | |
{ | |
var dlMgr = Components.classes["@mozilla.org/download-manager;1"] | |
.getService(Components.interfaces.nsIDownloadManager); | |
var dlsToRemove = []; | |
if (this.range) { | |
// First, remove the completed/cancelled downloads | |
dlMgr.removeDownloadsByTimeframe(this.range[0], this.range[1]); | |
// Queue up any active downloads that started in the time span as well | |
for (let dlsEnum of [dlMgr.activeDownloads, dlMgr.activePrivateDownloads]) { | |
while (dlsEnum.hasMoreElements()) { | |
var dl = dlsEnum.next(); | |
if (dl.startTime >= this.range[0]) | |
dlsToRemove.push(dl); | |
} | |
} | |
} | |
else { | |
// Clear all completed/cancelled downloads | |
dlMgr.cleanUp(); | |
dlMgr.cleanUpPrivate(); | |
// Queue up all active ones as well | |
for (let dlsEnum of [dlMgr.activeDownloads, dlMgr.activePrivateDownloads]) { | |
while (dlsEnum.hasMoreElements()) { | |
dlsToRemove.push(dlsEnum.next()); | |
} | |
} | |
} | |
// Remove any queued up active downloads | |
dlsToRemove.forEach(function (dl) { | |
dl.remove(); | |
}); | |
}, | |
get canClear() | |
{ | |
var dlMgr = Components.classes["@mozilla.org/download-manager;1"] | |
.getService(Components.interfaces.nsIDownloadManager); | |
return dlMgr.canCleanUp || dlMgr.canCleanUpPrivate; | |
} | |
}, | |
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); | |
var count = pwmgr.countLogins("", "", ""); // count all logins | |
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; | |
} | |
} | |
} | |
}; | |
// "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 "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); | |
//@line 474 "/builds/slave/rel-m-rel-osx64_bld-0000000000/build/browser/base/content/sanitize.js" | |
ww.openWindow(null, // make this an app-modal window on Mac | |
//@line 478 "/builds/slave/rel-m-rel-osx64_bld-0000000000/build/browser/base/content/sanitize.js" | |
"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(); | |
}; | |
// 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().then(function() { | |
prefs.setBoolPref(Sanitizer.prefDidShutdown, true); | |
}); | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment