2 * Copyright (C) 2013 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "modules/encryptedmedia/MediaKeySession.h"
29 #include "bindings/v8/ExceptionState.h"
30 #include "core/dom/ExceptionCode.h"
31 #include "core/events/GenericEventQueue.h"
32 #include "core/html/MediaKeyError.h"
33 #include "modules/encryptedmedia/MediaKeyMessageEvent.h"
34 #include "modules/encryptedmedia/MediaKeys.h"
35 #include "platform/Logging.h"
36 #include "public/platform/WebContentDecryptionModule.h"
37 #include "public/platform/WebString.h"
38 #include "public/platform/WebURL.h"
42 PassOwnPtr<MediaKeySession::PendingAction> MediaKeySession::PendingAction::CreatePendingUpdate(PassRefPtr<Uint8Array> data)
45 return adoptPtr(new PendingAction(Update, data));
48 PassOwnPtr<MediaKeySession::PendingAction> MediaKeySession::PendingAction::CreatePendingRelease()
50 return adoptPtr(new PendingAction(Release, PassRefPtr<Uint8Array>()));
53 MediaKeySession::PendingAction::PendingAction(Type type, PassRefPtr<Uint8Array> data)
59 MediaKeySession::PendingAction::~PendingAction()
63 MediaKeySession* MediaKeySession::create(ExecutionContext* context, blink::WebContentDecryptionModule* cdm, MediaKeys* keys)
65 MediaKeySession* session = adoptRefCountedGarbageCollectedWillBeNoop(new MediaKeySession(context, cdm, keys));
66 session->suspendIfNeeded();
70 MediaKeySession::MediaKeySession(ExecutionContext* context, blink::WebContentDecryptionModule* cdm, MediaKeys* keys)
71 : ActiveDOMObject(context)
72 , m_keySystem(keys->keySystem())
73 , m_asyncEventQueue(GenericEventQueue::create(this))
74 , m_session(adoptPtr(cdm->createSession(this)))
77 , m_actionTimer(this, &MediaKeySession::actionTimerFired)
79 WTF_LOG(Media, "MediaKeySession::MediaKeySession");
80 ScriptWrappable::init(this);
84 MediaKeySession::~MediaKeySession()
88 // MediaKeySession and m_asyncEventQueue always become unreachable
89 // together. So MediaKeySession and m_asyncEventQueue are destructed in the
90 // same GC. We don't need to call cancelAllEvents explicitly in Oilpan.
91 m_asyncEventQueue->cancelAllEvents();
95 void MediaKeySession::setError(MediaKeyError* error)
100 String MediaKeySession::sessionId() const
102 return m_session->sessionId();
105 void MediaKeySession::initializeNewSession(const String& mimeType, const Uint8Array& initData)
108 m_session->initializeNewSession(mimeType, initData.data(), initData.length());
111 void MediaKeySession::update(Uint8Array* response, ExceptionState& exceptionState)
113 WTF_LOG(Media, "MediaKeySession::update");
116 // From <https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html#dom-update>:
117 // The update(response) method must run the following steps:
118 // 1. If response is an empty array, throw an INVALID_ACCESS_ERR
119 // exception and abort these steps.
120 if (!response->length()) {
121 exceptionState.throwDOMException(InvalidAccessError, String::format("The response argument provided is %s.", response ? "an empty array" : "invalid"));
125 // 2. If the session is not in the PENDING state, throw an INVALID_STATE_ERR.
126 // FIXME: Implement states in MediaKeySession.
128 // 3. Schedule a task to handle the call, providing response.
129 m_pendingActions.append(PendingAction::CreatePendingUpdate(response));
131 if (!m_actionTimer.isActive())
132 m_actionTimer.startOneShot(0, FROM_HERE);
135 void MediaKeySession::release(ExceptionState& exceptionState)
137 WTF_LOG(Media, "MediaKeySession::release");
140 // From <https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html#dom-release>:
141 // The release() method must run the following steps:
142 // 1. If the state of the MediaKeySession is CLOSED then abort these steps.
143 // 2. If the state of the MediaKeySession is ERROR, throw an INVALID_STATE_ERR
144 // exception and abort these steps.
145 // FIXME: Implement states in MediaKeySession.
147 // 3. Schedule a task to handle the call.
148 m_pendingActions.append(PendingAction::CreatePendingRelease());
150 if (!m_actionTimer.isActive())
151 m_actionTimer.startOneShot(0, FROM_HERE);
154 void MediaKeySession::actionTimerFired(Timer<MediaKeySession>*)
156 ASSERT(m_pendingActions.size());
158 while (!m_pendingActions.isEmpty()) {
159 OwnPtr<PendingAction> pendingAction(m_pendingActions.takeFirst());
161 switch (pendingAction->type) {
162 case PendingAction::Update:
163 // NOTE: Continued from step 3. of MediaKeySession::update()
164 // 3.1. Let cdm be the cdm loaded in the MediaKeys constructor.
165 // 3.2. Let request be null.
166 // 3.3. Use cdm to execute the following steps:
167 // 3.3.1 Process response.
168 m_session->update(pendingAction->data->data(), pendingAction->data->length());
170 case PendingAction::Release:
171 // NOTE: Continued from step 3. of MediaKeySession::release().
172 // 3.1 Let cdm be the cdm loaded in the MediaKeys constructor.
173 // 3.2 Use cdm to execute the following steps:
174 // 3.2.1 Process the release request.
175 m_session->release();
181 // Queue a task to fire a simple event named keymessage at the new object
182 void MediaKeySession::message(const unsigned char* message, size_t messageLength, const blink::WebURL& destinationURL)
184 WTF_LOG(Media, "MediaKeySession::message");
186 MediaKeyMessageEventInit init;
187 init.bubbles = false;
188 init.cancelable = false;
189 init.message = Uint8Array::create(message, messageLength);
190 init.destinationURL = destinationURL.string();
192 RefPtrWillBeRawPtr<MediaKeyMessageEvent> event = MediaKeyMessageEvent::create(EventTypeNames::message, init);
193 event->setTarget(this);
194 m_asyncEventQueue->enqueueEvent(event.release());
197 void MediaKeySession::ready()
199 WTF_LOG(Media, "MediaKeySession::ready");
201 RefPtrWillBeRawPtr<Event> event = Event::create(EventTypeNames::ready);
202 event->setTarget(this);
203 m_asyncEventQueue->enqueueEvent(event.release());
206 void MediaKeySession::close()
208 WTF_LOG(Media, "MediaKeySession::close");
210 RefPtrWillBeRawPtr<Event> event = Event::create(EventTypeNames::close);
211 event->setTarget(this);
212 m_asyncEventQueue->enqueueEvent(event.release());
214 // Once closed, the session can no longer be the target of events from
215 // the CDM so this object can be garbage collected.
219 // Queue a task to fire a simple event named keyadded at the MediaKeySession object.
220 void MediaKeySession::error(MediaKeyErrorCode errorCode, unsigned long systemCode)
222 WTF_LOG(Media, "MediaKeySession::error: errorCode=%d, systemCode=%lu", errorCode, systemCode);
224 MediaKeyError::Code mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN;
226 case MediaKeyErrorCodeUnknown:
227 mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN;
229 case MediaKeyErrorCodeClient:
230 mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_CLIENT;
234 // 1. Create a new MediaKeyError object with the following attributes:
235 // code = the appropriate MediaKeyError code
236 // systemCode = a Key System-specific value, if provided, and 0 otherwise
237 // 2. Set the MediaKeySession object's error attribute to the error object created in the previous step.
238 m_error = MediaKeyError::create(mediaKeyErrorCode, systemCode);
240 // 3. queue a task to fire a simple event named keyerror at the MediaKeySession object.
241 RefPtrWillBeRawPtr<Event> event = Event::create(EventTypeNames::error);
242 event->setTarget(this);
243 m_asyncEventQueue->enqueueEvent(event.release());
246 void MediaKeySession::error(blink::WebContentDecryptionModuleException exception, unsigned long systemCode, const blink::WebString& errorMessage)
248 WTF_LOG(Media, "MediaKeySession::error: exception=%d, systemCode=%lu", exception, systemCode);
250 // FIXME: EME-WD MediaKeyError now derives from DOMException. Figure out how
251 // to implement this without breaking prefixed EME, which has a totally
252 // different definition. The spec may also change to be just a DOMException.
253 // For now, simply generate an existing MediaKeyError.
254 MediaKeyErrorCode errorCode;
256 case blink::WebContentDecryptionModuleExceptionClientError:
257 errorCode = MediaKeyErrorCodeClient;
260 // All other exceptions get converted into Unknown.
261 errorCode = MediaKeyErrorCodeUnknown;
264 error(errorCode, systemCode);
267 const AtomicString& MediaKeySession::interfaceName() const
269 return EventTargetNames::MediaKeySession;
272 ExecutionContext* MediaKeySession::executionContext() const
274 return ActiveDOMObject::executionContext();
277 bool MediaKeySession::hasPendingActivity() const
279 // Remain around if there are pending events or MediaKeys is still around
280 // and we're not closed.
281 return ActiveDOMObject::hasPendingActivity()
282 || !m_pendingActions.isEmpty()
283 || m_asyncEventQueue->hasPendingEvents()
284 || (m_keys && !m_isClosed);
287 void MediaKeySession::stop()
289 // Stop the CDM from firing any more events for this session.
293 if (m_actionTimer.isActive())
294 m_actionTimer.stop();
295 m_pendingActions.clear();
296 m_asyncEventQueue->close();
299 void MediaKeySession::trace(Visitor* visitor)
301 visitor->trace(m_asyncEventQueue);
302 visitor->trace(m_keys);
303 EventTargetWithInlineData::trace(visitor);