Bug 1286798 - Part 14: Enhance clearStoragesForPrincipal() to support clearing of storages for specific quota client; r=asuth
authorJan Varga <jan.varga@gmail.com>
Thu, 29 Nov 2018 21:47:58 +0100
changeset 508012 20606f9de28fd7dff30f30b3862b2bdc68df85b3
parent 508011 c63189db48cb7b515e53c33bc3203a92761e1d32
child 508013 bb1594f32faefd90e7b2851fbe438e8bfbef81d9
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth
bugs1286798, 1402254
milestone65.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 1286798 - Part 14: Enhance clearStoragesForPrincipal() to support clearing of storages for specific quota client; r=asuth See also bug 1402254, original patch by baku.
browser/base/content/test/sanitize/browser_cookiePermission.js
browser/components/contextualidentity/test/browser/browser_forgetAPI_quota_clearStoragesForPrincipal.js
browser/components/extensions/parent/ext-browsingData.js
browser/components/extensions/test/browser/browser_ext_browsingData_indexedDB.js
browser/components/preferences/in-content/tests/siteData/browser.ini
browser/components/preferences/in-content/tests/siteData/browser_siteData.js
browser/modules/SiteDataManager.jsm
dom/quota/ActorsParent.cpp
dom/quota/Client.h
dom/quota/PQuota.ipdl
dom/quota/QuotaManager.h
dom/quota/QuotaManagerService.cpp
dom/quota/SerializationHelpers.h
dom/quota/nsIQuotaManagerService.idl
mobile/android/modules/Sanitizer.jsm
testing/web-platform/tests/tools/wptrunner/wptrunner/executors/executormarionette.py
toolkit/components/cleardata/ClearDataService.js
--- a/browser/base/content/test/sanitize/browser_cookiePermission.js
+++ b/browser/base/content/test/sanitize/browser_cookiePermission.js
@@ -272,17 +272,17 @@ add_task(async function deleteStorageInA
 
   ok(await checkDataForAboutURL(), "about:newtab data is not deleted.");
 
   // Clean up.
   await Sanitizer.sanitize([ "cookies", "offlineApps" ]);
 
   let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin("about:newtab");
   await new Promise(aResolve => {
-    let req = Services.qms.clearStoragesForPrincipal(principal, null, false);
+    let req = Services.qms.clearStoragesForPrincipal(principal);
     req.callback = () => { aResolve(); };
   });
 });
 
 add_task(async function deleteStorageOnlyCustomPermissionInAboutURL() {
   info("Test about:newtab + permissions");
 
   // Let's clean up all the data.
@@ -308,14 +308,14 @@ add_task(async function deleteStorageOnl
 
   ok(await checkDataForAboutURL(), "about:newtab data is not deleted.");
 
   // Clean up.
   await Sanitizer.sanitize([ "cookies", "offlineApps" ]);
 
   let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin("about:newtab");
   await new Promise(aResolve => {
-    let req = Services.qms.clearStoragesForPrincipal(principal, null, false);
+    let req = Services.qms.clearStoragesForPrincipal(principal);
     req.callback = () => { aResolve(); };
   });
 
   Services.perms.remove(uri, "cookie");
 });
--- a/browser/components/contextualidentity/test/browser/browser_forgetAPI_quota_clearStoragesForPrincipal.js
+++ b/browser/components/contextualidentity/test/browser/browser_forgetAPI_quota_clearStoragesForPrincipal.js
@@ -122,17 +122,17 @@ add_task(async function test_quota_clear
 
   // Using quota manager to clear all indexed DB for a given domain.
   let caUtils = {};
   Services.scriptloader.loadSubScript("chrome://global/content/contentAreaUtils.js",
                                       caUtils);
   let httpURI = caUtils.makeURI("http://" + TEST_HOST);
   let httpPrincipal = Services.scriptSecurityManager
                               .createCodebasePrincipal(httpURI, {});
-  Services.qms.clearStoragesForPrincipal(httpPrincipal, null, true);
+  Services.qms.clearStoragesForPrincipal(httpPrincipal, null, null, true);
 
   for (let userContextId of Object.keys(USER_CONTEXTS)) {
     // Open our tab in the given user context.
     tabs[userContextId] = await openTabInUserContext(TEST_URL + "empty_file.html", userContextId);
 
     // Check whether indexed DB has been cleared.
     await checkIndexedDB(tabs[userContextId].browser);
 
--- a/browser/components/extensions/parent/ext-browsingData.js
+++ b/browser/components/extensions/parent/ext-browsingData.js
@@ -72,32 +72,36 @@ const clearFormData = options => {
 
 const clearHistory = options => {
   return Sanitizer.items.history.clear(makeRange(options));
 };
 
 const clearIndexedDB = async function(options) {
   let promises = [];
 
-  await new Promise(resolve => {
+  await new Promise((resolve, reject) => {
     quotaManagerService.getUsage(request => {
       if (request.resultCode != Cr.NS_OK) {
-        // We are probably shutting down. We don't want to propagate the error,
-        // rejecting the promise.
-        resolve();
+        reject({message: "Clear indexedDB failed"});
         return;
       }
 
       for (let item of request.result) {
         let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(item.origin);
-        let uri = principal.URI;
-        if (uri.scheme == "http" || uri.scheme == "https" || uri.scheme == "file") {
-          promises.push(new Promise(r => {
-            let req = quotaManagerService.clearStoragesForPrincipal(principal, null, false);
-            req.callback = () => { r(); };
+        let scheme = principal.URI.scheme;
+        if (scheme == "http" || scheme == "https" || scheme == "file") {
+          promises.push(new Promise((resolve, reject) => {
+            let clearRequest = quotaManagerService.clearStoragesForPrincipal(principal, null, "idb");
+            clearRequest.callback = () => {
+              if (clearRequest.resultCode == Cr.NS_OK) {
+                resolve();
+              } else {
+                reject({message: "Clear indexedDB failed"});
+              }
+            };
           }));
         }
       }
 
       resolve();
     });
   });
 
--- a/browser/components/extensions/test/browser/browser_ext_browsingData_indexedDB.js
+++ b/browser/components/extensions/test/browser/browser_ext_browsingData_indexedDB.js
@@ -48,16 +48,19 @@ add_task(async function testIndexedDB() 
   await extension.awaitMessage("indexedDBCreated");
   await extension.awaitMessage("indexedDBCreated");
 
   function getOrigins() {
     return new Promise(resolve => {
       let origins = [];
       Services.qms.getUsage(request => {
         for (let i = 0; i < request.result.length; ++i) {
+          if (request.result[i].usage === 0) {
+            continue;
+          }
           if (request.result[i].origin.startsWith("http://mochi.test") ||
               request.result[i].origin.startsWith("http://example.com")) {
             origins.push(request.result[i].origin);
           }
         }
         resolve(origins);
       });
     });
--- a/browser/components/preferences/in-content/tests/siteData/browser.ini
+++ b/browser/components/preferences/in-content/tests/siteData/browser.ini
@@ -4,12 +4,12 @@ support-files =
   site_data_test.html
   service_worker_test.html
   service_worker_test.js
   offline/offline.html
   offline/manifest.appcache
 
 [browser_clearSiteData.js]
 [browser_siteData.js]
-skip-if = (os == 'linux' && debug) # Bug 1439332
+skip-if = (os == 'linux' && debug) || verify # Bug 1439332 and bug 1436395
 [browser_siteData2.js]
 [browser_siteData3.js]
 [browser_siteData_multi_select.js]
--- a/browser/components/preferences/in-content/tests/siteData/browser_siteData.js
+++ b/browser/components/preferences/in-content/tests/siteData/browser_siteData.js
@@ -39,17 +39,17 @@ add_task(async function() {
   let qoutaUsageSite = frameDoc.querySelector(`richlistitem[host="${TEST_QUOTA_USAGE_HOST}"]`);
   ok(qoutaUsageSite, "Should list site using quota usage");
 
   // Always remember to clean up
   OfflineAppCacheHelper.clear();
   await new Promise(resolve => {
     let principal = Services.scriptSecurityManager
                             .createCodebasePrincipalFromOrigin(TEST_QUOTA_USAGE_ORIGIN);
-    let request = Services.qms.clearStoragesForPrincipal(principal, null, true);
+    let request = Services.qms.clearStoragesForPrincipal(principal, null, null, true);
     request.callback = resolve;
   });
 
   await SiteDataManager.removeAll();
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
 }).skip(); // Bug 1414751
 
 // Test buttons are disabled and loading message shown while updating sites
--- a/browser/modules/SiteDataManager.jsm
+++ b/browser/modules/SiteDataManager.jsm
@@ -299,17 +299,17 @@ var SiteDataManager = {
         // below we have already removed across OAs so skip the same origin without suffix
         continue;
       }
       removals.add(originNoSuffix);
       promises.push(new Promise(resolve => {
         // We are clearing *All* across OAs so need to ensure a principal without suffix here,
         // or the call of `clearStoragesForPrincipal` would fail.
         principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(originNoSuffix);
-        let request = this._qms.clearStoragesForPrincipal(principal, null, true);
+        let request = this._qms.clearStoragesForPrincipal(principal, null, null, true);
         request.callback = resolve;
       }));
     }
     return Promise.all(promises);
   },
 
   _removeAppCache(site) {
     for (let cache of site.appCacheList) {
--- a/dom/quota/ActorsParent.cpp
+++ b/dom/quota/ActorsParent.cpp
@@ -948,16 +948,17 @@ class NormalOriginOperationBase
   : public OriginOperationBase
   , public OpenDirectoryListener
 {
   RefPtr<DirectoryLock> mDirectoryLock;
 
 protected:
   Nullable<PersistenceType> mPersistenceType;
   OriginScope mOriginScope;
+  Nullable<Client::Type> mClientType;
   mozilla::Atomic<bool> mCanceled;
   const bool mExclusive;
 
 public:
   void
   RunImmediately()
   {
     MOZ_ASSERT(GetState() == State_Initial);
@@ -5501,26 +5502,32 @@ QuotaManager::EnsureOriginDirectory(nsIF
     return rv;
   }
 
   return NS_OK;
 }
 
 void
 QuotaManager::OriginClearCompleted(PersistenceType aPersistenceType,
-                                   const nsACString& aOrigin)
+                                   const nsACString& aOrigin,
+                                   const Nullable<Client::Type>& aClientType)
 {
   AssertIsOnIOThread();
 
-  if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
-    mInitializedOrigins.RemoveElement(aOrigin);
-  }
-
-  for (uint32_t index = 0; index < uint32_t(Client::TypeMax()); index++) {
-    mClients[index]->OnOriginClearCompleted(aPersistenceType, aOrigin);
+  if (aClientType.IsNull()) {
+    if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
+      mInitializedOrigins.RemoveElement(aOrigin);
+    }
+
+    for (uint32_t index = 0; index < uint32_t(Client::TypeMax()); index++) {
+      mClients[index]->OnOriginClearCompleted(aPersistenceType, aOrigin);
+    }
+  } else {
+    mClients[aClientType.Value()]->OnOriginClearCompleted(aPersistenceType,
+                                                          aOrigin);
   }
 }
 
 void
 QuotaManager::ResetOrClearCompleted()
 {
   AssertIsOnIOThread();
 
@@ -5982,17 +5989,18 @@ QuotaManager::CheckTemporaryStorageLimit
 #endif
 
       doomedOrigins.AppendElement(OriginParams(persistenceType, origin));
     }
   }
 
   for (const OriginParams& doomedOrigin : doomedOrigins) {
     OriginClearCompleted(doomedOrigin.mPersistenceType,
-                         doomedOrigin.mOrigin);
+                         doomedOrigin.mOrigin,
+                         Nullable<Client::Type>());
   }
 
   if (mTemporaryStorageUsage > mTemporaryStorageLimit) {
     // If disk space is still low after origin clear, notify storage pressure.
     NotifyStoragePressure(mTemporaryStorageUsage);
   }
 }
 
@@ -6522,17 +6530,18 @@ nsresult
 FinalizeOriginEvictionOp::DoDirectoryWork(QuotaManager* aQuotaManager)
 {
   AssertIsOnIOThread();
 
   AUTO_PROFILER_LABEL("FinalizeOriginEvictionOp::DoDirectoryWork", OTHER);
 
   for (RefPtr<DirectoryLockImpl>& lock : mLocks) {
     aQuotaManager->OriginClearCompleted(lock->GetPersistenceType().Value(),
-                                        lock->GetOriginScope().GetOrigin());
+                                        lock->GetOriginScope().GetOrigin(),
+                                        Nullable<Client::Type>());
   }
 
   return NS_OK;
 }
 
 void
 FinalizeOriginEvictionOp::UnblockOpen()
 {
@@ -6556,17 +6565,17 @@ NormalOriginOperationBase::Open()
   AssertIsOnOwningThread();
   MOZ_ASSERT(GetState() == State_CreatingQuotaManager);
   MOZ_ASSERT(QuotaManager::Get());
 
   AdvanceState();
 
   QuotaManager::Get()->OpenDirectoryInternal(mPersistenceType,
                                              mOriginScope,
-                                             Nullable<Client::Type>(),
+                                             mClientType,
                                              mExclusive,
                                              this);
 }
 
 void
 NormalOriginOperationBase::UnblockOpen()
 {
   AssertIsOnOwningThread();
@@ -7708,38 +7717,100 @@ ClearRequestBase::DeleteFiles(QuotaManag
                                                          &persisted,
                                                          suffix,
                                                          group,
                                                          origin);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return;
     }
 
+    UsageInfo usageInfo;
+
+    if (!mClientType.IsNull()) {
+      Client::Type clientType = mClientType.Value();
+
+      nsAutoString clientDirectoryName;
+      rv = Client::TypeToText(clientType, clientDirectoryName);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return;
+      }
+
+      rv = file->Append(clientDirectoryName);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return;
+      }
+
+      bool exists;
+      rv = file->Exists(&exists);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return;
+      }
+
+      if (!exists) {
+        continue;
+      }
+
+      bool initialized;
+      if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
+        initialized = aQuotaManager->IsOriginInitialized(origin);
+      } else {
+        initialized = aQuotaManager->IsTemporaryStorageInitialized();
+      }
+
+      Client* client = aQuotaManager->GetClient(clientType);
+      MOZ_ASSERT(client);
+
+      Atomic<bool> dummy(false);
+      if (initialized) {
+        rv = client->GetUsageForOrigin(aPersistenceType,
+                                       group,
+                                       origin,
+                                       dummy,
+                                       &usageInfo);
+      } else {
+        rv = client->InitOrigin(aPersistenceType,
+                                group,
+                                origin,
+                                dummy,
+                                &usageInfo);
+
+      }
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return;
+      }
+    }
+
     for (uint32_t index = 0; index < 10; index++) {
       // We can't guarantee that this will always succeed on Windows...
       if (NS_SUCCEEDED((rv = file->Remove(true)))) {
         break;
       }
 
       NS_WARNING("Failed to remove directory, retrying after a short delay.");
 
       PR_Sleep(PR_MillisecondsToInterval(200));
     }
 
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to remove directory, giving up!");
     }
 
     if (aPersistenceType != PERSISTENCE_TYPE_PERSISTENT) {
-      aQuotaManager->RemoveQuotaForOrigin(aPersistenceType, group, origin);
-    }
-
-    aQuotaManager->OriginClearCompleted(aPersistenceType, origin);
-  }
-
+      if (mClientType.IsNull()) {
+        aQuotaManager->RemoveQuotaForOrigin(aPersistenceType, group, origin);
+      } else {
+        aQuotaManager->DecreaseUsageForOrigin(aPersistenceType,
+                                              group,
+                                              origin,
+                                              usageInfo.TotalUsage());
+      }
+    }
+
+    aQuotaManager->OriginClearCompleted(aPersistenceType, origin, mClientType);
+  }
 }
 
 nsresult
 ClearRequestBase::DoDirectoryWork(QuotaManager* aQuotaManager)
 {
   AssertIsOnIOThread();
 
   AUTO_PROFILER_LABEL("ClearRequestBase::DoDirectoryWork", OTHER);
@@ -7773,16 +7844,22 @@ ClearOriginOp::Init(Quota* aQuota)
   }
 
   if (mParams.persistenceTypeIsExplicit()) {
     MOZ_ASSERT(mParams.persistenceType() != PERSISTENCE_TYPE_INVALID);
 
     mPersistenceType.SetValue(mParams.persistenceType());
   }
 
