Commit d2362c2c authored by John Rummell's avatar John Rummell Committed by Commit Bot

[EME] Add ability to query for encryption scheme support

Update navigator.requestMediaKeySystemAccess() to support querying for
encryption scheme support. This is based on
https://github.com/WICG/encrypted-media-encryption-scheme/blob/master/explainer.md.

BUG=657957
TEST=new layout and browser tests pass

Change-Id: I5ba39861bab673a61dd65c12fe82d6ba3bd63ddb
Reviewed-on: https://chromium-review.googlesource.com/959459Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Reviewed-by: default avatarNate Chapin <japhet@chromium.org>
Reviewed-by: default avatarXiaohan Wang <xhwang@chromium.org>
Commit-Queue: John Rummell <jrummell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#567105}
parent d6dd162b
...@@ -61,9 +61,11 @@ const char kSuccessResult[] = "success"; ...@@ -61,9 +61,11 @@ const char kSuccessResult[] = "success";
const char kUnsupportedResult[] = const char kUnsupportedResult[] =
"Unsupported keySystem or supportedConfigurations."; "Unsupported keySystem or supportedConfigurations.";
const char kUnexpectedResult[] = "unexpected result"; const char kUnexpectedResult[] = "unexpected result";
const char kTypeErrorResult[] = "TypeError";
#define EXPECT_SUCCESS(test) EXPECT_EQ(kSuccessResult, test) #define EXPECT_SUCCESS(test) EXPECT_EQ(kSuccessResult, test)
#define EXPECT_UNSUPPORTED(test) EXPECT_EQ(kUnsupportedResult, test) #define EXPECT_UNSUPPORTED(test) EXPECT_EQ(kUnsupportedResult, test)
#define EXPECT_TYPEERROR(test) EXPECT_EQ(kTypeErrorResult, test)
#if BUILDFLAG(USE_PROPRIETARY_CODECS) #if BUILDFLAG(USE_PROPRIETARY_CODECS)
#define EXPECT_PROPRIETARY EXPECT_SUCCESS #define EXPECT_PROPRIETARY EXPECT_SUCCESS
...@@ -171,14 +173,16 @@ class EncryptedMediaSupportedTypesTest : public InProcessBrowserTest { ...@@ -171,14 +173,16 @@ class EncryptedMediaSupportedTypesTest : public InProcessBrowserTest {
return clear_key_exclusive_video_common_codecs_; return clear_key_exclusive_video_common_codecs_;
} }
#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
void SetUpDefaultCommandLine(base::CommandLine* command_line) override { void SetUpDefaultCommandLine(base::CommandLine* command_line) override {
#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
base::CommandLine default_command_line(base::CommandLine::NO_PROGRAM); base::CommandLine default_command_line(base::CommandLine::NO_PROGRAM);
InProcessBrowserTest::SetUpDefaultCommandLine(&default_command_line); InProcessBrowserTest::SetUpDefaultCommandLine(&default_command_line);
test_launcher_utils::RemoveCommandLineSwitch( test_launcher_utils::RemoveCommandLineSwitch(
default_command_line, switches::kDisableComponentUpdate, command_line); default_command_line, switches::kDisableComponentUpdate, command_line);
}
#endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) #endif // BUILDFLAG(ENABLE_LIBRARY_CDMS)
command_line->AppendSwitchASCII("enable-blink-features",
"EncryptedMediaEncryptionSchemeQuery");
}
void SetUpOnMainThread() override { void SetUpOnMainThread() override {
// Load the test page needed so that checkKeySystemWithMediaMimeType() // Load the test page needed so that checkKeySystemWithMediaMimeType()
...@@ -201,29 +205,40 @@ class EncryptedMediaSupportedTypesTest : public InProcessBrowserTest { ...@@ -201,29 +205,40 @@ class EncryptedMediaSupportedTypesTest : public InProcessBrowserTest {
return content_type; return content_type;
} }
// Format: {contentType: |content_type|, robustness: |robustness|}, or // Format: {contentType: |content_type|, encryptionScheme:
// {contentType: |content_type|} if |robustness| is null. // |encryption_scheme|, robustness: |robustness|}. encryptionScheme and
// robustness will not be included if |encryption_scheme| or |robustness|
// is null, respectively.
static std::string MakeMediaCapability(const std::string& content_type, static std::string MakeMediaCapability(const std::string& content_type,
const char* robustness) { const char* robustness,
if (!robustness) const char* encryption_scheme) {
return base::StringPrintf("{contentType: '%s'}", content_type.c_str()); std::string capability =
base::StringPrintf("{contentType: '%s'", content_type.c_str());
return base::StringPrintf("{contentType: '%s', robustness: '%s'}", if (encryption_scheme) {
content_type.c_str(), robustness); base::StringAppendF(&capability, ", encryptionScheme: '%s'",
encryption_scheme);
}
if (robustness) {
base::StringAppendF(&capability, ", robustness: '%s'", robustness);
}
base::StringAppendF(&capability, "}");
return capability;
} }
static std::string MakeMediaCapabilities(const std::string& mime_type, static std::string MakeMediaCapabilities(const std::string& mime_type,
const CodecVector& codecs, const CodecVector& codecs,
const char* robustness) { const char* robustness,
const char* encryption_scheme) {
std::string capabilities("["); std::string capabilities("[");
if (codecs.empty()) { if (codecs.empty()) {
capabilities += MakeMediaCapability( capabilities +=
MakeContentType(mime_type, std::string()), robustness); MakeMediaCapability(MakeContentType(mime_type, std::string()),
robustness, encryption_scheme);
} else { } else {
for (auto codec : codecs) { for (auto codec : codecs) {
capabilities += capabilities += MakeMediaCapability(MakeContentType(mime_type, codec),
MakeMediaCapability(MakeContentType(mime_type, codec), robustness) + robustness, encryption_scheme) +
","; ",";
} }
// Remove trailing comma. // Remove trailing comma.
capabilities.erase(capabilities.length() - 1); capabilities.erase(capabilities.length() - 1);
...@@ -238,6 +253,7 @@ class EncryptedMediaSupportedTypesTest : public InProcessBrowserTest { ...@@ -238,6 +253,7 @@ class EncryptedMediaSupportedTypesTest : public InProcessBrowserTest {
base::ASCIIToUTF16(kSuccessResult)); base::ASCIIToUTF16(kSuccessResult));
title_watcher.AlsoWaitForTitle(base::ASCIIToUTF16(kUnsupportedResult)); title_watcher.AlsoWaitForTitle(base::ASCIIToUTF16(kUnsupportedResult));
title_watcher.AlsoWaitForTitle(base::ASCIIToUTF16(kUnexpectedResult)); title_watcher.AlsoWaitForTitle(base::ASCIIToUTF16(kUnexpectedResult));
title_watcher.AlsoWaitForTitle(base::ASCIIToUTF16(kTypeErrorResult));
EXPECT_TRUE(content::ExecuteScript(contents, command)); EXPECT_TRUE(content::ExecuteScript(contents, command));
base::string16 result = title_watcher.WaitAndGetTitle(); base::string16 result = title_watcher.WaitAndGetTitle();
return base::UTF16ToASCII(result); return base::UTF16ToASCII(result);
...@@ -265,7 +281,8 @@ class EncryptedMediaSupportedTypesTest : public InProcessBrowserTest { ...@@ -265,7 +281,8 @@ class EncryptedMediaSupportedTypesTest : public InProcessBrowserTest {
const std::string& mime_type, const std::string& mime_type,
const CodecVector& codecs, const CodecVector& codecs,
SessionType session_type = SessionType::kTemporary, SessionType session_type = SessionType::kTemporary,
const char* robustness = nullptr) { const char* robustness = nullptr,
const char* encryption_scheme = nullptr) {
// Choose the appropriate init data type for the sub type. // Choose the appropriate init data type for the sub type.
size_t pos = mime_type.find('/'); size_t pos = mime_type.find('/');
DCHECK(pos > 0); DCHECK(pos > 0);
...@@ -280,7 +297,8 @@ class EncryptedMediaSupportedTypesTest : public InProcessBrowserTest { ...@@ -280,7 +297,8 @@ class EncryptedMediaSupportedTypesTest : public InProcessBrowserTest {
bool is_audio = mime_type.compare(0, 5, "audio") == 0; bool is_audio = mime_type.compare(0, 5, "audio") == 0;
DCHECK(is_audio || mime_type.compare(0, 5, "video") == 0); DCHECK(is_audio || mime_type.compare(0, 5, "video") == 0);
auto capabilities = MakeMediaCapabilities(mime_type, codecs, robustness); auto capabilities =
MakeMediaCapabilities(mime_type, codecs, robustness, encryption_scheme);
auto audio_capabilities = is_audio ? capabilities : "null"; auto audio_capabilities = is_audio ? capabilities : "null";
auto video_capabilities = !is_audio ? capabilities : "null"; auto video_capabilities = !is_audio ? capabilities : "null";
auto session_type_string = GetSessionTypeString(session_type); auto session_type_string = GetSessionTypeString(session_type);
...@@ -315,6 +333,20 @@ class EncryptedMediaSupportedTypesTest : public InProcessBrowserTest { ...@@ -315,6 +333,20 @@ class EncryptedMediaSupportedTypesTest : public InProcessBrowserTest {
robustness); robustness);
} }
std::string IsAudioEncryptionSchemeSupported(const std::string& key_system,
const char* encryption_scheme) {
return IsSupportedByKeySystem(key_system, kAudioWebMMimeType,
audio_webm_codecs(), SessionType::kTemporary,
nullptr, encryption_scheme);
}
std::string IsVideoEncryptionSchemeSupported(const std::string& key_system,
const char* encryption_scheme) {
return IsSupportedByKeySystem(key_system, kVideoWebMMimeType,
video_webm_codecs(), SessionType::kTemporary,
nullptr, encryption_scheme);
}
private: private:
const CodecVector no_codecs_; const CodecVector no_codecs_;
CodecVector audio_webm_codecs_; CodecVector audio_webm_codecs_;
...@@ -587,6 +619,24 @@ IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesClearKeyTest, Robustness) { ...@@ -587,6 +619,24 @@ IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesClearKeyTest, Robustness) {
EXPECT_UNSUPPORTED(IsAudioRobustnessSupported(kClearKey, "SW_SECURE_CRYPTO")); EXPECT_UNSUPPORTED(IsAudioRobustnessSupported(kClearKey, "SW_SECURE_CRYPTO"));
} }
IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesClearKeyTest,
EncryptionScheme) {
EXPECT_SUCCESS(IsAudioEncryptionSchemeSupported(kClearKey, nullptr));
EXPECT_SUCCESS(IsAudioEncryptionSchemeSupported(kClearKey, "cenc"));
EXPECT_SUCCESS(IsAudioEncryptionSchemeSupported(kClearKey, "cbcs"));
EXPECT_SUCCESS(IsVideoEncryptionSchemeSupported(kClearKey, nullptr));
EXPECT_SUCCESS(IsVideoEncryptionSchemeSupported(kClearKey, "cenc"));
EXPECT_SUCCESS(IsVideoEncryptionSchemeSupported(kClearKey, "cbcs"));
// Invalid encryption schemes will be rejected. However, invalid values
// generate a TypeError (The provided value '...' is not a valid enum value
// of type EncryptionScheme), which is not handled by the test page.
EXPECT_TYPEERROR(IsAudioEncryptionSchemeSupported(kClearKey, "Invalid"));
EXPECT_TYPEERROR(IsVideoEncryptionSchemeSupported(kClearKey, "Invalid"));
EXPECT_TYPEERROR(IsAudioEncryptionSchemeSupported(kClearKey, ""));
EXPECT_TYPEERROR(IsVideoEncryptionSchemeSupported(kClearKey, ""));
}
// //
// External Clear Key // External Clear Key
// //
...@@ -774,6 +824,26 @@ IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesExternalClearKeyTest, ...@@ -774,6 +824,26 @@ IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesExternalClearKeyTest,
IsAudioRobustnessSupported(kExternalClearKey, "SW_SECURE_CRYPTO")); IsAudioRobustnessSupported(kExternalClearKey, "SW_SECURE_CRYPTO"));
} }
IN_PROC_BROWSER_TEST_F(EncryptedMediaSupportedTypesExternalClearKeyTest,
EncryptionScheme) {
EXPECT_SUCCESS(IsAudioEncryptionSchemeSupported(kExternalClearKey, nullptr));
EXPECT_SUCCESS(IsAudioEncryptionSchemeSupported(kExternalClearKey, "cenc"));
EXPECT_SUCCESS(IsAudioEncryptionSchemeSupported(kExternalClearKey, "cbcs"));
EXPECT_SUCCESS(IsVideoEncryptionSchemeSupported(kExternalClearKey, nullptr));
EXPECT_SUCCESS(IsVideoEncryptionSchemeSupported(kExternalClearKey, "cenc"));
EXPECT_SUCCESS(IsVideoEncryptionSchemeSupported(kExternalClearKey, "cbcs"));
// Invalid encryption schemes will be rejected. However, invalid values
// generate a TypeError (The provided value '...' is not a valid enum value
// of type EncryptionScheme), which is not handled by the test page.
EXPECT_TYPEERROR(
IsAudioEncryptionSchemeSupported(kExternalClearKey, "Invalid"));
EXPECT_TYPEERROR(
IsVideoEncryptionSchemeSupported(kExternalClearKey, "Invalid"));
EXPECT_TYPEERROR(IsAudioEncryptionSchemeSupported(kExternalClearKey, ""));
EXPECT_TYPEERROR(IsVideoEncryptionSchemeSupported(kExternalClearKey, ""));
}
// External Clear Key is disabled by default. // External Clear Key is disabled by default.
IN_PROC_BROWSER_TEST_F( IN_PROC_BROWSER_TEST_F(
EncryptedMediaSupportedTypesExternalClearKeyNotEnabledTest, EncryptedMediaSupportedTypesExternalClearKeyNotEnabledTest,
......
...@@ -29,6 +29,8 @@ namespace media { ...@@ -29,6 +29,8 @@ namespace media {
using EmeFeatureRequirement = using EmeFeatureRequirement =
blink::WebMediaKeySystemConfiguration::Requirement; blink::WebMediaKeySystemConfiguration::Requirement;
using EmeEncryptionScheme =
blink::WebMediaKeySystemMediaCapability::EncryptionScheme;
namespace { namespace {
...@@ -360,6 +362,31 @@ bool KeySystemConfigSelector::IsSupportedContentType( ...@@ -360,6 +362,31 @@ bool KeySystemConfigSelector::IsSupportedContentType(
return true; return true;
} }
bool KeySystemConfigSelector::IsSupportedEncryptionScheme(
const std::string& key_system,
const EmeEncryptionScheme encryption_scheme) {
switch (encryption_scheme) {
// https://github.com/WICG/encrypted-media-encryption-scheme/blob/master/explainer.md.
// "A missing or null value indicates that any encryption scheme is
// acceptable."
// "Even if the application does not specify an encryption scheme,
// MediaKeySystemAccess.getConfiguration() must fill in a supported
// value."
// As Chrome has always supported 'cenc', assume this if encryption
// scheme is not specified.
case EmeEncryptionScheme::kNotSpecified:
case EmeEncryptionScheme::kCenc:
return key_systems_->IsEncryptionSchemeSupported(key_system,
EncryptionMode::kCenc);
case EmeEncryptionScheme::kCbcs:
return key_systems_->IsEncryptionSchemeSupported(key_system,
EncryptionMode::kCbcs);
}
NOTREACHED();
return false;
}
bool KeySystemConfigSelector::GetSupportedCapabilities( bool KeySystemConfigSelector::GetSupportedCapabilities(
const std::string& key_system, const std::string& key_system,
EmeMediaType media_type, EmeMediaType media_type,
...@@ -427,6 +454,14 @@ bool KeySystemConfigSelector::GetSupportedCapabilities( ...@@ -427,6 +454,14 @@ bool KeySystemConfigSelector::GetSupportedCapabilities(
continue; continue;
} }
// Check for encryption scheme support.
// https://github.com/WICG/encrypted-media-encryption-scheme/blob/master/explainer.md.
if (!IsSupportedEncryptionScheme(key_system,
capability.encryption_scheme)) {
DVLOG(3) << "Encryption scheme is not supported.";
continue;
}
// 3.13.1. Add requested media capability to supported media capabilities. // 3.13.1. Add requested media capability to supported media capabilities.
supported_media_capabilities->push_back(capability); supported_media_capabilities->push_back(capability);
......
...@@ -15,12 +15,12 @@ ...@@ -15,12 +15,12 @@
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "media/base/eme_constants.h" #include "media/base/eme_constants.h"
#include "media/blink/media_blink_export.h" #include "media/blink/media_blink_export.h"
#include "third_party/blink/public/platform/web_media_key_system_media_capability.h"
#include "third_party/blink/public/platform/web_vector.h" #include "third_party/blink/public/platform/web_vector.h"
namespace blink { namespace blink {
struct WebMediaKeySystemConfiguration; struct WebMediaKeySystemConfiguration;
struct WebMediaKeySystemMediaCapability;
class WebString; class WebString;
} // namespace blink } // namespace blink
...@@ -91,6 +91,11 @@ class MEDIA_BLINK_EXPORT KeySystemConfigSelector { ...@@ -91,6 +91,11 @@ class MEDIA_BLINK_EXPORT KeySystemConfigSelector {
const std::string& codecs, const std::string& codecs,
ConfigState* config_state); ConfigState* config_state);
bool IsSupportedEncryptionScheme(
const std::string& key_system,
const blink::WebMediaKeySystemMediaCapability::EncryptionScheme
encryption_scheme);
const KeySystems* key_systems_; const KeySystems* key_systems_;
MediaPermission* media_permission_; MediaPermission* media_permission_;
......
...@@ -29,6 +29,7 @@ using blink::WebMediaKeySystemConfiguration; ...@@ -29,6 +29,7 @@ using blink::WebMediaKeySystemConfiguration;
using blink::WebMediaKeySystemMediaCapability; using blink::WebMediaKeySystemMediaCapability;
using blink::WebString; using blink::WebString;
using MediaKeysRequirement = WebMediaKeySystemConfiguration::Requirement; using MediaKeysRequirement = WebMediaKeySystemConfiguration::Requirement;
using EncryptionScheme = WebMediaKeySystemMediaCapability::EncryptionScheme;
// Key system strings. Clear Key support is hardcoded in KeySystemConfigSelector // Key system strings. Clear Key support is hardcoded in KeySystemConfigSelector
// so kClearKeyKeySystem is the real key system string. The rest key system // so kClearKeyKeySystem is the real key system string. The rest key system
...@@ -70,6 +71,26 @@ const char kExtendedVideoCodecStripped[] = "video_extended_codec"; ...@@ -70,6 +71,26 @@ const char kExtendedVideoCodecStripped[] = "video_extended_codec";
// in IsSupportedMediaType() when |use_aes_decryptor| is true. // in IsSupportedMediaType() when |use_aes_decryptor| is true.
const char kUnsupportedByAesDecryptorCodec[] = "unsupported_by_aes_decryptor"; const char kUnsupportedByAesDecryptorCodec[] = "unsupported_by_aes_decryptor";
// Encryption schemes. For testing 'cenc' is supported, while 'cbcs' is not.
// Note that WebMediaKeySystemMediaCapability defaults to kNotSpecified,
// which is treated as 'cenc' by KeySystemConfigSelector.
constexpr EncryptionScheme kSupportedEncryptionScheme = EncryptionScheme::kCenc;
constexpr EncryptionScheme kUnSupportedEncryptionScheme =
EncryptionScheme::kCbcs;
EncryptionMode ConvertEncryptionScheme(EncryptionScheme encryption_scheme) {
switch (encryption_scheme) {
case EncryptionScheme::kNotSpecified:
case EncryptionScheme::kCenc:
return EncryptionMode::kCenc;
case EncryptionScheme::kCbcs:
return EncryptionMode::kCbcs;
}
NOTREACHED();
return EncryptionMode::kUnencrypted;
}
WebString MakeCodecs(const std::string& a, const std::string& b) { WebString MakeCodecs(const std::string& a, const std::string& b) {
return WebString::FromUTF8(a + "," + b); return WebString::FromUTF8(a + "," + b);
} }
...@@ -196,9 +217,8 @@ class FakeKeySystems : public KeySystems { ...@@ -196,9 +217,8 @@ class FakeKeySystems : public KeySystems {
bool IsEncryptionSchemeSupported( bool IsEncryptionSchemeSupported(
const std::string& key_system, const std::string& key_system,
EncryptionMode encryption_scheme) const override { EncryptionMode encryption_scheme) const override {
// TODO(crbug.com/658026): Implement this once value passed from blink. return encryption_scheme ==
NOTREACHED(); ConvertEncryptionScheme(kSupportedEncryptionScheme);
return false;
} }
EmeConfigRule GetContentTypeConfigRule( EmeConfigRule GetContentTypeConfigRule(
...@@ -1043,6 +1063,39 @@ TEST_F(KeySystemConfigSelectorTest, ...@@ -1043,6 +1063,39 @@ TEST_F(KeySystemConfigSelectorTest,
EXPECT_EQ(MediaKeysRequirement::kNotAllowed, config_.distinctive_identifier); EXPECT_EQ(MediaKeysRequirement::kNotAllowed, config_.distinctive_identifier);
} }
TEST_F(KeySystemConfigSelectorTest,
VideoCapabilities_EncryptionScheme_Supported) {
std::vector<blink::WebMediaKeySystemMediaCapability> video_capabilities(1);
video_capabilities[0].content_type = "a";
video_capabilities[0].mime_type = kSupportedVideoContainer;
video_capabilities[0].codecs = kSupportedVideoCodec;
video_capabilities[0].encryption_scheme = kSupportedEncryptionScheme;
blink::WebMediaKeySystemConfiguration config = EmptyConfiguration();
config.video_capabilities = video_capabilities;
configs_.push_back(config);
SelectConfigReturnsConfig();
ASSERT_EQ(1u, config_.video_capabilities.size());
EXPECT_EQ(kSupportedEncryptionScheme,
config_.video_capabilities[0].encryption_scheme);
}
TEST_F(KeySystemConfigSelectorTest,
VideoCapabilities_EncryptionScheme_Unsupported) {
std::vector<blink::WebMediaKeySystemMediaCapability> video_capabilities(1);
video_capabilities[0].content_type = "a";
video_capabilities[0].mime_type = kSupportedVideoContainer;
video_capabilities[0].codecs = kSupportedVideoCodec;
video_capabilities[0].encryption_scheme = kUnSupportedEncryptionScheme;
blink::WebMediaKeySystemConfiguration config = EmptyConfiguration();
config.video_capabilities = video_capabilities;
configs_.push_back(config);
SelectConfigReturnsError();
}
// --- HW Secure Codecs and Robustness --- // --- HW Secure Codecs and Robustness ---
TEST_F(KeySystemConfigSelectorTest, HwSecureCodec_RequireHwSecureCodec) { TEST_F(KeySystemConfigSelectorTest, HwSecureCodec_RequireHwSecureCodec) {
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
// Since promises run asynchronously, use the pages title to keep track // Since promises run asynchronously, use the pages title to keep track
// of the result. // of the result.
function setResultInTitle(title) { function setResultInTitle(title) {
if (title == "" || title == "success" || if (title == "" || title == "success" || title == "TypeError" ||
title == "Unsupported keySystem or supportedConfigurations.") { title == "Unsupported keySystem or supportedConfigurations.") {
document.title = title; document.title = title;
} else { } else {
...@@ -82,7 +82,9 @@ ...@@ -82,7 +82,9 @@
} }
setResultInTitle("success"); setResultInTitle("success");
}) })
.catch(function(err) { setResultInTitle(err.message); }); .catch(function(err) {
setResultInTitle((err.name == 'TypeError') ? err.name : err.message);
});
}; };
function checkKeySystemWithMediaMimeType(keySystem, initDataType, function checkKeySystemWithMediaMimeType(keySystem, initDataType,
......
<!DOCTYPE html>
<html>
<head>
<title>Test Encryption Scheme</title>
<script src="encrypted-media-utils.js"></script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<script>
// Checks that calling navigator.requestMediaKeySystemAccess()
// with |configuration| selects a configuration where the first
// entry in audioCapabilities and videoCapabilities contains
// encryptionScheme of |expectedAudioScheme| or
// |expectedVideoScheme|, respectively. If |expectedAudioScheme| or
// |expectedVideoScheme| is undefined, then no corresponding
// capability should have been selected.
function expectSupport(configuration, expectedAudioScheme, expectedVideoScheme, testName) {
promise_test(async test => {
var access = await navigator.requestMediaKeySystemAccess('org.w3.clearkey', configuration);
var result = access.getConfiguration();
if (result.audioCapabilities.length > 0) {
assert_equals(result.audioCapabilities[0].encryptionScheme, expectedAudioScheme, 'audio scheme');
} else {
assert_equals(undefined, expectedAudioScheme, 'expected audio scheme missing');
}
if (result.videoCapabilities.length > 0) {
assert_equals(result.videoCapabilities[0].encryptionScheme, expectedVideoScheme, 'video scheme');
} else {
assert_equals(undefined, expectedVideoScheme, 'expected video scheme missing');
}
}, testName);
}
// Verifies that calling navigator.requestMediaKeySystemAccess()
// with |configuration| throws a TypeError.
function expectTypeError(configuration, testName) {
promise_test(async test => {
try {
await navigator.requestMediaKeySystemAccess('org.w3.clearkey', configuration);
assert_unreached('Unexpected requestMediaKeySystemAccess() success.');
} catch(error) {
// Error must be a TypeError.
assert_equals(error.name, 'TypeError', 'unexpected error');
}
}, testName);
}
// Creates an audioCapabilities sequence with one entry, including
// |encryption_scheme| if specified.
function getAudioCapabilities(encryptionScheme) {
var capability = [{contentType: 'audio/webm; codecs="opus"'}];
if (encryptionScheme !== undefined) {
capability[0].encryptionScheme = encryptionScheme;
}
return capability;
}
// Creates an videoCapabilities sequence with one entry, including
// |encryption_scheme| if specified.
function getVideoCapabilities(encryptionScheme) {
var capability = [{contentType: 'video/webm; codecs="vp8"'}];
if (encryptionScheme !== undefined) {
capability[0].encryptionScheme = encryptionScheme;
}
return capability;
}
// Takes a set of capabilities (passed as arguments) and combines
// them into a MediaKeySystemConfiguration dictionary. If multiple
// arguments define the same property, the last one overrides the
// previous entry.
function buildConfiguration() {
var configuration = { initDataTypes : [ 'webm' ] };
for (var i = 0; i < arguments.length; i++) {
for (var arg in arguments[i]) {
if (arg == 'audio') {
configuration['audioCapabilities'] =
getAudioCapabilities(arguments[i][arg]);
} else if (arg == 'video') {
configuration['videoCapabilities'] =
getVideoCapabilities(arguments[i][arg]);
}
}
}
return configuration;
}
// Test "cenc".
expectSupport([buildConfiguration({audio: 'cenc'})],
'cenc', undefined, 'Audio supports cenc');
expectSupport([buildConfiguration({video: 'cenc'})],
undefined, 'cenc', 'Video supports cenc');
expectSupport([buildConfiguration({audio: 'cenc'}, {video: 'cenc'})],
'cenc', 'cenc', 'Audio and Video supports cenc');
// Test "cbcs".
expectSupport([buildConfiguration({audio: 'cbcs'})],
'cbcs', undefined, 'Audio supports cbcs');
expectSupport([buildConfiguration({video: 'cbcs'})],
undefined, 'cbcs', 'Video supports cbcs');
expectSupport([buildConfiguration({audio: 'cbcs'}, {video: 'cbcs'})],
'cbcs', 'cbcs', 'Audio and Video supports cbcs');
// Test "invalid", which should fail.
expectTypeError([buildConfiguration({audio: 'invalid'})],
'Audio fails with invalid scheme');
expectTypeError([buildConfiguration({video: 'invalid'})],
'Video fails with invalid scheme');
expectTypeError([buildConfiguration({audio: 'invalid'}, {video: 'invalid'})],
'Audio and Video fails with invalid scheme');
expectTypeError([buildConfiguration({audio: 'cenc'}, {video: 'invalid'})],
'Audio valid, Video invalid scheme');
expectTypeError([buildConfiguration({audio: 'invalid'}, {video: 'cbcs'})],
'Audio invalid scheme, Video valid');
// Test without encryptionScheme, which should default to "cenc".
expectSupport([buildConfiguration({audio: null})],
'cenc', undefined, 'Audio handles missing encryptionScheme');
expectSupport([buildConfiguration({video: null})],
undefined, 'cenc', 'Video handles missing encryptionScheme');
expectSupport([buildConfiguration({audio: null}, {video: null})],
'cenc', 'cenc', 'Audio and Video handles missing encryptionScheme');
expectSupport([buildConfiguration({audio: 'cbcs'}, {video: null})],
'cbcs', 'cenc', 'Audio valid and Video missing');
expectSupport([buildConfiguration({audio: null}, {video: 'cbcs'})],
'cenc', 'cbcs', 'Audio missing and Video valid');
// Test with "", which is invalid.
expectTypeError([buildConfiguration({audio: ''})],
'Audio handles empty encryptionScheme');
expectTypeError([buildConfiguration({video: ''})],
'Video handles empty encryptionScheme');
expectTypeError([buildConfiguration({audio: ''}, {video: ''})],
'Audio and Video handles empty encryptionScheme');
expectTypeError([buildConfiguration({audio: 'cbcs'}, {video: ''})],
'Audio valid and Video empty');
expectTypeError([buildConfiguration({audio: ''}, {video: 'cbcs'})],
'Audio empty and Video valid');
// Test mixed combinations.
expectSupport([buildConfiguration({audio: 'cbcs'}, {video: 'cenc'})],
'cbcs', 'cenc', 'Audio supports cbcs and Video supports cenc');
expectSupport([buildConfiguration({audio: 'cenc'}, {video: 'cbcs'})],
'cenc', 'cbcs', 'Audio supports cenc and Video supports cbcs');
// Test multiple configurations.
expectSupport([buildConfiguration({audio: 'cenc'}),
buildConfiguration({audio: 'cbcs'})],
'cenc', undefined, 'Multiple audio entries supports cenc');
expectSupport([buildConfiguration({audio: 'cbcs'}),
buildConfiguration({audio: 'cenc'})],
'cbcs', undefined, 'Multiple audio entries supports cbcs');
expectSupport([buildConfiguration({video: 'cenc'}),
buildConfiguration({video: 'cbcs'})],
undefined, 'cenc', 'Multiple video entries supports cenc');
expectSupport([buildConfiguration({video: 'cbcs'}),
buildConfiguration({video: 'cenc'})],
undefined, 'cbcs', 'Multiple video entries supports cbcs');
// Test mixed multiple configurations.
expectSupport([buildConfiguration({audio: 'cenc'}),
buildConfiguration({video: 'cbcs'})],
'cenc', undefined, 'Mixed entries supports cenc');
expectSupport([buildConfiguration({video: 'cbcs'}),
buildConfiguration({audio: 'cenc'})],
undefined, 'cbcs', 'Mixed entries supports cbcs');
// Test invalid values.
expectTypeError([buildConfiguration({audio: 3})],
'Audio fails with numeric scheme');
expectTypeError([buildConfiguration({audio: [1, 2, 3]})],
'Audio fails with array scheme');
expectTypeError([buildConfiguration({audio: {test: 'test'}})],
'Audio fails with object scheme');
</script>
</body>
</html>
...@@ -10,12 +10,19 @@ ...@@ -10,12 +10,19 @@
namespace blink { namespace blink {
struct WebMediaKeySystemMediaCapability { struct WebMediaKeySystemMediaCapability {
enum class EncryptionScheme {
kNotSpecified,
kCenc,
kCbcs,
};
WebMediaKeySystemMediaCapability() = default; WebMediaKeySystemMediaCapability() = default;
WebString content_type; WebString content_type;
WebString mime_type; WebString mime_type;
WebString codecs; WebString codecs;
WebString robustness; WebString robustness;
EncryptionScheme encryption_scheme = EncryptionScheme::kNotSpecified;
}; };
} // namespace blink } // namespace blink
......
...@@ -73,6 +73,28 @@ static Vector<String> ConvertInitDataTypes( ...@@ -73,6 +73,28 @@ static Vector<String> ConvertInitDataTypes(
return result; return result;
} }
static String ConvertEncryptionScheme(
WebMediaKeySystemMediaCapability::EncryptionScheme encryption_scheme) {
switch (encryption_scheme) {
// https://github.com/WICG/encrypted-media-encryption-scheme/blob/master/explainer.md.
// "A missing or null value indicates that any encryption scheme is
// acceptable."
// "Even if the application does not specify an encryption scheme,
// MediaKeySystemAccess.getConfiguration() must fill in a supported
// value."
// As Chrome has always supported 'cenc', assume this if encryption
// scheme is not specified.
case WebMediaKeySystemMediaCapability::EncryptionScheme::kNotSpecified:
case WebMediaKeySystemMediaCapability::EncryptionScheme::kCenc:
return "cenc";
case WebMediaKeySystemMediaCapability::EncryptionScheme::kCbcs:
return "cbcs";
}
NOTREACHED();
return "";
}
static HeapVector<MediaKeySystemMediaCapability> ConvertCapabilities( static HeapVector<MediaKeySystemMediaCapability> ConvertCapabilities(
const WebVector<WebMediaKeySystemMediaCapability>& capabilities) { const WebVector<WebMediaKeySystemMediaCapability>& capabilities) {
HeapVector<MediaKeySystemMediaCapability> result(capabilities.size()); HeapVector<MediaKeySystemMediaCapability> result(capabilities.size());
...@@ -80,6 +102,8 @@ static HeapVector<MediaKeySystemMediaCapability> ConvertCapabilities( ...@@ -80,6 +102,8 @@ static HeapVector<MediaKeySystemMediaCapability> ConvertCapabilities(
MediaKeySystemMediaCapability capability; MediaKeySystemMediaCapability capability;
capability.setContentType(capabilities[i].content_type); capability.setContentType(capabilities[i].content_type);
capability.setRobustness(capabilities[i].robustness); capability.setRobustness(capabilities[i].robustness);
capability.setEncryptionScheme(
ConvertEncryptionScheme(capabilities[i].encryption_scheme));
result[i] = capability; result[i] = capability;
} }
return result; return result;
......
...@@ -4,7 +4,10 @@ ...@@ -4,7 +4,10 @@
// https://w3c.github.io/encrypted-media/#idl-def-MediaKeySystemMediaCapability // https://w3c.github.io/encrypted-media/#idl-def-MediaKeySystemMediaCapability
enum EncryptionScheme { "cenc", "cbcs" };
dictionary MediaKeySystemMediaCapability { dictionary MediaKeySystemMediaCapability {
DOMString contentType = ""; DOMString contentType = "";
DOMString robustness = ""; DOMString robustness = "";
[RuntimeEnabled=EncryptedMediaEncryptionSchemeQuery] EncryptionScheme? encryptionScheme = null;
}; };
...@@ -50,6 +50,17 @@ static WebVector<WebEncryptedMediaInitDataType> ConvertInitDataTypes( ...@@ -50,6 +50,17 @@ static WebVector<WebEncryptedMediaInitDataType> ConvertInitDataTypes(
return result; return result;
} }
static WebMediaKeySystemMediaCapability::EncryptionScheme
ConvertEncryptionScheme(const String& encryption_scheme) {
if (encryption_scheme == "cenc")
return WebMediaKeySystemMediaCapability::EncryptionScheme::kCenc;
if (encryption_scheme == "cbcs")
return WebMediaKeySystemMediaCapability::EncryptionScheme::kCbcs;
NOTREACHED();
return WebMediaKeySystemMediaCapability::EncryptionScheme::kNotSpecified;
}
static WebVector<WebMediaKeySystemMediaCapability> ConvertCapabilities( static WebVector<WebMediaKeySystemMediaCapability> ConvertCapabilities(
const HeapVector<MediaKeySystemMediaCapability>& capabilities) { const HeapVector<MediaKeySystemMediaCapability>& capabilities) {
WebVector<WebMediaKeySystemMediaCapability> result(capabilities.size()); WebVector<WebMediaKeySystemMediaCapability> result(capabilities.size());
...@@ -70,6 +81,17 @@ static WebVector<WebMediaKeySystemMediaCapability> ConvertCapabilities( ...@@ -70,6 +81,17 @@ static WebVector<WebMediaKeySystemMediaCapability> ConvertCapabilities(
result[i].codecs = type.ParameterValueForName("codecs"); result[i].codecs = type.ParameterValueForName("codecs");
} }
result[i].robustness = capabilities[i].robustness(); result[i].robustness = capabilities[i].robustness();
// From
// https://github.com/WICG/encrypted-media-encryption-scheme/blob/master/explainer.md
// "Asking for "any" encryption scheme is unrealistic. Defining null as
// "any scheme" is convenient for backward compatibility, though.
// Applications which ignore this feature by leaving encryptionScheme null
// get the same user agent behavior they did before this feature existed."
result[i].encryption_scheme =
capabilities[i].hasEncryptionScheme()
? ConvertEncryptionScheme(capabilities[i].encryptionScheme())
: WebMediaKeySystemMediaCapability::EncryptionScheme::kNotSpecified;
} }
return result; return result;
} }
......
...@@ -399,6 +399,10 @@ ...@@ -399,6 +399,10 @@
name: "EmbedderCSPEnforcement", name: "EmbedderCSPEnforcement",
status: "stable", status: "stable",
}, },
{
name: "EncryptedMediaEncryptionSchemeQuery",
status: "test",
},
{ {
name: "EncryptedMediaHdcpPolicyCheck", name: "EncryptedMediaHdcpPolicyCheck",
origin_trial_feature_name: "EncryptedMediaHdcpPolicyCheck", origin_trial_feature_name: "EncryptedMediaHdcpPolicyCheck",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment