Bug 1410416 - Part 1: Have SiteDateManager.jsm call `propagateUnregister` to remove service workers, r=baku
☠☠ backed out by 6c869780c87e ☠ ☠
authorFischer.json <fischer.json@gmail.com>
Fri, 20 Oct 2017 21:35:58 +0800
changeset 443174 c2d97da8bfedf7396aa3683153164accb580aaf9
parent 443173 4ecf1d9c1975c76b41a5f9fbf564e8693b26fcd5
child 443175 b0d6deea2bca0a798a2966630f7cfc25e494507c
push id1618
push userCallek@gmail.com
push dateThu, 11 Jan 2018 17:45:48 +0000
treeherdermozilla-release@882ca853e05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1410416
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1410416 - Part 1: Have SiteDateManager.jsm call `propagateUnregister` to remove service workers, r=baku MozReview-Commit-ID: BNUhm6a2x1b
browser/components/preferences/SiteDataManager.jsm
--- a/browser/components/preferences/SiteDataManager.jsm
+++ b/browser/components/preferences/SiteDataManager.jsm
@@ -1,11 +1,11 @@
 "use strict";
 
-const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "OfflineAppCacheHelper",
                                   "resource:///modules/offlineAppCache.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
                                   "resource://gre/modules/ContextualIdentityService.jsm");
