Bug 1247201 - Run cleaners async to clear as much as possible draft
authormilindl <i.milind.luthra@gmail.com>
Mon, 27 Mar 2017 19:27:35 +0530
changeset 551876 8a7889b77eb4f5426a7816b2f6e48ea0199b4ab7
parent 501295 1b9293be51637f841275541d8991314ca56561a5
child 551877 b3e52d6878fe313615df746cb791169cf533a397
push id51178
push userbmo:i.milind.luthra@gmail.com
push dateMon, 27 Mar 2017 15:42:31 +0000
bugs1247201
milestone55.0a1
Bug 1247201 - Run cleaners async to clear as much as possible Amended to fix review changes (stylistic + other) Turns all cleaners into promises so they run asyc MozReview-Commit-ID: DV5ug6vNXkS
browser/components/places/content/controller.js
toolkit/forgetaboutsite/ForgetAboutSite.jsm
--- a/browser/components/places/content/controller.js
+++ b/browser/components/places/content/controller.js
@@ -244,17 +244,18 @@ PlacesController.prototype = {
       break;
     case "placesCmd_deleteDataHost":
       var host;
       if (PlacesUtils.nodeIsHost(this._view.selectedNode)) {
         var queries = this._view.selectedNode.getQueries();
         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();
       break;
     case "placesCmd_open":
       PlacesUIUtils.openNodeIn(this._view.selectedNode, "current", this._view);
       break;
     case "placesCmd_open:window":
--- a/toolkit/forgetaboutsite/ForgetAboutSite.jsm
+++ b/toolkit/forgetaboutsite/ForgetAboutSite.jsm
@@ -39,188 +39,213 @@ function hasRootDomain(str, aDomain) {
          (prevChar == "." || prevChar == "/");
 }
 
 const Cc = Components.classes;
 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,
                         Ci.nsISiteSecurityService.HEADER_HPKP]) {
         // Also remove HSTS/HPKP information for subdomains by enumerating the
         // information in the site security service.
         let enumerator = sss.enumerate(type);
         while (enumerator.hasMoreElements()) {
           let entry = enumerator.getNext();
           let hostname = entry.QueryInterface(Ci.nsISiteSecurityState).hostname;
           // 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);
+    }));
+
+    let ErrorCount = 0;
+    for (let promise of promises) {
+      try {
+        yield promise;
+      } catch (ex) {
+        Cu.reportError(ex);
+        ErrorCount++;
+      }
     }
-
-    return Promise.all(promises);
-  }
-};
+    if (ErrorCount !== 0)
+      throw new Error(`There were a total of ${ErrorCount} errors during removal`);
+  })
+}