Bug 1060179 - Store and retrieve EME node id. r=jesup

This commit is contained in:
Chris Pearce 2014-10-13 11:53:44 +13:00
parent c46ad3053d
commit 2dc76780b9
4 changed files with 237 additions and 34 deletions

View File

@ -4,6 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "GMPService.h" #include "GMPService.h"
#include "prio.h"
#include "prlog.h" #include "prlog.h"
#include "GMPParent.h" #include "GMPParent.h"
#include "GMPVideoDecoderParent.h" #include "GMPVideoDecoderParent.h"
@ -26,6 +27,11 @@
#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX) #if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
#include "mozilla/Sandbox.h" #include "mozilla/Sandbox.h"
#endif #endif
#include "nsAppDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
#include "nsDirectoryServiceDefs.h"
#include "nsHashKeys.h"
#include "nsIFile.h"
namespace mozilla { namespace mozilla {
@ -155,7 +161,7 @@ GeckoMediaPluginService::~GeckoMediaPluginService()
MOZ_ASSERT(mAsyncShutdownPlugins.IsEmpty()); MOZ_ASSERT(mAsyncShutdownPlugins.IsEmpty());
} }
void nsresult
GeckoMediaPluginService::Init() GeckoMediaPluginService::Init()
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
@ -170,9 +176,26 @@ GeckoMediaPluginService::Init()
prefs->AddObserver("media.gmp.plugin.crash", this, false); 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.
nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mStorageBaseDir));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mStorageBaseDir->AppendNative(NS_LITERAL_CSTRING("gmp"));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mStorageBaseDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
if (NS_WARN_IF(NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)) {
return rv;
}
// Kick off scanning for plugins // Kick off scanning for plugins
nsCOMPtr<nsIThread> thread; nsCOMPtr<nsIThread> thread;
unused << GetThread(getter_AddRefs(thread)); return GetThread(getter_AddRefs(thread));
} }
void void
@ -855,6 +878,86 @@ GeckoMediaPluginService::ReAddOnGMPThread(nsRefPtr<GMPParent>& aOld)
NS_DispatchToCurrentThread(WrapRunnableNM(&Dummy, aOld)); NS_DispatchToCurrentThread(WrapRunnableNM(&Dummy, aOld));
} }
NS_IMETHODIMP
GeckoMediaPluginService::GetStorageDir(nsIFile** aOutFile)
{
if (NS_WARN_IF(!mStorageBaseDir)) {
return NS_ERROR_FAILURE;
}
return mStorageBaseDir->Clone(aOutFile);
}
static nsresult
WriteToFile(nsIFile* aPath,
const nsCString& aFileName,
const nsCString& aData)
{
nsCOMPtr<nsIFile> path;
nsresult rv = aPath->Clone(getter_AddRefs(path));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = path->AppendNative(aFileName);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
PRFileDesc* f = nullptr;
rv = path->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, PR_IRWXU, &f);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
int32_t len = PR_Write(f, aData.get(), aData.Length());
PR_Close(f);
if (NS_WARN_IF(len < 0 || (size_t)len != aData.Length())) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
static nsresult
ReadFromFile(nsIFile* aPath,
const nsCString& aFileName,
nsCString& aOutData,
int32_t aMaxLength)
{
nsCOMPtr<nsIFile> path;
nsresult rv = aPath->Clone(getter_AddRefs(path));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = path->AppendNative(aFileName);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
PRFileDesc* f = nullptr;
rv = path->OpenNSPRFileDesc(PR_RDONLY | PR_CREATE_FILE, PR_IRWXU, &f);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
auto size = PR_Seek(f, 0, PR_SEEK_END);
PR_Seek(f, 0, PR_SEEK_SET);
if (size > aMaxLength) {
return NS_ERROR_FAILURE;
}
aOutData.SetLength(size);
auto len = PR_Read(f, aOutData.BeginWriting(), size);
PR_Close(f);
if (NS_WARN_IF(len != size)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP NS_IMETHODIMP
GeckoMediaPluginService::GetNodeId(const nsAString& aOrigin, GeckoMediaPluginService::GetNodeId(const nsAString& aOrigin,
const nsAString& aTopLevelOrigin, const nsAString& aTopLevelOrigin,
@ -867,14 +970,116 @@ GeckoMediaPluginService::GetNodeId(const nsAString& aOrigin,
NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(), NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(),
(aInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"))); (aInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing")));
nsresult rv;
const uint32_t NodeIdSaltLength = 32;
if (aInPrivateBrowsing ||
aOrigin.EqualsLiteral("null") ||
aOrigin.IsEmpty() ||
aTopLevelOrigin.EqualsLiteral("null") ||
aTopLevelOrigin.IsEmpty()) {
// Non-persistent session; just generate a random node id.
nsAutoCString salt;
rv = GenerateRandomPathName(salt, NodeIdSaltLength);
aOutId = salt;
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;
}
rv = path->AppendNative(NS_LITERAL_CSTRING("id"));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// $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;
}
rv = path->Create(nsIFile::DIRECTORY_TYPE, 0700);
if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsIFile> saltFile;
rv = path->Clone(getter_AddRefs(saltFile));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = saltFile->AppendNative(NS_LITERAL_CSTRING("salt"));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsAutoCString salt; nsAutoCString salt;
nsresult rv = GenerateRandomPathName(salt, 32); bool exists = false;
NS_ENSURE_SUCCESS(rv, rv); rv = saltFile->Exists(&exists);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!exists) {
// 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
rv = WriteToFile(path, NS_LITERAL_CSTRING("salt"), salt);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// $profileDir/gmp/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
rv = WriteToFile(path,
NS_LITERAL_CSTRING("topLevelOrigin"),
NS_ConvertUTF16toUTF8(aTopLevelOrigin));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
} else {
rv = ReadFromFile(path,
NS_LITERAL_CSTRING("salt"),
salt,
NodeIdSaltLength);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
aOutId = salt; aOutId = salt;
// TODO: Store salt, so it can be retrieved in subsequent sessions.
return NS_OK; return NS_OK;
} }

