Home | History | Annotate | Download | only in encryptedmedia
      1 /*
      2  * Copyright (C) 2013 Apple Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
     14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
     17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
     23  * THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "modules/encryptedmedia/MediaKeys.h"
     28 
     29 #include "bindings/v8/ExceptionState.h"
     30 #include "core/dom/ContextLifecycleObserver.h"
     31 #include "core/dom/Document.h"
     32 #include "core/dom/ExecutionContext.h"
     33 #include "core/html/HTMLMediaElement.h"
     34 #include "modules/encryptedmedia/MediaKeyMessageEvent.h"
     35 #include "modules/encryptedmedia/MediaKeysClient.h"
     36 #include "modules/encryptedmedia/MediaKeysController.h"
     37 #include "platform/ContentType.h"
     38 #include "platform/Logging.h"
     39 #include "platform/MIMETypeRegistry.h"
     40 #include "platform/UUID.h"
     41 #include "public/platform/Platform.h"
     42 #include "public/platform/WebContentDecryptionModule.h"
     43 #include "wtf/HashSet.h"
     44 
     45 namespace WebCore {
     46 
     47 static bool isKeySystemSupportedWithContentType(const String& keySystem, const String& contentType)
     48 {
     49     ASSERT(!keySystem.isEmpty());
     50 
     51     ContentType type(contentType);
     52     String codecs = type.parameter("codecs");
     53     return MIMETypeRegistry::isSupportedEncryptedMediaMIMEType(keySystem, type.type(), codecs);
     54 }
     55 
     56 MediaKeys* MediaKeys::create(ExecutionContext* context, const String& keySystem, ExceptionState& exceptionState)
     57 {
     58     // From <http://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html#dom-media-keys-constructor>:
     59     // The MediaKeys(keySystem) constructor must run the following steps:
     60 
     61     // 1. If keySystem is an empty string, throw an InvalidAccessError exception and abort these steps.
     62     if (keySystem.isEmpty()) {
     63         exceptionState.throwDOMException(InvalidAccessError, "The key system provided is invalid.");
     64         return 0;
     65     }
     66 
     67     // 2. If keySystem is not one of the user agent's supported Key Systems, throw a NotSupportedError and abort these steps.
     68     if (!isKeySystemSupportedWithContentType(keySystem, "")) {
     69         exceptionState.throwDOMException(NotSupportedError, "The '" + keySystem + "' key system is not supported.");
     70         return 0;
     71     }
     72 
     73     // 3. Let cdm be the content decryption module corresponding to keySystem.
     74     // 4. Load cdm if necessary.
     75     Document* document = toDocument(context);
     76     MediaKeysController* controller = MediaKeysController::from(document->page());
     77     OwnPtr<blink::WebContentDecryptionModule> cdm = controller->createContentDecryptionModule(context, keySystem);
     78     if (!cdm) {
     79         exceptionState.throwDOMException(NotSupportedError, "A content decryption module could not be loaded for the '" + keySystem + "' key system.");
     80         return 0;
     81     }
     82 
     83     // 5. Create a new MediaKeys object.
     84     // 5.1 Let the keySystem attribute be keySystem.
     85     // 6. Return the new object to the caller.
     86     return new MediaKeys(context, keySystem, cdm.release());
     87 }
     88 
     89 MediaKeys::MediaKeys(ExecutionContext* context, const String& keySystem, PassOwnPtr<blink::WebContentDecryptionModule> cdm)
     90     : ContextLifecycleObserver(context)
     91     , m_keySystem(keySystem)
     92     , m_cdm(cdm)
     93     , m_initializeNewSessionTimer(this, &MediaKeys::initializeNewSessionTimerFired)
     94 {
     95     WTF_LOG(Media, "MediaKeys::MediaKeys");
     96     ScriptWrappable::init(this);
     97 }
     98 
     99 MediaKeys::~MediaKeys()
    100 {
    101 }
    102 
    103 MediaKeySession* MediaKeys::createSession(ExecutionContext* context, const String& contentType, Uint8Array* initData, ExceptionState& exceptionState)
    104 {
    105     WTF_LOG(Media, "MediaKeys::createSession");
    106 
    107     // From <http://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html#dom-createsession>:
    108     // The createSession(type, initData) method must run the following steps:
    109     // Note: The contents of initData are container-specific Initialization Data.
    110 
    111     if (contentType.isEmpty()) {
    112         exceptionState.throwDOMException(InvalidAccessError, "The contentType provided ('" + contentType + "') is empty.");
    113         return 0;
    114     }
    115 
    116     if (!initData->length()) {
    117         exceptionState.throwDOMException(InvalidAccessError, "The initData provided is empty.");
    118         return 0;
    119     }
    120 
    121     // 1. If type contains a MIME type that is not supported or is not supported by the keySystem,
    122     // throw a NOT_SUPPORTED_ERR exception and abort these steps.
    123     if (!isKeySystemSupportedWithContentType(m_keySystem, contentType)) {
    124         exceptionState.throwDOMException(NotSupportedError, "The type provided ('" + contentType + "') is unsupported.");
    125         return 0;
    126     }
    127 
    128     // 2. Create a new MediaKeySession object.
    129     MediaKeySession* session = MediaKeySession::create(context, m_cdm.get(), this);
    130     // 2.1 Let the keySystem attribute be keySystem.
    131     ASSERT(!session->keySystem().isEmpty());
    132     // FIXME: 2.2 Let the state of the session be CREATED.
    133 
    134     // 3. Add the new object to an internal list of session objects (not needed).
    135 
    136     // 4. Schedule a task to initialize the session, providing type, initData, and the new object.
    137     m_pendingInitializeNewSessionData.append(InitializeNewSessionData(session, contentType, initData));
    138 
    139     if (!m_initializeNewSessionTimer.isActive())
    140         m_initializeNewSessionTimer.startOneShot(0, FROM_HERE);
    141 
    142     // 5. Return the new object to the caller.
    143     return session;
    144 }
    145 
    146 bool MediaKeys::isTypeSupported(const String& keySystem, const String& contentType)
    147 {
    148     WTF_LOG(Media, "MediaKeys::isTypeSupported(%s, %s)", keySystem.ascii().data(), contentType.ascii().data());
    149 
    150     // 1. If keySystem is an empty string, return false and abort these steps.
    151     if (keySystem.isEmpty())
    152         return false;
    153 
    154     // 2. If keySystem contains an unrecognized or unsupported Key System, return false and abort
    155     // these steps. Key system string comparison is case-sensitive.
    156     if (!isKeySystemSupportedWithContentType(keySystem, ""))
    157         return false;
    158 
    159     // 3. If contentType is an empty string, return true and abort these steps.
    160     if (contentType.isEmpty())
    161         return true;
    162 
    163     // 4. If the Key System specified by keySystem does not support decrypting the container and/or
    164     // codec specified by contentType, return false and abort these steps.
    165     return isKeySystemSupportedWithContentType(keySystem, contentType);
    166 }
    167 
    168 blink::WebContentDecryptionModule* MediaKeys::contentDecryptionModule()
    169 {
    170     return m_cdm.get();
    171 }
    172 
    173 void MediaKeys::initializeNewSessionTimerFired(Timer<MediaKeys>*)
    174 {
    175     ASSERT(m_pendingInitializeNewSessionData.size());
    176 
    177     while (!m_pendingInitializeNewSessionData.isEmpty()) {
    178         InitializeNewSessionData data = m_pendingInitializeNewSessionData.takeFirst();
    179         // FIXME: Refer to the spec to see what needs to be done in blink.
    180         data.session->initializeNewSession(data.contentType, *data.initData);
    181     }
    182 }
    183 
    184 void MediaKeys::trace(Visitor* visitor)
    185 {
    186     visitor->trace(m_pendingInitializeNewSessionData);
    187 }
    188 
    189 void MediaKeys::InitializeNewSessionData::trace(Visitor* visitor)
    190 {
    191     visitor->trace(session);
    192 }
    193 
    194 void MediaKeys::contextDestroyed()
    195 {
    196     ContextLifecycleObserver::contextDestroyed();
    197 
    198     // We don't need the CDM anymore.
    199     m_cdm.clear();
    200 }
    201 
    202 }
    203