Bug 1111787 - Part 3: Clear plugis and storage data associated with the node IDs. r=cpearce
authorJW Wang <jwwang@mozilla.com>
Mon, 29 Dec 2014 22:22:00 -0500
changeset 238996 f3c7e9a00af88d87187eb2e8062fb3edaf684fcf
parent 238995 20298762b610fe8726459f0d17a7cefafc071c5c
child 238997 3595d450fac19f74f8ba272f13ef66769611ed38
push id7472
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 20:36:27 +0000
treeherdermozilla-aurora@300ca104f8fb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce
bugs1111787
milestone37.0a1
Bug 1111787 - Part 3: Clear plugis and storage data associated with the node IDs. r=cpearce
dom/media/gmp/GMPService.cpp
--- a/dom/media/gmp/GMPService.cpp
+++ b/dom/media/gmp/GMPService.cpp
@@ -1137,34 +1137,86 @@ GeckoMediaPluginService::GetNodeId(const
 
   aOutId = salt;
   mPersistentStorageAllowed.Put(salt, true);
 
   return NS_OK;
 }
 
 static bool
-MatchOrigin(nsIFile* aPath, const nsACString& aMatch)
+MatchOrigin(nsIFile* aPath, const nsACString& aOrigin)
 {
   // http://en.wikipedia.org/wiki/Domain_Name_System#Domain_name_syntax
   static const uint32_t MaxDomainLength = 253;
 
   nsresult rv;
   nsCString str;
   rv = ReadFromFile(aPath, NS_LITERAL_CSTRING("origin"), str, MaxDomainLength);
-  if (NS_SUCCEEDED(rv) && aMatch.Equals(str)) {
+  if (NS_SUCCEEDED(rv) && aOrigin.Equals(str)) {
     return true;
   }
   rv = ReadFromFile(aPath, NS_LITERAL_CSTRING("topLevelOrigin"), str, MaxDomainLength);
-  if (NS_SUCCEEDED(rv) && aMatch.Equals(str)) {
+  if (NS_SUCCEEDED(rv) && aOrigin.Equals(str)) {
     return true;
   }
   return false;
 }
 
+template<typename T> static void
+KillPlugins(const nsTArray<nsRefPtr<GMPParent>>& aPlugins,
+            Mutex& aMutex, T&& aFilter)
+{
+  // Shutdown the plugins when |aFilter| evaluates to true.
+  // After we clear storage data, node IDs will become invalid and shouldn't be
+  // used anymore. We need to kill plugins with such nodeIDs.
+  // Note: we can't shut them down while holding the lock,
+  // as the lock is not re-entrant and shutdown requires taking the lock.
+  // The plugin list is only edited on the GMP thread, so this should be OK.
+  nsTArray<nsRefPtr<GMPParent>> pluginsToKill;
+  {
+    MutexAutoLock lock(aMutex);
+    for (size_t i = 0; i < aPlugins.Length(); i++) {
+      nsRefPtr<GMPParent> parent(aPlugins[i]);
+      if (aFilter(parent)) {
+        pluginsToKill.AppendElement(parent);
+      }
+    }
+  }
+
+  for (size_t i = 0; i < pluginsToKill.Length(); i++) {
+    pluginsToKill[i]->CloseActive(false);
+    // Abort async shutdown because we're going to wipe the plugin's storage,
+    // so we don't want it writing more data in its async shutdown path.
+    pluginsToKill[i]->AbortAsyncShutdown();
+  }
+}
+
+static nsresult
+DeleteDir(nsIFile* aPath)
+{
+  bool exists = false;
+  nsresult rv = aPath->Exists(&exists);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  if (exists) {
+    return aPath->Remove(true);
+  }
+  return NS_OK;
+}
+
+struct NodeFilter {
+  explicit NodeFilter(const nsTArray<nsCString>& nodeIDs) : mNodeIDs(nodeIDs) {}
+  bool operator()(GMPParent* aParent) {
+    return mNodeIDs.Contains(aParent->GetNodeId());
+  }
+private:
+  const nsTArray<nsCString>& mNodeIDs;
+};
+
 void
 GeckoMediaPluginService::ForgetThisSiteOnGMPThread(const nsACString& aOrigin)
 {
 #define ERR_RET(x) NS_ENSURE_SUCCESS_VOID(x)
 #define ERR_CONT(x) if (NS_FAILED(x)) { continue; }
 
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   LOGD(("%s::%s: origin=%s", __CLASS__, __FUNCTION__, aOrigin.Data()));
@@ -1199,30 +1251,43 @@ GeckoMediaPluginService::ForgetThisSiteO
       continue;
     }
 
     // Check if origin or topLevelOrigin match the origin being forgotten.
     if (!MatchOrigin(dirEntry, aOrigin)) {
       continue;
     }
 
-    // Keep node IDs to clear data/plugins associated with them later.
     nsAutoCString salt;
     if (NS_SUCCEEDED(ReadSalt(dirEntry, salt))) {
+      // Keep node IDs to clear data/plugins associated with them later.
       nodeIDsToClear.AppendElement(salt);
+      // Also remove node IDs from the table.
+      mPersistentStorageAllowed.Remove(salt);
     }
     // Now we can remove the directory for the origin pair.
     if (NS_FAILED(dirEntry->Remove(true))) {
       NS_WARNING("Failed to delete the directory for the origin pair");
     }
   }
 
-  // TODO: Clear plugins/data associated with the node IDs in nodeIDsToClear
-  // in next patch.
-  nodeIDsToClear.Clear();
+  // Kill plugins that have node IDs to be cleared.
+  KillPlugins(mPlugins, mMutex, NodeFilter(nodeIDsToClear));
+
+  // Clear all matching $profileDir/gmp/storage/$nodeId/
+  ERR_RET(GetStorageDir(getter_AddRefs(path)));
+  ERR_RET(path->AppendNative(NS_LITERAL_CSTRING("storage")));
+  for (size_t i = 0; i < nodeIDsToClear.Length(); i++) {
+    nsCOMPtr<nsIFile> dirEntry;
+    ERR_CONT(path->Clone(getter_AddRefs(dirEntry)));
+    ERR_CONT(dirEntry->AppendNative(nodeIDsToClear[i]));
+    if (NS_FAILED(DeleteDir(dirEntry))) {
+      NS_WARNING("Failed to delete GMP storage directory for the node");
+    }
+  }
 
 #undef ERR_RET
 #undef ERR_CONT
 }
 
 NS_IMETHODIMP
 GeckoMediaPluginService::ForgetThisSite(const nsAString& aOrigin)
 {
@@ -1240,59 +1305,40 @@ public:
     MOZ_ASSERT(obsService);
     if (obsService) {
       obsService->NotifyObservers(nullptr, "gmp-clear-storage-complete", nullptr);
     }
     return NS_OK;
   }
 };
 
