Bumping manifests a=b2g-bump
[gecko.git] / toolkit / forgetaboutsite / ForgetAboutSite.jsm
blobe8ea71ab4ee6bba38a3354e38f0adaa9e45c9340
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 Components.utils.import("resource://gre/modules/Services.jsm");
6 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
7 Components.utils.import("resource://gre/modules/NetUtil.jsm");
8 Components.utils.import("resource://gre/modules/Task.jsm");
9 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
10                                   "resource://gre/modules/PlacesUtils.jsm");
11 XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
12                                   "resource://gre/modules/Downloads.jsm");
14 this.EXPORTED_SYMBOLS = ["ForgetAboutSite"];
16 /**
17  * Returns true if the string passed in is part of the root domain of the
18  * current string.  For example, if this is "www.mozilla.org", and we pass in
19  * "mozilla.org", this will return true.  It would return false the other way
20  * around.
21  */
22 function hasRootDomain(str, aDomain)
24   let index = str.indexOf(aDomain);
25   // If aDomain is not found, we know we do not have it as a root domain.
26   if (index == -1)
27     return false;
29   // If the strings are the same, we obviously have a match.
30   if (str == aDomain)
31     return true;
33   // Otherwise, we have aDomain as our root domain iff the index of aDomain is
34   // aDomain.length subtracted from our length and (since we do not have an
35   // exact match) the character before the index is a dot or slash.
36   let prevChar = str[index - 1];
37   return (index == (str.length - aDomain.length)) &&
38          (prevChar == "." || prevChar == "/");
41 const Cc = Components.classes;
42 const Ci = Components.interfaces;
43 const Cu = Components.utils;
45 this.ForgetAboutSite = {
46   removeDataFromDomain: function CRH_removeDataFromDomain(aDomain)
47   {
48     PlacesUtils.history.removePagesFromHost(aDomain, true);
50     // Cache
51     let (cs = Cc["@mozilla.org/netwerk/cache-storage-service;1"].
52               getService(Ci.nsICacheStorageService)) {
53       // NOTE: there is no way to clear just that domain, so we clear out
54       //       everything)
55       try {
56         cs.clear();
57       } catch (ex) {
58         Cu.reportError("Exception thrown while clearing the cache: " +
59           ex.toString());
60       }
61     }
63     // Image Cache
64     let (imageCache = Cc["@mozilla.org/image/tools;1"].
65                       getService(Ci.imgITools).getImgCacheForDocument(null)) {
66       try {
67         imageCache.clearCache(false); // true=chrome, false=content
68       } catch (ex) {
69         Cu.reportError("Exception thrown while clearing the image cache: " +
70           ex.toString());
71       }
72     }
74     // Cookies
75     let (cm = Cc["@mozilla.org/cookiemanager;1"].
76               getService(Ci.nsICookieManager2)) {
77       let enumerator = cm.getCookiesFromHost(aDomain);
78       while (enumerator.hasMoreElements()) {
79         let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie);
80         cm.remove(cookie.host, cookie.name, cookie.path, false);
81       }
82     }
84     // Plugin data
85     const phInterface = Ci.nsIPluginHost;
86     const FLAG_CLEAR_ALL = phInterface.FLAG_CLEAR_ALL;
87     let (ph = Cc["@mozilla.org/plugin/host;1"].getService(phInterface)) {
88       let tags = ph.getPluginTags();
89       for (let i = 0; i < tags.length; i++) {
90         try {
91           ph.clearSiteData(tags[i], aDomain, FLAG_CLEAR_ALL, -1);
92         } catch (e) {
93           // Ignore errors from the plugin
94         }
95       }
96     }
98     // Downloads
99     let useJSTransfer = false;
100     try {
101       // This method throws an exception if the old Download Manager is disabled.
102       Services.downloads.activeDownloadCount;
103     } catch (ex) {
104       useJSTransfer = true;
105     }
107     if (useJSTransfer) {
108       Task.spawn(function() {
109         let list = yield Downloads.getList(Downloads.ALL);
110         list.removeFinished(download => hasRootDomain(
111              NetUtil.newURI(download.source.url).host, aDomain));
112       }).then(null, Cu.reportError);
113     }
114     else {
115       let (dm = Cc["@mozilla.org/download-manager;1"].
116                 getService(Ci.nsIDownloadManager)) {
117         // Active downloads
118         for (let enumerator of [dm.activeDownloads, dm.activePrivateDownloads]) {
119           while (enumerator.hasMoreElements()) {
120             let dl = enumerator.getNext().QueryInterface(Ci.nsIDownload);
121             if (hasRootDomain(dl.source.host, aDomain)) {
122               dl.cancel();
123               dl.remove();
124             }
125           }
126         }
128         function deleteAllLike(db) {
129           // NOTE: This is lossy, but we feel that it is OK to be lossy here and not
130           //       invoke the cost of creating a URI for each download entry and
131           //       ensure that the hostname matches.
132           let stmt = db.createStatement(
133             "DELETE FROM moz_downloads " +
134             "WHERE source LIKE ?1 ESCAPE '/' " +
135             "AND state NOT IN (?2, ?3, ?4)"
136           );
137           let pattern = stmt.escapeStringForLIKE(aDomain, "/");
138           stmt.bindByIndex(0, "%" + pattern + "%");
139           stmt.bindByIndex(1, Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING);
140           stmt.bindByIndex(2, Ci.nsIDownloadManager.DOWNLOAD_PAUSED);
141           stmt.bindByIndex(3, Ci.nsIDownloadManager.DOWNLOAD_QUEUED);
142           try {
143             stmt.execute();
144           }
145           finally {
146             stmt.finalize();
147           }
148         }
150         // Completed downloads
151         deleteAllLike(dm.DBConnection);
152         deleteAllLike(dm.privateDBConnection);
154         // We want to rebuild the list if the UI is showing, so dispatch the
155         // observer topic
156         let os = Cc["@mozilla.org/observer-service;1"].
157                  getService(Ci.nsIObserverService);
158         os.notifyObservers(null, "download-manager-remove-download", null);
159       }
160     }
162     // Passwords
163     let (lm = Cc["@mozilla.org/login-manager;1"].
164               getService(Ci.nsILoginManager)) {
165       // Clear all passwords for domain
166       try {
167         let logins = lm.getAllLogins();
168         for (let i = 0; i < logins.length; i++)
169           if (hasRootDomain(logins[i].hostname, aDomain))
170             lm.removeLogin(logins[i]);
171       }
172       // XXXehsan: is there a better way to do this rather than this
173       // hacky comparison?
174       catch (ex if ex.message.indexOf("User canceled Master Password entry") != -1) { }
176       // Clear any "do not save for this site" for this domain
177       let disabledHosts = lm.getAllDisabledHosts();
178       for (let i = 0; i < disabledHosts.length; i++)
179         if (hasRootDomain(disabledHosts[i], aDomain))
180           lm.setLoginSavingEnabled(disabledHosts, true);
181     }
183     // Permissions
184     let (pm = Cc["@mozilla.org/permissionmanager;1"].
185               getService(Ci.nsIPermissionManager)) {
186       // Enumerate all of the permissions, and if one matches, remove it
187       let enumerator = pm.enumerator;
188       while (enumerator.hasMoreElements()) {
189         let perm = enumerator.getNext().QueryInterface(Ci.nsIPermission);
190         if (hasRootDomain(perm.host, aDomain))
191           pm.remove(perm.host, perm.type);
192       }
193     }
195     // Offline Storages
196     let (qm = Cc["@mozilla.org/dom/quota/manager;1"].
197               getService(Ci.nsIQuotaManager)) {
198       // delete data from both HTTP and HTTPS sites
199       let caUtils = {};
200       let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
201                          getService(Ci.mozIJSSubScriptLoader);
202       scriptLoader.loadSubScript("chrome://global/content/contentAreaUtils.js",
203                                  caUtils);
204       let httpURI = caUtils.makeURI("http://" + aDomain);
205       let httpsURI = caUtils.makeURI("https://" + aDomain);
206       qm.clearStoragesForURI(httpURI);
207       qm.clearStoragesForURI(httpsURI);
208     }
210     function onContentPrefsRemovalFinished() {
211       // Everybody else (including extensions)
212       Services.obs.notifyObservers(null, "browser:purge-domain-data", aDomain);
213     }
215     // Content Preferences
216     let cps2 = Cc["@mozilla.org/content-pref/service;1"].
217                getService(Ci.nsIContentPrefService2);
218     cps2.removeBySubdomain(aDomain, null, {
219       handleCompletion: function() onContentPrefsRemovalFinished(),
220       handleError: function() {}
221     });
223     // Predictive network data - like cache, no way to clear this per
224     // domain, so just trash it all
225     let np = Cc["@mozilla.org/network/predictor;1"].
226              getService(Ci.nsINetworkPredictor);
227     np.reset();
228   }