diff options
Diffstat (limited to 'Source/WebCore/Modules/encryptedmedia/CDM.cpp')
-rw-r--r-- | Source/WebCore/Modules/encryptedmedia/CDM.cpp | 663 |
1 files changed, 663 insertions, 0 deletions
diff --git a/Source/WebCore/Modules/encryptedmedia/CDM.cpp b/Source/WebCore/Modules/encryptedmedia/CDM.cpp new file mode 100644 index 000000000..7588b0628 --- /dev/null +++ b/Source/WebCore/Modules/encryptedmedia/CDM.cpp @@ -0,0 +1,663 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "CDM.h" + +#if ENABLE(ENCRYPTED_MEDIA) + +#include "CDMPrivate.h" +#include "Document.h" +#include "InitDataRegistry.h" +#include "MediaKeysRestrictions.h" +#include "MediaPlayer.h" +#include "NotImplemented.h" +#include "ParsedContentType.h" +#include "ScriptExecutionContext.h" +#include "SecurityOrigin.h" +#include <wtf/NeverDestroyed.h> + +namespace WebCore { + +static Vector<CDMFactory*>& cdmFactories() +{ + static NeverDestroyed<Vector<CDMFactory*>> factories; + return factories; +} + +static std::unique_ptr<CDMPrivate> createCDMPrivateForKeySystem(const String& keySystem, CDM& cdm) +{ + for (auto* factory : cdmFactories()) { + if (factory->supportsKeySystem(keySystem)) + return factory->createCDM(cdm); + } + ASSERT_NOT_REACHED(); + return nullptr; +} + +void CDM::registerCDMFactory(CDMFactory& factory) +{ + ASSERT(!cdmFactories().contains(&factory)); + cdmFactories().append(&factory); +} + +void CDM::unregisterCDMFactory(CDMFactory& factory) +{ + ASSERT(cdmFactories().contains(&factory)); + cdmFactories().removeAll(&factory); +} + +bool CDM::supportsKeySystem(const String& keySystem) +{ + for (auto* factory : cdmFactories()) { + if (factory->supportsKeySystem(keySystem)) + return true; + } + return false; +} + +Ref<CDM> CDM::create(Document& document, const String& keySystem) +{ + return adoptRef(*new CDM(document, keySystem)); +} + +CDM::CDM(Document& document, const String& keySystem) + : ContextDestructionObserver(&document) + , m_keySystem(keySystem) + , m_private(createCDMPrivateForKeySystem(keySystem, *this)) + , m_weakPtrFactory(this) +{ + ASSERT(supportsKeySystem(keySystem)); + for (auto* factory : cdmFactories()) { + if (!factory->supportsKeySystem(keySystem)) + continue; + m_private = factory->createCDM(*this); + } +} + +CDM::~CDM() = default; + +void CDM::getSupportedConfiguration(MediaKeySystemConfiguration&& candidateConfiguration, SupportedConfigurationCallback&& callback) +{ + // https://w3c.github.io/encrypted-media/#get-supported-configuration + // W3C Editor's Draft 09 November 2016 + + // 3.1.1.1 Get Supported Configuration + // Given a Key Systems implementation implementation, MediaKeySystemConfiguration candidate configuration, and origin, + // this algorithm returns a supported configuration or NotSupported as appropriate. + + // 1. Let supported configuration be ConsentDenied. + // 2. Initialize restrictions to indicate that no configurations have had user consent denied. + MediaKeysRestrictions restrictions { }; + doSupportedConfigurationStep(WTFMove(candidateConfiguration), WTFMove(restrictions), WTFMove(callback)); +} + +void CDM::doSupportedConfigurationStep(MediaKeySystemConfiguration&& candidateConfiguration, MediaKeysRestrictions&& restrictions, SupportedConfigurationCallback&& callback) +{ + // https://w3c.github.io/encrypted-media/#get-supported-configuration + // W3C Editor's Draft 09 November 2016, ctd. + + // 3.1.1.1 Get Supported Configuration + // 3. Repeat the following step while supported configuration is ConsentDenied: + // 3.1. Let supported configuration and, if provided, restrictions be the result of executing the + // Get Supported Configuration and Consent algorithm with implementation, candidate configuration, + // restrictions and origin. + auto optionalConfiguration = getSupportedConfiguration(candidateConfiguration, restrictions); + if (!optionalConfiguration) { + callback(std::nullopt); + return; + } + + auto consentCallback = [weakThis = createWeakPtr(), callback = WTFMove(callback)] (ConsentStatus status, MediaKeySystemConfiguration&& configuration, MediaKeysRestrictions&& restrictions) mutable { + if (!weakThis) { + callback(std::nullopt); + return; + } + // 3.1.1.2 Get Supported Configuration and Consent, ctd. + // 22. Let consent status and updated restrictions be the result of running the Get Consent Status algorithm on accumulated configuration, + // restrictions and origin and follow the steps for the value of consent status from the following list: + switch (status) { + case ConsentStatus::ConsentDenied: + // ↳ ConsentDenied: + // Return ConsentDenied and updated restrictions. + weakThis->doSupportedConfigurationStep(WTFMove(configuration), WTFMove(restrictions), WTFMove(callback)); + return; + + case ConsentStatus::InformUser: + // ↳ InformUser + // Inform the user that accumulated configuration is in use in the origin including, specifically, the information that + // Distinctive Identifier(s) and/or Distinctive Permanent Identifier(s) as appropriate will be used if the + // distinctiveIdentifier member of accumulated configuration is "required". Continue to the next step. + // NOTE: Implement. + break; + + case ConsentStatus::Allowed: + // ↳ Allowed: + // Continue to the next step. + break; + } + // 23. Return accumulated configuration. + callback(WTFMove(configuration)); + }; + getConsentStatus(WTFMove(optionalConfiguration.value()), WTFMove(restrictions), consentCallback); +} + +bool CDM::isPersistentType(MediaKeySessionType sessionType) +{ + // https://w3c.github.io/encrypted-media/#is-persistent-session-type + // W3C Editor's Draft 09 November 2016 + + // 5.1.1. Is persistent session type? + // 1. Let the session type be the specified MediaKeySessionType value. + // 2. Follow the steps for the value of session type from the following list: + switch (sessionType) { + case MediaKeySessionType::Temporary: + // ↳ "temporary" + return false; + case MediaKeySessionType::PersistentLicense: + case MediaKeySessionType::PersistentUsageRecord: + // ↳ "persistent-license" + return true; + } +} + +std::optional<MediaKeySystemConfiguration> CDM::getSupportedConfiguration(const MediaKeySystemConfiguration& candidateConfiguration, MediaKeysRestrictions& restrictions) +{ + // https://w3c.github.io/encrypted-media/#get-supported-configuration-and-consent + // W3C Editor's Draft 09 November 2016 + + ASSERT(m_private); + if (!m_private) + return std::nullopt; + + // 3.1.1.2 Get Supported Configuration and Consent + // Given a Key Systems implementation implementation, MediaKeySystemConfiguration candidate configuration, + // restrictions and origin, this algorithm returns a supported configuration, NotSupported, or ConsentDenied + // as appropriate and, in the ConsentDenied case, restrictions. + + // 1. Let accumulated configuration be a new MediaKeySystemConfiguration dictionary. + MediaKeySystemConfiguration accumulatedConfiguration { }; + + // 2. Set the label member of accumulated configuration to equal the label member of candidate configuration. + accumulatedConfiguration.label = candidateConfiguration.label; + + // 3. If the initDataTypes member of candidate configuration is non-empty, run the following steps: + if (!candidateConfiguration.initDataTypes.isEmpty()) { + // 3.1. Let supported types be an empty sequence of DOMStrings. + Vector<String> supportedTypes; + + // 3.2. For each value in candidate configuration's initDataTypes member: + for (auto initDataType : candidateConfiguration.initDataTypes) { + // 3.2.1. Let initDataType be the value. + // 3.2.2. If the implementation supports generating requests based on initDataType, add initDataType + // to supported types. String comparison is case-sensitive. The empty string is never supported. + if (initDataType.isEmpty()) + continue; + + if (m_private && m_private->supportsInitDataType(initDataType)) + supportedTypes.append(initDataType); + } + + // 3.3. If supported types is empty, return NotSupported. + if (supportedTypes.isEmpty()) + return std::nullopt; + + // 3.4. Set the initDataTypes member of accumulated configuration to supported types. + accumulatedConfiguration.initDataTypes = WTFMove(supportedTypes); + } + + // 4. Let distinctive identifier requirement be the value of candidate configuration's distinctiveIdentifier member. + MediaKeysRequirement distinctiveIdentifierRequirement = candidateConfiguration.distinctiveIdentifier; + + // 5. If distinctive identifier requirement is "optional" and Distinctive Identifiers are not allowed according to + // restrictions, set distinctive identifier requirement to "not-allowed". + if (distinctiveIdentifierRequirement == MediaKeysRequirement::Optional && restrictions.distinctiveIdentifierDenied) + distinctiveIdentifierRequirement = MediaKeysRequirement::NotAllowed; + + // 6. Follow the steps for distinctive identifier requirement from the following list: + switch (distinctiveIdentifierRequirement) { + case MediaKeysRequirement::Required: + // ↳ "required" + // If the implementation does not support use of Distinctive Identifier(s) in combination + // with accumulated configuration and restrictions, return NotSupported. + if (m_private->distinctiveIdentifiersRequirement(accumulatedConfiguration, restrictions) == MediaKeysRequirement::NotAllowed) + return std::nullopt; + break; + + case MediaKeysRequirement::Optional: + // ↳ "optional" + // Continue with the following steps. + break; + + case MediaKeysRequirement::NotAllowed: + // ↳ "not-allowed" + // If the implementation requires use Distinctive Identifier(s) or Distinctive Permanent Identifier(s) + // in combination with accumulated configuration and restrictions, return NotSupported. + if (m_private->distinctiveIdentifiersRequirement(accumulatedConfiguration, restrictions) == MediaKeysRequirement::Required) + return std::nullopt; + break; + } + + // 7. Set the distinctiveIdentifier member of accumulated configuration to equal distinctive identifier requirement. + accumulatedConfiguration.distinctiveIdentifier = distinctiveIdentifierRequirement; + + // 8. Let persistent state requirement be equal to the value of candidate configuration's persistentState member. + MediaKeysRequirement persistentStateRequirement = candidateConfiguration.persistentState; + + // 9. If persistent state requirement is "optional" and persisting state is not allowed according to restrictions, + // set persistent state requirement to "not-allowed". + if (persistentStateRequirement == MediaKeysRequirement::Optional && restrictions.persistentStateDenied) + persistentStateRequirement = MediaKeysRequirement::NotAllowed; + + // 10. Follow the steps for persistent state requirement from the following list: + switch (persistentStateRequirement) { + case MediaKeysRequirement::Required: + // ↳ "required" + // If the implementation does not support persisting state in combination with accumulated configuration + // and restrictions, return NotSupported. + if (m_private->persistentStateRequirement(accumulatedConfiguration, restrictions) == MediaKeysRequirement::NotAllowed) + return std::nullopt; + break; + + case MediaKeysRequirement::Optional: + // ↳ "optional" + // Continue with the following steps. + break; + + case MediaKeysRequirement::NotAllowed: + // ↳ "not-allowed" + // If the implementation requires persisting state in combination with accumulated configuration + // and restrictions, return NotSupported + if (m_private->persistentStateRequirement(accumulatedConfiguration, restrictions) == MediaKeysRequirement::Required) + return std::nullopt; + break; + } + + // 11. Set the persistentState member of accumulated configuration to equal the value of persistent state requirement. + accumulatedConfiguration.persistentState = persistentStateRequirement; + + // 12. Follow the steps for the first matching condition from the following list: + Vector<MediaKeySessionType> sessionTypes; + + if (!candidateConfiguration.sessionTypes.isEmpty()) { + // ↳ If the sessionTypes member is present [WebIDL] in candidate configuration + // Let session types be candidate configuration's sessionTypes member. + sessionTypes = candidateConfiguration.sessionTypes; + } else { + // ↳ Otherwise + // Let session types be [ "temporary" ]. + sessionTypes = { MediaKeySessionType::Temporary }; + } + + // 13. For each value in session types: + for (auto& sessionType : sessionTypes) { + // 13.1. Let session type be the value. + // 13.2. If accumulated configuration's persistentState value is "not-allowed" and the + // Is persistent session type? algorithm returns true for session type return NotSupported. + if (accumulatedConfiguration.persistentState == MediaKeysRequirement::NotAllowed && isPersistentType(sessionType)) + return std::nullopt; + + // 13.3. If the implementation does not support session type in combination with accumulated configuration + // and restrictions for other reasons, return NotSupported. + if (!m_private->supportsSessionTypeWithConfiguration(sessionType, accumulatedConfiguration)) + return std::nullopt; + + // 13.4 If accumulated configuration's persistentState value is "optional" and the result of running the Is + // persistent session type? algorithm on session type is true, change accumulated configuration's persistentState + // value to "required". + if (accumulatedConfiguration.persistentState == MediaKeysRequirement::Optional && isPersistentType(sessionType)) + accumulatedConfiguration.persistentState = MediaKeysRequirement::Required; + } + + // 14. Set the sessionTypes member of accumulated configuration to session types. + accumulatedConfiguration.sessionTypes = sessionTypes; + + // 15. If the videoCapabilities and audioCapabilities members in candidate configuration are both empty, return NotSupported. + if (candidateConfiguration.videoCapabilities.isEmpty() && candidateConfiguration.audioCapabilities.isEmpty()) + return std::nullopt; + + // 16. ↳ If the videoCapabilities member in candidate configuration is non-empty: + if (!candidateConfiguration.videoCapabilities.isEmpty()) { + // 16.1. Let video capabilities be the result of executing the Get Supported Capabilities for Audio/Video Type algorithm on + // Video, candidate configuration's videoCapabilities member, accumulated configuration, and restrictions. + auto videoCapabilities = getSupportedCapabilitiesForAudioVideoType(AudioVideoType::Video, candidateConfiguration.videoCapabilities, accumulatedConfiguration, restrictions); + + // 16.2. If video capabilities is null, return NotSupported. + if (!videoCapabilities) + return std::nullopt; + + // 16.3 Set the videoCapabilities member of accumulated configuration to video capabilities. + accumulatedConfiguration.videoCapabilities = WTFMove(videoCapabilities.value()); + } else { + // 16. ↳ Otherwise: + // Set the videoCapabilities member of accumulated configuration to an empty sequence. + accumulatedConfiguration.videoCapabilities = { }; + } + + // 17. ↳ If the audioCapabilities member in candidate configuration is non-empty: + if (!candidateConfiguration.audioCapabilities.isEmpty()) { + // 17.1. Let audio capabilities be the result of executing the Get Supported Capabilities for Audio/Video Type algorithm on + // Audio, candidate configuration's audioCapabilities member, accumulated configuration, and restrictions. + auto audioCapabilities = getSupportedCapabilitiesForAudioVideoType(AudioVideoType::Audio, candidateConfiguration.audioCapabilities, accumulatedConfiguration, restrictions); + + // 17.2. If audio capabilities is null, return NotSupported. + if (!audioCapabilities) + return std::nullopt; + + // 17.3 Set the audioCapabilities member of accumulated configuration to audio capabilities. + accumulatedConfiguration.audioCapabilities = WTFMove(audioCapabilities.value()); + } else { + // 17. ↳ Otherwise: + // Set the audioCapabilities member of accumulated configuration to an empty sequence. + accumulatedConfiguration.audioCapabilities = { }; + } + + // 18. If accumulated configuration's distinctiveIdentifier value is "optional", follow the steps for the first matching + // condition from the following list: + if (accumulatedConfiguration.distinctiveIdentifier == MediaKeysRequirement::Optional) { + // ↳ If the implementation requires use Distinctive Identifier(s) or Distinctive Permanent Identifier(s) for any of the + // combinations in accumulated configuration + if (m_private->distinctiveIdentifiersRequirement(accumulatedConfiguration, restrictions) == MediaKeysRequirement::Required) { + // Change accumulated configuration's distinctiveIdentifier value to "required". + accumulatedConfiguration.distinctiveIdentifier = MediaKeysRequirement::Required; + } else { + // ↳ Otherwise + // Change accumulated configuration's distinctiveIdentifier value to "not-allowed". + accumulatedConfiguration.distinctiveIdentifier = MediaKeysRequirement::NotAllowed; + } + } + + // 19. If accumulated configuration's persistentState value is "optional", follow the steps for the first matching + // condition from the following list: + if (accumulatedConfiguration.persistentState == MediaKeysRequirement::Optional) { + // ↳ If the implementation requires persisting state for any of the combinations in accumulated configuration + if (m_private->persistentStateRequirement(accumulatedConfiguration, restrictions) == MediaKeysRequirement::Required) { + // Change accumulated configuration's persistentState value to "required". + accumulatedConfiguration.persistentState = MediaKeysRequirement::Required; + } else { + // ↳ Otherwise + // Change accumulated configuration's persistentState value to "not-allowed". + accumulatedConfiguration.persistentState = MediaKeysRequirement::NotAllowed; + } + } + + // 20. If implementation in the configuration specified by the combination of the values in accumulated configuration + // is not supported or not allowed in the origin, return NotSupported. + if (!m_private->supportsConfiguration(accumulatedConfiguration)) + return std::nullopt; + + Document* document = downcast<Document>(m_scriptExecutionContext); + if (!document) + return std::nullopt; + + SecurityOrigin& origin = document->securityOrigin(); + SecurityOrigin& topOrigin = document->topOrigin(); + + if ((accumulatedConfiguration.distinctiveIdentifier == MediaKeysRequirement::Required || accumulatedConfiguration.persistentState == MediaKeysRequirement::Required) && !origin.canAccessLocalStorage(&topOrigin)) + return std::nullopt; + + return WTFMove(accumulatedConfiguration); + // NOTE: Continued in getConsentStatus(). +} + +std::optional<Vector<MediaKeySystemMediaCapability>> CDM::getSupportedCapabilitiesForAudioVideoType(CDM::AudioVideoType type, const Vector<MediaKeySystemMediaCapability>& requestedCapabilities, const MediaKeySystemConfiguration& partialConfiguration, MediaKeysRestrictions& restrictions) +{ + // https://w3c.github.io/encrypted-media/#get-supported-capabilities-for-audio-video-type + // W3C Editor's Draft 09 November 2016 + + ASSERT(m_private); + if (!m_private) + return std::nullopt; + + // 3.1.1.3 Get Supported Capabilities for Audio/Video Type + + // Given an audio/video type, MediaKeySystemMediaCapability sequence requested media capabilities, MediaKeySystemConfiguration + // partial configuration, and restrictions, this algorithm returns a sequence of supported MediaKeySystemMediaCapability values + // for this audio/video type or null as appropriate. + + // 1. Let local accumulated configuration be a local copy of partial configuration. + MediaKeySystemConfiguration accumulatedConfiguration = partialConfiguration; + + // 2. Let supported media capabilities be an empty sequence of MediaKeySystemMediaCapability dictionaries. + Vector<MediaKeySystemMediaCapability> supportedMediaCapabilities { }; + + // 3. For each requested media capability in requested media capabilities: + for (auto& requestedCapability : requestedCapabilities) { + // 3.1. Let content type be requested media capability's contentType member. + // 3.2. Let robustness be requested media capability's robustness member. + String robustness = requestedCapability.robustness; + + // 3.3. If content type is the empty string, return null. + if (requestedCapability.contentType.isEmpty()) + return std::nullopt; + + // 3.4. If content type is an invalid or unrecognized MIME type, continue to the next iteration. + if (!isValidContentType(requestedCapability.contentType)) + continue; + + // 3.5. Let container be the container type specified by content type. + ParsedContentType contentType { requestedCapability.contentType }; + String container = contentType.mimeType(); + + // 3.6. If the user agent does not support container, continue to the next iteration. The case-sensitivity + // of string comparisons is determined by the appropriate RFC. + // 3.7. Let parameters be the RFC 6381 [RFC6381] parameters, if any, specified by content type. + // 3.8. If the user agent does not recognize one or more parameters, continue to the next iteration. + // 3.9. Let media types be the set of codecs and codec constraints specified by parameters. The case-sensitivity + // of string comparisons is determined by the appropriate RFC or other specification. + String codecs = contentType.parameterValueForName("codecs"); + if (contentType.parameterCount() > (codecs.isEmpty() ? 0 : 1)) + continue; + + // 3.10. If media types is empty: + if (codecs.isEmpty()) { + // ↳ If container normatively implies a specific set of codecs and codec constraints: + // ↳ Otherwise: + notImplemented(); + } + + // 3.11. If content type is not strictly a audio/video type, continue to the next iteration. + // 3.12. If robustness is not the empty string and contains an unrecognized value or a value not supported by + // implementation, continue to the next iteration. String comparison is case-sensitive. + if (!robustness.isEmpty() && !m_private->supportsRobustness(robustness)) + continue; + + // 3.13. If the user agent and implementation definitely support playback of encrypted media data for the + // combination of container, media types, robustness and local accumulated configuration in combination + // with restrictions: + MediaEngineSupportParameters parameters; + parameters.type = contentType.mimeType(); + parameters.codecs = codecs; + if (!MediaPlayer::supportsType(parameters, nullptr)) { + // Try with Media Source: + parameters.isMediaSource = true; + if (!MediaPlayer::supportsType(parameters, nullptr)) + continue; + } + + if (!m_private->supportsConfigurationWithRestrictions(accumulatedConfiguration, restrictions)) + continue; + + // 3.13.1. Add requested media capability to supported media capabilities. + supportedMediaCapabilities.append(requestedCapability); + + // 3.13.2. ↳ If audio/video type is Video: + // Add requested media capability to the videoCapabilities member of local accumulated configuration. + if (type == AudioVideoType::Video) + accumulatedConfiguration.videoCapabilities.append(requestedCapability); + // 3.13.2. ↳ If audio/video type is Audio: + // Add requested media capability to the audioCapabilities member of local accumulated configuration. + else + accumulatedConfiguration.audioCapabilities.append(requestedCapability); + } + + // 4. If supported media capabilities is empty, return null. + if (supportedMediaCapabilities.isEmpty()) + return std::nullopt; + + // 5. Return supported media capabilities. + return supportedMediaCapabilities; +} + +void CDM::getConsentStatus(MediaKeySystemConfiguration&& accumulatedConfiguration, MediaKeysRestrictions&& restrictions, ConsentStatusCallback&& callback) +{ + // https://w3c.github.io/encrypted-media/#get-supported-configuration-and-consent + // W3C Editor's Draft 09 November 2016 + if (!m_scriptExecutionContext) { + callback(ConsentStatus::ConsentDenied, WTFMove(accumulatedConfiguration), WTFMove(restrictions)); + return; + } + + // NOTE: In the future, these checks belowe will involve asking the page client, possibly across a process boundary. + // They will by necessity be asynchronous with callbacks. For now, imply this behavior by performing it in an async task. + + m_scriptExecutionContext->postTask([this, weakThis = createWeakPtr(), accumulatedConfiguration = WTFMove(accumulatedConfiguration), restrictions = WTFMove(restrictions), callback = WTFMove(callback)] (ScriptExecutionContext&) mutable { + if (!weakThis || !m_private) { + callback(ConsentStatus::ConsentDenied, WTFMove(accumulatedConfiguration), WTFMove(restrictions)); + return; + } + + Document* document = downcast<Document>(m_scriptExecutionContext); + if (!document) { + callback(ConsentStatus::ConsentDenied, WTFMove(accumulatedConfiguration), WTFMove(restrictions)); + return; + } + + SecurityOrigin& origin = document->securityOrigin(); + SecurityOrigin& topOrigin = document->topOrigin(); + + // 3.1.1.2 Get Supported Configuration and Consent, ctd. + // 21. If accumulated configuration's distinctiveIdentifier value is "required" and the Distinctive Identifier(s) associated + // with accumulated configuration are not unique per origin and profile and clearable: + if (accumulatedConfiguration.distinctiveIdentifier == MediaKeysRequirement::Required && !m_private->distinctiveIdentifiersAreUniquePerOriginAndClearable(accumulatedConfiguration)) { + // 21.1. Update restrictions to reflect that all configurations described by accumulated configuration do not have user consent. + restrictions.distinctiveIdentifierDenied = true; + callback(ConsentStatus::ConsentDenied, WTFMove(accumulatedConfiguration), WTFMove(restrictions)); + return; + } + + // https://w3c.github.io/encrypted-media/#get-consent-status + // 3.1.1.4 Get Consent Status + // Given an accumulated configuration, restrictions and origin, this algorithm returns the consent status for accumulated + // configuration and origin as one of ConsentDenied, InformUser or Allowed, together with an updated value for restrictions + // in the ConsentDenied case. + + // 1. If there is persisted denial for origin indicating that accumulated configuration is not allowed, run the following steps: + // 1.1. Update restrictions to reflect the configurations for which consent has been denied. + // 1.2. Return ConsentDenied and restrictions. + // 2. If there is persisted consent for origin indicating accumulated configuration is allowed, return Allowed. + // NOTE: persisted denial / consent unimplemented. + + // 3. If any of the following are true: + // ↳ The distinctiveIdentifier member of accumulated configuration is not "not-allowed" and the combination of the User Agent, + // implementation and accumulated configuration does not follow all the recommendations of Allow Persistent Data to Be Cleared + // with respect to Distinctive Identifier(s). + // NOTE: assume that implementations follow all recommendations. + + // ↳ The user agent requires explicit user consent for the accumulated configuration for other reasons. + // NOTE: assume the user agent does not require explicit user consent. + + // 3.1. Request user consent to use accumulated configuration in the origin and wait for the user response. + // The consent must include consent to use a Distinctive Identifier(s) and/or Distinctive Permanent Identifier(s) as appropriate + // if accumulated configuration's distinctiveIdentifier member is "required". + // 3.2. If consent was denied, run the following steps: + // 3.2.1. Update restrictions to reflect the configurations for which consent was denied. + // 3.2.1. Return ConsentDenied and restrictions. + // NOTE: assume implied consent if the combination of origin and topOrigin allows it. + if (accumulatedConfiguration.distinctiveIdentifier == MediaKeysRequirement::Required && !origin.canAccessLocalStorage(&topOrigin)) { + restrictions.distinctiveIdentifierDenied = true; + callback(ConsentStatus::ConsentDenied, WTFMove(accumulatedConfiguration), WTFMove(restrictions)); + return; + } + + // 4. If the distinctiveIdentifier member of accumulated configuration is not "not-allowed", return InformUser. + if (accumulatedConfiguration.distinctiveIdentifier != MediaKeysRequirement::NotAllowed) { + callback(ConsentStatus::InformUser, WTFMove(accumulatedConfiguration), WTFMove(restrictions)); + return; + } + + // 5. If the user agent requires informing the user for the accumulated configuration for other reasons, return InformUser. + // NOTE: assume the user agent does not require informing the user. + + // 6. Return Allowed. + callback(ConsentStatus::Allowed, WTFMove(accumulatedConfiguration), WTFMove(restrictions)); + }); +} + +void CDM::loadAndInitialize() +{ + if (m_private) + m_private->loadAndInitialize(); +} + +RefPtr<CDMInstance> CDM::createInstance() +{ + if (!m_private) + return nullptr; + return m_private->createInstance(); +} + +bool CDM::supportsServerCertificates() const +{ + return m_private && m_private->supportsServerCertificates(); +} + +bool CDM::supportsSessions() const +{ + return m_private && m_private->supportsSessions(); +} + +bool CDM::supportsInitDataType(const AtomicString& initDataType) const +{ + return m_private && m_private->supportsInitDataType(initDataType); +} + +RefPtr<SharedBuffer> CDM::sanitizeInitData(const AtomicString& initDataType, const SharedBuffer& initData) +{ + return InitDataRegistry::shared().sanitizeInitData(initDataType, initData); +} + +bool CDM::supportsInitData(const AtomicString& initDataType, const SharedBuffer& initData) +{ + return m_private && m_private->supportsInitData(initDataType, initData); +} + +RefPtr<SharedBuffer> CDM::sanitizeResponse(const SharedBuffer& response) +{ + if (!m_private) + return nullptr; + return m_private->sanitizeResponse(response); +} + +std::optional<String> CDM::sanitizeSessionId(const String& sessionId) +{ + if (!m_private) + return std::nullopt; + return m_private->sanitizeSessionId(sessionId); +} + +} + +#endif |