+static bool IsNodeIdValid(GMPParent* aParent) {
+  return !aParent->GetNodeId().IsEmpty();
+}
+
 void
 GeckoMediaPluginService::ClearStorage()
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   LOGD(("%s::%s", __CLASS__, __FUNCTION__));
 
 #ifdef MOZ_WIDGET_GONK
   NS_WARNING("GeckoMediaPluginService::ClearStorage not implemented on B2G");
   return;
 #endif
 
-  // Shutdown all plugins that have valid node IDs as those IDs will become
-  // invalid and shouldn't be used anymore after we clear storage data.
-  // Note: we can't shut them down while holding the lock,
-  // as the lock is not re-entrant and shutdown requires taking the lock.
-  // The plugin list is only edited on the GMP thread, so this should be OK.
-  nsTArray<nsRefPtr<GMPParent>> pluginsToKill;
-  {
-    MutexAutoLock lock(mMutex);
-    for (size_t i = 0; i < mPlugins.Length(); i++) {
-      nsRefPtr<GMPParent> parent(mPlugins[i]);
-      if (!parent->GetNodeId().IsEmpty()) {
-        pluginsToKill.AppendElement(parent);
-      }
-    }
-  }
-
-  for (size_t i = 0; i < pluginsToKill.Length(); i++) {
-    pluginsToKill[i]->CloseActive(false);
-    // Abort async shutdown because we're going to wipe the plugin's storage,
-    // so we don't want it writing more data in its async shutdown path.
-    pluginsToKill[i]->AbortAsyncShutdown();
-  }
+  // Kill plugins with valid nodeIDs.
+  KillPlugins(mPlugins, mMutex, &IsNodeIdValid);
 
   nsCOMPtr<nsIFile> path; // $profileDir/gmp/
   nsresult rv = GetStorageDir(getter_AddRefs(path));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
-  bool exists = false;
-  if (NS_SUCCEEDED(path->Exists(&exists)) &&
-      exists &&
-      NS_FAILED(path->Remove(true))) {
+  if (NS_FAILED(DeleteDir(path))) {
     NS_WARNING("Failed to delete GMP storage directory");
   }
   NS_DispatchToMainThread(new StorageClearedTask(), NS_DISPATCH_NORMAL);
 }
 
 } // namespace gmp
 } // namespace mozilla