1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "webencryptedmediaclient_impl.h"
7 #include "base/logging.h"
8 #include "base/metrics/histogram.h"
9 #include "base/strings/string_util.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "media/base/key_systems.h"
12 #include "media/base/media_permission.h"
13 #include "net/base/mime_util.h"
14 #include "third_party/WebKit/public/platform/WebEncryptedMediaRequest.h"
15 #include "third_party/WebKit/public/platform/WebMediaKeySystemConfiguration.h"
16 #include "third_party/WebKit/public/platform/WebString.h"
17 #include "third_party/WebKit/public/platform/WebVector.h"
18 #include "webcontentdecryptionmodule_impl.h"
19 #include "webcontentdecryptionmoduleaccess_impl.h"
23 // These names are used by UMA.
24 const char kKeySystemSupportUMAPrefix
[] =
25 "Media.EME.RequestMediaKeySystemAccess.";
27 static bool IsSupportedContentType(
28 const std::string
& key_system
,
29 const std::string
& mime_type
,
30 const std::string
& codecs
) {
31 // Per RFC 6838, "Both top-level type and subtype names are case-insensitive."
32 // TODO(sandersd): Check that |container| matches the capability:
33 // - audioCapabilitys: audio/mp4 or audio/webm.
34 // - videoCapabilitys: video/mp4 or video/webm.
35 // http://crbug.com/429781.
36 std::string container
= base::StringToLowerASCII(mime_type
);
38 // TODO(sandersd): Strict checking for codecs. http://crbug.com/374751.
39 bool strip_codec_suffixes
= !net::IsStrictMediaMimeType(container
);
40 std::vector
<std::string
> codec_vector
;
41 net::ParseCodecString(codecs
, &codec_vector
, strip_codec_suffixes
);
42 return IsSupportedKeySystemWithMediaMimeType(container
, codec_vector
,
46 static bool GetSupportedConfiguration(
47 const std::string
& key_system
,
48 const blink::WebMediaKeySystemConfiguration
& candidate
,
49 const blink::WebSecurityOrigin
& security_origin
,
50 blink::WebMediaKeySystemConfiguration
* accumulated_configuration
) {
51 if (!candidate
.initDataTypes
.isEmpty()) {
52 std::vector
<blink::WebString
> init_data_types
;
54 for (size_t i
= 0; i
< candidate
.initDataTypes
.size(); i
++) {
55 const blink::WebString
& init_data_type
= candidate
.initDataTypes
[i
];
56 if (init_data_type
.isEmpty())
58 if (base::IsStringASCII(init_data_type
) &&
59 IsSupportedKeySystemWithInitDataType(
60 key_system
, base::UTF16ToASCII(init_data_type
))) {
61 init_data_types
.push_back(init_data_type
);
65 if (init_data_types
.empty())
68 accumulated_configuration
->initDataTypes
= init_data_types
;
71 // TODO(sandersd): Implement distinctiveIdentifier and persistentState checks.
72 if (candidate
.distinctiveIdentifier
!=
73 blink::WebMediaKeySystemConfiguration::Requirement::Optional
||
74 candidate
.persistentState
!=
75 blink::WebMediaKeySystemConfiguration::Requirement::Optional
) {
79 if (!candidate
.audioCapabilities
.isEmpty()) {
80 std::vector
<blink::WebMediaKeySystemMediaCapability
> audio_capabilities
;
82 for (size_t i
= 0; i
< candidate
.audioCapabilities
.size(); i
++) {
83 const blink::WebMediaKeySystemMediaCapability
& capabilities
=
84 candidate
.audioCapabilities
[i
];
85 if (capabilities
.mimeType
.isEmpty())
87 if (!base::IsStringASCII(capabilities
.mimeType
) ||
88 !base::IsStringASCII(capabilities
.codecs
) ||
89 !IsSupportedContentType(
90 key_system
, base::UTF16ToASCII(capabilities
.mimeType
),
91 base::UTF16ToASCII(capabilities
.codecs
))) {
94 // TODO(sandersd): Support robustness.
95 if (!capabilities
.robustness
.isEmpty())
97 audio_capabilities
.push_back(capabilities
);
100 if (audio_capabilities
.empty())
103 accumulated_configuration
->audioCapabilities
= audio_capabilities
;
106 if (!candidate
.videoCapabilities
.isEmpty()) {
107 std::vector
<blink::WebMediaKeySystemMediaCapability
> video_capabilities
;
109 for (size_t i
= 0; i
< candidate
.videoCapabilities
.size(); i
++) {
110 const blink::WebMediaKeySystemMediaCapability
& capabilities
=
111 candidate
.videoCapabilities
[i
];
112 if (capabilities
.mimeType
.isEmpty())
114 if (!base::IsStringASCII(capabilities
.mimeType
) ||
115 !base::IsStringASCII(capabilities
.codecs
) ||
116 !IsSupportedContentType(
117 key_system
, base::UTF16ToASCII(capabilities
.mimeType
),
118 base::UTF16ToASCII(capabilities
.codecs
))) {
121 // TODO(sandersd): Support robustness.
122 if (!capabilities
.robustness
.isEmpty())
124 video_capabilities
.push_back(capabilities
);
127 if (video_capabilities
.empty())
130 accumulated_configuration
->videoCapabilities
= video_capabilities
;
133 // TODO(sandersd): Prompt for distinctive identifiers and/or persistent state
134 // if required. Make sure that future checks are silent.
135 // http://crbug.com/446263.
140 // Report usage of key system to UMA. There are 2 different counts logged:
141 // 1. The key system is requested.
142 // 2. The requested key system and options are supported.
143 // Each stat is only reported once per renderer frame per key system.
144 // Note that WebEncryptedMediaClientImpl is only created once by each
146 class WebEncryptedMediaClientImpl::Reporter
{
148 enum KeySystemSupportStatus
{
149 KEY_SYSTEM_REQUESTED
= 0,
150 KEY_SYSTEM_SUPPORTED
= 1,
151 KEY_SYSTEM_SUPPORT_STATUS_COUNT
154 explicit Reporter(const std::string
& key_system_for_uma
)
155 : uma_name_(kKeySystemSupportUMAPrefix
+ key_system_for_uma
),
156 is_request_reported_(false),
157 is_support_reported_(false) {}
160 void ReportRequested() {
161 if (is_request_reported_
)
163 Report(KEY_SYSTEM_REQUESTED
);
164 is_request_reported_
= true;
167 void ReportSupported() {
168 DCHECK(is_request_reported_
);
169 if (is_support_reported_
)
171 Report(KEY_SYSTEM_SUPPORTED
);
172 is_support_reported_
= true;
176 void Report(KeySystemSupportStatus status
) {
177 // Not using UMA_HISTOGRAM_ENUMERATION directly because UMA_* macros
178 // require the names to be constant throughout the process' lifetime.
179 base::LinearHistogram::FactoryGet(
180 uma_name_
, 1, KEY_SYSTEM_SUPPORT_STATUS_COUNT
,
181 KEY_SYSTEM_SUPPORT_STATUS_COUNT
+ 1,
182 base::Histogram::kUmaTargetedHistogramFlag
)->Add(status
);
185 const std::string uma_name_
;
186 bool is_request_reported_
;
187 bool is_support_reported_
;
190 WebEncryptedMediaClientImpl::WebEncryptedMediaClientImpl(
191 scoped_ptr
<CdmFactory
> cdm_factory
,
192 MediaPermission
* media_permission
)
193 : cdm_factory_(cdm_factory
.Pass()), weak_factory_(this) {
194 // TODO(sandersd): Use |media_permission| to check for media permissions in
196 DCHECK(media_permission
);
199 WebEncryptedMediaClientImpl::~WebEncryptedMediaClientImpl() {
202 void WebEncryptedMediaClientImpl::requestMediaKeySystemAccess(
203 blink::WebEncryptedMediaRequest request
) {
204 // TODO(jrummell): This should be asynchronous.
206 // Continued from requestMediaKeySystemAccess(), step 7, from
207 // https://w3c.github.io/encrypted-media/#requestmediakeysystemaccess
209 // 7.1 If keySystem is not one of the Key Systems supported by the user
210 // agent, reject promise with with a new DOMException whose name is
211 // NotSupportedError. String comparison is case-sensitive.
212 if (!base::IsStringASCII(request
.keySystem())) {
213 request
.requestNotSupported("Only ASCII keySystems are supported");
217 std::string key_system
= base::UTF16ToASCII(request
.keySystem());
219 // Report this request to the appropriate Reporter.
220 Reporter
* reporter
= GetReporter(key_system
);
221 reporter
->ReportRequested();
223 if (!IsConcreteSupportedKeySystem(key_system
)) {
224 request
.requestNotSupported("Unsupported keySystem");
228 // 7.2 Let implementation be the implementation of keySystem.
229 // 7.3 For each value in supportedConfigurations, run the GetSupported
230 // Configuration algorithm and if successful, resolve promise with access
231 // and abort these steps.
232 const blink::WebVector
<blink::WebMediaKeySystemConfiguration
>&
233 configurations
= request
.supportedConfigurations();
235 // TODO(sandersd): Remove once Blink requires the configurations parameter for
236 // requestMediaKeySystemAccess().
237 if (configurations
.isEmpty()) {
238 reporter
->ReportSupported();
239 request
.requestSucceeded(WebContentDecryptionModuleAccessImpl::Create(
240 request
.keySystem(), blink::WebMediaKeySystemConfiguration(),
241 request
.securityOrigin(), weak_factory_
.GetWeakPtr()));
245 for (size_t i
= 0; i
< configurations
.size(); i
++) {
246 const blink::WebMediaKeySystemConfiguration
& candidate
= configurations
[i
];
247 blink::WebMediaKeySystemConfiguration accumulated_configuration
;
248 if (GetSupportedConfiguration(key_system
, candidate
,
249 request
.securityOrigin(),
250 &accumulated_configuration
)) {
251 reporter
->ReportSupported();
252 request
.requestSucceeded(WebContentDecryptionModuleAccessImpl::Create(
253 request
.keySystem(), accumulated_configuration
,
254 request
.securityOrigin(), weak_factory_
.GetWeakPtr()));
259 // 7.4 Reject promise with a new DOMException whose name is NotSupportedError.
260 request
.requestNotSupported(
261 "None of the requested configurations were supported.");
264 void WebEncryptedMediaClientImpl::CreateCdm(
265 const blink::WebString
& key_system
,
266 const blink::WebSecurityOrigin
& security_origin
,
267 blink::WebContentDecryptionModuleResult result
) {
268 WebContentDecryptionModuleImpl::Create(cdm_factory_
.get(), security_origin
,
272 // Lazily create Reporters.
273 WebEncryptedMediaClientImpl::Reporter
* WebEncryptedMediaClientImpl::GetReporter(
274 const std::string
& key_system
) {
275 std::string uma_name
= GetKeySystemNameForUMA(key_system
);
276 Reporter
* reporter
= reporters_
.get(uma_name
);
277 if (reporter
!= nullptr)
280 // Reporter not found, so create one.
282 reporters_
.add(uma_name
, make_scoped_ptr(new Reporter(uma_name
)));
283 DCHECK(result
.second
);
284 return result
.first
->second
;