+  if (mParams.clientTypeIsExplicit()) {
+    MOZ_ASSERT(mParams.clientType() != Client::TYPE_MAX);
+
+    mClientType.SetValue(mParams.clientType());
+  }
+
   mNeedsMainThreadInit = true;
 
   return true;
 }
 
 nsresult
 ClearOriginOp::DoInitOnMainThread()
 {
--- a/dom/quota/Client.h
+++ b/dom/quota/Client.h
@@ -117,16 +117,34 @@ public:
     }
     else {
       return NS_ERROR_FAILURE;
     }
 
     return NS_OK;
   }
 
+  static nsresult
+  NullableTypeFromText(const nsAString& aText, Nullable<Type>* aType)
+  {
+    if (aText.IsVoid()) {
+      *aType = Nullable<Type>();
+      return NS_OK;
+    }
+
+    Type type;
+    nsresult rv = TypeFromText(aText, type);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    *aType = Nullable<Type>(type);
+    return NS_OK;
+  }
+
   // Methods which are called on the IO thread.
   virtual nsresult
   UpgradeStorageFrom1_0To2_0(nsIFile* aDirectory)
   {
     return NS_OK;
   }
 
   virtual nsresult
--- a/dom/quota/PQuota.ipdl
+++ b/dom/quota/PQuota.ipdl
@@ -8,16 +8,19 @@ include protocol PQuotaUsageRequest;
 
 include PBackgroundSharedTypes;
 
 include "mozilla/dom/quota/SerializationHelpers.h";
 
 using mozilla::dom::quota::PersistenceType
   from "mozilla/dom/quota/PersistenceType.h";
 
