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