Handle createCDM() when frame destroyed
[chromium-blink-merge.git] / media / blink / webencryptedmediaclient_impl.cc
blob264d5c6d12b7013cb2978fd12ad531dcbc49b93b
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"
21 namespace media {
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,
43 key_system);
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())
57 return false;
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())
66 return false;
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) {
76 return false;
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())
86 return false;
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))) {
92 continue;
94 // TODO(sandersd): Support robustness.
95 if (!capabilities.robustness.isEmpty())
96 continue;
97 audio_capabilities.push_back(capabilities);
100 if (audio_capabilities.empty())
101 return false;
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())
113 return false;
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))) {
119 continue;
121 // TODO(sandersd): Support robustness.
122 if (!capabilities.robustness.isEmpty())
123 continue;
124 video_capabilities.push_back(capabilities);
127 if (video_capabilities.empty())
128 return false;
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.
137 return true;
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
145 // renderer frame.
146 class WebEncryptedMediaClientImpl::Reporter {
147 public:
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) {}
158 ~Reporter() {}
160 void ReportRequested() {
161 if (is_request_reported_)
162 return;
163 Report(KEY_SYSTEM_REQUESTED);
164 is_request_reported_ = true;
167 void ReportSupported() {
168 DCHECK(is_request_reported_);
169 if (is_support_reported_)
170 return;
171 Report(KEY_SYSTEM_SUPPORTED);
172 is_support_reported_ = true;
175 private:
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
195 // this class.
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");
214 return;
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");
225 return;
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()));
242 return;
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()));
255 return;
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,
269 key_system, result);
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)
278 return reporter;
280 // Reporter not found, so create one.
281 auto result =
282 reporters_.add(uma_name, make_scoped_ptr(new Reporter(uma_name)));
283 DCHECK(result.second);
284 return result.first->second;
287 } // namespace media