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/MediaKeySession.h"
     28 
     29 #include "bindings/v8/ExceptionState.h"
     30 #include "core/events/Event.h"
     31 #include "core/dom/ExceptionCode.h"
     32 #include "core/events/GenericEventQueue.h"
     33 #include "core/html/MediaKeyError.h"
     34 #include "modules/encryptedmedia/MediaKeyMessageEvent.h"
     35 #include "modules/encryptedmedia/MediaKeys.h"
     36 #include "platform/drm/ContentDecryptionModule.h"
     37 
     38 namespace WebCore {
     39 
     40 PassRefPtr<MediaKeySession> MediaKeySession::create(ExecutionContext* context, ContentDecryptionModule* cdm, MediaKeys* keys)
     41 {
     42     return adoptRef(new MediaKeySession(context, cdm, keys));
     43 }
     44 
     45 MediaKeySession::MediaKeySession(ExecutionContext* context, ContentDecryptionModule* cdm, MediaKeys* keys)
     46     : ContextLifecycleObserver(context)
     47     , m_keySystem(keys->keySystem())
     48     , m_asyncEventQueue(GenericEventQueue::create(this))
     49     , m_session(cdm->createSession(this))
     50     , m_keys(keys)
     51     , m_keyRequestTimer(this, &MediaKeySession::keyRequestTimerFired)
     52     , m_addKeyTimer(this, &MediaKeySession::addKeyTimerFired)
     53 {
     54     ScriptWrappable::init(this);
     55 }
     56 
     57 MediaKeySession::~MediaKeySession()
     58 {
     59     close();
     60 }
     61 
     62 void MediaKeySession::setError(MediaKeyError* error)
     63 {
     64     m_error = error;
     65 }
     66 
     67 void MediaKeySession::close()
     68 {
     69     ASSERT(!m_keys == !m_session);
     70 
     71     if (m_session)
     72         m_session->close();
     73     m_session.clear();
     74     m_asyncEventQueue->cancelAllEvents();
     75 
     76     // FIXME: Release ref that MediaKeys has by removing it from m_sessions.
     77     // if (m_keys) m_keys->sessionClosed(this);
     78     m_keys = 0;
     79 }
     80 
     81 String MediaKeySession::sessionId() const
     82 {
     83     return m_session->sessionId();
     84 }
     85 
     86 void MediaKeySession::generateKeyRequest(const String& mimeType, Uint8Array* initData)
     87 {
     88     m_pendingKeyRequests.append(PendingKeyRequest(mimeType, initData));
     89     // FIXME: Eliminate timers. Asynchronicity will be handled in Chromium.
     90     m_keyRequestTimer.startOneShot(0);
     91 }
     92 
     93 void MediaKeySession::keyRequestTimerFired(Timer<MediaKeySession>*)
     94 {
     95     ASSERT(m_pendingKeyRequests.size());
     96     if (!m_session)
     97         return;
     98 
     99     while (!m_pendingKeyRequests.isEmpty()) {
    100         PendingKeyRequest request = m_pendingKeyRequests.takeFirst();
    101 
    102         // NOTE: Continued from step 5 in MediaKeys::createSession().
    103         // The user agent will asynchronously execute the following steps in the task:
    104 
    105         // 1. Let cdm be the cdm loaded in the MediaKeys constructor.
    106         // 2. Let destinationURL be null.
    107 
    108         // 3. Use cdm to generate a key request and follow the steps for the first matching condition from the following list:
    109         m_session->generateKeyRequest(request.mimeType, *request.initData);
    110     }
    111 }
    112 
    113 void MediaKeySession::update(Uint8Array* key, ExceptionState& exceptionState)
    114 {
    115     // From <http://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html#dom-addkey>:
    116     // The addKey(key) method must run the following steps:
    117     // 1. If the first or second argument [sic] is null or an empty array, throw an InvalidAccessError.
    118     // NOTE: the reference to a "second argument" is a spec bug.
    119     if (!key || !key->length()) {
    120         exceptionState.throwUninformativeAndGenericDOMException(InvalidAccessError);
    121         return;
    122     }
    123 
    124     // 2. Schedule a task to handle the call, providing key.
    125     m_pendingKeys.append(key);
    126     m_addKeyTimer.startOneShot(0);
    127 }
    128 
    129 void MediaKeySession::addKeyTimerFired(Timer<MediaKeySession>*)
    130 {
    131     ASSERT(m_pendingKeys.size());
    132     if (!m_session)
    133         return;
    134 
    135     while (!m_pendingKeys.isEmpty()) {
    136         RefPtr<Uint8Array> pendingKey = m_pendingKeys.takeFirst();
    137 
    138         // NOTE: Continued from step 2. of MediaKeySession::update()
    139         // 2.1. Let cdm be the cdm loaded in the MediaKeys constructor.
    140         // NOTE: This is m_session.
    141         // 2.2. Let 'did store key' be false.
    142         // 2.3. Let 'next message' be null.
    143         // 2.4. Use cdm to handle key.
    144         m_session->update(*pendingKey);
    145     }
    146 }
    147 
    148 void MediaKeySession::keyAdded()
    149 {
    150     RefPtr<Event> event = Event::create(EventTypeNames::webkitkeyadded);
    151     event->setTarget(this);
    152     m_asyncEventQueue->enqueueEvent(event.release());
    153 }
    154 
    155 // Queue a task to fire a simple event named keyadded at the MediaKeySession object.
    156 void MediaKeySession::keyError(MediaKeyErrorCode errorCode, unsigned long systemCode)
    157 {
    158     MediaKeyError::Code mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN;
    159     switch (errorCode) {
    160     case UnknownError:
    161         mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN;
    162         break;
    163     case ClientError:
    164         mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_CLIENT;
    165         break;
    166     }
    167 
    168     // 1. Create a new MediaKeyError object with the following attributes:
    169     //    code = the appropriate MediaKeyError code
    170     //    systemCode = a Key System-specific value, if provided, and 0 otherwise
    171     // 2. Set the MediaKeySession object's error attribute to the error object created in the previous step.
    172     m_error = MediaKeyError::create(mediaKeyErrorCode, systemCode);
    173 
    174     // 3. queue a task to fire a simple event named keyerror at the MediaKeySession object.
    175     RefPtr<Event> event = Event::create(EventTypeNames::webkitkeyerror);
    176     event->setTarget(this);
    177     m_asyncEventQueue->enqueueEvent(event.release());
    178 }
    179 
    180 // Queue a task to fire a simple event named keymessage at the new object
    181 void MediaKeySession::keyMessage(const unsigned char* message, size_t messageLength, const KURL& destinationURL)
    182 {
    183     MediaKeyMessageEventInit init;
    184     init.bubbles = false;
    185     init.cancelable = false;
    186     init.message = Uint8Array::create(message, messageLength);
    187     init.destinationURL = destinationURL;
    188 
    189     RefPtr<MediaKeyMessageEvent> event = MediaKeyMessageEvent::create(EventTypeNames::webkitkeymessage, init);
    190     event->setTarget(this);
    191     m_asyncEventQueue->enqueueEvent(event.release());
    192 }
    193 
    194 const AtomicString& MediaKeySession::interfaceName() const
    195 {
    196     return EventTargetNames::MediaKeySession;
    197 }
    198 
    199 ExecutionContext* MediaKeySession::executionContext() const
    200 {
    201     return ContextLifecycleObserver::executionContext();
    202 }
    203 
    204 }
    205