b7d37d6a1af0a530e777c176bcdfb22375e8f84d
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / modules / encryptedmedia / MediaKeySession.cpp
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/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"
39
40 namespace WebCore {
41
42 PassOwnPtr<MediaKeySession::PendingAction> MediaKeySession::PendingAction::CreatePendingUpdate(PassRefPtr<Uint8Array> data)
43 {
44     ASSERT(data);
45     return adoptPtr(new PendingAction(Update, data));
46 }
47
48 PassOwnPtr<MediaKeySession::PendingAction> MediaKeySession::PendingAction::CreatePendingRelease()
49 {
50     return adoptPtr(new PendingAction(Release, PassRefPtr<Uint8Array>()));
51 }
52
53 MediaKeySession::PendingAction::PendingAction(Type type, PassRefPtr<Uint8Array> data)
54     : type(type)
55     , data(data)
56 {
57 }
58
59 MediaKeySession::PendingAction::~PendingAction()
60 {
61 }
62
63 MediaKeySession* MediaKeySession::create(ExecutionContext* context, blink::WebContentDecryptionModule* cdm, MediaKeys* keys)
64 {
65     MediaKeySession* session = adoptRefCountedGarbageCollectedWillBeNoop(new MediaKeySession(context, cdm, keys));
66     session->suspendIfNeeded();
67     return session;
68 }
69
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)))
75     , m_keys(keys)
76     , m_isClosed(false)
77     , m_actionTimer(this, &MediaKeySession::actionTimerFired)
78 {
79     WTF_LOG(Media, "MediaKeySession::MediaKeySession");
80     ScriptWrappable::init(this);
81     ASSERT(m_session);
82 }
83
84 MediaKeySession::~MediaKeySession()
85 {
86     m_session.clear();
87 #if !ENABLE(OILPAN)
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();
92 #endif
93 }
94
95 void MediaKeySession::setError(MediaKeyError* error)
96 {
97     m_error = error;
98 }
99
100 String MediaKeySession::sessionId() const
101 {
102     return m_session->sessionId();
103 }
104
105 void MediaKeySession::initializeNewSession(const String& mimeType, const Uint8Array& initData)
106 {
107     ASSERT(!m_isClosed);
108     m_session->initializeNewSession(mimeType, initData.data(), initData.length());
109 }
110
111 void MediaKeySession::update(Uint8Array* response, ExceptionState& exceptionState)
112 {
113     WTF_LOG(Media, "MediaKeySession::update");
114     ASSERT(!m_isClosed);
115
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"));
122         return;
123     }
124
125     // 2. If the session is not in the PENDING state, throw an INVALID_STATE_ERR.
126     // FIXME: Implement states in MediaKeySession.
127
128     // 3. Schedule a task to handle the call, providing response.
129     m_pendingActions.append(PendingAction::CreatePendingUpdate(response));
130
131     if (!m_actionTimer.isActive())
132         m_actionTimer.startOneShot(0, FROM_HERE);
133 }
134
135 void MediaKeySession::release(ExceptionState& exceptionState)
136 {
137     WTF_LOG(Media, "MediaKeySession::release");
138     ASSERT(!m_isClosed);
139
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.
146
147     // 3. Schedule a task to handle the call.
148     m_pendingActions.append(PendingAction::CreatePendingRelease());
149
150     if (!m_actionTimer.isActive())
151         m_actionTimer.startOneShot(0, FROM_HERE);
152 }
153
154 void MediaKeySession::actionTimerFired(Timer<MediaKeySession>*)
155 {
156     ASSERT(m_pendingActions.size());
157
158     while (!m_pendingActions.isEmpty()) {
159         OwnPtr<PendingAction> pendingAction(m_pendingActions.takeFirst());
160
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());
169             break;
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();
176             break;
177         }
178     }
179 }
180
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)
183 {
184     WTF_LOG(Media, "MediaKeySession::message");
185
186     MediaKeyMessageEventInit init;
187     init.bubbles = false;
188     init.cancelable = false;
189     init.message = Uint8Array::create(message, messageLength);
190     init.destinationURL = destinationURL.string();
191
192     RefPtrWillBeRawPtr<MediaKeyMessageEvent> event = MediaKeyMessageEvent::create(EventTypeNames::message, init);
193     event->setTarget(this);
194     m_asyncEventQueue->enqueueEvent(event.release());
195 }
196
197 void MediaKeySession::ready()
198 {
199     WTF_LOG(Media, "MediaKeySession::ready");
200
201     RefPtrWillBeRawPtr<Event> event = Event::create(EventTypeNames::ready);
202     event->setTarget(this);
203     m_asyncEventQueue->enqueueEvent(event.release());
204 }
205
206 void MediaKeySession::close()
207 {
208     WTF_LOG(Media, "MediaKeySession::close");
209
210     RefPtrWillBeRawPtr<Event> event = Event::create(EventTypeNames::close);
211     event->setTarget(this);
212     m_asyncEventQueue->enqueueEvent(event.release());
213
214     // Once closed, the session can no longer be the target of events from
215     // the CDM so this object can be garbage collected.
216     m_isClosed = true;
217 }
218
219 // Queue a task to fire a simple event named keyadded at the MediaKeySession object.
220 void MediaKeySession::error(MediaKeyErrorCode errorCode, unsigned long systemCode)
221 {
222     WTF_LOG(Media, "MediaKeySession::error: errorCode=%d, systemCode=%lu", errorCode, systemCode);
223
224     MediaKeyError::Code mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN;
225     switch (errorCode) {
226     case MediaKeyErrorCodeUnknown:
227         mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN;
228         break;
229     case MediaKeyErrorCodeClient:
230         mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_CLIENT;
231         break;
232     }
233
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);
239
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());
244 }
245
246 void MediaKeySession::error(blink::WebContentDecryptionModuleException exception, unsigned long systemCode, const blink::WebString& errorMessage)
247 {
248     WTF_LOG(Media, "MediaKeySession::error: exception=%d, systemCode=%lu", exception, systemCode);
249
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;
255     switch (exception) {
256     case blink::WebContentDecryptionModuleExceptionClientError:
257         errorCode = MediaKeyErrorCodeClient;
258         break;
259     default:
260         // All other exceptions get converted into Unknown.
261         errorCode = MediaKeyErrorCodeUnknown;
262         break;
263     }
264     error(errorCode, systemCode);
265 }
266
267 const AtomicString& MediaKeySession::interfaceName() const
268 {
269     return EventTargetNames::MediaKeySession;
270 }
271
272 ExecutionContext* MediaKeySession::executionContext() const
273 {
274     return ActiveDOMObject::executionContext();
275 }
276
277 bool MediaKeySession::hasPendingActivity() const
278 {
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);
285 }
286
287 void MediaKeySession::stop()
288 {
289     // Stop the CDM from firing any more events for this session.
290     m_session.clear();
291     m_isClosed = true;
292
293     if (m_actionTimer.isActive())
294         m_actionTimer.stop();
295     m_pendingActions.clear();
296     m_asyncEventQueue->close();
297 }
298
299 void MediaKeySession::trace(Visitor* visitor)
300 {
301     visitor->trace(m_asyncEventQueue);
302     visitor->trace(m_keys);
303     EventTargetWithInlineData::trace(visitor);
304 }
305
306 }