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 38namespace WebCore { 39 40PassRefPtr<MediaKeySession> MediaKeySession::create(ScriptExecutionContext* context, ContentDecryptionModule* cdm, MediaKeys* keys) 41{ 42 return adoptRef(new MediaKeySession(context, cdm, keys)); 43} 44 45MediaKeySession::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 57MediaKeySession::~MediaKeySession() 58{ 59 close(); 60} 61 62void MediaKeySession::setError(MediaKeyError* error) 63{ 64 m_error = error; 65} 66 67void 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 81String MediaKeySession::sessionId() const 82{ 83 return m_session->sessionId(); 84} 85 86void 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 93void 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 113void 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 129void 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 150void 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. 158void 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 183void 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 196const AtomicString& MediaKeySession::interfaceName() const 197{ 198 return eventNames().interfaceForMediaKeySession; 199} 200 201ScriptExecutionContext* MediaKeySession::scriptExecutionContext() const 202{ 203 return ContextLifecycleObserver::scriptExecutionContext(); 204} 205 206} 207