Bug 1169249 - Unregister service worker registration when uninstalling a service-worker-enabled application. r=baku
authorFernando Jimenez <ferjmoreno@gmail.com>
Thu, 11 Jun 2015 19:42:38 +0200
changeset 248372 82951c8ba8c53a3d7b1707bdff3508594e8391a3
parent 248371 e986f0bc71c5dcc73a0a2779f77d8543a4029382
child 248373 3257bc1b54987843b019009412bf7ea6722dfecb
push id28893
push userkwierso@gmail.com
push dateFri, 12 Jun 2015 00:02:58 +0000
treeherderautoland@8cf9d3e497f9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1169249
milestone41.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 1169249 - Unregister service worker registration when uninstalling a service-worker-enabled application. r=baku
dom/workers/ServiceWorkerManager.cpp
dom/workers/ServiceWorkerManager.h
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #include "ServiceWorkerManager.h"
 
+#include "mozIApplication.h"
+#include "mozIApplicationClearPrivateDataParams.h"
 #include "nsIAppsService.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDocument.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIStreamLoader.h"
 #include "nsIHttpChannel.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsIHttpHeaderVisitor.h"
@@ -73,16 +75,17 @@
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 
 BEGIN_WORKERS_NAMESPACE
 
 #define PURGE_DOMAIN_DATA "browser:purge-domain-data"
 #define PURGE_SESSION_HISTORY "browser:purge-session-history"
+#define WEBAPPS_CLEAR_DATA "webapps-clear-data"
 
 static_assert(nsIHttpChannelInternal::CORS_MODE_SAME_ORIGIN == static_cast<uint32_t>(RequestMode::Same_origin),
               "RequestMode enumeration value should match Necko CORS mode value.");
 static_assert(nsIHttpChannelInternal::CORS_MODE_NO_CORS == static_cast<uint32_t>(RequestMode::No_cors),
               "RequestMode enumeration value should match Necko CORS mode value.");
 static_assert(nsIHttpChannelInternal::CORS_MODE_CORS == static_cast<uint32_t>(RequestMode::Cors),
               "RequestMode enumeration value should match Necko CORS mode value.");
 static_assert(nsIHttpChannelInternal::CORS_MODE_CORS_WITH_FORCED_PREFLIGHT == static_cast<uint32_t>(RequestMode::Cors_with_forced_preflight),
@@ -410,16 +413,18 @@ ServiceWorkerManager::Init()
     if (obs) {
       DebugOnly<nsresult> rv;
       rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false /* ownsWeak */);
       MOZ_ASSERT(NS_SUCCEEDED(rv));
       rv = obs->AddObserver(this, PURGE_SESSION_HISTORY, false /* ownsWeak */);
       MOZ_ASSERT(NS_SUCCEEDED(rv));
       rv = obs->AddObserver(this, PURGE_DOMAIN_DATA, false /* ownsWeak */);
       MOZ_ASSERT(NS_SUCCEEDED(rv));
+      rv = obs->AddObserver(this, WEBAPPS_CLEAR_DATA, false /* ownsWeak */);
+      MOZ_ASSERT(NS_SUCCEEDED(rv));
     }
   }
 }
 
 class ContinueLifecycleTask : public nsISupports
 {
   NS_DECL_ISUPPORTS
 
@@ -4027,37 +4032,37 @@ HasRootDomain(nsIURI* aURI, const nsACSt
     // Equals failed so this is fine.
     return false;
   }
 
   char prevChar = *(--start);
   return prevChar == '.';
 }
 
