Backed out changeset f93b8ceabc6e (bug 1060179)
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 09 Oct 2014 12:48:18 +0200
changeset 209557 039f888740aa14e1ebd1a22c4aa434a351794fc6
parent 209556 354abc9c4565587eb8a61a82051f7064a9fc7495
child 209558 2da7ce468fb6e90bfbbc7f7682dce0f9961b7986
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
bugs1060179
milestone35.0a1
backs outf93b8ceabc6e0b120558e0ba2db846dd82075460
Backed out changeset f93b8ceabc6e (bug 1060179)
content/media/gmp/GMPParent.cpp
content/media/gmp/GMPService.cpp
content/media/gmp/GMPService.h
content/media/gmp/GMPStorageParent.cpp
content/media/gmp/GMPStorageParent.h
content/media/gmp/mozIGeckoMediaPluginService.idl
--- a/content/media/gmp/GMPParent.cpp
+++ b/content/media/gmp/GMPParent.cpp
@@ -706,22 +706,18 @@ GMPParent::DeallocPGMPStorageParent(PGMP
 {
   GMPStorageParent* p = static_cast<GMPStorageParent*>(aActor);
   p->Shutdown();
   mStorage.RemoveElement(p);
   return true;
 }
 
 bool
-GMPParent::RecvPGMPStorageConstructor(PGMPStorageParent* aActor)
+GMPParent::RecvPGMPStorageConstructor(PGMPStorageParent* actor)
 {
-  GMPStorageParent* p  = (GMPStorageParent*)aActor;
-  if (NS_WARN_IF(NS_FAILED(p->Init()))) {
-    return false;
-  }
   return true;
 }
 
 bool
 GMPParent::RecvPGMPTimerConstructor(PGMPTimerParent* actor)
 {
   return true;
 }
--- a/content/media/gmp/GMPService.cpp
+++ b/content/media/gmp/GMPService.cpp
@@ -164,17 +164,16 @@ nsresult
 GeckoMediaPluginService::Init()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
   MOZ_ASSERT(obsService);
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, "profile-change-teardown", false)));
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false)));
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, "last-pb-context-exited", false)));
 
   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
   if (prefs) {
     prefs->AddObserver("media.gmp.plugin.crash", this, false);
   }
 
   // Directory service is main thread only, so cache the profile dir here
   // so that we can use it off main thread.
@@ -307,23 +306,16 @@ GeckoMediaPluginService::Observe(nsISupp
       // See bug 1057908.
       MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default || mShuttingDown);
       mGMPThread.swap(gmpThread);
     }
 
     if (gmpThread) {
       gmpThread->Shutdown();
     }
