From a6aa98ac0eac51451a8a4c0d5ce3813fcf87564a Mon Sep 17 00:00:00 2001 From: Chris Pearce Date: Sat, 7 Jun 2014 08:52:15 +1200 Subject: [PATCH] Bug 1016162 - Base EME DOM APIs, not connected to a CDM yet. r=bz --- browser/app/profile/firefox.js | 2 + content/base/src/nsGkAtomList.h | 3 + .../html/content/public/HTMLMediaElement.h | 32 ++ content/html/content/src/HTMLMediaElement.cpp | 80 ++++- content/html/content/src/HTMLSourceElement.h | 10 + content/media/eme/CDMProxy.cpp | 199 ++++++++++++ content/media/eme/CDMProxy.h | 171 ++++++++++ content/media/eme/EMELog.cpp | 32 ++ content/media/eme/EMELog.h | 35 +++ content/media/eme/MediaKeyError.cpp | 39 +++ content/media/eme/MediaKeyError.h | 38 +++ content/media/eme/MediaKeyMessageEvent.cpp | 127 ++++++++ content/media/eme/MediaKeyMessageEvent.h | 65 ++++ content/media/eme/MediaKeyNeededEvent.cpp | 118 +++++++ content/media/eme/MediaKeyNeededEvent.h | 62 ++++ content/media/eme/MediaKeySession.cpp | 178 +++++++++++ content/media/eme/MediaKeySession.h | 93 ++++++ content/media/eme/MediaKeys.cpp | 291 ++++++++++++++++++ content/media/eme/MediaKeys.h | 120 ++++++++ content/media/eme/moz.build | 31 ++ content/media/moz.build | 2 + dom/events/EventNameList.h | 1 - dom/webidl/HTMLMediaElement.webidl | 22 ++ dom/webidl/HTMLSourceElement.webidl | 6 + dom/webidl/MediaKeyError.webidl | 19 ++ dom/webidl/MediaKeyMessageEvent.webidl | 23 ++ dom/webidl/MediaKeyNeededEvent.webidl | 23 ++ dom/webidl/MediaKeySession.webidl | 43 +++ dom/webidl/MediaKeys.webidl | 37 +++ dom/webidl/moz.build | 5 + dom/workers/WorkerFeature.h | 6 + parser/html/nsHtml5AtomList.h | 1 + widget/BasicEvents.h | 1 + 33 files changed, 1911 insertions(+), 4 deletions(-) create mode 100644 content/media/eme/CDMProxy.cpp create mode 100644 content/media/eme/CDMProxy.h create mode 100644 content/media/eme/EMELog.cpp create mode 100644 content/media/eme/EMELog.h create mode 100644 content/media/eme/MediaKeyError.cpp create mode 100644 content/media/eme/MediaKeyError.h create mode 100644 content/media/eme/MediaKeyMessageEvent.cpp create mode 100644 content/media/eme/MediaKeyMessageEvent.h create mode 100644 content/media/eme/MediaKeyNeededEvent.cpp create mode 100644 content/media/eme/MediaKeyNeededEvent.h create mode 100644 content/media/eme/MediaKeySession.cpp create mode 100644 content/media/eme/MediaKeySession.h create mode 100644 content/media/eme/MediaKeys.cpp create mode 100644 content/media/eme/MediaKeys.h create mode 100644 content/media/eme/moz.build create mode 100644 dom/webidl/MediaKeyError.webidl create mode 100644 dom/webidl/MediaKeyMessageEvent.webidl create mode 100644 dom/webidl/MediaKeyNeededEvent.webidl create mode 100644 dom/webidl/MediaKeySession.webidl create mode 100644 dom/webidl/MediaKeys.webidl diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index cb92730e3b1..111930505df 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1565,6 +1565,8 @@ pref("identity.fxaccounts.settings.uri", "https://accounts.firefox.com/settings" pref("ui.key.menuAccessKeyFocuses", true); #endif +// Encrypted media extensions. +pref("media.eme.enabled", false); // Delete HTTP cache v2 data of users that didn't opt-in manually pref("browser.cache.auto_delete_cache_version", 1); diff --git a/content/base/src/nsGkAtomList.h b/content/base/src/nsGkAtomList.h index f4af867d792..da71e08aba3 100644 --- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -497,6 +497,7 @@ GK_ATOM(keydown, "keydown") GK_ATOM(keygen, "keygen") GK_ATOM(keypress, "keypress") GK_ATOM(keyset, "keyset") +GK_ATOM(keysystem, "keysystem") GK_ATOM(keytext, "keytext") GK_ATOM(keyup, "keyup") GK_ATOM(kind, "kind") @@ -1944,6 +1945,8 @@ GK_ATOM(oncuechange, "oncuechange") GK_ATOM(oncurrentchange, "oncurrentchange") GK_ATOM(onenter, "onenter") GK_ATOM(onexit, "onexit") +GK_ATOM(onneedkey, "onneedkey") +GK_ATOM(needkey, "needkey") GK_ATOM(onremovetrack, "onremovetrack") GK_ATOM(loadstart, "loadstart") GK_ATOM(suspend, "suspend") diff --git a/content/html/content/public/HTMLMediaElement.h b/content/html/content/public/HTMLMediaElement.h index ca0db56aee5..045c4156c42 100644 --- a/content/html/content/public/HTMLMediaElement.h +++ b/content/html/content/public/HTMLMediaElement.h @@ -20,6 +20,16 @@ #include "mozilla/dom/AudioChannelBinding.h" #include "mozilla/dom/TextTrackManager.h" #include "MediaDecoder.h" +#include "mozilla/dom/MediaKeys.h" + +// Something on Linux #defines None, which is an entry in the +// MediaWaitingFor enum, so undef it here before including the binfing, +// so that the build doesn't fail... +#ifdef None +#undef None +#endif + +#include "mozilla/dom/HTMLMediaElementBinding.h" // Define to output information on decoding and painting framerate /* #define DEBUG_FRAME_RATE 1 */ @@ -37,6 +47,7 @@ class MediaResource; class MediaDecoder; class VideoFrameContainer; namespace dom { +class MediaKeys; class TextTrack; class TimeRanges; class WakeLock; @@ -478,6 +489,22 @@ public: // XPCOM MozPreservesPitch() is OK + MediaKeys* GetMediaKeys() const; + + already_AddRefed SetMediaKeys(MediaKeys* mediaKeys, + ErrorResult& aRv); + + MediaWaitingFor WaitingFor() const; + + mozilla::dom::EventHandlerNonNull* GetOnneedkey(); + void SetOnneedkey(mozilla::dom::EventHandlerNonNull* listener); + + void DispatchNeedKey(const nsTArray& aInitData, + const nsAString& aInitDataType); + + + bool IsEventAttributeName(nsIAtom* aName) MOZ_OVERRIDE; + bool MozAutoplayEnabled() const { return mAutoplayEnabled; @@ -1010,6 +1037,9 @@ protected: // Range of time played. nsRefPtr mPlayed; + // Encrypted Media Extension media keys. + nsRefPtr mMediaKeys; + // Stores the time at the start of the current 'played' range. double mCurrentPlayRangeStart; @@ -1139,6 +1169,8 @@ protected: nsCOMPtr mAudioChannelAgent; nsRefPtr mTextTrackManager; + + MediaWaitingFor mWaitingFor; }; } // namespace dom diff --git a/content/html/content/src/HTMLMediaElement.cpp b/content/html/content/src/HTMLMediaElement.cpp index 1ab030a3071..2bd1d9c84f6 100644 --- a/content/html/content/src/HTMLMediaElement.cpp +++ b/content/html/content/src/HTMLMediaElement.cpp @@ -9,6 +9,8 @@ #include "mozilla/dom/ElementInlines.h" #include "mozilla/ArrayUtils.h" #include "mozilla/MathAlgorithms.h" +#include "mozilla/dom/MediaKeyNeededEvent.h" +#include "mozilla/AsyncEventDispatcher.h" #include "base/basictypes.h" #include "nsIDOMHTMLMediaElement.h" @@ -423,6 +425,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTM } NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlayed); NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTextTrackManager) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeys) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTMLElement) @@ -439,10 +442,11 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTMLE NS_IMPL_CYCLE_COLLECTION_UNLINK(mAudioChannelAgent) NS_IMPL_CYCLE_COLLECTION_UNLINK(mError) for (uint32_t i = 0; i < tmp->mOutputStreams.Length(); ++i) { - NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputStreams[i].mStream); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputStreams[i].mStream) } - NS_IMPL_CYCLE_COLLECTION_UNLINK(mPlayed); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mPlayed) NS_IMPL_CYCLE_COLLECTION_UNLINK(mTextTrackManager) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mMediaKeys) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLMediaElement) @@ -895,6 +899,9 @@ void HTMLMediaElement::LoadFromSourceChildren() ReportLoadError("MediaLoadUnsupportedTypeAttribute", params, ArrayLength(params)); continue; } + // TODO: "If candidate has a keySystem attribute whose value represents a + // Key System that the user agent knows it cannot use with type, + // then end the synchronous section[...]" (Bug 1016707) nsAutoString media; if (child->GetAttr(kNameSpaceID_None, nsGkAtoms::media, media) && !media.IsEmpty()) { nsCSSParser cssParser; @@ -2003,7 +2010,8 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed& aNodeInfo) mHasAudio(false), mDownloadSuspendedByCache(false), mAudioChannelFaded(false), - mPlayingThroughTheAudioChannel(false) + mPlayingThroughTheAudioChannel(false), + mWaitingFor(MediaWaitingFor::None) { #ifdef PR_LOGGING if (!gMediaElementLog) { @@ -3894,6 +3902,72 @@ NS_IMETHODIMP HTMLMediaElement::CanPlayChanged(int32_t canPlay) return NS_OK; } +MediaKeys* +HTMLMediaElement::GetMediaKeys() const +{ + return mMediaKeys; +} + +already_AddRefed +HTMLMediaElement::SetMediaKeys(mozilla::dom::MediaKeys* aMediaKeys, + ErrorResult& aRv) +{ + nsCOMPtr global = + do_QueryInterface(OwnerDoc()->GetInnerWindow()); + if (!global) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return nullptr; + } + // TODO: Need to shutdown existing MediaKeys instance? bug 1016709. + nsRefPtr promise = new Promise(global); + if (mMediaKeys != aMediaKeys) { + mMediaKeys = aMediaKeys; + } + promise->MaybeResolve(JS::UndefinedHandleValue); + return promise.forget(); +} + +MediaWaitingFor +HTMLMediaElement::WaitingFor() const +{ + return mWaitingFor; +} + +EventHandlerNonNull* +HTMLMediaElement::GetOnneedkey() +{ + EventListenerManager *elm = GetExistingListenerManager(); + return elm ? elm->GetEventHandler(nsGkAtoms::onneedkey, EmptyString()) + : nullptr; +} + +void +HTMLMediaElement::SetOnneedkey(EventHandlerNonNull* handler) +{ + EventListenerManager *elm = GetOrCreateListenerManager(); + if (elm) { + elm->SetEventHandler(nsGkAtoms::onneedkey, EmptyString(), handler); + } +} + +void +HTMLMediaElement::DispatchNeedKey(const nsTArray& aInitData, + const nsAString& aInitDataType) +{ + nsRefPtr event( + MediaKeyNeededEvent::Constructor(this, aInitDataType, aInitData)); + nsRefPtr asyncDispatcher = + new AsyncEventDispatcher(this, event); + asyncDispatcher->PostDOMEvent(); +} + +bool +HTMLMediaElement::IsEventAttributeName(nsIAtom* aName) +{ + return aName == nsGkAtoms::onneedkey || + nsGenericHTMLElement::IsEventAttributeName(aName); +} + NS_IMETHODIMP HTMLMediaElement::WindowVolumeChanged() { SetVolumeInternal(); diff --git a/content/html/content/src/HTMLSourceElement.h b/content/html/content/src/HTMLSourceElement.h index f52b7a57d22..985a80a58ed 100644 --- a/content/html/content/src/HTMLSourceElement.h +++ b/content/html/content/src/HTMLSourceElement.h @@ -64,6 +64,16 @@ public: SetHTMLAttr(nsGkAtoms::media, aMedia, rv); } + void GetKeySystem(nsString& aKeySystem) const + { + GetHTMLAttr(nsGkAtoms::keysystem, aKeySystem); + } + + void SetKeySystem(const nsAString& aKeySystem) + { + SetHTMLAttr(nsGkAtoms::keysystem, aKeySystem); + } + protected: virtual JSObject* WrapNode(JSContext* aCx) MOZ_OVERRIDE; diff --git a/content/media/eme/CDMProxy.cpp b/content/media/eme/CDMProxy.cpp new file mode 100644 index 00000000000..6c4bfac8c7d --- /dev/null +++ b/content/media/eme/CDMProxy.cpp @@ -0,0 +1,199 @@ +/* -*- 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 "mozIGeckoMediaPluginService.h" +#include "nsContentCID.h" +#include "nsServiceManagerUtils.h" +#include "MainThreadUtils.h" + +// TODO: Change the functions in this file to do IPC via the Gecko Media +// Plugins API. In the meantime, the code here merely implements the +// interface we expect will be required when the IPC is working. + +namespace mozilla { + +CDMProxy::CDMProxy(dom::MediaKeys* aKeys, const nsAString& aKeySystem) + : mKeys(aKeys) + , mKeySystem(aKeySystem) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_COUNT_CTOR(CDMProxy); +} + +CDMProxy::~CDMProxy() +{ + MOZ_COUNT_DTOR(CDMProxy); +} + +void +CDMProxy::Init(PromiseId aPromiseId) +{ + MOZ_ASSERT(NS_IsMainThread()); + if (!mGMPThread) { + nsCOMPtr mps = + do_GetService("@mozilla.org/gecko-media-plugin-service;1"); + if (!mps) { + RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR); + return; + } + mps->GetThread(getter_AddRefs(mGMPThread)); + if (!mGMPThread) { + RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR); + return; + } + } + + // TODO: Dispatch task to GMPThread to initialize CDM via IPC. + + mKeys->OnCDMCreated(aPromiseId); +} + +static int sFakeSessionIdNum = 0; + +void +CDMProxy::CreateSession(dom::SessionType aSessionType, + PromiseId aPromiseId, + const nsAString& aInitDataType, + const Uint8Array& aInitData) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mGMPThread); + + // TODO: Dispatch task to GMPThread to call CDM CreateSession via IPC. + + // Make a fake session id. We'll get this from the CDM normally. + nsAutoString id; + id.AppendASCII("FakeSessionId_"); + id.AppendInt(sFakeSessionIdNum++); + + mKeys->OnSessionActivated(aPromiseId, id); +} + +void +CDMProxy::LoadSession(PromiseId aPromiseId, + const nsAString& aSessionId) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mGMPThread); + + // TODO: Dispatch task to GMPThread to call CDM LoadSession via IPC. + // make MediaKeys::mPendingSessions CC'd + + mKeys->OnSessionActivated(aPromiseId, aSessionId); +} + +void +CDMProxy::SetServerCertificate(PromiseId aPromiseId, + const Uint8Array& aCertData) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mGMPThread); + + // TODO: Dispatch task to GMPThread to call CDM SetServerCertificate via IPC. + + ResolvePromise(aPromiseId); +} + +static int sUpdateCount = 0; + +void +CDMProxy::UpdateSession(const nsAString& aSessionId, + PromiseId aPromiseId, + const Uint8Array& aResponse) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mGMPThread); + NS_ENSURE_TRUE_VOID(!mKeys.IsNull()); + + // TODO: Dispatch task to GMPThread to call CDM UpdateSession via IPC. + + nsRefPtr session(mKeys->GetSession(aSessionId)); + nsAutoCString str(NS_LITERAL_CSTRING("Update_")); + str.AppendInt(sUpdateCount++); + nsTArray msg; + msg.AppendElements(str.get(), str.Length()); + session->DispatchKeyMessage(msg, NS_LITERAL_STRING("http://bogus.url")); + ResolvePromise(aPromiseId); +} + +void +CDMProxy::CloseSession(const nsAString& aSessionId, + PromiseId aPromiseId) +{ + MOZ_ASSERT(NS_IsMainThread()); + NS_ENSURE_TRUE_VOID(!mKeys.IsNull()); + + // TODO: Dispatch task to GMPThread to call CDM CloseSession via IPC. + + nsRefPtr session(mKeys->GetSession(aSessionId)); + + // Pretend that the CDM actually does close the session... + // "...the [MediaKeySession's] closed attribute promise is resolved + // when the session is closed." + session->OnClosed(); + + // "The promise is resolved when the request has been processed." + ResolvePromise(aPromiseId); +} + +void +CDMProxy::RemoveSession(const nsAString& aSessionId, + PromiseId aPromiseId) +{ + MOZ_ASSERT(NS_IsMainThread()); + + // TODO: Dispatch task to GMPThread to call CDM RemoveSession via IPC. + + // Assume CDM immediately removes session's data, then close the session + // as per the spec. + CloseSession(aSessionId, aPromiseId); +} + +void +CDMProxy::Shutdown() +{ + MOZ_ASSERT(NS_IsMainThread()); + mKeys.Clear(); +} + +void +CDMProxy::RejectPromise(PromiseId aId, nsresult aCode) +{ + if (NS_IsMainThread()) { + if (!mKeys.IsNull()) { + mKeys->RejectPromise(aId, aCode); + } else { + NS_WARNING("CDMProxy unable to reject promise!"); + } + } else { + nsRefPtr 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 task; + task = NS_NewRunnableMethodWithArg(this, + &CDMProxy::ResolvePromise, + aId); + NS_DispatchToMainThread(task); + } +} + +} // namespace mozilla diff --git a/content/media/eme/CDMProxy.h b/content/media/eme/CDMProxy.h new file mode 100644 index 00000000000..edf0fb2f49d --- /dev/null +++ b/content/media/eme/CDMProxy.h @@ -0,0 +1,171 @@ +/* -*- 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 "nsProxyRelease.h" +#include "mozilla/dom/MediaKeys.h" +#include "mozilla/dom/TypedArray.h" + +class nsIThread; + +namespace mozilla { + +namespace dom { +class MediaKeySession; +} + +// A placeholder proxy to the CDM. +// TODO: The functions here need to do IPC to talk to the CDM via the +// Gecko Media Plugin API, which we'll need to extend for H.264 and EME +// content. +// 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; + typedef dom::Uint8Array Uint8Array; +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); + + // Main thread only. + // Uses the CDM to create a key session. + // Caller is responsible for calling aInitData.ComputeLengthAndData(). + // Calls MediaKeys::OnSessionActivated() when session is created. + void CreateSession(dom::SessionType aSessionType, + PromiseId aPromiseId, + const nsAString& aInitDataType, + const Uint8Array& 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. + // Caller is responsible for calling aCert.ComputeLengthAndData(). + // Calls MediaKeys->ResolvePromise(aPromiseId) after the CDM has + // processed the request. + void SetServerCertificate(PromiseId aPromiseId, + const Uint8Array& aCert); + + // Main thread only. + // Sends an update to the CDM. + // Caller is responsible for calling aResponse.ComputeLengthAndData(). + // Calls MediaKeys->ResolvePromise(aPromiseId) after the CDM has + // processed the request. + void UpdateSession(const nsAString& aSessionId, + PromiseId aPromiseId, + const Uint8Array& 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(); + +private: + + 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 mProxy; + PromiseId mId; + nsresult mCode; + }; + + // 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); + + ~CDMProxy(); + + // Helper to enforce that a raw pointer is only accessed on the main thread. + template + class MainThreadOnlyRawPtr { + public: + 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_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 mKeys; + + const nsAutoString mKeySystem; + + // Gecko Media Plugin thread. All interactions with the out-of-process + // EME plugin must come from this thread. + nsRefPtr mGMPThread; +}; + +} // namespace mozilla + +#endif // CDMProxy_h_ diff --git a/content/media/eme/EMELog.cpp b/content/media/eme/EMELog.cpp new file mode 100644 index 00000000000..4f79fb94ed5 --- /dev/null +++ b/content/media/eme/EMELog.cpp @@ -0,0 +1,32 @@ +/* -*- 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 "EMELog.h" +#include "mozilla/NullPtr.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 + +} // namespace mozilla diff --git a/content/media/eme/EMELog.h b/content/media/eme/EMELog.h new file mode 100644 index 00000000000..46a6a2a90eb --- /dev/null +++ b/content/media/eme/EMELog.h @@ -0,0 +1,35 @@ +/* -*- 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 "prlog.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 + +#endif // PR_LOGGING +} // namespace mozilla diff --git a/content/media/eme/MediaKeyError.cpp b/content/media/eme/MediaKeyError.cpp new file mode 100644 index 00000000000..7a21754e533 --- /dev/null +++ b/content/media/eme/MediaKeyError.cpp @@ -0,0 +1,39 @@ +/* -*- 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) +{ + SetIsDOMBinding(); +} + +MediaKeyError::~MediaKeyError() +{ +} + +uint32_t +MediaKeyError::SystemCode() const +{ + return mSystemCode; +} + +JSObject* +MediaKeyError::WrapObject(JSContext* aCx) +{ + return MediaKeyErrorBinding::Wrap(aCx, this); +} + + +} // namespace dom +} // namespace mozilla diff --git a/content/media/eme/MediaKeyError.h b/content/media/eme/MediaKeyError.h new file mode 100644 index 00000000000..a423da942f6 --- /dev/null +++ b/content/media/eme/MediaKeyError.h @@ -0,0 +1,38 @@ +/* -*- 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 MOZ_FINAL : public Event +{ +public: + NS_FORWARD_TO_EVENT + + MediaKeyError(EventTarget* aOwner, uint32_t aSystemCode); + ~MediaKeyError(); + + virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; + + uint32_t SystemCode() const; + +private: + uint32_t mSystemCode; +}; + +} // namespace dom +} // namespace mozilla + +#endif diff --git a/content/media/eme/MediaKeyMessageEvent.cpp b/content/media/eme/MediaKeyMessageEvent.cpp new file mode 100644 index 00000000000..0c440ad6095 --- /dev/null +++ b/content/media/eme/MediaKeyMessageEvent.cpp @@ -0,0 +1,127 @@ +/* -*- 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" + +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::WrapObject(JSContext* aCx) +{ + return MediaKeyMessageEventBinding::Wrap(aCx, this); +} + +already_AddRefed +MediaKeyMessageEvent::Constructor(EventTarget* aOwner, + const nsAString& aURL, + const nsTArray& aMessage) +{ + nsRefPtr e = new MediaKeyMessageEvent(aOwner); + e->InitEvent(NS_LITERAL_STRING("message"), false, false); + e->mRawMessage = aMessage; + e->mDestinationURL = aURL; + e->SetTrusted(true); + return e.forget(); +} + +already_AddRefed +MediaKeyMessageEvent::Constructor(const GlobalObject& aGlobal, + const nsAString& aType, + const MediaKeyMessageEventInit& aEventInitDict, + ErrorResult& aRv) +{ + nsCOMPtr owner = do_QueryInterface(aGlobal.GetAsSupports()); + nsRefPtr e = new MediaKeyMessageEvent(owner); + bool trusted = e->Init(owner); + e->InitEvent(aType, aEventInitDict.mBubbles, aEventInitDict.mCancelable); + if (aEventInitDict.mMessage.WasPassed()) { + const auto& a = aEventInitDict.mMessage.Value(); + a.ComputeLengthAndData(); + e->mMessage = Uint8Array::Create(aGlobal.GetContext(), owner, a.Length(), a.Data()); + } else { + e->mMessage = Uint8Array::Create(aGlobal.GetContext(), owner, 0, nullptr); + } + if (!e->mMessage) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return nullptr; + } + e->mDestinationURL = aEventInitDict.mDestinationURL; + e->SetTrusted(trusted); + return e.forget(); +} + +JSObject* +MediaKeyMessageEvent::GetMessage(JSContext* cx, ErrorResult& aRv) +{ + if (!mMessage) { + mMessage = Uint8Array::Create(cx, + this, + mRawMessage.Length(), + mRawMessage.Elements()); + if (!mMessage) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return nullptr; + } + mRawMessage.Clear(); + } + JS::ExposeObjectToActiveJS(mMessage); + return mMessage; +} + +void +MediaKeyMessageEvent::GetDestinationURL(nsString& aRetVal) const +{ + aRetVal = mDestinationURL; +} + +} // namespace dom +} // namespace mozilla diff --git a/content/media/eme/MediaKeyMessageEvent.h b/content/media/eme/MediaKeyMessageEvent.h new file mode 100644 index 00000000000..d7a91f092bf --- /dev/null +++ b/content/media/eme/MediaKeyMessageEvent.h @@ -0,0 +1,65 @@ +/* -*- 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 { + +class MediaKeyMessageEventInit; + +class MediaKeyMessageEvent MOZ_FINAL : public Event +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(MediaKeyMessageEvent, Event) + virtual ~MediaKeyMessageEvent(); +protected: + MediaKeyMessageEvent(EventTarget* aOwner); + + JS::Heap mMessage; + nsString mDestinationURL; + +public: + virtual MediaKeyMessageEvent* AsMediaKeyMessageEvent(); + + virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; + + static already_AddRefed + Constructor(EventTarget* aOwner, + const nsAString& aURL, + const nsTArray& aMessage); + + static already_AddRefed + Constructor(const GlobalObject& aGlobal, + const nsAString& aType, + const MediaKeyMessageEventInit& aEventInitDict, + ErrorResult& aRv); + + JSObject* GetMessage(JSContext* cx, ErrorResult& aRv); + + void GetDestinationURL(nsString& aRetVal) const; + +private: + nsTArray mRawMessage; +}; + + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_MediaKeyMessageEvent_h__ diff --git a/content/media/eme/MediaKeyNeededEvent.cpp b/content/media/eme/MediaKeyNeededEvent.cpp new file mode 100644 index 00000000000..1463ff7325e --- /dev/null +++ b/content/media/eme/MediaKeyNeededEvent.cpp @@ -0,0 +1,118 @@ +/* -*- 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 "MediaKeyNeededEvent.h" +#include "mozilla/dom/MediaKeyNeededEventBinding.h" +#include "nsContentUtils.h" +#include "jsfriendapi.h" +#include "nsINode.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_CLASS(MediaKeyNeededEvent) + +NS_IMPL_ADDREF_INHERITED(MediaKeyNeededEvent, Event) +NS_IMPL_RELEASE_INHERITED(MediaKeyNeededEvent, Event) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MediaKeyNeededEvent, Event) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(MediaKeyNeededEvent, Event) + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mInitData) +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MediaKeyNeededEvent, Event) + tmp->mInitData = nullptr; +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaKeyNeededEvent) +NS_INTERFACE_MAP_END_INHERITING(Event) + +MediaKeyNeededEvent::MediaKeyNeededEvent(EventTarget* aOwner) + : Event(aOwner, nullptr, nullptr) +{ + mozilla::HoldJSObjects(this); +} + +MediaKeyNeededEvent::~MediaKeyNeededEvent() +{ + mInitData = nullptr; + mozilla::DropJSObjects(this); +} + +JSObject* +MediaKeyNeededEvent::WrapObject(JSContext* aCx) +{ + return MediaKeyNeededEventBinding::Wrap(aCx, this); +} + +already_AddRefed +MediaKeyNeededEvent::Constructor(EventTarget* aOwner, + const nsAString& aInitDataType, + const nsTArray& aInitData) +{ + nsRefPtr e = new MediaKeyNeededEvent(aOwner); + e->InitEvent(NS_LITERAL_STRING("needkey"), false, false); + e->mInitDataType = aInitDataType; + e->mRawInitData = aInitData; + e->SetTrusted(true); + return e.forget(); +} + +already_AddRefed +MediaKeyNeededEvent::Constructor(const GlobalObject& aGlobal, + const nsAString& aType, + const MediaKeyNeededEventInit& aEventInitDict, + ErrorResult& aRv) +{ + nsCOMPtr owner = do_QueryInterface(aGlobal.GetAsSupports()); + nsRefPtr e = new MediaKeyNeededEvent(owner); + bool trusted = e->Init(owner); + e->InitEvent(aType, aEventInitDict.mBubbles, aEventInitDict.mCancelable); + e->mInitDataType = aEventInitDict.mInitDataType; + if (aEventInitDict.mInitData.WasPassed() && + !aEventInitDict.mInitData.Value().IsNull()) { + const auto& a = aEventInitDict.mInitData.Value().Value(); + a.ComputeLengthAndData(); + e->mInitData = Uint8Array::Create(aGlobal.GetContext(), owner, a.Length(), a.Data()); + if (!e->mInitData) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return nullptr; + } + } + e->SetTrusted(trusted); + return e.forget(); +} + +void +MediaKeyNeededEvent::GetInitDataType(nsString& aRetVal) const +{ + aRetVal = mInitDataType; +} + +JSObject* +MediaKeyNeededEvent::GetInitData(JSContext* cx, ErrorResult& aRv) +{ + if (mRawInitData.Length()) { + mInitData = Uint8Array::Create(cx, + this, + mRawInitData.Length(), + mRawInitData.Elements()); + if (!mInitData) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return nullptr; + } + mRawInitData.Clear(); + } + if (mInitData) { + JS::ExposeObjectToActiveJS(mInitData); + } + return mInitData; +} + +} // namespace dom +} // namespace mozilla diff --git a/content/media/eme/MediaKeyNeededEvent.h b/content/media/eme/MediaKeyNeededEvent.h new file mode 100644 index 00000000000..5e2a224d96c --- /dev/null +++ b/content/media/eme/MediaKeyNeededEvent.h @@ -0,0 +1,62 @@ +/* -*- 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/MediaKeyNeededEventBinding.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 MediaKeyNeededEvent MOZ_FINAL : public Event +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(MediaKeyNeededEvent, Event) + virtual ~MediaKeyNeededEvent(); +protected: + MediaKeyNeededEvent(EventTarget* aOwner); + + nsString mInitDataType; + JS::Heap mInitData; + +public: + + virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; + + static already_AddRefed + Constructor(EventTarget* aOwner, + const nsAString& aInitDataType, + const nsTArray& aInitData); + + static already_AddRefed + Constructor(const GlobalObject& aGlobal, + const nsAString& aType, + const MediaKeyNeededEventInit& aEventInitDict, + ErrorResult& aRv); + + void GetInitDataType(nsString& aRetVal) const; + + JSObject* GetInitData(JSContext* cx, ErrorResult& aRv); +private: + nsTArray mRawInitData; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_MediaKeyNeededEvent_h__ diff --git a/content/media/eme/MediaKeySession.cpp b/content/media/eme/MediaKeySession.cpp new file mode 100644 index 00000000000..a5aee257304 --- /dev/null +++ b/content/media/eme/MediaKeySession.cpp @@ -0,0 +1,178 @@ +/* -*- 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/MediaKeyNeededEvent.h" +#include "nsCycleCollectionParticipant.h" +#include "mozilla/CDMProxy.h" +#include "mozilla/AsyncEventDispatcher.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaKeySession, + DOMEventTargetHelper, + mMediaKeyError, + mKeys, + 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) + +MediaKeySession::MediaKeySession(nsPIDOMWindow* aParent, + MediaKeys* aKeys, + const nsAString& aKeySystem, + SessionType aSessionType) + : DOMEventTargetHelper(aParent) + , mKeys(aKeys) + , mKeySystem(aKeySystem) + , mSessionType(aSessionType) + , mIsClosed(false) +{ + MOZ_ASSERT(aParent); + mClosed = mKeys->MakePromise(); +} + +void MediaKeySession::Init(const nsAString& aSessionId) +{ + mSessionId = aSessionId; +} + +MediaKeySession::~MediaKeySession() +{ +} + +MediaKeyError* +MediaKeySession::GetError() const +{ + return mMediaKeyError; +} + +void +MediaKeySession::GetKeySystem(nsString& aKeySystem) const +{ + aKeySystem = mKeySystem; +} + +void +MediaKeySession::GetSessionId(nsString& aSessionId) const +{ + aSessionId = mSessionId; +} + +JSObject* +MediaKeySession::WrapObject(JSContext* aCx) +{ + return MediaKeySessionBinding::Wrap(aCx, this); +} + +double +MediaKeySession::Expiration() const +{ + return JS::GenericNaN(); +} + +Promise* +MediaKeySession::Closed() const +{ + return mClosed; +} + +already_AddRefed +MediaKeySession::Update(const Uint8Array& aResponse) +{ + nsRefPtr promise(mKeys->MakePromise()); + aResponse.ComputeLengthAndData(); + if (IsClosed() || + !mKeys->GetCDMProxy() || + !aResponse.Length()) { + promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR); + return promise.forget(); + } + mKeys->GetCDMProxy()->UpdateSession(mSessionId, + mKeys->StorePromise(promise), + aResponse); + return promise.forget(); +} + +already_AddRefed +MediaKeySession::Close() +{ + nsRefPtr promise(mKeys->MakePromise()); + 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; + // TODO: reset usableKeyIds + mKeys->OnSessionClosed(this); + mKeys = nullptr; + mClosed->MaybeResolve(JS::UndefinedHandleValue); +} + +bool +MediaKeySession::IsClosed() const +{ + return mIsClosed; +} + +already_AddRefed +MediaKeySession::Remove() +{ + nsRefPtr promise(mKeys->MakePromise()); + 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(const nsTArray& aMessage, + const nsString& aURL) +{ + nsRefPtr event( + MediaKeyMessageEvent::Constructor(this, aURL, aMessage)); + nsRefPtr asyncDispatcher = + new AsyncEventDispatcher(this, event); + asyncDispatcher->PostDOMEvent(); +} + +void +MediaKeySession::DispatchKeyError(uint32_t aSystemCode) +{ + RefPtr event(new MediaKeyError(this, aSystemCode)); + nsRefPtr asyncDispatcher = + new AsyncEventDispatcher(this, event); + asyncDispatcher->PostDOMEvent(); +} + +} // namespace dom +} // namespace mozilla diff --git a/content/media/eme/MediaKeySession.h b/content/media/eme/MediaKeySession.h new file mode 100644 index 00000000000..8e616c0c1a8 --- /dev/null +++ b/content/media/eme/MediaKeySession.h @@ -0,0 +1,93 @@ +/* -*- 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" + +struct JSContext; + +namespace mozilla { + +class CDMProxy; + +namespace dom { + +class MediaKeyError; + +class MediaKeySession MOZ_FINAL : public DOMEventTargetHelper +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaKeySession, + DOMEventTargetHelper) +public: + MediaKeySession(nsPIDOMWindow* aParent, + MediaKeys* aKeys, + const nsAString& aKeySystem, + SessionType aSessionType); + + void Init(const nsAString& aSessionId); + + ~MediaKeySession(); + + virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; + + // Mark this as resultNotAddRefed to return raw pointers + MediaKeyError* GetError() const; + + void GetKeySystem(nsString& aRetval) const; + + void GetSessionId(nsString& aRetval) 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 Update(const Uint8Array& response); + + already_AddRefed Close(); + + already_AddRefed Remove(); + + void DispatchKeyMessage(const nsTArray& aMessage, + const nsString& aURL); + + void DispatchKeyError(uint32_t system_code); + + void OnClosed(); + + bool IsClosed() const; + +private: + nsRefPtr mClosed; + + nsRefPtr mMediaKeyError; + nsRefPtr mKeys; + const nsString mKeySystem; + nsString mSessionId; + const SessionType mSessionType; + bool mIsClosed; +}; + +} // namespace dom +} // namespace mozilla + +#endif diff --git a/content/media/eme/MediaKeys.cpp b/content/media/eme/MediaKeys.cpp new file mode 100644 index 00000000000..a701328d4ef --- /dev/null +++ b/content/media/eme/MediaKeys.cpp @@ -0,0 +1,291 @@ +/* -*- 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/MediaKeys.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/CDMProxy.h" +#include "nsContentUtils.h" +#include "EMELog.h" + +namespace mozilla { + +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MediaKeys, + 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) +{ + SetIsDOMBinding(); +} + +MediaKeys::~MediaKeys() +{ + if (mProxy) { + mProxy->Shutdown(); + mProxy = nullptr; + } +} + +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 +MediaKeys::SetServerCertificate(const Uint8Array& aCert) +{ + aCert.ComputeLengthAndData(); + nsRefPtr promise(MakePromise()); + mProxy->SetServerCertificate(StorePromise(promise), aCert); + return promise.forget(); +} + +/* static */ +IsTypeSupportedResult +MediaKeys::IsTypeSupported(const GlobalObject& aGlobal, + const nsAString& aKeySystem, + const Optional& aInitDataType, + const Optional& aContentType, + const Optional& aCapability) +{ + // TODO: Query list of known CDMs and their supported content types. + // TODO: Should really get spec changed to this is async, so we can wait + // for user to consent to running plugin. + return IsTypeSupportedResult::Maybe; +} + +already_AddRefed +MediaKeys::MakePromise() +{ + nsCOMPtr global = do_QueryInterface(GetParentObject()); + if (!global) { + NS_WARNING("Passed non-global to MediaKeys ctor!"); + return nullptr; + } + nsRefPtr promise = new Promise(global); + return promise.forget(); +} + +PromiseId +MediaKeys::StorePromise(Promise* aPromise) +{ + static uint32_t sEMEPromiseCount = 1; + MOZ_ASSERT(aPromise); + uint32_t id = sEMEPromiseCount++; + mPromises.Put(id, aPromise); + return id; +} + +already_AddRefed +MediaKeys::RetrievePromise(PromiseId aId) +{ + nsRefPtr promise; + mPromises.Remove(aId, getter_AddRefs(promise)); + return promise.forget(); +} + +void +MediaKeys::RejectPromise(PromiseId aId, nsresult aExceptionCode) +{ + nsRefPtr promise(RetrievePromise(aId)); + if (!promise) { + NS_WARNING("MediaKeys tried to reject a non-existent 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); +} + +void +MediaKeys::ResolvePromise(PromiseId aId) +{ + nsRefPtr promise(RetrievePromise(aId)); + if (!promise) { + NS_WARNING("MediaKeys tried to resolve a non-existent promise"); + return; + } + // We should not resolve CreateSession or LoadSession calls via this path, + // OnSessionActivated() should be called instead. + MOZ_ASSERT(!mPendingSessions.Contains(aId)); + promise->MaybeResolve(JS::UndefinedHandleValue); +} + +/* static */ +already_AddRefed +MediaKeys::Create(const GlobalObject& aGlobal, + const nsAString& aKeySystem, + ErrorResult& aRv) +{ + // CDMProxy keeps MediaKeys alive until it resolves the promise and thus + // returns the MediaKeys object to JS. + nsCOMPtr window = do_QueryInterface(aGlobal.GetAsSupports()); + if (!window) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsRefPtr keys = new MediaKeys(window, aKeySystem); + nsRefPtr promise(keys->MakePromise()); + if (!promise) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return nullptr; + } + + if (!aKeySystem.EqualsASCII("org.w3.clearkey")) { + aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return nullptr; + } + + keys->mProxy = new CDMProxy(keys, aKeySystem); + keys->mProxy->Init(keys->StorePromise(promise)); + + return promise.forget(); +} + +void +MediaKeys::OnCDMCreated(PromiseId aId) +{ + nsRefPtr promise(RetrievePromise(aId)); + if (!promise) { + NS_WARNING("MediaKeys tried to resolve a non-existent promise"); + return; + } + nsRefPtr keys(this); + promise->MaybeResolve(keys); +} + +already_AddRefed +MediaKeys::LoadSession(const nsAString& aSessionId) +{ + nsRefPtr promise(MakePromise()); + + if (aSessionId.IsEmpty()) { + promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR); + // "The sessionId parameter is empty." + return promise.forget(); + } + + // TODO: The spec doesn't specify what to do in this case... + if (mKeySessions.Contains(aSessionId)) { + promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR); + return promise.forget(); + } + + // Create session. + nsRefPtr session( + new MediaKeySession(GetParentObject(), this, mKeySystem, SessionType::Persistent)); + + // Proxy owns session object until resolving promise. + mProxy->LoadSession(StorePromise(promise), + aSessionId); + + return promise.forget(); +} + +already_AddRefed +MediaKeys::CreateSession(const nsAString& initDataType, + const Uint8Array& aInitData, + SessionType aSessionType) +{ + aInitData.ComputeLengthAndData(); + nsRefPtr promise(MakePromise()); + nsRefPtr session = new MediaKeySession(GetParentObject(), + this, + mKeySystem, + aSessionType); + auto pid = StorePromise(promise); + // Hang onto session until the CDM has finished setting it up. + mPendingSessions.Put(pid, session); + mProxy->CreateSession(aSessionType, + pid, + initDataType, + aInitData); + + return promise.forget(); +} + +void +MediaKeys::OnSessionActivated(PromiseId aId, const nsAString& aSessionId) +{ + nsRefPtr promise(RetrievePromise(aId)); + if (!promise) { + NS_WARNING("MediaKeys tried to resolve a non-existent promise"); + return; + } + MOZ_ASSERT(mPendingSessions.Contains(aId)); + + nsRefPtr session; + if (!mPendingSessions.Get(aId, getter_AddRefs(session)) || !session) { + NS_WARNING("Received activation for non-existent session!"); + promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR); + return; + } + + // Session has completed creation/loading, remove it from mPendingSessions, + // and resolve the promise with it. We store it in mKeySessions, so we can + // find it again if we need to send messages to it etc. + mPendingSessions.Remove(aId); + session->Init(aSessionId); + mKeySessions.Put(aSessionId, session); + promise->MaybeResolve(session); +} + +void +MediaKeys::OnSessionClosed(MediaKeySession* aSession) +{ + nsAutoString id; + aSession->GetSessionId(id); + mKeySessions.Remove(id); +} + +already_AddRefed +MediaKeys::GetSession(const nsAString& aSessionId) +{ + nsRefPtr session; + mKeySessions.Get(aSessionId, getter_AddRefs(session)); + return session.forget(); +} + +} // namespace dom +} // namespace mozilla diff --git a/content/media/eme/MediaKeys.h b/content/media/eme/MediaKeys.h new file mode 100644 index 00000000000..75f3168bce5 --- /dev/null +++ b/content/media/eme/MediaKeys.h @@ -0,0 +1,120 @@ +/* -*- 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" + +namespace mozilla { + +class CDMProxy; + +namespace dom { + +class MediaKeySession; + +typedef nsRefPtrHashtable KeySessionHashMap; +typedef nsRefPtrHashtable PromiseHashMap; +typedef nsRefPtrHashtable PendingKeySessionsHashMap; +typedef uint32_t PromiseId; + +// This class is used on the main thread only. +// Note: it's addref/release is not (and can't be) thread safe! +class MediaKeys MOZ_FINAL : public nsISupports, + public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MediaKeys) + + MediaKeys(nsPIDOMWindow* aParentWindow, const nsAString& aKeySystem); + + ~MediaKeys(); + + nsPIDOMWindow* GetParentObject() const; + + virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; + + // Javascript: readonly attribute DOMString keySystem; + void GetKeySystem(nsString& retval) const; + + // JavaScript: MediaKeys.createSession() + already_AddRefed CreateSession(const nsAString& aInitDataType, + const Uint8Array& aInitData, + SessionType aSessionType); + + // JavaScript: MediaKeys.loadSession() + already_AddRefed LoadSession(const nsAString& aSessionId); + + // JavaScript: MediaKeys.SetServerCertificate() + already_AddRefed SetServerCertificate(const Uint8Array& aServerCertificate); + + // JavaScript: MediaKeys.create() + static + already_AddRefed Create(const GlobalObject& aGlobal, + const nsAString& aKeySystem, + ErrorResult& aRv); + + // JavaScript: MediaKeys.IsTypeSupported() + static IsTypeSupportedResult IsTypeSupported(const GlobalObject& aGlobal, + const nsAString& aKeySystem, + const Optional& aInitDataType, + const Optional& aContentType, + const Optional& aCapability); + + already_AddRefed GetSession(const nsAString& aSessionId); + + // Called once a Create() operation succeeds. + void OnCDMCreated(PromiseId aId); + // Called once a CreateSession or LoadSession succeeds. + void OnSessionActivated(PromiseId aId, const nsAString& aSessionId); + // Called once a session has closed. + void OnSessionClosed(MediaKeySession* aSession); + + CDMProxy* GetCDMProxy() { return mProxy; } + + // Makes a new promise, or nullptr on failure. + already_AddRefed MakePromise(); + // 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); + +private: + + // Removes promise from mPromises, and returns it. + already_AddRefed 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 mProxy; + + nsCOMPtr mParent; + nsString mKeySystem; + KeySessionHashMap mKeySessions; + PromiseHashMap mPromises; + PendingKeySessionsHashMap mPendingSessions; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_mediakeys_h__ diff --git a/content/media/eme/moz.build b/content/media/eme/moz.build new file mode 100644 index 00000000000..256fad6fed6 --- /dev/null +++ b/content/media/eme/moz.build @@ -0,0 +1,31 @@ +# -*- 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 += [ + 'MediaKeyError.h', + 'MediaKeyMessageEvent.h', + 'MediaKeyNeededEvent.h', + 'MediaKeys.h', + 'MediaKeySession.h', +] + +EXPORTS.mozilla += [ + 'CDMProxy.h', +] + +UNIFIED_SOURCES += [ + 'CDMProxy.cpp', + 'EMELog.cpp', + 'MediaKeyError.cpp', + 'MediaKeyMessageEvent.cpp', + 'MediaKeyNeededEvent.cpp', + 'MediaKeys.cpp', + 'MediaKeySession.cpp', +] + +FINAL_LIBRARY = 'gklayout' + +FAIL_ON_WARNINGS = True diff --git a/content/media/moz.build b/content/media/moz.build index 67e08dcd5c3..7fb87d200e9 100644 --- a/content/media/moz.build +++ b/content/media/moz.build @@ -50,6 +50,8 @@ if CONFIG['MOZ_OMX_DECODER']: PARALLEL_DIRS += ['webspeech'] +PARALLEL_DIRS += ['eme'] + TEST_DIRS += [ 'test', 'gtest', diff --git a/dom/events/EventNameList.h b/dom/events/EventNameList.h index 83702a6dfe8..74d27da4cbe 100644 --- a/dom/events/EventNameList.h +++ b/dom/events/EventNameList.h @@ -293,7 +293,6 @@ EVENT(mozpointerlockerror, NS_POINTERLOCKERROR, EventNameType_HTML, NS_EVENT) - EVENT(pointerdown, NS_POINTER_DOWN, EventNameType_All, diff --git a/dom/webidl/HTMLMediaElement.webidl b/dom/webidl/HTMLMediaElement.webidl index bdfdf3e5df1..148480cfe8c 100644 --- a/dom/webidl/HTMLMediaElement.webidl +++ b/dom/webidl/HTMLMediaElement.webidl @@ -130,3 +130,25 @@ partial interface HTMLMediaElement { // because of the audiochannel manager. // * onmozinterruptend - called when the interruption is concluded }; + +enum MediaWaitingFor { + "none", + "data", + "key" +}; + +// Encrypted Media Extensions +partial interface HTMLMediaElement { + [Pref="media.eme.enabled"] + readonly attribute MediaKeys? mediaKeys; + + // Promise + [Pref="media.eme.enabled", Throws, NewObject] + Promise setMediaKeys(MediaKeys? mediaKeys); + + [Pref="media.eme.enabled"] + attribute EventHandler onneedkey; + + [Pref="media.eme.enabled"] + readonly attribute MediaWaitingFor waitingFor; +}; diff --git a/dom/webidl/HTMLSourceElement.webidl b/dom/webidl/HTMLSourceElement.webidl index 8119a1e33cf..516f8c25f86 100644 --- a/dom/webidl/HTMLSourceElement.webidl +++ b/dom/webidl/HTMLSourceElement.webidl @@ -19,3 +19,9 @@ interface HTMLSourceElement : HTMLElement { [SetterThrows] attribute DOMString media; }; + +// Encrypted Media Extensions +partial interface HTMLSourceElement { + [Pref="media.eme.enabled"] + attribute DOMString keySystem; +}; diff --git a/dom/webidl/MediaKeyError.webidl b/dom/webidl/MediaKeyError.webidl new file mode 100644 index 00000000000..bf035e01ed4 --- /dev/null +++ b/dom/webidl/MediaKeyError.webidl @@ -0,0 +1,19 @@ +/* -*- 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.enabled"] +interface MediaKeyError : Event { + readonly attribute unsigned long systemCode; +}; diff --git a/dom/webidl/MediaKeyMessageEvent.webidl b/dom/webidl/MediaKeyMessageEvent.webidl new file mode 100644 index 00000000000..6317039b931 --- /dev/null +++ b/dom/webidl/MediaKeyMessageEvent.webidl @@ -0,0 +1,23 @@ +/* -*- 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.enabled", Constructor(DOMString type, optional MediaKeyMessageEventInit eventInitDict)] +interface MediaKeyMessageEvent : Event { + [Throws] + readonly attribute Uint8Array message; + readonly attribute DOMString? destinationURL; +}; + +dictionary MediaKeyMessageEventInit : EventInit { + Uint8Array message; + DOMString? destinationURL = ""; +}; diff --git a/dom/webidl/MediaKeyNeededEvent.webidl b/dom/webidl/MediaKeyNeededEvent.webidl new file mode 100644 index 00000000000..ad088fcb44c --- /dev/null +++ b/dom/webidl/MediaKeyNeededEvent.webidl @@ -0,0 +1,23 @@ +/* -*- 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.enabled", Constructor(DOMString type, optional MediaKeyNeededEventInit eventInitDict)] +interface MediaKeyNeededEvent : Event { + readonly attribute DOMString initDataType; + [Throws] + readonly attribute Uint8Array? initData; +}; + +dictionary MediaKeyNeededEventInit : EventInit { + DOMString initDataType = ""; + Uint8Array? initData; +}; diff --git a/dom/webidl/MediaKeySession.webidl b/dom/webidl/MediaKeySession.webidl new file mode 100644 index 00000000000..0878d77f386 --- /dev/null +++ b/dom/webidl/MediaKeySession.webidl @@ -0,0 +1,43 @@ +/* -*- 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.enabled"] +interface MediaKeySession : EventTarget { + // error state + readonly attribute MediaKeyError? error; + + // session properties + readonly attribute DOMString keySystem; + readonly attribute DOMString sessionId; + + // Invalid WebIDL, doesn't work. + // https://www.w3.org/Bugs/Public/show_bug.cgi?id=25594 + // readonly attribute Array usableKeyIds; + + readonly attribute unrestricted double expiration; + + // Promise + readonly attribute Promise closed; + + // session operations + //Promise + [NewObject] + Promise update(Uint8Array response); + + // Promise + [NewObject] + Promise close(); + + // Promise + [NewObject] + Promise remove(); +}; diff --git a/dom/webidl/MediaKeys.webidl b/dom/webidl/MediaKeys.webidl new file mode 100644 index 00000000000..afdaa9a70eb --- /dev/null +++ b/dom/webidl/MediaKeys.webidl @@ -0,0 +1,37 @@ +/* -*- 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.enabled"] +interface MediaKeys { + readonly attribute DOMString keySystem; + + // Promise + [NewObject] + Promise createSession(DOMString initDataType, Uint8Array initData, optional SessionType sessionType = "temporary"); + + // Promise + [NewObject] + Promise loadSession(DOMString sessionId); + + // Promise + [NewObject] + Promise setServerCertificate(Uint8Array serverCertificate); + + // Promise + [Throws,NewObject] + static Promise create(DOMString keySystem); + static IsTypeSupportedResult isTypeSupported(DOMString keySystem, optional DOMString initDataType, optional DOMString contentType, optional DOMString capability); + +}; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index c56857791c9..f0c1c9393d6 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -231,6 +231,11 @@ WEBIDL_FILES = [ 'LockedFile.webidl', 'MediaElementAudioSourceNode.webidl', 'MediaError.webidl', + 'MediaKeyError.webidl', + 'MediaKeyMessageEvent.webidl', + 'MediaKeyNeededEvent.webidl', + 'MediaKeys.webidl', + 'MediaKeySession.webidl', 'MediaList.webidl', 'MediaQueryList.webidl', 'MediaRecorder.webidl', diff --git a/dom/workers/WorkerFeature.h b/dom/workers/WorkerFeature.h index 62dbdc1329e..4542e856257 100644 --- a/dom/workers/WorkerFeature.h +++ b/dom/workers/WorkerFeature.h @@ -28,6 +28,12 @@ BEGIN_WORKERS_NAMESPACE * | Killing | yes | yes | doesn't run | * +-------------+-------------+-----------------+----------------+ */ + +#ifdef Status +/* Xlib headers insist on this for some reason... Nuke it because + it'll override our member name */ +#undef Status +#endif enum Status { // Not yet scheduled. diff --git a/parser/html/nsHtml5AtomList.h b/parser/html/nsHtml5AtomList.h index a673f1c4f51..b678a99579f 100644 --- a/parser/html/nsHtml5AtomList.h +++ b/parser/html/nsHtml5AtomList.h @@ -168,6 +168,7 @@ HTML5_ATOM(order, "order") HTML5_ATOM(other, "other") HTML5_ATOM(oncut, "oncut") HTML5_ATOM(nargs, "nargs") +HTML5_ATOM(keysystem, "keysystem") HTML5_ATOM(media, "media") HTML5_ATOM(label, "label") HTML5_ATOM(local, "local") diff --git a/widget/BasicEvents.h b/widget/BasicEvents.h index 805591fdc37..ca5d4d5bb13 100644 --- a/widget/BasicEvents.h +++ b/widget/BasicEvents.h @@ -322,6 +322,7 @@ enum nsEventStructType #define NS_RATECHANGE (NS_MEDIA_EVENT_START+17) #define NS_DURATIONCHANGE (NS_MEDIA_EVENT_START+18) #define NS_VOLUMECHANGE (NS_MEDIA_EVENT_START+19) +#define NS_NEED_KEY (NS_MEDIA_EVENT_START+20) // paint notification events #define NS_NOTIFYPAINT_START 3400