From d901d9c90a7e378678aeb21e797162ebf2c352c3 Mon Sep 17 00:00:00 2001 From: milindl Date: Mon, 27 Mar 2017 19:27:35 +0530 Subject: [PATCH] Bug 1247201 - Run cleaners async to clear as much as possible r=mak Amended to fix review changes (stylistic + other) Turns all cleaners into promises so they run asyc MozReview-Commit-ID: DV5ug6vNXkS --HG-- extra : rebase_source : df9d9cd98f25e2a899c0a74f836f217d3ad52426 --- .../components/places/content/controller.js | 3 +- toolkit/forgetaboutsite/ForgetAboutSite.jsm | 229 ++++++++++-------- 2 files changed, 129 insertions(+), 103 deletions(-) diff --git a/browser/components/places/content/controller.js b/browser/components/places/content/controller.js index 291a54095670..ab996477a260 100644 --- a/browser/components/places/content/controller.js +++ b/browser/components/places/content/controller.js @@ -249,7 +249,8 @@ PlacesController.prototype = { host = queries[0].domain; } else host = NetUtil.newURI(this._view.selectedNode.uri).host; - ForgetAboutSite.removeDataFromDomain(host); + ForgetAboutSite.removeDataFromDomain(host) + .catch(Components.utils.reportError); break; case "cmd_selectAll": this.selectAll(); diff --git a/toolkit/forgetaboutsite/ForgetAboutSite.jsm b/toolkit/forgetaboutsite/ForgetAboutSite.jsm index fb13a295348e..e6e7e224b6a3 100644 --- a/toolkit/forgetaboutsite/ForgetAboutSite.jsm +++ b/toolkit/forgetaboutsite/ForgetAboutSite.jsm @@ -44,160 +44,176 @@ const Ci = Components.interfaces; const Cu = Components.utils; this.ForgetAboutSite = { - removeDataFromDomain: function CRH_removeDataFromDomain(aDomain) { + removeDataFromDomain: Task.async(function* (aDomain) { PlacesUtils.history.removePagesFromHost(aDomain, true); + let promises = []; // Cache - 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) - try { + 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) { - Cu.reportError("Exception thrown while clearing the cache: " + - ex.toString()); - } + }).catch(ex => { + throw new Error("Exception thrown while clearing the cache: " + ex); + })); // Image Cache - let imageCache = Cc["@mozilla.org/image/tools;1"]. - getService(Ci.imgITools).getImgCacheForDocument(null); - try { + 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) { - Cu.reportError("Exception thrown while clearing the image cache: " + - ex.toString()); - } + }).catch(ex => { + throw new Error("Exception thrown while clearing the image cache: " + ex); + })); // Cookies - 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); - } + // 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); + })); // EME - let mps = Cc["@mozilla.org/gecko-media-plugin-service;1"]. - getService(Ci.mozIGeckoMediaPluginChromeService); - mps.forgetThisSite(aDomain, JSON.stringify({})); + 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); + })); + // 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(); - let promises = []; for (let i = 0; i < tags.length; i++) { - let promise = new Promise(resolve => { + promises.push(new Promise(resolve => { try { - ph.clearSiteData(tags[i], aDomain, FLAG_CLEAR_ALL, -1, function(rv) { - resolve(); - }); + 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(); } - }); - promises.push(promise); + })); } // Downloads - Task.spawn(function*() { + promises.push(Task.spawn(function*() { let list = yield Downloads.getList(Downloads.ALL); list.removeFinished(download => hasRootDomain( - NetUtil.newURI(download.source.url).host, aDomain)); - }).then(null, Cu.reportError); + NetUtil.newURI(download.source.url).host, aDomain)); + }).catch(ex => { + throw new Error("Exception in clearing Downloads: " + ex); + })); // Passwords - let lm = Cc["@mozilla.org/login-manager;1"]. - getService(Ci.nsILoginManager); - // Clear all passwords for domain - try { + 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) { + }).catch(ex => { // XXXehsan: is there a better way to do this rather than this // hacky comparison? if (ex.message.indexOf("User canceled Master Password entry") == -1) { - throw ex; + 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 - enumerator = pm.enumerator; + let enumerator = pm.enumerator; while (enumerator.hasMoreElements()) { let perm = enumerator.getNext().QueryInterface(Ci.nsIPermission); - try { - if (hasRootDomain(perm.principal.URI.host, aDomain)) { - pm.removePermission(perm); - } - } catch (e) { - /* Ignore entry */ - } + 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 - let qms = Cc["@mozilla.org/dom/quota-manager-service;1"]. - getService(Ci.nsIQuotaManagerService); - // delete data from both HTTP and HTTPS sites - let caUtils = {}; - let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"]. - getService(Ci.mozIJSSubScriptLoader); - scriptLoader.loadSubScript("chrome://global/content/contentAreaUtils.js", - caUtils); - let httpURI = caUtils.makeURI("http://" + aDomain); - let httpsURI = caUtils.makeURI("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); - - - function onContentPrefsRemovalFinished() { - // Everybody else (including extensions) - Services.obs.notifyObservers(null, "browser:purge-domain-data", aDomain); - } + 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 - let cps2 = Cc["@mozilla.org/content-pref/service;1"]. - getService(Ci.nsIContentPrefService2); - cps2.removeBySubdomain(aDomain, null, { - handleCompletion: () => onContentPrefsRemovalFinished(), - handleError() {} - }); + 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 - let np = Cc["@mozilla.org/network/predictor;1"]. - getService(Ci.nsINetworkPredictor); - np.reset(); + 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(new Promise((resolve, reject) => { - var push = Cc["@mozilla.org/push/Service;1"] - .getService(Ci.nsIPushService); + promises.push(Task.spawn(function*() { + var push = Cc["@mozilla.org/push/Service;1"]. + getService(Ci.nsIPushService); push.clearForDomain(aDomain, status => { - (Components.isSuccessCode(status) ? resolve : reject)(status); + if (!Components.isSuccessCode(status)) { + throw new Error("Exception occured while clearing push notifications: " + status); + } }); - }).catch(e => { - Cu.reportError("Exception thrown while clearing Push notifications: " + - e.toString()); })); // HSTS and HPKP - try { + promises.push(Task.spawn(function*() { let sss = Cc["@mozilla.org/ssservice;1"]. getService(Ci.nsISiteSecurityService); for (let type of [Ci.nsISiteSecurityService.HEADER_HSTS, @@ -211,16 +227,25 @@ this.ForgetAboutSite = { // If the hostname is aDomain's subdomain, we remove its state. if (hostname == aDomain || hostname.endsWith("." + aDomain)) { // This uri is used as a key to remove the state. - let uri = caUtils.makeURI("https://" + hostname); + let uri = NetUtil.newURI("https://" + hostname); sss.removeState(type, uri, 0, entry.originAttributes); } } } - } catch (e) { - Cu.reportError("Exception thrown while clearing HSTS/HPKP: " + - e.toString()); - } + }).catch(ex => { + throw new Error("Exception thrown while clearing HSTS/HPKP: " + ex); + })); - return Promise.all(promises); - } -}; + 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`); + }) +}