-  } else if (!strcmp("last-pb-context-exited", aTopic)) {
-    // When Private Browsing mode exits, all we need to do is clear
-    // mTempNodeIds. This drops all the node ids we've cached in memory
-    // for PB origin-pairs. If we try to open an origin-pair for non-PB
-    // mode, we'll get the NodeId salt stored on-disk, and if we try to
-    // open a PB mode origin-pair, we'll re-generate new salt.
-    mTempNodeIds.Clear();
   }
   return NS_OK;
 }
 
 // always call with getter_AddRefs, because it does
 NS_IMETHODIMP
 GeckoMediaPluginService::GetThread(nsIThread** aThread)
 {
@@ -961,78 +953,40 @@ ReadFromFile(nsIFile* aPath,
   if (NS_WARN_IF(len != size)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-GeckoMediaPluginService::IsPersistentStorageAllowed(const nsACString& aNodeId,
-                                                    bool* aOutAllowed)
-{
-  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
-  NS_ENSURE_ARG(aOutAllowed);
-  *aOutAllowed = mPersistentStorageAllowed.Get(aNodeId);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 GeckoMediaPluginService::GetNodeId(const nsAString& aOrigin,
                                    const nsAString& aTopLevelOrigin,
                                    bool aInPrivateBrowsing,
                                    nsACString& aOutId)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   LOGD(("%s::%s: (%s, %s), %s", __CLASS__, __FUNCTION__,
        NS_ConvertUTF16toUTF8(aOrigin).get(),
        NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(),
        (aInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing")));
 
   nsresult rv;
   const uint32_t NodeIdSaltLength = 32;
 
-  if (aOrigin.EqualsLiteral("null") ||
+  if (aInPrivateBrowsing ||
+      aOrigin.EqualsLiteral("null") ||
       aOrigin.IsEmpty() ||
       aTopLevelOrigin.EqualsLiteral("null") ||
       aTopLevelOrigin.IsEmpty()) {
-    // At least one of the (origin, topLevelOrigin) is null or empty;
-    // probably a local file. Generate a random node id, and don't store
-    // it so that the GMP's storage is temporary and not shared.
+    // Non-persistent session; just generate a random node id.
     nsAutoCString salt;
     rv = GenerateRandomPathName(salt, NodeIdSaltLength);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
     aOutId = salt;
-    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.
-    nsCString* salt = nullptr;
-    if (!(salt = mTempNodeIds.Get(hash))) {
-      // 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);
-      mPersistentStorageAllowed.Put(*salt, false);
-    }
-    aOutId = *salt;
-    return NS_OK;
+    return rv;
   }
 
   // Otherwise, try to see if we've previously generated and stored salt
   // for this origin pair.
   nsCOMPtr<nsIFile> path; // $profileDir/gmp/
   rv = GetStorageDir(getter_AddRefs(path));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
@@ -1044,16 +998,18 @@ GeckoMediaPluginService::GetNodeId(const
   }
 
   // $profileDir/gmp/id/
   rv = path->Create(nsIFile::DIRECTORY_TYPE, 0700);
   if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  uint32_t hash = AddToHash(HashString(aOrigin),
+                            HashString(aTopLevelOrigin));
   nsAutoCString hashStr;
   hashStr.AppendInt((int64_t)hash);
 
   // $profileDir/gmp/id/$hash
   rv = path->AppendNative(hashStr);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
@@ -1117,15 +1073,14 @@ GeckoMediaPluginService::GetNodeId(const
                       salt,
                       NodeIdSaltLength);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   aOutId = salt;
-  mPersistentStorageAllowed.Put(salt, true);
 
   return NS_OK;
 }
 
 } // namespace gmp
 } // namespace mozilla
--- a/content/media/gmp/GMPService.h
+++ b/content/media/gmp/GMPService.h
@@ -12,18 +12,16 @@
 #include "nsTArray.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Monitor.h"
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "nsIThread.h"
 #include "nsThreadUtils.h"
 #include "nsITimer.h"
-#include "nsClassHashtable.h"
-#include "nsDataHashtable.h"
 
 template <class> struct already_AddRefed;
 
 namespace mozilla {
 namespace gmp {
 
 class GMPParent;
 
@@ -110,22 +108,14 @@ private:
   };
 
   MainThreadOnly<bool> mWaitingForPluginsAsyncShutdown;
 
   nsTArray<nsRefPtr<GMPParent>> mAsyncShutdownPlugins; // GMP Thread only.
   nsCOMPtr<nsITimer> mAsyncShutdownTimeout; // GMP Thread only.
 
   nsCOMPtr<nsIFile> mStorageBaseDir;
-
-  // Hashes of (origin,topLevelOrigin) to the node id for
-  // non-persistent sessions.
-  nsClassHashtable<nsUint32HashKey, nsCString> mTempNodeIds;
-
-  // Hashes node id to whether that node id is allowed to store data
-  // persistently on disk.
-  nsDataHashtable<nsCStringHashKey, bool> mPersistentStorageAllowed;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
 #endif // GMPService_h_
--- a/content/media/gmp/GMPStorageParent.cpp
+++ b/content/media/gmp/GMPStorageParent.cpp
@@ -7,22 +7,16 @@
 #include "mozilla/SyncRunnable.h"
 #include "plhash.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "GMPParent.h"
 #include "gmp-storage.h"
 #include "mozilla/unused.h"
-#include "nsTHashtable.h"
-#include "nsDataHashtable.h"
-#include "prio.h"
-#include "mozIGeckoMediaPluginService.h"
-#include "nsContentCID.h"
-#include "nsServiceManagerUtils.h"
 
 namespace mozilla {
 
 #ifdef LOG
 #undef LOG
 #endif
 
 #ifdef PR_LOGGING
@@ -83,16 +77,24 @@ GetGMPStorageDir(nsIFile** aTempDir, con
     return rv;
   }
 
   tmpFile.forget(aTempDir);
 
   return NS_OK;
 }
 
+GMPStorageParent::GMPStorageParent(const nsCString& aNodeId,
+                                   GMPParent* aPlugin)
+  : mNodeId(aNodeId)
+  , mPlugin(aPlugin)
+  , mShutdown(false)
+{
+}
+
 enum OpenFileMode  { ReadWrite, Truncate };
 
 nsresult
 OpenStorageFile(const nsCString& aRecordName,
                 const nsCString& aNodeId,
                 const OpenFileMode aMode,
                 PRFileDesc** aOutFD)
 {
@@ -111,319 +113,159 @@ OpenStorageFile(const nsCString& aRecord
   auto mode = PR_RDWR | PR_CREATE_FILE;
   if (aMode == Truncate) {
     mode |= PR_TRUNCATE;
   }
 
   return f->OpenNSPRFileDesc(mode, PR_IRWXU, aOutFD);
 }
 
-PLDHashOperator
-CloseFile(const nsACString& key, PRFileDesc*& entry, void* cx)
-{
-  if (PR_Close(entry) != PR_SUCCESS) {
-    NS_WARNING("GMPDiskStorage Failed to clsose file.");
-  }
-  return PL_DHASH_REMOVE;
-}
-
-class GMPDiskStorage : public GMPStorage {
-public:
-  GMPDiskStorage(const nsCString& aNodeId)
-    : mNodeId(aNodeId)
-  {
-  }
-  ~GMPDiskStorage() {
-    mFiles.Enumerate(CloseFile, nullptr);
-    MOZ_ASSERT(!mFiles.Count());
-  }
-
-  virtual GMPErr Open(const nsCString& aRecordName) MOZ_OVERRIDE
-  {
-    MOZ_ASSERT(!IsOpen(aRecordName));
-    PRFileDesc* fd = nullptr;
-    if (NS_FAILED(OpenStorageFile(aRecordName, mNodeId, ReadWrite, &fd))) {
-      NS_WARNING("Failed to open storage file.");
-      return GMPGenericErr;
-    }
-    mFiles.Put(aRecordName, fd);
-    return GMPNoErr;
-  }
-
-  virtual bool IsOpen(const nsCString& aRecordName) MOZ_OVERRIDE {
-    return mFiles.Contains(aRecordName);
-  }
-
-  virtual GMPErr Read(const nsCString& aRecordName,
-                      nsTArray<uint8_t>& aOutBytes) MOZ_OVERRIDE
-  {
-    PRFileDesc* fd = mFiles.Get(aRecordName);
-    if (!fd) {
-      return GMPGenericErr;
-    }
-
-    int32_t len = PR_Seek(fd, 0, PR_SEEK_END);
-    PR_Seek(fd, 0, PR_SEEK_SET);
-
-    if (len > GMP_MAX_RECORD_SIZE) {
-      // Refuse to read big records.
-      return GMPQuotaExceededErr;
-    }
-    aOutBytes.SetLength(len);
-    auto bytesRead = PR_Read(fd, aOutBytes.Elements(), len);
-    return (bytesRead == len) ? GMPNoErr : GMPGenericErr;
-  }
-
-  virtual GMPErr Write(const nsCString& aRecordName,
-                       const nsTArray<uint8_t>& aBytes) MOZ_OVERRIDE
-  {
-    PRFileDesc* fd = mFiles.Get(aRecordName);
-    if (!fd) {
-      return GMPGenericErr;
-    }
-
-    // Write operations overwrite the entire record. So re-open the file
-    // in truncate mode, to clear its contents.
-    PR_Close(fd);
-    mFiles.Remove(aRecordName);
-    if (NS_FAILED(OpenStorageFile(aRecordName, mNodeId, Truncate, &fd))) {
-      return GMPGenericErr;
-    }
-    mFiles.Put(aRecordName, fd);
-
-    int32_t bytesWritten = PR_Write(fd, aBytes.Elements(), aBytes.Length());
-    return (bytesWritten == (int32_t)aBytes.Length()) ? GMPNoErr : GMPGenericErr;
-  }
-
-  virtual void Close(const nsCString& aRecordName) MOZ_OVERRIDE
-  {
-    PRFileDesc* fd = mFiles.Get(aRecordName);
-    if (fd) {
-      if (PR_Close(fd) == PR_SUCCESS) {
-        mFiles.Remove(aRecordName);
-      } else {
-        NS_WARNING("GMPDiskStorage Failed to clsose file.");
-      }
-    }
-  }
-
-private:
-  nsDataHashtable<nsCStringHashKey, PRFileDesc*> mFiles;
-  const nsAutoCString mNodeId;
-};
-
-class GMPMemoryStorage : public GMPStorage {
-public:
-  virtual GMPErr Open(const nsCString& aRecordName) MOZ_OVERRIDE
-  {
-    MOZ_ASSERT(!IsOpen(aRecordName));
-
-    Record* record = nullptr;
-    if (!mRecords.Get(aRecordName, &record)) {
-      record = new Record();
-      mRecords.Put(aRecordName, record);
-    }
-    record->mIsOpen = true;
-    return GMPNoErr;
-  }
-
-  virtual bool IsOpen(const nsCString& aRecordName) MOZ_OVERRIDE {
-    Record* record = nullptr;
-    if (!mRecords.Get(aRecordName, &record)) {
-      return false;
-    }
-    return record->mIsOpen;
-  }
-
-  virtual GMPErr Read(const nsCString& aRecordName,
-                      nsTArray<uint8_t>& aOutBytes) MOZ_OVERRIDE
-  {
-    Record* record = nullptr;
-    if (!mRecords.Get(aRecordName, &record)) {
-      return GMPGenericErr;
-    }
-    aOutBytes = record->mData;
-    return GMPNoErr;
-  }
-
-  virtual GMPErr Write(const nsCString& aRecordName,
-                       const nsTArray<uint8_t>& aBytes) MOZ_OVERRIDE
-  {
-    Record* record = nullptr;
-    if (!mRecords.Get(aRecordName, &record)) {
-      return GMPClosedErr;
-    }
-    record->mData = aBytes;
-    return GMPNoErr;
-  }
-
-  virtual void Close(const nsCString& aRecordName) MOZ_OVERRIDE
-  {
-    Record* record = nullptr;
-    if (!mRecords.Get(aRecordName, &record)) {
-      return;
-    }
-    if (!record->mData.Length()) {
-      // Record is empty, delete.
-      mRecords.Remove(aRecordName);
-    } else {
-      record->mIsOpen = false;
-    }
-  }
-
-private:
-
-  struct Record {
-    Record() : mIsOpen(false) {}
-    nsTArray<uint8_t> mData;
-    bool mIsOpen;
-  };
-
-  nsClassHashtable<nsCStringHashKey, Record> mRecords;
-};
-
-GMPStorageParent::GMPStorageParent(const nsCString& aNodeId,
-                                   GMPParent* aPlugin)
-  : mNodeId(aNodeId)
-  , mPlugin(aPlugin)
-  , mShutdown(false)
-{
-}
-
-nsresult
-GMPStorageParent::Init()
-{
-  if (NS_WARN_IF(mNodeId.IsEmpty())) {
-    return NS_ERROR_FAILURE;
-  }
-  nsCOMPtr<mozIGeckoMediaPluginService> mps =
-    do_GetService("@mozilla.org/gecko-media-plugin-service;1");
-  if (NS_WARN_IF(!mps)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  bool persistent = false;
-  if (NS_WARN_IF(NS_FAILED(mps->IsPersistentStorageAllowed(mNodeId, &persistent)))) {
-    return NS_ERROR_FAILURE;
-  }
-  if (persistent) {
-    mStorage = MakeUnique<GMPDiskStorage>(mNodeId);
-  } else {
-    mStorage = MakeUnique<GMPMemoryStorage>();
-  }
-
-  return NS_OK;
-}
-
 bool
 GMPStorageParent::RecvOpen(const nsCString& aRecordName)
 {
   if (mShutdown) {
-    return false;
+    return true;
   }
 
   if (mNodeId.EqualsLiteral("null")) {
     // Refuse to open storage if the page is opened from local disk,
     // or shared across origin.
     NS_WARNING("Refusing to open storage for null NodeId");
     unused << SendOpenComplete(aRecordName, GMPGenericErr);
     return true;
   }
 
-  if (aRecordName.IsEmpty()) {
+  if (aRecordName.IsEmpty() || mFiles.Contains(aRecordName)) {
+    unused << SendOpenComplete(aRecordName, GMPRecordInUse);
+    return true;
+  }
+
+  PRFileDesc* fd = nullptr;
+  nsresult rv = OpenStorageFile(aRecordName, mNodeId, ReadWrite, &fd);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to open storage file.");
     unused << SendOpenComplete(aRecordName, GMPGenericErr);
     return true;
   }
 
-  if (mStorage->IsOpen(aRecordName)) {
-    unused << SendOpenComplete(aRecordName, GMPRecordInUse);
-    return true;
-  }
-
-  auto err = mStorage->Open(aRecordName);
-  MOZ_ASSERT(GMP_FAILED(err) || mStorage->IsOpen(aRecordName));
-  unused << SendOpenComplete(aRecordName, err);
+  mFiles.Put(aRecordName, fd);
+  unused << SendOpenComplete(aRecordName, GMPNoErr);
 
   return true;
 }
 
 bool
 GMPStorageParent::RecvRead(const nsCString& aRecordName)
 {
   LOGD(("%s::%s: %p record=%s", __CLASS__, __FUNCTION__, this, aRecordName.get()));
 
   if (mShutdown) {
-    return false;
+    return true;
+  }
+
+  PRFileDesc* fd = mFiles.Get(aRecordName);
+  nsTArray<uint8_t> data;
+  if (!fd) {
+    unused << SendReadComplete(aRecordName, GMPClosedErr, data);
+    return true;
   }
 
-  nsTArray<uint8_t> data;
-  if (!mStorage->IsOpen(aRecordName)) {
-    unused << SendReadComplete(aRecordName, GMPClosedErr, data);
-  } else {
-    unused << SendReadComplete(aRecordName, mStorage->Read(aRecordName, data), data);
+  int32_t len = PR_Seek(fd, 0, PR_SEEK_END);
+  PR_Seek(fd, 0, PR_SEEK_SET);
+
+  if (len > GMP_MAX_RECORD_SIZE) {
+    // Refuse to read big records.
+    unused << SendReadComplete(aRecordName, GMPQuotaExceededErr, data);
+    return true;
   }
+  data.SetLength(len);
+  auto bytesRead = PR_Read(fd, data.Elements(), len);
+  auto res = (bytesRead == len) ? GMPNoErr : GMPGenericErr;
+  unused << SendReadComplete(aRecordName, res, data);
 
   return true;
 }
 
 bool
 GMPStorageParent::RecvWrite(const nsCString& aRecordName,
                             const InfallibleTArray<uint8_t>& aBytes)
 {
   LOGD(("%s::%s: %p record=%s", __CLASS__, __FUNCTION__, this, aRecordName.get()));
 
   if (mShutdown) {
-    return false;
-  }
-
-  if (!mStorage->IsOpen(aRecordName)) {
-    unused << SendWriteComplete(aRecordName, GMPClosedErr);
     return true;
   }
-
   if (aBytes.Length() > GMP_MAX_RECORD_SIZE) {
     unused << SendWriteComplete(aRecordName, GMPQuotaExceededErr);
     return true;
   }
 
-  unused << SendWriteComplete(aRecordName, mStorage->Write(aRecordName, aBytes));
+  PRFileDesc* fd = mFiles.Get(aRecordName);
+  if (!fd) {
+    unused << SendWriteComplete(aRecordName, GMPGenericErr);
+    return true;
+  }
 
+  // Write operations overwrite the entire record. So re-open the file
+  // in truncate mode, to clear its contents.
+  PR_Close(fd);
+  mFiles.Remove(aRecordName);
+  if (NS_FAILED(OpenStorageFile(aRecordName, mNodeId, Truncate, &fd))) {
+    unused << SendWriteComplete(aRecordName, GMPGenericErr);
+    return true;
+  }
+  mFiles.Put(aRecordName, fd);
+
+  int32_t bytesWritten = PR_Write(fd, aBytes.Elements(), aBytes.Length());
+  auto res = (bytesWritten == (int32_t)aBytes.Length()) ? GMPNoErr : GMPGenericErr;
+  unused << SendWriteComplete(aRecordName, res);
   return true;
 }
 
 bool
 GMPStorageParent::RecvClose(const nsCString& aRecordName)
 {
   LOGD(("%s::%s: %p record=%s", __CLASS__, __FUNCTION__, this, aRecordName.get()));
 
   if (mShutdown) {
     return true;
   }
 
-  mStorage->Close(aRecordName);
-
+  PRFileDesc* fd = mFiles.Get(aRecordName);
+  if (!fd) {
+    return true;
+  }
+  PR_Close(fd);
+  mFiles.Remove(aRecordName);
   return true;
 }
 
 void
 GMPStorageParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
   Shutdown();
 }
 
+PLDHashOperator
+CloseFile(const nsACString& key, PRFileDesc*& entry, void* cx)
+{
+  PR_Close(entry);
+  return PL_DHASH_REMOVE;
+}
+
 void
 GMPStorageParent::Shutdown()
 {
   LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
 
   if (mShutdown) {
     return;
   }
   mShutdown = true;
   unused << SendShutdown();
 
-  mStorage = nullptr;
-
+  mFiles.Enumerate(CloseFile, nullptr);
+  MOZ_ASSERT(!mFiles.Count());
 }
 
 } // namespace gmp
 } // namespace mozilla
--- a/content/media/gmp/GMPStorageParent.h
+++ b/content/media/gmp/GMPStorageParent.h
@@ -3,59 +3,45 @@
  * 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 GMPStorageParent_h_
 #define GMPStorageParent_h_
 
 #include "mozilla/gmp/PGMPStorageParent.h"
 #include "gmp-storage.h"
-#include "mozilla/UniquePtr.h"
+#include "nsTHashtable.h"
+#include "nsDataHashtable.h"
+#include "prio.h"
 
 namespace mozilla {
 namespace gmp {
 
 class GMPParent;
 
-class GMPStorage {
-public:
-  virtual ~GMPStorage() {}
-
-  virtual GMPErr Open(const nsCString& aRecordName) = 0;
-  virtual bool IsOpen(const nsCString& aRecordName) = 0;
-  virtual GMPErr Read(const nsCString& aRecordName,
-                      nsTArray<uint8_t>& aOutBytes) = 0;
-  virtual GMPErr Write(const nsCString& aRecordName,
-                       const nsTArray<uint8_t>& aBytes) = 0;
-  virtual void Close(const nsCString& aRecordName) = 0;
-};
-
 class GMPStorageParent : public PGMPStorageParent {
 public:
   NS_INLINE_DECL_REFCOUNTING(GMPStorageParent)
-  GMPStorageParent(const nsCString& aNodeId,
-                   GMPParent* aPlugin);
+  GMPStorageParent(const nsCString& aNodeId, GMPParent* aPlugin);
 
-  nsresult Init();
   void Shutdown();
 
 protected:
   virtual bool RecvOpen(const nsCString& aRecordName) MOZ_OVERRIDE;
   virtual bool RecvRead(const nsCString& aRecordName) MOZ_OVERRIDE;
   virtual bool RecvWrite(const nsCString& aRecordName,
                          const InfallibleTArray<uint8_t>& aBytes) MOZ_OVERRIDE;
   virtual bool RecvClose(const nsCString& aRecordName) MOZ_OVERRIDE;
   virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 
 private:
   ~GMPStorageParent() {}
 
-  UniquePtr<GMPStorage> mStorage;
-
-  const nsCString mNodeId;
+  nsDataHashtable<nsCStringHashKey, PRFileDesc*> mFiles;
+  const nsAutoCString mNodeId;
   nsRefPtr<GMPParent> mPlugin;
   bool mShutdown;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
 #endif // GMPStorageParent_h_
--- a/content/media/gmp/mozIGeckoMediaPluginService.idl
+++ b/content/media/gmp/mozIGeckoMediaPluginService.idl
@@ -21,17 +21,17 @@ class GMPVideoHost;
 [ptr] native GMPVideoDecoderProxy(GMPVideoDecoderProxy);
 [ptr] native GMPVideoEncoderProxy(GMPVideoEncoderProxy);
 [ptr] native GMPVideoHost(GMPVideoHost);
 [ptr] native MessageLoop(MessageLoop);
 [ptr] native TagArray(nsTArray<nsCString>);
 [ptr] native GMPDecryptorProxy(GMPDecryptorProxy);
 [ptr] native GMPAudioDecoderProxy(GMPAudioDecoderProxy);
 
-[scriptable, uuid(b350d3b6-00c9-4602-bdfe-84c2be8d1e7a)]
+[scriptable, uuid(e5cde76d-f926-4b3f-84ff-62864c7a750a)]
 interface mozIGeckoMediaPluginService : nsISupports
 {
 
   /**
    * The GMP thread. Callable from any thread.
    */
   readonly attribute nsIThread thread;
 
@@ -94,20 +94,13 @@ interface mozIGeckoMediaPluginService : 
   /**
    * Gets the NodeId for a (origin, urlbarOrigin, isInprivateBrowsing) tuple.
    */
   ACString getNodeId(in AString origin,
                      in AString topLevelOrigin,
                      in bool inPrivateBrowsingMode);
 
   /**
-   * Returns true if the given node id is allowed to store things
-   * persistently on disk. Private Browsing and local content are not
-   * allowed to store persistent data.
-   */
-  bool isPersistentStorageAllowed(in ACString nodeId);
-
-  /**
    * Returns the directory to use as the base for storing data about GMPs.
    */
   nsIFile getStorageDir();
 
 };