Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
|
ddorwin
2014/08/22 21:08:48
Is this far too different from WMPI.cc to get any
acolwell GONE FROM CHROMIUM
2014/08/22 23:20:22
I don't know what you mean. Most of this code is s
| |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "content/renderer/media/crypto/encrypted_media_support_impl.h" | |
| 6 | |
| 7 #include <string> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "base/callback_helpers.h" | |
| 11 #include "base/metrics/histogram.h" | |
| 12 #include "base/strings/string_number_conversions.h" | |
| 13 #include "base/strings/string_util.h" | |
| 14 #include "base/strings/utf_string_conversions.h" | |
| 15 #include "content/renderer/media/crypto/key_systems.h" | |
| 16 #include "content/renderer/media/webcontentdecryptionmodule_impl.h" | |
| 17 #include "content/renderer/pepper/pepper_webplugin_impl.h" | |
| 18 #include "media/base/bind_to_current_loop.h" | |
| 19 #include "third_party/WebKit/public/platform/WebContentDecryptionModule.h" | |
| 20 #include "third_party/WebKit/public/platform/WebContentDecryptionModuleResult.h" | |
| 21 #include "third_party/WebKit/public/platform/WebMediaPlayerClient.h" | |
| 22 #include "third_party/WebKit/public/web/WebDocument.h" | |
| 23 #include "third_party/WebKit/public/web/WebLocalFrame.h" | |
| 24 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h" | |
| 25 | |
| 26 #if defined(ENABLE_PEPPER_CDMS) | |
| 27 #include "content/renderer/media/crypto/pepper_cdm_wrapper_impl.h" | |
| 28 #endif | |
| 29 | |
| 30 using blink::WebMediaPlayer; | |
| 31 using blink::WebMediaPlayerClient; | |
| 32 using blink::WebString; | |
| 33 | |
| 34 namespace content { | |
| 35 | |
| 36 #define BIND_TO_RENDER_LOOP(function) \ | |
| 37 (media::BindToCurrentLoop(base::Bind(function, AsWeakPtr()))) | |
| 38 | |
| 39 #define BIND_TO_RENDER_LOOP1(function, arg1) \ | |
| 40 (media::BindToCurrentLoop(base::Bind(function, AsWeakPtr(), arg1))) | |
| 41 | |
| 42 | |
| 43 // Prefix for histograms related to Encrypted Media Extensions. | |
| 44 static const char* kMediaEme = "Media.EME."; | |
| 45 | |
| 46 // Used for calls to decryptor_ready_cb where the result can be ignored. | |
| 47 static void DoNothing(bool success) { | |
| 48 } | |
| 49 | |
| 50 // Convert a WebString to ASCII, falling back on an empty string in the case | |
| 51 // of a non-ASCII string. | |
| 52 static std::string ToASCIIOrEmpty(const WebString& string) { | |
| 53 return base::IsStringASCII(string) ? base::UTF16ToASCII(string) | |
| 54 : std::string(); | |
| 55 } | |
| 56 | |
| 57 // Helper functions to report media EME related stats to UMA. They follow the | |
| 58 // convention of more commonly used macros UMA_HISTOGRAM_ENUMERATION and | |
| 59 // UMA_HISTOGRAM_COUNTS. The reason that we cannot use those macros directly is | |
| 60 // that UMA_* macros require the names to be constant throughout the process' | |
| 61 // lifetime. | |
| 62 static void EmeUMAHistogramEnumeration(const std::string& key_system, | |
| 63 const std::string& method, | |
| 64 int sample, | |
| 65 int boundary_value) { | |
| 66 base::LinearHistogram::FactoryGet( | |
| 67 kMediaEme + KeySystemNameForUMA(key_system) + "." + method, | |
| 68 1, boundary_value, boundary_value + 1, | |
| 69 base::Histogram::kUmaTargetedHistogramFlag)->Add(sample); | |
| 70 } | |
| 71 | |
| 72 static void EmeUMAHistogramCounts(const std::string& key_system, | |
| 73 const std::string& method, | |
| 74 int sample) { | |
| 75 // Use the same parameters as UMA_HISTOGRAM_COUNTS. | |
| 76 base::Histogram::FactoryGet( | |
| 77 kMediaEme + KeySystemNameForUMA(key_system) + "." + method, | |
| 78 1, 1000000, 50, base::Histogram::kUmaTargetedHistogramFlag)->Add(sample); | |
| 79 } | |
| 80 | |
| 81 // Helper enum for reporting generateKeyRequest/addKey histograms. | |
| 82 enum MediaKeyException { | |
| 83 kUnknownResultId, | |
| 84 kSuccess, | |
| 85 kKeySystemNotSupported, | |
| 86 kInvalidPlayerState, | |
| 87 kMaxMediaKeyException | |
| 88 }; | |
| 89 | |
| 90 static MediaKeyException MediaKeyExceptionForUMA( | |
| 91 WebMediaPlayer::MediaKeyException e) { | |
| 92 switch (e) { | |
| 93 case WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported: | |
| 94 return kKeySystemNotSupported; | |
| 95 case WebMediaPlayer::MediaKeyExceptionInvalidPlayerState: | |
| 96 return kInvalidPlayerState; | |
| 97 case WebMediaPlayer::MediaKeyExceptionNoError: | |
| 98 return kSuccess; | |
| 99 default: | |
| 100 return kUnknownResultId; | |
| 101 } | |
| 102 } | |
| 103 | |
| 104 // Helper for converting |key_system| name and exception |e| to a pair of enum | |
| 105 // values from above, for reporting to UMA. | |
| 106 static void ReportMediaKeyExceptionToUMA(const std::string& method, | |
| 107 const std::string& key_system, | |
| 108 WebMediaPlayer::MediaKeyException e) { | |
| 109 MediaKeyException result_id = MediaKeyExceptionForUMA(e); | |
| 110 DCHECK_NE(result_id, kUnknownResultId) << e; | |
| 111 EmeUMAHistogramEnumeration( | |
| 112 key_system, method, result_id, kMaxMediaKeyException); | |
| 113 } | |
| 114 | |
| 115 // Guess the type of |init_data|. This is only used to handle some corner cases | |
| 116 // so we keep it as simple as possible without breaking major use cases. | |
| 117 static std::string GuessInitDataType(const unsigned char* init_data, | |
| 118 unsigned init_data_length) { | |
| 119 // Most WebM files use KeyId of 16 bytes. MP4 init data are always >16 bytes. | |
| 120 if (init_data_length == 16) | |
| 121 return "video/webm"; | |
| 122 | |
| 123 return "video/mp4"; | |
| 124 } | |
| 125 | |
| 126 scoped_ptr<EncryptedMediaSupport> EncryptedMediaSupport::create( | |
| 127 blink::WebMediaPlayerClient* client) { | |
| 128 return scoped_ptr<EncryptedMediaSupport>( | |
| 129 new EncryptedMediaSupportImpl(client)); | |
| 130 } | |
| 131 | |
| 132 EncryptedMediaSupportImpl::EncryptedMediaSupportImpl( | |
| 133 blink::WebMediaPlayerClient* client) | |
| 134 : client_(client), | |
| 135 web_cdm_(NULL) { | |
| 136 } | |
| 137 | |
| 138 EncryptedMediaSupportImpl::~EncryptedMediaSupportImpl() { | |
| 139 } | |
| 140 | |
| 141 WebMediaPlayer::MediaKeyException | |
| 142 EncryptedMediaSupportImpl::generateKeyRequest( | |
| 143 blink::WebLocalFrame* frame, | |
| 144 const WebString& key_system, | |
| 145 const unsigned char* init_data, | |
| 146 unsigned init_data_length) { | |
| 147 DVLOG(1) << "generateKeyRequest: " << base::string16(key_system) << ": " | |
| 148 << std::string(reinterpret_cast<const char*>(init_data), | |
| 149 static_cast<size_t>(init_data_length)); | |
| 150 | |
| 151 std::string ascii_key_system = | |
| 152 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system)); | |
| 153 | |
| 154 WebMediaPlayer::MediaKeyException e = | |
| 155 GenerateKeyRequestInternal(frame, ascii_key_system, init_data, | |
| 156 init_data_length); | |
| 157 ReportMediaKeyExceptionToUMA("generateKeyRequest", ascii_key_system, e); | |
| 158 return e; | |
| 159 } | |
| 160 | |
| 161 | |
| 162 WebMediaPlayer::MediaKeyException | |
| 163 EncryptedMediaSupportImpl::GenerateKeyRequestInternal( | |
| 164 blink::WebLocalFrame* frame, | |
| 165 const std::string& key_system, | |
| 166 const unsigned char* init_data, | |
| 167 unsigned init_data_length) { | |
| 168 if (!IsConcreteSupportedKeySystem(key_system)) | |
| 169 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; | |
| 170 | |
| 171 // We do not support run-time switching between key systems for now. | |
| 172 if (current_key_system_.empty()) { | |
| 173 if (!proxy_decryptor_) { | |
| 174 proxy_decryptor_.reset(new ProxyDecryptor( | |
| 175 #if defined(ENABLE_PEPPER_CDMS) | |
| 176 // Create() must be called synchronously as |frame| may not be | |
| 177 // valid afterwards. | |
| 178 base::Bind(&PepperCdmWrapperImpl::Create, frame), | |
| 179 #elif defined(ENABLE_BROWSER_CDMS) | |
| 180 #error Browser side CDM in WMPI for prefixed EME API not supported yet. | |
| 181 #endif | |
| 182 BIND_TO_RENDER_LOOP(&EncryptedMediaSupportImpl::OnKeyAdded), | |
| 183 BIND_TO_RENDER_LOOP(&EncryptedMediaSupportImpl::OnKeyError), | |
| 184 BIND_TO_RENDER_LOOP(&EncryptedMediaSupportImpl::OnKeyMessage))); | |
| 185 } | |
| 186 | |
| 187 GURL security_origin(frame->document().securityOrigin().toString()); | |
| 188 if (!proxy_decryptor_->InitializeCDM(key_system, security_origin)) | |
| 189 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; | |
| 190 | |
| 191 if (proxy_decryptor_ && !decryptor_ready_cb_.is_null()) { | |
| 192 base::ResetAndReturn(&decryptor_ready_cb_) | |
| 193 .Run(proxy_decryptor_->GetDecryptor(), base::Bind(DoNothing)); | |
| 194 } | |
| 195 | |
| 196 current_key_system_ = key_system; | |
| 197 } else if (key_system != current_key_system_) { | |
| 198 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState; | |
| 199 } | |
| 200 | |
| 201 std::string init_data_type = init_data_type_; | |
| 202 if (init_data_type.empty()) | |
| 203 init_data_type = GuessInitDataType(init_data, init_data_length); | |
| 204 | |
| 205 // TODO(xhwang): We assume all streams are from the same container (thus have | |
| 206 // the same "type") for now. In the future, the "type" should be passed down | |
| 207 // from the application. | |
| 208 if (!proxy_decryptor_->GenerateKeyRequest( | |
| 209 init_data_type, init_data, init_data_length)) { | |
| 210 current_key_system_.clear(); | |
| 211 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; | |
| 212 } | |
| 213 | |
| 214 return WebMediaPlayer::MediaKeyExceptionNoError; | |
| 215 } | |
| 216 | |
| 217 WebMediaPlayer::MediaKeyException EncryptedMediaSupportImpl::addKey( | |
| 218 const WebString& key_system, | |
| 219 const unsigned char* key, | |
| 220 unsigned key_length, | |
| 221 const unsigned char* init_data, | |
| 222 unsigned init_data_length, | |
| 223 const WebString& session_id) { | |
| 224 DVLOG(1) << "addKey: " << base::string16(key_system) << ": " | |
| 225 << std::string(reinterpret_cast<const char*>(key), | |
| 226 static_cast<size_t>(key_length)) << ", " | |
| 227 << std::string(reinterpret_cast<const char*>(init_data), | |
| 228 static_cast<size_t>(init_data_length)) << " [" | |
| 229 << base::string16(session_id) << "]"; | |
| 230 | |
| 231 std::string ascii_key_system = | |
| 232 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system)); | |
| 233 std::string ascii_session_id = ToASCIIOrEmpty(session_id); | |
| 234 | |
| 235 WebMediaPlayer::MediaKeyException e = AddKeyInternal(ascii_key_system, | |
| 236 key, | |
| 237 key_length, | |
| 238 init_data, | |
| 239 init_data_length, | |
| 240 ascii_session_id); | |
| 241 ReportMediaKeyExceptionToUMA("addKey", ascii_key_system, e); | |
| 242 return e; | |
| 243 } | |
| 244 | |
| 245 WebMediaPlayer::MediaKeyException EncryptedMediaSupportImpl::AddKeyInternal( | |
| 246 const std::string& key_system, | |
| 247 const unsigned char* key, | |
| 248 unsigned key_length, | |
| 249 const unsigned char* init_data, | |
| 250 unsigned init_data_length, | |
| 251 const std::string& session_id) { | |
| 252 DCHECK(key); | |
| 253 DCHECK_GT(key_length, 0u); | |
| 254 | |
| 255 if (!IsConcreteSupportedKeySystem(key_system)) | |
| 256 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; | |
| 257 | |
| 258 if (current_key_system_.empty() || key_system != current_key_system_) | |
| 259 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState; | |
| 260 | |
| 261 proxy_decryptor_->AddKey( | |
| 262 key, key_length, init_data, init_data_length, session_id); | |
| 263 return WebMediaPlayer::MediaKeyExceptionNoError; | |
| 264 } | |
| 265 | |
| 266 WebMediaPlayer::MediaKeyException EncryptedMediaSupportImpl::cancelKeyRequest( | |
| 267 const WebString& key_system, | |
| 268 const WebString& session_id) { | |
| 269 DVLOG(1) << "cancelKeyRequest: " << base::string16(key_system) << ": " | |
| 270 << " [" << base::string16(session_id) << "]"; | |
| 271 | |
| 272 std::string ascii_key_system = | |
| 273 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system)); | |
| 274 std::string ascii_session_id = ToASCIIOrEmpty(session_id); | |
| 275 | |
| 276 WebMediaPlayer::MediaKeyException e = | |
| 277 CancelKeyRequestInternal(ascii_key_system, ascii_session_id); | |
| 278 ReportMediaKeyExceptionToUMA("cancelKeyRequest", ascii_key_system, e); | |
| 279 return e; | |
| 280 } | |
| 281 | |
| 282 WebMediaPlayer::MediaKeyException | |
| 283 EncryptedMediaSupportImpl::CancelKeyRequestInternal( | |
| 284 const std::string& key_system, | |
| 285 const std::string& session_id) { | |
| 286 if (!IsConcreteSupportedKeySystem(key_system)) | |
| 287 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; | |
| 288 | |
| 289 if (current_key_system_.empty() || key_system != current_key_system_) | |
| 290 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState; | |
| 291 | |
| 292 proxy_decryptor_->CancelKeyRequest(session_id); | |
| 293 return WebMediaPlayer::MediaKeyExceptionNoError; | |
| 294 } | |
| 295 | |
| 296 void EncryptedMediaSupportImpl::setContentDecryptionModule( | |
| 297 blink::WebContentDecryptionModule* cdm) { | |
| 298 // TODO(xhwang): Support setMediaKeys(0) if necessary: http://crbug.com/330324 | |
| 299 if (!cdm) | |
| 300 return; | |
| 301 | |
| 302 web_cdm_ = ToWebContentDecryptionModuleImpl(cdm); | |
| 303 | |
| 304 if (web_cdm_ && !decryptor_ready_cb_.is_null()) | |
| 305 base::ResetAndReturn(&decryptor_ready_cb_) | |
| 306 .Run(web_cdm_->GetDecryptor(), base::Bind(DoNothing)); | |
| 307 } | |
| 308 | |
| 309 void EncryptedMediaSupportImpl::setContentDecryptionModule( | |
| 310 blink::WebContentDecryptionModule* cdm, | |
| 311 blink::WebContentDecryptionModuleResult result) { | |
| 312 // TODO(xhwang): Support setMediaKeys(0) if necessary: http://crbug.com/330324 | |
| 313 if (!cdm) { | |
| 314 result.completeWithError( | |
| 315 blink::WebContentDecryptionModuleExceptionNotSupportedError, | |
| 316 0, | |
| 317 "Null MediaKeys object is not supported."); | |
| 318 return; | |
| 319 } | |
| 320 | |
| 321 web_cdm_ = ToWebContentDecryptionModuleImpl(cdm); | |
| 322 | |
| 323 if (web_cdm_ && !decryptor_ready_cb_.is_null()) { | |
| 324 base::ResetAndReturn(&decryptor_ready_cb_) | |
| 325 .Run(web_cdm_->GetDecryptor(), | |
| 326 BIND_TO_RENDER_LOOP1( | |
| 327 &EncryptedMediaSupportImpl::ContentDecryptionModuleAttached, | |
| 328 result)); | |
| 329 } else { | |
| 330 // No pipeline/decoder connected, so resolve the promise. When something | |
| 331 // is connected, setting the CDM will happen in SetDecryptorReadyCB(). | |
| 332 ContentDecryptionModuleAttached(result, true); | |
| 333 } | |
| 334 } | |
| 335 | |
| 336 void EncryptedMediaSupportImpl::setContentDecryptionModuleSync( | |
| 337 blink::WebContentDecryptionModule* cdm) { | |
| 338 // Used when loading media and no pipeline/decoder attached yet. | |
| 339 DCHECK(decryptor_ready_cb_.is_null()); | |
| 340 | |
| 341 web_cdm_ = ToWebContentDecryptionModuleImpl(cdm); | |
| 342 } | |
| 343 | |
| 344 void EncryptedMediaSupportImpl::ContentDecryptionModuleAttached( | |
| 345 blink::WebContentDecryptionModuleResult result, | |
| 346 bool success) { | |
| 347 if (success) { | |
| 348 result.complete(); | |
| 349 return; | |
| 350 } | |
| 351 | |
| 352 result.completeWithError( | |
| 353 blink::WebContentDecryptionModuleExceptionNotSupportedError, | |
| 354 0, | |
| 355 "Unable to set MediaKeys object"); | |
| 356 } | |
| 357 | |
| 358 media::SetDecryptorReadyCB | |
| 359 EncryptedMediaSupportImpl::CreateSetDecryptorReadyCB() { | |
| 360 return BIND_TO_RENDER_LOOP(&EncryptedMediaSupportImpl::SetDecryptorReadyCB); | |
| 361 } | |
| 362 | |
| 363 media::Demuxer::NeedKeyCB | |
| 364 EncryptedMediaSupportImpl::CreateNeedKeyCB() { | |
| 365 return BIND_TO_RENDER_LOOP(&EncryptedMediaSupportImpl::OnNeedKey); | |
| 366 } | |
| 367 | |
| 368 void EncryptedMediaSupportImpl::OnDecryptError() { | |
| 369 EmeUMAHistogramCounts(current_key_system_, "DecryptError", 1); | |
| 370 } | |
| 371 | |
| 372 void EncryptedMediaSupportImpl::OnNeedKey(const std::string& type, | |
| 373 const std::vector<uint8>& init_data) { | |
| 374 // Do not fire NeedKey event if encrypted media is not enabled. | |
| 375 if (!blink::WebRuntimeFeatures::isPrefixedEncryptedMediaEnabled() && | |
| 376 !blink::WebRuntimeFeatures::isEncryptedMediaEnabled()) { | |
| 377 return; | |
| 378 } | |
| 379 | |
| 380 UMA_HISTOGRAM_COUNTS(kMediaEme + std::string("NeedKey"), 1); | |
| 381 | |
| 382 DCHECK(init_data_type_.empty() || type.empty() || type == init_data_type_); | |
| 383 if (init_data_type_.empty()) | |
| 384 init_data_type_ = type; | |
| 385 | |
| 386 const uint8* init_data_ptr = init_data.empty() ? NULL : &init_data[0]; | |
| 387 client_->keyNeeded( | |
| 388 WebString::fromUTF8(type), init_data_ptr, init_data.size()); | |
| 389 } | |
| 390 | |
| 391 void EncryptedMediaSupportImpl::OnKeyAdded(const std::string& session_id) { | |
| 392 EmeUMAHistogramCounts(current_key_system_, "KeyAdded", 1); | |
| 393 client_->keyAdded( | |
| 394 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)), | |
| 395 WebString::fromUTF8(session_id)); | |
| 396 } | |
| 397 | |
| 398 void EncryptedMediaSupportImpl::OnKeyError(const std::string& session_id, | |
| 399 media::MediaKeys::KeyError error_code, | |
| 400 uint32 system_code) { | |
| 401 EmeUMAHistogramEnumeration(current_key_system_, "KeyError", | |
| 402 error_code, media::MediaKeys::kMaxKeyError); | |
| 403 | |
| 404 uint16 short_system_code = 0; | |
| 405 if (system_code > std::numeric_limits<uint16>::max()) { | |
| 406 LOG(WARNING) << "system_code exceeds unsigned short limit."; | |
| 407 short_system_code = std::numeric_limits<uint16>::max(); | |
| 408 } else { | |
| 409 short_system_code = static_cast<uint16>(system_code); | |
| 410 } | |
| 411 | |
| 412 client_->keyError( | |
| 413 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)), | |
| 414 WebString::fromUTF8(session_id), | |
| 415 static_cast<WebMediaPlayerClient::MediaKeyErrorCode>(error_code), | |
| 416 short_system_code); | |
| 417 } | |
| 418 | |
| 419 void EncryptedMediaSupportImpl::OnKeyMessage(const std::string& session_id, | |
| 420 const std::vector<uint8>& message, | |
| 421 const GURL& destination_url) { | |
| 422 DCHECK(destination_url.is_empty() || destination_url.is_valid()); | |
| 423 | |
| 424 client_->keyMessage( | |
| 425 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)), | |
| 426 WebString::fromUTF8(session_id), | |
| 427 message.empty() ? NULL : &message[0], | |
| 428 message.size(), | |
| 429 destination_url); | |
| 430 } | |
| 431 | |
| 432 void EncryptedMediaSupportImpl::SetDecryptorReadyCB( | |
| 433 const media::DecryptorReadyCB& decryptor_ready_cb) { | |
| 434 // Cancels the previous decryptor request. | |
| 435 if (decryptor_ready_cb.is_null()) { | |
| 436 if (!decryptor_ready_cb_.is_null()) { | |
| 437 base::ResetAndReturn(&decryptor_ready_cb_) | |
| 438 .Run(NULL, base::Bind(DoNothing)); | |
| 439 } | |
| 440 return; | |
| 441 } | |
| 442 | |
| 443 // TODO(xhwang): Support multiple decryptor notification request (e.g. from | |
| 444 // video and audio). The current implementation is okay for the current | |
| 445 // media pipeline since we initialize audio and video decoders in sequence. | |
| 446 // But WebMediaPlayerImpl should not depend on media pipeline's implementation | |
| 447 // detail. | |
| 448 DCHECK(decryptor_ready_cb_.is_null()); | |
| 449 | |
| 450 // Mixed use of prefixed and unprefixed EME APIs is disallowed by Blink. | |
| 451 DCHECK(!proxy_decryptor_ || !web_cdm_); | |
| 452 | |
| 453 if (proxy_decryptor_) { | |
| 454 decryptor_ready_cb.Run(proxy_decryptor_->GetDecryptor(), | |
| 455 base::Bind(DoNothing)); | |
| 456 return; | |
| 457 } | |
| 458 | |
| 459 if (web_cdm_) { | |
| 460 decryptor_ready_cb.Run(web_cdm_->GetDecryptor(), base::Bind(DoNothing)); | |
| 461 return; | |
| 462 } | |
| 463 | |
| 464 decryptor_ready_cb_ = decryptor_ready_cb; | |
| 465 } | |
| 466 | |
| 467 } // namespace content | |
| OLD | NEW |