+using mozilla::dom::quota::Client::Type
+  from "mozilla/dom/quota/Client.h";
+
 namespace mozilla {
 namespace dom {
 namespace quota {
 
 struct InitParams
 {
 };
 
@@ -48,16 +51,18 @@ union UsageRequestParams
   OriginUsageParams;
 };
 
 struct ClearOriginParams
 {
   PrincipalInfo principalInfo;
   PersistenceType persistenceType;
   bool persistenceTypeIsExplicit;
+  Type clientType;
+  bool clientTypeIsExplicit;
   bool clearAll;
 };
 
 struct ClearDataParams
 {
   nsString pattern;
 };
 
--- a/dom/quota/QuotaManager.h
+++ b/dom/quota/QuotaManager.h
@@ -303,17 +303,18 @@ public:
   EnsureTemporaryStorageIsInitialized();
 
   nsresult
   EnsureOriginDirectory(nsIFile* aDirectory,
                         bool* aCreated);
 
   void
   OriginClearCompleted(PersistenceType aPersistenceType,
-                       const nsACString& aOrigin);
+                       const nsACString& aOrigin,
+                       const Nullable<Client::Type>& aClientType);
 
   void
   ResetOrClearCompleted();
 
   void
   StartIdleMaintenance()
   {
     AssertIsOnOwningThread();
--- a/dom/quota/QuotaManagerService.cpp
+++ b/dom/quota/QuotaManagerService.cpp
@@ -605,16 +605,17 @@ QuotaManagerService::Clear(nsIQuotaReque
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 QuotaManagerService::ClearStoragesForPrincipal(nsIPrincipal* aPrincipal,
                                                const nsACString& aPersistenceType,
+                                               const nsAString& aClientType,
                                                bool aClearAll,
                                                nsIQuotaRequest** _retval)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aPrincipal);
 
   nsCString suffix;
   aPrincipal->OriginAttributesRef().CreateSuffix(suffix);
@@ -643,16 +644,29 @@ QuotaManagerService::ClearStoragesForPri
 
   if (persistenceType.IsNull()) {
     params.persistenceTypeIsExplicit() = false;
   } else {
     params.persistenceType() = persistenceType.Value();
     params.persistenceTypeIsExplicit() = true;
   }
 
+  Nullable<Client::Type> clientType;
+  rv = Client::NullableTypeFromText(aClientType, &clientType);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  if (clientType.IsNull()) {
+    params.clientTypeIsExplicit() = false;
+  } else {
+    params.clientType() = clientType.Value();
+    params.clientTypeIsExplicit() = true;
+  }
+
   params.clearAll() = aClearAll;
 
   nsAutoPtr<PendingRequestInfo> info(new RequestInfo(request, params));
 
   rv = InitiateRequest(info);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
--- a/dom/quota/SerializationHelpers.h
+++ b/dom/quota/SerializationHelpers.h
@@ -4,30 +4,39 @@
  * 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/. */
 
 #ifndef mozilla_dom_quota_SerializationHelpers_h
 #define mozilla_dom_quota_SerializationHelpers_h
 
 #include "ipc/IPCMessageUtils.h"
 
+#include "mozilla/dom/quota/Client.h"
 #include "mozilla/dom/quota/PersistenceType.h"
 #include "mozilla/OriginAttributes.h"
 
 namespace IPC {
 
 template <>
 struct ParamTraits<mozilla::dom::quota::PersistenceType> :
   public ContiguousEnumSerializer<
                                mozilla::dom::quota::PersistenceType,
                                mozilla::dom::quota::PERSISTENCE_TYPE_PERSISTENT,
                                mozilla::dom::quota::PERSISTENCE_TYPE_INVALID>
 { };
 
 template <>
+struct ParamTraits<mozilla::dom::quota::Client::Type> :
+  public ContiguousEnumSerializer<
+                          mozilla::dom::quota::Client::Type,
+                          mozilla::dom::quota::Client::IDB,
+                          mozilla::dom::quota::Client::TYPE_MAX>
+{ };
+
+template <>
 struct ParamTraits<mozilla::OriginAttributesPattern>
 {
   typedef mozilla::OriginAttributesPattern paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.mAppId);
     WriteParam(aMsg, aParam.mFirstPartyDomain);
--- a/dom/quota/nsIQuotaManagerService.idl
+++ b/dom/quota/nsIQuotaManagerService.idl
@@ -99,24 +99,38 @@ interface nsIQuotaManagerService : nsISu
   /**
    * Removes all storages stored for the given principal. The files may not be
    * deleted immediately depending on prohibitive concurrent operations.
    *
    * @param aPrincipal
    *        A principal for the origin whose storages are to be cleared.
    * @param aPersistenceType
    *        An optional string that tells what persistence type of storages
-   *        will be cleared.
+   *        will be cleared.  If omitted (or void), all persistence types will
+   *        be cleared for the principal.  If a single persistence type
+   *        ("persistent", "temporary", or "default") is provided, then only
+   *        that persistence directory will be considered.  Note that
+   *        "persistent" is different than being "persisted" via persist() and
+   *        is only for chrome principals.  See bug 1354500 for more info.
+   *        In general, null is the right thing to pass here.
+   * @param aClientType
+   *        An optional string that tells what client type of storages
+   *        will be cleared.  If omitted (or void), all client types will be
+   *        cleared for the principal.  If a single client type is provided
+   *        from Client.h, then only that client's storage will be cleared.
+   *        If you want to clear multiple client types (but not all), then you
+   *        must call this method multiple times.
    * @param aClearAll
    *        An optional boolean to indicate clearing all storages under the
    *        given origin.
    */
   [must_use] nsIQuotaRequest
   clearStoragesForPrincipal(in nsIPrincipal aPrincipal,
                             [optional] in ACString aPersistenceType,
+                            [optional] in AString aClientType,
                             [optional] in boolean aClearAll);
 
   /**
    * Resets quota and storage management. This can be used to force
    * reinitialization of the temp storage, for example when the pref for
    * overriding the temp storage limit has changed.
    * Be carefull, this invalidates all live storages!
    *
--- a/mobile/android/modules/Sanitizer.jsm
+++ b/mobile/android/modules/Sanitizer.jsm
@@ -200,17 +200,17 @@ Sanitizer.prototype = {
               return;
             }
 
             for (let item of request.result) {
               let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(item.origin);
               let uri = principal.URI;
               if (uri.scheme == "http" || uri.scheme == "https" || uri.scheme == "file") {
                 promises.push(new Promise(r => {
-                  let req = quotaManagerService.clearStoragesForPrincipal(principal, null, false);
+                  let req = quotaManagerService.clearStoragesForPrincipal(principal);
                   req.callback = () => { r(); };
                 }));
               }
             }
             resolve();
           });
         });
 
--- a/testing/web-platform/tests/tools/wptrunner/wptrunner/executors/executormarionette.py
+++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/executors/executormarionette.py
@@ -287,17 +287,17 @@ class MarionetteStorageProtocolPart(Stor
             let uri = Components.classes["@mozilla.org/network/io-service;1"]
                                 .getService(Ci.nsIIOService)
                                 .newURI(url);
             let ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
                                 .getService(Ci.nsIScriptSecurityManager);
             let principal = ssm.createCodebasePrincipal(uri, {});
             let qms = Components.classes["@mozilla.org/dom/quota-manager-service;1"]
                                 .getService(Components.interfaces.nsIQuotaManagerService);
-            qms.clearStoragesForPrincipal(principal, "default", true);
+            qms.clearStoragesForPrincipal(principal, "default", null, true);
             """ % url
         with self.marionette.using_context(self.marionette.CONTEXT_CHROME):
             self.marionette.execute_script(script)
 
 
 class MarionetteAssertsProtocolPart(AssertsProtocolPart):
     def setup(self):
         self.assert_count = {"chrome": 0, "content": 0}
--- a/toolkit/components/cleardata/ClearDataService.js
+++ b/toolkit/components/cleardata/ClearDataService.js
@@ -296,17 +296,17 @@ const QuotaCleaner = {
                                  aPrincipal.URI.host);
 
     // ServiceWorkers: they must be removed before cleaning QuotaManager.
     return ServiceWorkerCleanUp.removeFromPrincipal(aPrincipal)
       .then(_ => /* exceptionThrown = */ false, _ => /* exceptionThrown = */ true)
       .then(exceptionThrown => {
         // QuotaManager
         return new Promise((aResolve, aReject) => {
-          let req = Services.qms.clearStoragesForPrincipal(aPrincipal, null, false);
+          let req = Services.qms.clearStoragesForPrincipal(aPrincipal);
           req.callback = () => {
             if (exceptionThrown) {
               aReject();
             } else {
               aResolve();
             }
           };
         });
@@ -329,21 +329,21 @@ const QuotaCleaner = {
         let httpURI = Services.io.newURI("http://" + aHost);
         let httpsURI = Services.io.newURI("https://" + aHost);
         let httpPrincipal = Services.scriptSecurityManager
                                      .createCodebasePrincipal(httpURI, aOriginAttributes);
         let httpsPrincipal = Services.scriptSecurityManager
                                      .createCodebasePrincipal(httpsURI, aOriginAttributes);
         return Promise.all([
           new Promise(aResolve => {
-            let req = Services.qms.clearStoragesForPrincipal(httpPrincipal, null, true);
+            let req = Services.qms.clearStoragesForPrincipal(httpPrincipal, null, null, true);
             req.callback = () => { aResolve(); };
           }),
           new Promise(aResolve => {
-            let req = Services.qms.clearStoragesForPrincipal(httpsPrincipal, null, true);
+            let req = Services.qms.clearStoragesForPrincipal(httpsPrincipal, null, null, true);
             req.callback = () => { aResolve(); };
           }),
         ]).then(() => {
           return exceptionThrown ? Promise.reject() : Promise.resolve();
         });
       });
   },
 
@@ -390,17 +390,17 @@ const QuotaCleaner = {
 
             let promises = [];
             for (let item of aRequest.result) {
               let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(item.origin);
               if (principal.URI.scheme == "http" ||
                   principal.URI.scheme == "https" ||
                   principal.URI.scheme == "file") {
                 promises.push(new Promise(aResolve => {
-                  let req = Services.qms.clearStoragesForPrincipal(principal, null, false);
+                  let req = Services.qms.clearStoragesForPrincipal(principal);
                   req.callback = () => { aResolve(); };
                 }));
               }
             }
 
             Promise.all(promises).then(exceptionThrown ? aReject : aResolve);
           });
         });