2019-03-11 03:26:37 -07:00
|
|
|
/* 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/. */
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
Components.utils.import("resource://gre/modules/Services.jsm");
|
|
|
|
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
|
Components.utils.import("resource://gre/modules/NetUtil.jsm");
|
|
|
|
Components.utils.import("resource://gre/modules/Task.jsm");
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
|
|
|
"resource://gre/modules/PlacesUtils.jsm");
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
|
|
|
|
"resource://gre/modules/Downloads.jsm");
|
|
|
|
|
|
|
|
this.EXPORTED_SYMBOLS = ["ForgetAboutSite"];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if the string passed in is part of the root domain of the
|
|
|
|
* current string. For example, if this is "www.mozilla.org", and we pass in
|
|
|
|
* "mozilla.org", this will return true. It would return false the other way
|
|
|
|
* around.
|
|
|
|
*/
|
|
|
|
function hasRootDomain(str, aDomain)
|
|
|
|
{
|
|
|
|
let index = str.indexOf(aDomain);
|
|
|
|
// If aDomain is not found, we know we do not have it as a root domain.
|
|
|
|
if (index == -1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// If the strings are the same, we obviously have a match.
|
|
|
|
if (str == aDomain)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// Otherwise, we have aDomain as our root domain iff the index of aDomain is
|
|
|
|
// aDomain.length subtracted from our length and (since we do not have an
|
|
|
|
// exact match) the character before the index is a dot or slash.
|
|
|
|
let prevChar = str[index - 1];
|
|
|
|
return (index == (str.length - aDomain.length)) &&
|
|
|
|
(prevChar == "." || prevChar == "/");
|
|
|
|
}
|
|
|
|
|
|
|
|
const Cc = Components.classes;
|
|
|
|
const Ci = Components.interfaces;
|
|
|
|
const Cu = Components.utils;
|
|
|
|
|
|
|
|
this.ForgetAboutSite = {
|
|
|
|
removeDataFromDomain: Task.async(function* (aDomain)
|
|
|
|
{
|
|
|
|
PlacesUtils.history.removePagesFromHost(aDomain, true);
|
|
|
|
|
|
|
|
let promises = [];
|
|
|
|
// Cache
|
|
|
|
promises.push(Task.spawn(function*() {
|
|
|
|
let cs = Cc["@mozilla.org/netwerk/cache-storage-service;1"].
|
|
|
|
getService(Ci.nsICacheStorageService);
|
|
|
|
// NOTE: there is no way to clear just that domain, so we clear out
|
|
|
|
// everything)
|
|
|
|
cs.clear();
|
|
|
|
}).catch(ex => {
|
|
|
|
throw new Error("Exception thrown while clearing the cache: " + ex);
|
|
|
|
}));
|
|
|
|
|
|
|
|
// Image Cache
|
|
|
|
promises.push(Task.spawn(function*() {
|
|
|
|
let imageCache = Cc["@mozilla.org/image/tools;1"].
|
|
|
|
getService(Ci.imgITools).getImgCacheForDocument(null);
|
|
|
|
imageCache.clearCache(false); // true=chrome, false=content
|
|
|
|
}).catch(ex => {
|
|
|
|
throw new Error("Exception thrown while clearing the image cache: " + ex);
|
|
|
|
}));
|
|
|
|
|
|
|
|
// Cookies
|
|
|
|
// Need to maximize the number of cookies cleaned here
|
|
|
|
promises.push(Task.spawn(function*() {
|
|
|
|
let cm = Cc["@mozilla.org/cookiemanager;1"].
|
|
|
|
getService(Ci.nsICookieManager2);
|
|
|
|
let enumerator = cm.getCookiesWithOriginAttributes(JSON.stringify({}), aDomain);
|
|
|
|
while (enumerator.hasMoreElements()) {
|
|
|
|
let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie);
|
|
|
|
cm.remove(cookie.host, cookie.name, cookie.path, false, cookie.originAttributes);
|
|
|
|
}
|
|
|
|
}).catch(ex => {
|
|
|
|
throw new Error("Exception thrown while clearning cookies: " + ex);
|
|
|
|
}));
|
|
|
|
|
2019-03-12 09:36:48 -07:00
|
|
|
#ifdef THE_GMP
|
2019-03-11 03:26:37 -07:00
|
|
|
// EME
|
|
|
|
promises.push(Task.spawn(function*() {
|
|
|
|
let mps = Cc["@mozilla.org/gecko-media-plugin-service;1"].
|
|
|
|
getService(Ci.mozIGeckoMediaPluginChromeService);
|
|
|
|
mps.forgetThisSite(aDomain, JSON.stringify({}));
|
|
|
|
}).catch(ex => {
|
|
|
|
throw new Error("Exception thrown while clearing Encrypted Media Extensions: " + ex);
|
|
|
|
}));
|
2019-03-12 09:36:48 -07:00
|
|
|
#endif
|
2019-03-11 03:26:37 -07:00
|
|
|
|
|
|
|
// Plugin data
|
|
|
|
const phInterface = Ci.nsIPluginHost;
|
|
|
|
const FLAG_CLEAR_ALL = phInterface.FLAG_CLEAR_ALL;
|
|
|
|
let ph = Cc["@mozilla.org/plugin/host;1"].getService(phInterface);
|
|
|
|
let tags = ph.getPluginTags();
|
|
|
|
for (let i = 0; i < tags.length; i++) {
|
|
|
|
promises.push(new Promise(resolve => {
|
|
|
|
try {
|
|
|
|
ph.clearSiteData(tags[i], aDomain, FLAG_CLEAR_ALL, -1, resolve);
|
|
|
|
} catch (e) {
|
|
|
|
// Ignore errors from the plugin, but resolve the promise
|
|
|
|
// We cannot check if something is a bailout or an error
|
|
|
|
resolve();
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Downloads
|
|
|
|
promises.push(Task.spawn(function*() {
|
|
|
|
let list = yield Downloads.getList(Downloads.ALL);
|
|
|
|
list.removeFinished(download => hasRootDomain(
|
|
|
|
NetUtil.newURI(download.source.url).host, aDomain));
|
|
|
|
}).catch(ex => {
|
|
|
|
throw new Error("Exception in clearing Downloads: " + ex);
|
|
|
|
}));
|
|
|
|
|
|
|
|
// Passwords
|
|
|
|
promises.push(Task.spawn(function*() {
|
|
|
|
let lm = Cc["@mozilla.org/login-manager;1"].
|
|
|
|
getService(Ci.nsILoginManager);
|
|
|
|
// Clear all passwords for domain
|
|
|
|
let logins = lm.getAllLogins();
|
|
|
|
for (let i = 0; i < logins.length; i++) {
|
|
|
|
if (hasRootDomain(logins[i].hostname, aDomain)) {
|
|
|
|
lm.removeLogin(logins[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}).catch(ex => {
|
|
|
|
// XXX:
|
|
|
|
// Is there a better way to do this rather than this hacky comparison?
|
|
|
|
// Copied this from toolkit/components/passwordmgr/crypto-SDR.js
|
|
|
|
if (!ex.message.includes("User canceled master password entry")) {
|
|
|
|
throw new Error("Exception occured in clearing passwords: " + ex);
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
|
|
|
|
// Permissions
|
|
|
|
let pm = Cc["@mozilla.org/permissionmanager;1"].
|
|
|
|
getService(Ci.nsIPermissionManager);
|
|
|
|
// Enumerate all of the permissions, and if one matches, remove it
|
|
|
|
let enumerator = pm.enumerator;
|
|
|
|
while (enumerator.hasMoreElements()) {
|
|
|
|
let perm = enumerator.getNext().QueryInterface(Ci.nsIPermission);
|
|
|
|
promises.push(new Promise((resolve, reject) => {
|
|
|
|
try {
|
|
|
|
if (hasRootDomain(perm.principal.URI.host, aDomain)) {
|
|
|
|
pm.removePermission(perm);
|
|
|
|
}
|
|
|
|
} catch (ex) {
|
|
|
|
// Ignore entry
|
|
|
|
} finally {
|
|
|
|
resolve();
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Offline Storages
|
|
|
|
promises.push(Task.spawn(function*() {
|
|
|
|
let qms = Cc["@mozilla.org/dom/quota-manager-service;1"].
|
|
|
|
getService(Ci.nsIQuotaManagerService);
|
|
|
|
// delete data from both HTTP and HTTPS sites
|
|
|
|
let httpURI = NetUtil.newURI("http://" + aDomain);
|
|
|
|
let httpsURI = NetUtil.newURI("https://" + aDomain);
|
|
|
|
// Following code section has been reverted to the state before Bug 1238183,
|
|
|
|
// but added a new argument to clearStoragesForPrincipal() for indicating
|
|
|
|
// clear all storages under a given origin.
|
|
|
|
let httpPrincipal = Services.scriptSecurityManager
|
|
|
|
.createCodebasePrincipal(httpURI, {});
|
|
|
|
let httpsPrincipal = Services.scriptSecurityManager
|
|
|
|
.createCodebasePrincipal(httpsURI, {});
|
|
|
|
qms.clearStoragesForPrincipal(httpPrincipal, null, true);
|
|
|
|
qms.clearStoragesForPrincipal(httpsPrincipal, null, true);
|
|
|
|
}).catch(ex => {
|
|
|
|
throw new Error("Exception occured while clearing offline storages: " + ex);
|
|
|
|
}));
|
|
|
|
|
|
|
|
// Content Preferences
|
|
|
|
promises.push(Task.spawn(function*() {
|
|
|
|
let cps2 = Cc["@mozilla.org/content-pref/service;1"].
|
|
|
|
getService(Ci.nsIContentPrefService2);
|
|
|
|
cps2.removeBySubdomain(aDomain, null, {
|
|
|
|
handleCompletion: (reason) => {
|
|
|
|
// Notify other consumers, including extensions
|
|
|
|
Services.obs.notifyObservers(null, "browser:purge-domain-data", aDomain);
|
|
|
|
if (reason === cps2.COMPLETE_ERROR) {
|
|
|
|
throw new Error("Exception occured while clearing content preferences");
|
|
|
|
}
|
|
|
|
},
|
|
|
|
handleError() {}
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
// Predictive network data - like cache, no way to clear this per
|
|
|
|
// domain, so just trash it all
|
|
|
|
promises.push(Task.spawn(function*() {
|
|
|
|
let np = Cc["@mozilla.org/network/predictor;1"].
|
|
|
|
getService(Ci.nsINetworkPredictor);
|
|
|
|
np.reset();
|
|
|
|
}).catch(ex => {
|
|
|
|
throw new Error("Exception occured while clearing predictive network data: " + ex);
|
|
|
|
}));
|
|
|
|
|
|
|
|
// Push notifications
|
|
|
|
promises.push(Task.spawn(function*() {
|
|
|
|
var push = Cc["@mozilla.org/push/Service;1"].
|
|
|
|
getService(Ci.nsIPushService);
|
|
|
|
push.clearForDomain(aDomain, status => {
|
|
|
|
if (!Components.isSuccessCode(status)) {
|
|
|
|
throw new Error("Exception occured while clearing push notifications: " + status);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
2020-05-07 04:44:50 -07:00
|
|
|
// HSTS
|
|
|
|
// TODO (bug 1290529): also remove HSTS information for subdomains.
|
2019-03-11 03:26:37 -07:00
|
|
|
// Since we can't enumerate the information in the site security service
|
|
|
|
// (bug 1115712), we can't implement this right now.
|
|
|
|
promises.push(Task.spawn(function*() {
|
|
|
|
let sss = Cc["@mozilla.org/ssservice;1"].
|
|
|
|
getService(Ci.nsISiteSecurityService);
|
|
|
|
let httpsURI = NetUtil.newURI("https://" + aDomain);
|
|
|
|
sss.removeState(Ci.nsISiteSecurityService.HEADER_HSTS, httpsURI, 0);
|
|
|
|
}).catch(ex => {
|
2020-05-07 04:44:50 -07:00
|
|
|
throw new Error("Exception thrown while clearing HSTS: " + ex);
|
2019-03-11 03:26:37 -07:00
|
|
|
}));
|
|
|
|
|
|
|
|
let ErrorCount = 0;
|
|
|
|
for (let promise of promises) {
|
|
|
|
try {
|
|
|
|
yield promise;
|
|
|
|
} catch (ex) {
|
|
|
|
Cu.reportError(ex);
|
|
|
|
ErrorCount++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ErrorCount !== 0)
|
|
|
|
throw new Error(`There were a total of ${ErrorCount} errors during removal`);
|
|
|
|
})
|
|
|
|
}
|