Bug 1095257 - Implement Navigator.requestMediaKeySystemAccess(). r=edwin r=bz r=peterv

This commit is contained in:
Chris Pearce 2014-11-18 22:13:02 +13:00
parent 0b1a324eb4
commit fc70ad371b
19 changed files with 732 additions and 196 deletions

View File

@ -2533,5 +2533,43 @@ Navigator::GetUserAgent(nsPIDOMWindow* aWindow, nsIURI* aURI,
return siteSpecificUA->GetUserAgentForURIAndWindow(aURI, aWindow, aUserAgent);
}
#ifdef MOZ_EME
already_AddRefed<Promise>
Navigator::RequestMediaKeySystemAccess(const nsAString& aKeySystem,
const Optional<Sequence<MediaKeySystemOptions>>& aOptions,
ErrorResult& aRv)
{
nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
nsRefPtr<Promise> p = Promise::Create(go, aRv);
if (aRv.Failed()) {
return nullptr;
}
if (aKeySystem.IsEmpty() ||
(aOptions.WasPassed() && aOptions.Value().IsEmpty())) {
p->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
return p.forget();
}
if (!MediaKeySystemAccess::IsKeySystemSupported(aKeySystem)) {
p->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return p.forget();
}
// TODO: Wait (async) until the CDM is downloaded, if it's not already.
if (!aOptions.WasPassed() ||
MediaKeySystemAccess::IsSupported(aKeySystem, aOptions.Value())) {
nsRefPtr<MediaKeySystemAccess> access(new MediaKeySystemAccess(mWindow, aKeySystem));
p->MaybeResolve(access);
return p.forget();
}
p->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return p.forget();
}
#endif
} // namespace dom
} // namespace mozilla

View File

@ -18,6 +18,9 @@
#include "nsInterfaceHashtable.h"
#include "nsString.h"
#include "nsTArray.h"
#ifdef MOZ_EME
#include "mozilla/dom/MediaKeySystemAccess.h"
#endif
class nsPluginArray;
class nsMimeTypeArray;
@ -316,6 +319,13 @@ public:
virtual JSObject* WrapObject(JSContext* cx) MOZ_OVERRIDE;
#ifdef MOZ_EME
already_AddRefed<Promise>
RequestMediaKeySystemAccess(const nsAString& aKeySystem,
const Optional<Sequence<MediaKeySystemOptions>>& aOptions,
ErrorResult& aRv);
#endif
private:
virtual ~Navigator();

View File

@ -335,8 +335,9 @@ IsMP4SupportedType(const nsACString& aType,
#ifdef MOZ_OMX_DECODER
return false;
#else
bool haveAAC, haveMP3, haveH264;
return Preferences::GetBool("media.fragmented-mp4.exposed", false) &&
MP4Decoder::CanHandleMediaType(aType, aCodecs);
MP4Decoder::CanHandleMediaType(aType, aCodecs, haveAAC, haveH264, haveMP3);
#endif
}
#endif

View File

