|
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/. */ |
|
4 |
|
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"); |
|
13 |
|
14 this.EXPORTED_SYMBOLS = ["ForgetAboutSite"]; |
|
15 |
|
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) |
|
23 { |
|
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; |
|
28 |
|
29 // If the strings are the same, we obviously have a match. |
|
30 if (str == aDomain) |
|
31 return true; |
|
32 |
|
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 == "/"); |
|
39 } |
|
40 |
|
41 const Cc = Components.classes; |
|
42 const Ci = Components.interfaces; |
|
43 const Cu = Components.utils; |
|
44 |
|
45 this.ForgetAboutSite = { |
|
46 removeDataFromDomain: function CRH_removeDataFromDomain(aDomain) |
|
47 { |
|
48 PlacesUtils.history.removePagesFromHost(aDomain, true); |
|
49 |
|
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 } |
|
62 |
|
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 } |
|
73 |
|
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 } |
|
83 |
|
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 } |
|
97 |
|
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 } |
|
106 |
|
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 } |
|
127 |
|
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 } |
|
149 |
|
150 // Completed downloads |
|
151 deleteAllLike(dm.DBConnection); |
|
152 deleteAllLike(dm.privateDBConnection); |
|
153 |
|
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 } |
|
161 |
|
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) { } |
|
175 |
|
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 } |
|
182 |
|
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 } |
|
194 |
|
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 } |
|
209 |
|
210 function onContentPrefsRemovalFinished() { |
|
211 // Everybody else (including extensions) |
|
212 Services.obs.notifyObservers(null, "browser:purge-domain-data", aDomain); |
|
213 } |
|
214 |
|
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 }); |
|
222 |
|
223 // Predictive network data - like cache, no way to clear this per |
|
224 // domain, so just trash it all |
|
225 let ns = Cc["@mozilla.org/network/seer;1"]. |
|
226 getService(Ci.nsINetworkSeer); |
|
227 ns.reset(); |
|
228 } |
|
229 }; |