blob: 6b3e8949884f521f8a29d7e3eed8eb2999852fda [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/blink/webencryptedmediaclient_impl.h"
#include <utility>
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "media/base/key_systems.h"
#include "media/base/media_client.h"
#include "media/base/media_permission.h"
#include "media/blink/webcontentdecryptionmodule_impl.h"
#include "media/blink/webcontentdecryptionmoduleaccess_impl.h"
#include "third_party/WebKit/public/platform/URLConversion.h"
#include "third_party/WebKit/public/platform/WebContentDecryptionModuleResult.h"
#include "third_party/WebKit/public/platform/WebEncryptedMediaRequest.h"
#include "third_party/WebKit/public/platform/WebMediaKeySystemConfiguration.h"
#include "third_party/WebKit/public/platform/WebSecurityOrigin.h"
#include "third_party/WebKit/public/platform/WebString.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace media {
namespace {
// Used to name UMAs in Reporter.
const char kKeySystemSupportUMAPrefix[] =
"Media.EME.RequestMediaKeySystemAccess.";
} // namespace
// Report usage of key system to UMA. There are 2 different counts logged:
// 1. The key system is requested.
// 2. The requested key system and options are supported.
// Each stat is only reported once per renderer frame per key system.
// Note that WebEncryptedMediaClientImpl is only created once by each
// renderer frame.
class WebEncryptedMediaClientImpl::Reporter {
public:
enum KeySystemSupportStatus {
KEY_SYSTEM_REQUESTED = 0,
KEY_SYSTEM_SUPPORTED = 1,
KEY_SYSTEM_SUPPORT_STATUS_COUNT
};
explicit Reporter(const std::string& key_system_for_uma)
: uma_name_(kKeySystemSupportUMAPrefix + key_system_for_uma),
is_request_reported_(false),
is_support_reported_(false) {}
~Reporter() {}
void ReportRequested() {
if (is_request_reported_)
return;
Report(KEY_SYSTEM_REQUESTED);
is_request_reported_ = true;
}
void ReportSupported() {
DCHECK(is_request_reported_);
if (is_support_reported_)
return;
Report(KEY_SYSTEM_SUPPORTED);
is_support_reported_ = true;
}
private:
void Report(KeySystemSupportStatus status) {
// Not using UMA_HISTOGRAM_ENUMERATION directly because UMA_* macros
// require the names to be constant throughout the process' lifetime.
base::LinearHistogram::FactoryGet(
uma_name_, 1, KEY_SYSTEM_SUPPORT_STATUS_COUNT,
KEY_SYSTEM_SUPPORT_STATUS_COUNT + 1,
base::Histogram::kUmaTargetedHistogramFlag)->Add(status);
}
const std::string uma_name_;
bool is_request_reported_;
bool is_support_reported_;
};
WebEncryptedMediaClientImpl::WebEncryptedMediaClientImpl(
base::Callback<bool(void)> are_secure_codecs_supported_cb,
CdmFactory* cdm_factory,
MediaPermission* media_permission)
: are_secure_codecs_supported_cb_(are_secure_codecs_supported_cb),
cdm_factory_(cdm_factory),
key_system_config_selector_(KeySystems::GetInstance(), media_permission),
weak_factory_(this) {
DCHECK(cdm_factory_);
}
WebEncryptedMediaClientImpl::~WebEncryptedMediaClientImpl() {
}
void WebEncryptedMediaClientImpl::requestMediaKeySystemAccess(
blink::WebEncryptedMediaRequest request) {
GetReporter(request.keySystem())->ReportRequested();
if (GetMediaClient()) {
GURL security_origin(url::Origin(request.getSecurityOrigin()).GetURL());
GetMediaClient()->RecordRapporURL("Media.OriginUrl.EME", security_origin);
if (!request.getSecurityOrigin().isPotentiallyTrustworthy()) {
GetMediaClient()->RecordRapporURL("Media.OriginUrl.EME.Insecure",
security_origin);
}
}
key_system_config_selector_.SelectConfig(
request.keySystem(), request.supportedConfigurations(),
request.getSecurityOrigin(), are_secure_codecs_supported_cb_.Run(),
base::Bind(&WebEncryptedMediaClientImpl::OnRequestSucceeded,
weak_factory_.GetWeakPtr(), request),
base::Bind(&WebEncryptedMediaClientImpl::OnRequestNotSupported,
weak_factory_.GetWeakPtr(), request));
}
void WebEncryptedMediaClientImpl::CreateCdm(
const blink::WebString& key_system,
const blink::WebSecurityOrigin& security_origin,
const CdmConfig& cdm_config,
std::unique_ptr<blink::WebContentDecryptionModuleResult> result) {
WebContentDecryptionModuleImpl::Create(
cdm_factory_, key_system, security_origin, cdm_config, std::move(result));
}
void WebEncryptedMediaClientImpl::OnRequestSucceeded(
blink::WebEncryptedMediaRequest request,
const blink::WebMediaKeySystemConfiguration& accumulated_configuration,
const CdmConfig& cdm_config) {
GetReporter(request.keySystem())->ReportSupported();
// TODO(sandersd): Pass |are_secure_codecs_required| along and use it to
// configure the CDM security level and use of secure surfaces on Android.
// If the frame is closed while the permission prompt is displayed,
// the permission prompt is dismissed and this may result in the
// requestMediaKeySystemAccess request succeeding. However, the blink
// objects may have been cleared, so check if this is the case and simply
// reject the request.
blink::WebSecurityOrigin origin = request.getSecurityOrigin();
if (origin.isNull()) {
request.requestNotSupported("Unable to create MediaKeySystemAccess");
return;
}
request.requestSucceeded(WebContentDecryptionModuleAccessImpl::Create(
request.keySystem(), origin, accumulated_configuration, cdm_config,
weak_factory_.GetWeakPtr()));
}
void WebEncryptedMediaClientImpl::OnRequestNotSupported(
blink::WebEncryptedMediaRequest request,
const blink::WebString& error_message) {
request.requestNotSupported(error_message);
}
WebEncryptedMediaClientImpl::Reporter* WebEncryptedMediaClientImpl::GetReporter(
const blink::WebString& key_system) {
// Assumes that empty will not be found by GetKeySystemNameForUMA().
// TODO(sandersd): Avoid doing ASCII conversion more than once.
std::string key_system_ascii;
if (key_system.containsOnlyASCII())
key_system_ascii = key_system.ascii();
// Return a per-frame singleton so that UMA reports will be once-per-frame.
std::string uma_name = GetKeySystemNameForUMA(key_system_ascii);
Reporter* reporter = reporters_.get(uma_name);
if (!reporter) {
reporter = new Reporter(uma_name);
reporters_.add(uma_name, base::WrapUnique(reporter));
}
return reporter;
}
} // namespace media