// 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 "modules/encryptedmedia/MediaKeySystemAccess.h" #include "bindings/core/v8/ScriptPromiseResolver.h" #include "bindings/core/v8/ScriptState.h" #include "core/dom/DOMException.h" #include "core/dom/Document.h" #include "core/dom/ExceptionCode.h" #include "modules/encryptedmedia/ContentDecryptionModuleResultPromise.h" #include "modules/encryptedmedia/EncryptedMediaUtils.h" #include "modules/encryptedmedia/MediaKeySession.h" #include "modules/encryptedmedia/MediaKeys.h" #include "modules/encryptedmedia/MediaKeysController.h" #include "platform/Logging.h" #include "platform/Timer.h" #include "public/platform/WebContentDecryptionModule.h" #include "public/platform/WebEncryptedMediaTypes.h" #include "public/platform/WebMediaKeySystemConfiguration.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 { WTF_MAKE_NONCOPYABLE(NewCdmResultPromise); public: NewCdmResultPromise(ScriptState* scriptState, const WebVector& supportedSessionTypes) : ContentDecryptionModuleResultPromise(scriptState) , m_supportedSessionTypes(supportedSessionTypes) { } ~NewCdmResultPromise() override { } // ContentDecryptionModuleResult implementation. void completeWithContentDecryptionModule(WebContentDecryptionModule* cdm) override { // NOTE: Continued from step 2.8 of createMediaKeys(). // 2.9. Let media keys be a new MediaKeys object. MediaKeys* mediaKeys = MediaKeys::create(executionContext(), m_supportedSessionTypes, adoptPtr(cdm)); // 2.10. Resolve promise with media keys. resolve(mediaKeys); } private: WebVector m_supportedSessionTypes; }; // These methods are the inverses of those with the same names in // NavigatorRequestMediaKeySystemAccess. static Vector convertInitDataTypes(const WebVector& initDataTypes) { Vector result(initDataTypes.size()); for (size_t i = 0; i < initDataTypes.size(); i++) result[i] = EncryptedMediaUtils::convertFromInitDataType(initDataTypes[i]); return result; } static HeapVector convertCapabilities(const WebVector& capabilities) { HeapVector result(capabilities.size()); for (size_t i = 0; i < capabilities.size(); i++) { MediaKeySystemMediaCapability capability; capability.setContentType(capabilities[i].contentType); capability.setRobustness(capabilities[i].robustness); result[i] = capability; } return result; } static String convertMediaKeysRequirement(WebMediaKeySystemConfiguration::Requirement requirement) { switch (requirement) { case WebMediaKeySystemConfiguration::Requirement::Required: return "required"; case WebMediaKeySystemConfiguration::Requirement::Optional: return "optional"; case WebMediaKeySystemConfiguration::Requirement::NotAllowed: return "not-allowed"; } ASSERT_NOT_REACHED(); return "not-allowed"; } static Vector convertSessionTypes(const WebVector& sessionTypes) { Vector result(sessionTypes.size()); for (size_t i = 0; i < sessionTypes.size(); i++) result[i] = EncryptedMediaUtils::convertFromSessionType(sessionTypes[i]); return result; } } // namespace MediaKeySystemAccess::MediaKeySystemAccess(const String& keySystem, PassOwnPtr access) : m_keySystem(keySystem) , m_access(access) { } MediaKeySystemAccess::~MediaKeySystemAccess() { } void MediaKeySystemAccess::getConfiguration(MediaKeySystemConfiguration& result) { WebMediaKeySystemConfiguration configuration = m_access->getConfiguration(); // |initDataTypes|, |audioCapabilities|, and |videoCapabilities| can only be // empty if they were not present in the requested configuration. if (!configuration.initDataTypes.isEmpty()) result.setInitDataTypes(convertInitDataTypes(configuration.initDataTypes)); if (!configuration.audioCapabilities.isEmpty()) result.setAudioCapabilities(convertCapabilities(configuration.audioCapabilities)); if (!configuration.videoCapabilities.isEmpty()) result.setVideoCapabilities(convertCapabilities(configuration.videoCapabilities)); // |distinctiveIdentifier|, |persistentState|, and |sessionTypes| are always // set by requestMediaKeySystemAccess(). result.setDistinctiveIdentifier(convertMediaKeysRequirement(configuration.distinctiveIdentifier)); result.setPersistentState(convertMediaKeysRequirement(configuration.persistentState)); result.setSessionTypes(convertSessionTypes(configuration.sessionTypes)); // |label| will (and should) be a null string if it was not set. result.setLabel(configuration.label); } ScriptPromise MediaKeySystemAccess::createMediaKeys(ScriptState* scriptState) { // 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 = m_access->getConfiguration(); // 1. Let promise be a new promise. NewCdmResultPromise* helper = new NewCdmResultPromise(scriptState, configuration.sessionTypes); 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). m_access->createContentDecryptionModule(helper->result()); // 3. Return promise. return promise; } DEFINE_TRACE(MediaKeySystemAccess) { } } // namespace blink