Bug 1060179 - Generate a random node id for every EME (origin,topLevelOrigin) pair. r=bz
--- a/content/html/content/public/HTMLMediaElement.h
+++ b/content/html/content/public/HTMLMediaElement.h
@@ -540,16 +540,21 @@ public:
mozilla::dom::EventHandlerNonNull* GetOnencrypted();
void SetOnencrypted(mozilla::dom::EventHandlerNonNull* listener);
void DispatchEncrypted(const nsTArray<uint8_t>& aInitData,
const nsAString& aInitDataType);
bool IsEventAttributeName(nsIAtom* aName) MOZ_OVERRIDE;
+
+ // Returns the principal of the "top level" document; the origin displayed
+ // in the URL bar of the browser window.
+ already_AddRefed<nsIPrincipal> GetTopLevelPrincipal();
+
#endif // MOZ_EME
bool MozAutoplayEnabled() const
{
return mAutoplayEnabled;
}
already_AddRefed<DOMMediaStream> MozCaptureStream(ErrorResult& aRv);
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -3402,16 +3402,21 @@ void HTMLMediaElement::NotifyDecoderPrin
mDecoder->UpdateSameOriginStatus(
!principal ||
(NS_SUCCEEDED(NodePrincipal()->Subsumes(principal, &subsumes)) && subsumes));
for (uint32_t i = 0; i < mOutputStreams.Length(); ++i) {
OutputMediaStream* ms = &mOutputStreams[i];
ms->mStream->CombineWithPrincipal(principal);
}
+#ifdef MOZ_EME
+ if (mMediaKeys && NS_FAILED(mMediaKeys->CheckPrincipals())) {
+ mMediaKeys->Shutdown();
+ }
+#endif
}
void HTMLMediaElement::UpdateMediaSize(nsIntSize size)
{
mMediaSize = size;
}
void HTMLMediaElement::SuspendOrResumeElement(bool aPauseElement, bool aSuspendEvents)
@@ -4003,26 +4008,44 @@ HTMLMediaElement::SetMediaKeys(mozilla::
ErrorResult& aRv)
{
nsCOMPtr<nsIGlobalObject> global =
do_QueryInterface(OwnerDoc()->GetInnerWindow());
if (!global) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
- // TODO: Need to shutdown existing MediaKeys instance? bug 1016709.
nsRefPtr<Promise> promise = Promise::Create(global, aRv);
if (aRv.Failed()) {
return nullptr;
}
- if (mMediaKeys != aMediaKeys) {
- mMediaKeys = aMediaKeys;
- }
- if (mDecoder) {
- mDecoder->SetCDMProxy(mMediaKeys->GetCDMProxy());
+ if (mMediaKeys == aMediaKeys) {
+ promise->MaybeResolve(JS::UndefinedHandleValue);
+ return promise.forget();
+ }
+ if (aMediaKeys && aMediaKeys->IsBoundToMediaElement()) {
+ promise->MaybeReject(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
+ return promise.forget();
+ }
+ if (mMediaKeys) {
+ // Existing MediaKeys object. Shut it down.
+ mMediaKeys->Shutdown();
+ mMediaKeys = nullptr;
+ }
+
+ mMediaKeys = aMediaKeys;
+ if (mMediaKeys) {
+ if (NS_FAILED(mMediaKeys->Bind(this))) {
+ promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+ mMediaKeys = nullptr;
+ return promise.forget();
+ }
+ if (mDecoder) {
+ mDecoder->SetCDMProxy(mMediaKeys->GetCDMProxy());
+ }
}
promise->MaybeResolve(JS::UndefinedHandleValue);
return promise.forget();
}
MediaWaitingFor
HTMLMediaElement::WaitingFor() const
{
@@ -4058,16 +4081,38 @@ HTMLMediaElement::DispatchEncrypted(cons
}
bool
HTMLMediaElement::IsEventAttributeName(nsIAtom* aName)
{
return aName == nsGkAtoms::onencrypted ||
nsGenericHTMLElement::IsEventAttributeName(aName);
}
+
+already_AddRefed<nsIPrincipal>
+HTMLMediaElement::GetTopLevelPrincipal()
+{
+ nsRefPtr<nsIPrincipal> principal;
+ nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(OwnerDoc()->GetParentObject());
+ nsCOMPtr<nsIDOMWindow> topWindow;
+ if (!window) {
+ return nullptr;
+ }
+ window->GetTop(getter_AddRefs(topWindow));
+ nsCOMPtr<nsPIDOMWindow> top = do_QueryInterface(topWindow);
+ if (!top) {
+ return nullptr;
+ }
+ nsIDocument* doc = top->GetExtantDoc();
+ if (!doc) {
+ return nullptr;
+ }
+ principal = doc->NodePrincipal();
+ return principal.forget();
+}
#endif // MOZ_EME
NS_IMETHODIMP HTMLMediaElement::WindowVolumeChanged()
{
SetVolumeInternal();
return NS_OK;
}
--- a/content/media/eme/CDMProxy.cpp
+++ b/content/media/eme/CDMProxy.cpp
@@ -31,87 +31,115 @@ CDMProxy::CDMProxy(dom::MediaKeys* aKeys
}
CDMProxy::~CDMProxy()
{
MOZ_COUNT_DTOR(CDMProxy);
}
void
-CDMProxy::Init(PromiseId aPromiseId)
+CDMProxy::Init(PromiseId aPromiseId,
+ const nsAString& aOrigin,
+ const nsAString& aTopLevelOrigin,
+ bool aInPrivateBrowsing)
{
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
- mNodeId = mKeys->GetNodeId();
- if (mNodeId.IsEmpty()) {
- RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
- return;
- }
- EME_LOG("Creating CDMProxy for origin='%s'", GetNodeId().get());
+ EME_LOG("CDMProxy::Init (%s, %s) %s",
+ NS_ConvertUTF16toUTF8(aOrigin).get(),
+ NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(),
+ (aInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"));
if (!mGMPThread) {
nsCOMPtr<mozIGeckoMediaPluginService> mps =
do_GetService("@mozilla.org/gecko-media-plugin-service;1");
if (!mps) {
RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
mps->GetThread(getter_AddRefs(mGMPThread));
if (!mGMPThread) {
RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
}
-
- nsRefPtr<nsIRunnable> task(NS_NewRunnableMethodWithArg<uint32_t>(this, &CDMProxy::gmp_Init, aPromiseId));
+ nsAutoPtr<InitData> data(new InitData());
+ data->mPromiseId = aPromiseId;
+ data->mOrigin = aOrigin;
+ data->mTopLevelOrigin = aTopLevelOrigin;
+ data->mInPrivateBrowsing = aInPrivateBrowsing;
+ nsRefPtr<nsIRunnable> task(
+ NS_NewRunnableMethodWithArg<nsAutoPtr<InitData>>(this,
+ &CDMProxy::gmp_Init,
+ data));
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
}
#ifdef DEBUG
bool
CDMProxy::IsOnGMPThread()
{
return NS_GetCurrentThread() == mGMPThread;
}
#endif
void
-CDMProxy::gmp_Init(uint32_t aPromiseId)
+CDMProxy::gmp_Init(nsAutoPtr<InitData> aData)
{
MOZ_ASSERT(IsOnGMPThread());
nsCOMPtr<mozIGeckoMediaPluginService> mps =
do_GetService("@mozilla.org/gecko-media-plugin-service;1");
if (!mps) {
- RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+ RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
+ nsresult rv = mps->GetNodeId(aData->mOrigin,
+ aData->mTopLevelOrigin,
+ aData->mInPrivateBrowsing,
+ mNodeId);
+ MOZ_ASSERT(!GetNodeId().IsEmpty());
+ if (NS_FAILED(rv)) {
+ RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+ return;
+ }
+
+ EME_LOG("CDMProxy::gmp_Init (%s, %s) %s NodeId=%s",
+ NS_ConvertUTF16toUTF8(aData->mOrigin).get(),
+ NS_ConvertUTF16toUTF8(aData->mTopLevelOrigin).get(),
+ (aData->mInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"),
+ GetNodeId().get());
+
nsTArray<nsCString> tags;
tags.AppendElement(NS_ConvertUTF16toUTF8(mKeySystem));
- nsresult rv = mps->GetGMPDecryptor(&tags, GetNodeId(), &mCDM);
+ rv = mps->GetGMPDecryptor(&tags, GetNodeId(), &mCDM);
if (NS_FAILED(rv) || !mCDM) {
- RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+ RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
} else {
mCallback = new CDMCallbackProxy(this);
mCDM->Init(mCallback);
- nsRefPtr<nsIRunnable> task(NS_NewRunnableMethodWithArg<uint32_t>(this, &CDMProxy::OnCDMCreated, aPromiseId));
+ nsRefPtr<nsIRunnable> task(
+ NS_NewRunnableMethodWithArg<uint32_t>(this,
+ &CDMProxy::OnCDMCreated,
+ aData->mPromiseId));
NS_DispatchToMainThread(task);
}
}
void
CDMProxy::OnCDMCreated(uint32_t aPromiseId)
{
MOZ_ASSERT(NS_IsMainThread());
if (mKeys.IsNull()) {
return;
}
- mKeys->OnCDMCreated(aPromiseId);
+ MOZ_ASSERT(!GetNodeId().IsEmpty());
+ mKeys->OnCDMCreated(aPromiseId, GetNodeId());
}
void
CDMProxy::CreateSession(dom::SessionType aSessionType,
PromiseId aPromiseId,
const nsAString& aInitDataType,
nsTArray<uint8_t>& aInitData)
{
@@ -298,19 +326,17 @@ CDMProxy::gmp_RemoveSession(nsAutoPtr<Se
void
CDMProxy::Shutdown()
{
MOZ_ASSERT(NS_IsMainThread());
mKeys.Clear();
// Note: This may end up being the last owning reference to the CDMProxy.
nsRefPtr<nsIRunnable> task(NS_NewRunnableMethod(this, &CDMProxy::gmp_Shutdown));
- if (mGMPThread) {
- mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
- }
+ mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
}
void
CDMProxy::gmp_Shutdown()
{
MOZ_ASSERT(IsOnGMPThread());
// Abort any pending decrypt jobs, to awaken any clients waiting on a job.
--- a/content/media/eme/CDMProxy.h
+++ b/content/media/eme/CDMProxy.h
@@ -43,17 +43,20 @@ public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CDMProxy)
// Main thread only.
CDMProxy(dom::MediaKeys* aKeys, const nsAString& aKeySystem);
// Main thread only.
// Loads the CDM corresponding to mKeySystem.
// Calls MediaKeys::OnCDMCreated() when the CDM is created.
- void Init(PromiseId aPromiseId);
+ void Init(PromiseId aPromiseId,
+ const nsAString& aOrigin,
+ const nsAString& aTopLevelOrigin,
+ bool aInPrivateBrowsing);
// Main thread only.
// Uses the CDM to create a key session.
// Calls MediaKeys::OnSessionActivated() when session is created.
// Assumes ownership of (Move()s) aInitData's contents.
void CreateSession(dom::SessionType aSessionType,
PromiseId aPromiseId,
const nsAString& aInitDataType,
@@ -160,18 +163,25 @@ public:
CDMCaps& Capabilites();
#ifdef DEBUG
bool IsOnGMPThread();
#endif
private:
+ struct InitData {
+ uint32_t mPromiseId;
+ nsAutoString mOrigin;
+ nsAutoString mTopLevelOrigin;
+ bool mInPrivateBrowsing;
+ };
+
// GMP thread only.
- void gmp_Init(uint32_t aPromiseId);
+ void gmp_Init(nsAutoPtr<InitData> aData);
// GMP thread only.
void gmp_Shutdown();
// Main thread only.
void OnCDMCreated(uint32_t aPromiseId);
struct CreateSessionData {
--- a/content/media/eme/MediaKeys.cpp
+++ b/content/media/eme/MediaKeys.cpp
@@ -20,16 +20,17 @@
#include "mozilla/WindowsVersion.h"
#endif
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MediaKeys,
+ mElement,
mParent,
mKeySessions,
mPromises,
mPendingSessions);
NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaKeys)
NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaKeys)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaKeys)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
@@ -219,58 +220,124 @@ MediaKeys::ResolvePromise(PromiseId aId)
already_AddRefed<Promise>
MediaKeys::Create(const GlobalObject& aGlobal,
const nsAString& aKeySystem,
ErrorResult& aRv)
{
// CDMProxy keeps MediaKeys alive until it resolves the promise and thus
// returns the MediaKeys object to JS.
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
- if (!window) {
+ if (!window || !window->GetExtantDoc()) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsRefPtr<MediaKeys> keys = new MediaKeys(window, aKeySystem);
- nsRefPtr<Promise> promise(keys->MakePromise(aRv));
+ return keys->Init(aRv);
+}
+
+already_AddRefed<Promise>
+MediaKeys::Init(ErrorResult& aRv)
+{
+ nsRefPtr<Promise> promise(MakePromise(aRv));
if (aRv.Failed()) {
return nullptr;
}
- if (!IsSupportedKeySystem(aKeySystem)) {
- aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
- return nullptr;
+ if (!IsSupportedKeySystem(mKeySystem)) {
+ promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+ return promise.forget();
}
- keys->mProxy = new CDMProxy(keys, aKeySystem);
+ mProxy = new CDMProxy(this, mKeySystem);
+
+ // Determine principal (at creation time) of the MediaKeys object.
+ nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(GetParentObject());
+ if (!sop) {
+ promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return promise.forget();
+ }
+ mPrincipal = sop->GetPrincipal();
+
+ // Determine principal of the "top-level" window; the principal of the
+ // page that will display in the URL bar.
+ nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(GetParentObject());
+ if (!window) {
+ promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return promise.forget();
+ }
+ nsCOMPtr<nsIDOMWindow> topWindow;
+ window->GetTop(getter_AddRefs(topWindow));
+ nsCOMPtr<nsPIDOMWindow> top = do_QueryInterface(topWindow);
+ if (!top || !top->GetExtantDoc()) {
+ promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return promise.forget();
+ }
+
+ mTopLevelPrincipal = top->GetExtantDoc()->NodePrincipal();
+
+ if (!mPrincipal || !mTopLevelPrincipal) {
+ NS_WARNING("Failed to get principals when creating MediaKeys");
+ promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return promise.forget();
+ }
+
+ nsAutoString origin;
+ nsresult rv = nsContentUtils::GetUTFOrigin(mPrincipal, origin);
+ if (NS_FAILED(rv)) {
+ promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return promise.forget();
+ }
+ nsAutoString topLevelOrigin;
+ rv = nsContentUtils::GetUTFOrigin(mTopLevelPrincipal, topLevelOrigin);
+ if (NS_FAILED(rv)) {
+ promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return promise.forget();
+ }
+
+ if (!window) {
+ promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return promise.forget();
+ }
+ nsIDocument* doc = window->GetExtantDoc();
+ const bool inPrivateBrowsing = nsContentUtils::IsInPrivateBrowsing(doc);
+
+ EME_LOG("MediaKeys::Create() (%s, %s), %s",
+ NS_ConvertUTF16toUTF8(origin).get(),
+ NS_ConvertUTF16toUTF8(topLevelOrigin).get(),
+ (inPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"));
// The CDMProxy's initialization is asynchronous. The MediaKeys is
// refcounted, and its instance is returned to JS by promise once
// it's been initialized. No external refs exist to the MediaKeys while
// we're waiting for the promise to be resolved, so we must hold a
// reference to the new MediaKeys object until it's been created,
// or its creation has failed. Store the id of the promise returned
// here, and hold a self-reference until that promise is resolved or
// rejected.
- MOZ_ASSERT(!keys->mCreatePromiseId, "Should only be created once!");
- keys->mCreatePromiseId = keys->StorePromise(promise);
- keys->AddRef();
- keys->mProxy->Init(keys->mCreatePromiseId);
+ MOZ_ASSERT(!mCreatePromiseId, "Should only be created once!");
+ mCreatePromiseId = StorePromise(promise);
+ AddRef();
+ mProxy->Init(mCreatePromiseId,
+ origin,
+ topLevelOrigin,
+ inPrivateBrowsing);
return promise.forget();
}
void
-MediaKeys::OnCDMCreated(PromiseId aId)
+MediaKeys::OnCDMCreated(PromiseId aId, const nsACString& aNodeId)
{
nsRefPtr<Promise> promise(RetrievePromise(aId));
if (!promise) {
NS_WARNING("MediaKeys tried to resolve a non-existent promise");
return;
}
+ mNodeId = aNodeId;
nsRefPtr<MediaKeys> keys(this);
promise->MaybeResolve(keys);
if (mCreatePromiseId == aId) {
Release();
}
}
already_AddRefed<MediaKeySession>
@@ -362,40 +429,67 @@ already_AddRefed<MediaKeySession>
MediaKeys::GetSession(const nsAString& aSessionId)
{
nsRefPtr<MediaKeySession> session;
mKeySessions.Get(aSessionId, getter_AddRefs(session));
return session.forget();
}
const nsCString&
-MediaKeys::GetNodeId()
+MediaKeys::GetNodeId() const
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ return mNodeId;
+}
+
+bool
+MediaKeys::IsBoundToMediaElement() const
{
MOZ_ASSERT(NS_IsMainThread());
-
- // TODO: Bug 1035637, return a combination of origin and URL bar origin.
+ return mElement != nullptr;
+}
- if (!mNodeId.IsEmpty()) {
- return mNodeId;
+nsresult
+MediaKeys::Bind(HTMLMediaElement* aElement)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (IsBoundToMediaElement()) {
+ return NS_ERROR_FAILURE;
}
- nsIPrincipal* principal = nullptr;
- nsCOMPtr<nsPIDOMWindow> pWindow = do_QueryInterface(GetParentObject());
- nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal =
- do_QueryInterface(pWindow);
- if (scriptPrincipal) {
- principal = scriptPrincipal->GetPrincipal();
- }
- nsAutoString id;
- if (principal && NS_SUCCEEDED(nsContentUtils::GetUTFOrigin(principal, id))) {
- CopyUTF16toUTF8(id, mNodeId);
- EME_LOG("EME Origin = '%s'", mNodeId.get());
+ mElement = aElement;
+ nsresult rv = CheckPrincipals();
+ if (NS_FAILED(rv)) {
+ mElement = nullptr;
+ return rv;
}
- return mNodeId;
+ return NS_OK;
+}
+
+nsresult
+MediaKeys::CheckPrincipals()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!IsBoundToMediaElement()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsRefPtr<nsIPrincipal> elementPrincipal(mElement->GetCurrentPrincipal());
+ nsRefPtr<nsIPrincipal> elementTopLevelPrincipal(mElement->GetTopLevelPrincipal());
+ if (!elementPrincipal ||
+ !mPrincipal ||
+ !elementPrincipal->Equals(mPrincipal) ||
+ !elementTopLevelPrincipal ||
+ !mTopLevelPrincipal ||
+ !elementTopLevelPrincipal->Equals(mTopLevelPrincipal)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
}
bool
CopyArrayBufferViewOrArrayBufferData(const ArrayBufferViewOrArrayBuffer& aBufferOrView,
nsTArray<uint8_t>& aOutData)
{
if (aBufferOrView.IsArrayBuffer()) {
const ArrayBuffer& buffer = aBufferOrView.GetAsArrayBuffer();
--- a/content/media/eme/MediaKeys.h
+++ b/content/media/eme/MediaKeys.h
@@ -13,24 +13,26 @@
#include "mozilla/Attributes.h"
#include "mozilla/RefPtr.h"
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsRefPtrHashtable.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/MediaKeysBinding.h"
#include "mozilla/dom/UnionTypes.h"
+#include "mozIGeckoMediaPluginService.h"
namespace mozilla {
class CDMProxy;
namespace dom {
class MediaKeySession;
+class HTMLMediaElement;
typedef nsRefPtrHashtable<nsStringHashKey, MediaKeySession> KeySessionHashMap;
typedef nsRefPtrHashtable<nsUint32HashKey, dom::Promise> PromiseHashMap;
typedef nsRefPtrHashtable<nsUint32HashKey, MediaKeySession> PendingKeySessionsHashMap;
typedef uint32_t PromiseId;
// Helper function to extract data coming in from JS in an
// (ArrayBuffer or ArrayBufferView) IDL typed function argument.
@@ -50,16 +52,18 @@ public:
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MediaKeys)
MediaKeys(nsPIDOMWindow* aParentWindow, const nsAString& aKeySystem);
nsPIDOMWindow* GetParentObject() const;
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+ nsresult Bind(HTMLMediaElement* aElement);
+
// Javascript: readonly attribute DOMString keySystem;
void GetKeySystem(nsString& retval) const;
// JavaScript: MediaKeys.createSession()
already_AddRefed<MediaKeySession> CreateSession(SessionType aSessionType,
ErrorResult& aRv);
// JavaScript: MediaKeys.SetServerCertificate()
@@ -77,17 +81,17 @@ public:
const nsAString& aKeySystem,
const Optional<nsAString>& aInitDataType,
const Optional<nsAString>& aContentType,
const Optional<nsAString>& aCapability);
already_AddRefed<MediaKeySession> GetSession(const nsAString& aSessionId);
// Called once a Create() operation succeeds.
- void OnCDMCreated(PromiseId aId);
+ void OnCDMCreated(PromiseId aId, const nsACString& aNodeId);
// Called when GenerateRequest or Load have been called on a MediaKeySession
// and we are waiting for its initialisation to finish.
void OnSessionPending(PromiseId aId, MediaKeySession* aSession);
// Called once a CreateSession succeeds.
void OnSessionCreated(PromiseId aId, const nsAString& aSessionId);
// Called once a LoadSession succeeds.
void OnSessionLoaded(PromiseId aId, bool aSuccess);
// Called once a session has closed.
@@ -102,34 +106,50 @@ public:
// promises to be resolved.
PromiseId StorePromise(Promise* aPromise);
// Reject promise with DOMException corresponding to aExceptionCode.
void RejectPromise(PromiseId aId, nsresult aExceptionCode);
// Resolves promise with "undefined".
void ResolvePromise(PromiseId aId);
- const nsCString& GetNodeId();
+ const nsCString& GetNodeId() const;
void Shutdown();
+ // Returns true if this MediaKeys has been bound to a media element.
+ bool IsBoundToMediaElement() const;
+
+ // Return NS_OK if the principals are the same as when the MediaKeys
+ // was created, failure otherwise.
+ nsresult CheckPrincipals();
+
private:
+ bool IsInPrivateBrowsing();
+ already_AddRefed<Promise> Init(ErrorResult& aRv);
+
// Removes promise from mPromises, and returns it.
already_AddRefed<Promise> RetrievePromise(PromiseId aId);
// Owning ref to proxy. The proxy has a weak reference back to the MediaKeys,
// and the MediaKeys destructor clears the proxy's reference to the MediaKeys.
nsRefPtr<CDMProxy> mProxy;
+ nsRefPtr<HTMLMediaElement> mElement;
+
nsCOMPtr<nsPIDOMWindow> mParent;
nsString mKeySystem;
nsCString mNodeId;
KeySessionHashMap mKeySessions;
PromiseHashMap mPromises;
PendingKeySessionsHashMap mPendingSessions;
PromiseId mCreatePromiseId;
+
+ nsRefPtr<nsIPrincipal> mPrincipal;
+ nsRefPtr<nsIPrincipal> mTopLevelPrincipal;
+
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_mediakeys_h__
--- a/content/media/gmp/GMPService.cpp
+++ b/content/media/gmp/GMPService.cpp
@@ -17,16 +17,17 @@
#include "nsNativeCharsetUtils.h"
#include "nsIConsoleService.h"
#include "mozilla/unused.h"
#include "GMPDecryptorParent.h"
#include "GMPAudioDecoderParent.h"
#include "nsComponentManagerUtils.h"
#include "mozilla/Preferences.h"
#include "runnable_utils.h"
+#include "VideoUtils.h"
#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
#include "mozilla/Sandbox.h"
#endif
namespace mozilla {
#ifdef LOG
#undef LOG
@@ -849,10 +850,33 @@ GeckoMediaPluginService::ReAddOnGMPThrea
MutexAutoLock lock(mMutex);
mPlugins.RemoveElement(aOld);
// Schedule aOld to be destroyed. We can't destroy it from here since we
// may be inside ActorDestroyed() for it.
NS_DispatchToCurrentThread(WrapRunnableNM(&Dummy, aOld));
}
+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")));
+
+ nsAutoCString salt;
+ nsresult rv = GenerateRandomPathName(salt, 32);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aOutId = salt;
+
+ // TODO: Store salt, so it can be retrieved in subsequent sessions.
+
+ return NS_OK;
+}
+
} // namespace gmp
} // namespace mozilla
--- a/content/media/gmp/GMPStorageParent.cpp
+++ b/content/media/gmp/GMPStorageParent.cpp
@@ -73,22 +73,17 @@ GetGMPStorageDir(nsIFile** aTempDir, con
return rv;
}
rv = tmpFile->AppendNative(nsDependentCString("mozilla-gmp-storage"));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
- // TODO: When aOrigin is the same node-id as the GMP sees in the child
- // process (a UUID or somesuch), we can just append it un-hashed here.
- // This should reduce the chance of hash collsions exposing data.
- nsAutoString nodeIdHash;
- nodeIdHash.AppendInt(HashString(aNodeId));
- rv = tmpFile->Append(nodeIdHash);
+ rv = tmpFile->AppendNative(aNodeId);
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;
}
--- a/content/media/gmp/mozIGeckoMediaPluginService.idl
+++ b/content/media/gmp/mozIGeckoMediaPluginService.idl
@@ -20,17 +20,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(88ade941-a423-48f9-aa3d-a383af8de4b8)]
+[scriptable, uuid(3d811f9f-e1f8-48a5-a385-3657a641ee76)]
interface mozIGeckoMediaPluginService : nsISupports
{
/**
* The GMP thread. Callable from any thread.
*/
readonly attribute nsIThread thread;
@@ -84,9 +84,16 @@ interface mozIGeckoMediaPluginService :
*/
void addPluginDirectory(in AString directory);
/**
* Remove a directory for gecko media plugins.
* @note Main-thread API.
*/
void removePluginDirectory(in AString directory);
+
+ /**
+ * Gets the NodeId for a (origin, urlbarOrigin, isInprivateBrowsing) tuple.
+ */
+ ACString getNodeId(in AString origin,
+ in AString topLevelOrigin,
+ in bool inPrivateBrowsingMode);
};