Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | // -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | XPCOMUtils.defineLazyModuleGetter(this, "LoadContextInfo", |
michael@0 | 7 | "resource://gre/modules/LoadContextInfo.jsm"); |
michael@0 | 8 | function Sanitizer() {} |
michael@0 | 9 | |
michael@0 | 10 | Sanitizer.prototype = { |
michael@0 | 11 | // warning to the caller: this one may raise an exception (e.g. bug #265028) |
michael@0 | 12 | clearItem: function (aItemName) |
michael@0 | 13 | { |
michael@0 | 14 | if (this.items[aItemName].canClear) |
michael@0 | 15 | this.items[aItemName].clear(); |
michael@0 | 16 | }, |
michael@0 | 17 | |
michael@0 | 18 | canClearItem: function (aItemName, aCallback, aArg) |
michael@0 | 19 | { |
michael@0 | 20 | let canClear = this.items[aItemName].canClear; |
michael@0 | 21 | if (typeof canClear == "function"){ |
michael@0 | 22 | canClear(aCallback, aArg); |
michael@0 | 23 | } else { |
michael@0 | 24 | aCallback(aItemName, canClear, aArg); |
michael@0 | 25 | } |
michael@0 | 26 | }, |
michael@0 | 27 | |
michael@0 | 28 | _prefDomain: "privacy.item.", |
michael@0 | 29 | getNameFromPreference: function (aPreferenceName) |
michael@0 | 30 | { |
michael@0 | 31 | return aPreferenceName.substr(this._prefDomain.length); |
michael@0 | 32 | }, |
michael@0 | 33 | |
michael@0 | 34 | /** |
michael@0 | 35 | * Deletes privacy sensitive data in a batch, according to user preferences |
michael@0 | 36 | * |
michael@0 | 37 | * @returns null if everything's fine; an object in the form |
michael@0 | 38 | * { itemName: error, ... } on (partial) failure |
michael@0 | 39 | */ |
michael@0 | 40 | sanitize: function () |
michael@0 | 41 | { |
michael@0 | 42 | var branch = Services.prefs.getBranch(this._prefDomain); |
michael@0 | 43 | var errors = null; |
michael@0 | 44 | for (var itemName in this.items) { |
michael@0 | 45 | if ("clear" in item && branch.getBoolPref(itemName)) { |
michael@0 | 46 | // Some of these clear() may raise exceptions (see bug #265028) |
michael@0 | 47 | // to sanitize as much as possible, we catch and store them, |
michael@0 | 48 | // rather than fail fast. |
michael@0 | 49 | // Callers should check returned errors and give user feedback |
michael@0 | 50 | // about items that could not be sanitized |
michael@0 | 51 | let clearCallback = (itemName, aCanClear) => { |
michael@0 | 52 | let item = this.items[itemName]; |
michael@0 | 53 | try{ |
michael@0 | 54 | if (aCanClear){ |
michael@0 | 55 | item.clear(); |
michael@0 | 56 | } |
michael@0 | 57 | } catch(er){ |
michael@0 | 58 | if (!errors){ |
michael@0 | 59 | errors = {}; |
michael@0 | 60 | } |
michael@0 | 61 | errors[itemName] = er; |
michael@0 | 62 | dump("Error sanitizing " + itemName + ":" + er + "\n"); |
michael@0 | 63 | } |
michael@0 | 64 | } |
michael@0 | 65 | this.canClearItem(itemName, clearCallback); |
michael@0 | 66 | } |
michael@0 | 67 | } |
michael@0 | 68 | return errors; |
michael@0 | 69 | }, |
michael@0 | 70 | |
michael@0 | 71 | items: { |
michael@0 | 72 | // Clear Sync account before passwords so that Sync still has access to the |
michael@0 | 73 | // credentials to clean up device-specific records on the server. Also |
michael@0 | 74 | // disable it before wiping history so we don't accidentally sync that. |
michael@0 | 75 | syncAccount: { |
michael@0 | 76 | clear: function () |
michael@0 | 77 | { |
michael@0 | 78 | Sync.disconnect(); |
michael@0 | 79 | }, |
michael@0 | 80 | |
michael@0 | 81 | get canClear() |
michael@0 | 82 | { |
michael@0 | 83 | return (Weave.Status.checkSetup() != Weave.CLIENT_NOT_CONFIGURED); |
michael@0 | 84 | } |
michael@0 | 85 | }, |
michael@0 | 86 | |
michael@0 | 87 | cache: { |
michael@0 | 88 | clear: function () |
michael@0 | 89 | { |
michael@0 | 90 | var cache = Cc["@mozilla.org/netwerk/cache-storage-service;1"].getService(Ci.nsICacheStorageService); |
michael@0 | 91 | try { |
michael@0 | 92 | cache.clear(); |
michael@0 | 93 | } catch(er) {} |
michael@0 | 94 | |
michael@0 | 95 | let imageCache = Cc["@mozilla.org/image/cache;1"].getService(Ci.imgICache); |
michael@0 | 96 | try { |
michael@0 | 97 | imageCache.clearCache(false); // true=chrome, false=content |
michael@0 | 98 | } catch(er) {} |
michael@0 | 99 | }, |
michael@0 | 100 | |
michael@0 | 101 | get canClear() |
michael@0 | 102 | { |
michael@0 | 103 | return true; |
michael@0 | 104 | } |
michael@0 | 105 | }, |
michael@0 | 106 | |
michael@0 | 107 | cookies: { |
michael@0 | 108 | clear: function () |
michael@0 | 109 | { |
michael@0 | 110 | var cookieMgr = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager); |
michael@0 | 111 | cookieMgr.removeAll(); |
michael@0 | 112 | }, |
michael@0 | 113 | |
michael@0 | 114 | get canClear() |
michael@0 | 115 | { |
michael@0 | 116 | return true; |
michael@0 | 117 | } |
michael@0 | 118 | }, |
michael@0 | 119 | |
michael@0 | 120 | siteSettings: { |
michael@0 | 121 | clear: function () |
michael@0 | 122 | { |
michael@0 | 123 | // Clear site-specific permissions like "Allow this site to open popups" |
michael@0 | 124 | Services.perms.removeAll(); |
michael@0 | 125 | |
michael@0 | 126 | // Clear site-specific settings like page-zoom level |
michael@0 | 127 | var cps = Cc["@mozilla.org/content-pref/service;1"].getService(Ci.nsIContentPrefService2); |
michael@0 | 128 | cps.removeAllDomains(null); |
michael@0 | 129 | |
michael@0 | 130 | // Clear "Never remember passwords for this site", which is not handled by |
michael@0 | 131 | // the permission manager |
michael@0 | 132 | var pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager); |
michael@0 | 133 | var hosts = pwmgr.getAllDisabledHosts({}) |
michael@0 | 134 | for each (var host in hosts) { |
michael@0 | 135 | pwmgr.setLoginSavingEnabled(host, true); |
michael@0 | 136 | } |
michael@0 | 137 | }, |
michael@0 | 138 | |
michael@0 | 139 | get canClear() |
michael@0 | 140 | { |
michael@0 | 141 | return true; |
michael@0 | 142 | } |
michael@0 | 143 | }, |
michael@0 | 144 | |
michael@0 | 145 | offlineApps: { |
michael@0 | 146 | clear: function () |
michael@0 | 147 | { |
michael@0 | 148 | var cacheService = Cc["@mozilla.org/netwerk/cache-storage-service;1"].getService(Ci.nsICacheStorageService); |
michael@0 | 149 | var appCacheStorage = cacheService.appCacheStorage(LoadContextInfo.default, null); |
michael@0 | 150 | try { |
michael@0 | 151 | appCacheStorage.asyncEvictStorage(null); |
michael@0 | 152 | } catch(er) {} |
michael@0 | 153 | }, |
michael@0 | 154 | |
michael@0 | 155 | get canClear() |
michael@0 | 156 | { |
michael@0 | 157 | return true; |
michael@0 | 158 | } |
michael@0 | 159 | }, |
michael@0 | 160 | |
michael@0 | 161 | history: { |
michael@0 | 162 | clear: function () |
michael@0 | 163 | { |
michael@0 | 164 | try { |
michael@0 | 165 | Services.obs.notifyObservers(null, "browser:purge-session-history", ""); |
michael@0 | 166 | } |
michael@0 | 167 | catch (e) { |
michael@0 | 168 | Components.utils.reportError("Failed to notify observers of " |
michael@0 | 169 | + "browser:purge-session-history: " |
michael@0 | 170 | + e); |
michael@0 | 171 | } |
michael@0 | 172 | }, |
michael@0 | 173 | |
michael@0 | 174 | get canClear() |
michael@0 | 175 | { |
michael@0 | 176 | // bug 347231: Always allow clearing history due to dependencies on |
michael@0 | 177 | // the browser:purge-session-history notification. (like error console) |
michael@0 | 178 | return true; |
michael@0 | 179 | } |
michael@0 | 180 | }, |
michael@0 | 181 | |
michael@0 | 182 | formdata: { |
michael@0 | 183 | clear: function () |
michael@0 | 184 | { |
michael@0 | 185 | //Clear undo history of all searchBars |
michael@0 | 186 | var windows = Services.wm.getEnumerator("navigator:browser"); |
michael@0 | 187 | while (windows.hasMoreElements()) { |
michael@0 | 188 | var searchBar = windows.getNext().document.getElementById("searchbar"); |
michael@0 | 189 | if (searchBar) { |
michael@0 | 190 | searchBar.value = ""; |
michael@0 | 191 | searchBar.textbox.editor.transactionManager.clear(); |
michael@0 | 192 | } |
michael@0 | 193 | } |
michael@0 | 194 | FormHistory.update({op : "remove"}); |
michael@0 | 195 | }, |
michael@0 | 196 | |
michael@0 | 197 | canClear : function(aCallback, aArg) |
michael@0 | 198 | { |
michael@0 | 199 | let count = 0; |
michael@0 | 200 | let countDone = { |
michael@0 | 201 | handleResult : function(aResult) { count = aResult; }, |
michael@0 | 202 | handleError : function(aError) { Components.utils.reportError(aError); }, |
michael@0 | 203 | handleCompletion : function(aReason) { aCallback("formdata", aReason == 0 && count > 0, aArg); } |
michael@0 | 204 | }; |
michael@0 | 205 | FormHistory.count({}, countDone); |
michael@0 | 206 | } |
michael@0 | 207 | }, |
michael@0 | 208 | |
michael@0 | 209 | downloads: { |
michael@0 | 210 | clear: function () |
michael@0 | 211 | { |
michael@0 | 212 | var dlMgr = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager); |
michael@0 | 213 | dlMgr.cleanUp(); |
michael@0 | 214 | }, |
michael@0 | 215 | |
michael@0 | 216 | get canClear() |
michael@0 | 217 | { |
michael@0 | 218 | var dlMgr = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager); |
michael@0 | 219 | return dlMgr.canCleanUp; |
michael@0 | 220 | } |
michael@0 | 221 | }, |
michael@0 | 222 | |
michael@0 | 223 | passwords: { |
michael@0 | 224 | clear: function () |
michael@0 | 225 | { |
michael@0 | 226 | var pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager); |
michael@0 | 227 | pwmgr.removeAllLogins(); |
michael@0 | 228 | }, |
michael@0 | 229 | |
michael@0 | 230 | get canClear() |
michael@0 | 231 | { |
michael@0 | 232 | var pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager); |
michael@0 | 233 | var count = pwmgr.countLogins("", "", ""); // count all logins |
michael@0 | 234 | return (count > 0); |
michael@0 | 235 | } |
michael@0 | 236 | }, |
michael@0 | 237 | |
michael@0 | 238 | sessions: { |
michael@0 | 239 | clear: function () |
michael@0 | 240 | { |
michael@0 | 241 | // clear all auth tokens |
michael@0 | 242 | var sdr = Cc["@mozilla.org/security/sdr;1"].getService(Ci.nsISecretDecoderRing); |
michael@0 | 243 | sdr.logoutAndTeardown(); |
michael@0 | 244 | |
michael@0 | 245 | // clear plain HTTP auth sessions |
michael@0 | 246 | var authMgr = Cc['@mozilla.org/network/http-auth-manager;1'].getService(Ci.nsIHttpAuthManager); |
michael@0 | 247 | authMgr.clearAll(); |
michael@0 | 248 | }, |
michael@0 | 249 | |
michael@0 | 250 | get canClear() |
michael@0 | 251 | { |
michael@0 | 252 | return true; |
michael@0 | 253 | } |
michael@0 | 254 | } |
michael@0 | 255 | } |
michael@0 | 256 | }; |
michael@0 | 257 | |
michael@0 | 258 | |
michael@0 | 259 | // "Static" members |
michael@0 | 260 | Sanitizer.prefDomain = "privacy.sanitize."; |
michael@0 | 261 | Sanitizer.prefShutdown = "sanitizeOnShutdown"; |
michael@0 | 262 | Sanitizer.prefDidShutdown = "didShutdownSanitize"; |
michael@0 | 263 | |
michael@0 | 264 | Sanitizer._prefs = null; |
michael@0 | 265 | Sanitizer.__defineGetter__("prefs", function() |
michael@0 | 266 | { |
michael@0 | 267 | return Sanitizer._prefs ? Sanitizer._prefs |
michael@0 | 268 | : Sanitizer._prefs = Cc["@mozilla.org/preferences-service;1"] |
michael@0 | 269 | .getService(Ci.nsIPrefService) |
michael@0 | 270 | .getBranch(Sanitizer.prefDomain); |
michael@0 | 271 | }); |
michael@0 | 272 | |
michael@0 | 273 | /** |
michael@0 | 274 | * Deletes privacy sensitive data in a batch, optionally showing the |
michael@0 | 275 | * sanitize UI, according to user preferences |
michael@0 | 276 | * |
michael@0 | 277 | * @returns null if everything's fine |
michael@0 | 278 | * an object in the form { itemName: error, ... } on (partial) failure |
michael@0 | 279 | */ |
michael@0 | 280 | Sanitizer.sanitize = function() |
michael@0 | 281 | { |
michael@0 | 282 | return new Sanitizer().sanitize(); |
michael@0 | 283 | }; |
michael@0 | 284 | |
michael@0 | 285 | Sanitizer.onStartup = function() |
michael@0 | 286 | { |
michael@0 | 287 | // we check for unclean exit with pending sanitization |
michael@0 | 288 | Sanitizer._checkAndSanitize(); |
michael@0 | 289 | }; |
michael@0 | 290 | |
michael@0 | 291 | Sanitizer.onShutdown = function() |
michael@0 | 292 | { |
michael@0 | 293 | // we check if sanitization is needed and perform it |
michael@0 | 294 | Sanitizer._checkAndSanitize(); |
michael@0 | 295 | }; |
michael@0 | 296 | |
michael@0 | 297 | // this is called on startup and shutdown, to perform pending sanitizations |
michael@0 | 298 | Sanitizer._checkAndSanitize = function() |
michael@0 | 299 | { |
michael@0 | 300 | const prefs = Sanitizer.prefs; |
michael@0 | 301 | if (prefs.getBoolPref(Sanitizer.prefShutdown) && |
michael@0 | 302 | !prefs.prefHasUserValue(Sanitizer.prefDidShutdown)) { |
michael@0 | 303 | // this is a shutdown or a startup after an unclean exit |
michael@0 | 304 | Sanitizer.sanitize() || // sanitize() returns null on full success |
michael@0 | 305 | prefs.setBoolPref(Sanitizer.prefDidShutdown, true); |
michael@0 | 306 | } |
michael@0 | 307 | }; |
michael@0 | 308 | |
michael@0 | 309 |