1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/forgetaboutsite/ForgetAboutSite.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,229 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +Components.utils.import("resource://gre/modules/Services.jsm"); 1.9 +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); 1.10 +Components.utils.import("resource://gre/modules/NetUtil.jsm"); 1.11 +Components.utils.import("resource://gre/modules/Task.jsm"); 1.12 +XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", 1.13 + "resource://gre/modules/PlacesUtils.jsm"); 1.14 +XPCOMUtils.defineLazyModuleGetter(this, "Downloads", 1.15 + "resource://gre/modules/Downloads.jsm"); 1.16 + 1.17 +this.EXPORTED_SYMBOLS = ["ForgetAboutSite"]; 1.18 + 1.19 +/** 1.20 + * Returns true if the string passed in is part of the root domain of the 1.21 + * current string. For example, if this is "www.mozilla.org", and we pass in 1.22 + * "mozilla.org", this will return true. It would return false the other way 1.23 + * around. 1.24 + */ 1.25 +function hasRootDomain(str, aDomain) 1.26 +{ 1.27 + let index = str.indexOf(aDomain); 1.28 + // If aDomain is not found, we know we do not have it as a root domain. 1.29 + if (index == -1) 1.30 + return false; 1.31 + 1.32 + // If the strings are the same, we obviously have a match. 1.33 + if (str == aDomain) 1.34 + return true; 1.35 + 1.36 + // Otherwise, we have aDomain as our root domain iff the index of aDomain is 1.37 + // aDomain.length subtracted from our length and (since we do not have an 1.38 + // exact match) the character before the index is a dot or slash. 1.39 + let prevChar = str[index - 1]; 1.40 + return (index == (str.length - aDomain.length)) && 1.41 + (prevChar == "." || prevChar == "/"); 1.42 +} 1.43 + 1.44 +const Cc = Components.classes; 1.45 +const Ci = Components.interfaces; 1.46 +const Cu = Components.utils; 1.47 + 1.48 +this.ForgetAboutSite = { 1.49 + removeDataFromDomain: function CRH_removeDataFromDomain(aDomain) 1.50 + { 1.51 + PlacesUtils.history.removePagesFromHost(aDomain, true); 1.52 + 1.53 + // Cache 1.54 + let (cs = Cc["@mozilla.org/netwerk/cache-storage-service;1"]. 1.55 + getService(Ci.nsICacheStorageService)) { 1.56 + // NOTE: there is no way to clear just that domain, so we clear out 1.57 + // everything) 1.58 + try { 1.59 + cs.clear(); 1.60 + } catch (ex) { 1.61 + Cu.reportError("Exception thrown while clearing the cache: " + 1.62 + ex.toString()); 1.63 + } 1.64 + } 1.65 + 1.66 + // Image Cache 1.67 + let (imageCache = Cc["@mozilla.org/image/tools;1"]. 1.68 + getService(Ci.imgITools).getImgCacheForDocument(null)) { 1.69 + try { 1.70 + imageCache.clearCache(false); // true=chrome, false=content 1.71 + } catch (ex) { 1.72 + Cu.reportError("Exception thrown while clearing the image cache: " + 1.73 + ex.toString()); 1.74 + } 1.75 + } 1.76 + 1.77 + // Cookies 1.78 + let (cm = Cc["@mozilla.org/cookiemanager;1"]. 1.79 + getService(Ci.nsICookieManager2)) { 1.80 + let enumerator = cm.getCookiesFromHost(aDomain); 1.81 + while (enumerator.hasMoreElements()) { 1.82 + let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie); 1.83 + cm.remove(cookie.host, cookie.name, cookie.path, false); 1.84 + } 1.85 + } 1.86 + 1.87 + // Plugin data 1.88 + const phInterface = Ci.nsIPluginHost; 1.89 + const FLAG_CLEAR_ALL = phInterface.FLAG_CLEAR_ALL; 1.90 + let (ph = Cc["@mozilla.org/plugin/host;1"].getService(phInterface)) { 1.91 + let tags = ph.getPluginTags(); 1.92 + for (let i = 0; i < tags.length; i++) { 1.93 + try { 1.94 + ph.clearSiteData(tags[i], aDomain, FLAG_CLEAR_ALL, -1); 1.95 + } catch (e) { 1.96 + // Ignore errors from the plugin 1.97 + } 1.98 + } 1.99 + } 1.100 + 1.101 + // Downloads 1.102 + let useJSTransfer = false; 1.103 + try { 1.104 + // This method throws an exception if the old Download Manager is disabled. 1.105 + Services.downloads.activeDownloadCount; 1.106 + } catch (ex) { 1.107 + useJSTransfer = true; 1.108 + } 1.109 + 1.110 + if (useJSTransfer) { 1.111 + Task.spawn(function() { 1.112 + let list = yield Downloads.getList(Downloads.ALL); 1.113 + list.removeFinished(download => hasRootDomain( 1.114 + NetUtil.newURI(download.source.url).host, aDomain)); 1.115 + }).then(null, Cu.reportError); 1.116 + } 1.117 + else { 1.118 + let (dm = Cc["@mozilla.org/download-manager;1"]. 1.119 + getService(Ci.nsIDownloadManager)) { 1.120 + // Active downloads 1.121 + for (let enumerator of [dm.activeDownloads, dm.activePrivateDownloads]) { 1.122 + while (enumerator.hasMoreElements()) { 1.123 + let dl = enumerator.getNext().QueryInterface(Ci.nsIDownload); 1.124 + if (hasRootDomain(dl.source.host, aDomain)) { 1.125 + dl.cancel(); 1.126 + dl.remove(); 1.127 + } 1.128 + } 1.129 + } 1.130 + 1.131 + function deleteAllLike(db) { 1.132 + // NOTE: This is lossy, but we feel that it is OK to be lossy here and not 1.133 + // invoke the cost of creating a URI for each download entry and 1.134 + // ensure that the hostname matches. 1.135 + let stmt = db.createStatement( 1.136 + "DELETE FROM moz_downloads " + 1.137 + "WHERE source LIKE ?1 ESCAPE '/' " + 1.138 + "AND state NOT IN (?2, ?3, ?4)" 1.139 + ); 1.140 + let pattern = stmt.escapeStringForLIKE(aDomain, "/"); 1.141 + stmt.bindByIndex(0, "%" + pattern + "%"); 1.142 + stmt.bindByIndex(1, Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING); 1.143 + stmt.bindByIndex(2, Ci.nsIDownloadManager.DOWNLOAD_PAUSED); 1.144 + stmt.bindByIndex(3, Ci.nsIDownloadManager.DOWNLOAD_QUEUED); 1.145 + try { 1.146 + stmt.execute(); 1.147 + } 1.148 + finally { 1.149 + stmt.finalize(); 1.150 + } 1.151 + } 1.152 + 1.153 + // Completed downloads 1.154 + deleteAllLike(dm.DBConnection); 1.155 + deleteAllLike(dm.privateDBConnection); 1.156 + 1.157 + // We want to rebuild the list if the UI is showing, so dispatch the 1.158 + // observer topic 1.159 + let os = Cc["@mozilla.org/observer-service;1"]. 1.160 + getService(Ci.nsIObserverService); 1.161 + os.notifyObservers(null, "download-manager-remove-download", null); 1.162 + } 1.163 + } 1.164 + 1.165 + // Passwords 1.166 + let (lm = Cc["@mozilla.org/login-manager;1"]. 1.167 + getService(Ci.nsILoginManager)) { 1.168 + // Clear all passwords for domain 1.169 + try { 1.170 + let logins = lm.getAllLogins(); 1.171 + for (let i = 0; i < logins.length; i++) 1.172 + if (hasRootDomain(logins[i].hostname, aDomain)) 1.173 + lm.removeLogin(logins[i]); 1.174 + } 1.175 + // XXXehsan: is there a better way to do this rather than this 1.176 + // hacky comparison? 1.177 + catch (ex if ex.message.indexOf("User canceled Master Password entry") != -1) { } 1.178 + 1.179 + // Clear any "do not save for this site" for this domain 1.180 + let disabledHosts = lm.getAllDisabledHosts(); 1.181 + for (let i = 0; i < disabledHosts.length; i++) 1.182 + if (hasRootDomain(disabledHosts[i], aDomain)) 1.183 + lm.setLoginSavingEnabled(disabledHosts, true); 1.184 + } 1.185 + 1.186 + // Permissions 1.187 + let (pm = Cc["@mozilla.org/permissionmanager;1"]. 1.188 + getService(Ci.nsIPermissionManager)) { 1.189 + // Enumerate all of the permissions, and if one matches, remove it 1.190 + let enumerator = pm.enumerator; 1.191 + while (enumerator.hasMoreElements()) { 1.192 + let perm = enumerator.getNext().QueryInterface(Ci.nsIPermission); 1.193 + if (hasRootDomain(perm.host, aDomain)) 1.194 + pm.remove(perm.host, perm.type); 1.195 + } 1.196 + } 1.197 + 1.198 + // Offline Storages 1.199 + let (qm = Cc["@mozilla.org/dom/quota/manager;1"]. 1.200 + getService(Ci.nsIQuotaManager)) { 1.201 + // delete data from both HTTP and HTTPS sites 1.202 + let caUtils = {}; 1.203 + let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"]. 1.204 + getService(Ci.mozIJSSubScriptLoader); 1.205 + scriptLoader.loadSubScript("chrome://global/content/contentAreaUtils.js", 1.206 + caUtils); 1.207 + let httpURI = caUtils.makeURI("http://" + aDomain); 1.208 + let httpsURI = caUtils.makeURI("https://" + aDomain); 1.209 + qm.clearStoragesForURI(httpURI); 1.210 + qm.clearStoragesForURI(httpsURI); 1.211 + } 1.212 + 1.213 + function onContentPrefsRemovalFinished() { 1.214 + // Everybody else (including extensions) 1.215 + Services.obs.notifyObservers(null, "browser:purge-domain-data", aDomain); 1.216 + } 1.217 + 1.218 + // Content Preferences 1.219 + let cps2 = Cc["@mozilla.org/content-pref/service;1"]. 1.220 + getService(Ci.nsIContentPrefService2); 1.221 + cps2.removeBySubdomain(aDomain, null, { 1.222 + handleCompletion: function() onContentPrefsRemovalFinished(), 1.223 + handleError: function() {} 1.224 + }); 1.225 + 1.226 + // Predictive network data - like cache, no way to clear this per 1.227 + // domain, so just trash it all 1.228 + let ns = Cc["@mozilla.org/network/seer;1"]. 1.229 + getService(Ci.nsINetworkSeer); 1.230 + ns.reset(); 1.231 + } 1.232 +};