Remove EME (Encrypted Media Extensions) DRM support.

This resolves #102.
This commit is contained in:
wolfbeast
2016-08-12 10:37:10 +02:00
committed by roytam1
parent ad5ddb707d
commit 02112270b5
76 changed files with 2 additions and 6368 deletions
-7
View File
@@ -866,10 +866,3 @@ bin/libfreebl_32int64_3.so
#if defined(MOZ_ASAN) && defined(CLANG_CL)
@BINPATH@/clang_rt.asan_dynamic-i386.dll
#endif
; media
#ifdef MOZ_EME
@RESPATH@/gmp-clearkey/0.1/@DLL_PREFIX@clearkey@DLL_SUFFIX@
@RESPATH@/gmp-clearkey/0.1/clearkey.info
#endif
-18
View File
@@ -5290,23 +5290,6 @@ MOZ_ARG_DISABLE_BOOL(fmp4,
if test -n "$MOZ_FMP4"; then
AC_DEFINE(MOZ_FMP4)
MOZ_EME=1
fi;
dnl ========================================================
dnl = EME support
dnl ========================================================
MOZ_ARG_DISABLE_BOOL(eme,
[ --disable-eme Disable support for Encrypted Media Extensions],
MOZ_EME=,
MOZ_EME=1)
if test -n "$MOZ_EME"; then
if test -z "$MOZ_FMP4"; then
AC_MSG_ERROR([Encrypted Media Extension support requires Fragmented MP4 support])
fi
AC_DEFINE(MOZ_EME)
fi;
dnl ========================================================
@@ -8737,7 +8720,6 @@ AC_SUBST(MOZ_WEBM)
AC_SUBST(MOZ_WMF)
AC_SUBST(MOZ_FFMPEG)
AC_SUBST(MOZ_FMP4)
AC_SUBST(MOZ_EME)
AC_SUBST(MOZ_DIRECTSHOW)
AC_SUBST(MOZ_ANDROID_OMX)
AC_SUBST(MOZ_APPLEMEDIA)
-35
View File
@@ -112,10 +112,6 @@
#include "mozilla/dom/FeatureList.h"
#ifdef MOZ_EME
#include "mozilla/EMEUtils.h"
#endif
namespace mozilla {
namespace dom {
@@ -200,9 +196,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Navigator)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedResolveResults)
#ifdef MOZ_EME
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeySystemAccessManager)
#endif
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
@@ -317,12 +310,6 @@ Navigator::Invalidate()
mServiceWorkerContainer = nullptr;
#ifdef MOZ_EME
if (mMediaKeySystemAccessManager) {
mMediaKeySystemAccessManager->Shutdown();
mMediaKeySystemAccessManager = nullptr;
}
#endif
}
//*****************************************************************************
@@ -2630,27 +2617,5 @@ Navigator::GetUserAgent(nsPIDOMWindow* aWindow, nsIURI* aURI,
return siteSpecificUA->GetUserAgentForURIAndWindow(aURI, aWindow, aUserAgent);
}
#ifdef MOZ_EME
already_AddRefed<Promise>
Navigator::RequestMediaKeySystemAccess(const nsAString& aKeySystem,
const Optional<Sequence<MediaKeySystemOptions>>& aOptions,
ErrorResult& aRv)
{
nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
nsRefPtr<Promise> promise = Promise::Create(go, aRv);
if (aRv.Failed()) {
return nullptr;
}
if (!mMediaKeySystemAccessManager) {
mMediaKeySystemAccessManager = new MediaKeySystemAccessManager(mWindow);
}
mMediaKeySystemAccessManager->Request(promise, aKeySystem, aOptions);
return promise.forget();
}
#endif
} // namespace dom
} // namespace mozilla
-12
View File
@@ -18,9 +18,6 @@
#include "nsInterfaceHashtable.h"
#include "nsString.h"
#include "nsTArray.h"
#ifdef MOZ_EME
#include "mozilla/dom/MediaKeySystemAccessManager.h"
#endif
class nsPluginArray;
class nsMimeTypeArray;
@@ -330,15 +327,6 @@ public:
// any, else null.
static already_AddRefed<nsPIDOMWindow> GetWindowFromGlobal(JSObject* aGlobal);
#ifdef MOZ_EME
already_AddRefed<Promise>
RequestMediaKeySystemAccess(const nsAString& aKeySystem,
const Optional<Sequence<MediaKeySystemOptions>>& aOptions,
ErrorResult& aRv);
private:
nsRefPtr<MediaKeySystemAccessManager> mMediaKeySystemAccessManager;
#endif
private:
virtual ~Navigator();
-34
View File
@@ -4554,32 +4554,6 @@ nsDocument::SetScopeObject(nsIGlobalObject* aGlobal)
}
}
#ifdef MOZ_EME
static void
CheckIfContainsEMEContent(nsISupports* aSupports, void* aContainsEME)
{
nsCOMPtr<nsIDOMHTMLMediaElement> domMediaElem(do_QueryInterface(aSupports));
if (domMediaElem) {
nsCOMPtr<nsIContent> content(do_QueryInterface(domMediaElem));
MOZ_ASSERT(content, "aSupports is not a content");
HTMLMediaElement* mediaElem = static_cast<HTMLMediaElement*>(content.get());
bool* contains = static_cast<bool*>(aContainsEME);
if (mediaElem->GetMediaKeys()) {
*contains = true;
}
}
}
bool
nsDocument::ContainsEMEContent()
{
bool containsEME = false;
EnumerateActivityObservers(CheckIfContainsEMEContent,
static_cast<void*>(&containsEME));
return containsEME;
}
#endif // MOZ_EME
static void
CheckIfContainsMSEContent(nsISupports* aSupports, void* aContainsMSE)
{
@@ -8855,14 +8829,6 @@ nsDocument::CanSavePresentation(nsIRequest *aNewRequest)
}
#endif // MOZ_WEBRTC
#ifdef MOZ_EME
// Don't save presentations for documents containing EME content, so that
// CDMs reliably shutdown upon user navigation.
if (ContainsEMEContent()) {
return false;
}
#endif
// Don't save presentations for documents containing MSE content, to
// reduce memory usage.
if (ContainsMSEContent()) {
-4
View File
@@ -1473,10 +1473,6 @@ public:
js::ExpandoAndGeneration mExpandoAndGeneration;
#ifdef MOZ_EME
bool ContainsEMEContent();
#endif
bool ContainsMSEContent();
protected:
-201
View File
@@ -11,9 +11,6 @@
#include "mozilla/ArrayUtils.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/AsyncEventDispatcher.h"
#ifdef MOZ_EME
#include "mozilla/dom/MediaEncryptedEvent.h"
#endif
#include "base/basictypes.h"
#include "nsIDOMHTMLMediaElement.h"
@@ -451,9 +448,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTM
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTextTrackManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioTrackList)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVideoTrackList)
#ifdef MOZ_EME
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeys)
#endif
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTMLElement)
@@ -476,9 +470,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTMLE
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTextTrackManager)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAudioTrackList)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mVideoTrackList)
#ifdef MOZ_EME
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMediaKeys)
#endif
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLMediaElement)
@@ -642,14 +633,6 @@ void HTMLMediaElement::ShutdownDecoder()
void HTMLMediaElement::AbortExistingLoads()
{
#ifdef MOZ_EME
// If there is no existing decoder then we don't have anything to
// report. This prevents reporting the initial load from an
// empty video element as a failed EME load.
if (mDecoder) {
ReportEMETelemetry();
}
#endif
// Abort any already-running instance of the resource selection algorithm.
mLoadWaitStatus = NOT_WAITING;
@@ -1183,14 +1166,6 @@ nsresult HTMLMediaElement::LoadResource()
// Set the media element's CORS mode only when loading a resource
mCORSMode = AttrValueToCORSMode(GetParsedAttr(nsGkAtoms::crossorigin));
#ifdef MOZ_EME
if (mMediaKeys &&
!IsMediaStreamURI(mLoadingSrc) &&
Preferences::GetBool("media.eme.mse-only", true)) {
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
#endif
HTMLMediaElement* other = LookupMediaElementURITable(mLoadingSrc);
if (other && other->mDecoder) {
// Clone it.
@@ -1881,11 +1856,6 @@ HTMLMediaElement::CaptureStreamInternal(bool aFinishWhenEnded)
if (!window) {
return nullptr;
}
#ifdef MOZ_EME
if (ContainsRestrictedContent()) {
return nullptr;
}
#endif
OutputMediaStream* out = mOutputStreams.AppendElement();
out->mStream = DOMMediaStream::CreateTrackUnionStream(window);
nsRefPtr<nsIPrincipal> principal = GetCurrentPrincipal();
@@ -2587,20 +2557,6 @@ nsresult HTMLMediaElement::BindToTree(nsIDocument* aDocument, nsIContent* aParen
return rv;
}
#ifdef MOZ_EME
void
HTMLMediaElement::ReportEMETelemetry()
{
// Report telemetry for EME videos when a page is unloaded.
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
if (mIsEncrypted && Preferences::GetBool("media.eme.enabled")) {
Telemetry::Accumulate(Telemetry::VIDEO_EME_PLAY_SUCCESS, mLoadedDataFired);
LOG(PR_LOG_DEBUG, ("%p VIDEO_EME_PLAY_SUCCESS = %s",
this, mLoadedDataFired ? "true" : "false"));
}
}
#endif
void
HTMLMediaElement::ReportMSETelemetry()
{
@@ -2845,12 +2801,6 @@ nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder,
return rv;
}
#ifdef MOZ_EME
if (mMediaKeys) {
mDecoder->SetCDMProxy(mMediaKeys->GetCDMProxy());
}
#endif
// Decoder successfully created, the decoder now owns the MediaResource
// which owns the channel.
mChannel = nullptr;
@@ -3909,25 +3859,8 @@ void HTMLMediaElement::SuspendOrResumeElement(bool aPauseElement, bool aSuspendE
if (aPauseElement) {
if (mMediaSource) {
ReportMSETelemetry();
#ifdef MOZ_EME
ReportEMETelemetry();
#endif
}
#ifdef MOZ_EME
// For EME content, force destruction of the CDM client (and CDM
// instance if this is the last client for that CDM instance) and
// the CDM's decoder. This ensures the CDM gets reliable and prompt
// shutdown notifications, as it may have book-keeping it needs
// to do on shutdown.
if (mMediaKeys) {
mMediaKeys->Shutdown();
mMediaKeys = nullptr;
if (mDecoder) {
ShutdownDecoder();
}
}
#endif
if (mDecoder) {
mDecoder->Pause();
mDecoder->Suspend();
@@ -3936,9 +3869,6 @@ void HTMLMediaElement::SuspendOrResumeElement(bool aPauseElement, bool aSuspendE
}
mEventDeliveryPaused = aSuspendEvents;
} else {
#ifdef MOZ_EME
MOZ_ASSERT(!mMediaKeys);
#endif
if (mDecoder) {
mDecoder->Resume(false);
if (!mPaused && !mDecoder->IsEndedOrShutdown()) {
@@ -4496,137 +4426,6 @@ NS_IMETHODIMP HTMLMediaElement::CanPlayChanged(int32_t canPlay)
return NS_OK;
}
#ifdef MOZ_EME
MediaKeys*
HTMLMediaElement::GetMediaKeys() const
{
return mMediaKeys;
}
bool
HTMLMediaElement::ContainsRestrictedContent()
{
return GetMediaKeys() != nullptr;
}
already_AddRefed<Promise>
HTMLMediaElement::SetMediaKeys(mozilla::dom::MediaKeys* aMediaKeys,
ErrorResult& aRv)
{
if (MozAudioCaptured()) {
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return nullptr;
}
nsCOMPtr<nsIGlobalObject> global =
do_QueryInterface(OwnerDoc()->GetInnerWindow());
if (!global) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
nsRefPtr<Promise> promise = Promise::Create(global, aRv);
if (aRv.Failed()) {
return nullptr;
}
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;
}
if (mDecoder &&
!mMediaSource &&
Preferences::GetBool("media.eme.mse-only", true)) {
ShutdownDecoder();
promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return promise.forget();
}
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();
}
EventHandlerNonNull*
HTMLMediaElement::GetOnencrypted()
{
EventListenerManager *elm = GetExistingListenerManager();
return elm ? elm->GetEventHandler(nsGkAtoms::onencrypted, EmptyString())
: nullptr;
}
void
HTMLMediaElement::SetOnencrypted(EventHandlerNonNull* handler)
{
EventListenerManager *elm = GetOrCreateListenerManager();
if (elm) {
elm->SetEventHandler(nsGkAtoms::onencrypted, EmptyString(), handler);
}
}
void
HTMLMediaElement::DispatchEncrypted(const nsTArray<uint8_t>& aInitData,
const nsAString& aInitDataType)
{
nsRefPtr<MediaEncryptedEvent> event;
if (IsCORSSameOrigin()) {
event = MediaEncryptedEvent::Constructor(this, aInitDataType, aInitData);
} else {
event = MediaEncryptedEvent::Constructor(this);
}
nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
new AsyncEventDispatcher(this, event);
asyncDispatcher->PostDOMEvent();
}
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();
-33
View File
@@ -20,9 +20,6 @@
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/TextTrackManager.h"
#include "MediaDecoder.h"
#ifdef MOZ_EME
#include "mozilla/dom/MediaKeys.h"
#endif
#include "nsGkAtoms.h"
// X.h on Linux #defines CurrentTime as 0L, so we have to #undef it here.
@@ -552,28 +549,6 @@ public:
// XPCOM MozPreservesPitch() is OK
#ifdef MOZ_EME
MediaKeys* GetMediaKeys() const;
already_AddRefed<Promise> SetMediaKeys(MediaKeys* mediaKeys,
ErrorResult& aRv);
mozilla::dom::EventHandlerNonNull* GetOnencrypted();
void SetOnencrypted(mozilla::dom::EventHandlerNonNull* listener);
void DispatchEncrypted(const nsTArray<uint8_t>& aInitData,
const nsAString& aInitDataType) override;
bool IsEventAttributeName(nsIAtom* aName) override;
// Returns the principal of the "top level" document; the origin displayed
// in the URL bar of the browser window.
already_AddRefed<nsIPrincipal> GetTopLevelPrincipal();
bool ContainsRestrictedContent();
#endif // MOZ_EME
bool MozAutoplayEnabled() const
{
return mAutoplayEnabled;
@@ -982,9 +957,6 @@ protected:
return isPaused;
}
#ifdef MOZ_EME
void ReportEMETelemetry();
#endif
void ReportMSETelemetry();
// Check the permissions for audiochannel.
@@ -1190,11 +1162,6 @@ protected:
// Timer used for updating progress events
nsCOMPtr<nsITimer> mProgressTimer;
#ifdef MOZ_EME
// Encrypted Media Extension media keys.
nsRefPtr<MediaKeys> mMediaKeys;
#endif
// Stores the time at the start of the current 'played' range.
double mCurrentPlayRangeStart;
-7
View File
@@ -25,9 +25,6 @@ class ReentrantMonitor;
class VideoFrameContainer;
class TimedMetadata;
class MediaDecoderOwner;
#ifdef MOZ_EME
class CDMProxy;
#endif
typedef nsDataHashtable<nsCStringHashKey, nsCString> MetadataTags;
@@ -159,10 +156,6 @@ public:
AbstractMediaDecoder* mDecoder;
};
#ifdef MOZ_EME
virtual nsresult SetCDMProxy(CDMProxy* aProxy) { return NS_ERROR_NOT_IMPLEMENTED; }
virtual CDMProxy* GetCDMProxy() { return nullptr; }
#endif
};
class MetadataContainer
-21
View File
@@ -1776,27 +1776,6 @@ bool MediaDecoder::CanPlayThrough()
return stats.mDownloadPosition > stats.mPlaybackPosition + readAheadMargin;
}
#ifdef MOZ_EME
nsresult
MediaDecoder::SetCDMProxy(CDMProxy* aProxy)
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
MOZ_ASSERT(NS_IsMainThread());
mProxy = aProxy;
// Awaken any readers waiting for the proxy.
NotifyWaitingForResourcesStatusChanged();
return NS_OK;
}
CDMProxy*
MediaDecoder::GetCDMProxy()
{
GetReentrantMonitor().AssertCurrentThreadIn();
MOZ_ASSERT(OnDecodeThread() || NS_IsMainThread());
return mProxy;
}
#endif
#ifdef MOZ_RAW
bool
MediaDecoder::IsRawEnabled()
-15
View File
@@ -196,9 +196,6 @@ destroying the MediaDecoder object.
#include "MediaStreamGraph.h"
#include "AbstractMediaDecoder.h"
#include "necko-config.h"
#ifdef MOZ_EME
#include "mozilla/CDMProxy.h"
#endif
class nsIStreamListener;
class nsIPrincipal;
@@ -853,14 +850,6 @@ public:
// The decoder monitor must be held.
bool IsLogicallyPlaying();
#ifdef MOZ_EME
// This takes the decoder monitor.
virtual nsresult SetCDMProxy(CDMProxy* aProxy) override;
// Decoder monitor must be held.
virtual CDMProxy* GetCDMProxy() override;
#endif
#ifdef MOZ_RAW
static bool IsRawEnabled();
#endif
@@ -1117,10 +1106,6 @@ private:
// change. Explicitly private for force access via GetReentrantMonitor.
ReentrantMonitor mReentrantMonitor;
#ifdef MOZ_EME
nsRefPtr<CDMProxy> mProxy;
#endif
protected:
// Data about MediaStreams that are being fed by this decoder.
nsTArray<OutputStreamData> mOutputStreams;
-8
View File
@@ -133,14 +133,6 @@ public:
// Called by the media decoder object, on the main thread,
// when the connection between Rtsp server and client gets lost.
virtual void ResetConnectionState() = 0;
#ifdef MOZ_EME
// Dispatches a "encrypted" event to the HTMLMediaElement, with the
// provided init data.
// Main thread only.
virtual void DispatchEncrypted(const nsTArray<uint8_t>& aInitData,
const nsAString& aInitDataType) = 0;
#endif
};
}
-319
View File
@@ -1,319 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#include "mozilla/CDMCallbackProxy.h"
#include "mozilla/CDMProxy.h"
#include "nsString.h"
#include "mozilla/dom/MediaKeys.h"
#include "mozilla/dom/MediaKeySession.h"
#include "mozIGoannaMediaPluginService.h"
#include "nsContentCID.h"
#include "nsServiceManagerUtils.h"
#include "MainThreadUtils.h"
#include "mozilla/EMEUtils.h"
namespace mozilla {
CDMCallbackProxy::CDMCallbackProxy(CDMProxy* aProxy)
: mProxy(aProxy)
{
}
class SetSessionIdTask : public nsRunnable {
public:
SetSessionIdTask(CDMProxy* aProxy,
uint32_t aToken,
const nsCString& aSessionId)
: mProxy(aProxy)
, mToken(aToken)
, mSid(NS_ConvertUTF8toUTF16(aSessionId))
{
}
NS_IMETHOD Run() {
mProxy->OnSetSessionId(mToken, mSid);
return NS_OK;
}
nsRefPtr<CDMProxy> mProxy;
uint32_t mToken;
nsString mSid;
};
void
CDMCallbackProxy::SetSessionId(uint32_t aToken,
const nsCString& aSessionId)
{
MOZ_ASSERT(mProxy->IsOnGMPThread());
nsRefPtr<nsIRunnable> task(new SetSessionIdTask(mProxy,
aToken,
aSessionId));
NS_DispatchToMainThread(task);
}
class LoadSessionTask : public nsRunnable {
public:
LoadSessionTask(CDMProxy* aProxy,
uint32_t aPromiseId,
bool aSuccess)
: mProxy(aProxy)
, mPid(aPromiseId)
, mSuccess(aSuccess)
{
}
NS_IMETHOD Run() {
mProxy->OnResolveLoadSessionPromise(mPid, mSuccess);
return NS_OK;
}
nsRefPtr<CDMProxy> mProxy;
dom::PromiseId mPid;
bool mSuccess;
};
void
CDMCallbackProxy::ResolveLoadSessionPromise(uint32_t aPromiseId, bool aSuccess)
{
MOZ_ASSERT(mProxy->IsOnGMPThread());
nsRefPtr<nsIRunnable> task(new LoadSessionTask(mProxy,
aPromiseId,
aSuccess));
NS_DispatchToMainThread(task);
}
void
CDMCallbackProxy::ResolvePromise(uint32_t aPromiseId)
{
MOZ_ASSERT(mProxy->IsOnGMPThread());
// Note: CDMProxy proxies this from non-main threads to main thread.
mProxy->ResolvePromise(aPromiseId);
}
class RejectPromiseTask : public nsRunnable {
public:
RejectPromiseTask(CDMProxy* aProxy,
uint32_t aPromiseId,
nsresult aException,
const nsCString& aMessage)
: mProxy(aProxy)
, mPid(aPromiseId)
, mException(aException)
, mMsg(NS_ConvertUTF8toUTF16(aMessage))
{
}
NS_IMETHOD Run() {
mProxy->OnRejectPromise(mPid, mException, mMsg);
return NS_OK;
}
nsRefPtr<CDMProxy> mProxy;
dom::PromiseId mPid;
nsresult mException;
nsString mMsg;
};
void
CDMCallbackProxy::RejectPromise(uint32_t aPromiseId,
nsresult aException,
const nsCString& aMessage)
{
MOZ_ASSERT(mProxy->IsOnGMPThread());
nsRefPtr<nsIRunnable> task;
task = new RejectPromiseTask(mProxy,
aPromiseId,
aException,
aMessage);
NS_DispatchToMainThread(task);
}
class SessionMessageTask : public nsRunnable {
public:
SessionMessageTask(CDMProxy* aProxy,
const nsCString& aSessionId,
GMPSessionMessageType aMessageType,
const nsTArray<uint8_t>& aMessage)
: mProxy(aProxy)
, mSid(NS_ConvertUTF8toUTF16(aSessionId))
, mMsgType(aMessageType)
{
mMsg.AppendElements(aMessage);
}
NS_IMETHOD Run() {
mProxy->OnSessionMessage(mSid, mMsgType, mMsg);
return NS_OK;
}
nsRefPtr<CDMProxy> mProxy;
dom::PromiseId mPid;
nsString mSid;
GMPSessionMessageType mMsgType;
nsTArray<uint8_t> mMsg;
};
void
CDMCallbackProxy::SessionMessage(const nsCString& aSessionId,
GMPSessionMessageType aMessageType,
const nsTArray<uint8_t>& aMessage)
{
MOZ_ASSERT(mProxy->IsOnGMPThread());
nsRefPtr<nsIRunnable> task;
task = new SessionMessageTask(mProxy,
aSessionId,
aMessageType,
aMessage);
NS_DispatchToMainThread(task);
}
class ExpirationChangeTask : public nsRunnable {
public:
ExpirationChangeTask(CDMProxy* aProxy,
const nsCString& aSessionId,
GMPTimestamp aExpiryTime)
: mProxy(aProxy)
, mSid(NS_ConvertUTF8toUTF16(aSessionId))
, mTimestamp(aExpiryTime)
{}
NS_IMETHOD Run() {
mProxy->OnExpirationChange(mSid, mTimestamp);
return NS_OK;
}
nsRefPtr<CDMProxy> mProxy;
nsString mSid;
GMPTimestamp mTimestamp;
};
void
CDMCallbackProxy::ExpirationChange(const nsCString& aSessionId,
GMPTimestamp aExpiryTime)
{
MOZ_ASSERT(mProxy->IsOnGMPThread());
nsRefPtr<nsIRunnable> task;
task = new ExpirationChangeTask(mProxy,
aSessionId,
aExpiryTime);
NS_DispatchToMainThread(task);
}
void
CDMCallbackProxy::SessionClosed(const nsCString& aSessionId)
{
MOZ_ASSERT(mProxy->IsOnGMPThread());
nsRefPtr<nsIRunnable> task;
task = NS_NewRunnableMethodWithArg<nsString>(mProxy,
&CDMProxy::OnSessionClosed,
NS_ConvertUTF8toUTF16(aSessionId));
NS_DispatchToMainThread(task);
}
class SessionErrorTask : public nsRunnable {
public:
SessionErrorTask(CDMProxy* aProxy,
const nsCString& aSessionId,
nsresult aException,
uint32_t aSystemCode,
const nsCString& aMessage)
: mProxy(aProxy)
, mSid(NS_ConvertUTF8toUTF16(aSessionId))
, mException(aException)
, mSystemCode(aSystemCode)
, mMsg(NS_ConvertUTF8toUTF16(aMessage))
{}
NS_IMETHOD Run() {
mProxy->OnSessionError(mSid, mException, mSystemCode, mMsg);
return NS_OK;
}
nsRefPtr<CDMProxy> mProxy;
dom::PromiseId mPid;
nsString mSid;
nsresult mException;
uint32_t mSystemCode;
nsString mMsg;
};
void
CDMCallbackProxy::SessionError(const nsCString& aSessionId,
nsresult aException,
uint32_t aSystemCode,
const nsCString& aMessage)
{
MOZ_ASSERT(mProxy->IsOnGMPThread());
nsRefPtr<nsIRunnable> task;
task = new SessionErrorTask(mProxy,
aSessionId,
aException,
aSystemCode,
aMessage);
NS_DispatchToMainThread(task);
}
void
CDMCallbackProxy::KeyStatusChanged(const nsCString& aSessionId,
const nsTArray<uint8_t>& aKeyId,
GMPMediaKeyStatus aStatus)
{
MOZ_ASSERT(mProxy->IsOnGMPThread());
bool keyStatusesChange = false;
{
CDMCaps::AutoLock caps(mProxy->Capabilites());
keyStatusesChange = caps.SetKeyStatus(aKeyId,
NS_ConvertUTF8toUTF16(aSessionId),
aStatus);
}
if (keyStatusesChange) {
nsRefPtr<nsIRunnable> task;
task = NS_NewRunnableMethodWithArg<nsString>(mProxy,
&CDMProxy::OnKeyStatusesChange,
NS_ConvertUTF8toUTF16(aSessionId));
NS_DispatchToMainThread(task);
}
}
void
CDMCallbackProxy::SetCaps(uint64_t aCaps)
{
MOZ_ASSERT(mProxy->IsOnGMPThread());
CDMCaps::AutoLock caps(mProxy->Capabilites());
caps.SetCaps(aCaps);
}
void
CDMCallbackProxy::Decrypted(uint32_t aId,
GMPErr aResult,
const nsTArray<uint8_t>& aDecryptedData)
{
MOZ_ASSERT(mProxy->IsOnGMPThread());
mProxy->gmp_Decrypted(aId, aResult, aDecryptedData);
}
void
CDMCallbackProxy::Terminated()
{
MOZ_ASSERT(mProxy->IsOnGMPThread());
nsRefPtr<nsIRunnable> task = NS_NewRunnableMethod(mProxy, &CDMProxy::Terminated);
NS_DispatchToMainThread(task);
}
} // namespace mozilla
-70
View File
@@ -1,70 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 CDMCallbackProxy_h_
#define CDMCallbackProxy_h_
#include "mozilla/CDMProxy.h"
#include "gmp-decryption.h"
#include "GMPDecryptorProxy.h"
namespace mozilla {
// Proxies call backs from the CDM on the GMP thread back to the MediaKeys
// object on the main thread.
class CDMCallbackProxy : public GMPDecryptorProxyCallback {
public:
virtual void SetSessionId(uint32_t aCreateSessionToken,
const nsCString& aSessionId) override;
virtual void ResolveLoadSessionPromise(uint32_t aPromiseId,
bool aSuccess) override;
virtual void ResolvePromise(uint32_t aPromiseId) override;
virtual void RejectPromise(uint32_t aPromiseId,
nsresult aException,
const nsCString& aSessionId) override;
virtual void SessionMessage(const nsCString& aSessionId,
GMPSessionMessageType aMessageType,
const nsTArray<uint8_t>& aMessage) override;
virtual void ExpirationChange(const nsCString& aSessionId,
GMPTimestamp aExpiryTime) override;
virtual void SessionClosed(const nsCString& aSessionId) override;
virtual void SessionError(const nsCString& aSessionId,
nsresult aException,
uint32_t aSystemCode,
const nsCString& aMessage) override;
virtual void KeyStatusChanged(const nsCString& aSessionId,
const nsTArray<uint8_t>& aKeyId,
GMPMediaKeyStatus aStatus) override;
virtual void SetCaps(uint64_t aCaps) override;
virtual void Decrypted(uint32_t aId,
GMPErr aResult,
const nsTArray<uint8_t>& aDecryptedData) override;
virtual void Terminated() override;
~CDMCallbackProxy() {}
private:
friend class CDMProxy;
explicit CDMCallbackProxy(CDMProxy* aProxy);
// Warning: Weak ref.
CDMProxy* mProxy;
};
} // namespace mozilla
#endif // CDMCallbackProxy_h_
-236
View File
@@ -1,236 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#include "mozilla/CDMCaps.h"
#include "mozilla/EMEUtils.h"
#include "nsThreadUtils.h"
#include "SamplesWaitingForKey.h"
namespace mozilla {
CDMCaps::CDMCaps()
: mMonitor("CDMCaps")
, mCaps(0)
{
}
CDMCaps::~CDMCaps()
{
}
void
CDMCaps::Lock()
{
mMonitor.Lock();
}
void
CDMCaps::Unlock()
{
mMonitor.Unlock();
}
bool
CDMCaps::HasCap(uint64_t aCap)
{
mMonitor.AssertCurrentThreadOwns();
return (mCaps & aCap) == aCap;
}
CDMCaps::AutoLock::AutoLock(CDMCaps& aInstance)
: mData(aInstance)
{
mData.Lock();
}
CDMCaps::AutoLock::~AutoLock()
{
mData.Unlock();
}
#ifdef PR_LOGGING
static void
TestCap(uint64_t aFlag,
uint64_t aCaps,
const nsACString& aCapName,
nsACString& aCapStr)
{
if (!(aFlag & aCaps)) {
return;
}
if (!aCapStr.IsEmpty()) {
aCapStr.AppendLiteral(",");
}
aCapStr.Append(aCapName);
}
nsCString
CapsToString(uint64_t aCaps)
{
nsCString capsStr;
TestCap(GMP_EME_CAP_DECRYPT_AUDIO, aCaps, NS_LITERAL_CSTRING("DecryptAudio"), capsStr);
TestCap(GMP_EME_CAP_DECRYPT_VIDEO, aCaps, NS_LITERAL_CSTRING("DecryptVideo"), capsStr);
TestCap(GMP_EME_CAP_DECRYPT_AND_DECODE_AUDIO, aCaps, NS_LITERAL_CSTRING("DecryptAndDecodeAudio"), capsStr);
TestCap(GMP_EME_CAP_DECRYPT_AND_DECODE_VIDEO, aCaps, NS_LITERAL_CSTRING("DecryptAndDecodeVideo"), capsStr);
return capsStr;
}
#endif // PR_LOGGING
void
CDMCaps::AutoLock::SetCaps(uint64_t aCaps)
{
EME_LOG("SetCaps() %s", CapsToString(aCaps).get());
mData.mMonitor.AssertCurrentThreadOwns();
mData.mCaps = aCaps;
for (size_t i = 0; i < mData.mWaitForCaps.Length(); i++) {
NS_DispatchToMainThread(mData.mWaitForCaps[i], NS_DISPATCH_NORMAL);
}
mData.mWaitForCaps.Clear();
}
void
CDMCaps::AutoLock::CallOnMainThreadWhenCapsAvailable(nsIRunnable* aContinuation)
{
mData.mMonitor.AssertCurrentThreadOwns();
if (mData.mCaps) {
NS_DispatchToMainThread(aContinuation, NS_DISPATCH_NORMAL);
MOZ_ASSERT(mData.mWaitForCaps.IsEmpty());
} else {
mData.mWaitForCaps.AppendElement(aContinuation);
}
}
bool
CDMCaps::AutoLock::IsKeyUsable(const CencKeyId& aKeyId)
{
mData.mMonitor.AssertCurrentThreadOwns();
const auto& keys = mData.mKeyStatuses;
for (size_t i = 0; i < keys.Length(); i++) {
if (keys[i].mId != aKeyId) {
continue;
}
if (keys[i].mStatus == kGMPUsable ||
keys[i].mStatus == kGMPOutputDownscaled) {
return true;
}
}
return false;
}
bool
CDMCaps::AutoLock::SetKeyStatus(const CencKeyId& aKeyId,
const nsString& aSessionId,
GMPMediaKeyStatus aStatus)
{
mData.mMonitor.AssertCurrentThreadOwns();
KeyStatus key(aKeyId, aSessionId, aStatus);
auto index = mData.mKeyStatuses.IndexOf(key);
if (aStatus == kGMPUnknown) {
// Return true if the element is found to notify key changes.
return mData.mKeyStatuses.RemoveElement(key);
}
if (index != mData.mKeyStatuses.NoIndex) {
if (mData.mKeyStatuses[index].mStatus == aStatus) {
return false;
}
auto oldStatus = mData.mKeyStatuses[index].mStatus;
mData.mKeyStatuses[index].mStatus = aStatus;
if (oldStatus == kGMPUsable || oldStatus == kGMPOutputDownscaled) {
return true;
}
} else {
mData.mKeyStatuses.AppendElement(key);
}
// Both kGMPUsable and kGMPOutputDownscaled are treated able to decrypt.
// We don't need to notify when transition happens between kGMPUsable and
// kGMPOutputDownscaled. Only call NotifyUsable() when we are going from
// ![kGMPUsable|kGMPOutputDownscaled] to [kGMPUsable|kGMPOutputDownscaled]
if (aStatus != kGMPUsable && aStatus != kGMPOutputDownscaled) {
return true;
}
auto& waiters = mData.mWaitForKeys;
size_t i = 0;
while (i < waiters.Length()) {
auto& w = waiters[i];
if (w.mKeyId == aKeyId) {
w.mListener->NotifyUsable(aKeyId);
waiters.RemoveElementAt(i);
} else {
i++;
}
}
return true;
}
void
CDMCaps::AutoLock::NotifyWhenKeyIdUsable(const CencKeyId& aKey,
SamplesWaitingForKey* aListener)
{
mData.mMonitor.AssertCurrentThreadOwns();
MOZ_ASSERT(!IsKeyUsable(aKey));
MOZ_ASSERT(aListener);
mData.mWaitForKeys.AppendElement(WaitForKeys(aKey, aListener));
}
bool
CDMCaps::AutoLock::AreCapsKnown()
{
mData.mMonitor.AssertCurrentThreadOwns();
return mData.mCaps != 0;
}
bool
CDMCaps::AutoLock::CanDecryptAndDecodeAudio()
{
return mData.HasCap(GMP_EME_CAP_DECRYPT_AND_DECODE_AUDIO);
}
bool
CDMCaps::AutoLock::CanDecryptAndDecodeVideo()
{
return mData.HasCap(GMP_EME_CAP_DECRYPT_AND_DECODE_VIDEO);
}
bool
CDMCaps::AutoLock::CanDecryptAudio()
{
return mData.HasCap(GMP_EME_CAP_DECRYPT_AUDIO);
}
bool
CDMCaps::AutoLock::CanDecryptVideo()
{
return mData.HasCap(GMP_EME_CAP_DECRYPT_VIDEO);
}
void
CDMCaps::AutoLock::GetKeyStatusesForSession(const nsAString& aSessionId,
nsTArray<KeyStatus>& aOutKeyStatuses)
{
for (size_t i = 0; i < mData.mKeyStatuses.Length(); i++) {
const auto& key = mData.mKeyStatuses[i];
if (key.mSessionId.Equals(aSessionId)) {
aOutKeyStatuses.AppendElement(key);
}
}
}
void
CDMCaps::AutoLock::GetSessionIdsForKeyId(const CencKeyId& aKeyId,
nsTArray<nsCString>& aOutSessionIds)
{
for (const auto& keyStatus : mData.mKeyStatuses) {
if (keyStatus.mId == aKeyId) {
aOutSessionIds.AppendElement(NS_ConvertUTF16toUTF8(keyStatus.mSessionId));
}
}
}
} // namespace mozilla
-125
View File
@@ -1,125 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 CDMCaps_h_
#define CDMCaps_h_
#include "nsString.h"
#include "nsAutoPtr.h"
#include "mozilla/Monitor.h"
#include "nsIThread.h"
#include "nsTArray.h"
#include "mozilla/Attributes.h"
#include "SamplesWaitingForKey.h"
#include "gmp-decryption.h"
namespace mozilla {
// CDM capabilities; what keys a CDMProxy can use, and whether it can decrypt, or
// decrypt-and-decode on a per stream basis. Must be locked to access state.
class CDMCaps {
public:
CDMCaps();
~CDMCaps();
struct KeyStatus {
KeyStatus(const CencKeyId& aId,
const nsString& aSessionId,
GMPMediaKeyStatus aStatus)
: mId(aId)
, mSessionId(aSessionId)
, mStatus(aStatus)
{}
KeyStatus(const KeyStatus& aOther)
: mId(aOther.mId)
, mSessionId(aOther.mSessionId)
, mStatus(aOther.mStatus)
{}
bool operator==(const KeyStatus& aOther) const {
return mId == aOther.mId &&
mSessionId == aOther.mSessionId;
};
CencKeyId mId;
nsString mSessionId;
GMPMediaKeyStatus mStatus;
};
// Locks the CDMCaps. It must be locked to access its shared state.
// Threadsafe when locked.
class MOZ_STACK_CLASS AutoLock {
public:
explicit AutoLock(CDMCaps& aKeyCaps);
~AutoLock();
// Returns true if the capabilities of the CDM are known, i.e. they have
// been reported by the CDM to Goanna.
bool AreCapsKnown();
bool IsKeyUsable(const CencKeyId& aKeyId);
// Returns true if key status changed,
// i.e. the key status changed from usable to expired.
bool SetKeyStatus(const CencKeyId& aKeyId, const nsString& aSessionId, GMPMediaKeyStatus aStatus);
void GetKeyStatusesForSession(const nsAString& aSessionId,
nsTArray<KeyStatus>& aOutKeyStatuses);
void GetSessionIdsForKeyId(const CencKeyId& aKeyId,
nsTArray<nsCString>& aOutSessionIds);
// Sets the capabilities of the CDM. aCaps is the logical OR of the
// GMP_EME_CAP_* flags from gmp-decryption.h.
void SetCaps(uint64_t aCaps);
bool CanDecryptAndDecodeAudio();
bool CanDecryptAndDecodeVideo();
bool CanDecryptAudio();
bool CanDecryptVideo();
void CallOnMainThreadWhenCapsAvailable(nsIRunnable* aContinuation);
// Notifies the SamplesWaitingForKey when key become usable.
void NotifyWhenKeyIdUsable(const CencKeyId& aKey,
SamplesWaitingForKey* aSamplesWaiting);
private:
// Not taking a strong ref, since this should be allocated on the stack.
CDMCaps& mData;
};
private:
void Lock();
void Unlock();
bool HasCap(uint64_t);
struct WaitForKeys {
WaitForKeys(const CencKeyId& aKeyId,
SamplesWaitingForKey* aListener)
: mKeyId(aKeyId)
, mListener(aListener)
{}
CencKeyId mKeyId;
nsRefPtr<SamplesWaitingForKey> mListener;
};
Monitor mMonitor;
nsTArray<KeyStatus> mKeyStatuses;
nsTArray<WaitForKeys> mWaitForKeys;
nsTArray<nsRefPtr<nsIRunnable>> mWaitForCaps;
uint64_t mCaps;
// It is not safe to copy this object.
CDMCaps(const CDMCaps&) = delete;
CDMCaps& operator=(const CDMCaps&) = delete;
};
} // namespace mozilla
#endif
-615
View File
@@ -1,615 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#include "mozilla/CDMProxy.h"
#include "nsString.h"
#include "mozilla/dom/MediaKeys.h"
#include "mozilla/dom/MediaKeySession.h"
#include "mozIGoannaMediaPluginService.h"
#include "nsContentCID.h"
#include "nsServiceManagerUtils.h"
#include "MainThreadUtils.h"
#include "mozilla/EMEUtils.h"
#include "nsIConsoleService.h"
#include "prenv.h"
#include "mozilla/PodOperations.h"
#include "mozilla/CDMCallbackProxy.h"
namespace mozilla {
CDMProxy::CDMProxy(dom::MediaKeys* aKeys, const nsAString& aKeySystem)
: mKeys(aKeys)
, mKeySystem(aKeySystem)
, mCDM(nullptr)
, mDecryptionJobCount(0)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_COUNT_CTOR(CDMProxy);
}
CDMProxy::~CDMProxy()
{
MOZ_COUNT_DTOR(CDMProxy);
}
void
CDMProxy::Init(PromiseId aPromiseId,
const nsAString& aOrigin,
const nsAString& aTopLevelOrigin,
bool aInPrivateBrowsing)
{
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
EME_LOG("CDMProxy::Init (%s, %s) %s",
NS_ConvertUTF16toUTF8(aOrigin).get(),
NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(),
(aInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"));
if (!mGMPThread) {
nsCOMPtr<mozIGoannaMediaPluginService> mps =
do_GetService("@mozilla.org/goanna-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;
}
}
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(nsAutoPtr<InitData> aData)
{
MOZ_ASSERT(IsOnGMPThread());
nsCOMPtr<mozIGoannaMediaPluginService> mps =
do_GetService("@mozilla.org/goanna-media-plugin-service;1");
if (!mps) {
RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
nsCString version;
nsTArray<nsCString> tags;
tags.AppendElement(NS_ConvertUTF16toUTF8(mKeySystem));
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());
rv = mps->GetGMPDecryptor(&tags, GetNodeId(), &mCDM);
if (NS_FAILED(rv) || !mCDM) {
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,
aData->mPromiseId));
NS_DispatchToMainThread(task);
}
}
void
CDMProxy::OnCDMCreated(uint32_t aPromiseId)
{
MOZ_ASSERT(NS_IsMainThread());
if (mKeys.IsNull()) {
return;
}
MOZ_ASSERT(!GetNodeId().IsEmpty());
if (mCDM) {
mKeys->OnCDMCreated(aPromiseId, GetNodeId(), mCDM->GetPluginId());
} else {
// No CDM? Just reject the promise.
mKeys->RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
}
}
void
CDMProxy::CreateSession(uint32_t aCreateSessionToken,
dom::SessionType aSessionType,
PromiseId aPromiseId,
const nsAString& aInitDataType,
nsTArray<uint8_t>& aInitData)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mGMPThread);
nsAutoPtr<CreateSessionData> data(new CreateSessionData());
data->mSessionType = aSessionType;
data->mCreateSessionToken = aCreateSessionToken;
data->mPromiseId = aPromiseId;
data->mInitDataType = NS_ConvertUTF16toUTF8(aInitDataType);
data->mInitData = Move(aInitData);
nsRefPtr<nsIRunnable> task(
NS_NewRunnableMethodWithArg<nsAutoPtr<CreateSessionData>>(this, &CDMProxy::gmp_CreateSession, data));
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
}
GMPSessionType
ToGMPSessionType(dom::SessionType aSessionType) {
switch (aSessionType) {
case dom::SessionType::Temporary: return kGMPTemporySession;
case dom::SessionType::Persistent: return kGMPPersistentSession;
default: return kGMPTemporySession;
};
};
void
CDMProxy::gmp_CreateSession(nsAutoPtr<CreateSessionData> aData)
{
MOZ_ASSERT(IsOnGMPThread());
if (!mCDM) {
RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
mCDM->CreateSession(aData->mCreateSessionToken,
aData->mPromiseId,
aData->mInitDataType,
aData->mInitData,
ToGMPSessionType(aData->mSessionType));
}
void
CDMProxy::LoadSession(PromiseId aPromiseId,
const nsAString& aSessionId)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mGMPThread);
nsAutoPtr<SessionOpData> data(new SessionOpData());
data->mPromiseId = aPromiseId;
data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
nsRefPtr<nsIRunnable> task(
NS_NewRunnableMethodWithArg<nsAutoPtr<SessionOpData>>(this, &CDMProxy::gmp_LoadSession, data));
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
}
void
CDMProxy::gmp_LoadSession(nsAutoPtr<SessionOpData> aData)
{
MOZ_ASSERT(IsOnGMPThread());
if (!mCDM) {
RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
mCDM->LoadSession(aData->mPromiseId, aData->mSessionId);
}
void
CDMProxy::SetServerCertificate(PromiseId aPromiseId,
nsTArray<uint8_t>& aCert)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mGMPThread);
nsAutoPtr<SetServerCertificateData> data;
data->mPromiseId = aPromiseId;
data->mCert = Move(aCert);
nsRefPtr<nsIRunnable> task(
NS_NewRunnableMethodWithArg<nsAutoPtr<SetServerCertificateData>>(this, &CDMProxy::gmp_SetServerCertificate, data));
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
}
void
CDMProxy::gmp_SetServerCertificate(nsAutoPtr<SetServerCertificateData> aData)
{
MOZ_ASSERT(IsOnGMPThread());
if (!mCDM) {
RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
mCDM->SetServerCertificate(aData->mPromiseId, aData->mCert);
}
void
CDMProxy::UpdateSession(const nsAString& aSessionId,
PromiseId aPromiseId,
nsTArray<uint8_t>& aResponse)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mGMPThread);
NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
nsAutoPtr<UpdateSessionData> data(new UpdateSessionData());
data->mPromiseId = aPromiseId;
data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
data->mResponse = Move(aResponse);
nsRefPtr<nsIRunnable> task(
NS_NewRunnableMethodWithArg<nsAutoPtr<UpdateSessionData>>(this, &CDMProxy::gmp_UpdateSession, data));
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
}
void
CDMProxy::gmp_UpdateSession(nsAutoPtr<UpdateSessionData> aData)
{
MOZ_ASSERT(IsOnGMPThread());
if (!mCDM) {
RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
mCDM->UpdateSession(aData->mPromiseId,
aData->mSessionId,
aData->mResponse);
}
void
CDMProxy::CloseSession(const nsAString& aSessionId,
PromiseId aPromiseId)
{
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
nsAutoPtr<SessionOpData> data(new SessionOpData());
data->mPromiseId = aPromiseId;
data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
nsRefPtr<nsIRunnable> task(
NS_NewRunnableMethodWithArg<nsAutoPtr<SessionOpData>>(this, &CDMProxy::gmp_CloseSession, data));
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
}
void
CDMProxy::gmp_CloseSession(nsAutoPtr<SessionOpData> aData)
{
MOZ_ASSERT(IsOnGMPThread());
if (!mCDM) {
RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
mCDM->CloseSession(aData->mPromiseId, aData->mSessionId);
}
void
CDMProxy::RemoveSession(const nsAString& aSessionId,
PromiseId aPromiseId)
{
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
nsAutoPtr<SessionOpData> data(new SessionOpData());
data->mPromiseId = aPromiseId;
data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
nsRefPtr<nsIRunnable> task(
NS_NewRunnableMethodWithArg<nsAutoPtr<SessionOpData>>(this, &CDMProxy::gmp_RemoveSession, data));
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
}
void
CDMProxy::gmp_RemoveSession(nsAutoPtr<SessionOpData> aData)
{
MOZ_ASSERT(IsOnGMPThread());
if (!mCDM) {
RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
mCDM->RemoveSession(aData->mPromiseId, aData->mSessionId);
}
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);
}
}
void
CDMProxy::gmp_Shutdown()
{
MOZ_ASSERT(IsOnGMPThread());
// Abort any pending decrypt jobs, to awaken any clients waiting on a job.
for (size_t i = 0; i < mDecryptionJobs.Length(); i++) {
DecryptJob* job = mDecryptionJobs[i];
job->mClient->Decrypted(GMPAbortedErr, nullptr);
}
mDecryptionJobs.Clear();
if (mCDM) {
mCDM->Close();
mCDM = nullptr;
}
}
void
CDMProxy::RejectPromise(PromiseId aId, nsresult aCode)
{
if (NS_IsMainThread()) {
if (!mKeys.IsNull()) {
mKeys->RejectPromise(aId, aCode);
}
} else {
nsRefPtr<nsIRunnable> task(new RejectPromiseTask(this, aId, aCode));
NS_DispatchToMainThread(task);
}
}
void
CDMProxy::ResolvePromise(PromiseId aId)
{
if (NS_IsMainThread()) {
if (!mKeys.IsNull()) {
mKeys->ResolvePromise(aId);
} else {
NS_WARNING("CDMProxy unable to resolve promise!");
}
} else {
nsRefPtr<nsIRunnable> task;
task = NS_NewRunnableMethodWithArg<PromiseId>(this,
&CDMProxy::ResolvePromise,
aId);
NS_DispatchToMainThread(task);
}
}
const nsCString&
CDMProxy::GetNodeId() const
{
return mNodeId;
}
void
CDMProxy::OnSetSessionId(uint32_t aCreateSessionToken,
const nsAString& aSessionId)
{
MOZ_ASSERT(NS_IsMainThread());
if (mKeys.IsNull()) {
return;
}
nsRefPtr<dom::MediaKeySession> session(mKeys->GetPendingSession(aCreateSessionToken));
if (session) {
session->SetSessionId(aSessionId);
}
}
void
CDMProxy::OnResolveLoadSessionPromise(uint32_t aPromiseId, bool aSuccess)
{
MOZ_ASSERT(NS_IsMainThread());
if (mKeys.IsNull()) {
return;
}
mKeys->OnSessionLoaded(aPromiseId, aSuccess);
}
static dom::MediaKeyMessageType
ToMediaKeyMessageType(GMPSessionMessageType aMessageType) {
switch (aMessageType) {
case kGMPLicenseRequest: return dom::MediaKeyMessageType::License_request;
case kGMPLicenseRenewal: return dom::MediaKeyMessageType::License_renewal;
case kGMPLicenseRelease: return dom::MediaKeyMessageType::License_release;
case kGMPIndividualizationRequest: return dom::MediaKeyMessageType::Individualization_request;
default: return dom::MediaKeyMessageType::License_request;
};
};
void
CDMProxy::OnSessionMessage(const nsAString& aSessionId,
GMPSessionMessageType aMessageType,
nsTArray<uint8_t>& aMessage)
{
MOZ_ASSERT(NS_IsMainThread());
if (mKeys.IsNull()) {
return;
}
nsRefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
if (session) {
session->DispatchKeyMessage(ToMediaKeyMessageType(aMessageType), aMessage);
}
}
void
CDMProxy::OnKeyStatusesChange(const nsAString& aSessionId)
{
MOZ_ASSERT(NS_IsMainThread());
if (mKeys.IsNull()) {
return;
}
nsRefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
if (session) {
session->DispatchKeyStatusesChange();
}
}
void
CDMProxy::OnExpirationChange(const nsAString& aSessionId,
GMPTimestamp aExpiryTime)
{
MOZ_ASSERT(NS_IsMainThread());
NS_WARNING("CDMProxy::OnExpirationChange() not implemented");
}
void
CDMProxy::OnSessionClosed(const nsAString& aSessionId)
{
MOZ_ASSERT(NS_IsMainThread());
if (mKeys.IsNull()) {
return;
}
nsRefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
if (session) {
session->OnClosed();
}
}
static void
LogToConsole(const nsAString& aMsg)
{
nsCOMPtr<nsIConsoleService> console(
do_GetService("@mozilla.org/consoleservice;1"));
if (!console) {
NS_WARNING("Failed to log message to console.");
return;
}
nsAutoString msg(aMsg);
console->LogStringMessage(msg.get());
}
void
CDMProxy::OnSessionError(const nsAString& aSessionId,
nsresult aException,
uint32_t aSystemCode,
const nsAString& aMsg)
{
MOZ_ASSERT(NS_IsMainThread());
if (mKeys.IsNull()) {
return;
}
nsRefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
if (session) {
session->DispatchKeyError(aSystemCode);
}
LogToConsole(aMsg);
}
void
CDMProxy::OnRejectPromise(uint32_t aPromiseId,
nsresult aDOMException,
const nsAString& aMsg)
{
MOZ_ASSERT(NS_IsMainThread());
RejectPromise(aPromiseId, aDOMException);
LogToConsole(aMsg);
}
const nsString&
CDMProxy::KeySystem() const
{
return mKeySystem;
}
CDMCaps&
CDMProxy::Capabilites() {
return mCapabilites;
}
void
CDMProxy::Decrypt(mp4_demuxer::MP4Sample* aSample,
DecryptionClient* aClient)
{
nsAutoPtr<DecryptJob> job(new DecryptJob(aSample, aClient));
nsRefPtr<nsIRunnable> task(
NS_NewRunnableMethodWithArg<nsAutoPtr<DecryptJob>>(this, &CDMProxy::gmp_Decrypt, job));
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
}
void
CDMProxy::gmp_Decrypt(nsAutoPtr<DecryptJob> aJob)
{
MOZ_ASSERT(IsOnGMPThread());
MOZ_ASSERT(aJob->mClient);
MOZ_ASSERT(aJob->mSample);
if (!mCDM) {
aJob->mClient->Decrypted(GMPAbortedErr, nullptr);
return;
}
aJob->mId = ++mDecryptionJobCount;
nsTArray<uint8_t> data;
data.AppendElements(aJob->mSample->data, aJob->mSample->size);
mCDM->Decrypt(aJob->mId, aJob->mSample->crypto, data);
mDecryptionJobs.AppendElement(aJob.forget());
}
void
CDMProxy::gmp_Decrypted(uint32_t aId,
GMPErr aResult,
const nsTArray<uint8_t>& aDecryptedData)
{
MOZ_ASSERT(IsOnGMPThread());
for (size_t i = 0; i < mDecryptionJobs.Length(); i++) {
DecryptJob* job = mDecryptionJobs[i];
if (job->mId == aId) {
if (aDecryptedData.Length() != job->mSample->size) {
NS_WARNING("CDM returned incorrect number of decrypted bytes");
}
if (GMP_SUCCEEDED(aResult)) {
PodCopy(job->mSample->data,
aDecryptedData.Elements(),
std::min<size_t>(aDecryptedData.Length(), job->mSample->size));
job->mClient->Decrypted(GMPNoErr, job->mSample.forget());
} else if (aResult == GMPNoKeyErr) {
NS_WARNING("CDM returned GMPNoKeyErr");
// We still have the encrypted sample, so we can re-enqueue it to be
// decrypted again once the key is usable again.
job->mClient->Decrypted(GMPNoKeyErr, job->mSample.forget());
} else {
nsAutoCString str("CDM returned decode failure GMPErr=");
str.AppendInt(aResult);
NS_WARNING(str.get());
job->mClient->Decrypted(aResult, nullptr);
}
mDecryptionJobs.RemoveElementAt(i);
return;
}
}
NS_WARNING("GMPDecryptorChild returned incorrect job ID");
}
void
CDMProxy::GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
nsTArray<nsCString>& aSessionIds)
{
CDMCaps::AutoLock caps(Capabilites());
caps.GetSessionIdsForKeyId(aKeyId, aSessionIds);
}
void
CDMProxy::Terminated()
{
MOZ_ASSERT(NS_IsMainThread());
NS_WARNING("CDM terminated");
if (!mKeys.IsNull()) {
mKeys->Terminated();
}
}
} // namespace mozilla
-328
View File
@@ -1,328 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 CDMProxy_h_
#define CDMProxy_h_
#include "nsString.h"
#include "nsAutoPtr.h"
#include "mozilla/dom/MediaKeys.h"
#include "mozilla/Monitor.h"
#include "nsIThread.h"
#include "GMPDecryptorProxy.h"
#include "mozilla/CDMCaps.h"
#include "mp4_demuxer/DecoderData.h"
namespace mozilla {
class CDMCallbackProxy;
namespace dom {
class MediaKeySession;
}
class DecryptionClient {
public:
virtual ~DecryptionClient() {}
virtual void Decrypted(GMPErr aResult,
mp4_demuxer::MP4Sample* aSample) = 0;
};
// Proxies calls GMP/CDM, and proxies calls back.
// Note: Promises are passed in via a PromiseId, so that the ID can be
// passed via IPC to the CDM, which can then signal when to reject or
// resolve the promise using its PromiseId.
class CDMProxy {
typedef dom::PromiseId PromiseId;
typedef dom::SessionType SessionType;
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,
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(uint32_t aCreateSessionToken,
dom::SessionType aSessionType,
PromiseId aPromiseId,
const nsAString& aInitDataType,
nsTArray<uint8_t>& aInitData);
// Main thread only.
// Uses the CDM to load a presistent session stored on disk.
// Calls MediaKeys::OnSessionActivated() when session is loaded.
void LoadSession(PromiseId aPromiseId,
const nsAString& aSessionId);
// Main thread only.
// Sends a new certificate to the CDM.
// Calls MediaKeys->ResolvePromise(aPromiseId) after the CDM has
// processed the request.
// Assumes ownership of (Move()s) aCert's contents.
void SetServerCertificate(PromiseId aPromiseId,
nsTArray<uint8_t>& aCert);
// Main thread only.
// Sends an update to the CDM.
// Calls MediaKeys->ResolvePromise(aPromiseId) after the CDM has
// processed the request.
// Assumes ownership of (Move()s) aResponse's contents.
void UpdateSession(const nsAString& aSessionId,
PromiseId aPromiseId,
nsTArray<uint8_t>& aResponse);
// Main thread only.
// Calls MediaKeys->ResolvePromise(aPromiseId) after the CDM has
// processed the request.
// If processing this operation results in the session actually closing,
// we also call MediaKeySession::OnClosed(), which in turn calls
// MediaKeys::OnSessionClosed().
void CloseSession(const nsAString& aSessionId,
PromiseId aPromiseId);
// Main thread only.
// Removes all data for a persisent session.
// Calls MediaKeys->ResolvePromise(aPromiseId) after the CDM has
// processed the request.
void RemoveSession(const nsAString& aSessionId,
PromiseId aPromiseId);
// Main thread only.
void Shutdown();
// Main thread only.
void Terminated();
// Threadsafe.
const nsCString& GetNodeId() const;
// Main thread only.
void OnSetSessionId(uint32_t aCreateSessionToken,
const nsAString& aSessionId);
// Main thread only.
void OnResolveLoadSessionPromise(uint32_t aPromiseId, bool aSuccess);
// Main thread only.
void OnSessionMessage(const nsAString& aSessionId,
GMPSessionMessageType aMessageType,
nsTArray<uint8_t>& aMessage);
// Main thread only.
void OnExpirationChange(const nsAString& aSessionId,
GMPTimestamp aExpiryTime);
// Main thread only.
void OnSessionClosed(const nsAString& aSessionId);
// Main thread only.
void OnSessionError(const nsAString& aSessionId,
nsresult aException,
uint32_t aSystemCode,
const nsAString& aMsg);
// Main thread only.
void OnRejectPromise(uint32_t aPromiseId,
nsresult aDOMException,
const nsAString& aMsg);
// Threadsafe.
void Decrypt(mp4_demuxer::MP4Sample* aSample,
DecryptionClient* aSink);
// Reject promise with DOMException corresponding to aExceptionCode.
// Can be called from any thread.
void RejectPromise(PromiseId aId, nsresult aExceptionCode);
// Resolves promise with "undefined".
// Can be called from any thread.
void ResolvePromise(PromiseId aId);
// Threadsafe.
const nsString& KeySystem() const;
// GMP thread only.
void gmp_Decrypted(uint32_t aId,
GMPErr aResult,
const nsTArray<uint8_t>& aDecryptedData);
CDMCaps& Capabilites();
// Main thread only.
void OnKeyStatusesChange(const nsAString& aSessionId);
void GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
nsTArray<nsCString>& aSessionIds);
#ifdef DEBUG
bool IsOnGMPThread();
#endif
private:
struct InitData {
uint32_t mPromiseId;
nsAutoString mOrigin;
nsAutoString mTopLevelOrigin;
bool mInPrivateBrowsing;
};
// GMP thread only.
void gmp_Init(nsAutoPtr<InitData> aData);
// GMP thread only.
void gmp_Shutdown();
// Main thread only.
void OnCDMCreated(uint32_t aPromiseId);
struct CreateSessionData {
dom::SessionType mSessionType;
uint32_t mCreateSessionToken;
PromiseId mPromiseId;
nsAutoCString mInitDataType;
nsTArray<uint8_t> mInitData;
};
// GMP thread only.
void gmp_CreateSession(nsAutoPtr<CreateSessionData> aData);
struct SessionOpData {
PromiseId mPromiseId;
nsAutoCString mSessionId;
};
// GMP thread only.
void gmp_LoadSession(nsAutoPtr<SessionOpData> aData);
struct SetServerCertificateData {
PromiseId mPromiseId;
nsTArray<uint8_t> mCert;
};
// GMP thread only.
void gmp_SetServerCertificate(nsAutoPtr<SetServerCertificateData> aData);
struct UpdateSessionData {
PromiseId mPromiseId;
nsAutoCString mSessionId;
nsTArray<uint8_t> mResponse;
};
// GMP thread only.
void gmp_UpdateSession(nsAutoPtr<UpdateSessionData> aData);
// GMP thread only.
void gmp_CloseSession(nsAutoPtr<SessionOpData> aData);
// GMP thread only.
void gmp_RemoveSession(nsAutoPtr<SessionOpData> aData);
struct DecryptJob {
DecryptJob(mp4_demuxer::MP4Sample* aSample,
DecryptionClient* aClient)
: mId(0)
, mSample(aSample)
, mClient(aClient)
{}
uint32_t mId;
nsAutoPtr<mp4_demuxer::MP4Sample> mSample;
nsAutoPtr<DecryptionClient> mClient;
};
// GMP thread only.
void gmp_Decrypt(nsAutoPtr<DecryptJob> aJob);
class RejectPromiseTask : public nsRunnable {
public:
RejectPromiseTask(CDMProxy* aProxy,
PromiseId aId,
nsresult aCode)
: mProxy(aProxy)
, mId(aId)
, mCode(aCode)
{
}
NS_METHOD Run() {
mProxy->RejectPromise(mId, mCode);
return NS_OK;
}
private:
nsRefPtr<CDMProxy> mProxy;
PromiseId mId;
nsresult mCode;
};
~CDMProxy();
// Helper to enforce that a raw pointer is only accessed on the main thread.
template<class Type>
class MainThreadOnlyRawPtr {
public:
explicit MainThreadOnlyRawPtr(Type* aPtr)
: mPtr(aPtr)
{
MOZ_ASSERT(NS_IsMainThread());
}
bool IsNull() const {
MOZ_ASSERT(NS_IsMainThread());
return !mPtr;
}
void Clear() {
MOZ_ASSERT(NS_IsMainThread());
mPtr = nullptr;
}
Type* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN {
MOZ_ASSERT(NS_IsMainThread());
return mPtr;
}
private:
Type* mPtr;
};
// Our reference back to the MediaKeys object.
// WARNING: This is a non-owning reference that is cleared by MediaKeys
// destructor. only use on main thread, and always nullcheck before using!
MainThreadOnlyRawPtr<dom::MediaKeys> mKeys;
const nsAutoString mKeySystem;
// Goanna Media Plugin thread. All interactions with the out-of-process
// EME plugin must come from this thread.
nsRefPtr<nsIThread> mGMPThread;
nsCString mNodeId;
GMPDecryptorProxy* mCDM;
CDMCaps mCapabilites;
nsAutoPtr<CDMCallbackProxy> mCallback;
// Decryption jobs sent to CDM, awaiting result.
// GMP thread only.
nsTArray<nsAutoPtr<DecryptJob>> mDecryptionJobs;
// Number of buffers we've decrypted. Used to uniquely identify
// decryption jobs sent to CDM. Note we can't just use the length of
// mDecryptionJobs as that shrinks as jobs are completed and removed
// from it.
// GMP thread only.
uint32_t mDecryptionJobCount;
};
} // namespace mozilla
#endif // CDMProxy_h_
-116
View File
@@ -1,116 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#include "mozilla/EMEUtils.h"
namespace mozilla {
#ifdef PR_LOGGING
PRLogModuleInfo* GetEMELog() {
static PRLogModuleInfo* log = nullptr;
if (!log) {
log = PR_NewLogModule("EME");
}
return log;
}
PRLogModuleInfo* GetEMEVerboseLog() {
static PRLogModuleInfo* log = nullptr;
if (!log) {
log = PR_NewLogModule("EMEV");
}
return log;
}
#endif
static bool
ContainsOnlyDigits(const nsAString& aString)
{
nsAString::const_iterator iter, end;
aString.BeginReading(iter);
aString.EndReading(end);
while (iter != end) {
char16_t ch = *iter;
if (ch < '0' || ch > '9') {
return false;
}
iter++;
}
return true;
}
static bool
ParseKeySystem(const nsAString& aExpectedKeySystem,
const nsAString& aInputKeySystem,
int32_t& aOutCDMVersion)
{
if (!StringBeginsWith(aInputKeySystem, aExpectedKeySystem)) {
return false;
}
if (aInputKeySystem.Length() > aExpectedKeySystem.Length() + 8) {
// Allow up to 8 bytes for the ".version" field. 8 bytes should
// be enough for any versioning scheme...
NS_WARNING("Input KeySystem including was suspiciously long");
return false;
}
const char16_t* versionStart = aInputKeySystem.BeginReading() + aExpectedKeySystem.Length();
const char16_t* end = aInputKeySystem.EndReading();
if (versionStart == end) {
// No version supplied with keysystem.
aOutCDMVersion = NO_CDM_VERSION;
return true;
}
if (*versionStart != '.') {
// version not in correct format.
NS_WARNING("EME keySystem version string not prefixed by '.'");
return false;
}
versionStart++;
const nsAutoString versionStr(Substring(versionStart, end));
if (!ContainsOnlyDigits(versionStr)) {
NS_WARNING("Non-digit character in EME keySystem string's version suffix");
return false;
}
nsresult rv;
int32_t version = versionStr.ToInteger(&rv);
if (NS_FAILED(rv) || version < 0) {
NS_WARNING("Invalid version in EME keySystem string");
return false;
}
aOutCDMVersion = version;
return true;
}
static const char16_t* sKeySystems[] = {
MOZ_UTF16("org.w3.clearkey"),
MOZ_UTF16("com.adobe.access"),
MOZ_UTF16("com.adobe.primetime"),
};
bool
ParseKeySystem(const nsAString& aInputKeySystem,
nsAString& aOutKeySystem,
int32_t& aOutCDMVersion)
{
for (const char16_t* keySystem : sKeySystems) {
int32_t minCDMVersion = NO_CDM_VERSION;
if (ParseKeySystem(nsDependentString(keySystem),
aInputKeySystem,
minCDMVersion)) {
aOutKeySystem = keySystem;
aOutCDMVersion = minCDMVersion;
return true;
}
}
return false;
}
} // namespace mozilla
-69
View File
@@ -1,69 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 EME_LOG_H_
#define EME_LOG_H_
#include "prlog.h"
#include "nsString.h"
namespace mozilla {
#ifdef PR_LOGGING
#ifndef EME_LOG
PRLogModuleInfo* GetEMELog();
#define EME_LOG(...) PR_LOG(GetEMELog(), PR_LOG_DEBUG, (__VA_ARGS__))
#endif
#ifndef EME_VERBOSE_LOG
PRLogModuleInfo* GetEMEVerboseLog();
#define EME_VERBOSE_LOG(...) PR_LOG(GetEMEVerboseLog(), PR_LOG_DEBUG, (__VA_ARGS__))
#else
#ifndef EME_LOG
#define EME_LOG(...)
#endif
#ifndef EME_VERBOSE_LOG
#define EME_VERBOSE_LOG(...)
#endif
#endif
#else
#ifndef EME_LOG
#define EME_LOG(...)
#endif
#ifndef EME_VERBOSE_LOG
#define EME_VERBOSE_LOG(...)
#endif
#endif // PR_LOGGING
#define NO_CDM_VERSION -1
// Checks a keySystem string against a whitelist, and determines whether
// the keySystem is in the whitelist, and extracts the requested minimum
// CDM version.
//
// Format of EME keysystems:
// com.domain.keysystem[.minVersionAsInt]
// i.e. org.w3.clearkey, com.adobe.primetime.7
//
// Returns true if aKeySystem contains a valid keySystem which we support,
// false otherwise.
//
// On success, aOutKeySystem contains the keySystem string stripped of the
// min version number, and aOutMinCDMVersion contains the min version number
// if present. If it was not present, aOutMinCDMVersion is NO_CDM_VERSION.
bool ParseKeySystem(const nsAString& aKeySystem,
nsAString& aOutKeySystem,
int32_t& aOutMinCDMVersion);
} // namespace mozilla
#endif // EME_LOG_H_
-130
View File
@@ -1,130 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#include "MediaEncryptedEvent.h"
#include "mozilla/dom/MediaEncryptedEventBinding.h"
#include "nsContentUtils.h"
#include "jsfriendapi.h"
#include "nsINode.h"
#include "mozilla/dom/MediaKeys.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_CLASS(MediaEncryptedEvent)
NS_IMPL_ADDREF_INHERITED(MediaEncryptedEvent, Event)
NS_IMPL_RELEASE_INHERITED(MediaEncryptedEvent, Event)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MediaEncryptedEvent, Event)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(MediaEncryptedEvent, Event)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mInitData)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MediaEncryptedEvent, Event)
tmp->mInitData = nullptr;
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaEncryptedEvent)
NS_INTERFACE_MAP_END_INHERITING(Event)
MediaEncryptedEvent::MediaEncryptedEvent(EventTarget* aOwner)
: Event(aOwner, nullptr, nullptr)
{
mozilla::HoldJSObjects(this);
}
MediaEncryptedEvent::~MediaEncryptedEvent()
{
mInitData = nullptr;
mozilla::DropJSObjects(this);
}
JSObject*
MediaEncryptedEvent::WrapObjectInternal(JSContext* aCx)
{
return MediaEncryptedEventBinding::Wrap(aCx, this);
}
already_AddRefed<MediaEncryptedEvent>
MediaEncryptedEvent::Constructor(EventTarget* aOwner)
{
nsRefPtr<MediaEncryptedEvent> e = new MediaEncryptedEvent(aOwner);
e->InitEvent(NS_LITERAL_STRING("encrypted"), false, false);
e->SetTrusted(true);
return e.forget();
}
already_AddRefed<MediaEncryptedEvent>
MediaEncryptedEvent::Constructor(EventTarget* aOwner,
const nsAString& aInitDataType,
const nsTArray<uint8_t>& aInitData)
{
nsRefPtr<MediaEncryptedEvent> e = new MediaEncryptedEvent(aOwner);
e->InitEvent(NS_LITERAL_STRING("encrypted"), false, false);
e->mInitDataType = aInitDataType;
e->mRawInitData = aInitData;
e->SetTrusted(true);
return e.forget();
}
already_AddRefed<MediaEncryptedEvent>
MediaEncryptedEvent::Constructor(const GlobalObject& aGlobal,
const nsAString& aType,
const MediaKeyNeededEventInit& aEventInitDict,
ErrorResult& aRv)
{
nsCOMPtr<EventTarget> owner = do_QueryInterface(aGlobal.GetAsSupports());
nsRefPtr<MediaEncryptedEvent> e = new MediaEncryptedEvent(owner);
bool trusted = e->Init(owner);
e->InitEvent(aType, aEventInitDict.mBubbles, aEventInitDict.mCancelable);
e->mInitDataType = aEventInitDict.mInitDataType;
if (!aEventInitDict.mInitData.IsNull()) {
const auto& a = aEventInitDict.mInitData.Value();
a.ComputeLengthAndData();
e->mInitData = ArrayBuffer::Create(aGlobal.Context(),
a.Length(),
a.Data());
if (!e->mInitData) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return nullptr;
}
}
e->SetTrusted(trusted);
return e.forget();
}
void
MediaEncryptedEvent::GetInitDataType(nsString& aRetVal) const
{
aRetVal = mInitDataType;
}
void
MediaEncryptedEvent::GetInitData(JSContext* cx,
JS::MutableHandle<JSObject*> aData,
ErrorResult& aRv)
{
if (mRawInitData.Length()) {
mInitData = ArrayBuffer::Create(cx,
mRawInitData.Length(),
mRawInitData.Elements());
if (!mInitData) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
mRawInitData.Clear();
}
if (mInitData) {
JS::ExposeObjectToActiveJS(mInitData);
}
aData.set(mInitData);
}
} // namespace dom
} // namespace mozilla
-67
View File
@@ -1,67 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 mozilla_dom_MediaKeyNeededEvent_h__
#define mozilla_dom_MediaKeyNeededEvent_h__
#include "mozilla/dom/MediaEncryptedEventBinding.h"
#include "mozilla/Attributes.h"
#include "mozilla/ErrorResult.h"
#include "nsCycleCollectionParticipant.h"
#include "nsWrapperCache.h"
#include "nsCOMPtr.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/TypedArray.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/BindingUtils.h"
#include "js/TypeDecls.h"
namespace mozilla {
namespace dom {
class MediaEncryptedEvent final : public Event
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(MediaEncryptedEvent, Event)
protected:
virtual ~MediaEncryptedEvent();
explicit MediaEncryptedEvent(EventTarget* aOwner);
nsString mInitDataType;
JS::Heap<JSObject*> mInitData;
public:
virtual JSObject* WrapObjectInternal(JSContext* aCx) override;
static already_AddRefed<MediaEncryptedEvent>
Constructor(EventTarget* aOwner);
static already_AddRefed<MediaEncryptedEvent>
Constructor(EventTarget* aOwner,
const nsAString& aInitDataType,
const nsTArray<uint8_t>& aInitData);
static already_AddRefed<MediaEncryptedEvent>
Constructor(const GlobalObject& aGlobal,
const nsAString& aType,
const MediaKeyNeededEventInit& aEventInitDict,
ErrorResult& aRv);
void GetInitDataType(nsString& aRetVal) const;
void GetInitData(JSContext* cx,
JS::MutableHandle<JSObject*> aData,
ErrorResult& aRv);
private:
nsTArray<uint8_t> mRawInitData;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_MediaKeyNeededEvent_h__
-39
View File
@@ -1,39 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#include "MediaKeyError.h"
#include "mozilla/dom/MediaKeyErrorBinding.h"
#include "nsContentUtils.h"
namespace mozilla {
namespace dom {
MediaKeyError::MediaKeyError(EventTarget* aOwner, uint32_t aSystemCode)
: Event(aOwner, nullptr, nullptr)
, mSystemCode(aSystemCode)
{
InitEvent(NS_LITERAL_STRING("error"), false, false);
}
MediaKeyError::~MediaKeyError()
{
}
uint32_t
MediaKeyError::SystemCode() const
{
return mSystemCode;
}
JSObject*
MediaKeyError::WrapObjectInternal(JSContext* aCx)
{
return MediaKeyErrorBinding::Wrap(aCx, this);
}
} // namespace dom
} // namespace mozilla
-38
View File
@@ -1,38 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 mozilla_dom_MediaKeyError_h
#define mozilla_dom_MediaKeyError_h
#include "mozilla/Attributes.h"
#include "mozilla/ErrorResult.h"
#include "nsWrapperCache.h"
#include "mozilla/dom/Event.h"
#include "js/TypeDecls.h"
namespace mozilla {
namespace dom {
class MediaKeyError final : public Event
{
public:
NS_FORWARD_TO_EVENT
MediaKeyError(EventTarget* aOwner, uint32_t aSystemCode);
~MediaKeyError();
virtual JSObject* WrapObjectInternal(JSContext* aCx) override;
uint32_t SystemCode() const;
private:
uint32_t mSystemCode;
};
} // namespace dom
} // namespace mozilla
#endif
-125
View File
@@ -1,125 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#include "mozilla/dom/MediaKeyMessageEvent.h"
#include "mozilla/dom/MediaKeyMessageEventBinding.h"
#include "js/GCAPI.h"
#include "jsfriendapi.h"
#include "mozilla/dom/Nullable.h"
#include "mozilla/dom/PrimitiveConversions.h"
#include "mozilla/HoldDropJSObjects.h"
#include "mozilla/dom/TypedArray.h"
#include "nsContentUtils.h"
#include "mozilla/dom/MediaKeys.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_CLASS(MediaKeyMessageEvent)
NS_IMPL_ADDREF_INHERITED(MediaKeyMessageEvent, Event)
NS_IMPL_RELEASE_INHERITED(MediaKeyMessageEvent, Event)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MediaKeyMessageEvent, Event)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(MediaKeyMessageEvent, Event)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mMessage)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MediaKeyMessageEvent, Event)
tmp->mMessage = nullptr;
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaKeyMessageEvent)
NS_INTERFACE_MAP_END_INHERITING(Event)
MediaKeyMessageEvent::MediaKeyMessageEvent(EventTarget* aOwner)
: Event(aOwner, nullptr, nullptr)
{
mozilla::HoldJSObjects(this);
}
MediaKeyMessageEvent::~MediaKeyMessageEvent()
{
mMessage = nullptr;
mozilla::DropJSObjects(this);
}
MediaKeyMessageEvent*
MediaKeyMessageEvent::AsMediaKeyMessageEvent()
{
return this;
}
JSObject*
MediaKeyMessageEvent::WrapObjectInternal(JSContext* aCx)
{
return MediaKeyMessageEventBinding::Wrap(aCx, this);
}
already_AddRefed<MediaKeyMessageEvent>
MediaKeyMessageEvent::Constructor(EventTarget* aOwner,
MediaKeyMessageType aMessageType,
const nsTArray<uint8_t>& aMessage)
{
nsRefPtr<MediaKeyMessageEvent> e = new MediaKeyMessageEvent(aOwner);
e->InitEvent(NS_LITERAL_STRING("message"), false, false);
e->mMessageType = aMessageType;
e->mRawMessage = aMessage;
e->SetTrusted(true);
return e.forget();
}
already_AddRefed<MediaKeyMessageEvent>
MediaKeyMessageEvent::Constructor(const GlobalObject& aGlobal,
const nsAString& aType,
const MediaKeyMessageEventInit& aEventInitDict,
ErrorResult& aRv)
{
nsCOMPtr<EventTarget> owner = do_QueryInterface(aGlobal.GetAsSupports());
nsRefPtr<MediaKeyMessageEvent> e = new MediaKeyMessageEvent(owner);
bool trusted = e->Init(owner);
e->InitEvent(aType, aEventInitDict.mBubbles, aEventInitDict.mCancelable);
const uint8_t* data = nullptr;
size_t length = 0;
if (aEventInitDict.mMessage.WasPassed()) {
const auto& a = aEventInitDict.mMessage.Value();
a.ComputeLengthAndData();
data = a.Data();
length = a.Length();
}
e->mMessage = ArrayBuffer::Create(aGlobal.Context(), length, data);
if (!e->mMessage) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return nullptr;
}
e->mMessageType = aEventInitDict.mMessageType;
e->SetTrusted(trusted);
return e.forget();
}
void
MediaKeyMessageEvent::GetMessage(JSContext* cx,
JS::MutableHandle<JSObject*> aMessage,
ErrorResult& aRv)
{
if (!mMessage) {
mMessage = ArrayBuffer::Create(cx,
mRawMessage.Length(),
mRawMessage.Elements());
if (!mMessage) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
mRawMessage.Clear();
}
JS::ExposeObjectToActiveJS(mMessage);
aMessage.set(mMessage);
}
} // namespace dom
} // namespace mozilla
-67
View File
@@ -1,67 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 mozilla_dom_MediaKeyMessageEvent_h__
#define mozilla_dom_MediaKeyMessageEvent_h__
#include "mozilla/Attributes.h"
#include "mozilla/ErrorResult.h"
#include "nsCycleCollectionParticipant.h"
#include "nsWrapperCache.h"
#include "nsCOMPtr.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/TypedArray.h"
#include "js/TypeDecls.h"
#include "mozilla/dom/MediaKeyMessageEventBinding.h"
namespace mozilla {
namespace dom {
struct MediaKeyMessageEventInit;
class MediaKeyMessageEvent final : public Event
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(MediaKeyMessageEvent, Event)
protected:
virtual ~MediaKeyMessageEvent();
explicit MediaKeyMessageEvent(EventTarget* aOwner);
MediaKeyMessageType mMessageType;
JS::Heap<JSObject*> mMessage;
public:
virtual MediaKeyMessageEvent* AsMediaKeyMessageEvent();
virtual JSObject* WrapObjectInternal(JSContext* aCx) override;
static already_AddRefed<MediaKeyMessageEvent>
Constructor(EventTarget* aOwner,
MediaKeyMessageType aMessageType,
const nsTArray<uint8_t>& aMessage);
static already_AddRefed<MediaKeyMessageEvent>
Constructor(const GlobalObject& aGlobal,
const nsAString& aType,
const MediaKeyMessageEventInit& aEventInitDict,
ErrorResult& aRv);
MediaKeyMessageType MessageType() const { return mMessageType; }
void GetMessage(JSContext* cx,
JS::MutableHandle<JSObject*> aMessage,
ErrorResult& aRv);
private:
nsTArray<uint8_t> mRawMessage;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_MediaKeyMessageEvent_h__
-323
View File
@@ -1,323 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#include "mozilla/dom/HTMLMediaElement.h"
#include "mozilla/dom/MediaKeySession.h"
#include "mozilla/dom/MediaKeyError.h"
#include "mozilla/dom/MediaKeyMessageEvent.h"
#include "mozilla/dom/MediaEncryptedEvent.h"
#include "mozilla/dom/MediaKeyStatusMap.h"
#include "nsCycleCollectionParticipant.h"
#include "mozilla/CDMProxy.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/Move.h"
#include "nsContentUtils.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaKeySession,
DOMEventTargetHelper,
mMediaKeyError,
mKeys,
mKeyStatusMap,
mClosed)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaKeySession)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
NS_IMPL_ADDREF_INHERITED(MediaKeySession, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(MediaKeySession, DOMEventTargetHelper)
// Count of number of instances. Used to give each instance a
// unique token.
static uint32_t sMediaKeySessionNum = 0;
MediaKeySession::MediaKeySession(JSContext* aCx,
nsPIDOMWindow* aParent,
MediaKeys* aKeys,
const nsAString& aKeySystem,
SessionType aSessionType,
ErrorResult& aRv)
: DOMEventTargetHelper(aParent)
, mKeys(aKeys)
, mKeySystem(aKeySystem)
, mSessionType(aSessionType)
, mToken(sMediaKeySessionNum++)
, mIsClosed(false)
, mUninitialized(true)
, mKeyStatusMap(new MediaKeyStatusMap(aCx, aParent, aRv))
{
MOZ_ASSERT(aParent);
if (aRv.Failed()) {
return;
}
mClosed = mKeys->MakePromise(aRv);
}
void MediaKeySession::SetSessionId(const nsAString& aSessionId)
{
if (NS_WARN_IF(!mSessionId.IsEmpty())) {
return;
}
mSessionId = aSessionId;
mKeys->OnSessionIdReady(this);
}
MediaKeySession::~MediaKeySession()
{
}
MediaKeyError*
MediaKeySession::GetError() const
{
return mMediaKeyError;
}
void
MediaKeySession::GetKeySystem(nsString& aKeySystem) const
{
aKeySystem = mKeySystem;
}
void
MediaKeySession::GetSessionId(nsString& aSessionId) const
{
aSessionId = GetSessionId();
}
const nsString&
MediaKeySession::GetSessionId() const
{
return mSessionId;
}
JSObject*
MediaKeySession::WrapObject(JSContext* aCx)
{
return MediaKeySessionBinding::Wrap(aCx, this);
}
double
MediaKeySession::Expiration() const
{
return JS::GenericNaN();
}
Promise*
MediaKeySession::Closed() const
{
return mClosed;
}
void
MediaKeySession::UpdateKeyStatusMap()
{
MOZ_ASSERT(!IsClosed());
if (!mKeys->GetCDMProxy()) {
return;
}
nsTArray<CDMCaps::KeyStatus> keyStatuses;
{
CDMCaps::AutoLock caps(mKeys->GetCDMProxy()->Capabilites());
caps.GetKeyStatusesForSession(mSessionId, keyStatuses);
}
mKeyStatusMap->Update(keyStatuses);
}
MediaKeyStatusMap*
MediaKeySession::KeyStatuses() const
{
return mKeyStatusMap;
}
already_AddRefed<Promise>
MediaKeySession::GenerateRequest(const nsAString& aInitDataType,
const ArrayBufferViewOrArrayBuffer& aInitData,
ErrorResult& aRv)
{
nsRefPtr<Promise> promise(mKeys->MakePromise(aRv));
if (aRv.Failed()) {
return nullptr;
}
if (!mUninitialized) {
promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
return promise.forget();
}
mUninitialized = false;
nsTArray<uint8_t> data;
if (aInitDataType.IsEmpty() ||
!CopyArrayBufferViewOrArrayBufferData(aInitData, data)) {
promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
return promise.forget();
}
PromiseId pid = mKeys->StorePromise(promise);
mKeys->GetCDMProxy()->CreateSession(Token(),
mSessionType,
pid,
aInitDataType, data);
return promise.forget();
}
already_AddRefed<Promise>
MediaKeySession::Load(const nsAString& aSessionId, ErrorResult& aRv)
{
nsRefPtr<Promise> promise(mKeys->MakePromise(aRv));
if (aRv.Failed()) {
return nullptr;
}
if (aSessionId.IsEmpty()) {
promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
// "The sessionId parameter is empty."
return promise.forget();
}
if (!mUninitialized) {
promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
return promise.forget();
}
mUninitialized = false;
// We now know the sessionId being loaded into this session. Remove the
// session from its owning MediaKey's set of sessions awaiting a sessionId.
nsRefPtr<MediaKeySession> session(mKeys->GetPendingSession(Token()));
MOZ_ASSERT(session == this, "Session should be awaiting id on its own token");
// Associate with the known sessionId.
SetSessionId(aSessionId);
mKeys->GetCDMProxy()->LoadSession(mKeys->StorePromise(promise), aSessionId);
return promise.forget();
}
already_AddRefed<Promise>
MediaKeySession::Update(const ArrayBufferViewOrArrayBuffer& aResponse, ErrorResult& aRv)
{
nsRefPtr<Promise> promise(mKeys->MakePromise(aRv));
if (aRv.Failed()) {
return nullptr;
}
nsTArray<uint8_t> data;
if (IsClosed() ||
!mKeys->GetCDMProxy() ||
!CopyArrayBufferViewOrArrayBufferData(aResponse, data)) {
promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
return promise.forget();
}
mKeys->GetCDMProxy()->UpdateSession(mSessionId,
mKeys->StorePromise(promise),
data);
return promise.forget();
}
already_AddRefed<Promise>
MediaKeySession::Close(ErrorResult& aRv)
{
nsRefPtr<Promise> promise(mKeys->MakePromise(aRv));
if (aRv.Failed()) {
return nullptr;
}
if (IsClosed() || !mKeys->GetCDMProxy()) {
promise->MaybeResolve(JS::UndefinedHandleValue);
return promise.forget();
}
mKeys->GetCDMProxy()->CloseSession(mSessionId, mKeys->StorePromise(promise));
return promise.forget();
}
void
MediaKeySession::OnClosed()
{
if (IsClosed()) {
return;
}
mIsClosed = true;
mKeys->OnSessionClosed(this);
mKeys = nullptr;
mClosed->MaybeResolve(JS::UndefinedHandleValue);
}
bool
MediaKeySession::IsClosed() const
{
return mIsClosed;
}
already_AddRefed<Promise>
MediaKeySession::Remove(ErrorResult& aRv)
{
nsRefPtr<Promise> promise(mKeys->MakePromise(aRv));
if (aRv.Failed()) {
return nullptr;
}
if (mSessionType != SessionType::Persistent) {
promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
// "The operation is not supported on session type sessions."
return promise.forget();
}
if (IsClosed() || !mKeys->GetCDMProxy()) {
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
// "The session is closed."
return promise.forget();
}
mKeys->GetCDMProxy()->RemoveSession(mSessionId, mKeys->StorePromise(promise));
return promise.forget();
}
void
MediaKeySession::DispatchKeyMessage(MediaKeyMessageType aMessageType,
const nsTArray<uint8_t>& aMessage)
{
nsRefPtr<MediaKeyMessageEvent> event(
MediaKeyMessageEvent::Constructor(this, aMessageType, aMessage));
nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
new AsyncEventDispatcher(this, event);
asyncDispatcher->PostDOMEvent();
}
void
MediaKeySession::DispatchKeyError(uint32_t aSystemCode)
{
RefPtr<MediaKeyError> event(new MediaKeyError(this, aSystemCode));
nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
new AsyncEventDispatcher(this, event);
asyncDispatcher->PostDOMEvent();
}
void
MediaKeySession::DispatchKeyStatusesChange()
{
if (IsClosed()) {
return;
}
UpdateKeyStatusMap();
nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
new AsyncEventDispatcher(this, NS_LITERAL_STRING("keystatuseschange"), false);
asyncDispatcher->PostDOMEvent();
}
uint32_t
MediaKeySession::Token() const
{
return mToken;
}
} // namespace dom
} // namespace mozilla
-120
View File
@@ -1,120 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 mozilla_dom_MediaKeySession_h
#define mozilla_dom_MediaKeySession_h
#include "mozilla/Attributes.h"
#include "mozilla/ErrorResult.h"
#include "nsCycleCollectionParticipant.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "nsCOMPtr.h"
#include "mozilla/dom/TypedArray.h"
#include "mozilla/Mutex.h"
#include "mozilla/dom/Date.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/MediaKeySessionBinding.h"
#include "mozilla/dom/MediaKeysBinding.h"
#include "mozilla/dom/MediaKeyMessageEventBinding.h"
struct JSContext;
namespace mozilla {
class CDMProxy;
namespace dom {
class ArrayBufferViewOrArrayBuffer;
class MediaKeyError;
class MediaKeyStatusMap;
class MediaKeySession final : public DOMEventTargetHelper
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaKeySession,
DOMEventTargetHelper)
public:
MediaKeySession(JSContext* aCx,
nsPIDOMWindow* aParent,
MediaKeys* aKeys,
const nsAString& aKeySystem,
SessionType aSessionType,
ErrorResult& aRv);
void SetSessionId(const nsAString& aSessionId);
virtual JSObject* WrapObject(JSContext* aCx) override;
// Mark this as resultNotAddRefed to return raw pointers
MediaKeyError* GetError() const;
MediaKeyStatusMap* KeyStatuses() const;
void GetKeySystem(nsString& aRetval) const;
void GetSessionId(nsString& aRetval) const;
const nsString& GetSessionId() const;
// Number of ms since epoch at which expiration occurs, or NaN if unknown.
// TODO: The type of this attribute is still under contention.
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=25902
double Expiration() const;
Promise* Closed() const;
already_AddRefed<Promise> GenerateRequest(const nsAString& aInitDataType,
const ArrayBufferViewOrArrayBuffer& aInitData,
ErrorResult& aRv);
already_AddRefed<Promise> Load(const nsAString& aSessionId,
ErrorResult& aRv);
already_AddRefed<Promise> Update(const ArrayBufferViewOrArrayBuffer& response,
ErrorResult& aRv);
already_AddRefed<Promise> Close(ErrorResult& aRv);
already_AddRefed<Promise> Remove(ErrorResult& aRv);
void DispatchKeyMessage(MediaKeyMessageType aMessageType,
const nsTArray<uint8_t>& aMessage);
void DispatchKeyError(uint32_t system_code);
void DispatchKeyStatusesChange();
void OnClosed();
bool IsClosed() const;
// Process-unique identifier.
uint32_t Token() const;
private:
~MediaKeySession();
void UpdateKeyStatusMap();
nsRefPtr<Promise> mClosed;
nsRefPtr<MediaKeyError> mMediaKeyError;
nsRefPtr<MediaKeys> mKeys;
const nsString mKeySystem;
nsString mSessionId;
const SessionType mSessionType;
const uint32_t mToken;
bool mIsClosed;
bool mUninitialized;
nsRefPtr<MediaKeyStatusMap> mKeyStatusMap;
};
} // namespace dom
} // namespace mozilla
#endif
-246
View File
@@ -1,246 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#include "mozilla/dom/MediaKeyStatusMap.h"
#include "nsPIDOMWindow.h"
#include "mozilla/dom/UnionTypes.h"
#include "mozilla/dom/ToJSValue.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaKeyStatusMap)
NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaKeyStatusMap)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaKeyStatusMap)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTION_CLASS(MediaKeyStatusMap)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MediaKeyStatusMap)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
tmp->mMap = nullptr;
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(MediaKeyStatusMap)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(MediaKeyStatusMap)
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mMap)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
MediaKeyStatusMap::MediaKeyStatusMap(JSContext* aCx,
nsPIDOMWindow* aParent,
ErrorResult& aRv)
: mParent(aParent)
, mUpdateError(NS_OK)
{
mMap = JS::NewMapObject(aCx);
if (NS_WARN_IF(!mMap)) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
}
mozilla::HoldJSObjects(this);
}
MediaKeyStatusMap::~MediaKeyStatusMap()
{
mozilla::DropJSObjects(this);
}
JSObject*
MediaKeyStatusMap::WrapObject(JSContext* aCx)
{
return MediaKeyStatusMapBinding::Wrap(aCx, this);
}
nsPIDOMWindow*
MediaKeyStatusMap::GetParentObject() const
{
return mParent;
}
MediaKeyStatus
MediaKeyStatusMap::Get(JSContext* aCx,
const ArrayBufferViewOrArrayBuffer& aKey,
ErrorResult& aRv) const
{
if (NS_FAILED(mUpdateError)) {
aRv.Throw(mUpdateError);
return MediaKeyStatus::Internal_error;
}
JS::Rooted<JSObject*> map(aCx, mMap);
JS::Rooted<JS::Value> key(aCx);
JS::Rooted<JS::Value> val(aCx);
if (!aKey.ToJSVal(aCx, map, &key) ||
!JS::MapGet(aCx, map, key, &val)) {
aRv.Throw(NS_ERROR_FAILURE);
return MediaKeyStatus::Internal_error;
}
bool ok;
int index = FindEnumStringIndex<true>(
aCx, val, MediaKeyStatusValues::strings,
"MediaKeyStatus", "Invalid MediaKeyStatus value", &ok);
return ok ? static_cast<MediaKeyStatus>(index) :
MediaKeyStatus::Internal_error;
}
bool
MediaKeyStatusMap::Has(JSContext* aCx,
const ArrayBufferViewOrArrayBuffer& aKey,
ErrorResult& aRv) const
{
if (NS_FAILED(mUpdateError)) {
aRv.Throw(mUpdateError);
return false;
}
JS::Rooted<JSObject*> map(aCx, mMap);
JS::Rooted<JS::Value> key(aCx);
bool result = false;
if (!aKey.ToJSVal(aCx, map, &key) ||
!JS::MapHas(aCx, map, key, &result)) {
aRv.Throw(NS_ERROR_FAILURE);
}
return result;
}
template<decltype(JS::MapKeys) Method>
static void CallMapMethod(JSContext* aCx,
const JS::Heap<JSObject*>& aMap,
JS::MutableHandle<JSObject*> aResult,
ErrorResult& aRv,
nsresult aUpdateError)
{
if (NS_FAILED(aUpdateError)) {
aRv.Throw(aUpdateError);
return;
}
JS::Rooted<JSObject*> map(aCx, aMap);
JS::Rooted<JS::Value> result(aCx);
if (!Method(aCx, map, &result)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
aResult.set(&result.toObject());
}
void
MediaKeyStatusMap::Keys(JSContext* aCx,
JS::MutableHandle<JSObject*> aResult,
ErrorResult& aRv) const
{
CallMapMethod<JS::MapKeys>(aCx, mMap, aResult, aRv, mUpdateError);
}
void
MediaKeyStatusMap::Values(JSContext* aCx,
JS::MutableHandle<JSObject*> aResult,
ErrorResult& aRv) const
{
CallMapMethod<JS::MapValues>(aCx, mMap, aResult, aRv, mUpdateError);
}
void
MediaKeyStatusMap::Entries(JSContext* aCx,
JS::MutableHandle<JSObject*> aResult,
ErrorResult& aRv) const
{
CallMapMethod<JS::MapEntries>(aCx, mMap, aResult, aRv, mUpdateError);
}
uint32_t
MediaKeyStatusMap::GetSize(JSContext* aCx, ErrorResult& aRv) const
{
if (NS_FAILED(mUpdateError)) {
aRv.Throw(mUpdateError);
return 0;
}
JS::Rooted<JSObject*> map(aCx, mMap);
return JS::MapSize(aCx, map);
}
static MediaKeyStatus
ToMediaKeyStatus(GMPMediaKeyStatus aStatus) {
switch (aStatus) {
case kGMPUsable: return MediaKeyStatus::Usable;
case kGMPExpired: return MediaKeyStatus::Expired;
case kGMPOutputDownscaled: return MediaKeyStatus::Output_downscaled;
case kGMPOutputNotAllowed: return MediaKeyStatus::Output_not_allowed;
case kGMPInternalError: return MediaKeyStatus::Internal_error;
default: return MediaKeyStatus::Internal_error;
}
}
static bool
ToJSString(JSContext* aCx, GMPMediaKeyStatus aStatus,
JS::MutableHandle<JS::Value> aResult)
{
auto val = uint32_t(ToMediaKeyStatus(aStatus));
MOZ_ASSERT(val < ArrayLength(MediaKeyStatusValues::strings));
JSString* str = JS_NewStringCopyN(aCx,
MediaKeyStatusValues::strings[val].value,
MediaKeyStatusValues::strings[val].length);
if (!str) {
return false;
}
aResult.setString(str);
return true;
}
nsresult
MediaKeyStatusMap::UpdateInternal(const nsTArray<CDMCaps::KeyStatus>& keys)
{
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(mParent))) {
return NS_ERROR_FAILURE;
}
jsapi.TakeOwnershipOfErrorReporting();
JSContext* cx = jsapi.cx();
JS::Rooted<JSObject*> map(cx, mMap);
if (!JS::MapClear(cx, map)) {
return NS_ERROR_FAILURE;
}
for (size_t i = 0; i < keys.Length(); i++) {
const auto& ks = keys[i];
JS::Rooted<JS::Value> key(cx);
JS::Rooted<JS::Value> val(cx);
if (!ToJSValue(cx, TypedArrayCreator<ArrayBuffer>(ks.mId), &key) ||
!ToJSString(cx, ks.mStatus, &val) ||
!JS::MapSet(cx, map, key, val)) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
return NS_OK;
}
void
MediaKeyStatusMap::Update(const nsTArray<CDMCaps::KeyStatus>& keys)
{
// Since we can't leave the map in a partial update state, we need
// to remember the error and throw it next time the interface methods
// are called.
mUpdateError = UpdateInternal(keys);
}
} // namespace dom
} // namespace mozilla
-81
View File
@@ -1,81 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 mozilla_dom_MediaKeyStatuses_h
#define mozilla_dom_MediaKeyStatuses_h
#include "mozilla/ErrorResult.h"
#include "mozilla/Attributes.h"
#include "nsCycleCollectionParticipant.h"
#include "nsWrapperCache.h"
#include "mozilla/dom/TypedArray.h"
#include "mozilla/dom/MediaKeyStatusMapBinding.h"
#include "mozilla/CDMCaps.h"
class nsPIDOMWindow;
namespace mozilla {
namespace dom {
class ArrayBufferViewOrArrayBuffer;
class MediaKeyStatusMap final : public nsISupports,
public nsWrapperCache
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MediaKeyStatusMap)
public:
explicit MediaKeyStatusMap(JSContext* aCx,
nsPIDOMWindow* aParent,
ErrorResult& aRv);
protected:
~MediaKeyStatusMap();
public:
nsPIDOMWindow* GetParentObject() const;
virtual JSObject* WrapObject(JSContext* aCx) override;
MediaKeyStatus Get(JSContext* aCx,
const ArrayBufferViewOrArrayBuffer& aKey,
ErrorResult& aRv) const;
bool Has(JSContext* aCx,
const ArrayBufferViewOrArrayBuffer& aKey,
ErrorResult& aRv) const;
void Keys(JSContext* aCx,
JS::MutableHandle<JSObject*> aResult,
ErrorResult& aRv) const;
void Values(JSContext* aCx,
JS::MutableHandle<JSObject*> aResult,
ErrorResult& aRv) const;
void Entries(JSContext* aCx,
JS::MutableHandle<JSObject*> aResult,
ErrorResult& aRv) const;
uint32_t GetSize(JSContext* aCx, ErrorResult& aRv) const;
void Update(const nsTArray<CDMCaps::KeyStatus>& keys);
private:
nsresult UpdateInternal(const nsTArray<CDMCaps::KeyStatus>& keys);
nsCOMPtr<nsPIDOMWindow> mParent;
JS::Heap<JSObject*> mMap;
nsresult mUpdateError;
};
} // namespace dom
} // namespace mozilla
#endif
-301
View File
@@ -1,301 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#include "mozilla/dom/MediaKeySystemAccess.h"
#include "mozilla/dom/MediaKeySystemAccessBinding.h"
#include "mozilla/Preferences.h"
#include "nsContentTypeParser.h"
#ifdef MOZ_FMP4
#include "MP4Decoder.h"
#endif
#ifdef XP_WIN
#include "mozilla/WindowsVersion.h"
#include "WMFDecoderModule.h"
#endif
#include "nsContentCID.h"
#include "nsServiceManagerUtils.h"
#include "mozIGoannaMediaPluginService.h"
#include "VideoUtils.h"
#include "mozilla/Services.h"
#include "nsIObserverService.h"
#include "mozilla/EMEUtils.h"
#include "GMPUtils.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MediaKeySystemAccess,
mParent)
NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaKeySystemAccess)
NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaKeySystemAccess)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaKeySystemAccess)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
MediaKeySystemAccess::MediaKeySystemAccess(nsPIDOMWindow* aParent,
const nsAString& aKeySystem)
: mParent(aParent)
, mKeySystem(aKeySystem)
{
}
MediaKeySystemAccess::~MediaKeySystemAccess()
{
}
JSObject*
MediaKeySystemAccess::WrapObject(JSContext* aCx)
{
return MediaKeySystemAccessBinding::Wrap(aCx, this);
}
nsPIDOMWindow*
MediaKeySystemAccess::GetParentObject() const
{
return mParent;
}
void
MediaKeySystemAccess::GetKeySystem(nsString& aRetVal) const
{
aRetVal = mKeySystem;
}
already_AddRefed<Promise>
MediaKeySystemAccess::CreateMediaKeys(ErrorResult& aRv)
{
nsRefPtr<MediaKeys> keys(new MediaKeys(mParent, mKeySystem));
return keys->Init(aRv);
}
static bool
HaveGMPFor(mozIGoannaMediaPluginService* aGMPService,
const nsCString& aKeySystem,
const nsCString& aAPI,
const nsCString& aTag = EmptyCString())
{
nsTArray<nsCString> tags;
tags.AppendElement(aKeySystem);
if (!aTag.IsEmpty()) {
tags.AppendElement(aTag);
}
bool hasPlugin = false;
if (NS_FAILED(aGMPService->HasPluginForAPI(aAPI,
&tags,
&hasPlugin))) {
return false;
}
return hasPlugin;
}
static MediaKeySystemStatus
EnsureMinCDMVersion(mozIGoannaMediaPluginService* aGMPService,
const nsAString& aKeySystem,
int32_t aMinCdmVersion)
{
if (aMinCdmVersion == NO_CDM_VERSION) {
return MediaKeySystemStatus::Available;
}
nsTArray<nsCString> tags;
tags.AppendElement(NS_ConvertUTF16toUTF8(aKeySystem));
nsAutoCString versionStr;
if (NS_FAILED(aGMPService->GetPluginVersionForAPI(NS_LITERAL_CSTRING(GMP_API_DECRYPTOR),
&tags,
versionStr)) &&
// XXX to be removed later in bug 1147692
NS_FAILED(aGMPService->GetPluginVersionForAPI(NS_LITERAL_CSTRING(GMP_API_DECRYPTOR_COMPAT),
&tags,
versionStr))) {
return MediaKeySystemStatus::Error;
}
nsresult rv;
int32_t version = versionStr.ToInteger(&rv);
if (NS_FAILED(rv) || version < 0 || aMinCdmVersion > version) {
return MediaKeySystemStatus::Cdm_insufficient_version;
}
return MediaKeySystemStatus::Available;
}
/* static */
MediaKeySystemStatus
MediaKeySystemAccess::GetKeySystemStatus(const nsAString& aKeySystem,
int32_t aMinCdmVersion)
{
MOZ_ASSERT(Preferences::GetBool("media.eme.enabled", false));
nsCOMPtr<mozIGoannaMediaPluginService> mps =
do_GetService("@mozilla.org/goanna-media-plugin-service;1");
if (NS_WARN_IF(!mps)) {
return MediaKeySystemStatus::Error;
}
if (aKeySystem.EqualsLiteral("org.w3.clearkey")) {
if (!Preferences::GetBool("media.eme.clearkey.enabled", true)) {
return MediaKeySystemStatus::Cdm_disabled;
}
if (!HaveGMPFor(mps,
NS_LITERAL_CSTRING("org.w3.clearkey"),
NS_LITERAL_CSTRING(GMP_API_DECRYPTOR))) {
return MediaKeySystemStatus::Cdm_not_installed;
}
return EnsureMinCDMVersion(mps, aKeySystem, aMinCdmVersion);
}
#ifdef XP_WIN
if ((aKeySystem.EqualsLiteral("com.adobe.access") ||
aKeySystem.EqualsLiteral("com.adobe.primetime"))) {
// Win Vista and later only.
if (!IsVistaOrLater()) {
return MediaKeySystemStatus::Cdm_not_supported;
}
if (!Preferences::GetBool("media.gmp-eme-adobe.enabled", false)) {
return MediaKeySystemStatus::Cdm_disabled;
}
if ((!WMFDecoderModule::HasH264() || !WMFDecoderModule::HasAAC()) ||
!EMEVoucherFileExists()) {
// The system doesn't have the codecs that Adobe EME relies
// on installed, or doesn't have a voucher for the plugin-container.
// Adobe EME isn't going to work, so don't advertise that it will.
return MediaKeySystemStatus::Cdm_not_supported;
}
if (!HaveGMPFor(mps,
NS_ConvertUTF16toUTF8(aKeySystem),
NS_LITERAL_CSTRING(GMP_API_DECRYPTOR)) &&
// XXX to be removed later in bug 1147692
!HaveGMPFor(mps,
NS_ConvertUTF16toUTF8(aKeySystem),
NS_LITERAL_CSTRING(GMP_API_DECRYPTOR_COMPAT))) {
return MediaKeySystemStatus::Cdm_not_installed;
}
return EnsureMinCDMVersion(mps, aKeySystem, aMinCdmVersion);
}
#endif
return MediaKeySystemStatus::Cdm_not_supported;
}
static bool
IsPlayableWithGMP(mozIGoannaMediaPluginService* aGMPS,
const nsAString& aKeySystem,
const nsAString& aContentType)
{
#ifdef MOZ_FMP4
nsContentTypeParser parser(aContentType);
nsAutoString mimeType;
nsresult rv = parser.GetType(mimeType);
if (NS_FAILED(rv)) {
return false;
}
if (!mimeType.EqualsLiteral("audio/mp4") &&
!mimeType.EqualsLiteral("audio/x-m4a") &&
!mimeType.EqualsLiteral("video/mp4")) {
return false;
}
nsAutoString codecs;
parser.GetParameter("codecs", codecs);
NS_ConvertUTF16toUTF8 mimeTypeUTF8(mimeType);
bool hasAAC = false;
bool hasH264 = false;
bool hasMP3 = false;
if (!MP4Decoder::CanHandleMediaType(mimeTypeUTF8,
codecs,
hasAAC,
hasH264,
hasMP3) ||
hasMP3) {
return false;
}
return (!hasAAC ||
!(HaveGMPFor(aGMPS,
NS_ConvertUTF16toUTF8(aKeySystem),
NS_LITERAL_CSTRING(GMP_API_DECRYPTOR),
NS_LITERAL_CSTRING("aac")) ||
// XXX remove later in bug 1147692
HaveGMPFor(aGMPS,
NS_ConvertUTF16toUTF8(aKeySystem),
NS_LITERAL_CSTRING(GMP_API_DECRYPTOR_COMPAT),
NS_LITERAL_CSTRING("aac")))) &&
(!hasH264 ||
!(HaveGMPFor(aGMPS,
NS_ConvertUTF16toUTF8(aKeySystem),
NS_LITERAL_CSTRING(GMP_API_DECRYPTOR),
NS_LITERAL_CSTRING("h264")) ||
// XXX remove later in bug 1147692
HaveGMPFor(aGMPS,
NS_ConvertUTF16toUTF8(aKeySystem),
NS_LITERAL_CSTRING(GMP_API_DECRYPTOR_COMPAT),
NS_LITERAL_CSTRING("h264"))));
#else
return false;
#endif
}
/* static */
bool
MediaKeySystemAccess::IsSupported(const nsAString& aKeySystem,
const Sequence<MediaKeySystemOptions>& aOptions)
{
nsCOMPtr<mozIGoannaMediaPluginService> mps =
do_GetService("@mozilla.org/goanna-media-plugin-service;1");
if (NS_WARN_IF(!mps)) {
return false;
}
for (size_t i = 0; i < aOptions.Length(); i++) {
const MediaKeySystemOptions& options = aOptions[i];
if (!options.mInitDataType.EqualsLiteral("cenc")) {
continue;
}
if (!options.mAudioCapability.IsEmpty() ||
!options.mVideoCapability.IsEmpty()) {
// Don't support any capabilites until we know we have a CDM with
// capabilities...
continue;
}
if (!options.mAudioType.IsEmpty() &&
!IsPlayableWithGMP(mps, aKeySystem, options.mAudioType)) {
continue;
}
if (!options.mVideoType.IsEmpty() &&
!IsPlayableWithGMP(mps, aKeySystem, options.mVideoType)) {
continue;
}
// Our sandbox provides an origin specific unique identifier, and the
// ability to persist data. We don't yet have a way to turn those off
// and on for specific GMPs/CDMs, so we don't check the uniqueidentifier
// and stateful attributes here.
return true;
}
return false;
}
/* static */
void
MediaKeySystemAccess::NotifyObservers(nsIDOMWindow* aWindow,
const nsAString& aKeySystem,
MediaKeySystemStatus aStatus)
{
RequestMediaKeySystemAccessNotification data;
data.mKeySystem = aKeySystem;
data.mStatus = aStatus;
nsAutoString json;
data.ToJSON(json);
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (obs) {
obs->NotifyObservers(aWindow, "mediakeys-request", json.get());
}
}
} // namespace dom
} // namespace mozilla
-65
View File
@@ -1,65 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 mozilla_dom_MediaKeySystemAccess_h
#define mozilla_dom_MediaKeySystemAccess_h
#include "mozilla/Attributes.h"
#include "mozilla/ErrorResult.h"
#include "nsCycleCollectionParticipant.h"
#include "nsWrapperCache.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/MediaKeySystemAccessBinding.h"
#include "mozilla/dom/MediaKeysRequestStatusBinding.h"
#include "js/TypeDecls.h"
namespace mozilla {
namespace dom {
class MediaKeySystemAccess final : public nsISupports,
public nsWrapperCache
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MediaKeySystemAccess)
public:
explicit MediaKeySystemAccess(nsPIDOMWindow* aParent,
const nsAString& aKeySystem);
protected:
~MediaKeySystemAccess();
public:
nsPIDOMWindow* GetParentObject() const;
virtual JSObject* WrapObject(JSContext* aCx) override;
void GetKeySystem(nsString& aRetVal) const;
already_AddRefed<Promise> CreateMediaKeys(ErrorResult& aRv);
static MediaKeySystemStatus GetKeySystemStatus(const nsAString& aKeySystem,
int32_t aMinCdmVersion);
static bool IsSupported(const nsAString& aKeySystem,
const Sequence<MediaKeySystemOptions>& aOptions);
static void NotifyObservers(nsIDOMWindow* aWindow,
const nsAString& aKeySystem,
MediaKeySystemStatus aStatus);
private:
nsCOMPtr<nsPIDOMWindow> mParent;
const nsString mKeySystem;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_MediaKeySystemAccess_h
@@ -1,280 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#include "MediaKeySystemAccessManager.h"
#include "mozilla/Preferences.h"
#include "mozilla/EMEUtils.h"
#include "nsServiceManagerUtils.h"
#include "nsComponentManagerUtils.h"
#include "nsIObserverService.h"
#include "mozilla/Services.h"
namespace mozilla {
namespace dom {
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaKeySystemAccessManager)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaKeySystemAccessManager)
NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaKeySystemAccessManager)
NS_IMPL_CYCLE_COLLECTION_CLASS(MediaKeySystemAccessManager)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MediaKeySystemAccessManager)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
for (size_t i = 0; i < tmp->mRequests.Length(); i++) {
tmp->mRequests[i].RejectPromise();
tmp->mRequests[i].CancelTimer();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mRequests[i].mPromise)
}
tmp->mRequests.Clear();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(MediaKeySystemAccessManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
for (size_t i = 0; i < tmp->mRequests.Length(); i++) {
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRequests[i].mPromise)
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
MediaKeySystemAccessManager::MediaKeySystemAccessManager(nsPIDOMWindow* aWindow)
: mWindow(aWindow)
, mAddedObservers(false)
{
}
MediaKeySystemAccessManager::~MediaKeySystemAccessManager()
{
Shutdown();
}
void
MediaKeySystemAccessManager::Request(Promise* aPromise,
const nsAString& aKeySystem,
const Optional<Sequence<MediaKeySystemOptions>>& aOptions)
{
if (aKeySystem.IsEmpty() || (aOptions.WasPassed() && aOptions.Value().IsEmpty())) {
aPromise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
return;
}
Sequence<MediaKeySystemOptions> optionsNotPassed;
const auto& options = aOptions.WasPassed() ? aOptions.Value() : optionsNotPassed;
Request(aPromise, aKeySystem, options, RequestType::Initial);
}
void
MediaKeySystemAccessManager::Request(Promise* aPromise,
const nsAString& aKeySystem,
const Sequence<MediaKeySystemOptions>& aOptions,
RequestType aType)
{
EME_LOG("MediaKeySystemAccessManager::Request %s", NS_ConvertUTF16toUTF8(aKeySystem).get());
if (!Preferences::GetBool("media.eme.enabled", false)) {
// EME disabled by user, send notification to chrome so UI can
// inform user.
MediaKeySystemAccess::NotifyObservers(mWindow,
aKeySystem,
MediaKeySystemStatus::Api_disabled);
aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return;
}
// Parse keysystem, split it out into keySystem prefix, and version suffix.
nsAutoString keySystem;
int32_t minCdmVersion = NO_CDM_VERSION;
if (!ParseKeySystem(aKeySystem,
keySystem,
minCdmVersion)) {
// Invalid keySystem string, or unsupported keySystem. Send notification
// to chrome to show a failure notice.
MediaKeySystemAccess::NotifyObservers(mWindow, aKeySystem, MediaKeySystemStatus::Cdm_not_supported);
aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return;
}
MediaKeySystemStatus status = MediaKeySystemAccess::GetKeySystemStatus(keySystem, minCdmVersion);
if ((status == MediaKeySystemStatus::Cdm_not_installed ||
status == MediaKeySystemStatus::Cdm_insufficient_version) &&
keySystem.EqualsLiteral("com.adobe.primetime")) {
// These are cases which could be resolved by downloading a new(er) CDM.
// When we send the status to chrome, chrome's GMPProvider will attempt to
// download or update the CDM. In AwaitInstall() we add listeners to wait
// for the update to complete, and we'll call this function again with
// aType==Subsequent once the download has completed and the GMPService
// has had a new plugin added. AwaitInstall() sets a timer to fail if the
// update/download takes too long or fails.
if (aType == RequestType::Initial &&
AwaitInstall(aPromise, aKeySystem, aOptions)) {
// Notify chrome that we're going to wait for the CDM to download/update.
// Note: If we're re-trying, we don't re-send the notificaiton,
// as chrome is already displaying the "we can't play, updating"
// notification.
MediaKeySystemAccess::NotifyObservers(mWindow, keySystem, status);
} else {
// We waited or can't wait for an update and we still can't service
// the request. Give up. Chrome will still be showing a "I can't play,
// updating" notification.
aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
}
return;
}
if (status != MediaKeySystemStatus::Available) {
if (status != MediaKeySystemStatus::Error) {
// Failed due to user disabling something, send a notification to
// chrome, so we can show some UI to explain how the user can rectify
// the situation.
MediaKeySystemAccess::NotifyObservers(mWindow, keySystem, status);
}
aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return;
}
if (aOptions.IsEmpty() ||
MediaKeySystemAccess::IsSupported(keySystem, aOptions)) {
nsRefPtr<MediaKeySystemAccess> access(new MediaKeySystemAccess(mWindow, keySystem));
aPromise->MaybeResolve(access);
return;
}
aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
}
MediaKeySystemAccessManager::PendingRequest::PendingRequest(Promise* aPromise,
const nsAString& aKeySystem,
const Sequence<MediaKeySystemOptions>& aOptions,
nsITimer* aTimer)
: mPromise(aPromise)
, mKeySystem(aKeySystem)
, mOptions(aOptions)
, mTimer(aTimer)
{
MOZ_COUNT_CTOR(MediaKeySystemAccessManager::PendingRequest);
}
MediaKeySystemAccessManager::PendingRequest::PendingRequest(const PendingRequest& aOther)
: mPromise(aOther.mPromise)
, mKeySystem(aOther.mKeySystem)
, mOptions(aOther.mOptions)
, mTimer(aOther.mTimer)
{
MOZ_COUNT_CTOR(MediaKeySystemAccessManager::PendingRequest);
}
MediaKeySystemAccessManager::PendingRequest::~PendingRequest()
{
MOZ_COUNT_DTOR(MediaKeySystemAccessManager::PendingRequest);
}
void
MediaKeySystemAccessManager::PendingRequest::CancelTimer()
{
if (mTimer) {
mTimer->Cancel();
}
}
void
MediaKeySystemAccessManager::PendingRequest::RejectPromise()
{
if (mPromise) {
mPromise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
}
}
bool
MediaKeySystemAccessManager::AwaitInstall(Promise* aPromise,
const nsAString& aKeySystem,
const Sequence<MediaKeySystemOptions>& aOptions)
{
EME_LOG("MediaKeySystemAccessManager::AwaitInstall %s", NS_ConvertUTF16toUTF8(aKeySystem).get());
if (!EnsureObserversAdded()) {
NS_WARNING("Failed to add pref observer");
return false;
}
nsCOMPtr<nsITimer> timer(do_CreateInstance("@mozilla.org/timer;1"));
if (!timer || NS_FAILED(timer->Init(this, 60 * 1000, nsITimer::TYPE_ONE_SHOT))) {
NS_WARNING("Failed to create timer to await CDM install.");
return false;
}
mRequests.AppendElement(PendingRequest(aPromise, aKeySystem, aOptions, timer));
return true;
}
void
MediaKeySystemAccessManager::RetryRequest(PendingRequest& aRequest)
{
aRequest.CancelTimer();
Request(aRequest.mPromise, aRequest.mKeySystem, aRequest.mOptions, RequestType::Subsequent);
}
nsresult
MediaKeySystemAccessManager::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aData)
{
EME_LOG("MediaKeySystemAccessManager::Observe %s", aTopic);
if (!strcmp(aTopic, "gmp-path-added")) {
nsTArray<PendingRequest> requests(Move(mRequests));
// Retry all pending requests, but this time fail if the CDM is not installed.
for (PendingRequest& request : requests) {
RetryRequest(request);
}
} else if (!strcmp(aTopic, "timer-callback")) {
// Find the timer that expired and re-run the request for it.
nsCOMPtr<nsITimer> timer(do_QueryInterface(aSubject));
for (size_t i = 0; i < mRequests.Length(); i++) {
if (mRequests[i].mTimer == timer) {
EME_LOG("MediaKeySystemAccessManager::AwaitInstall resuming request");
PendingRequest request = mRequests[i];
mRequests.RemoveElementAt(i);
RetryRequest(request);
break;
}
}
}
return NS_OK;
}
bool
MediaKeySystemAccessManager::EnsureObserversAdded()
{
if (mAddedObservers) {
return true;
}
nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
if (NS_WARN_IF(!obsService)) {
return false;
}
mAddedObservers = NS_SUCCEEDED(obsService->AddObserver(this, "gmp-path-added", false));
return mAddedObservers;
}
void
MediaKeySystemAccessManager::Shutdown()
{
EME_LOG("MediaKeySystemAccessManager::Shutdown");
nsTArray<PendingRequest> requests(Move(mRequests));
for (PendingRequest& request : requests) {
// Cancel all requests; we're shutting down.
request.CancelTimer();
request.RejectPromise();
}
if (mAddedObservers) {
nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
if (obsService) {
obsService->RemoveObserver(this, "gmp-path-added");
mAddedObservers = false;
}
}
}
} // namespace dom
} // namespace mozilla
@@ -1,80 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 mozilla_dom_MediaKeySystemAccessManager_h
#define mozilla_dom_MediaKeySystemAccessManager_h
#include "mozilla/dom/MediaKeySystemAccess.h"
#include "nsIObserver.h"
#include "nsCycleCollectionParticipant.h"
#include "nsISupportsImpl.h"
#include "nsITimer.h"
namespace mozilla {
namespace dom {
class MediaKeySystemAccessManager final : public nsIObserver
{
public:
explicit MediaKeySystemAccessManager(nsPIDOMWindow* aWindow);
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(MediaKeySystemAccessManager, nsIObserver)
NS_DECL_NSIOBSERVER
void Request(Promise* aPromise,
const nsAString& aKeySystem,
const Optional<Sequence<MediaKeySystemOptions>>& aOptions);
void Shutdown();
struct PendingRequest {
PendingRequest(Promise* aPromise,
const nsAString& aKeySystem,
const Sequence<MediaKeySystemOptions>& aOptions,
nsITimer* aTimer);
PendingRequest(const PendingRequest& aOther);
~PendingRequest();
void CancelTimer();
void RejectPromise();
nsRefPtr<Promise> mPromise;
const nsString mKeySystem;
const Sequence<MediaKeySystemOptions> mOptions;
nsCOMPtr<nsITimer> mTimer;
};
private:
enum RequestType {
Initial,
Subsequent
};
void Request(Promise* aPromise,
const nsAString& aKeySystem,
const Sequence<MediaKeySystemOptions>& aOptions,
RequestType aType);
~MediaKeySystemAccessManager();
bool EnsureObserversAdded();
bool AwaitInstall(Promise* aPromise,
const nsAString& aKeySystem,
const Sequence<MediaKeySystemOptions>& aOptions);
void RetryRequest(PendingRequest& aRequest);
nsTArray<PendingRequest> mRequests;
nsCOMPtr<nsPIDOMWindow> mWindow;
bool mAddedObservers;
};
} // namespace dom
} // namespace mozilla
#endif
-597
View File
@@ -1,597 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#include "mozilla/dom/MediaKeys.h"
#include "GMPService.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/dom/HTMLMediaElement.h"
#include "mozilla/dom/MediaKeysBinding.h"
#include "mozilla/dom/MediaKeyMessageEvent.h"
#include "mozilla/dom/MediaKeyError.h"
#include "mozilla/dom/MediaKeySession.h"
#include "mozilla/dom/DOMException.h"
#include "mozilla/dom/PluginCrashedEvent.h"
#include "mozilla/dom/UnionTypes.h"
#include "mozilla/CDMProxy.h"
#include "mozilla/EMEUtils.h"
#include "nsContentUtils.h"
#include "nsIScriptObjectPrincipal.h"
#include "mozilla/Preferences.h"
#include "nsContentTypeParser.h"
#ifdef MOZ_FMP4
#include "MP4Decoder.h"
#endif
#ifdef XP_WIN
#include "mozilla/WindowsVersion.h"
#endif
#include "nsContentCID.h"
#include "nsServiceManagerUtils.h"
#include "mozilla/dom/MediaKeySystemAccess.h"
#include "nsPrintfCString.h"
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
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
MediaKeys::MediaKeys(nsPIDOMWindow* aParent, const nsAString& aKeySystem)
: mParent(aParent)
, mKeySystem(aKeySystem)
, mCreatePromiseId(0)
{
}
static PLDHashOperator
RejectPromises(const uint32_t& aKey,
nsRefPtr<dom::Promise>& aPromise,
void* aClosure)
{
aPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
((MediaKeys*)aClosure)->Release();
return PL_DHASH_NEXT;
}
MediaKeys::~MediaKeys()
{
Shutdown();
}
static PLDHashOperator
CopySessions(const nsAString& aKey,
nsRefPtr<MediaKeySession>& aSession,
void* aClosure)
{
KeySessionHashMap* p = static_cast<KeySessionHashMap*>(aClosure);
p->Put(aSession->GetSessionId(), aSession);
return PL_DHASH_NEXT;
}
static PLDHashOperator
CloseSessions(const nsAString& aKey,
nsRefPtr<MediaKeySession>& aSession,
void* aClosure)
{
aSession->OnClosed();
return PL_DHASH_NEXT;
}
void
MediaKeys::Terminated()
{
KeySessionHashMap keySessions;
// Remove entries during iteration will screw it. Make a copy first.
mKeySessions.Enumerate(&CopySessions, &keySessions);
keySessions.Enumerate(&CloseSessions, nullptr);
keySessions.Clear();
MOZ_ASSERT(mKeySessions.Count() == 0);
// Notify the element about that CDM has terminated.
if (mElement) {
mElement->DecodeError();
}
Shutdown();
}
void
MediaKeys::Shutdown()
{
if (mProxy) {
mProxy->Shutdown();
mProxy = nullptr;
}
nsRefPtr<MediaKeys> kungFuDeathGrip = this;
mPromises.Enumerate(&RejectPromises, this);
mPromises.Clear();
}
nsPIDOMWindow*
MediaKeys::GetParentObject() const
{
return mParent;
}
JSObject*
MediaKeys::WrapObject(JSContext* aCx)
{
return MediaKeysBinding::Wrap(aCx, this);
}
void
MediaKeys::GetKeySystem(nsString& retval) const
{
retval = mKeySystem;
}
already_AddRefed<Promise>
MediaKeys::SetServerCertificate(const ArrayBufferViewOrArrayBuffer& aCert, ErrorResult& aRv)
{
nsRefPtr<Promise> promise(MakePromise(aRv));
if (aRv.Failed()) {
return nullptr;
}
if (!mProxy) {
NS_WARNING("Tried to use a MediaKeys without a CDM");
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
return promise.forget();
}
nsTArray<uint8_t> data;
if (!CopyArrayBufferViewOrArrayBufferData(aCert, data)) {
promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
return promise.forget();
}
mProxy->SetServerCertificate(StorePromise(promise), data);
return promise.forget();
}
already_AddRefed<Promise>
MediaKeys::MakePromise(ErrorResult& aRv)
{
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
if (!global) {
NS_WARNING("Passed non-global to MediaKeys ctor!");
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
return Promise::Create(global, aRv);
}
PromiseId
MediaKeys::StorePromise(Promise* aPromise)
{
static uint32_t sEMEPromiseCount = 1;
MOZ_ASSERT(aPromise);
uint32_t id = sEMEPromiseCount++;
EME_LOG("MediaKeys[%p]::StorePromise() id=%d", this, id);
// Keep MediaKeys alive for the lifetime of its promises. Any still-pending
// promises are rejected in Shutdown().
AddRef();
mPromises.Put(id, aPromise);
return id;
}
already_AddRefed<Promise>
MediaKeys::RetrievePromise(PromiseId aId)
{
if (!mPromises.Contains(aId)) {
NS_WARNING(nsPrintfCString("Tried to retrieve a non-existent promise id=%d", aId).get());
return nullptr;
}
nsRefPtr<Promise> promise;
mPromises.Remove(aId, getter_AddRefs(promise));
Release();
return promise.forget();
}
void
MediaKeys::RejectPromise(PromiseId aId, nsresult aExceptionCode)
{
EME_LOG("MediaKeys[%p]::RejectPromise(%d, 0x%x)", this, aId, aExceptionCode);
nsRefPtr<Promise> promise(RetrievePromise(aId));
if (!promise) {
return;
}
if (mPendingSessions.Contains(aId)) {
// This promise could be a createSession or loadSession promise,
// so we might have a pending session waiting to be resolved into
// the promise on success. We've been directed to reject to promise,
// so we can throw away the corresponding session object.
mPendingSessions.Remove(aId);
}
MOZ_ASSERT(NS_FAILED(aExceptionCode));
promise->MaybeReject(aExceptionCode);
if (mCreatePromiseId == aId) {
// Note: This will probably destroy the MediaKeys object!
Release();
}
}
void
MediaKeys::OnSessionIdReady(MediaKeySession* aSession)
{
if (!aSession) {
NS_WARNING("Invalid MediaKeySession passed to OnSessionIdReady()");
return;
}
if (mKeySessions.Contains(aSession->GetSessionId())) {
NS_WARNING("MediaKeySession's made ready multiple times!");
return;
}
if (mPendingSessions.Contains(aSession->Token())) {
NS_WARNING("MediaKeySession made ready when it wasn't waiting to be ready!");
return;
}
if (aSession->GetSessionId().IsEmpty()) {
NS_WARNING("MediaKeySession with invalid sessionId passed to OnSessionIdReady()");
return;
}
mKeySessions.Put(aSession->GetSessionId(), aSession);
}
void
MediaKeys::ResolvePromise(PromiseId aId)
{
EME_LOG("MediaKeys[%p]::ResolvePromise(%d)", this, aId);
nsRefPtr<Promise> promise(RetrievePromise(aId));
if (!promise) {
return;
}
if (mPendingSessions.Contains(aId)) {
// We should only resolve LoadSession calls via this path,
// not CreateSession() promises.
nsRefPtr<MediaKeySession> session;
if (!mPendingSessions.Get(aId, getter_AddRefs(session)) ||
!session ||
session->GetSessionId().IsEmpty()) {
NS_WARNING("Received activation for non-existent session!");
promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
mPendingSessions.Remove(aId);
return;
}
mPendingSessions.Remove(aId);
mKeySessions.Put(session->GetSessionId(), session);
promise->MaybeResolve(session);
} else {
promise->MaybeResolve(JS::UndefinedHandleValue);
}
}
already_AddRefed<Promise>
MediaKeys::Init(ErrorResult& aRv)
{
nsRefPtr<Promise> promise(MakePromise(aRv));
if (aRv.Failed()) {
return nullptr;
}
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[%p]::Create() (%s, %s), %s",
this,
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(!mCreatePromiseId, "Should only be created once!");
mCreatePromiseId = StorePromise(promise);
AddRef();
mProxy->Init(mCreatePromiseId,
origin,
topLevelOrigin,
inPrivateBrowsing);
return promise.forget();
}
class CrashHandler : public gmp::GoannaMediaPluginService::PluginCrashCallback
{
public:
CrashHandler(const nsACString& aPluginId,
nsPIDOMWindow* aParentWindow,
nsIDocument* aDocument)
: gmp::GoannaMediaPluginService::PluginCrashCallback(aPluginId)
, mParentWindowWeakPtr(do_GetWeakReference(aParentWindow))
, mDocumentWeakPtr(do_GetWeakReference(aDocument))
{
}
virtual void Run(const nsACString& aPluginName, const nsAString& aPluginDumpId) override
{
PluginCrashedEventInit init;
init.mBubbles = true;
init.mCancelable = true;
init.mGmpPlugin = true;
init.mPluginDumpID = aPluginDumpId;
CopyUTF8toUTF16(aPluginName, init.mPluginName);
init.mSubmittedCrashReport = false;
// The following PluginCrashedEvent fields stay empty:
// init.mBrowserDumpID
// init.mPluginFilename
// TODO: Can/should we fill them?
nsCOMPtr<nsPIDOMWindow> parentWindow;
nsCOMPtr<nsIDocument> document;
if (!GetParentWindowAndDocumentIfValid(parentWindow, document)) {
return;
}
nsRefPtr<PluginCrashedEvent> event =
PluginCrashedEvent::Constructor(document, NS_LITERAL_STRING("PluginCrashed"), init);
event->SetTrusted(true);
event->GetInternalNSEvent()->mFlags.mOnlyChromeDispatch = true;
EventDispatcher::DispatchDOMEvent(parentWindow, nullptr, event, nullptr, nullptr);
}
virtual bool IsStillValid() override
{
nsCOMPtr<nsPIDOMWindow> parentWindow;
nsCOMPtr<nsIDocument> document;
return GetParentWindowAndDocumentIfValid(parentWindow, document);
}
private:
virtual ~CrashHandler()
{ }
bool
GetParentWindowAndDocumentIfValid(nsCOMPtr<nsPIDOMWindow>& parentWindow,
nsCOMPtr<nsIDocument>& document)
{
parentWindow = do_QueryReferent(mParentWindowWeakPtr);
if (!parentWindow) {
return false;
}
document = do_QueryReferent(mDocumentWeakPtr);
if (!document) {
return false;
}
nsCOMPtr<nsIDocument> parentWindowDocument = parentWindow->GetExtantDoc();
if (!parentWindowDocument || document.get() != parentWindowDocument.get()) {
return false;
}
return true;
}
nsWeakPtr mParentWindowWeakPtr;
nsWeakPtr mDocumentWeakPtr;
};
void
MediaKeys::OnCDMCreated(PromiseId aId, const nsACString& aNodeId, const nsACString& aPluginId)
{
nsRefPtr<Promise> promise(RetrievePromise(aId));
if (!promise) {
return;
}
mNodeId = aNodeId;
nsRefPtr<MediaKeys> keys(this);
EME_LOG("MediaKeys[%p]::OnCDMCreated() resolve promise id=%d", this, aId);
promise->MaybeResolve(keys);
if (mCreatePromiseId == aId) {
Release();
}
MediaKeySystemAccess::NotifyObservers(mParent,
mKeySystem,
MediaKeySystemStatus::Cdm_created);
if (!aPluginId.IsEmpty()) {
// Prepare plugin crash reporter.
nsRefPtr<gmp::GoannaMediaPluginService> service =
gmp::GoannaMediaPluginService::GetGoannaMediaPluginService();
if (NS_WARN_IF(!service)) {
return;
}
if (NS_WARN_IF(!mParent)) {
return;
}
nsCOMPtr<nsIDocument> doc = mParent->GetExtantDoc();
if (NS_WARN_IF(!doc)) {
return;
}
service->AddPluginCrashCallback(new CrashHandler(aPluginId, mParent, doc));
EME_LOG("MediaKeys[%p]::OnCDMCreated() registered crash handler for pluginId '%s'",
this, aPluginId.Data());
}
}
already_AddRefed<MediaKeySession>
MediaKeys::CreateSession(JSContext* aCx,
SessionType aSessionType,
ErrorResult& aRv)
{
if (!mProxy) {
NS_WARNING("Tried to use a MediaKeys which lost its CDM");
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr;
}
nsRefPtr<MediaKeySession> session = new MediaKeySession(aCx,
GetParentObject(),
this,
mKeySystem,
aSessionType,
aRv);
if (aRv.Failed()) {
return nullptr;
}
// Add session to the set of sessions awaiting their sessionId being ready.
mPendingSessions.Put(session->Token(), session);
return session.forget();
}
void
MediaKeys::OnSessionLoaded(PromiseId aId, bool aSuccess)
{
nsRefPtr<Promise> promise(RetrievePromise(aId));
if (!promise) {
return;
}
EME_LOG("MediaKeys[%p]::OnSessionLoaded() resolve promise id=%d", this, aId);
promise->MaybeResolve(aSuccess);
}
void
MediaKeys::OnSessionClosed(MediaKeySession* aSession)
{
nsAutoString id;
aSession->GetSessionId(id);
mKeySessions.Remove(id);
}
already_AddRefed<MediaKeySession>
MediaKeys::GetSession(const nsAString& aSessionId)
{
nsRefPtr<MediaKeySession> session;
mKeySessions.Get(aSessionId, getter_AddRefs(session));
return session.forget();
}
already_AddRefed<MediaKeySession>
MediaKeys::GetPendingSession(uint32_t aToken)
{
nsRefPtr<MediaKeySession> session;
mPendingSessions.Get(aToken, getter_AddRefs(session));
mPendingSessions.Remove(aToken);
return session.forget();
}
const nsCString&
MediaKeys::GetNodeId() const
{
MOZ_ASSERT(NS_IsMainThread());
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;
return NS_OK;
}
bool
CopyArrayBufferViewOrArrayBufferData(const ArrayBufferViewOrArrayBuffer& aBufferOrView,
nsTArray<uint8_t>& aOutData)
{
if (aBufferOrView.IsArrayBuffer()) {
const ArrayBuffer& buffer = aBufferOrView.GetAsArrayBuffer();
buffer.ComputeLengthAndData();
aOutData.AppendElements(buffer.Data(), buffer.Length());
} else if (aBufferOrView.IsArrayBufferView()) {
const ArrayBufferView& bufferview = aBufferOrView.GetAsArrayBufferView();
bufferview.ComputeLengthAndData();
aOutData.AppendElements(bufferview.Data(), bufferview.Length());
} else {
return false;
}
return true;
}
} // namespace dom
} // namespace mozilla
-151
View File
@@ -1,151 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 mozilla_dom_mediakeys_h__
#define mozilla_dom_mediakeys_h__
#include "nsIDOMMediaError.h"
#include "nsWrapperCache.h"
#include "nsISupports.h"
#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 "mozIGoannaMediaPluginService.h"
namespace mozilla {
class CDMProxy;
namespace dom {
class ArrayBufferViewOrArrayBuffer;
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.
bool
CopyArrayBufferViewOrArrayBufferData(const ArrayBufferViewOrArrayBuffer& aBufferOrView,
nsTArray<uint8_t>& aOutData);
// This class is used on the main thread only.
// Note: it's addref/release is not (and can't be) thread safe!
class MediaKeys final : public nsISupports,
public nsWrapperCache
{
~MediaKeys();
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MediaKeys)
MediaKeys(nsPIDOMWindow* aParentWindow, const nsAString& aKeySystem);
already_AddRefed<Promise> Init(ErrorResult& aRv);
nsPIDOMWindow* GetParentObject() const;
virtual JSObject* WrapObject(JSContext* aCx) override;
nsresult Bind(HTMLMediaElement* aElement);
// Javascript: readonly attribute DOMString keySystem;
void GetKeySystem(nsString& retval) const;
// JavaScript: MediaKeys.createSession()
already_AddRefed<MediaKeySession> CreateSession(JSContext* aCx,
SessionType aSessionType,
ErrorResult& aRv);
// JavaScript: MediaKeys.SetServerCertificate()
already_AddRefed<Promise> SetServerCertificate(const ArrayBufferViewOrArrayBuffer& aServerCertificate,
ErrorResult& aRv);
already_AddRefed<MediaKeySession> GetSession(const nsAString& aSessionId);
// Removes and returns MediaKeySession from the set of sessions awaiting
// their sessionId to be assigned.
already_AddRefed<MediaKeySession> GetPendingSession(uint32_t aToken);
// Called once a Init() operation succeeds.
void OnCDMCreated(PromiseId aId,
const nsACString& aNodeId, const nsACString& aPluginId);
// Called once the CDM generates a sessionId while servicing a
// MediaKeySession.generateRequest() or MediaKeySession.load() call,
// once the sessionId of a MediaKeySession is known.
void OnSessionIdReady(MediaKeySession* aSession);
// Called once a LoadSession succeeds.
void OnSessionLoaded(PromiseId aId, bool aSuccess);
// Called once a session has closed.
void OnSessionClosed(MediaKeySession* aSession);
CDMProxy* GetCDMProxy() { return mProxy; }
// Makes a new promise, or nullptr on failure.
already_AddRefed<Promise> MakePromise(ErrorResult& aRv);
// Stores promise in mPromises, returning an ID that can be used to retrieve
// it later. The ID is passed to the CDM, so that it can signal specific
// 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;
void Shutdown();
// Called by CDMProxy when CDM crashes or shuts down. It is different from
// Shutdown which is called from the script/dom side.
void Terminated();
// Returns true if this MediaKeys has been bound to a media element.
bool IsBoundToMediaElement() const;
private:
bool IsInPrivateBrowsing();
// 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__
-42
View File
@@ -1,42 +0,0 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# 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/.
EXPORTS.mozilla.dom += [
'MediaEncryptedEvent.h',
'MediaKeyError.h',
'MediaKeyMessageEvent.h',
'MediaKeys.h',
'MediaKeySession.h',
'MediaKeyStatusMap.h',
'MediaKeySystemAccess.h',
'MediaKeySystemAccessManager.h',
]
EXPORTS.mozilla += [
'CDMCallbackProxy.h',
'CDMCaps.h',
'CDMProxy.h',
'EMEUtils.h'
]
UNIFIED_SOURCES += [
'CDMCallbackProxy.cpp',
'CDMCaps.cpp',
'CDMProxy.cpp',
'EMEUtils.cpp',
'MediaEncryptedEvent.cpp',
'MediaKeyError.cpp',
'MediaKeyMessageEvent.cpp',
'MediaKeys.cpp',
'MediaKeySession.cpp',
'MediaKeyStatusMap.cpp',
'MediaKeySystemAccess.cpp',
'MediaKeySystemAccessManager.cpp',
]
FINAL_LIBRARY = 'xul'
FAIL_ON_WARNINGS = True
-22
View File
@@ -9,9 +9,6 @@
#include "MediaDecoderStateMachine.h"
#include "mozilla/Preferences.h"
#include "nsCharSeparatedTokenizer.h"
#ifdef MOZ_EME
#include "mozilla/CDMProxy.h"
#endif
#include "prlog.h"
#ifdef XP_WIN
@@ -36,25 +33,6 @@ MediaDecoderStateMachine* MP4Decoder::CreateStateMachine()
return new MediaDecoderStateMachine(this, new MP4Reader(this));
}
#ifdef MOZ_EME
nsresult
MP4Decoder::SetCDMProxy(CDMProxy* aProxy)
{
nsresult rv = MediaDecoder::SetCDMProxy(aProxy);
NS_ENSURE_SUCCESS(rv, rv);
if (aProxy) {
// The MP4Reader can't decrypt EME content until it has a CDMProxy,
// and the CDMProxy knows the capabilities of the CDM. The MP4Reader
// remains in "waiting for resources" state until then.
CDMCaps::AutoLock caps(aProxy->Capabilites());
nsRefPtr<nsIRunnable> task(
NS_NewRunnableMethod(this, &MediaDecoder::NotifyWaitingForResourcesStatusChanged));
caps.CallOnMainThreadWhenCapsAvailable(task);
}
return NS_OK;
}
#endif
static bool
IsSupportedAudioCodec(const nsAString& aCodec,
bool& aOutContainsAAC,
-4
View File
@@ -24,10 +24,6 @@ public:
virtual MediaDecoderStateMachine* CreateStateMachine() override;
#ifdef MOZ_EME
virtual nsresult SetCDMProxy(CDMProxy* aProxy) override;
#endif
// Returns true if aMIMEType is a type that we think we can render with the
// a MP4 platform decoder backend. If aCodecs is non emtpy, it is filled
// with a comma-delimited list of codecs to check support for. Notes in
+1 -88
View File
@@ -22,10 +22,6 @@
#include "SharedDecoderManager.h"
#include <algorithm>
#ifdef MOZ_EME
#include "mozilla/CDMProxy.h"
#endif
using mozilla::layers::Image;
using mozilla::layers::LayerManager;
using mozilla::layers::LayersBackend;
@@ -263,34 +259,6 @@ MP4Reader::Init(MediaDecoderReader* aCloneDonor)
return NS_OK;
}
#ifdef MOZ_EME
class DispatchKeyNeededEvent : public nsRunnable {
public:
DispatchKeyNeededEvent(AbstractMediaDecoder* aDecoder,
nsTArray<uint8_t>& aInitData,
const nsString& aInitDataType)
: mDecoder(aDecoder)
, mInitData(aInitData)
, mInitDataType(aInitDataType)
{
}
NS_IMETHOD Run() {
// Note: Null check the owner, as the decoder could have been shutdown
// since this event was dispatched.
MediaDecoderOwner* owner = mDecoder->GetOwner();
if (owner) {
owner->DispatchEncrypted(mInitData, mInitDataType);
}
mDecoder = nullptr;
return NS_OK;
}
private:
nsRefPtr<AbstractMediaDecoder> mDecoder;
nsTArray<uint8_t> mInitData;
nsString mInitDataType;
};
#endif
void MP4Reader::RequestCodecResource() {
if (mVideo.mDecoder) {
mVideo.mDecoder->AllocateMediaResources();
@@ -302,29 +270,8 @@ bool MP4Reader::IsWaitingOnCodecResource() {
}
bool MP4Reader::IsWaitingOnCDMResource() {
#ifdef MOZ_EME
nsRefPtr<CDMProxy> proxy;
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
if (!mIsEncrypted) {
// Not encrypted, no need to wait for CDMProxy.
return false;
}
proxy = mDecoder->GetCDMProxy();
if (!proxy) {
// We're encrypted, we need a CDMProxy to decrypt file.
return true;
}
}
// We'll keep waiting if the CDM hasn't informed Goanna of its capabilities.
{
CDMCaps::AutoLock caps(proxy->Capabilites());
LOG("capsKnown=%d", caps.AreCapsKnown());
return !caps.AreCapsKnown();
}
#else
// EME Stub
return false;
#endif
}
bool MP4Reader::IsWaitingMediaResources()
@@ -411,42 +358,8 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo,
}
if (mDemuxer->Crypto().valid) {
#ifdef MOZ_EME
// We have encrypted audio or video. We'll need a CDM to decrypt and
// possibly decode this. Wait until we've received a CDM from the
// JavaScript player app. Note: we still go through the motions here
// even if EME is disabled, so that if script tries and fails to create
// a CDM, we can detect that and notify chrome and show some UI explaining
// that we failed due to EME being disabled.
nsRefPtr<CDMProxy> proxy;
nsTArray<uint8_t> initData;
ExtractCryptoInitData(initData);
if (initData.Length() == 0) {
return NS_ERROR_FAILURE;
}
if (!mInitDataEncountered.Contains(initData)) {
mInitDataEncountered.AppendElement(initData);
NS_DispatchToMainThread(new DispatchKeyNeededEvent(mDecoder, initData, NS_LITERAL_STRING("cenc")));
}
if (IsWaitingMediaResources()) {
return NS_OK;
}
MOZ_ASSERT(!IsWaitingMediaResources());
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
proxy = mDecoder->GetCDMProxy();
}
MOZ_ASSERT(proxy);
mPlatform = PlatformDecoderModule::CreateCDMWrapper(proxy,
HasAudio(),
HasVideo());
NS_ENSURE_TRUE(mPlatform, NS_ERROR_FAILURE);
#else
// EME not supported.
return NS_ERROR_FAILURE;
#endif
} else {
mPlatform = PlatformDecoderModule::Create();
NS_ENSURE_TRUE(mPlatform, NS_ERROR_FAILURE);
-38
View File
@@ -25,10 +25,6 @@
#include "GMPDecoderModule.h"
#include "mozilla/Preferences.h"
#ifdef MOZ_EME
#include "EMEDecoderModule.h"
#include "mozilla/CDMProxy.h"
#endif
#include "SharedThreadPool.h"
#include "MediaTaskQueue.h"
@@ -81,40 +77,6 @@ PlatformDecoderModule::Init()
#endif
}
#ifdef MOZ_EME
/* static */
already_AddRefed<PlatformDecoderModule>
PlatformDecoderModule::CreateCDMWrapper(CDMProxy* aProxy,
bool aHasAudio,
bool aHasVideo)
{
bool cdmDecodesAudio;
bool cdmDecodesVideo;
{
CDMCaps::AutoLock caps(aProxy->Capabilites());
cdmDecodesAudio = caps.CanDecryptAndDecodeAudio();
cdmDecodesVideo = caps.CanDecryptAndDecodeVideo();
}
nsRefPtr<PlatformDecoderModule> pdm;
if ((!cdmDecodesAudio && aHasAudio) || (!cdmDecodesVideo && aHasVideo)) {
// The CDM itself can't decode. We need to wrap a PDM to decode the
// decrypted output of the CDM.
pdm = Create();
if (!pdm) {
return nullptr;
}
}
nsRefPtr<PlatformDecoderModule> emepdm(
new AVCCDecoderModule(new EMEDecoderModule(aProxy,
pdm,
cdmDecodesAudio,
cdmDecodesVideo)));
return emepdm.forget();
}
#endif
/* static */
already_AddRefed<PlatformDecoderModule>
PlatformDecoderModule::Create()
-12
View File
@@ -74,18 +74,6 @@ public:
// This is called on the decode task queue.
virtual nsresult Startup() { return NS_OK; };
#ifdef MOZ_EME
// Creates a PlatformDecoderModule that uses a CDMProxy to decrypt or
// decrypt-and-decode EME encrypted content. If the CDM only decrypts and
// does not decode, we create a PDM and use that to create MediaDataDecoders
// that we use on on aTaskQueue to decode the decrypted stream.
// This is called on the decode task queue.
static already_AddRefed<PlatformDecoderModule>
CreateCDMWrapper(CDMProxy* aProxy,
bool aHasAudio,
bool aHasVideo);
#endif
// Creates an H.264 decoder. The layers backend is passed in so that
// decoders can determine whether hardware accelerated decoding can be used.
// Asynchronous decoding of video should be done in runnables dispatched
-37
View File
@@ -1,37 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#include "EMEAudioDecoder.h"
#include "mozilla/CDMProxy.h"
namespace mozilla {
void
EMEAudioCallbackAdapter::Error(GMPErr aErr)
{
if (aErr == GMPNoKeyErr) {
// The GMP failed to decrypt a frame due to not having a key. This can
// happen if a key expires or a session is closed during playback.
NS_WARNING("GMP failed to decrypt due to lack of key");
return;
}
AudioCallbackAdapter::Error(aErr);
}
void
EMEAudioDecoder::InitTags(nsTArray<nsCString>& aTags)
{
GMPAudioDecoder::InitTags(aTags);
aTags.AppendElement(NS_ConvertUTF16toUTF8(mProxy->KeySystem()));
}
nsCString
EMEAudioDecoder::GetNodeId()
{
return mProxy->GetNodeId();
}
} // namespace mozilla
-44
View File
@@ -1,44 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 EMEAudioDecoder_h_
#define EMEAudioDecoder_h_
#include "GMPAudioDecoder.h"
#include "PlatformDecoderModule.h"
namespace mozilla {
class EMEAudioCallbackAdapter : public AudioCallbackAdapter {
public:
explicit EMEAudioCallbackAdapter(MediaDataDecoderCallbackProxy* aCallback)
: AudioCallbackAdapter(aCallback)
{}
virtual void Error(GMPErr aErr) override;
};
class EMEAudioDecoder : public GMPAudioDecoder {
public:
EMEAudioDecoder(CDMProxy* aProxy,
const mp4_demuxer::AudioDecoderConfig& aConfig,
MediaTaskQueue* aTaskQueue,
MediaDataDecoderCallbackProxy* aCallback)
: GMPAudioDecoder(aConfig, aTaskQueue, aCallback, new EMEAudioCallbackAdapter(aCallback))
, mProxy(aProxy)
{
}
private:
virtual void InitTags(nsTArray<nsCString>& aTags) override;
virtual nsCString GetNodeId() override;
nsRefPtr<CDMProxy> mProxy;
};
} // namespace mozilla
#endif
-312
View File
@@ -1,312 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#include "EMEDecoderModule.h"
#include "EMEAudioDecoder.h"
#include "EMEVideoDecoder.h"
#include "MediaDataDecoderProxy.h"
#include "mozIGoannaMediaPluginService.h"
#include "mozilla/CDMProxy.h"
#include "mozilla/unused.h"
#include "nsServiceManagerUtils.h"
namespace mozilla {
class EMEDecryptor : public MediaDataDecoder {
typedef mp4_demuxer::MP4Sample MP4Sample;
public:
EMEDecryptor(MediaDataDecoder* aDecoder,
MediaDataDecoderCallback* aCallback,
CDMProxy* aProxy)
: mDecoder(aDecoder)
, mCallback(aCallback)
, mTaskQueue(CreateFlushableMediaDecodeTaskQueue())
, mProxy(aProxy)
, mSamplesWaitingForKey(new SamplesWaitingForKey(this, mTaskQueue, mProxy))
#ifdef DEBUG
, mIsShutdown(false)
#endif
{
}
virtual nsresult Init() override {
MOZ_ASSERT(!mIsShutdown);
nsresult rv = mTaskQueue->SyncDispatch(
NS_NewRunnableMethod(mDecoder, &MediaDataDecoder::Init));
unused << NS_WARN_IF(NS_FAILED(rv));
return rv;
}
class DeliverDecrypted : public DecryptionClient {
public:
DeliverDecrypted(EMEDecryptor* aDecryptor, FlushableMediaTaskQueue* aTaskQueue)
: mDecryptor(aDecryptor)
, mTaskQueue(aTaskQueue)
{}
virtual void Decrypted(GMPErr aResult,
mp4_demuxer::MP4Sample* aSample) override {
if (aResult == GMPNoKeyErr) {
RefPtr<nsIRunnable> task;
task = NS_NewRunnableMethodWithArg<MP4Sample*>(mDecryptor,
&EMEDecryptor::Input,
aSample);
mTaskQueue->Dispatch(task.forget());
} else if (GMP_FAILED(aResult)) {
if (mDecryptor->mCallback) {
mDecryptor->mCallback->Error();
}
MOZ_ASSERT(!aSample);
} else {
RefPtr<nsIRunnable> task;
task = NS_NewRunnableMethodWithArg<MP4Sample*>(mDecryptor,
&EMEDecryptor::Decrypted,
aSample);
mTaskQueue->Dispatch(task.forget());
}
mTaskQueue = nullptr;
mDecryptor = nullptr;
}
private:
nsRefPtr<EMEDecryptor> mDecryptor;
nsRefPtr<FlushableMediaTaskQueue> mTaskQueue;
};
virtual nsresult Input(MP4Sample* aSample) override {
MOZ_ASSERT(!mIsShutdown);
// We run the PDM on its own task queue. We can't run it on the decode
// task queue, because that calls into Input() in a loop and waits until
// output is delivered. We need to defer some Input() calls while we wait
// for keys to become usable, and once they do we need to dispatch an event
// to run the PDM on the same task queue, but since the decode task queue
// is waiting in MP4Reader::Decode() for output our task would never run.
// So we dispatch tasks to make all calls into the wrapped decoder.
if (mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)) {
return NS_OK;
}
mProxy->GetSessionIdsForKeyId(aSample->crypto.key,
aSample->crypto.session_ids);
mProxy->Decrypt(aSample, new DeliverDecrypted(this, mTaskQueue));
return NS_OK;
}
void Decrypted(mp4_demuxer::MP4Sample* aSample) {
MOZ_ASSERT(!mIsShutdown);
nsresult rv = mTaskQueue->Dispatch(
NS_NewRunnableMethodWithArg<mp4_demuxer::MP4Sample*>(
mDecoder,
&MediaDataDecoder::Input,
aSample));
unused << NS_WARN_IF(NS_FAILED(rv));
}
virtual nsresult Flush() override {
MOZ_ASSERT(!mIsShutdown);
nsresult rv = mTaskQueue->SyncDispatch(
NS_NewRunnableMethod(
mDecoder,
&MediaDataDecoder::Flush));
unused << NS_WARN_IF(NS_FAILED(rv));
mSamplesWaitingForKey->Flush();
return rv;
}
virtual nsresult Drain() override {
MOZ_ASSERT(!mIsShutdown);
nsresult rv = mTaskQueue->Dispatch(
NS_NewRunnableMethod(
mDecoder,
&MediaDataDecoder::Drain));
unused << NS_WARN_IF(NS_FAILED(rv));
return rv;
}
virtual nsresult Shutdown() override {
MOZ_ASSERT(!mIsShutdown);
#ifdef DEBUG
mIsShutdown = true;
#endif
nsresult rv = mTaskQueue->SyncDispatch(
NS_NewRunnableMethod(
mDecoder,
&MediaDataDecoder::Shutdown));
unused << NS_WARN_IF(NS_FAILED(rv));
mSamplesWaitingForKey->BreakCycles();
mSamplesWaitingForKey = nullptr;
mDecoder = nullptr;
mTaskQueue->BeginShutdown();
mTaskQueue->AwaitShutdownAndIdle();
mTaskQueue = nullptr;
mProxy = nullptr;
mCallback = nullptr;
return rv;
}
private:
nsRefPtr<MediaDataDecoder> mDecoder;
MediaDataDecoderCallback* mCallback;
nsRefPtr<FlushableMediaTaskQueue> mTaskQueue;
nsRefPtr<CDMProxy> mProxy;
nsRefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
#ifdef DEBUG
bool mIsShutdown;
#endif
};
class EMEMediaDataDecoderProxy : public MediaDataDecoderProxy {
public:
EMEMediaDataDecoderProxy(nsIThread* aProxyThread, MediaDataDecoderCallback* aCallback, CDMProxy* aProxy, FlushableMediaTaskQueue* aTaskQueue)
: MediaDataDecoderProxy(aProxyThread, aCallback)
, mSamplesWaitingForKey(new SamplesWaitingForKey(this, aTaskQueue, aProxy))
, mProxy(aProxy)
{
}
virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) override;
virtual nsresult Shutdown() override;
private:
nsRefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
nsRefPtr<CDMProxy> mProxy;
};
nsresult
EMEMediaDataDecoderProxy::Input(mp4_demuxer::MP4Sample* aSample)
{
if (mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)) {
return NS_OK;
}
mProxy->GetSessionIdsForKeyId(aSample->crypto.key,
aSample->crypto.session_ids);
return MediaDataDecoderProxy::Input(aSample);
}
nsresult
EMEMediaDataDecoderProxy::Shutdown()
{
nsresult rv = MediaDataDecoderProxy::Shutdown();
mSamplesWaitingForKey->BreakCycles();
mSamplesWaitingForKey = nullptr;
mProxy = nullptr;
return rv;
}
EMEDecoderModule::EMEDecoderModule(CDMProxy* aProxy,
PlatformDecoderModule* aPDM,
bool aCDMDecodesAudio,
bool aCDMDecodesVideo)
: mProxy(aProxy)
, mPDM(aPDM)
, mCDMDecodesAudio(aCDMDecodesAudio)
, mCDMDecodesVideo(aCDMDecodesVideo)
{
}
EMEDecoderModule::~EMEDecoderModule()
{
}
static already_AddRefed<MediaDataDecoderProxy>
CreateDecoderWrapper(MediaDataDecoderCallback* aCallback, CDMProxy* aProxy, FlushableMediaTaskQueue* aTaskQueue)
{
nsCOMPtr<mozIGoannaMediaPluginService> gmpService = do_GetService("@mozilla.org/goanna-media-plugin-service;1");
if (!gmpService) {
return nullptr;
}
nsCOMPtr<nsIThread> thread;
nsresult rv = gmpService->GetThread(getter_AddRefs(thread));
if (NS_FAILED(rv)) {
return nullptr;
}
nsRefPtr<MediaDataDecoderProxy> decoder(new EMEMediaDataDecoderProxy(thread, aCallback, aProxy, aTaskQueue));
return decoder.forget();
}
already_AddRefed<MediaDataDecoder>
EMEDecoderModule::CreateVideoDecoder(const VideoDecoderConfig& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer,
FlushableMediaTaskQueue* aVideoTaskQueue,
MediaDataDecoderCallback* aCallback)
{
if (mCDMDecodesVideo && aConfig.crypto.valid) {
nsRefPtr<MediaDataDecoderProxy> wrapper = CreateDecoderWrapper(aCallback, mProxy, aVideoTaskQueue);
wrapper->SetProxyTarget(new EMEVideoDecoder(mProxy,
aConfig,
aLayersBackend,
aImageContainer,
aVideoTaskQueue,
wrapper->Callback()));
return wrapper.forget();
}
nsRefPtr<MediaDataDecoder> decoder(mPDM->CreateVideoDecoder(aConfig,
aLayersBackend,
aImageContainer,
aVideoTaskQueue,
aCallback));
if (!decoder) {
return nullptr;
}
if (!aConfig.crypto.valid) {
return decoder.forget();
}
nsRefPtr<MediaDataDecoder> emeDecoder(new EMEDecryptor(decoder,
aCallback,
mProxy));
return emeDecoder.forget();
}
already_AddRefed<MediaDataDecoder>
EMEDecoderModule::CreateAudioDecoder(const AudioDecoderConfig& aConfig,
FlushableMediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback)
{
if (mCDMDecodesAudio && aConfig.crypto.valid) {
nsRefPtr<MediaDataDecoderProxy> wrapper = CreateDecoderWrapper(aCallback, mProxy, aAudioTaskQueue);
wrapper->SetProxyTarget(new EMEAudioDecoder(mProxy,
aConfig,
aAudioTaskQueue,
wrapper->Callback()));
return wrapper.forget();
}
nsRefPtr<MediaDataDecoder> decoder(mPDM->CreateAudioDecoder(aConfig,
aAudioTaskQueue,
aCallback));
if (!decoder) {
return nullptr;
}
if (!aConfig.crypto.valid) {
return decoder.forget();
}
nsRefPtr<MediaDataDecoder> emeDecoder(new EMEDecryptor(decoder,
aCallback,
mProxy));
return emeDecoder.forget();
}
bool
EMEDecoderModule::DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig)
{
return mCDMDecodesVideo && aConfig.crypto.valid;
}
} // namespace mozilla
-61
View File
@@ -1,61 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#if !defined(EMEDecoderModule_h_)
#define EMEDecoderModule_h_
#include "PlatformDecoderModule.h"
#include "gmp-decryption.h"
namespace mozilla {
class CDMProxy;
class FlushableMediaTaskQueue;
class EMEDecoderModule : public PlatformDecoderModule {
private:
typedef mp4_demuxer::AudioDecoderConfig AudioDecoderConfig;
typedef mp4_demuxer::VideoDecoderConfig VideoDecoderConfig;
public:
EMEDecoderModule(CDMProxy* aProxy,
PlatformDecoderModule* aPDM,
bool aCDMDecodesAudio,
bool aCDMDecodesVideo);
virtual ~EMEDecoderModule();
// Decode thread.
virtual already_AddRefed<MediaDataDecoder>
CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer,
FlushableMediaTaskQueue* aVideoTaskQueue,
MediaDataDecoderCallback* aCallback) override;
// Decode thread.
virtual already_AddRefed<MediaDataDecoder>
CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
FlushableMediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback) override;
virtual bool
DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig) override;
private:
nsRefPtr<CDMProxy> mProxy;
// Will be null if CDM has decoding capability.
nsRefPtr<PlatformDecoderModule> mPDM;
// We run the PDM on its own task queue.
nsRefPtr<FlushableMediaTaskQueue> mTaskQueue;
bool mCDMDecodesAudio;
bool mCDMDecodesVideo;
};
} // namespace mozilla
#endif // EMEDecoderModule_h_
-48
View File
@@ -1,48 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#include "EMEVideoDecoder.h"
#include "GMPVideoEncodedFrameImpl.h"
#include "mozilla/CDMProxy.h"
namespace mozilla {
void
EMEVideoCallbackAdapter::Error(GMPErr aErr)
{
if (aErr == GMPNoKeyErr) {
// The GMP failed to decrypt a frame due to not having a key. This can
// happen if a key expires or a session is closed during playback.
NS_WARNING("GMP failed to decrypt due to lack of key");
return;
}
VideoCallbackAdapter::Error(aErr);
}
void
EMEVideoDecoder::InitTags(nsTArray<nsCString>& aTags)
{
GMPVideoDecoder::InitTags(aTags);
aTags.AppendElement(NS_ConvertUTF16toUTF8(mProxy->KeySystem()));
}
nsCString
EMEVideoDecoder::GetNodeId()
{
return mProxy->GetNodeId();
}
GMPUnique<GMPVideoEncodedFrame>::Ptr
EMEVideoDecoder::CreateFrame(mp4_demuxer::MP4Sample* aSample)
{
GMPUnique<GMPVideoEncodedFrame>::Ptr frame = GMPVideoDecoder::CreateFrame(aSample);
if (frame && aSample->crypto.valid) {
static_cast<gmp::GMPVideoEncodedFrameImpl*>(frame.get())->InitCrypto(aSample->crypto);
}
return frame;
}
} // namespace mozilla
-54
View File
@@ -1,54 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 EMEVideoDecoder_h_
#define EMEVideoDecoder_h_
#include "GMPVideoDecoder.h"
#include "PlatformDecoderModule.h"
namespace mozilla {
class CDMProxy;
class MediaTaskQueue;
class EMEVideoCallbackAdapter : public VideoCallbackAdapter {
public:
EMEVideoCallbackAdapter(MediaDataDecoderCallbackProxy* aCallback,
VideoInfo aVideoInfo,
layers::ImageContainer* aImageContainer)
: VideoCallbackAdapter(aCallback, aVideoInfo, aImageContainer)
{}
virtual void Error(GMPErr aErr) override;
};
class EMEVideoDecoder : public GMPVideoDecoder {
public:
EMEVideoDecoder(CDMProxy* aProxy,
const mp4_demuxer::VideoDecoderConfig& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer,
MediaTaskQueue* aTaskQueue,
MediaDataDecoderCallbackProxy* aCallback)
: GMPVideoDecoder(aConfig, aLayersBackend, aImageContainer, aTaskQueue, aCallback,
new EMEVideoCallbackAdapter(aCallback, VideoInfo(aConfig.display_width,
aConfig.display_height), aImageContainer))
, mProxy(aProxy)
{
}
private:
virtual void InitTags(nsTArray<nsCString>& aTags) override;
virtual nsCString GetNodeId() override;
virtual GMPUnique<GMPVideoEncodedFrame>::Ptr CreateFrame(mp4_demuxer::MP4Sample* aSample) override;
nsRefPtr<CDMProxy> mProxy;
};
}
#endif // EMEVideoDecoder_h_
@@ -1,82 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#include "SamplesWaitingForKey.h"
#include "mozilla/CDMProxy.h"
#include "mozilla/CDMCaps.h"
namespace mozilla {
SamplesWaitingForKey::SamplesWaitingForKey(MediaDataDecoder* aDecoder,
MediaTaskQueue* aTaskQueue,
CDMProxy* aProxy)
: mMutex("SamplesWaitingForKey")
, mDecoder(aDecoder)
, mTaskQueue(aTaskQueue)
, mProxy(aProxy)
{
}
SamplesWaitingForKey::~SamplesWaitingForKey()
{
}
bool
SamplesWaitingForKey::WaitIfKeyNotUsable(MP4Sample* aSample)
{
if (!aSample || !aSample->crypto.valid || !mProxy) {
return false;
}
CDMCaps::AutoLock caps(mProxy->Capabilites());
const auto& keyid = aSample->crypto.key;
if (!caps.IsKeyUsable(keyid)) {
{
MutexAutoLock lock(mMutex);
mSamples.AppendElement(aSample);
}
caps.NotifyWhenKeyIdUsable(aSample->crypto.key, this);
return true;
}
return false;
}
void
SamplesWaitingForKey::NotifyUsable(const CencKeyId& aKeyId)
{
MutexAutoLock lock(mMutex);
size_t i = 0;
while (i < mSamples.Length()) {
if (aKeyId == mSamples[i]->crypto.key) {
RefPtr<nsIRunnable> task;
task = NS_NewRunnableMethodWithArg<MP4Sample*>(mDecoder,
&MediaDataDecoder::Input,
mSamples[i].forget());
mSamples.RemoveElementAt(i);
mTaskQueue->Dispatch(task.forget());
} else {
i++;
}
}
}
void
SamplesWaitingForKey::Flush()
{
MutexAutoLock lock(mMutex);
mSamples.Clear();
}
void
SamplesWaitingForKey::BreakCycles()
{
MutexAutoLock lock(mMutex);
mDecoder = nullptr;
mTaskQueue = nullptr;
mProxy = nullptr;
mSamples.Clear();
}
} // namespace mozilla
-57
View File
@@ -1,57 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 SamplesWaitingForKey_h_
#define SamplesWaitingForKey_h_
#include "mp4_demuxer/DecoderData.h"
#include "MediaTaskQueue.h"
#include "PlatformDecoderModule.h"
namespace mozilla {
typedef nsTArray<uint8_t> CencKeyId;
class CDMProxy;
// Encapsulates the task of waiting for the CDMProxy to have the necessary
// keys to decypt a given sample.
class SamplesWaitingForKey {
typedef mp4_demuxer::MP4Sample MP4Sample;
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SamplesWaitingForKey)
explicit SamplesWaitingForKey(MediaDataDecoder* aDecoder,
MediaTaskQueue* aTaskQueue,
CDMProxy* aProxy);
// Returns true if we need to wait for a key to become usable.
// Will callback MediaDataDecoder::Input(aSample) on mDecoder once the
// sample is ready to be decrypted. The order of input samples is
// preserved.
bool WaitIfKeyNotUsable(MP4Sample* aSample);
void NotifyUsable(const CencKeyId& aKeyId);
void Flush();
void BreakCycles();
protected:
~SamplesWaitingForKey();
private:
Mutex mMutex;
nsRefPtr<MediaDataDecoder> mDecoder;
nsRefPtr<MediaTaskQueue> mTaskQueue;
nsRefPtr<CDMProxy> mProxy;
nsTArray<nsAutoPtr<MP4Sample>> mSamples;
};
} // namespace mozilla
#endif // SamplesWaitingForKey_h_
-28
View File
@@ -1,28 +0,0 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# 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/.
EXPORTS += [
'EMEAudioDecoder.h',
'EMEDecoderModule.h',
'EMEVideoDecoder.h',
'SamplesWaitingForKey.h',
]
UNIFIED_SOURCES += [
'EMEAudioDecoder.cpp',
'EMEDecoderModule.cpp',
'EMEVideoDecoder.cpp',
'SamplesWaitingForKey.cpp',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'
FAIL_ON_WARNINGS = True
if CONFIG['OS_ARCH'] == 'WINNT':
DEFINES['NOMINMAX'] = True
-3
View File
@@ -31,9 +31,6 @@ DIRS += ['gmp']
if CONFIG['MOZ_WMF']:
DIRS += [ 'wmf' ];
if CONFIG['MOZ_EME']:
DIRS += ['eme']
if CONFIG['MOZ_FFMPEG']:
EXPORTS += [
'ffmpeg/FFmpegRuntimeLinker.h',
+1
View File
@@ -16,6 +16,7 @@
#include "nsComponentManagerUtils.h"
#include "nsIGfxInfo.h"
#include "GfxDriverInfo.h"
#include "nsServiceManagerUtils.h" // for do_GetService
namespace mozilla {
-66
View File
@@ -1,66 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#include "gtest/gtest.h"
#include "mozilla/EMEUtils.h"
using namespace std;
using namespace mozilla;
struct ParseKeySystemTestCase {
const char16_t* mInputKeySystemString;
int32_t mOutCDMVersion;
bool mShouldPass;
};
const ParseKeySystemTestCase ParseKeySystemTests[] = {
{
MOZ_UTF16("org.w3.clearkey"),
NO_CDM_VERSION,
true,
}, {
MOZ_UTF16("org.w3.clearkey.123"),
123,
true,
}, {
MOZ_UTF16("org.w3.clearkey.-1"),
NO_CDM_VERSION,
false,
}, {
MOZ_UTF16("org.w3.clearkey.NaN"),
NO_CDM_VERSION,
false,
}, {
MOZ_UTF16("org.w3.clearkey.0"),
0,
true,
}, {
MOZ_UTF16("org.w3.clearkey.123567890123567890123567890123567890123567890"),
NO_CDM_VERSION,
false,
}, {
MOZ_UTF16("org.w3.clearkey.0.1"),
NO_CDM_VERSION,
false,
}
};
TEST(EME, EMEParseKeySystem) {
const nsAutoString clearkey(MOZ_UTF16("org.w3.clearkey"));
for (const ParseKeySystemTestCase& test : ParseKeySystemTests) {
nsAutoString keySystem;
int32_t version;
bool rv = ParseKeySystem(nsDependentString(test.mInputKeySystemString),
keySystem,
version);
EXPECT_EQ(rv, test.mShouldPass) << "parse should succeed if expected to";
if (!test.mShouldPass) {
continue;
}
EXPECT_TRUE(keySystem.Equals(clearkey)) << NS_ConvertUTF16toUTF8(keySystem).get(); //"should extract expected keysystem" << ;
EXPECT_EQ(test.mOutCDMVersion, version) << "should extract expected version";
}
}
-5
View File
@@ -15,11 +15,6 @@ SOURCES += [
'TestWebMBuffered.cpp',
]
if CONFIG['MOZ_EME']:
SOURCES += [
'TestEME.cpp',
]
if CONFIG['MOZ_WEBM_ENCODER']:
SOURCES += [
'TestVideoTrackEncoder.cpp',
@@ -296,19 +296,6 @@ MediaSourceDecoder::GetMozDebugReaderData(nsAString& aString)
mReader->GetMozDebugReaderData(aString);
}
#ifdef MOZ_EME
nsresult
MediaSourceDecoder::SetCDMProxy(CDMProxy* aProxy)
{
nsresult rv = MediaDecoder::SetCDMProxy(aProxy);
NS_ENSURE_SUCCESS(rv, rv);
rv = mReader->SetCDMProxy(aProxy);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
#endif
bool
MediaSourceDecoder::IsActiveReader(MediaDecoderReader* aReader)
{
@@ -72,10 +72,6 @@ public:
// registered TrackBuffers essential for initialization.
void PrepareReaderInitialization();
#ifdef MOZ_EME
virtual nsresult SetCDMProxy(CDMProxy* aProxy) override;
#endif
MediaSourceReader* GetReader() { return mReader; }
// Returns true if aReader is a currently active audio or video
@@ -702,9 +702,6 @@ MediaSourceReader::CreateSubDecoder(const nsACString& aType, int64_t aTimestampO
MSE_DEBUG("subdecoder %p subreader %p",
decoder.get(), reader.get());
decoder->SetReader(reader);
#ifdef MOZ_EME
decoder->SetCDMProxy(mCDMProxy);
#endif
return decoder.forget();
}
@@ -1233,22 +1230,6 @@ MediaSourceReader::GetMozDebugReaderData(nsAString& aString)
aString += NS_ConvertUTF8toUTF16(result);
}
#ifdef MOZ_EME
nsresult
MediaSourceReader::SetCDMProxy(CDMProxy* aProxy)
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mCDMProxy = aProxy;
for (size_t i = 0; i < mTrackBuffers.Length(); i++) {
nsresult rv = mTrackBuffers[i]->SetCDMProxy(aProxy);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
#endif
bool
MediaSourceReader::IsActiveReader(MediaDecoderReader* aReader)
{
@@ -143,10 +143,6 @@ public:
// Set the duration of the attached mediasource element.
void SetMediaSourceDuration(double aDuration /* seconds */);
#ifdef MOZ_EME
nsresult SetCDMProxy(CDMProxy* aProxy);
#endif
virtual bool IsAsync() const override {
return (!GetAudioReader() || GetAudioReader()->IsAsync()) &&
(!GetVideoReader() || GetVideoReader()->IsAsync());
@@ -246,10 +242,6 @@ private:
return aType == MediaData::AUDIO_DATA ? mAudioWaitPromise : mVideoWaitPromise;
}
#ifdef MOZ_EME
nsRefPtr<CDMProxy> mCDMProxy;
#endif
// These are read and written on the decode task queue threads.
int64_t mLastAudioTime;
int64_t mLastVideoTime;
@@ -11,9 +11,6 @@
#include "MediaDecoderReader.h"
#include "SourceBufferResource.h"
#include "mozilla/Attributes.h"
#ifdef MOZ_EME
#include "mozilla/CDMProxy.h"
#endif
#include "mozilla/ReentrantMonitor.h"
namespace mozilla {
@@ -99,28 +96,8 @@ public:
mReader = nullptr;
}
mTaskQueue = nullptr;
#ifdef MOZ_EME
mCDMProxy = nullptr;
#endif
}
#ifdef MOZ_EME
virtual nsresult SetCDMProxy(CDMProxy* aProxy) override
{
MOZ_ASSERT(NS_IsMainThread());
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
mCDMProxy = aProxy;
return NS_OK;
}
virtual CDMProxy* GetCDMProxy() override
{
MOZ_ASSERT(OnDecodeThread() || NS_IsMainThread());
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
return mCDMProxy;
}
#endif
// Given a time convert it into an approximate byte offset from the
// cached data. Returns -1 if no such value is computable.
int64_t ConvertToByteOffset(double aTime);
@@ -163,10 +140,6 @@ private:
int64_t mRealMediaDuration;
// in seconds
double mTrimmedOffset;
#ifdef MOZ_EME
nsRefPtr<CDMProxy> mCDMProxy;
#endif
};
} // namespace mozilla
-25
View File
@@ -869,31 +869,6 @@ TrackBuffer::Decoders()
return mInitializedDecoders;
}
#ifdef MOZ_EME
nsresult
TrackBuffer::SetCDMProxy(CDMProxy* aProxy)
{
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
nsresult rv = mDecoders[i]->SetCDMProxy(aProxy);
NS_ENSURE_SUCCESS(rv, rv);
}
for (uint32_t i = 0; i < mWaitingDecoders.Length(); ++i) {
CDMCaps::AutoLock caps(aProxy->Capabilites());
caps.CallOnMainThreadWhenCapsAvailable(
NS_NewRunnableMethodWithArg<SourceBufferDecoder*>(this,
&TrackBuffer::QueueInitializeDecoder,
mWaitingDecoders[i]));
}
mWaitingDecoders.Clear();
return NS_OK;
}
#endif
#if defined(DEBUG)
void
TrackBuffer::Dump(const char* aPath)
-4
View File
@@ -110,10 +110,6 @@ public:
// currently not playable.
bool HasOnlyIncompleteMedia();
#ifdef MOZ_EME
nsresult SetCDMProxy(CDMProxy* aProxy);
#endif
#if defined(DEBUG)
void Dump(const char* aPath);
#endif
-3
View File
@@ -53,9 +53,6 @@ if CONFIG['MOZ_OMX_DECODER']:
DIRS += ['omx']
DIRS += ['omx/mediaresourcemanager']
if CONFIG['MOZ_EME']:
DIRS += ['eme']
TEST_DIRS += [
'compiledtest',
'gtest',
-6
View File
@@ -294,12 +294,6 @@ AudioContext::CreateMediaElementSource(HTMLMediaElement& aMediaElement,
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return nullptr;
}
#ifdef MOZ_EME
if (aMediaElement.ContainsRestrictedContent()) {
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return nullptr;
}
#endif
nsRefPtr<DOMMediaStream> stream = aMediaElement.MozCaptureStream(aRv);
if (aRv.Failed()) {
return nullptr;
-15
View File
@@ -141,18 +141,3 @@ partial interface HTMLMediaElement {
// because of the audiochannel manager.
// * onmozinterruptend - called when the interruption is concluded
};
#ifdef MOZ_EME
// Encrypted Media Extensions
partial interface HTMLMediaElement {
[Pref="media.eme.apiVisible"]
readonly attribute MediaKeys? mediaKeys;
// void, not any: https://www.w3.org/Bugs/Public/show_bug.cgi?id=26457
[Pref="media.eme.apiVisible", NewObject]
Promise<void> setMediaKeys(MediaKeys? mediaKeys);
[Pref="media.eme.apiVisible"]
attribute EventHandler onencrypted;
};
#endif
-23
View File
@@ -1,23 +0,0 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/.
*
* The origin of this IDL file is
* https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html
*
* Copyright © 2014 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved.
* W3C liability, trademark and document use rules apply.
*/
[Pref="media.eme.apiVisible", Constructor(DOMString type, optional MediaKeyNeededEventInit eventInitDict)]
interface MediaEncryptedEvent : Event {
readonly attribute DOMString initDataType;
[Throws]
readonly attribute ArrayBuffer? initData;
};
dictionary MediaKeyNeededEventInit : EventInit {
DOMString initDataType = "";
ArrayBuffer? initData = null;
};
-19
View File
@@ -1,19 +0,0 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/.
*
* The origin of this IDL file is
* https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html
*
* Copyright © 2014 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved.
* W3C liability, trademark and document use rules apply.
*/
// According to the spec, "The future of error events and MediaKeyError
// is uncertain."
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=21798
[Pref="media.eme.apiVisible"]
interface MediaKeyError : Event {
readonly attribute unsigned long systemCode;
};
-30
View File
@@ -1,30 +0,0 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/.
*
* The origin of this IDL file is
* https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html
*
* Copyright © 2014 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved.
* W3C liability, trademark and document use rules apply.
*/
enum MediaKeyMessageType {
"license-request",
"license-renewal",
"license-release",
"individualization-request"
};
[Pref="media.eme.apiVisible", Constructor(DOMString type, optional MediaKeyMessageEventInit eventInitDict)]
interface MediaKeyMessageEvent : Event {
readonly attribute MediaKeyMessageType messageType;
[Throws]
readonly attribute ArrayBuffer message;
};
dictionary MediaKeyMessageEventInit : EventInit {
MediaKeyMessageType messageType = "license-request";
ArrayBuffer message;
};
-43
View File
@@ -1,43 +0,0 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/.
*
* The origin of this IDL file is
* https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html
*
* Copyright © 2014 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved.
* W3C liability, trademark and document use rules apply.
*/
[Pref="media.eme.apiVisible"]
interface MediaKeySession : EventTarget {
// error state
readonly attribute MediaKeyError? error;
// session properties
readonly attribute DOMString keySystem;
readonly attribute DOMString sessionId;
readonly attribute unrestricted double expiration;
readonly attribute Promise<void> closed;
readonly attribute MediaKeyStatusMap keyStatuses;
[NewObject]
Promise<void> generateRequest(DOMString initDataType, BufferSource initData);
[NewObject]
Promise<boolean> load(DOMString sessionId);
// session operations
[NewObject]
Promise<void> update(BufferSource response);
[NewObject]
Promise<void> close();
[NewObject]
Promise<void> remove();
};
-36
View File
@@ -1,36 +0,0 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/.
*
* The origin of this IDL file is
* https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html
*
* Copyright © 2014 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved.
* W3C liability, trademark and document use rules apply.
*/
enum MediaKeyStatus {
"usable",
"expired",
"output-downscaled",
"output-not-allowed",
"internal-error"
};
[Pref="media.eme.apiVisible"]
interface MediaKeyStatusMap {
[Throws]
readonly attribute unsigned long size;
[Throws]
object keys();
[Throws]
object values();
[Throws]
object entries();
// XXX: forEach, @@iterator
};
-34
View File
@@ -1,34 +0,0 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/.
*
* The origin of this IDL file is
* https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html
*
* Copyright © 2014 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved.
* W3C liability, trademark and document use rules apply.
*/
enum MediaKeysRequirement {
"required",
"optional",
"disallowed"
};
dictionary MediaKeySystemOptions {
DOMString initDataType = "";
DOMString audioType = "";
DOMString audioCapability = "";
DOMString videoType = "";
DOMString videoCapability = "";
MediaKeysRequirement uniqueidentifier = "optional";
MediaKeysRequirement stateful = "optional";
};
[Pref="media.eme.apiVisible"]
interface MediaKeySystemAccess {
readonly attribute DOMString keySystem;
[NewObject]
Promise<MediaKeys> createMediaKeys();
};
-25
View File
@@ -1,25 +0,0 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/.
*
* The origin of this IDL file is
* https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html
*
* Copyright © 2014 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved.
* W3C liability, trademark and document use rules apply.
*/
enum IsTypeSupportedResult { "" /* empty string */, "maybe", "probably" };
enum SessionType { "temporary", "persistent" };
[Pref="media.eme.apiVisible"]
interface MediaKeys {
readonly attribute DOMString keySystem;
[NewObject, Throws]
MediaKeySession createSession(optional SessionType sessionType = "temporary");
[NewObject]
Promise<void> setServerCertificate((ArrayBufferView or ArrayBuffer) serverCertificate);
};
-21
View File
@@ -1,21 +0,0 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/.
*/
enum MediaKeySystemStatus {
"available",
"api-disabled",
"cdm-disabled",
"cdm-not-supported",
"cdm-not-installed",
"cdm-insufficient-version",
"cdm-created",
"error"
};
dictionary RequestMediaKeySystemAccessNotification {
required DOMString keySystem;
required MediaKeySystemStatus status;
};
-9
View File
@@ -412,15 +412,6 @@ partial interface Navigator {
readonly attribute TVManager? tv;
};
#ifdef MOZ_EME
partial interface Navigator {
[Pref="media.eme.apiVisible", NewObject]
Promise<MediaKeySystemAccess>
requestMediaKeySystemAccess(DOMString keySystem,
optional sequence<MediaKeySystemOptions> supportedConfigurations);
};
#endif
#ifdef NIGHTLY_BUILD
partial interface Navigator {
[Func="Navigator::IsE10sEnabled"]
-12
View File
@@ -808,18 +808,6 @@ if CONFIG['MOZ_BUILD_APP'] in ['browser', 'mobile/android', 'xulrunner']:
'External.webidl',
]
if CONFIG['MOZ_EME']:
WEBIDL_FILES += [
'MediaEncryptedEvent.webidl',
'MediaKeyError.webidl',
'MediaKeyMessageEvent.webidl',
'MediaKeys.webidl',
'MediaKeySession.webidl',
'MediaKeysRequestStatus.webidl',
'MediaKeyStatusMap.webidl',
'MediaKeySystemAccess.webidl',
]
if CONFIG['MOZ_PAY']:
WEBIDL_FILES += [
'MozPaymentProvider.webidl'
-6
View File
@@ -6669,12 +6669,6 @@ nsLayoutUtils::SurfaceFromElement(HTMLVideoElement* aElement,
NS_WARN_IF_FALSE((aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) == 0, "We can't support non-premultiplied alpha for video!");
#ifdef MOZ_EME
if (aElement->ContainsRestrictedContent()) {
return result;
}
#endif
uint16_t readyState;
if (NS_SUCCEEDED(aElement->GetReadyState(&readyState)) &&
(readyState == nsIDOMHTMLMediaElement::HAVE_NOTHING ||