diff --git a/content/html/content/public/HTMLMediaElement.h b/content/html/content/public/HTMLMediaElement.h
index 8eefc1bf384f..5086bbf3ebe7 100755
--- a/content/html/content/public/HTMLMediaElement.h
+++ b/content/html/content/public/HTMLMediaElement.h
@@ -545,6 +545,11 @@ public:
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 GetTopLevelPrincipal();
+
#endif // MOZ_EME
bool MozAutoplayEnabled() const
diff --git a/content/html/content/src/HTMLMediaElement.cpp b/content/html/content/src/HTMLMediaElement.cpp
index a1cd0da764d8..1d125bfd3d8f 100755
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -3407,6 +3407,11 @@ void HTMLMediaElement::NotifyDecoderPrincipalChanged()
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)
@@ -4008,16 +4013,34 @@ HTMLMediaElement::SetMediaKeys(mozilla::dom::MediaKeys* aMediaKeys,
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
- // TODO: Need to shutdown existing MediaKeys instance? bug 1016709.
nsRefPtr promise = Promise::Create(global, aRv);
if (aRv.Failed()) {
return nullptr;
}
- if (mMediaKeys != aMediaKeys) {
- mMediaKeys = aMediaKeys;
+ if (mMediaKeys == aMediaKeys) {
+ promise->MaybeResolve(JS::UndefinedHandleValue);
+ return promise.forget();
}
- if (mDecoder) {
- mDecoder->SetCDMProxy(mMediaKeys->GetCDMProxy());
+ 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();
@@ -4063,6 +4086,28 @@ HTMLMediaElement::IsEventAttributeName(nsIAtom* aName)
return aName == nsGkAtoms::onencrypted ||
nsGenericHTMLElement::IsEventAttributeName(aName);
}
+
+already_AddRefed
+HTMLMediaElement::GetTopLevelPrincipal()
+{
+ nsRefPtr principal;
+ nsCOMPtr window = do_QueryInterface(OwnerDoc()->GetParentObject());
+ nsCOMPtr topWindow;
+ if (!window) {
+ return nullptr;
+ }
+ window->GetTop(getter_AddRefs(topWindow));
+ nsCOMPtr 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()
diff --git a/content/media/eme/CDMProxy.cpp b/content/media/eme/CDMProxy.cpp
index a9dae6a909d0..7c177e42640b 100644
--- a/content/media/eme/CDMProxy.cpp
+++ b/content/media/eme/CDMProxy.cpp
@@ -36,17 +36,18 @@ CDMProxy::~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 mps =
@@ -61,8 +62,15 @@ CDMProxy::Init(PromiseId aPromiseId)
return;
}
}
-
- nsRefPtr task(NS_NewRunnableMethodWithArg(this, &CDMProxy::gmp_Init, aPromiseId));
+ nsAutoPtr data(new InitData());
+ data->mPromiseId = aPromiseId;
+ data->mOrigin = aOrigin;
+ data->mTopLevelOrigin = aTopLevelOrigin;
+ data->mInPrivateBrowsing = aInPrivateBrowsing;
+ nsRefPtr task(
+ NS_NewRunnableMethodWithArg>(this,
+ &CDMProxy::gmp_Init,
+ data));
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
}
@@ -75,26 +83,45 @@ CDMProxy::IsOnGMPThread()
#endif
void
-CDMProxy::gmp_Init(uint32_t aPromiseId)
+CDMProxy::gmp_Init(nsAutoPtr aData)
{
MOZ_ASSERT(IsOnGMPThread());
nsCOMPtr 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 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 task(NS_NewRunnableMethodWithArg(this, &CDMProxy::OnCDMCreated, aPromiseId));
+ nsRefPtr task(
+ NS_NewRunnableMethodWithArg(this,
+ &CDMProxy::OnCDMCreated,
+ aData->mPromiseId));
NS_DispatchToMainThread(task);
}
}
@@ -106,7 +133,8 @@ CDMProxy::OnCDMCreated(uint32_t aPromiseId)
if (mKeys.IsNull()) {
return;
}
- mKeys->OnCDMCreated(aPromiseId);
+ MOZ_ASSERT(!GetNodeId().IsEmpty());
+ mKeys->OnCDMCreated(aPromiseId, GetNodeId());
}
void
@@ -303,9 +331,7 @@ CDMProxy::Shutdown()
mKeys.Clear();
// Note: This may end up being the last owning reference to the CDMProxy.
nsRefPtr task(NS_NewRunnableMethod(this, &CDMProxy::gmp_Shutdown));
- if (mGMPThread) {
- mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
- }
+ mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
}
void
diff --git a/content/media/eme/CDMProxy.h b/content/media/eme/CDMProxy.h
index 802ff5b058ba..690eca3384da 100644
--- a/content/media/eme/CDMProxy.h
+++ b/content/media/eme/CDMProxy.h
@@ -48,7 +48,10 @@ public:
// 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.
@@ -165,8 +168,15 @@ public:
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 aData);
// GMP thread only.
void gmp_Shutdown();
diff --git a/content/media/eme/MediaKeys.cpp b/content/media/eme/MediaKeys.cpp
index f9b15da29b2e..9872f5dfa8a1 100644
--- a/content/media/eme/MediaKeys.cpp
+++ b/content/media/eme/MediaKeys.cpp
@@ -25,6 +25,7 @@ namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MediaKeys,
+ mElement,
mParent,
mKeySessions,
mPromises,
@@ -224,23 +225,85 @@ MediaKeys::Create(const GlobalObject& aGlobal,
// CDMProxy keeps MediaKeys alive until it resolves the promise and thus
// returns the MediaKeys object to JS.
nsCOMPtr window = do_QueryInterface(aGlobal.GetAsSupports());
- if (!window) {
+ if (!window || !window->GetExtantDoc()) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsRefPtr keys = new MediaKeys(window, aKeySystem);
- nsRefPtr promise(keys->MakePromise(aRv));
+ return keys->Init(aRv);
+}
+
+already_AddRefed
+MediaKeys::Init(ErrorResult& aRv)
+{
+ nsRefPtr 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 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 window = do_QueryInterface(GetParentObject());
+ if (!window) {
+ promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return promise.forget();
+ }
+ nsCOMPtr topWindow;
+ window->GetTop(getter_AddRefs(topWindow));
+ nsCOMPtr 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
@@ -250,22 +313,26 @@ MediaKeys::Create(const GlobalObject& aGlobal,
// 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(RetrievePromise(aId));
if (!promise) {
NS_WARNING("MediaKeys tried to resolve a non-existent promise");
return;
}
+ mNodeId = aNodeId;
nsRefPtr keys(this);
promise->MaybeResolve(keys);
if (mCreatePromiseId == aId) {
@@ -367,32 +434,59 @@ MediaKeys::GetSession(const nsAString& aSessionId)
}
const nsCString&
-MediaKeys::GetNodeId()
+MediaKeys::GetNodeId() const
{
MOZ_ASSERT(NS_IsMainThread());
-
- // TODO: Bug 1035637, return a combination of origin and URL bar origin.
-
- if (!mNodeId.IsEmpty()) {
- return mNodeId;
- }
-
- nsIPrincipal* principal = nullptr;
- nsCOMPtr pWindow = do_QueryInterface(GetParentObject());
- nsCOMPtr 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());
- }
-
return mNodeId;
}
+bool
+MediaKeys::IsBoundToMediaElement() const
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ return mElement != nullptr;
+}
+
+nsresult
+MediaKeys::Bind(HTMLMediaElement* aElement)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (IsBoundToMediaElement()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mElement = aElement;
+ nsresult rv = CheckPrincipals();
+ if (NS_FAILED(rv)) {
+ mElement = nullptr;
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+MediaKeys::CheckPrincipals()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!IsBoundToMediaElement()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsRefPtr elementPrincipal(mElement->GetCurrentPrincipal());
+ nsRefPtr 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& aOutData)
diff --git a/content/media/eme/MediaKeys.h b/content/media/eme/MediaKeys.h
index 20b245a2c212..b7bc934d1f10 100644
--- a/content/media/eme/MediaKeys.h
+++ b/content/media/eme/MediaKeys.h
@@ -18,6 +18,7 @@
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/MediaKeysBinding.h"
#include "mozilla/dom/UnionTypes.h"
+#include "mozIGeckoMediaPluginService.h"
namespace mozilla {
@@ -26,6 +27,7 @@ class CDMProxy;
namespace dom {
class MediaKeySession;
+class HTMLMediaElement;
typedef nsRefPtrHashtable KeySessionHashMap;
typedef nsRefPtrHashtable PromiseHashMap;
@@ -55,6 +57,8 @@ public:
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+ nsresult Bind(HTMLMediaElement* aElement);
+
// Javascript: readonly attribute DOMString keySystem;
void GetKeySystem(nsString& retval) const;
@@ -82,7 +86,7 @@ public:
already_AddRefed 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);
@@ -107,12 +111,22 @@ public:
// 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 Init(ErrorResult& aRv);
+
// Removes promise from mPromises, and returns it.
already_AddRefed RetrievePromise(PromiseId aId);
@@ -120,6 +134,8 @@ private:
// and the MediaKeys destructor clears the proxy's reference to the MediaKeys.
nsRefPtr mProxy;
+ nsRefPtr mElement;
+
nsCOMPtr mParent;
nsString mKeySystem;
nsCString mNodeId;
@@ -127,6 +143,10 @@ private:
PromiseHashMap mPromises;
PendingKeySessionsHashMap mPendingSessions;
PromiseId mCreatePromiseId;
+
+ nsRefPtr mPrincipal;
+ nsRefPtr mTopLevelPrincipal;
+
};
} // namespace dom
diff --git a/content/media/gmp/GMPService.cpp b/content/media/gmp/GMPService.cpp
index 84be732287de..2a35af292860 100644
--- a/content/media/gmp/GMPService.cpp
+++ b/content/media/gmp/GMPService.cpp
@@ -22,6 +22,7 @@
#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
@@ -854,5 +855,28 @@ GeckoMediaPluginService::ReAddOnGMPThread(nsRefPtr& aOld)
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
diff --git a/content/media/gmp/GMPStorageParent.cpp b/content/media/gmp/GMPStorageParent.cpp
index 3539d9714e68..e54537d3c024 100644
--- a/content/media/gmp/GMPStorageParent.cpp
+++ b/content/media/gmp/GMPStorageParent.cpp
@@ -78,12 +78,7 @@ GetGMPStorageDir(nsIFile** aTempDir, const nsCString& aNodeId)
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;
}
diff --git a/content/media/gmp/mozIGeckoMediaPluginService.idl b/content/media/gmp/mozIGeckoMediaPluginService.idl
index 8c1c812d4dc0..a61735ce84e9 100644
--- a/content/media/gmp/mozIGeckoMediaPluginService.idl
+++ b/content/media/gmp/mozIGeckoMediaPluginService.idl
@@ -25,7 +25,7 @@ class GMPVideoHost;
[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
{
@@ -89,4 +89,11 @@ interface mozIGeckoMediaPluginService : nsISupports
* @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);
};