Bug 1016162 - Base EME DOM APIs, not connected to a CDM yet. r=bz

This commit is contained in:
Chris Pearce 2014-06-07 08:52:15 +12:00
parent a0f8826cb9
commit 3c3777d2de
33 changed files with 1911 additions and 4 deletions

View File

@ -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);

View File

@ -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")

View File

@ -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<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;
@ -1010,6 +1037,9 @@ protected:
// 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;
@ -1139,6 +1169,8 @@ protected:
nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent;
nsRefPtr<TextTrackManager> mTextTrackManager;
MediaWaitingFor mWaitingFor;
};
} // namespace dom

View File

@ -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<nsINodeInfo>& 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<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();

View File

@ -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;

View File

@ -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

View File

@ -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_

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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__

View File

@ -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

View File

@ -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__

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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__

View File

@ -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

View File

@ -50,6 +50,8 @@ if CONFIG['MOZ_OMX_DECODER']:
PARALLEL_DIRS += ['webspeech']
PARALLEL_DIRS += ['eme']
TEST_DIRS += [
'test',
'gtest',

View File

@ -293,7 +293,6 @@ EVENT(mozpointerlockerror,
NS_POINTERLOCKERROR,
EventNameType_HTML,
NS_EVENT)
EVENT(pointerdown,
NS_POINTER_DOWN,
EventNameType_All,

View File

@ -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<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;
};

View File

@ -19,3 +19,9 @@ interface HTMLSourceElement : HTMLElement {
[SetterThrows]
attribute DOMString media;
};
// Encrypted Media Extensions
partial interface HTMLSourceElement {
[Pref="media.eme.enabled"]
attribute DOMString keySystem;
};

View File

@ -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;
};

View File

@ -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 = "";
};

View File

@ -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;
};

View File

@ -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();
};

View File

@ -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);
};

View File

@ -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',

View File

@ -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.

View File

@ -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")

View File

@ -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