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 "MediaKeySession.h" 28 29#if ENABLE(ENCRYPTED_MEDIA_V2) 30 31#include "CDM.h" 32#include "CDMSession.h" 33#include "Event.h" 34#include "GenericEventQueue.h" 35#include "MediaKeyError.h" 36#include "MediaKeyMessageEvent.h" 37#include "MediaKeys.h" 38 39namespace WebCore { 40 41PassRefPtr<MediaKeySession> MediaKeySession::create(ScriptExecutionContext* context, MediaKeys* keys, const String& keySystem) 42{ 43 auto session = adoptRef(new MediaKeySession(context, keys, keySystem)); 44 session->suspendIfNeeded(); 45 return session; 46} 47 48MediaKeySession::MediaKeySession(ScriptExecutionContext* context, MediaKeys* keys, const String& keySystem) 49 : ActiveDOMObject(context) 50 , m_keys(keys) 51 , m_keySystem(keySystem) 52 , m_asyncEventQueue(*this) 53 , m_session(keys->cdm()->createSession()) 54 , m_keyRequestTimer(this, &MediaKeySession::keyRequestTimerFired) 55 , m_addKeyTimer(this, &MediaKeySession::addKeyTimerFired) 56{ 57 m_session->setClient(this); 58} 59 60MediaKeySession::~MediaKeySession() 61{ 62 close(); 63} 64 65void MediaKeySession::setError(MediaKeyError* error) 66{ 67 m_error = error; 68} 69 70void MediaKeySession::close() 71{ 72 if (m_session) { 73 m_session->releaseKeys(); 74 m_session->setClient(nullptr); 75 } 76 m_session = nullptr; 77 m_asyncEventQueue.cancelAllEvents(); 78} 79 80const String& MediaKeySession::sessionId() const 81{ 82 return m_session->sessionId(); 83} 84 85void MediaKeySession::generateKeyRequest(const String& mimeType, Uint8Array* initData) 86{ 87 m_pendingKeyRequests.append(PendingKeyRequest(mimeType, initData)); 88 m_keyRequestTimer.startOneShot(0); 89} 90 91void MediaKeySession::keyRequestTimerFired(Timer<MediaKeySession>&) 92{ 93 ASSERT(m_pendingKeyRequests.size()); 94 if (!m_session) 95 return; 96 97 while (!m_pendingKeyRequests.isEmpty()) { 98 PendingKeyRequest request = m_pendingKeyRequests.takeFirst(); 99 100 // NOTE: Continued from step 5 in MediaKeys::createSession(). 101 // The user agent will asynchronously execute the following steps in the task: 102 103 // 1. Let cdm be the cdm loaded in the MediaKeys constructor. 104 // 2. Let destinationURL be null. 105 String destinationURL; 106 MediaKeyError::Code errorCode = 0; 107 unsigned long systemCode = 0; 108 109 // 3. Use cdm to generate a key request and follow the steps for the first matching condition from the following list: 110 111 RefPtr<Uint8Array> keyRequest = m_session->generateKeyRequest(request.mimeType, request.initData.get(), destinationURL, errorCode, systemCode); 112 113 // Otherwise [if a request is not successfully generated]: 114 if (!keyRequest) { 115 // 3.1. Create a new MediaKeyError object with the following attributes: 116 // code = the appropriate MediaKeyError code 117 // systemCode = a Key System-specific value, if provided, and 0 otherwise 118 // 3.2. Set the MediaKeySession object's error attribute to the error object created in the previous step. 119 // 3.3. queue a task to fire a simple event named keyerror at the MediaKeySession object. 120 sendError(errorCode, systemCode); 121 // 3.4. Abort the task. 122 continue; 123 } 124 125 // 4. queue a task to fire a simple event named keymessage at the new object 126 // The event is of type MediaKeyMessageEvent and has: 127 // message = key request 128 // destinationURL = destinationURL 129 sendMessage(keyRequest.get(), destinationURL); 130 } 131} 132 133void MediaKeySession::update(Uint8Array* key, ExceptionCode& ec) 134{ 135 // From <http://dvcs.w3.org/hg/html-media/raw-file/tip/encrypted-media/encrypted-media.html#dom-addkey>: 136 // The addKey(key) method must run the following steps: 137 // 1. If the first or second argument [sic] is null or an empty array, throw an INVALID_ACCESS_ERR. 138 // NOTE: the reference to a "second argument" is a spec bug. 139 if (!key || !key->length()) { 140 ec = INVALID_ACCESS_ERR; 141 return; 142 } 143 144 // 2. Schedule a task to handle the call, providing key. 145 m_pendingKeys.append(key); 146 m_addKeyTimer.startOneShot(0); 147} 148 149void MediaKeySession::addKeyTimerFired(Timer<MediaKeySession>&) 150{ 151 ASSERT(m_pendingKeys.size()); 152 if (!m_session) 153 return; 154 155 while (!m_pendingKeys.isEmpty()) { 156 RefPtr<Uint8Array> pendingKey = m_pendingKeys.takeFirst(); 157 unsigned short errorCode = 0; 158 unsigned long systemCode = 0; 159 160 // NOTE: Continued from step 2. of MediaKeySession::update() 161 // 2.1. Let cdm be the cdm loaded in the MediaKeys constructor. 162 // NOTE: This is m_session. 163 // 2.2. Let 'did store key' be false. 164 bool didStoreKey = false; 165 // 2.3. Let 'next message' be null. 166 RefPtr<Uint8Array> nextMessage; 167 // 2.4. Use cdm to handle key. 168 didStoreKey = m_session->update(pendingKey.get(), nextMessage, errorCode, systemCode); 169 // 2.5. If did store key is true and the media element is waiting for a key, queue a task to attempt to resume playback. 170 // TODO: Find and restart the media element 171 172 // 2.6. If next message is not null, queue a task to fire a simple event named keymessage at the MediaKeySession object. 173 // The event is of type MediaKeyMessageEvent and has: 174 // message = next message 175 // destinationURL = null 176 if (nextMessage) 177 sendMessage(nextMessage.get(), emptyString()); 178 179 // 2.7. If did store key is true, queue a task to fire a simple event named keyadded at the MediaKeySession object. 180 if (didStoreKey) { 181 RefPtr<Event> keyaddedEvent = Event::create(eventNames().webkitkeyaddedEvent, false, false); 182 keyaddedEvent->setTarget(this); 183 m_asyncEventQueue.enqueueEvent(keyaddedEvent.release()); 184 } 185 186 // 2.8. If any of the preceding steps in the task failed 187 if (errorCode) { 188 // 2.8.1. Create a new MediaKeyError object with the following attributes: 189 // code = the appropriate MediaKeyError code 190 // systemCode = a Key System-specific value, if provided, and 0 otherwise 191 // 2.8.2. Set the MediaKeySession object's error attribute to the error object created in the previous step. 192 // 2.8.3. queue a task to fire a simple event named keyerror at the MediaKeySession object. 193 sendError(errorCode, systemCode); 194 // 2.8.4. Abort the task. 195 // NOTE: no-op 196 } 197 } 198} 199 200void MediaKeySession::sendMessage(Uint8Array* message, String destinationURL) 201{ 202 MediaKeyMessageEventInit init; 203 init.bubbles = false; 204 init.cancelable = false; 205 init.message = message; 206 init.destinationURL = destinationURL; 207 RefPtr<MediaKeyMessageEvent> event = MediaKeyMessageEvent::create(eventNames().webkitkeymessageEvent, init); 208 event->setTarget(this); 209 m_asyncEventQueue.enqueueEvent(event.release()); 210} 211 212void MediaKeySession::sendError(CDMSessionClient::MediaKeyErrorCode errorCode, unsigned long systemCode) 213{ 214 RefPtr<MediaKeyError> error = MediaKeyError::create(errorCode, systemCode).get(); 215 setError(error.get()); 216 217 RefPtr<Event> keyerrorEvent = Event::create(eventNames().webkitkeyerrorEvent, false, false); 218 keyerrorEvent->setTarget(this); 219 m_asyncEventQueue.enqueueEvent(keyerrorEvent.release()); 220} 221 222} 223 224#endif 225