-struct UnregisterIfMatchesHostData
+struct UnregisterIfMatchesHostOrPrincipalData
 {
-  UnregisterIfMatchesHostData(
+  UnregisterIfMatchesHostOrPrincipalData(
     ServiceWorkerManager::RegistrationDataPerPrincipal* aRegistrationData,
     void* aUserData)
     : mRegistrationData(aRegistrationData)
     , mUserData(aUserData)
   {}
 
   ServiceWorkerManager::RegistrationDataPerPrincipal* mRegistrationData;
   void *mUserData;
 };
 
 // If host/aData is null, unconditionally unregisters.
 PLDHashOperator
 UnregisterIfMatchesHost(const nsACString& aScope,
                         ServiceWorkerRegistrationInfo* aReg,
                         void* aPtr)
 {
-  UnregisterIfMatchesHostData* data =
-    static_cast<UnregisterIfMatchesHostData*>(aPtr);
+  UnregisterIfMatchesHostOrPrincipalData* data =
+    static_cast<UnregisterIfMatchesHostOrPrincipalData*>(aPtr);
 
   // We avoid setting toRemove = aReg by default since there is a possibility
   // of failure when data->mUserData is passed, in which case we don't want to
   // remove the registration.
   ServiceWorkerRegistrationInfo* toRemove = nullptr;
   if (data->mUserData) {
     const nsACString& domain = *static_cast<nsACString*>(data->mUserData);
     nsCOMPtr<nsIURI> scopeURI;
@@ -4079,22 +4084,58 @@ UnregisterIfMatchesHost(const nsACString
 }
 
 // If host/aData is null, unconditionally unregisters.
 PLDHashOperator
 UnregisterIfMatchesHostPerPrincipal(const nsACString& aKey,
                                     ServiceWorkerManager::RegistrationDataPerPrincipal* aData,
                                     void* aUserData)
 {
-  UnregisterIfMatchesHostData data(aData, aUserData);
+  UnregisterIfMatchesHostOrPrincipalData data(aData, aUserData);
   aData->mInfos.EnumerateRead(UnregisterIfMatchesHost, &data);
   return PL_DHASH_NEXT;
 }
 
 PLDHashOperator
+UnregisterIfMatchesPrincipal(const nsACString& aScope,
+                             ServiceWorkerRegistrationInfo* aReg,
+                             void* aPtr)
+{
+  UnregisterIfMatchesHostOrPrincipalData* data =
+    static_cast<UnregisterIfMatchesHostOrPrincipalData*>(aPtr);
+
+  if (data->mUserData) {
+    nsIPrincipal *principal = static_cast<nsIPrincipal*>(data->mUserData);
+    MOZ_ASSERT(principal);
+    MOZ_ASSERT(aReg->mPrincipal);
+    bool equals;
+    aReg->mPrincipal->Equals(principal, &equals);
+    if (equals) {
+      nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+      swm->ForceUnregister(data->mRegistrationData, aReg);
+    }
+  }
+
+  return PL_DHASH_NEXT;
+}
+
+PLDHashOperator
+UnregisterIfMatchesPrincipal(const nsACString& aKey,
+                             ServiceWorkerManager::RegistrationDataPerPrincipal* aData,
+                             void* aUserData)
+{
+  UnregisterIfMatchesHostOrPrincipalData data(aData, aUserData);
+  // We can use EnumerateRead because ForceUnregister (and Unregister) are async.
+  // Otherwise doing some R/W operations on an hashtable during an EnumerateRead
+  // will crash.
+  aData->mInfos.EnumerateRead(UnregisterIfMatchesPrincipal, &data);
+  return PL_DHASH_NEXT;
+}
+
+PLDHashOperator
 GetAllRegistrationsEnumerator(const nsACString& aScope,
                               ServiceWorkerRegistrationInfo* aReg,
                               void* aData)
 {
   nsIMutableArray* array = static_cast<nsIMutableArray*>(aData);
   MOZ_ASSERT(aReg);
 
   if (aReg->mPendingUninstall) {
@@ -4252,16 +4293,27 @@ UpdateEachRegistration(const nsACString&
                        void* aUserArg) {
   auto This = static_cast<ServiceWorkerManager*>(aUserArg);
   MOZ_ASSERT(!aInfo->mScope.IsEmpty());
 
   This->SoftUpdate(aInfo->mPrincipal, aInfo->mScope);
   return PL_DHASH_NEXT;
 }
 
+void
+ServiceWorkerManager::RemoveAllRegistrations(nsIPrincipal* aPrincipal)
+{
+  AssertIsOnMainThread();
+
+  MOZ_ASSERT(aPrincipal);
+
+  mRegistrationInfos.EnumerateRead(UnregisterIfMatchesPrincipal,
+                                   aPrincipal);
+}
+
 static PLDHashOperator
 UpdateEachRegistrationPerPrincipal(const nsACString& aKey,
                                    ServiceWorkerManager::RegistrationDataPerPrincipal* aData,
                                    void* aUserArg) {
   aData->mInfos.EnumerateRead(UpdateEachRegistration, aUserArg);
   return PL_DHASH_NEXT;
 }
 
@@ -4293,22 +4345,53 @@ ServiceWorkerManager::Observe(nsISupport
     RemoveAll();
   } else if (strcmp(aTopic, PURGE_DOMAIN_DATA) == 0) {
     nsAutoString domain(aData);
     for (uint32_t i = 0; i < children.Length(); i++) {
       unused << children[i]->SendRemoveServiceWorkerRegistrationsForDomain(domain);
     }
 
     Remove(NS_ConvertUTF16toUTF8(domain));
+  } else if (strcmp(aTopic, WEBAPPS_CLEAR_DATA) == 0) {
+    nsCOMPtr<mozIApplicationClearPrivateDataParams> params =
+      do_QueryInterface(aSubject);
+    if (NS_WARN_IF(!params)) {
+      return NS_OK;
+    }
+
+    uint32_t appId;
+    nsresult rv = params->GetAppId(&appId);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIAppsService> appsService =
+      do_GetService(APPS_SERVICE_CONTRACTID);
+    if (NS_WARN_IF(!appsService)) {
+      return NS_OK;
+    }
+
+    nsCOMPtr<mozIApplication> app;
+    appsService->GetAppByLocalId(appId, getter_AddRefs(app));
+    if (NS_WARN_IF(!app)) {
+      return NS_OK;
+    }
+
+    nsCOMPtr<nsIPrincipal> principal;
+    app->GetPrincipal(getter_AddRefs(principal));
+    if (NS_WARN_IF(!principal)) {
+      return NS_OK;
+    }
+
+    RemoveAllRegistrations(principal);
   } else if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
       obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
       obs->RemoveObserver(this, PURGE_SESSION_HISTORY);
       obs->RemoveObserver(this, PURGE_DOMAIN_DATA);
+      obs->RemoveObserver(this, WEBAPPS_CLEAR_DATA);
     }
   } else {
     MOZ_CRASH("Received message we aren't supposed to be registered for!");
   }
 
   return NS_OK;
 }
 
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -527,16 +527,20 @@ private:
   // Does all cleanup except removing the registration from
   // mServiceWorkerRegistrationInfos. This is useful when we clear
   // registrations via remove()/removeAll() since we are iterating over the
   // hashtable and can cleanly remove within the hashtable enumeration
   // function.
   void
   RemoveRegistrationInternal(ServiceWorkerRegistrationInfo* aRegistration);
 
+  // Removes all service worker registrations for a given principal.
+  void
+  RemoveAllRegistrations(nsIPrincipal* aPrincipal);
+
   nsRefPtr<ServiceWorkerManagerChild> mActor;
 
   struct PendingOperation;
   nsTArray<PendingOperation> mPendingOperations;
 };
 
 } // namespace workers
 } // namespace dom