blob: 3ff12c560f673ef35e24e969c32374615713ce51 [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 "third_party/blink/renderer/modules/encryptedmedia/media_key_system_access.h"
#include <memory>
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "media/base/eme_constants.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "third_party/blink/public/platform/web_content_decryption_module.h"
#include "third_party/blink/public/platform/web_encrypted_media_types.h"
#include "third_party/blink/public/platform/web_media_key_system_configuration.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_media_key_system_media_capability.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/modules/encryptedmedia/content_decryption_module_result_promise.h"
#include "third_party/blink/renderer/modules/encryptedmedia/encrypted_media_utils.h"
#include "third_party/blink/renderer/modules/encryptedmedia/media_key_session.h"
#include "third_party/blink/renderer/modules/encryptedmedia/media_keys.h"
#include "third_party/blink/renderer/modules/encryptedmedia/media_keys_controller.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "third_party/blink/renderer/platform/timer.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
namespace blink {
namespace {
// This class wraps the promise resolver used when creating MediaKeys
// and is passed to Chromium to fullfill the promise. This implementation of
// completeWithCdm() will resolve the promise with a new MediaKeys object,
// while completeWithError() will reject the promise with an exception.
// All other complete methods are not expected to be called, and will
// reject the promise.
class NewCdmResultPromise : public ContentDecryptionModuleResultPromise {
public:
NewCdmResultPromise(
ScriptState* script_state,
const WebVector<WebEncryptedMediaSessionType>& supported_session_types,
EmeApiType type)
: ContentDecryptionModuleResultPromise(script_state, type),
supported_session_types_(supported_session_types) {}
~NewCdmResultPromise() override = default;
// ContentDecryptionModuleResult implementation.
void CompleteWithContentDecryptionModule(
WebContentDecryptionModule* cdm) override {
// NOTE: Continued from step 2.8 of createMediaKeys().
if (!IsValidToFulfillPromise())
return;
// 2.9. Let media keys be a new MediaKeys object.
auto* media_keys = MakeGarbageCollected<MediaKeys>(
GetExecutionContext(), supported_session_types_, base::WrapUnique(cdm));
// 2.10. Resolve promise with media keys.
Resolve(media_keys);
}
private:
WebVector<WebEncryptedMediaSessionType> supported_session_types_;
DISALLOW_COPY_AND_ASSIGN(NewCdmResultPromise);
};
// These methods are the inverses of those with the same names in
// NavigatorRequestMediaKeySystemAccess.
Vector<String> ConvertInitDataTypes(
const WebVector<media::EmeInitDataType>& init_data_types) {
Vector<String> result(SafeCast<wtf_size_t>(init_data_types.size()));
for (wtf_size_t i = 0; i < result.size(); i++)
result[i] =
EncryptedMediaUtils::ConvertFromInitDataType(init_data_types[i]);
return result;
}
HeapVector<Member<MediaKeySystemMediaCapability>> ConvertCapabilities(
const WebVector<WebMediaKeySystemMediaCapability>& capabilities) {
HeapVector<Member<MediaKeySystemMediaCapability>> result(
SafeCast<wtf_size_t>(capabilities.size()));
for (wtf_size_t i = 0; i < result.size(); i++) {
MediaKeySystemMediaCapability* capability =
MediaKeySystemMediaCapability::Create();
capability->setContentType(capabilities[i].content_type);
capability->setRobustness(capabilities[i].robustness);
switch (capabilities[i].encryption_scheme) {
case WebMediaKeySystemMediaCapability::EncryptionScheme::kNotSpecified:
// https://w3c.github.io/encrypted-media/#dom-mediakeysystemaccess-getconfiguration
// "If encryptionScheme was not given by the application, the
// accumulated configuration MUST still contain a encryptionScheme
// field with a value of null, so that polyfills can detect the user
// agent's support for the field without specifying specific values."
capability->setEncryptionScheme(String());
break;
case WebMediaKeySystemMediaCapability::EncryptionScheme::kCenc:
capability->setEncryptionScheme("cenc");
break;
case WebMediaKeySystemMediaCapability::EncryptionScheme::kCbcs:
capability->setEncryptionScheme("cbcs");
break;
case WebMediaKeySystemMediaCapability::EncryptionScheme::kCbcs_1_9:
capability->setEncryptionScheme("cbcs-1-9");
break;
case WebMediaKeySystemMediaCapability::EncryptionScheme::kUnrecognized:
NOTREACHED()
<< "Unrecognized encryption scheme should never be returned.";
break;
}
result[i] = capability;
}
return result;
}
Vector<String> ConvertSessionTypes(
const WebVector<WebEncryptedMediaSessionType>& session_types) {
Vector<String> result(SafeCast<wtf_size_t>(session_types.size()));
for (wtf_size_t i = 0; i < result.size(); i++)
result[i] = EncryptedMediaUtils::ConvertFromSessionType(session_types[i]);
return result;
}
void ReportMetrics(ExecutionContext* execution_context,
const String& key_system) {
// TODO(xhwang): Report other key systems here and for
// requestMediaKeySystemAccess().
const char kWidevineKeySystem[] = "com.widevine.alpha";
if (key_system != kWidevineKeySystem)
return;
auto* local_dom_window = To<LocalDOMWindow>(execution_context);
if (!local_dom_window)
return;
Document* document = local_dom_window->document();
if (!document)
return;
LocalFrame* frame = document->GetFrame();
if (!frame)
return;
ukm::builders::Media_EME_CreateMediaKeys builder(document->UkmSourceID());
builder.SetKeySystem(KeySystemForUkm::kWidevine);
builder.SetIsAdFrame(static_cast<int>(frame->IsAdSubframe()));
builder.SetIsCrossOrigin(static_cast<int>(frame->IsCrossOriginToMainFrame()));
builder.SetIsTopFrame(static_cast<int>(frame->IsMainFrame()));
builder.Record(document->UkmRecorder());
}
} // namespace
MediaKeySystemAccess::MediaKeySystemAccess(
const String& key_system,
std::unique_ptr<WebContentDecryptionModuleAccess> access)
: key_system_(key_system), access_(std::move(access)) {}
MediaKeySystemAccess::~MediaKeySystemAccess() = default;
MediaKeySystemConfiguration* MediaKeySystemAccess::getConfiguration() const {
WebMediaKeySystemConfiguration configuration = access_->GetConfiguration();
MediaKeySystemConfiguration* result = MediaKeySystemConfiguration::Create();
// |initDataTypes|, |audioCapabilities|, and |videoCapabilities| can only be
// empty if they were not present in the requested configuration.
if (!configuration.init_data_types.empty())
result->setInitDataTypes(
ConvertInitDataTypes(configuration.init_data_types));
if (!configuration.audio_capabilities.empty())
result->setAudioCapabilities(
ConvertCapabilities(configuration.audio_capabilities));
if (!configuration.video_capabilities.empty())
result->setVideoCapabilities(
ConvertCapabilities(configuration.video_capabilities));
// |distinctiveIdentifier|, |persistentState|, and |sessionTypes| are always
// set by requestMediaKeySystemAccess().
result->setDistinctiveIdentifier(
EncryptedMediaUtils::ConvertMediaKeysRequirementToString(
configuration.distinctive_identifier));
result->setPersistentState(
EncryptedMediaUtils::ConvertMediaKeysRequirementToString(
configuration.persistent_state));
result->setSessionTypes(ConvertSessionTypes(configuration.session_types));
// |label| will (and should) be a null string if it was not set.
result->setLabel(configuration.label);
return result;
}
ScriptPromise MediaKeySystemAccess::createMediaKeys(ScriptState* script_state) {
// From http://w3c.github.io/encrypted-media/#createMediaKeys
// (Reordered to be able to pass values into the promise constructor.)
// 2.4 Let configuration be the value of this object's configuration value.
// 2.5-2.8. [Set use distinctive identifier and persistent state allowed
// based on configuration.]
WebMediaKeySystemConfiguration configuration = access_->GetConfiguration();
// 1. Let promise be a new promise.
NewCdmResultPromise* helper = MakeGarbageCollected<NewCdmResultPromise>(
script_state, configuration.session_types, EmeApiType::kCreateMediaKeys);
ScriptPromise promise = helper->Promise();
// 2. Asynchronously create and initialize the MediaKeys object.
// 2.1 Let cdm be the CDM corresponding to this object.
// 2.2 Load and initialize the cdm if necessary.
// 2.3 If cdm fails to load or initialize, reject promise with a new
// DOMException whose name is the appropriate error name.
// (Done if completeWithException() called).
auto* execution_context = ExecutionContext::From(script_state);
access_->CreateContentDecryptionModule(
helper->Result(),
execution_context->GetTaskRunner(TaskType::kInternalMedia));
ReportMetrics(execution_context, key_system_);
// 3. Return promise.
return promise;
}
} // namespace blink