@ -0,0 +1,212 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/MediaKeySystemAccess.h"
#include "mozilla/dom/MediaKeySystemAccessBinding.h"
#include "mozilla/Preferences.h"
#include "nsContentTypeParser.h"
#ifdef MOZ_FMP4
#include "MP4Decoder.h"
#endif
#ifdef XP_WIN
#include "mozilla/WindowsVersion.h"
#endif
#include "nsContentCID.h"
#include "nsServiceManagerUtils.h"
#include "mozIGeckoMediaPluginService.h"
#include "VideoUtils.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MediaKeySystemAccess,
mParent)
NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaKeySystemAccess)
NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaKeySystemAccess)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaKeySystemAccess)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
MediaKeySystemAccess::MediaKeySystemAccess(nsPIDOMWindow* aParent,
const nsAString& aKeySystem)
: mParent(aParent)
, mKeySystem(aKeySystem)
{
}
MediaKeySystemAccess::~MediaKeySystemAccess()
{
}
JSObject*
MediaKeySystemAccess::WrapObject(JSContext* aCx)
{
return MediaKeySystemAccessBinding::Wrap(aCx, this);
}
nsPIDOMWindow*
MediaKeySystemAccess::GetParentObject() const
{
return mParent;
}
void
MediaKeySystemAccess::GetKeySystem(nsString& aRetVal) const
{
aRetVal = mKeySystem;
}
already_AddRefed<Promise>
MediaKeySystemAccess::CreateMediaKeys(ErrorResult& aRv)
{
nsRefPtr<MediaKeys> keys(new MediaKeys(mParent, mKeySystem));
return keys->Init(aRv);
}
static bool
HaveGMPFor(mozIGeckoMediaPluginService* aGMPService,
const nsCString& aKeySystem,
const nsCString& aAPI,
const nsCString& aTag = EmptyCString())
{
nsTArray<nsCString> tags;
tags.AppendElement(aKeySystem);
if (!aTag.IsEmpty()) {
tags.AppendElement(aTag);
}
// Note: EME plugins need a non-null nodeId here, as they must
// not be shared across origins.
bool hasPlugin = false;
if (NS_FAILED(aGMPService->HasPluginForAPI(aAPI,
&tags,
&hasPlugin))) {
return false;
}
return hasPlugin;
}
/* static */
bool
MediaKeySystemAccess::IsKeySystemSupported(const nsAString& aKeySystem)
{
nsCOMPtr<mozIGeckoMediaPluginService> mps =
do_GetService("@mozilla.org/gecko-media-plugin-service;1");
if (NS_WARN_IF(!mps)) {
return false;
}
if (aKeySystem.EqualsLiteral("org.w3.clearkey") &&
HaveGMPFor(mps,
NS_LITERAL_CSTRING("org.w3.clearkey"),
NS_LITERAL_CSTRING("eme-decrypt"))) {
return true;
}
#ifdef XP_WIN
if (aKeySystem.EqualsLiteral("com.adobe.access") &&
Preferences::GetBool("media.eme.adobe-access.enabled", false) &&
IsVistaOrLater() && // Win Vista and later only.
HaveGMPFor(mps,
NS_LITERAL_CSTRING("com.adobe.access"),
NS_LITERAL_CSTRING("eme-decrypt"))) {
return true;
}
#endif
return false;
}
static bool
IsPlayableWithGMP(mozIGeckoMediaPluginService* aGMPS,
const nsAString& aKeySystem,
const nsAString& aContentType)
{
#ifdef MOZ_FMP4
nsContentTypeParser parser(aContentType);
nsAutoString mimeType;
nsresult rv = parser.GetType(mimeType);
if (NS_FAILED(rv)) {
return false;
}
if (!mimeType.EqualsLiteral("audio/mp4") &&
!mimeType.EqualsLiteral("audio/x-m4a") &&
!mimeType.EqualsLiteral("video/mp4")) {
return false;
}
nsAutoString codecs;
parser.GetParameter("codecs", codecs);
NS_ConvertUTF16toUTF8 mimeTypeUTF8(mimeType);
bool hasAAC = false;
bool hasH264 = false;
bool hasMP3 = false;
if (!MP4Decoder::CanHandleMediaType(mimeTypeUTF8,
codecs,
hasAAC,
hasH264,
hasMP3) ||
hasMP3) {
return false;
}
return (!hasAAC || !HaveGMPFor(aGMPS,
NS_ConvertUTF16toUTF8(aKeySystem),
NS_LITERAL_CSTRING("eme-decrypt"),
NS_LITERAL_CSTRING("aac"))) &&
(!hasH264 || !HaveGMPFor(aGMPS,
NS_ConvertUTF16toUTF8(aKeySystem),
NS_LITERAL_CSTRING("eme-decrypt"),
NS_LITERAL_CSTRING("h264")));
#else
return false;
#endif
}
/* static */
bool
MediaKeySystemAccess::IsSupported(const nsAString& aKeySystem,
const Sequence<MediaKeySystemOptions>& aOptions)
{
nsCOMPtr<mozIGeckoMediaPluginService> mps =
do_GetService("@mozilla.org/gecko-media-plugin-service;1");
if (NS_WARN_IF(!mps)) {
return false;
}
for (size_t i = 0; i < aOptions.Length(); i++) {
const MediaKeySystemOptions& options = aOptions[i];
if (!options.mInitDataType.EqualsLiteral("cenc")) {
continue;
}
if (!options.mAudioCapability.IsEmpty() ||
!options.mVideoCapability.IsEmpty()) {
// Don't support any capabilites until we know we have a CDM with
// capabilities...
continue;
}
if (!options.mAudioType.IsEmpty() &&
!IsPlayableWithGMP(mps, aKeySystem, options.mAudioType)) {
continue;
}
if (!options.mVideoType.IsEmpty() &&
!IsPlayableWithGMP(mps, aKeySystem, options.mVideoType)) {
continue;
}
// Our sandbox provides an origin specific unique identifier, and the
// ability to persist data. We don't yet have a way to turn those off
// and on for specific GMPs/CDMs, so we don't check the uniqueidentifier
// and stateful attributes here.
return true;
}
return false;
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,58 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_MediaKeySystemAccess_h
#define mozilla_dom_MediaKeySystemAccess_h
#include "mozilla/Attributes.h"
#include "mozilla/ErrorResult.h"
#include "nsCycleCollectionParticipant.h"
#include "nsWrapperCache.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/MediaKeySystemAccessBinding.h"
#include "js/TypeDecls.h"
namespace mozilla {
namespace dom {
class MediaKeySystemAccess MOZ_FINAL : public nsISupports,
public nsWrapperCache
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MediaKeySystemAccess)
public:
explicit MediaKeySystemAccess(nsPIDOMWindow* aParent,
const nsAString& aKeySystem);
protected:
~MediaKeySystemAccess();
public:
nsPIDOMWindow* GetParentObject() const;
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
void GetKeySystem(nsString& aRetVal) const;
already_AddRefed<Promise> CreateMediaKeys(ErrorResult& aRv);
static bool IsKeySystemSupported(const nsAString& aKeySystem);
static bool IsSupported(const nsAString& aKeySystem,
const Sequence<MediaKeySystemOptions>& aOptions);
private:
nsCOMPtr<nsPIDOMWindow> mParent;
const nsString mKeySystem;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_MediaKeySystemAccess_h

View File

@ -114,111 +114,6 @@ MediaKeys::SetServerCertificate(const ArrayBufferViewOrArrayBuffer& aCert, Error
return promise.forget();
}
static bool
HaveGMPFor(const nsCString& aKeySystem,
const nsCString& aAPI,
const nsCString& aTag = EmptyCString())
{
nsCOMPtr<mozIGeckoMediaPluginService> mps =
do_GetService("@mozilla.org/gecko-media-plugin-service;1");
if (NS_WARN_IF(!mps)) {
return false;
}
nsTArray<nsCString> tags;
tags.AppendElement(aKeySystem);
if (!aTag.IsEmpty()) {
tags.AppendElement(aTag);
}
// Note: EME plugins need a non-null nodeId here, as they must
// not be shared across origins.
bool hasPlugin = false;
if (NS_FAILED(mps->HasPluginForAPI(aAPI,
&tags,
&hasPlugin))) {
return false;
}
return hasPlugin;
}
static bool
IsPlayableMP4Type(const nsAString& aContentType)
{
#ifdef MOZ_FMP4
nsContentTypeParser parser(aContentType);
nsAutoString mimeType;
nsresult rv = parser.GetType(mimeType);
if (NS_FAILED(rv)) {
return false;
}
nsAutoString codecs;
parser.GetParameter("codecs", codecs);
NS_ConvertUTF16toUTF8 mimeTypeUTF8(mimeType);
return MP4Decoder::CanHandleMediaType(mimeTypeUTF8, codecs);
#else
return false;
#endif
}
bool
MediaKeys::IsTypeSupported(const nsAString& aKeySystem,
const Optional<nsAString>& aInitDataType,
const Optional<nsAString>& aContentType)
{
if (aKeySystem.EqualsLiteral("org.w3.clearkey") &&
(!aInitDataType.WasPassed() || aInitDataType.Value().EqualsLiteral("cenc")) &&
(!aContentType.WasPassed() || IsPlayableMP4Type(aContentType.Value())) &&
HaveGMPFor(NS_LITERAL_CSTRING("org.w3.clearkey"),
NS_LITERAL_CSTRING("eme-decrypt"))) {
return true;
}
#ifdef XP_WIN
// Note: Adobe Access's GMP uses WMF to decode, so anything our MP4Reader
// thinks it can play on Windows, the Access GMP should be able to play.
if (aKeySystem.EqualsLiteral("com.adobe.access") &&
Preferences::GetBool("media.eme.adobe-access.enabled", false) &&
IsVistaOrLater() && // Win Vista and later only.
(!aInitDataType.WasPassed() || aInitDataType.Value().EqualsLiteral("cenc")) &&
(!aContentType.WasPassed() || IsPlayableMP4Type(aContentType.Value())) &&
HaveGMPFor(NS_LITERAL_CSTRING("com.adobe.access"),
NS_LITERAL_CSTRING("eme-decrypt")) &&
HaveGMPFor(NS_LITERAL_CSTRING("com.adobe.access"),
NS_LITERAL_CSTRING("decode-video"),
NS_LITERAL_CSTRING("h264")) &&
HaveGMPFor(NS_LITERAL_CSTRING("com.adobe.access"),
NS_LITERAL_CSTRING("decode-audio"),
NS_LITERAL_CSTRING("aac"))) {
return true;
}
#endif
return false;
}
/* static */
IsTypeSupportedResult
MediaKeys::IsTypeSupported(const GlobalObject& aGlobal,
const nsAString& aKeySystem,
const Optional<nsAString>& aInitDataType,
const Optional<nsAString>& aContentType,
const Optional<nsAString>& aCapability)
{
// TODO: Should really get spec changed to this is async, so we can wait
// for user to consent to running plugin.
bool supported = IsTypeSupported(aKeySystem, aInitDataType, aContentType);
EME_LOG("MediaKeys::IsTypeSupported keySystem='%s' initDataType='%s' contentType='%s' supported=%d",
NS_ConvertUTF16toUTF8(aKeySystem).get(),
(aInitDataType.WasPassed() ? NS_ConvertUTF16toUTF8(aInitDataType.Value()).get() : ""),
(aContentType.WasPassed() ? NS_ConvertUTF16toUTF8(aContentType.Value()).get() : ""),
supported);
return supported ? IsTypeSupportedResult::Probably
: IsTypeSupportedResult::_empty;
}
already_AddRefed<Promise>
MediaKeys::MakePromise(ErrorResult& aRv)
{
@ -303,24 +198,6 @@ MediaKeys::ResolvePromise(PromiseId aId)
}
}
/* 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 || !window->GetExtantDoc()) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsRefPtr<MediaKeys> keys = new MediaKeys(window, aKeySystem);
return keys->Init(aRv);
}
already_AddRefed<Promise>
MediaKeys::Init(ErrorResult& aRv)
{
@ -329,11 +206,6 @@ MediaKeys::Init(ErrorResult& aRv)
return nullptr;
}
if (!IsTypeSupported(mKeySystem)) {
promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return promise.forget();
}
mProxy = new CDMProxy(this, mKeySystem);
// Determine principal (at creation time) of the MediaKeys object.

View File

@ -53,6 +53,8 @@ public:
MediaKeys(nsPIDOMWindow* aParentWindow, const nsAString& aKeySystem);
already_AddRefed<Promise> Init(ErrorResult& aRv);
nsPIDOMWindow* GetParentObject() const;
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
@ -70,19 +72,6 @@ public:
already_AddRefed<Promise> SetServerCertificate(const ArrayBufferViewOrArrayBuffer& aServerCertificate,
ErrorResult& aRv);
// 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.
@ -128,12 +117,7 @@ public:
private:
static bool IsTypeSupported(const nsAString& aKeySystem,
const Optional<nsAString>& aInitDataType = Optional<nsAString>(),
const Optional<nsAString>& aContentType = Optional<nsAString>());
bool IsInPrivateBrowsing();
already_AddRefed<Promise> Init(ErrorResult& aRv);
// Removes promise from mPromises, and returns it.
already_AddRefed<Promise> RetrievePromise(PromiseId aId);

View File

@ -10,6 +10,7 @@ EXPORTS.mozilla.dom += [
'MediaKeyMessageEvent.h',
'MediaKeys.h',
'MediaKeySession.h',
'MediaKeySystemAccess.h',
]
EXPORTS.mozilla += [
@ -29,6 +30,7 @@ UNIFIED_SOURCES += [
'MediaKeyMessageEvent.cpp',
'MediaKeys.cpp',
'MediaKeySession.cpp',
'MediaKeySystemAccess.cpp',
]
FINAL_LIBRARY = 'xul'

View File

@ -54,14 +54,25 @@ MP4Decoder::SetCDMProxy(CDMProxy* aProxy)
#endif
static bool
IsSupportedAudioCodec(const nsAString& aCodec)
IsSupportedAudioCodec(const nsAString& aCodec,
bool& aOutContainsAAC,
bool& aOutContainsMP3)
{
// AAC-LC, HE-AAC or MP3 in M4A.
return aCodec.EqualsASCII("mp4a.40.2") ||
// AAC-LC or HE-AAC in M4A.
aOutContainsAAC = aCodec.EqualsASCII("mp4a.40.2") ||
aCodec.EqualsASCII("mp4a.40.5");
if (aOutContainsAAC) {
return true;
}
#ifndef MOZ_GONK_MEDIACODEC // B2G doesn't support MP3 in MP4 yet.
aCodec.EqualsASCII("mp3") ||
aOutContainsMP3 = aCodec.EqualsASCII("mp3");
if (aOutContainsMP3) {
return true;
}
#else
aOutContainsMP3 = false;
#endif
aCodec.EqualsASCII("mp4a.40.5");
return false;
}
static bool
@ -92,14 +103,20 @@ IsSupportedH264Codec(const nsAString& aCodec)
/* static */
bool
MP4Decoder::CanHandleMediaType(const nsACString& aType,
const nsAString& aCodecs)
const nsAString& aCodecs,
bool& aOutContainsAAC,
bool& aOutContainsH264,
bool& aOutContainsMP3)
{
if (!IsEnabled()) {
return false;
}
if (aType.EqualsASCII("audio/mp4") || aType.EqualsASCII("audio/x-m4a")) {
return aCodecs.IsEmpty() || IsSupportedAudioCodec(aCodecs);
return aCodecs.IsEmpty() ||
IsSupportedAudioCodec(aCodecs,
aOutContainsAAC,
aOutContainsMP3);
}
if (!aType.EqualsASCII("video/mp4")) {
@ -113,7 +130,13 @@ MP4Decoder::CanHandleMediaType(const nsACString& aType,
while (tokenizer.hasMoreTokens()) {
const nsSubstring& token = tokenizer.nextToken();
expectMoreTokens = tokenizer.separatorAfterCurrentToken();
if (IsSupportedAudioCodec(token) || IsSupportedH264Codec(token)) {
if (IsSupportedAudioCodec(token,
aOutContainsAAC,
aOutContainsMP3)) {
continue;
}
if (IsSupportedH264Codec(token)) {
aOutContainsH264 = true;
continue;
}
return false;
@ -122,8 +145,8 @@ MP4Decoder::CanHandleMediaType(const nsACString& aType,
// Last codec name was empty
return false;
}
return true;
return true;
}
static bool

View File

@ -30,9 +30,13 @@ public:
// Returns true if aMIMEType is a type that we think we can render with the
// a MP4 platform decoder backend. If aCodecs is non emtpy, it is filled
// with a comma-delimited list of codecs to check support for.
// with a comma-delimited list of codecs to check support for. Notes in
// out params wether the codecs string contains AAC or H.264.
static bool CanHandleMediaType(const nsACString& aMIMEType,
const nsAString& aCodecs = EmptyString());
const nsAString& aCodecs,
bool& aOutContainsAAC,
bool& aOutContainsH264,
bool& aOutContainsMP3);
// Returns true if the MP4 backend is preffed on, and we're running on a
// platform that is likely to have decoders for the contained formats.

View File

@ -192,23 +192,37 @@ function SetupEME(test, token, params)
v.addEventListener("encrypted", function(ev) {
Log(token, "got encrypted event");
MediaKeys.create(KEYSYSTEM_TYPE).then(function(mediaKeys) {
Log(token, "created MediaKeys object ok");
mediaKeys.sessions = [];
return v.setMediaKeys(mediaKeys);
}, bail("failed to create MediaKeys object")).then(function() {
Log(token, "set MediaKeys on <video> element ok");
var session = v.mediaKeys.createSession(test.sessionType);
if (params && params.onsessioncreated) {
params.onsessioncreated(session);
var options = [
{
initDataType: ev.initDataType,
videoType: test.type,
}
session.addEventListener("message", UpdateSessionFunc(test, token));
session.generateRequest(ev.initDataType, ev.initData).then(function() {
}, bail(token + " Failed to initialise MediaKeySession"));
];
navigator.requestMediaKeySystemAccess(KEYSYSTEM_TYPE, options)
.then(function(keySystemAccess) {
return keySystemAccess.createMediaKeys();
}, bail(token + " Failed to request key system access."))
.then(function(mediaKeys) {
Log(token, "created MediaKeys object ok");
mediaKeys.sessions = [];
return v.setMediaKeys(mediaKeys);
}, bail("failed to create MediaKeys object"))
.then(function() {
Log(token, "set MediaKeys on <video> element ok");
}, onSetKeysFail);
var session = v.mediaKeys.createSession(test.sessionType);
if (params && params.onsessioncreated) {
params.onsessioncreated(session);
}
session.addEventListener("message", UpdateSessionFunc(test, token));
return session.generateRequest(ev.initDataType, ev.initData);
}, onSetKeysFail)
.then(function() {
Log(token, "generated request");
}, bail(token + " Failed to request key system access2."));
});
return v;
}

View File

@ -364,6 +364,8 @@ skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
[test_eme_playback.html]
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
[test_eme_requestKeySystemAccess.html]
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
[test_eme_stream_capture_blocked.html]
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
[test_empty_resource.html]

View File

@ -51,8 +51,6 @@ function startTest(test, token)
var gotPlaying = false;
v.addEventListener("encrypted", function(ev) {
ok(MediaKeys.isTypeSupported(KEYSYSTEM_TYPE, ev.initDataType, test.type),
TimeStamp(token) + " MediaKeys should support this keysystem");
gotEncrypted = true;
});
@ -88,23 +86,7 @@ function startTest(test, token)
LoadTest(test, v, token).then(function(){v.play();}, bail(token + " failed to load"));
}
function testIsTypeSupported()
{
var t = MediaKeys.isTypeSupported;
const clearkey = "org.w3.clearkey";
ok(!t("bogus", "bogon", "video/bogus"), "Invalid type.");
ok(t(clearkey), "ClearKey supported.");
ok(!t(clearkey, "bogus"), "ClearKey bogus initDataType not supported.");
ok(t(clearkey, "cenc"), "ClearKey/cenc should be supported.");
ok(!t(clearkey, "cenc", "bogus"), "ClearKey/cenc bogus content type should be supported.");
ok(t(clearkey, "cenc", 'video/mp4'), "ClearKey/cenc video/mp4 supported.");
ok(t(clearkey, "cenc", 'video/mp4; codecs="avc1.4d4015,mp4a.40.2"'), "ClearKey/cenc H.264/AAC supported.");
ok(t(clearkey, "cenc", 'audio/mp4'), "ClearKey/cenc audio/mp4 supported.");
ok(t(clearkey, "cenc", 'audio/mp4; codecs="mp4a.40.2"'), "ClearKey/cenc AAC LC supported.");
}
function beginTest() {
testIsTypeSupported();
manager.runTests(gEMETests, startTest);
}

View File

@ -0,0 +1,293 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test Encrypted Media Extensions</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="text/javascript" src="manifest.js"></script>
<script type="text/javascript" src="eme.js"></script>
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
function Test(test) {
var name = "'" + test.name + "'";
return new Promise(function(resolve, reject) {
var p;
if (test.options) {
p = navigator.requestMediaKeySystemAccess(test.keySystem, test.options);
} else {
p = navigator.requestMediaKeySystemAccess(test.keySystem);
}
p.then(
function(keySystemAccess) {
ok(test.shouldPass, name + " passed and was expected to " + (test.shouldPass ? "pass" : "fail"));
resolve();
},
function(ex) {
if (test.shouldPass) {
info(name + " failed: " + ex);
}
ok(!test.shouldPass, name + " failed and was expected to " + (test.shouldPass ? "pass" : "fail"));
resolve();
});
});
}
const CLEARKEY_ID = 'org.w3.clearkey';
var tests = [
{
name: 'Empty keySystem string',
keySystem: '',
options: [ ],
shouldPass: false,
},
{
name: 'Empty options specified',
keySystem: CLEARKEY_ID,
options: [ ],
shouldPass: false,
},
{
name: 'Undefined options',
keySystem: CLEARKEY_ID,
shouldPass: true,
},
{
name: 'Basic MP4 cenc',
keySystem: CLEARKEY_ID,
options: [
{
initDataType: 'cenc',
videoType: 'video/mp4',
}
],
shouldPass: true,
},
{
name: 'Invalid keysystem failure',
keySystem: 'bogusKeySystem',
options: [],
shouldPass: false,
},
{
name: 'Invalid initDataType',
keySystem: CLEARKEY_ID,
options: [
{
initDataType: 'bogus',
}
],
shouldPass: false,
},
{
name: 'Invalid videoType',
keySystem: CLEARKEY_ID,
options: [
{
initDataType: 'cenc',
videoType: 'video/bogus',
}
],
shouldPass: false,
},
{
name: 'Invalid statefulness',
keySystem: CLEARKEY_ID,
options: [
{
initDataType: 'cenc',
videoType: 'video/mp4',
stateful: 'bogus',
}
],
shouldPass: false,
},
{
name: 'Invalid uniqueidentifier',
keySystem: CLEARKEY_ID,
options: [
{
initDataType: 'cenc',
videoType: 'video/mp4',
uniqueidentifier: 'bogus',
}
],
shouldPass: false,
},
{
name: 'Audio capabilities not supported by CLEARKEY_ID',
keySystem: CLEARKEY_ID,
options: [
{
initDataType: 'cenc',
videoType: 'video/mp4',
audioCapability: 'bogus',
}
],
shouldPass: false,
},
{
name: 'Video capabilities not supported by CLEARKEY_ID',
keySystem: CLEARKEY_ID,
options: [
{
initDataType: 'cenc',
videoType: 'video/mp4',
videoCapability: 'bogus',
}
],
shouldPass: false,
},
{
name: 'Invalid option followed by valid',
keySystem: CLEARKEY_ID,
options: [
{
bogus: 'bogon',
},
{
initDataType: 'cenc',
videoType: 'video/mp4',
}
],
shouldPass: true,
},
{
name: 'MP4 audio container',
keySystem: CLEARKEY_ID,
options: [
{
initDataType: 'cenc',
audioType: 'audio/mp4',
}
],
shouldPass: true,
},
{
name: 'MP4 audio container with AAC-LC',
keySystem: CLEARKEY_ID,
options: [
{
initDataType: 'cenc',
audioType: 'audio/mp4; codecs="mp4a.40.2"',
}
],
shouldPass: true,
},
{
name: 'MP4 audio container with invalid codecs',
keySystem: CLEARKEY_ID,
options: [
{
initDataType: 'cenc',
audioType: 'audio/mp4; codecs="bogus"',
}
],
shouldPass: false,
},
{
name: 'MP4 audio container with mp3 is unsupported',
keySystem: CLEARKEY_ID,
options: [
{
initDataType: 'cenc',
audioType: 'audio/mp4; codecs="mp3"',
}
],
shouldPass: false,
},
{
name: 'MP4 video container with mp3 and h264 is unsupported',
keySystem: CLEARKEY_ID,
options: [
{
initDataType: 'cenc',
videoType: 'video/mp4; codecs="avc1.42E01E,mp3"',
}
],
shouldPass: false,
},
{
name: 'MP4 video container with constrained baseline h.264',
keySystem: CLEARKEY_ID,
options: [
{
initDataType: 'cenc',
audioType: 'video/mp4; codecs="avc1.42E01E"',
}
],
shouldPass: true,
},
{
name: 'MP4 video container with invalid codecs',
keySystem: CLEARKEY_ID,
options: [
{
initDataType: 'cenc',
videoType: 'video/mp4; codecs="bogus"',
}
],
shouldPass: false,
},
{
name: 'MP4 video container with constrained baseline h.264 and AAC-LC',
keySystem: CLEARKEY_ID,
options: [
{
initDataType: 'cenc',
videoType: 'video/mp4; codecs="avc1.42E01E,mp4a.40.2"',
}
],
shouldPass: true,
},
{
name: 'MP4 audio and video type both specified',
keySystem: CLEARKEY_ID,
options: [
{
initDataType: 'cenc',
videoType: 'video/mp4; codecs="avc1.42E01E"',
audioType: 'audio/mp4; codecs="mp4a.40.2"',
}
],
shouldPass: true,
},
{
name: 'WebM CLEARKEY_ID not supported',
keySystem: CLEARKEY_ID,
options: [
{
initDataType: 'webm',
videoType: 'video/webm',
}
],
shouldPass: false,
},
];
function beginTest() {
Promise.all(tests.map(Test)).then(function() { SimpleTest.finish(); });
}
var prefs = [
[ "media.mediasource.enabled", true ],
[ "media.mediasource.mp4.enabled", true ],
];
if (/Linux/.test(navigator.userAgent) ||
!document.createElement('video').canPlayType("video/mp4")) {
// XXX remove once we have mp4 PlatformDecoderModules on all platforms.
prefs.push([ "media.fragmented-mp4.exposed", true ]);
prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]);
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
</script>
</pre>
</body>
</html>

View File

@ -661,6 +661,8 @@ var interfaceNamesInGlobalScope =
{name: "MediaKeys", pref: "media.eme.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MediaKeySession", pref: "media.eme.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MediaKeySystemAccess", pref: "media.eme.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MediaKeyMessageEvent", pref: "media.eme.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!

View File

@ -0,0 +1,34 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*
* The origin of this IDL file is
* https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html
*
* Copyright © 2014 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved.
* W3C liability, trademark and document use rules apply.
*/
enum MediaKeysRequirement {
"required",
"optional",
"disallowed"
};
dictionary MediaKeySystemOptions {
DOMString initDataType = "";
DOMString audioType = "";
DOMString audioCapability = "";
DOMString videoType = "";
DOMString videoCapability = "";
MediaKeysRequirement uniqueidentifier = "optional";
MediaKeysRequirement stateful = "optional";
};
[Pref="media.eme.enabled"]
interface MediaKeySystemAccess {
readonly attribute DOMString keySystem;
[NewObject, Throws]
Promise<MediaKeys> createMediaKeys();
};

View File

@ -22,9 +22,4 @@ interface MediaKeys {
[NewObject, Throws]
Promise<void> setServerCertificate((ArrayBufferView or ArrayBuffer) serverCertificate);
[Throws,NewObject]
static Promise<MediaKeys> create(DOMString keySystem);
static IsTypeSupportedResult isTypeSupported(DOMString keySystem, optional DOMString initDataType, optional DOMString contentType, optional DOMString capability);
};

View File

@ -397,3 +397,12 @@ partial interface Navigator {
[Pref="dom.tv.enabled", CheckPermissions="tv", Func="Navigator::HasTVSupport"]
readonly attribute TVManager? tv;
};
#ifdef MOZ_EME
partial interface Navigator {
[Pref="media.eme.enabled", Throws, NewObject]
Promise<MediaKeySystemAccess>
requestMediaKeySystemAccess(DOMString keySystem,
optional sequence<MediaKeySystemOptions> supportedConfigurations);
};
#endif

View File

@ -798,4 +798,5 @@ if CONFIG['MOZ_EME']:
'MediaKeyMessageEvent.webidl',
'MediaKeys.webidl',
'MediaKeySession.webidl',
'MediaKeySystemAccess.webidl',
]