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/dom/Event.h"
     31 #include "core/dom/ExceptionCode.h"
     32 #include "core/dom/GenericEventQueue.h"
     33 #include "core/html/MediaKeyError.h"
     34 #include "core/platform/graphics/ContentDecryptionModule.h"
     35 #include "modules/encryptedmedia/MediaKeyMessageEvent.h"
     36 #include "modules/encryptedmedia/MediaKeys.h"
     37 
     38 namespace WebCore {
     39 
     40 PassRefPtr<MediaKeySession> MediaKeySession::create(ScriptExecutionContext* context, ContentDecryptionModule* cdm, MediaKeys* keys)
     41 {
     42     return adoptRef(new MediaKeySession(context, cdm, keys));
     43 }
     44 
     45 MediaKeySession::MediaKeySession(ScriptExecutionContext* context, ContentDecryptionModule* cdm, MediaKeys* keys)
     46     : ContextLifecycleObserver(context)
     47     , m_asyncEventQueue(GenericEventQueue::create(this))
     48     , m_keySystem(keys->keySystem())
     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& es)
    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         es.throwDOMException(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         unsigned short errorCode = 0;
    138         unsigned long systemCode = 0;
    139 
    140         // NOTE: Continued from step 2. of MediaKeySession::update()
    141         // 2.1. Let cdm be the cdm loaded in the MediaKeys constructor.
    142         // NOTE: This is m_session.
    143         // 2.2. Let 'did store key' be false.
    144         // 2.3. Let 'next message' be null.
    145         // 2.4. Use cdm to handle key.
    146         m_session->update(*pendingKey);
    147     }
    148 }
    149 
    150 void MediaKeySession::keyAdded()
    151 {
    152     RefPtr<Event> event = Event::create(eventNames().webkitkeyaddedEvent, false, false);
    153     event->setTarget(this);
    154     m_asyncEventQueue->enqueueEvent(event.release());
    155 }
    156 
    157 // Queue a task to fire a simple event named keyadded at the MediaKeySession object.
    158 void MediaKeySession::keyError(MediaKeyErrorCode errorCode, unsigned long systemCode)
    159 {
    160     MediaKeyError::Code mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN;
    161     switch (errorCode) {
    162     case UnknownError:
    163         mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN;
    164         break;
    165     case ClientError:
    166         mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_CLIENT;
    167         break;
    168     }
    169 
    170     // 1. Create a new MediaKeyError object with the following attributes:
    171     //    code = the appropriate MediaKeyError code
    172     //    systemCode = a Key System-specific value, if provided, and 0 otherwise
    173     // 2. Set the MediaKeySession object's error attribute to the error object created in the previous step.
    174     m_error = MediaKeyError::create(mediaKeyErrorCode, systemCode);
    175 
    176     // 3. queue a task to fire a simple event named keyerror at the MediaKeySession object.
    177     RefPtr<Event> event = Event::create(eventNames().webkitkeyerrorEvent, false, false);
    178     event->setTarget(this);
    179     m_asyncEventQueue->enqueueEvent(event.release());
    180 }
    181 
    182 // Queue a task to fire a simple event named keymessage at the new object
    183 void MediaKeySession::keyMessage(const unsigned char* message, size_t messageLength, const KURL& destinationURL)
    184 {
    185     MediaKeyMessageEventInit init;
    186     init.bubbles = false;
    187     init.cancelable = false;
    188     init.message = Uint8Array::create(message, messageLength);
    189     init.destinationURL = destinationURL;
    190 
    191     RefPtr<MediaKeyMessageEvent> event = MediaKeyMessageEvent::create(eventNames().webkitkeymessageEvent, init);
    192     event->setTarget(this);
    193     m_asyncEventQueue->enqueueEvent(event.release());
    194 }
    195 
    196 const AtomicString& MediaKeySession::interfaceName() const
    197 {
    198     return eventNames().interfaceForMediaKeySession;
    199 }
    200 
    201 ScriptExecutionContext* MediaKeySession::scriptExecutionContext() const
    202 {
    203     return ContextLifecycleObserver::scriptExecutionContext();
    204 }
    205 
    206 }
    207