OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2013 Apple Inc. All rights reserved. | 2 * Copyright (C) 2013 Apple Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
11 * documentation and/or other materials provided with the distribution. | 11 * documentation and/or other materials provided with the distribution. |
12 * | 12 * |
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' | 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, | 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS | 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | 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 | 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | 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 | 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
23 * THE POSSIBILITY OF SUCH DAMAGE. | 23 * THE POSSIBILITY OF SUCH DAMAGE. |
24 */ | 24 */ |
25 | 25 |
26 #include "config.h" | 26 #include "config.h" |
27 #include "modules/encryptedmedia/MediaKeySession.h" | 27 #include "modules/encryptedmedia/MediaKeySession.h" |
28 | 28 |
29 #if ENABLE(ENCRYPTED_MEDIA_V2) | |
30 | |
31 #include "core/dom/Event.h" | 29 #include "core/dom/Event.h" |
32 #include "core/dom/GenericEventQueue.h" | 30 #include "core/dom/GenericEventQueue.h" |
33 #include "core/html/MediaKeyError.h" | 31 #include "core/html/MediaKeyError.h" |
34 #include "modules/encryptedmedia/CDM.h" | 32 #include "core/platform/graphics/ContentDecryptionModule.h" |
35 #include "modules/encryptedmedia/MediaKeyMessageEvent.h" | 33 #include "modules/encryptedmedia/MediaKeyMessageEvent.h" |
36 #include "modules/encryptedmedia/MediaKeys.h" | 34 #include "modules/encryptedmedia/MediaKeys.h" |
37 | 35 |
38 namespace WebCore { | 36 namespace WebCore { |
39 | 37 |
40 PassRefPtr<MediaKeySession> MediaKeySession::create(ScriptExecutionContext* cont
ext, MediaKeys* keys, const String& keySystem) | 38 PassRefPtr<MediaKeySession> MediaKeySession::create(ScriptExecutionContext* cont
ext, ContentDecryptionModule* cdm, MediaKeys* keys) |
41 { | 39 { |
42 return adoptRef(new MediaKeySession(context, keys, keySystem)); | 40 return adoptRef(new MediaKeySession(context, cdm, keys)); |
43 } | 41 } |
44 | 42 |
45 MediaKeySession::MediaKeySession(ScriptExecutionContext* context, MediaKeys* key
s, const String& keySystem) | 43 MediaKeySession::MediaKeySession(ScriptExecutionContext* context, ContentDecrypt
ionModule* cdm, MediaKeys* keys) |
46 : ContextDestructionObserver(context) | 44 : ContextDestructionObserver(context) |
| 45 , m_asyncEventQueue(GenericEventQueue::create(this)) |
| 46 , m_keySystem(keys->keySystem()) |
| 47 , m_session(cdm->createSession(this)) |
47 , m_keys(keys) | 48 , m_keys(keys) |
48 , m_keySystem(keySystem) | |
49 , m_asyncEventQueue(GenericEventQueue::create(this)) | |
50 , m_session(keys->cdm()->createSession()) | |
51 , m_keyRequestTimer(this, &MediaKeySession::keyRequestTimerFired) | 49 , m_keyRequestTimer(this, &MediaKeySession::keyRequestTimerFired) |
52 , m_addKeyTimer(this, &MediaKeySession::addKeyTimerFired) | 50 , m_addKeyTimer(this, &MediaKeySession::addKeyTimerFired) |
53 { | 51 { |
54 ScriptWrappable::init(this); | 52 ScriptWrappable::init(this); |
55 } | 53 } |
56 | 54 |
57 MediaKeySession::~MediaKeySession() | 55 MediaKeySession::~MediaKeySession() |
58 { | 56 { |
59 close(); | 57 close(); |
60 } | 58 } |
61 | 59 |
62 void MediaKeySession::setError(MediaKeyError* error) | 60 void MediaKeySession::setError(MediaKeyError* error) |
63 { | 61 { |
64 m_error = error; | 62 m_error = error; |
65 } | 63 } |
66 | 64 |
67 void MediaKeySession::close() | 65 void MediaKeySession::close() |
68 { | 66 { |
| 67 ASSERT(!m_keys == !m_session); |
| 68 |
69 if (m_session) | 69 if (m_session) |
70 m_session->releaseKeys(); | 70 m_session->close(); |
71 m_session.clear(); | 71 m_session.clear(); |
72 m_asyncEventQueue->cancelAllEvents(); | 72 m_asyncEventQueue->cancelAllEvents(); |
| 73 |
| 74 // FIXME: Release ref that MediaKeys has by removing it from m_sessions. |
| 75 // if (m_keys) m_keys->sessionClosed(this); |
| 76 m_keys = 0; |
73 } | 77 } |
74 | 78 |
75 const String& MediaKeySession::sessionId() const | 79 String MediaKeySession::sessionId() const |
76 { | 80 { |
77 return m_session->sessionId(); | 81 return m_session->sessionId(); |
78 } | 82 } |
79 | 83 |
80 void MediaKeySession::generateKeyRequest(const String& mimeType, Uint8Array* ini
tData) | 84 void MediaKeySession::generateKeyRequest(const String& mimeType, Uint8Array* ini
tData) |
81 { | 85 { |
82 m_pendingKeyRequests.append(PendingKeyRequest(mimeType, initData)); | 86 m_pendingKeyRequests.append(PendingKeyRequest(mimeType, initData)); |
| 87 // FIXME: Eliminate timers. Asynchronicity will be handled in Chromium. |
83 m_keyRequestTimer.startOneShot(0); | 88 m_keyRequestTimer.startOneShot(0); |
84 } | 89 } |
85 | 90 |
86 void MediaKeySession::keyRequestTimerFired(Timer<MediaKeySession>*) | 91 void MediaKeySession::keyRequestTimerFired(Timer<MediaKeySession>*) |
87 { | 92 { |
88 ASSERT(m_pendingKeyRequests.size()); | 93 ASSERT(m_pendingKeyRequests.size()); |
89 if (!m_session) | 94 if (!m_session) |
90 return; | 95 return; |
91 | 96 |
92 while (!m_pendingKeyRequests.isEmpty()) { | 97 while (!m_pendingKeyRequests.isEmpty()) { |
93 PendingKeyRequest request = m_pendingKeyRequests.takeFirst(); | 98 PendingKeyRequest request = m_pendingKeyRequests.takeFirst(); |
94 | 99 |
95 // NOTE: Continued from step 5 in MediaKeys::createSession(). | 100 // NOTE: Continued from step 5 in MediaKeys::createSession(). |
96 // The user agent will asynchronously execute the following steps in the
task: | 101 // The user agent will asynchronously execute the following steps in the
task: |
97 | 102 |
98 // 1. Let cdm be the cdm loaded in the MediaKeys constructor. | 103 // 1. Let cdm be the cdm loaded in the MediaKeys constructor. |
99 // 2. Let destinationURL be null. | 104 // 2. Let destinationURL be null. |
100 String destinationURL; | |
101 MediaKeyError::Code errorCode = 0; | |
102 unsigned long systemCode = 0; | |
103 | 105 |
104 // 3. Use cdm to generate a key request and follow the steps for the fir
st matching condition from the following list: | 106 // 3. Use cdm to generate a key request and follow the steps for the fir
st matching condition from the following list: |
105 | 107 m_session->generateKeyRequest(request.mimeType, *request.initData); |
106 RefPtr<Uint8Array> keyRequest = m_session->generateKeyRequest(request.mi
meType, request.initData.get(), destinationURL, errorCode, systemCode); | |
107 | |
108 // Otherwise [if a request is not successfully generated]: | |
109 if (!keyRequest) { | |
110 // 3.1. Create a new MediaKeyError object with the following attribu
tes: | |
111 // code = the appropriate MediaKeyError code | |
112 // systemCode = a Key System-specific value, if provided, and 0
otherwise | |
113 RefPtr<MediaKeyError> error = MediaKeyError::create(errorCode, syste
mCode).get(); | |
114 | |
115 // 3.2. Set the MediaKeySession object's error attribute to the erro
r object created in the previous step. | |
116 setError(error.get()); | |
117 | |
118 // 3.3. queue a task to fire a simple event named keyerror at the Me
diaKeySession object. | |
119 RefPtr<Event> event = Event::create(eventNames().webkitkeyerrorEvent
, false, false); | |
120 event->setTarget(this); | |
121 m_asyncEventQueue->enqueueEvent(event.release()); | |
122 | |
123 // 3.4. Abort the task. | |
124 continue; | |
125 } | |
126 | |
127 // 4. queue a task to fire a simple event named keymessage at the new ob
ject | |
128 // The event is of type MediaKeyMessageEvent and has: | |
129 // message = key request | |
130 // destinationURL = destinationURL | |
131 MediaKeyMessageEventInit init; | |
132 init.bubbles = false; | |
133 init.cancelable = false; | |
134 init.message = keyRequest; | |
135 init.destinationURL = destinationURL; | |
136 RefPtr<MediaKeyMessageEvent> event = MediaKeyMessageEvent::create(eventN
ames().webkitkeymessageEvent, init); | |
137 event->setTarget(this); | |
138 m_asyncEventQueue->enqueueEvent(event); | |
139 } | 108 } |
140 } | 109 } |
141 | 110 |
142 void MediaKeySession::update(Uint8Array* key, ExceptionCode& ec) | 111 void MediaKeySession::update(Uint8Array* key, ExceptionCode& ec) |
143 { | 112 { |
144 // From <http://dvcs.w3.org/hg/html-media/raw-file/tip/encrypted-media/encry
pted-media.html#dom-addkey>: | 113 // From <http://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/e
ncrypted-media.html#dom-addkey>: |
145 // The addKey(key) method must run the following steps: | 114 // The addKey(key) method must run the following steps: |
146 // 1. If the first or second argument [sic] is null or an empty array, throw
an INVALID_ACCESS_ERR. | 115 // 1. If the first or second argument [sic] is null or an empty array, throw
an INVALID_ACCESS_ERR. |
147 // NOTE: the reference to a "second argument" is a spec bug. | 116 // NOTE: the reference to a "second argument" is a spec bug. |
148 if (!key || !key->length()) { | 117 if (!key || !key->length()) { |
149 ec = INVALID_ACCESS_ERR; | 118 ec = INVALID_ACCESS_ERR; |
150 return; | 119 return; |
151 } | 120 } |
152 | 121 |
153 // 2. Schedule a task to handle the call, providing key. | 122 // 2. Schedule a task to handle the call, providing key. |
154 m_pendingKeys.append(key); | 123 m_pendingKeys.append(key); |
155 m_addKeyTimer.startOneShot(0); | 124 m_addKeyTimer.startOneShot(0); |
156 } | 125 } |
157 | 126 |
158 void MediaKeySession::addKeyTimerFired(Timer<MediaKeySession>*) | 127 void MediaKeySession::addKeyTimerFired(Timer<MediaKeySession>*) |
159 { | 128 { |
160 ASSERT(m_pendingKeys.size()); | 129 ASSERT(m_pendingKeys.size()); |
161 if (!m_session) | 130 if (!m_session) |
162 return; | 131 return; |
163 | 132 |
164 while (!m_pendingKeys.isEmpty()) { | 133 while (!m_pendingKeys.isEmpty()) { |
165 RefPtr<Uint8Array> pendingKey = m_pendingKeys.takeFirst(); | 134 RefPtr<Uint8Array> pendingKey = m_pendingKeys.takeFirst(); |
166 unsigned short errorCode = 0; | 135 unsigned short errorCode = 0; |
167 unsigned long systemCode = 0; | 136 unsigned long systemCode = 0; |
168 | 137 |
169 // NOTE: Continued from step 2. of MediaKeySession::update() | 138 // NOTE: Continued from step 2. of MediaKeySession::update() |
170 // 2.1. Let cdm be the cdm loaded in the MediaKeys constructor. | 139 // 2.1. Let cdm be the cdm loaded in the MediaKeys constructor. |
171 // NOTE: This is m_session. | 140 // NOTE: This is m_session. |
172 // 2.2. Let 'did store key' be false. | 141 // 2.2. Let 'did store key' be false. |
173 bool didStoreKey = false; | |
174 // 2.3. Let 'next message' be null. | 142 // 2.3. Let 'next message' be null. |
175 RefPtr<Uint8Array> nextMessage; | |
176 // 2.4. Use cdm to handle key. | 143 // 2.4. Use cdm to handle key. |
177 didStoreKey = m_session->update(pendingKey.get(), nextMessage, errorCode
, systemCode); | 144 m_session->update(*pendingKey); |
178 // 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. | |
179 // TODO: Find and restart the media element | |
180 | |
181 // 2.6. If next message is not null, queue a task to fire a simple event
named keymessage at the MediaKeySession object. | |
182 // The event is of type MediaKeyMessageEvent and has: | |
183 // message = next message | |
184 // destinationURL = null | |
185 if (nextMessage) { | |
186 MediaKeyMessageEventInit init; | |
187 init.bubbles = false; | |
188 init.cancelable = false; | |
189 init.message = nextMessage; | |
190 RefPtr<MediaKeyMessageEvent> event = MediaKeyMessageEvent::create(ev
entNames().webkitkeymessageEvent, init); | |
191 event->setTarget(this); | |
192 m_asyncEventQueue->enqueueEvent(event); | |
193 } | |
194 | |
195 // 2.7. If did store key is true, queue a task to fire a simple event na
med keyadded at the MediaKeySession object. | |
196 if (didStoreKey) { | |
197 RefPtr<Event> keyaddedEvent = Event::create(eventNames().webkitkeyad
dedEvent, false, false); | |
198 keyaddedEvent->setTarget(this); | |
199 m_asyncEventQueue->enqueueEvent(keyaddedEvent); | |
200 } | |
201 | |
202 // 2.8. If any of the preceding steps in the task failed | |
203 if (!didStoreKey) { | |
204 // 2.8.1. Create a new MediaKeyError object with the following attri
butes: | |
205 // code = the appropriate MediaKeyError code | |
206 // systemCode = a Key System-specific value, if provided, and
0 otherwise | |
207 RefPtr<MediaKeyError> error = MediaKeyError::create(errorCode, syste
mCode).get(); | |
208 | |
209 // 2.8.2. Set the MediaKeySession object's error attribute to the er
ror object created in the previous step. | |
210 setError(error.get()); | |
211 | |
212 // 2.8.3. queue a task to fire a simple event named keyerror at the
MediaKeySession object. | |
213 RefPtr<Event> keyerrorEvent = Event::create(eventNames().webkitkeyer
rorEvent, false, false); | |
214 keyerrorEvent->setTarget(this); | |
215 m_asyncEventQueue->enqueueEvent(keyerrorEvent.release()); | |
216 | |
217 // 2.8.4. Abort the task. | |
218 // NOTE: no-op | |
219 } | |
220 } | 145 } |
221 } | 146 } |
222 | 147 |
| 148 void MediaKeySession::keyAdded() |
| 149 { |
| 150 RefPtr<Event> event = Event::create(eventNames().webkitkeyaddedEvent, false,
false); |
| 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 obj
ect. |
| 156 void MediaKeySession::keyError(MediaKeyErrorCode errorCode, unsigned long system
Code) |
| 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 c
reated 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 MediaKeySess
ion object. |
| 175 RefPtr<Event> event = Event::create(eventNames().webkitkeyerrorEvent, false,
false); |
| 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 messageLen
gth, 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(eventNames
().webkitkeymessageEvent, init); |
| 190 event->setTarget(this); |
| 191 m_asyncEventQueue->enqueueEvent(event.release()); |
| 192 } |
| 193 |
223 const AtomicString& MediaKeySession::interfaceName() const | 194 const AtomicString& MediaKeySession::interfaceName() const |
224 { | 195 { |
225 return eventNames().interfaceForMediaKeySession; | 196 return eventNames().interfaceForMediaKeySession; |
226 } | 197 } |
227 | 198 |
228 ScriptExecutionContext* MediaKeySession::scriptExecutionContext() const | 199 ScriptExecutionContext* MediaKeySession::scriptExecutionContext() const |
229 { | 200 { |
230 return ContextDestructionObserver::scriptExecutionContext(); | 201 return ContextDestructionObserver::scriptExecutionContext(); |
231 } | 202 } |
232 | 203 |
233 } | 204 } |
234 | |
235 #endif | |
OLD | NEW |