@@ -44,47 +44,49 @@ this.SiteDataManager = {
   },
 
   _getQuotaUsage() {
     // Clear old data and requests first
     this._sites.clear();
     this._cancelGetQuotaUsage();
     this._getQuotaUsagePromise = new Promise(resolve => {
       let onUsageResult = request => {
-        let items = request.result;
-        for (let item of items) {
-          if (!item.persisted && item.usage <= 0) {
-            // An non-persistent-storage site with 0 byte quota usage is redundant for us so skip it.
-            continue;
-          }
-          let principal =
-            Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(item.origin);
-          let uri = principal.URI;
-          if (uri.scheme == "http" || uri.scheme == "https") {
-            let site = this._sites.get(uri.host);
-            if (!site) {
-              site = {
-                persisted: false,
-                quotaUsage: 0,
-                principals: [],
-                appCacheList: [],
-              };
+        if (request.resultCode == Cr.NS_OK) {
+          let items = request.result;
+          for (let item of items) {
+            if (!item.persisted && item.usage <= 0) {
+              // An non-persistent-storage site with 0 byte quota usage is redundant for us so skip it.
+              continue;
             }
-            // Assume 3 sites:
-            //   - Site A (not persisted): https://www.foo.com
-            //   - Site B (not persisted): https://www.foo.com^userContextId=2
-            //   - Site C (persisted):     https://www.foo.com:1234
-            // Although only C is persisted, grouping by host, as a result,
-            // we still mark as persisted here under this host group.
-            if (item.persisted) {
-              site.persisted = true;
+            let principal =
+              Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(item.origin);
+            let uri = principal.URI;
+            if (uri.scheme == "http" || uri.scheme == "https") {
+              let site = this._sites.get(uri.host);
+              if (!site) {
+                site = {
+                  persisted: false,
+                  quotaUsage: 0,
+                  principals: [],
+                  appCacheList: [],
+                };
+              }
+              // Assume 3 sites:
+              //   - Site A (not persisted): https://www.foo.com
+              //   - Site B (not persisted): https://www.foo.com^userContextId=2
+              //   - Site C (persisted):     https://www.foo.com:1234
+              // Although only C is persisted, grouping by host, as a result,
+              // we still mark as persisted here under this host group.
+              if (item.persisted) {
+                site.persisted = true;
+              }
+              site.principals.push(principal);
+              site.quotaUsage += item.usage;
+              this._sites.set(uri.host, site);
             }
-            site.principals.push(principal);
-            site.quotaUsage += item.usage;
-            this._sites.set(uri.host, site);
           }
         }
         resolve();
       };
       // XXX: The work of integrating localStorage into Quota Manager is in progress.
       //      After the bug 742822 and 1286798 landed, localStorage usage will be included.
       //      So currently only get indexedDB usage.
       this._quotaUsageRequest = this._qms.getUsage(onUsageResult);
@@ -217,78 +219,96 @@ this.SiteDataManager = {
             cookie.host, cookie.name, cookie.path, false, cookie.originAttributes);
         }
       }
 
       Services.obs.notifyObservers(null, "browser:purge-domain-data", principal.URI.host);
     }
   },
 
-  _removeServiceWorkers(site) {
+  _unregisterServiceWorker(serviceWorker) {
+    return new Promise(resolve => {
+      let unregisterCallback = {
+        unregisterSucceeded: resolve,
+        unregisterFailed: resolve, // We don't care about failures.
+        QueryInterface: XPCOMUtils.generateQI([Ci.nsIServiceWorkerUnregisterCallback])
+      };
+      serviceWorkerManager.propagateUnregister(serviceWorker.principal, unregisterCallback, serviceWorker.scope);
+    });
+  },
+
+  _removeServiceWorkersForSites(sites) {
+    let promises = [];
+    let targetHosts = sites.map(s => s.principals[0].URI.host);
     let serviceWorkers = serviceWorkerManager.getAllRegistrations();
     for (let i = 0; i < serviceWorkers.length; i++) {
       let sw = serviceWorkers.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);
-      for (let principal of site.principals) {
-        if (sw.principal.equals(principal)) {
-          serviceWorkerManager.removeAndPropagate(sw.principal.URI.host);
-          break;
-        }
+      // Sites are grouped and removed by host so we unregister service workers by the same host as well
+      if (targetHosts.includes(sw.principal.URI.host)) {
+        promises.push(this._unregisterServiceWorker(sw));
       }
     }
+    return Promise.all(promises);
   },
 
   remove(hosts) {
-    let promises = [];
     let unknownHost = "";
+    let targetSites = [];
     for (let host of hosts) {
       let site = this._sites.get(host);
       if (site) {
         this._removePermission(site);
         this._removeAppCache(site);
         this._removeCookie(site);
-        this._removeServiceWorkers(site);
-        promises.push(this._removeQuotaUsage(site));
+        targetSites.push(site);
       } else {
         unknownHost = host;
         break;
       }
     }
-    if (promises.length > 0) {
-      Promise.all(promises).then(() => this.updateSites());
+
+    if (targetSites.length > 0) {
+      this._removeServiceWorkersForSites(targetSites)
+          .then(() => {
+            let promises = targetSites.map(s => this._removeQuotaUsage(s));
+            return Promise.all(promises);
+          })
+          .then(() => this.updateSites());
     }
     if (unknownHost) {
       throw `SiteDataManager: removing unknown site of ${unknownHost}`;
     }
   },
 
   async removeAll() {
     Services.cache2.clear();
     Services.cookies.removeAll();
     OfflineAppCacheHelper.clear();
 
     // Iterate through the service workers and remove them.
+    let promises = [];
     let serviceWorkers = serviceWorkerManager.getAllRegistrations();
     for (let i = 0; i < serviceWorkers.length; i++) {
       let sw = serviceWorkers.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);
-      let host = sw.principal.URI.host;
-      serviceWorkerManager.removeAndPropagate(host);
+      promises.push(this._unregisterServiceWorker(sw));
     }
+    await Promise.all(promises);
 
     // Refresh sites using quota usage again.
     // This is for the case:
     //   1. User goes to the about:preferences Site Data section.
     //   2. With the about:preferences opened, user visits another website.
     //   3. The website saves to quota usage, like indexedDB.
     //   4. User goes back to the Site Data section and commands to clear all site data.
     // For this case, we should refresh the site list so not to miss the website in the step 3.
     // We don't do "Clear All" on the quota manager like the cookie, appcache, http cache above
     // because that would clear browser data as well too,
     // see https://bugzilla.mozilla.org/show_bug.cgi?id=1312361#c9
     await this._getQuotaUsage();
-    let promises = [];
+    promises = [];
     for (let site of this._sites.values()) {
       this._removePermission(site);
       promises.push(this._removeQuotaUsage(site));
     }
     return Promise.all(promises).then(() => this.updateSites());
   },
 
   isPrivateCookie(cookie) {