--- a/content/media/gmp/GMPParent.cpp
+++ b/content/media/gmp/GMPParent.cpp
@@ -706,18 +706,22 @@ GMPParent::DeallocPGMPStorageParent(PGMP
{
GMPStorageParent* p = static_cast<GMPStorageParent*>(aActor);
p->Shutdown();
mStorage.RemoveElement(p);
return true;
}
bool
-GMPParent::RecvPGMPStorageConstructor(PGMPStorageParent* actor)
+GMPParent::RecvPGMPStorageConstructor(PGMPStorageParent* aActor)
{
+ 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,16 +164,17 @@ 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.
@@ -306,16 +307,23 @@ 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)
{
@@ -953,40 +961,78 @@ 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 (aInPrivateBrowsing ||
- aOrigin.EqualsLiteral("null") ||
+ if (aOrigin.EqualsLiteral("null") ||
aOrigin.IsEmpty() ||
aTopLevelOrigin.EqualsLiteral("null") ||
aTopLevelOrigin.IsEmpty()) {
- // Non-persistent session; just generate a random node id.
+ // 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.
nsAutoCString salt;
rv = GenerateRandomPathName(salt, NodeIdSaltLength);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
aOutId = salt;
- return rv;
+ 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;
}
// 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;
@@ -998,18 +1044,16 @@ 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;
}
@@ -1073,14 +1117,15 @@ 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,16 +12,18 @@
#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;
@@ -108,14 +110,22 @@ 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,16 +7,22 @@
#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
@@ -77,24 +83,16 @@ 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)
{
@@ -113,159 +111,319 @@ 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 true;
+ return false;
}
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() || mFiles.Contains(aRecordName)) {
+ if (aRecordName.IsEmpty()) {
+ unused << SendOpenComplete(aRecordName, GMPGenericErr);
+ return true;
+ }
+
+ if (mStorage->IsOpen(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;
- }
-
- mFiles.Put(aRecordName, fd);
- unused << SendOpenComplete(aRecordName, GMPNoErr);
+ auto err = mStorage->Open(aRecordName);
+ MOZ_ASSERT(GMP_FAILED(err) || mStorage->IsOpen(aRecordName));
+ unused << SendOpenComplete(aRecordName, err);
return true;
}
bool
GMPStorageParent::RecvRead(const nsCString& aRecordName)
{
LOGD(("%s::%s: %p record=%s", __CLASS__, __FUNCTION__, this, aRecordName.get()));
if (mShutdown) {
- return true;
- }
-
- PRFileDesc* fd = mFiles.Get(aRecordName);
- nsTArray<uint8_t> data;
- if (!fd) {
- unused << SendReadComplete(aRecordName, GMPClosedErr, data);
- return true;
+ return false;
}
- 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;
+ nsTArray<uint8_t> data;
+ if (!mStorage->IsOpen(aRecordName)) {
+ unused << SendReadComplete(aRecordName, GMPClosedErr, data);
+ } else {
+ unused << SendReadComplete(aRecordName, mStorage->Read(aRecordName, data), data);
}
- 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;
}
- PRFileDesc* fd = mFiles.Get(aRecordName);
- if (!fd) {
- unused << SendWriteComplete(aRecordName, GMPGenericErr);
- return true;
- }
+ unused << SendWriteComplete(aRecordName, mStorage->Write(aRecordName, aBytes));
- // 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;
}
- PRFileDesc* fd = mFiles.Get(aRecordName);
- if (!fd) {
- return true;
- }
- PR_Close(fd);
- mFiles.Remove(aRecordName);
+ mStorage->Close(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();
- mFiles.Enumerate(CloseFile, nullptr);
- MOZ_ASSERT(!mFiles.Count());
+ mStorage = nullptr;
+
}
} // namespace gmp
} // namespace mozilla
--- a/content/media/gmp/GMPStorageParent.h
+++ b/content/media/gmp/GMPStorageParent.h
@@ -3,45 +3,59 @@
* 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 "nsTHashtable.h"
-#include "nsDataHashtable.h"
-#include "prio.h"
+#include "mozilla/UniquePtr.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() {}
- nsDataHashtable<nsCStringHashKey, PRFileDesc*> mFiles;
- const nsAutoCString mNodeId;
+ UniquePtr<GMPStorage> mStorage;
+
+ const nsCString 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(e5cde76d-f926-4b3f-84ff-62864c7a750a)]
+[scriptable, uuid(b350d3b6-00c9-4602-bdfe-84c2be8d1e7a)]
interface mozIGeckoMediaPluginService : nsISupports
{
/**
* The GMP thread. Callable from any thread.
*/
readonly attribute nsIThread thread;
@@ -94,13 +94,20 @@ 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();
};