View File

@ -32,7 +32,7 @@ public:
static already_AddRefed<GeckoMediaPluginService> GetGeckoMediaPluginService(); static already_AddRefed<GeckoMediaPluginService> GetGeckoMediaPluginService();
GeckoMediaPluginService(); GeckoMediaPluginService();
void Init(); nsresult Init();
NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_MOZIGECKOMEDIAPLUGINSERVICE NS_DECL_MOZIGECKOMEDIAPLUGINSERVICE
@ -111,6 +111,8 @@ private:
nsTArray<nsRefPtr<GMPParent>> mAsyncShutdownPlugins; // GMP Thread only. nsTArray<nsRefPtr<GMPParent>> mAsyncShutdownPlugins; // GMP Thread only.
nsCOMPtr<nsITimer> mAsyncShutdownTimeout; // GMP Thread only. nsCOMPtr<nsITimer> mAsyncShutdownTimeout; // GMP Thread only.
nsCOMPtr<nsIFile> mStorageBaseDir;
}; };
} // namespace gmp } // namespace gmp

View File

@ -8,6 +8,7 @@
#include "plhash.h" #include "plhash.h"
#include "nsDirectoryServiceUtils.h" #include "nsDirectoryServiceUtils.h"
#include "nsDirectoryServiceDefs.h" #include "nsDirectoryServiceDefs.h"
#include "nsAppDirectoryServiceDefs.h"
#include "GMPParent.h" #include "GMPParent.h"
#include "gmp-storage.h" #include "gmp-storage.h"
#include "mozilla/unused.h" #include "mozilla/unused.h"
@ -35,26 +36,8 @@ extern PRLogModuleInfo* GetGMPLog();
namespace gmp { namespace gmp {
class GetTempDirTask : public nsRunnable // We store the records in files in the profile dir.
{ // $profileDir/gmp/storage/$nodeId/
public:
NS_IMETHOD Run() {
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIFile> tmpFile;
nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpFile));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
tmpFile->GetPath(mPath);
return NS_OK;
}
nsString mPath;
};
// We store the records in files in the system temp dir.
static nsresult static nsresult
GetGMPStorageDir(nsIFile** aTempDir, const nsCString& aNodeId) GetGMPStorageDir(nsIFile** aTempDir, const nsCString& aNodeId)
{ {
@ -62,22 +45,28 @@ GetGMPStorageDir(nsIFile** aTempDir, const nsCString& aNodeId)
return NS_ERROR_INVALID_ARG; return NS_ERROR_INVALID_ARG;
} }
// Directory service is main thread only... nsCOMPtr<mozIGeckoMediaPluginService> mps =
nsRefPtr<GetTempDirTask> task = new GetTempDirTask(); do_GetService("@mozilla.org/gecko-media-plugin-service;1");
nsCOMPtr<nsIThread> mainThread = do_GetMainThread(); if (NS_WARN_IF(!mps)) {
mozilla::SyncRunnable::DispatchToThread(mainThread, task); return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIFile> tmpFile; nsCOMPtr<nsIFile> tmpFile;
nsresult rv = NS_NewLocalFile(task->mPath, false, getter_AddRefs(tmpFile)); nsresult rv = mps->GetStorageDir(getter_AddRefs(tmpFile));
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return rv; return rv;
} }
rv = tmpFile->AppendNative(nsDependentCString("mozilla-gmp-storage")); rv = tmpFile->AppendNative(NS_LITERAL_CSTRING("storage"));
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return 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(aNodeId); rv = tmpFile->AppendNative(aNodeId);
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return rv; return rv;

View File

@ -6,6 +6,7 @@
#include "nsISupports.idl" #include "nsISupports.idl"
#include "nsIThread.idl" #include "nsIThread.idl"
#include "nsIPrincipal.idl" #include "nsIPrincipal.idl"
#include "nsIFile.idl"
%{C++ %{C++
#include "nsTArray.h" #include "nsTArray.h"
@ -25,7 +26,7 @@ class GMPVideoHost;
[ptr] native GMPDecryptorProxy(GMPDecryptorProxy); [ptr] native GMPDecryptorProxy(GMPDecryptorProxy);
[ptr] native GMPAudioDecoderProxy(GMPAudioDecoderProxy); [ptr] native GMPAudioDecoderProxy(GMPAudioDecoderProxy);
[scriptable, uuid(3d811f9f-e1f8-48a5-a385-3657a641ee76)] [scriptable, uuid(e5cde76d-f926-4b3f-84ff-62864c7a750a)]
interface mozIGeckoMediaPluginService : nsISupports interface mozIGeckoMediaPluginService : nsISupports
{ {
@ -96,4 +97,10 @@ interface mozIGeckoMediaPluginService : nsISupports
ACString getNodeId(in AString origin, ACString getNodeId(in AString origin,
in AString topLevelOrigin, in AString topLevelOrigin,
in bool inPrivateBrowsingMode); in bool inPrivateBrowsingMode);
/**
* Returns the directory to use as the base for storing data about GMPs.
*/
nsIFile getStorageDir();
}; };