author | Chris Pearce <cpearce@mozilla.com> |
Sat, 07 Jun 2014 08:52:15 +1200 | |
changeset 207639 | e2b9d289514f019a8c6f9ca1e8fe8ef928a771fe |
parent 207638 | eca7bdeb0c6e333669cbbe585fbc3de979762aba |
child 207640 | 758bbf88e5a890023e1ad22e39b525a1ea743feb |
push id | 494 |
push user | raliiev@mozilla.com |
push date | Mon, 25 Aug 2014 18:42:16 +0000 |
treeherder | mozilla-release@a3cc3e46b571 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | bz |
bugs | 1016162 |
milestone | 32.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1560,16 +1560,18 @@ pref("identity.fxaccounts.remote.signin. // "identity.fxaccounts.remote.signup.uri" pref. pref("identity.fxaccounts.settings.uri", "https://accounts.firefox.com/settings"); // On GTK, we now default to showing the menubar only when alt is pressed: #ifdef MOZ_WIDGET_GTK 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); // Play with different values of the decay time and get telemetry, // 0 means to randomize (and persist) the experiment value in users' profiles, // -1 means no experiment is run and we use the preferred value for frecency (6h) pref("browser.cache.frecency_experiment", 0);
--- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -492,16 +492,17 @@ GK_ATOM(noautofocus, "noautofocus") GK_ATOM(keepcurrentinview, "keepcurrentinview") GK_ATOM(keepobjectsalive, "keepobjectsalive") GK_ATOM(key, "key") GK_ATOM(keycode, "keycode") 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") GK_ATOM(label, "label") GK_ATOM(lang, "lang") GK_ATOM(language, "language") GK_ATOM(last, "last") GK_ATOM(layer, "layer") @@ -1939,16 +1940,18 @@ GK_ATOM(onended, "onended") GK_ATOM(onratechange, "onratechange") GK_ATOM(ondurationchange, "ondurationchange") GK_ATOM(onvolumechange, "onvolumechange") GK_ATOM(onaddtrack, "onaddtrack") 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") GK_ATOM(emptied, "emptied") GK_ATOM(stalled, "stalled") GK_ATOM(play, "play") GK_ATOM(pause, "pause") GK_ATOM(loadedmetadata, "loadedmetadata")
--- a/content/html/content/public/HTMLMediaElement.h +++ b/content/html/content/public/HTMLMediaElement.h @@ -15,16 +15,26 @@ #include "DOMMediaStream.h" #include "AudioChannelCommon.h" #include "DecoderTraits.h" #include "nsIAudioChannelAgent.h" #include "mozilla/Attributes.h" #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 */ class nsIChannel; class nsIHttpChannel; class nsILoadGroup; @@ -32,16 +42,17 @@ typedef uint16_t nsMediaNetworkState; typedef uint16_t nsMediaReadyState; namespace mozilla { class ErrorResult; class MediaResource; class MediaDecoder; class VideoFrameContainer; namespace dom { +class MediaKeys; class TextTrack; class TimeRanges; class WakeLock; } } class nsITimer; class nsRange; @@ -473,16 +484,32 @@ public: bool MozPreservesPitch() const { return mPreservesPitch; } // XPCOM MozPreservesPitch() is OK + MediaKeys* GetMediaKeys() const; + + already_AddRefed<Promise> SetMediaKeys(MediaKeys* mediaKeys, + ErrorResult& aRv); + + MediaWaitingFor WaitingFor() const; + + mozilla::dom::EventHandlerNonNull* GetOnneedkey(); + void SetOnneedkey(mozilla::dom::EventHandlerNonNull* listener); + + void DispatchNeedKey(const nsTArray<uint8_t>& aInitData, + const nsAString& aInitDataType); + + + bool IsEventAttributeName(nsIAtom* aName) MOZ_OVERRIDE; + bool MozAutoplayEnabled() const { return mAutoplayEnabled; } already_AddRefed<DOMMediaStream> MozCaptureStream(ErrorResult& aRv); already_AddRefed<DOMMediaStream> MozCaptureStreamUntilEnded(ErrorResult& aRv); @@ -1005,16 +1032,19 @@ protected: // Reference to the source element last returned by GetNextSource(). // This is the child source element which we're trying to load from. nsCOMPtr<nsIContent> mSourceLoadCandidate; // Range of time played. nsRefPtr<TimeRanges> mPlayed; + // Encrypted Media Extension media keys. + nsRefPtr<MediaKeys> mMediaKeys; + // Stores the time at the start of the current 'played' range. double mCurrentPlayRangeStart; // If true then we have begun downloading the media content. // Set to false when completed, or not yet started. bool mBegun; // True when the decoder has loaded enough data to display the @@ -1134,14 +1164,16 @@ protected: // Is this media element playing? bool mPlayingThroughTheAudioChannel; // An agent used to join audio channel service. nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent; nsRefPtr<TextTrackManager> mTextTrackManager; + + MediaWaitingFor mWaitingFor; }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_HTMLMediaElement_h
--- a/content/html/content/src/HTMLMediaElement.cpp +++ b/content/html/content/src/HTMLMediaElement.cpp @@ -4,16 +4,18 @@ * 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/HTMLMediaElementBinding.h" #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" #include "nsIDOMHTMLSourceElement.h" #include "TimeRanges.h" #include "nsGenericHTMLElement.h" #include "nsAttrValueInlines.h" #include "nsPresContext.h" @@ -418,36 +420,38 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceLoadCandidate) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioChannelAgent) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError) for (uint32_t i = 0; i < tmp->mOutputStreams.Length(); ++i) { NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutputStreams[i].mStream); } 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) if (tmp->mSrcStream) { // Need to EndMediaStreamPlayback to clear mSrcStream and make sure everything // gets unhooked correctly. tmp->EndSrcMediaStreamPlayback(); } NS_IMPL_CYCLE_COLLECTION_UNLINK(mSrcAttrStream) NS_IMPL_CYCLE_COLLECTION_UNLINK(mMediaSource) NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourcePointer) NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoadBlockedDoc) NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceLoadCandidate) 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) NS_INTERFACE_MAP_ENTRY(nsIObserver) NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback) NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement) // nsIDOMHTMLMediaElement @@ -890,16 +894,19 @@ void HTMLMediaElement::LoadFromSourceChi nsAutoString type; if (child->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type) && GetCanPlay(type) == CANPLAY_NO) { DispatchAsyncSourceError(child); const char16_t* params[] = { type.get(), src.get() }; 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; nsRefPtr<nsMediaList> mediaList(new nsMediaList()); cssParser.ParseMediaList(media, nullptr, 0, mediaList, false); nsIPresShell* presShell = OwnerDoc()->GetShell(); if (presShell && !mediaList->Matches(presShell->GetPresContext(), nullptr)) { DispatchAsyncSourceError(child); @@ -1998,17 +2005,18 @@ HTMLMediaElement::HTMLMediaElement(alrea mHasSelfReference(false), mShuttingDown(false), mSuspendedForPreloadNone(false), mMediaSecurityVerified(false), mCORSMode(CORS_NONE), mHasAudio(false), mDownloadSuspendedByCache(false), mAudioChannelFaded(false), - mPlayingThroughTheAudioChannel(false) + mPlayingThroughTheAudioChannel(false), + mWaitingFor(MediaWaitingFor::None) { #ifdef PR_LOGGING if (!gMediaElementLog) { gMediaElementLog = PR_NewLogModule("nsMediaElement"); } if (!gMediaElementEventsLog) { gMediaElementEventsLog = PR_NewLogModule("nsMediaElementEvents"); } @@ -3889,16 +3897,82 @@ NS_IMETHODIMP HTMLMediaElement::CanPlayC NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE); UpdateChannelMuteState(static_cast<AudioChannelState>(canPlay)); mPaused.SetCanPlay(canPlay != AUDIO_CHANNEL_STATE_MUTED); return NS_OK; } +MediaKeys* +HTMLMediaElement::GetMediaKeys() const +{ + return mMediaKeys; +} + +already_AddRefed<Promise> +HTMLMediaElement::SetMediaKeys(mozilla::dom::MediaKeys* aMediaKeys, + ErrorResult& aRv) +{ + nsCOMPtr<nsIGlobalObject> global = + do_QueryInterface(OwnerDoc()->GetInnerWindow()); + if (!global) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return nullptr; + } + // TODO: Need to shutdown existing MediaKeys instance? bug 1016709. + nsRefPtr<Promise> promise = 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<uint8_t>& aInitData, + const nsAString& aInitDataType) +{ + nsRefPtr<MediaKeyNeededEvent> event( + MediaKeyNeededEvent::Constructor(this, aInitDataType, aInitData)); + nsRefPtr<AsyncEventDispatcher> asyncDispatcher = + new AsyncEventDispatcher(this, event); + asyncDispatcher->PostDOMEvent(); +} + +bool +HTMLMediaElement::IsEventAttributeName(nsIAtom* aName) +{ + return aName == nsGkAtoms::onneedkey || + nsGenericHTMLElement::IsEventAttributeName(aName); +} + NS_IMETHODIMP HTMLMediaElement::WindowVolumeChanged() { SetVolumeInternal(); return NS_OK; } /* readonly attribute TextTrackList textTracks; */ TextTrackList*
--- a/content/html/content/src/HTMLSourceElement.h +++ b/content/html/content/src/HTMLSourceElement.h @@ -59,16 +59,26 @@ public: { GetHTMLAttr(nsGkAtoms::media, aMedia); } void SetMedia(const nsAString& aMedia, mozilla::ErrorResult& rv) { 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; protected: virtual void GetItemValueText(nsAString& text) MOZ_OVERRIDE; virtual void SetItemValueText(const nsAString& text) MOZ_OVERRIDE; };
new file mode 100644 --- /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<mozIGeckoMediaPluginService> mps = + do_GetService("@mozilla.org/gecko-media-plugin-service;1"); + if (!mps) { + RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR); + return; + } + mps->GetThread(getter_AddRefs(mGMPThread)); + if (!mGMPThread) { + RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR); + return; + } + } + + // 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<dom::MediaKeySession> session(mKeys->GetSession(aSessionId)); + nsAutoCString str(NS_LITERAL_CSTRING("Update_")); + str.AppendInt(sUpdateCount++); + nsTArray<uint8_t> 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<dom::MediaKeySession> 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<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); + } +} + +} // namespace mozilla
new file mode 100644 --- /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<CDMProxy> 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 Type> + 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<dom::MediaKeys> mKeys; + + const nsAutoString mKeySystem; + + // Gecko Media Plugin thread. All interactions with the out-of-process + // EME plugin must come from this thread. + nsRefPtr<nsIThread> mGMPThread; +}; + +} // namespace mozilla + +#endif // CDMProxy_h_
new file mode 100644 --- /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
new file mode 100644 --- /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
new file mode 100644 --- /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
new file mode 100644 --- /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
new file mode 100644 --- /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> +MediaKeyMessageEvent::Constructor(EventTarget* aOwner, + const nsAString& aURL, + const nsTArray<uint8_t>& aMessage) +{ + nsRefPtr<MediaKeyMessageEvent> 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> +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); + 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
new file mode 100644 --- /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<JSObject*> mMessage; + nsString mDestinationURL; + +public: + virtual MediaKeyMessageEvent* AsMediaKeyMessageEvent(); + + virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; + + static already_AddRefed<MediaKeyMessageEvent> + Constructor(EventTarget* aOwner, + const nsAString& aURL, + const nsTArray<uint8_t>& aMessage); + + static already_AddRefed<MediaKeyMessageEvent> + 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<uint8_t> mRawMessage; +}; + + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_MediaKeyMessageEvent_h__
new file mode 100644 --- /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> +MediaKeyNeededEvent::Constructor(EventTarget* aOwner, + const nsAString& aInitDataType, + const nsTArray<uint8_t>& aInitData) +{ + nsRefPtr<MediaKeyNeededEvent> 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> +MediaKeyNeededEvent::Constructor(const GlobalObject& aGlobal, + const nsAString& aType, + const MediaKeyNeededEventInit& aEventInitDict, + ErrorResult& aRv) +{ + nsCOMPtr<EventTarget> owner = do_QueryInterface(aGlobal.GetAsSupports()); + nsRefPtr<MediaKeyNeededEvent> 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
new file mode 100644 --- /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<JSObject*> mInitData; + +public: + + virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; + + static already_AddRefed<MediaKeyNeededEvent> + Constructor(EventTarget* aOwner, + const nsAString& aInitDataType, + const nsTArray<uint8_t>& aInitData); + + static already_AddRefed<MediaKeyNeededEvent> + 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<uint8_t> mRawInitData; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_MediaKeyNeededEvent_h__
new file mode 100644 --- /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<Promise> +MediaKeySession::Update(const Uint8Array& aResponse) +{ + nsRefPtr<Promise> 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<Promise> +MediaKeySession::Close() +{ + nsRefPtr<Promise> 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<Promise> +MediaKeySession::Remove() +{ + nsRefPtr<Promise> 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<uint8_t>& aMessage, + const nsString& aURL) +{ + nsRefPtr<MediaKeyMessageEvent> event( + MediaKeyMessageEvent::Constructor(this, aURL, 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(); +} + +} // namespace dom +} // namespace mozilla
new file mode 100644 --- /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<Promise> Update(const Uint8Array& response); + + already_AddRefed<Promise> Close(); + + already_AddRefed<Promise> Remove(); + + void DispatchKeyMessage(const nsTArray<uint8_t>& aMessage, + const nsString& aURL); + + void DispatchKeyError(uint32_t system_code); + + void OnClosed(); + + bool IsClosed() const; + +private: + nsRefPtr<Promise> mClosed; + + nsRefPtr<MediaKeyError> mMediaKeyError; + nsRefPtr<MediaKeys> mKeys; + const nsString mKeySystem; + nsString mSessionId; + const SessionType mSessionType; + bool mIsClosed; +}; + +} // namespace dom +} // namespace mozilla + +#endif
new file mode 100644 --- /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<Promise> +MediaKeys::SetServerCertificate(const Uint8Array& aCert) +{ + aCert.ComputeLengthAndData(); + nsRefPtr<Promise> promise(MakePromise()); + mProxy->SetServerCertificate(StorePromise(promise), aCert); + return promise.forget(); +} + +/* static */ +IsTypeSupportedResult +MediaKeys::IsTypeSupported(const GlobalObject& aGlobal, + const nsAString& aKeySystem, + const Optional<nsAString>& aInitDataType, + const Optional<nsAString>& aContentType, + const Optional<nsAString>& 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<Promise> +MediaKeys::MakePromise() +{ + nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject()); + if (!global) { + NS_WARNING("Passed non-global to MediaKeys ctor!"); + return nullptr; + } + nsRefPtr<Promise> 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<Promise> +MediaKeys::RetrievePromise(PromiseId aId) +{ + nsRefPtr<Promise> promise; + mPromises.Remove(aId, getter_AddRefs(promise)); + return promise.forget(); +} + +void +MediaKeys::RejectPromise(PromiseId aId, nsresult aExceptionCode) +{ + nsRefPtr<Promise> 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> 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<Promise> +MediaKeys::Create(const GlobalObject& aGlobal, + const nsAString& aKeySystem, + ErrorResult& aRv) +{ + // CDMProxy keeps MediaKeys alive until it resolves the promise and thus + // returns the MediaKeys object to JS. + nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports()); + if (!window) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsRefPtr<MediaKeys> keys = new MediaKeys(window, aKeySystem); + nsRefPtr<Promise> 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> promise(RetrievePromise(aId)); + if (!promise) { + NS_WARNING("MediaKeys tried to resolve a non-existent promise"); + return; + } + nsRefPtr<MediaKeys> keys(this); + promise->MaybeResolve(keys); +} + +already_AddRefed<Promise> +MediaKeys::LoadSession(const nsAString& aSessionId) +{ + nsRefPtr<Promise> 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<MediaKeySession> 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<Promise> +MediaKeys::CreateSession(const nsAString& initDataType, + const Uint8Array& aInitData, + SessionType aSessionType) +{ + aInitData.ComputeLengthAndData(); + nsRefPtr<Promise> promise(MakePromise()); + nsRefPtr<MediaKeySession> 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> promise(RetrievePromise(aId)); + if (!promise) { + NS_WARNING("MediaKeys tried to resolve a non-existent promise"); + return; + } + MOZ_ASSERT(mPendingSessions.Contains(aId)); + + nsRefPtr<MediaKeySession> 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<MediaKeySession> +MediaKeys::GetSession(const nsAString& aSessionId) +{ + nsRefPtr<MediaKeySession> session; + mKeySessions.Get(aSessionId, getter_AddRefs(session)); + return session.forget(); +} + +} // namespace dom +} // namespace mozilla
new file mode 100644 --- /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<nsStringHashKey, MediaKeySession> KeySessionHashMap; +typedef nsRefPtrHashtable<nsUint32HashKey, dom::Promise> PromiseHashMap; +typedef nsRefPtrHashtable<nsUint32HashKey, MediaKeySession> 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<Promise> CreateSession(const nsAString& aInitDataType, + const Uint8Array& aInitData, + SessionType aSessionType); + + // JavaScript: MediaKeys.loadSession() + already_AddRefed<Promise> LoadSession(const nsAString& aSessionId); + + // JavaScript: MediaKeys.SetServerCertificate() + already_AddRefed<Promise> SetServerCertificate(const Uint8Array& aServerCertificate); + + // JavaScript: MediaKeys.create() + static + already_AddRefed<Promise> Create(const GlobalObject& aGlobal, + const nsAString& aKeySystem, + ErrorResult& aRv); + + // JavaScript: MediaKeys.IsTypeSupported() + static IsTypeSupportedResult IsTypeSupported(const GlobalObject& aGlobal, + const nsAString& aKeySystem, + const Optional<nsAString>& aInitDataType, + const Optional<nsAString>& aContentType, + const Optional<nsAString>& aCapability); + + already_AddRefed<MediaKeySession> GetSession(const nsAString& aSessionId); + + // Called once a Create() operation succeeds. + void OnCDMCreated(PromiseId aId); + // 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<Promise> 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<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; + + nsCOMPtr<nsPIDOMWindow> mParent; + nsString mKeySystem; + KeySessionHashMap mKeySessions; + PromiseHashMap mPromises; + PendingKeySessionsHashMap mPendingSessions; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_mediakeys_h__
new file mode 100644 --- /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
--- a/content/media/moz.build +++ b/content/media/moz.build @@ -45,16 +45,18 @@ if CONFIG['MOZ_APPLEMEDIA']: PARALLEL_DIRS += ['webrtc'] if CONFIG['MOZ_OMX_DECODER']: PARALLEL_DIRS += ['omx'] PARALLEL_DIRS += ['omx/mediaresourcemanager'] PARALLEL_DIRS += ['webspeech'] +PARALLEL_DIRS += ['eme'] + TEST_DIRS += [ 'test', 'gtest', ] EXPORTS += [ 'AbstractMediaDecoder.h', 'AudioChannelFormat.h',
--- a/dom/events/EventNameList.h +++ b/dom/events/EventNameList.h @@ -288,17 +288,16 @@ EVENT(mozfullscreenerror, EVENT(mozpointerlockchange, NS_POINTERLOCKCHANGE, EventNameType_HTML, NS_EVENT) EVENT(mozpointerlockerror, NS_POINTERLOCKERROR, EventNameType_HTML, NS_EVENT) - EVENT(pointerdown, NS_POINTER_DOWN, EventNameType_All, NS_POINTER_EVENT) EVENT(pointermove, NS_POINTER_MOVE, EventNameType_All, NS_POINTER_EVENT)
--- a/dom/webidl/HTMLMediaElement.webidl +++ b/dom/webidl/HTMLMediaElement.webidl @@ -125,8 +125,30 @@ partial interface HTMLMediaElement { [SetterThrows] attribute AudioChannel mozAudioChannelType; // In addition the media element has this new events: // * onmozinterruptbegin - called when the media element is interrupted // 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<any> + [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; +};
--- a/dom/webidl/HTMLSourceElement.webidl +++ b/dom/webidl/HTMLSourceElement.webidl @@ -14,8 +14,14 @@ interface HTMLSourceElement : HTMLElement { [SetterThrows] attribute DOMString src; [SetterThrows] attribute DOMString type; [SetterThrows] attribute DOMString media; }; + +// Encrypted Media Extensions +partial interface HTMLSourceElement { + [Pref="media.eme.enabled"] + attribute DOMString keySystem; +};
new file mode 100644 --- /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; +};
new file mode 100644 --- /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 = ""; +};
new file mode 100644 --- /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; +};
new file mode 100644 --- /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<Uint8Array> usableKeyIds; + + readonly attribute unrestricted double expiration; + + // Promise<any> + readonly attribute Promise closed; + + // session operations + //Promise<any> + [NewObject] + Promise update(Uint8Array response); + + // Promise<any> + [NewObject] + Promise close(); + + // Promise<any> + [NewObject] + Promise remove(); +};
new file mode 100644 --- /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<MediaKeySession> + [NewObject] + Promise createSession(DOMString initDataType, Uint8Array initData, optional SessionType sessionType = "temporary"); + + // Promise<MediaKeySession> + [NewObject] + Promise loadSession(DOMString sessionId); + + // Promise<any> + [NewObject] + Promise setServerCertificate(Uint8Array serverCertificate); + + // Promise<MediaKeys> + [Throws,NewObject] + static Promise create(DOMString keySystem); + static IsTypeSupportedResult isTypeSupported(DOMString keySystem, optional DOMString initDataType, optional DOMString contentType, optional DOMString capability); + +};
--- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -226,16 +226,21 @@ WEBIDL_FILES = [ 'KeyEvent.webidl', 'LegacyQueryInterface.webidl', 'LinkStyle.webidl', 'LocalMediaStream.webidl', 'Location.webidl', 'LockedFile.webidl', 'MediaElementAudioSourceNode.webidl', 'MediaError.webidl', + 'MediaKeyError.webidl', + 'MediaKeyMessageEvent.webidl', + 'MediaKeyNeededEvent.webidl', + 'MediaKeys.webidl', + 'MediaKeySession.webidl', 'MediaList.webidl', 'MediaQueryList.webidl', 'MediaRecorder.webidl', 'MediaSource.webidl', 'MediaStream.webidl', 'MediaStreamAudioDestinationNode.webidl', 'MediaStreamAudioSourceNode.webidl', 'MediaStreamTrack.webidl',
--- a/dom/workers/WorkerFeature.h +++ b/dom/workers/WorkerFeature.h @@ -23,16 +23,22 @@ BEGIN_WORKERS_NAMESPACE * +-------------+-------------+-----------------+----------------+ * | Terminating | yes | yes | no timeout | * +-------------+-------------+-----------------+----------------+ * | Canceling | yes | yes | short duration | * +-------------+-------------+-----------------+----------------+ * | 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. Pending = 0, // This status means that the close handler has not yet been scheduled. Running,
--- a/parser/html/nsHtml5AtomList.h +++ b/parser/html/nsHtml5AtomList.h @@ -163,16 +163,17 @@ HTML5_ATOM(fence, "fence") HTML5_ATOM(frame, "frame") HTML5_ATOM(ismap, "ismap") HTML5_ATOM(onend, "onend") HTML5_ATOM(index, "index") 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") HTML5_ATOM(width, "width") HTML5_ATOM(vlink, "vlink") HTML5_ATOM(value, "value") HTML5_ATOM(slope, "slope") HTML5_ATOM(shape, "shape")
--- a/widget/BasicEvents.h +++ b/widget/BasicEvents.h @@ -317,16 +317,17 @@ enum nsEventStructType #define NS_CANPLAYTHROUGH (NS_MEDIA_EVENT_START+12) #define NS_SEEKING (NS_MEDIA_EVENT_START+13) #define NS_SEEKED (NS_MEDIA_EVENT_START+14) #define NS_TIMEUPDATE (NS_MEDIA_EVENT_START+15) #define NS_ENDED (NS_MEDIA_EVENT_START+16) #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 #define NS_AFTERPAINT (NS_NOTIFYPAINT_START) // Simple gesture events #define NS_SIMPLE_GESTURE_EVENT_START 3500 #define NS_SIMPLE_GESTURE_SWIPE_START (NS_SIMPLE_GESTURE_EVENT_START)