Bug 1228215 - Store each GMP's storage and nodeId salt in separate directories. r=jwwang
authorChris Pearce <cpearce@mozilla.com>
Fri, 27 Nov 2015 10:53:32 +1300
changeset 308543 37e82b51a404cac7efcb72bb36fe4a135d0b1f42
parent 308542 584678105c1476f5276f8723681604ecea06d583
child 308544 1bf6fd3421e6cb23b386e6e01e718b05453b373a
push id5513
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 13:55:34 +0000
treeherdermozilla-beta@5ee97dd05b5c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwwang
bugs1228215
milestone45.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 1228215 - Store each GMP's storage and nodeId salt in separate directories. r=jwwang
dom/media/gmp/GMPServiceParent.cpp
dom/media/gmp/GMPServiceParent.h
dom/media/gmp/GMPStorageParent.cpp
dom/media/gtest/TestGMPCrossOrigin.cpp
--- a/dom/media/gmp/GMPServiceParent.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -286,18 +286,18 @@ GeckoMediaPluginServiceParent::InitStora
     return rv;
   }
 
   rv = mStorageBaseDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
   if (NS_WARN_IF(NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)) {
     return rv;
   }
 
-  // Prior to 42, GMP storage was stored in $profile/gmp/. After 42, it's
-  // stored in $profile/gmp/$platform/. So we must migrate any old records
+  // Prior to 42, GMP storage was stored in $profileDir/gmp/. After 42, it's
+  // stored in $profileDir/gmp/$platform/. So we must migrate any old records
   // from the old location to the new location, for forwards compatibility.
   MigratePreGecko42StorageDir(gmpDirWithoutPlatform, mStorageBaseDir);
 
   return GeckoMediaPluginService::Init();
 }
 
 NS_IMETHODIMP
 GeckoMediaPluginServiceParent::Observe(nsISupports* aSubject,
@@ -1240,58 +1240,70 @@ GeckoMediaPluginServiceParent::GetNodeId
     mPersistentStorageAllowed.Put(salt, false);
     return NS_OK;
   }
 
   const uint32_t hash = AddToHash(HashString(aOrigin),
                                   HashString(aTopLevelOrigin));
 
   if (aInPrivateBrowsing) {
-    // For PB mode, we store the node id, indexed by the origin pair,
-    // so that if the same origin pair is opened in this session, it gets
-    // the same node id.
+    // For PB mode, we store the node id, indexed by the origin pair and GMP name,
+    // so that if the same origin pair is opened for the same GMP in this session,
+    // it gets the same node id.
+    const uint32_t pbHash = AddToHash(HashString(aGMPName), hash);
     nsCString* salt = nullptr;
-    if (!(salt = mTempNodeIds.Get(hash))) {
+    if (!(salt = mTempNodeIds.Get(pbHash))) {
       // No salt stored, generate and temporarily store some for this id.
       nsAutoCString newSalt;
       rv = GenerateRandomPathName(newSalt, NodeIdSaltLength);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       salt = new nsCString(newSalt);
-      mTempNodeIds.Put(hash, salt);
+      mTempNodeIds.Put(pbHash, salt);
       mPersistentStorageAllowed.Put(*salt, false);
     }
     aOutId = *salt;
     return NS_OK;
   }
 
   // Otherwise, try to see if we've previously generated and stored salt
   // for this origin pair.
-  nsCOMPtr<nsIFile> path; // $profileDir/gmp/
+  nsCOMPtr<nsIFile> path; // $profileDir/gmp/$platform/
   rv = GetStorageDir(getter_AddRefs(path));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  rv = path->Append(aGMPName);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // $profileDir/gmp/$platform/$gmpName/
+  rv = path->Create(nsIFile::DIRECTORY_TYPE, 0700);
+  if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
   rv = path->AppendNative(NS_LITERAL_CSTRING("id"));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  // $profileDir/gmp/id/
+  // $profileDir/gmp/$platform/$gmpName/id/
   rv = path->Create(nsIFile::DIRECTORY_TYPE, 0700);
   if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsAutoCString hashStr;
   hashStr.AppendInt((int64_t)hash);
 
-  // $profileDir/gmp/id/$hash
+  // $profileDir/gmp/$platform/$gmpName/id/$hash
   rv = path->AppendNative(hashStr);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   rv = path->Create(nsIFile::DIRECTORY_TYPE, 0700);
   if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
@@ -1318,31 +1330,31 @@ GeckoMediaPluginServiceParent::GetNodeId
     // No stored salt for this origin. Generate salt, and store it and
     // the origin on disk.
     nsresult rv = GenerateRandomPathName(salt, NodeIdSaltLength);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     MOZ_ASSERT(salt.Length() == NodeIdSaltLength);
 
-    // $profileDir/gmp/id/$hash/salt
+    // $profileDir/gmp/$platform/$gmpName/id/$hash/salt
     rv = WriteToFile(path, NS_LITERAL_CSTRING("salt"), salt);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    // $profileDir/gmp/id/$hash/origin
+    // $profileDir/gmp/$platform/$gmpName/id/$hash/origin
     rv = WriteToFile(path,
                      NS_LITERAL_CSTRING("origin"),
                      NS_ConvertUTF16toUTF8(aOrigin));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    // $profileDir/gmp/id/$hash/topLevelOrigin
+    // $profileDir/gmp/$platform/$gmpName/id/$hash/topLevelOrigin
     rv = WriteToFile(path,
                      NS_LITERAL_CSTRING("topLevelOrigin"),
                      NS_ConvertUTF16toUTF8(aTopLevelOrigin));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
   } else {
@@ -1478,73 +1490,80 @@ struct NodeFilter {
   }
 private:
   const nsTArray<nsCString>& mNodeIDs;
 };
 
 void
 GeckoMediaPluginServiceParent::ClearNodeIdAndPlugin(DirectoryFilter& aFilter)
 {
-  nsresult rv;
+  // $profileDir/gmp/$platform/
   nsCOMPtr<nsIFile> path;
-
-  // $profileDir/gmp/
-  rv = GetStorageDir(getter_AddRefs(path));
+  nsresult rv = GetStorageDir(getter_AddRefs(path));
   if (NS_FAILED(rv)) {
     return;
   }
 
-  // $profileDir/gmp/id/
-  rv = path->AppendNative(NS_LITERAL_CSTRING("id"));
-  if (NS_FAILED(rv)) {
+  // Iterate all sub-folders of $profileDir/gmp/$platform/, i.e. the dirs in which
+  // specific GMPs store their data.
+  DirectoryEnumerator iter(path, DirectoryEnumerator::DirsOnly);
+  for (nsCOMPtr<nsIFile> pluginDir; (pluginDir = iter.Next()) != nullptr;) {
+    ClearNodeIdAndPlugin(pluginDir, aFilter);
+  }
+}
+
+void
+GeckoMediaPluginServiceParent::ClearNodeIdAndPlugin(nsIFile* aPluginStorageDir,
+                                                    DirectoryFilter& aFilter)
+{
+  // $profileDir/gmp/$platform/$gmpName/id/
+  nsCOMPtr<nsIFile> path = CloneAndAppend(aPluginStorageDir, NS_LITERAL_STRING("id"));
+  if (!path) {
     return;
   }
 
-  // Iterate all sub-folders of $profileDir/gmp/id/
+  // Iterate all sub-folders of $profileDir/gmp/$platform/$gmpName/id/
   nsTArray<nsCString> nodeIDsToClear;
   DirectoryEnumerator iter(path, DirectoryEnumerator::DirsOnly);
   for (nsCOMPtr<nsIFile> dirEntry; (dirEntry = iter.Next()) != nullptr;) {
+    // dirEntry is the hash of origins, i.e.:
+    // $profileDir/gmp/$platform/$gmpName/id/$originHash/
     if (!aFilter(dirEntry)) {
       continue;
     }
     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");
     }
   }
 
-  // Kill plugins that have node IDs to be cleared.
+  // Kill plugin instances that have node IDs being cleared.
   KillPlugins(mPlugins, mMutex, NodeFilter(nodeIDsToClear));
 
-  // Clear all matching $profileDir/gmp/storage/$nodeId/
-  rv = GetStorageDir(getter_AddRefs(path));
-  if (NS_FAILED(rv)) {
+  // Clear all storage in $profileDir/gmp/$platform/$gmpName/storage/$nodeId/
+  path = CloneAndAppend(aPluginStorageDir, NS_LITERAL_STRING("storage"));
+  if (!path) {
     return;
   }
 
-  rv = path->AppendNative(NS_LITERAL_CSTRING("storage"));
-  if (NS_FAILED(rv)) {
-    return;
-  }
-
-  for (size_t i = 0; i < nodeIDsToClear.Length(); i++) {
+  for (const nsCString& nodeId : nodeIDsToClear) {
     nsCOMPtr<nsIFile> dirEntry;
-    rv = path->Clone(getter_AddRefs(dirEntry));
+    nsresult rv = path->Clone(getter_AddRefs(dirEntry));
     if (NS_FAILED(rv)) {
       continue;
     }
 
-    rv = dirEntry->AppendNative(nodeIDsToClear[i]);
+    rv = dirEntry->AppendNative(nodeId);
     if (NS_FAILED(rv)) {
       continue;
     }
 
     if (NS_FAILED(DeleteDir(dirEntry))) {
       NS_WARNING("Failed to delete GMP storage directory for the node");
     }
   }
@@ -1569,26 +1588,19 @@ GeckoMediaPluginServiceParent::ForgetThi
 }
 
 void
 GeckoMediaPluginServiceParent::ClearRecentHistoryOnGMPThread(PRTime aSince)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   LOGD(("%s::%s: since=%lld", __CLASS__, __FUNCTION__, (int64_t)aSince));
 
-  nsCOMPtr<nsIFile> storagePath;
-  nsCOMPtr<nsIFile> temp;
-  if (NS_SUCCEEDED(GetStorageDir(getter_AddRefs(temp))) &&
-      NS_SUCCEEDED(temp->AppendNative(NS_LITERAL_CSTRING("storage")))) {
-    storagePath = temp.forget();
-  }
-
   struct MTimeFilter : public DirectoryFilter {
-    explicit MTimeFilter(PRTime aSince, already_AddRefed<nsIFile> aPath)
-      : mSince(aSince), mStoragePath(aPath) {}
+    explicit MTimeFilter(PRTime aSince)
+      : mSince(aSince) {}
 
     // Return true if any files under aPath is modified after |mSince|.
     bool IsModifiedAfter(nsIFile* aPath) {
       PRTime lastModified;
       nsresult rv = aPath->GetLastModifiedTime(&lastModified);
       if (NS_SUCCEEDED(rv) && lastModified >= mSince) {
         return true;
       }
@@ -1596,46 +1608,48 @@ GeckoMediaPluginServiceParent::ClearRece
       for (nsCOMPtr<nsIFile> dirEntry; (dirEntry = iter.Next()) != nullptr;) {
         if (IsModifiedAfter(dirEntry)) {
           return true;
         }
       }
       return false;
     }
 
-    // |aPath| is $profileDir/gmp/id/$hash
+    // |aPath| is $profileDir/gmp/$platform/$gmpName/id/$originHash/
     virtual bool operator()(nsIFile* aPath) {
       if (IsModifiedAfter(aPath)) {
         return true;
       }
 
       nsAutoCString salt;
-      nsresult rv = ReadSalt(aPath, salt);
-      if (NS_FAILED(rv)) {
+      if (NS_FAILED(ReadSalt(aPath, salt))) {
         return false;
       }
 
-      // $profileDir/gmp/storage/
-      if (!mStoragePath) {
+      // $profileDir/gmp/$platform/$gmpName/id/
+      nsCOMPtr<nsIFile> idDir;
+      if (NS_FAILED(aPath->GetParent(getter_AddRefs(idDir)))) {
         return false;
       }
-      // $profileDir/gmp/storage/$nodeId/
-      nsCOMPtr<nsIFile> path;
-      rv = mStoragePath->Clone(getter_AddRefs(path));
-      if (NS_FAILED(rv)) {
+      // $profileDir/gmp/$platform/$gmpName/
+      nsCOMPtr<nsIFile> temp;
+      if (NS_FAILED(idDir->GetParent(getter_AddRefs(temp)))) {
         return false;
       }
 
-      rv = path->AppendNative(salt);
-      return NS_SUCCEEDED(rv) && IsModifiedAfter(path);
+      // $profileDir/gmp/$platform/$gmpName/storage/
+      if (NS_FAILED(temp->Append(NS_LITERAL_STRING("storage")))) {
+        return false;
+      }
+      // $profileDir/gmp/$platform/$gmpName/storage/$originSalt
+      return NS_SUCCEEDED(temp->AppendNative(salt)) && IsModifiedAfter(temp);
     }
   private:
     const PRTime mSince;
-    const nsCOMPtr<nsIFile> mStoragePath;
-  } filter(aSince, storagePath.forget());
+  } filter(aSince);
 
   ClearNodeIdAndPlugin(filter);
 
   NS_DispatchToMainThread(new NotifyObserversTask("gmp-clear-storage-complete"), NS_DISPATCH_NORMAL);
 }
 
 NS_IMETHODIMP
 GeckoMediaPluginServiceParent::ForgetThisSite(const nsAString& aSite)
@@ -1654,17 +1668,17 @@ void
 GeckoMediaPluginServiceParent::ClearStorage()
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   LOGD(("%s::%s", __CLASS__, __FUNCTION__));
 
   // Kill plugins with valid nodeIDs.
   KillPlugins(mPlugins, mMutex, &IsNodeIdValid);
 
-  nsCOMPtr<nsIFile> path; // $profileDir/gmp/
+  nsCOMPtr<nsIFile> path; // $profileDir/gmp/$platform/
   nsresult rv = GetStorageDir(getter_AddRefs(path));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
   if (NS_FAILED(DeleteDir(path))) {
     NS_WARNING("Failed to delete GMP storage directory");
   }
--- a/dom/media/gmp/GMPServiceParent.h
+++ b/dom/media/gmp/GMPServiceParent.h
@@ -92,17 +92,18 @@ private:
 
   nsresult SetAsyncShutdownTimeout();
 
   struct DirectoryFilter {
     virtual bool operator()(nsIFile* aPath) = 0;
     ~DirectoryFilter() {}
   };
   void ClearNodeIdAndPlugin(DirectoryFilter& aFilter);
-
+  void ClearNodeIdAndPlugin(nsIFile* aPluginStorageDir,
+                            DirectoryFilter& aFilter);
   void ForgetThisSiteOnGMPThread(const nsACString& aOrigin);
   void ClearRecentHistoryOnGMPThread(PRTime aSince);
 
 protected:
   friend class GMPParent;
   void ReAddOnGMPThread(const RefPtr<GMPParent>& aOld);
   void PluginTerminated(const RefPtr<GMPParent>& aOld);
   virtual void InitializePlugins() override;
--- a/dom/media/gmp/GMPStorageParent.cpp
+++ b/dom/media/gmp/GMPStorageParent.cpp
@@ -27,20 +27,22 @@ namespace mozilla {
 
 extern LogModule* GetGMPLog();
 
 #define LOGD(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Debug, msg)
 #define LOG(level, msg) MOZ_LOG(GetGMPLog(), (level), msg)
 
 namespace gmp {
 
-// We store the records in files in the profile dir.
-// $profileDir/gmp/storage/$nodeId/
+// We store the records for a given GMP as files in the profile dir.
+// $profileDir/gmp/$platform/$gmpName/storage/$nodeId/
 static nsresult
-GetGMPStorageDir(nsIFile** aTempDir, const nsCString& aNodeId)
+GetGMPStorageDir(nsIFile** aTempDir,
+                 const nsString& aGMPName,
+                 const nsCString& aNodeId)
 {
   if (NS_WARN_IF(!aTempDir)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   nsCOMPtr<mozIGeckoMediaPluginChromeService> mps =
     do_GetService("@mozilla.org/gecko-media-plugin-service;1");
   if (NS_WARN_IF(!mps)) {
@@ -48,16 +50,26 @@ GetGMPStorageDir(nsIFile** aTempDir, con
   }
 
   nsCOMPtr<nsIFile> tmpFile;
   nsresult rv = mps->GetStorageDir(getter_AddRefs(tmpFile));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  rv = tmpFile->Append(aGMPName);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = tmpFile->Create(nsIFile::DIRECTORY_TYPE, 0700);
+  if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
   rv = tmpFile->AppendNative(NS_LITERAL_CSTRING("storage"));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   rv = tmpFile->Create(nsIFile::DIRECTORY_TYPE, 0700);
   if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
@@ -82,18 +94,20 @@ GetGMPStorageDir(nsIFile** aTempDir, con
 // the profile directory. The record name is a hash of the filename,
 // and we resolve hash collisions by just adding 1 to the hash code.
 // The format of records on disk is:
 //   4 byte, uint32_t $recordNameLength, in little-endian byte order,
 //   record name (i.e. $recordNameLength bytes, no null terminator)
 //   record bytes (entire remainder of file)
 class GMPDiskStorage : public GMPStorage {
 public:
-  explicit GMPDiskStorage(const nsCString& aNodeId)
+  explicit GMPDiskStorage(const nsCString& aNodeId,
+                          const nsString& aGMPName)
     : mNodeId(aNodeId)
+    , mGMPName(aGMPName)
   {
   }
 
   ~GMPDiskStorage() {
     // Close all open file handles.
     for (auto iter = mRecords.ConstIter(); !iter.Done(); iter.Next()) {
       Record* record = iter.UserData();
       if (record->mFileDesc) {
@@ -101,17 +115,17 @@ public:
         record->mFileDesc = nullptr;
       }
     }
   }
 
   nsresult Init() {
     // Build our index of records on disk.
     nsCOMPtr<nsIFile> storageDir;
-    nsresult rv = GetGMPStorageDir(getter_AddRefs(storageDir), mNodeId);
+    nsresult rv = GetGMPStorageDir(getter_AddRefs(storageDir), mGMPName, mNodeId);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return NS_ERROR_FAILURE;
     }
 
     DirectoryEnumerator iter(storageDir, DirectoryEnumerator::FilesAndDirs);
     for (nsCOMPtr<nsIFile> dirEntry; (dirEntry = iter.Next()) != nullptr;) {
       PRFileDesc* fd = nullptr;
       if (NS_FAILED(dirEntry->OpenNSPRFileDesc(PR_RDONLY, 0, &fd))) {
@@ -313,17 +327,17 @@ private:
 
   // We store records in a file which is a hash of the record name.
   // If there is a hash collision, we just keep adding 1 to the hash
   // code, until we find a free slot.
   nsresult GetUnusedFilename(const nsACString& aRecordName,
                              nsString& aOutFilename)
   {
     nsCOMPtr<nsIFile> storageDir;
-    nsresult rv = GetGMPStorageDir(getter_AddRefs(storageDir), mNodeId);
+    nsresult rv = GetGMPStorageDir(getter_AddRefs(storageDir), mGMPName, mNodeId);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     uint64_t recordNameHash = HashString(PromiseFlatCString(aRecordName).get());
     for (int i = 0; i < 1000000; i++) {
       nsCOMPtr<nsIFile> f;
       rv = storageDir->Clone(getter_AddRefs(f));
@@ -358,17 +372,17 @@ private:
 
   nsresult OpenStorageFile(const nsAString& aFileLeafName,
                            const OpenFileMode aMode,
                            PRFileDesc** aOutFD)
   {
     MOZ_ASSERT(aOutFD);
 
     nsCOMPtr<nsIFile> f;
-    nsresult rv = GetGMPStorageDir(getter_AddRefs(f), mNodeId);
+    nsresult rv = GetGMPStorageDir(getter_AddRefs(f), mGMPName, mNodeId);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     f->Append(aFileLeafName);
 
     auto mode = PR_RDWR | PR_CREATE_FILE;
     if (aMode == Truncate) {
       mode |= PR_TRUNCATE;
@@ -435,17 +449,17 @@ private:
     }
 
     return NS_OK;
   }
 
   nsresult RemoveStorageFile(const nsString& aFilename)
   {
     nsCOMPtr<nsIFile> f;
-    nsresult rv = GetGMPStorageDir(getter_AddRefs(f), mNodeId);
+    nsresult rv = GetGMPStorageDir(getter_AddRefs(f), mGMPName, mNodeId);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     rv = f->Append(aFilename);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return f->Remove(/* bool recursive= */ false);
@@ -464,16 +478,17 @@ private:
     nsString mFilename;
     nsCString mRecordName;
     PRFileDesc* mFileDesc;
   };
 
   // Hash record name to record data.
   nsClassHashtable<nsCStringHashKey, Record> mRecords;
   const nsAutoCString mNodeId;
+  const nsString mGMPName;
 };
 
 class GMPMemoryStorage : public GMPStorage {
 public:
   GMPErr Open(const nsCString& aRecordName) override
   {
     MOZ_ASSERT(!IsOpen(aRecordName));
 
@@ -571,17 +586,18 @@ GMPStorageParent::Init()
     return NS_ERROR_FAILURE;
   }
 
   bool persistent = false;
   if (NS_WARN_IF(NS_FAILED(mps->IsPersistentStorageAllowed(mNodeId, &persistent)))) {
     return NS_ERROR_FAILURE;
   }
   if (persistent) {
-    UniquePtr<GMPDiskStorage> storage = MakeUnique<GMPDiskStorage>(mNodeId);
+    UniquePtr<GMPDiskStorage> storage =
+      MakeUnique<GMPDiskStorage>(mNodeId, mPlugin->GetPluginBaseName());
     if (NS_FAILED(storage->Init())) {
       NS_WARNING("Failed to initialize on disk GMP storage");
       return NS_ERROR_FAILURE;
     }
     mStorage = Move(storage);
   } else {
     mStorage = MakeUnique<GMPMemoryStorage>();
   }
--- a/dom/media/gtest/TestGMPCrossOrigin.cpp
+++ b/dom/media/gtest/TestGMPCrossOrigin.cpp
@@ -268,34 +268,41 @@ EnumerateDir(nsIFile* aPath, T&& aDirIte
     }
 
     aDirIter(entry);
   }
   return NS_OK;
 }
 
 /**
- * Enumerate files under $profileDir/gmp/$aDir/ (non-recursive).
+ * Enumerate files under $profileDir/gmp/$platform/gmp-fake/$aDir/ (non-recursive).
  */
 template<typename T>
 static nsresult
 EnumerateGMPStorageDir(const nsACString& aDir, T&& aDirIter)
 {
   RefPtr<GeckoMediaPluginServiceParent> service =
     GeckoMediaPluginServiceParent::GetSingleton();
   MOZ_ASSERT(service);
 
-  // $profileDir/gmp/
+  // $profileDir/gmp/$platform/
   nsCOMPtr<nsIFile> path;
   nsresult rv = service->GetStorageDir(getter_AddRefs(path));
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  // $profileDir/gmp/$aDir/
+
+  // $profileDir/gmp/$platform/gmp-fake/
+  rv = path->Append(NS_LITERAL_STRING("gmp-fake"));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  // $profileDir/gmp/$platform/gmp-fake/$aDir/
   rv = path->AppendNative(aDir);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   return EnumerateDir(path, aDirIter);
 }
 
@@ -816,20 +823,20 @@ class GMPStorageTest : public GMPDecrypt
 
     rv = EnumerateGMPStorageDir(
         NS_LITERAL_CSTRING("storage"), StorageVerifier(aSiteInfo));
     EXPECT_TRUE(NS_SUCCEEDED(rv));
   }
 
   /**
    * 1. Generate some storage data.
-   * 2. Find the max mtime |t| in $profileDir/gmp/id/.
+   * 2. Find the max mtime |t| in $profileDir/gmp/$platform/gmp-fake/id/.
    * 3. Pass |t| to clear recent history.
-   * 4. Check if all directories in $profileDir/gmp/id/ and
-   *    $profileDir/gmp/storage are removed.
+   * 4. Check if all directories in $profileDir/gmp/$platform/gmp-fake/id/ and
+   *    $profileDir/gmp/$platform/gmp-fake/storage are removed.
    */
   void TestClearRecentHistory1() {
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     // Generate storage data for some site.
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
         this, &GMPStorageTest::TestClearRecentHistory1_Clear);
@@ -838,20 +845,20 @@ class GMPStorageTest : public GMPDecrypt
     CreateDecryptor(NS_LITERAL_STRING("http://example1.com"),
                     NS_LITERAL_STRING("http://example2.com"),
                     false,
                     NS_LITERAL_CSTRING("test-storage"));
 }
 
   /**
    * 1. Generate some storage data.
-   * 2. Find the max mtime |t| in $profileDir/gmp/storage/.
+   * 2. Find the max mtime |t| in $profileDir/gmp/$platform/gmp-fake/storage/.
    * 3. Pass |t| to clear recent history.
-   * 4. Check if all directories in $profileDir/gmp/id/ and
-   *    $profileDir/gmp/storage are removed.
+   * 4. Check if all directories in $profileDir/gmp/$platform/gmp-fake/id/ and
+   *    $profileDir/gmp/$platform/gmp-fake/storage are removed.
    */
   void TestClearRecentHistory2() {
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     // Generate storage data for some site.
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
         this, &GMPStorageTest::TestClearRecentHistory2_Clear);
@@ -860,20 +867,20 @@ class GMPStorageTest : public GMPDecrypt
     CreateDecryptor(NS_LITERAL_STRING("http://example1.com"),
                     NS_LITERAL_STRING("http://example2.com"),
                     false,
                     NS_LITERAL_CSTRING("test-storage"));
   }
 
   /**
    * 1. Generate some storage data.
-   * 2. Find the max mtime |t| in $profileDir/gmp/storage/.
+   * 2. Find the max mtime |t| in $profileDir/gmp/$platform/gmp-fake/storage/.
    * 3. Pass |t+1| to clear recent history.
-   * 4. Check if all directories in $profileDir/gmp/id/ and
-   *    $profileDir/gmp/storage remain unchanged.
+   * 4. Check if all directories in $profileDir/gmp/$platform/gmp-fake/id/ and
+   *    $profileDir/gmp/$platform/gmp-fake/storage remain unchanged.
    */
   void TestClearRecentHistory3() {
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     // Generate storage data for some site.
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
         this, &GMPStorageTest::TestClearRecentHistory3_Clear);
@@ -944,39 +951,39 @@ class GMPStorageTest : public GMPDecrypt
   private:
     int mCount;
   };
 
   void TestClearRecentHistory_CheckEmpty() {
     FileCounter c1;
     nsresult rv = EnumerateGMPStorageDir(NS_LITERAL_CSTRING("id"), c1);
     EXPECT_TRUE(NS_SUCCEEDED(rv));
-    // There should be no files under $profileDir/gmp/id/
+    // There should be no files under $profileDir/gmp/$platform/gmp-fake/id/
     EXPECT_EQ(c1.GetCount(), 0);
 
     FileCounter c2;
     rv = EnumerateGMPStorageDir(NS_LITERAL_CSTRING("storage"), c2);
     EXPECT_TRUE(NS_SUCCEEDED(rv));
-    // There should be no files under $profileDir/gmp/storage/
+    // There should be no files under $profileDir/gmp/$platform/gmp-fake/storage/
     EXPECT_EQ(c2.GetCount(), 0);
 
     SetFinished();
   }
 
   void TestClearRecentHistory_CheckNonEmpty() {
     FileCounter c1;
     nsresult rv = EnumerateGMPStorageDir(NS_LITERAL_CSTRING("id"), c1);
     EXPECT_TRUE(NS_SUCCEEDED(rv));
-    // There should be one directory under $profileDir/gmp/id/
+    // There should be one directory under $profileDir/gmp/$platform/gmp-fake/id/
     EXPECT_EQ(c1.GetCount(), 1);
 
     FileCounter c2;
     rv = EnumerateGMPStorageDir(NS_LITERAL_CSTRING("storage"), c2);
     EXPECT_TRUE(NS_SUCCEEDED(rv));
-    // There should be one directory under $profileDir/gmp/storage/
+    // There should be one directory under $profileDir/gmp/$platform/gmp-fake/storage/
     EXPECT_EQ(c2.GetCount(), 1);
 
     SetFinished();
   }
 
   void TestCrossOriginStorage() {
     EXPECT_TRUE(!mDecryptor);