1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
|
/* 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/PlacesUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
var Application = Components.classes["@mozilla.org/steel/application;1"]
.getService(Components.interfaces.steelIApplication);
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)
{
return this.items[aItemName].canClear;
},
prefDomain: "",
getNameFromPreference: function (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: function ()
{
var branch = Services.prefs.getBranch(this.prefDomain);
var errors = null;
// 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();
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: function ()
{
try {
// Cache doesn't consult timespan, nor does it have the
// facility for timespan-based eviction. Wipe it.
Services.cache.evictEntries(Ci.nsICache.STORE_ANYWHERE);
} catch(er) {}
},
get canClear()
{
return true;
}
},
cookies: {
clear: function ()
{
if (this.range) {
// Iterate through the cookies and delete any created after our cutoff.
var cookiesEnum = Services.cookies.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
Services.cookies.remove(cookie.host, cookie.name, cookie.path, false);
}
}
else {
// Remove everything
Services.cookies.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
}
}
}
}
}
// clear any network geolocation provider sessions
try {
var branch = Services.prefs.getBranch("geo.wifi.access_token.");
branch.deleteBranch("");
} catch (e) {}
},
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) { }
try {
var predictor = Components.classes["@mozilla.org/network/predictor;1"]
.getService(Components.interfaces.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 "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(Application.platformIsMac ? null : aParentWindow,
"chrome://messenger/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);
};
// 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() || // sanitize() returns null on full success
prefs.setBoolPref(Sanitizer.prefDidShutdown, true);
}
};
|