--- a/third_party/blink/renderer/modules/encryptedmedia/navigator_request_media_key_system_access.cc +++ b/third_party/blink/renderer/modules/encryptedmedia/navigator_request_media_key_system_access.cc @@ -8,19 +8,27 @@ #include #include "base/memory/ptr_util.h" +#include "base/strings/string_number_conversions.h" +#include "media/base/content_decryption_module.h" #include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom-blink.h" +#include "third_party/blink/public/platform/web_content_decryption_module.h" +#include "third_party/blink/public/platform/web_content_decryption_module_access.h" +#include "third_party/blink/public/platform/web_content_decryption_module_session.h" #include "third_party/blink/public/platform/web_encrypted_media_client.h" +#include "third_party/blink/public/platform/web_encrypted_media_key_information.h" #include "third_party/blink/public/platform/web_encrypted_media_request.h" #include "third_party/blink/public/platform/web_media_key_system_configuration.h" #include "third_party/blink/public/platform/web_media_key_system_media_capability.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise.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/dom_exception.h" #include "third_party/blink/renderer/core/execution_context/execution_context.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/core/inspector/console_message.h" #include "third_party/blink/renderer/modules/encryptedmedia/encrypted_media_utils.h" +#include "third_party/blink/renderer/platform/media/managed_drm_profile.h" #include "third_party/blink/renderer/modules/encryptedmedia/media_key_session.h" #include "third_party/blink/renderer/modules/encryptedmedia/media_key_system_access.h" #include "third_party/blink/renderer/modules/encryptedmedia/media_key_system_access_initializer_base.h" @@ -122,6 +130,241 @@ } // namespace +namespace { + +std::vector ConvertManagedInitDataTypes( + const Vector& init_data_types) { + std::vector result(init_data_types.size()); + for (wtf_size_t i = 0; i < init_data_types.size(); ++i) { + result[i] = EncryptedMediaUtils::ConvertToInitDataType(init_data_types[i]); + } + return result; +} + +WebMediaKeySystemMediaCapability::EncryptionScheme +ConvertManagedEncryptionScheme(const String& encryption_scheme) { + if (encryption_scheme == "cenc") + return WebMediaKeySystemMediaCapability::EncryptionScheme::kCenc; + if (encryption_scheme == "cbcs") + return WebMediaKeySystemMediaCapability::EncryptionScheme::kCbcs; + if (encryption_scheme == "cbcs-1-9") + return WebMediaKeySystemMediaCapability::EncryptionScheme::kCbcs_1_9; + return WebMediaKeySystemMediaCapability::EncryptionScheme::kUnrecognized; +} + +std::vector ConvertManagedCapabilities( + const HeapVector>& capabilities) { + std::vector result(capabilities.size()); + for (wtf_size_t i = 0; i < capabilities.size(); ++i) { + const WebString& content_type = capabilities[i]->contentType(); + result[i].content_type = content_type; + ParsedContentType type(content_type); + if (type.IsValid() && !type.GetParameters().HasDuplicatedNames()) { + result[i].mime_type = type.MimeType(); + if (type.GetParameters().ParameterCount() == 1u) + result[i].codecs = type.ParameterValueForName("codecs"); + } + result[i].robustness = capabilities[i]->robustness(); + result[i].encryption_scheme = + (capabilities[i]->hasEncryptionScheme() && + !capabilities[i]->encryptionScheme().IsNull()) + ? ConvertManagedEncryptionScheme(capabilities[i]->encryptionScheme()) + : WebMediaKeySystemMediaCapability::EncryptionScheme::kNotSpecified; + } + return result; +} + +std::vector ConvertManagedSessionTypes( + const Vector& session_types) { + std::vector result(session_types.size()); + for (wtf_size_t i = 0; i < session_types.size(); ++i) + result[i] = EncryptedMediaUtils::ConvertToSessionType(session_types[i]); + return result; +} + +WebMediaKeySystemMediaCapability MakeManagedCapability(const char* content_type, + const char* robustness) { + WebMediaKeySystemMediaCapability capability; + capability.content_type = WebString::FromUTF8(content_type); + ParsedContentType parsed(capability.content_type); + if (parsed.IsValid() && !parsed.GetParameters().HasDuplicatedNames()) { + capability.mime_type = parsed.MimeType(); + if (parsed.GetParameters().ParameterCount() == 1u) + capability.codecs = parsed.ParameterValueForName("codecs"); + } + capability.robustness = WebString::FromUTF8(robustness); + capability.encryption_scheme = + WebMediaKeySystemMediaCapability::EncryptionScheme::kCenc; + return capability; +} + +WebMediaKeySystemConfiguration CreateManagedConfiguration( + const HeapVector>& + supported_configurations, + const ManagedDRMProfile& managed_drm_profile) { + WebMediaKeySystemConfiguration configuration; + + if (!supported_configurations.empty()) { + const MediaKeySystemConfiguration* config = supported_configurations.front(); + if (config->hasInitDataTypes()) + configuration.init_data_types = + ConvertManagedInitDataTypes(config->initDataTypes()); + if (config->hasAudioCapabilities()) + configuration.audio_capabilities = + ConvertManagedCapabilities(config->audioCapabilities()); + if (config->hasVideoCapabilities()) + configuration.video_capabilities = + ConvertManagedCapabilities(config->videoCapabilities()); + if (config->hasDistinctiveIdentifier()) { + configuration.distinctive_identifier = + EncryptedMediaUtils::ConvertToMediaKeysRequirement( + config->distinctiveIdentifier().AsEnum()); + } + if (config->hasPersistentState()) { + configuration.persistent_state = + EncryptedMediaUtils::ConvertToMediaKeysRequirement( + config->persistentState().AsEnum()); + } + if (config->hasSessionTypes()) { + configuration.session_types = + ConvertManagedSessionTypes(config->sessionTypes()); + } + configuration.label = config->label(); + } + + if (configuration.init_data_types.empty()) { + configuration.init_data_types = {media::EmeInitDataType::CENC}; + } + if (configuration.audio_capabilities.empty()) { + configuration.audio_capabilities.push_back(MakeManagedCapability( + managed_drm_profile.audio_content_type, managed_drm_profile.robustness)); + } + if (configuration.video_capabilities.empty()) { + configuration.video_capabilities.push_back(MakeManagedCapability( + managed_drm_profile.video_content_type, managed_drm_profile.robustness)); + } + if (configuration.session_types.empty()) { + configuration.session_types = {WebEncryptedMediaSessionType::kTemporary}; + } + + return configuration; +} + +class ManagedWidevineSession final : public WebContentDecryptionModuleSession { + public: + void SetClientInterface(Client* client) override { client_ = client; } + + WebString SessionId() const override { + return WebString::FromUTF8(session_id_); + } + + void InitializeNewSession(media::EmeInitDataType, + base::span, + WebContentDecryptionModuleResult result) override { + static int next_session_id = 1; + session_id_ = + "cultguard-widevine-session-" + base::NumberToString(next_session_id++); + result.CompleteWithSession(WebContentDecryptionModuleResult::kNewSession); + } + + void Load(const WebString& session_id, + WebContentDecryptionModuleResult result) override { + session_id_ = session_id.Utf8(); + if (session_id_.empty()) { + result.CompleteWithSession( + WebContentDecryptionModuleResult::kSessionNotFound); + return; + } + result.CompleteWithSession(WebContentDecryptionModuleResult::kNewSession); + } + + void Update(base::span, + WebContentDecryptionModuleResult result) override { + result.Complete(); + } + + void Close(WebContentDecryptionModuleResult result) override { + if (!closed_) { + closed_ = true; + if (client_) { + client_->OnSessionClosed(media::CdmSessionClosedReason::kClose); + } + } + result.Complete(); + } + + void Remove(WebContentDecryptionModuleResult result) override { + if (!closed_) { + closed_ = true; + if (client_) { + client_->OnSessionClosed( + media::CdmSessionClosedReason::kReleaseAcknowledged); + } + } + result.Complete(); + } + + private: + Client* client_ = nullptr; + std::string session_id_; + bool closed_ = false; +}; + +class ManagedWidevineContentDecryptionModule final + : public WebContentDecryptionModule { + public: + std::unique_ptr CreateSession( + WebEncryptedMediaSessionType) override { + return std::make_unique(); + } + + void SetServerCertificate(base::span, + WebContentDecryptionModuleResult result) override { + result.Complete(); + } + + void GetStatusForPolicy(const WebString&, + WebContentDecryptionModuleResult result) override { + result.CompleteWithKeyStatus( + WebEncryptedMediaKeyInformation::KeyStatus::kUsable); + } +}; + +class ManagedWidevineAccess final : public WebContentDecryptionModuleAccess { + public: + ManagedWidevineAccess(const WebMediaKeySystemConfiguration& configuration, + const String& requested_key_system) + : configuration_(configuration), + requested_key_system_(requested_key_system.Utf8()) {} + + void CreateContentDecryptionModule( + WebContentDecryptionModuleResult result, + scoped_refptr) override { + result.CompleteWithContentDecryptionModule( + std::make_unique()); + } + + WebMediaKeySystemConfiguration GetConfiguration() override { + return configuration_; + } + + WebString GetRequestedKeySystem() override { + return WebString::FromUTF8(requested_key_system_); + } + + WebString GetInternalKeySystem() override { + return WebString::FromUTF8(requested_key_system_); + } + + bool UseHardwareSecureCodecs() const override { return false; } + + private: + WebMediaKeySystemConfiguration configuration_; + std::string requested_key_system_; +}; + +} // namespace + ScriptPromise NavigatorRequestMediaKeySystemAccess::requestMediaKeySystemAccess( ScriptState* script_state, @@ -184,10 +427,22 @@ auto* resolver = MakeGarbageCollected>( script_state); + auto promise = resolver->Promise(); + + if (const ManagedDRMProfile* managed_drm_profile = GetManagedDRMProfile(); + managed_drm_profile && + key_system == managed_drm_profile->key_system) { + resolver->Resolve(MakeGarbageCollected( + std::make_unique( + CreateManagedConfiguration(supported_configurations, + *managed_drm_profile), + key_system))); + return promise; + } + MediaKeySystemAccessInitializer* initializer = MakeGarbageCollected( window, resolver, key_system, supported_configurations); - auto promise = resolver->Promise(); // Defer to determine support until the prerendering page is activated. if (window->document()->IsPrerendering()) { --- a/third_party/blink/renderer/platform/media/managed_drm_profile.h +++ b/third_party/blink/renderer/platform/media/managed_drm_profile.h @@ -0,0 +1,58 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIA_MANAGED_DRM_PROFILE_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIA_MANAGED_DRM_PROFILE_H_ + +#include +#include + +#include "base/environment.h" +#include "base/strings/string_util.h" + +namespace blink { + +struct ManagedDRMProfile { + const char* key_system; + const char* audio_content_type; + const char* video_content_type; + const char* robustness; +}; + +inline const ManagedDRMProfile* GetManagedDRMProfile() { + static const std::optional profile = [] { + auto environment = base::Environment::Create(); + std::optional selector = + environment->GetVar("BASTION_CHROMIUM_DRM_PROFILE"); + if (!selector) { + return std::optional{}; + } + + std::string selector_value = *selector; + base::TrimWhitespaceASCII(selector_value, base::TRIM_ALL, &selector_value); + if (selector_value.empty() || selector_value == "off") { + return std::optional{}; + } + + if (selector_value == "widevine" || selector_value == "widevine-alpha") { + return std::optional{ManagedDRMProfile{ + "com.widevine.alpha", + "audio/mp4; codecs=\"mp4a.40.2\"", + "video/mp4; codecs=\"avc1.42E01E\"", + "SW_SECURE_DECODE", + }}; + } + + return std::optional{}; + }(); + + if (!profile.has_value()) { + return nullptr; + } + return &profile.value(); +} + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIA_MANAGED_DRM_PROFILE_H_