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