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 |
(...skipping 20 matching lines...) Expand all Loading... |
31 #include "bindings/core/v8/ScriptPromiseResolver.h" | 31 #include "bindings/core/v8/ScriptPromiseResolver.h" |
32 #include "bindings/core/v8/ScriptState.h" | 32 #include "bindings/core/v8/ScriptState.h" |
33 #include "core/dom/ExceptionCode.h" | 33 #include "core/dom/ExceptionCode.h" |
34 #include "core/events/Event.h" | 34 #include "core/events/Event.h" |
35 #include "core/events/GenericEventQueue.h" | 35 #include "core/events/GenericEventQueue.h" |
36 #include "core/html/MediaKeyError.h" | 36 #include "core/html/MediaKeyError.h" |
37 #include "modules/encryptedmedia/MediaKeyMessageEvent.h" | 37 #include "modules/encryptedmedia/MediaKeyMessageEvent.h" |
38 #include "modules/encryptedmedia/MediaKeys.h" | 38 #include "modules/encryptedmedia/MediaKeys.h" |
39 #include "modules/encryptedmedia/SimpleContentDecryptionModuleResult.h" | 39 #include "modules/encryptedmedia/SimpleContentDecryptionModuleResult.h" |
40 #include "platform/ContentDecryptionModuleResult.h" | 40 #include "platform/ContentDecryptionModuleResult.h" |
| 41 #include "platform/ContentType.h" |
41 #include "platform/Logging.h" | 42 #include "platform/Logging.h" |
| 43 #include "platform/MIMETypeRegistry.h" |
42 #include "platform/Timer.h" | 44 #include "platform/Timer.h" |
43 #include "public/platform/WebContentDecryptionModule.h" | 45 #include "public/platform/WebContentDecryptionModule.h" |
44 #include "public/platform/WebContentDecryptionModuleException.h" | 46 #include "public/platform/WebContentDecryptionModuleException.h" |
45 #include "public/platform/WebContentDecryptionModuleSession.h" | 47 #include "public/platform/WebContentDecryptionModuleSession.h" |
46 #include "public/platform/WebString.h" | 48 #include "public/platform/WebString.h" |
47 #include "public/platform/WebURL.h" | 49 #include "public/platform/WebURL.h" |
48 #include "wtf/ArrayBuffer.h" | 50 #include "wtf/ArrayBuffer.h" |
49 #include "wtf/ArrayBufferView.h" | 51 #include "wtf/ArrayBufferView.h" |
50 | 52 |
51 namespace blink { | 53 namespace blink { |
52 | 54 |
| 55 static bool isKeySystemSupportedWithInitDataType(const String& keySystem, const
String& initDataType) |
| 56 { |
| 57 ASSERT(!keySystem.isEmpty()); |
| 58 |
| 59 // FIXME: initDataType != contentType. Implement this properly. |
| 60 // http://crbug.com/385874. |
| 61 String contentType = initDataType; |
| 62 if (initDataType == "webm") { |
| 63 contentType = "video/webm"; |
| 64 } else if (initDataType == "cenc") { |
| 65 contentType = "video/mp4"; |
| 66 } |
| 67 |
| 68 ContentType type(contentType); |
| 69 return MIMETypeRegistry::isSupportedEncryptedMediaMIMEType(keySystem, type.t
ype(), type.parameter("codecs")); |
| 70 } |
| 71 |
53 // A class holding a pending action. | 72 // A class holding a pending action. |
54 class MediaKeySession::PendingAction : public GarbageCollectedFinalized<MediaKey
Session::PendingAction> { | 73 class MediaKeySession::PendingAction : public GarbageCollectedFinalized<MediaKey
Session::PendingAction> { |
55 public: | 74 public: |
56 enum Type { | 75 enum Type { |
| 76 GenerateRequest, |
57 Update, | 77 Update, |
58 Release, | 78 Release |
59 Message | |
60 }; | 79 }; |
61 | 80 |
62 Type type() const { return m_type; } | 81 Type type() const { return m_type; } |
63 | 82 |
64 const Persistent<ContentDecryptionModuleResult> result() const | 83 const Persistent<ContentDecryptionModuleResult> result() const |
65 { | 84 { |
66 ASSERT(m_type == Update || m_type == Release); | |
67 return m_result; | 85 return m_result; |
68 } | 86 } |
69 | 87 |
70 const RefPtr<ArrayBuffer> data() const | 88 const RefPtr<ArrayBuffer> data() const |
71 { | 89 { |
72 ASSERT(m_type == Update); | 90 ASSERT(m_type == GenerateRequest || m_type == Update); |
73 return m_data; | 91 return m_data; |
74 } | 92 } |
75 | 93 |
76 RefPtrWillBeRawPtr<Event> event() | 94 const String& initDataType() const |
77 { | 95 { |
78 ASSERT(m_type == Message); | 96 ASSERT(m_type == GenerateRequest); |
79 return m_event; | 97 return m_initDataType; |
| 98 } |
| 99 |
| 100 static PendingAction* CreatePendingGenerateRequest(ContentDecryptionModuleRe
sult* result, const String& initDataType, PassRefPtr<ArrayBuffer> initData) |
| 101 { |
| 102 ASSERT(result); |
| 103 ASSERT(initData); |
| 104 return new PendingAction(GenerateRequest, result, initDataType, initData
); |
80 } | 105 } |
81 | 106 |
82 static PendingAction* CreatePendingUpdate(ContentDecryptionModuleResult* res
ult, PassRefPtr<ArrayBuffer> data) | 107 static PendingAction* CreatePendingUpdate(ContentDecryptionModuleResult* res
ult, PassRefPtr<ArrayBuffer> data) |
83 { | 108 { |
84 ASSERT(result); | 109 ASSERT(result); |
85 ASSERT(data); | 110 ASSERT(data); |
86 return new PendingAction(Update, result, data); | 111 return new PendingAction(Update, result, String(), data); |
87 } | 112 } |
88 | 113 |
89 static PendingAction* CreatePendingRelease(ContentDecryptionModuleResult* re
sult) | 114 static PendingAction* CreatePendingRelease(ContentDecryptionModuleResult* re
sult) |
90 { | 115 { |
91 ASSERT(result); | 116 ASSERT(result); |
92 return new PendingAction(Release, result, PassRefPtr<ArrayBuffer>()); | 117 return new PendingAction(Release, result, String(), PassRefPtr<ArrayBuff
er>()); |
93 } | |
94 | |
95 static PendingAction* CreatePendingMessage(PassRefPtrWillBeRawPtr<Event> eve
nt) | |
96 { | |
97 ASSERT(event); | |
98 return new PendingAction(Message, event); | |
99 } | 118 } |
100 | 119 |
101 ~PendingAction() | 120 ~PendingAction() |
102 { | 121 { |
103 } | 122 } |
104 | 123 |
105 void trace(Visitor* visitor) | 124 void trace(Visitor* visitor) |
106 { | 125 { |
107 visitor->trace(m_result); | 126 visitor->trace(m_result); |
108 visitor->trace(m_event); | |
109 } | 127 } |
110 | 128 |
111 private: | 129 private: |
112 PendingAction(Type type, ContentDecryptionModuleResult* result, PassRefPtr<A
rrayBuffer> data) | 130 PendingAction(Type type, ContentDecryptionModuleResult* result, const String
& initDataType, PassRefPtr<ArrayBuffer> data) |
113 : m_type(type) | 131 : m_type(type) |
114 , m_result(result) | 132 , m_result(result) |
| 133 , m_initDataType(initDataType) |
115 , m_data(data) | 134 , m_data(data) |
116 { | 135 { |
117 } | 136 } |
118 | 137 |
119 PendingAction(Type type, PassRefPtrWillBeRawPtr<Event> event) | |
120 : m_type(type) | |
121 , m_event(event) | |
122 { | |
123 } | |
124 | |
125 const Type m_type; | 138 const Type m_type; |
126 const Member<ContentDecryptionModuleResult> m_result; | 139 const Member<ContentDecryptionModuleResult> m_result; |
| 140 const String m_initDataType; |
127 const RefPtr<ArrayBuffer> m_data; | 141 const RefPtr<ArrayBuffer> m_data; |
128 const RefPtrWillBeMember<Event> m_event; | |
129 }; | 142 }; |
130 | 143 |
131 // This class allows a MediaKeySession object to be created asynchronously. | 144 // This class wraps the promise resolver used when initializing a new session |
132 class MediaKeySessionInitializer : public ScriptPromiseResolver { | 145 // and is passed to Chromium to fullfill the promise. This implementation of |
133 WTF_MAKE_NONCOPYABLE(MediaKeySessionInitializer); | 146 // completeWithSession() will resolve the promise with void, while |
| 147 // completeWithError() will reject the promise with an exception. complete() |
| 148 // is not expected to be called, and will reject the promise. |
| 149 class NewSessionResult : public ContentDecryptionModuleResult { |
| 150 public: |
| 151 NewSessionResult(ScriptState* scriptState, MediaKeySession* session) |
| 152 : m_resolver(ScriptPromiseResolver::create(scriptState)) |
| 153 , m_session(session) |
| 154 { |
| 155 WTF_LOG(Media, "NewSessionResult(%p)", this); |
| 156 } |
134 | 157 |
135 public: | 158 virtual ~NewSessionResult() |
136 static ScriptPromise create(ScriptState*, MediaKeys*, const String& initData
Type, PassRefPtr<ArrayBuffer> initData, const String& sessionType); | |
137 virtual ~MediaKeySessionInitializer(); | |
138 | |
139 void completeWithSession(WebContentDecryptionModuleResult::SessionStatus); | |
140 void completeWithDOMException(ExceptionCode, const String& errorMessage); | |
141 | |
142 private: | |
143 MediaKeySessionInitializer(ScriptState*, MediaKeys*, const String& initDataT
ype, PassRefPtr<ArrayBuffer> initData, const String& sessionType); | |
144 void timerFired(Timer<MediaKeySessionInitializer>*); | |
145 | |
146 Persistent<MediaKeys> m_mediaKeys; | |
147 OwnPtr<WebContentDecryptionModuleSession> m_cdmSession; | |
148 | |
149 // The next 3 values are simply the initialization data saved so that the | |
150 // asynchronous creation has the data needed. | |
151 String m_initDataType; | |
152 RefPtr<ArrayBuffer> m_initData; | |
153 String m_sessionType; | |
154 | |
155 Timer<MediaKeySessionInitializer> m_timer; | |
156 }; | |
157 | |
158 // Represents the result used when a new WebContentDecryptionModuleSession | |
159 // object has been created. Needed as MediaKeySessionInitializer can't be both | |
160 // a ScriptPromiseResolver and ContentDecryptionModuleResult at the same time. | |
161 class NewMediaKeySessionResult FINAL : public ContentDecryptionModuleResult { | |
162 public: | |
163 NewMediaKeySessionResult(MediaKeySessionInitializer* initializer) | |
164 : m_initializer(initializer) | |
165 { | 159 { |
| 160 WTF_LOG(Media, "~NewSessionResult(%p)", this); |
166 } | 161 } |
167 | 162 |
168 // ContentDecryptionModuleResult implementation. | 163 // ContentDecryptionModuleResult implementation. |
169 virtual void complete() OVERRIDE | 164 virtual void complete() OVERRIDE |
170 { | 165 { |
171 ASSERT_NOT_REACHED(); | 166 ASSERT_NOT_REACHED(); |
172 m_initializer->completeWithDOMException(InvalidStateError, "Unexpected c
ompletion."); | 167 completeWithDOMException(InvalidStateError, "Unexpected completion."); |
173 } | 168 } |
174 | 169 |
175 virtual void completeWithSession(WebContentDecryptionModuleResult::SessionSt
atus status) OVERRIDE | 170 virtual void completeWithSession(WebContentDecryptionModuleResult::SessionSt
atus status) OVERRIDE |
176 { | 171 { |
177 m_initializer->completeWithSession(status); | 172 if (status != WebContentDecryptionModuleResult::NewSession) { |
| 173 ASSERT_NOT_REACHED(); |
| 174 completeWithDOMException(InvalidStateError, "Unexpected completion."
); |
| 175 } |
| 176 |
| 177 m_session->finishGenerateRequest(); |
| 178 m_resolver->resolve(); |
| 179 m_resolver.clear(); |
178 } | 180 } |
179 | 181 |
180 virtual void completeWithError(WebContentDecryptionModuleException code, uns
igned long systemCode, const WebString& message) OVERRIDE | 182 virtual void completeWithError(WebContentDecryptionModuleException exception
Code, unsigned long systemCode, const WebString& errorMessage) OVERRIDE |
181 { | 183 { |
182 m_initializer->completeWithDOMException(WebCdmExceptionToExceptionCode(c
ode), message); | 184 completeWithDOMException(WebCdmExceptionToExceptionCode(exceptionCode),
errorMessage); |
| 185 } |
| 186 |
| 187 // It is only valid to call this before completion. |
| 188 ScriptPromise promise() { return m_resolver->promise(); } |
| 189 |
| 190 void trace(Visitor* visitor) |
| 191 { |
| 192 visitor->trace(m_session); |
| 193 ContentDecryptionModuleResult::trace(visitor); |
183 } | 194 } |
184 | 195 |
185 private: | 196 private: |
186 MediaKeySessionInitializer* m_initializer; | 197 // Reject the promise with a DOMException. |
| 198 void completeWithDOMException(ExceptionCode code, const String& errorMessage
) |
| 199 { |
| 200 m_resolver->reject(DOMException::create(code, errorMessage)); |
| 201 m_resolver.clear(); |
| 202 } |
| 203 |
| 204 RefPtr<ScriptPromiseResolver> m_resolver; |
| 205 Member<MediaKeySession> m_session; |
187 }; | 206 }; |
188 | 207 |
189 ScriptPromise MediaKeySessionInitializer::create(ScriptState* scriptState, Media
Keys* mediaKeys, const String& initDataType, PassRefPtr<ArrayBuffer> initData, c
onst String& sessionType) | 208 MediaKeySession* MediaKeySession::create(ScriptState* scriptState, MediaKeys* me
diaKeys, const String& sessionType) |
190 { | 209 { |
191 RefPtr<MediaKeySessionInitializer> initializer = adoptRef(new MediaKeySessio
nInitializer(scriptState, mediaKeys, initDataType, initData, sessionType)); | 210 RefPtrWillBeRawPtr<MediaKeySession> session = adoptRefCountedGarbageCollecte
dWillBeNoop(new MediaKeySession(scriptState, mediaKeys, sessionType)); |
192 initializer->suspendIfNeeded(); | 211 session->suspendIfNeeded(); |
193 initializer->keepAliveWhilePending(); | 212 return session.get(); |
194 return initializer->promise(); | |
195 } | 213 } |
196 | 214 |
197 MediaKeySessionInitializer::MediaKeySessionInitializer(ScriptState* scriptState,
MediaKeys* mediaKeys, const String& initDataType, PassRefPtr<ArrayBuffer> initD
ata, const String& sessionType) | 215 MediaKeySession::MediaKeySession(ScriptState* scriptState, MediaKeys* mediaKeys,
const String& sessionType) |
198 : ScriptPromiseResolver(scriptState) | 216 : ActiveDOMObject(scriptState->executionContext()) |
| 217 , m_keySystem(mediaKeys->keySystem()) |
| 218 , m_asyncEventQueue(GenericEventQueue::create(this)) |
199 , m_mediaKeys(mediaKeys) | 219 , m_mediaKeys(mediaKeys) |
200 , m_initDataType(initDataType) | |
201 , m_initData(initData) | |
202 , m_sessionType(sessionType) | 220 , m_sessionType(sessionType) |
203 , m_timer(this, &MediaKeySessionInitializer::timerFired) | 221 , m_isUninitialized(true) |
204 { | 222 , m_isCallable(false) |
205 WTF_LOG(Media, "MediaKeySessionInitializer::MediaKeySessionInitializer"); | |
206 | |
207 // Start the timer so that MediaKeySession can be created asynchronously. | |
208 m_timer.startOneShot(0, FROM_HERE); | |
209 } | |
210 | |
211 MediaKeySessionInitializer::~MediaKeySessionInitializer() | |
212 { | |
213 WTF_LOG(Media, "MediaKeySessionInitializer::~MediaKeySessionInitializer"); | |
214 } | |
215 | |
216 void MediaKeySessionInitializer::timerFired(Timer<MediaKeySessionInitializer>*) | |
217 { | |
218 WTF_LOG(Media, "MediaKeySessionInitializer::timerFired"); | |
219 | |
220 // Continue MediaKeys::createSession() at step 7. | |
221 // 7.1 Let request be null. (Request provided by cdm in message event). | |
222 // 7.2 Let default URL be null. (Also provided by cdm in message event). | |
223 | |
224 // 7.3 Let cdm be the cdm loaded in create(). | |
225 WebContentDecryptionModule* cdm = m_mediaKeys->contentDecryptionModule(); | |
226 | |
227 // 7.4 Use the cdm to execute the following steps: | |
228 // 7.4.1 If the init data is not valid for initDataType, reject promise | |
229 // with a new DOMException whose name is "InvalidAccessError". | |
230 // 7.4.2 If the init data is not supported by the cdm, reject promise with | |
231 // a new DOMException whose name is "NotSupportedError". | |
232 // 7.4.3 Let request be a request (e.g. a license request) generated based | |
233 // on the init data, which is interpreteted per initDataType, and | |
234 // sessionType. If sessionType is "temporary", the request is for a | |
235 // temporary non-persisted license. If sessionType is "persistent", | |
236 // the request is for a persistable license. | |
237 // 7.4.4 If the init data indicates a default URL, let default URL be | |
238 // that URL. The URL may be validated and/or normalized. | |
239 m_cdmSession = adoptPtr(cdm->createSession()); | |
240 NewMediaKeySessionResult* result = new NewMediaKeySessionResult(this); | |
241 m_cdmSession->initializeNewSession(m_initDataType, static_cast<unsigned char
*>(m_initData->data()), m_initData->byteLength(), m_sessionType, result->result(
)); | |
242 | |
243 WTF_LOG(Media, "MediaKeySessionInitializer::timerFired done"); | |
244 // Note: As soon as the promise is resolved (or rejected), the | |
245 // ScriptPromiseResolver object (|this|) is freed. So if | |
246 // initializeNewSession() is synchronous, access to any members will crash. | |
247 } | |
248 | |
249 void MediaKeySessionInitializer::completeWithSession(WebContentDecryptionModuleR
esult::SessionStatus status) | |
250 { | |
251 WTF_LOG(Media, "MediaKeySessionInitializer::completeWithSession"); | |
252 | |
253 switch (status) { | |
254 case WebContentDecryptionModuleResult::NewSession: { | |
255 // Resume MediaKeys::createSession(). | |
256 // 7.5 Let the session ID be a unique Session ID string. It may be | |
257 // obtained from cdm (it is). | |
258 // 7.6 Let session be a new MediaKeySession object, and initialize it. | |
259 // (Object was created previously, complete the steps for 7.6). | |
260 RefPtrWillBeRawPtr<MediaKeySession> session = adoptRefCountedGarbageColl
ectedWillBeNoop(new MediaKeySession(executionContext(), m_mediaKeys, m_cdmSessio
n.release())); | |
261 session->suspendIfNeeded(); | |
262 | |
263 // 7.7 If any of the preceding steps failed, reject promise with a | |
264 // new DOMException whose name is the appropriate error name | |
265 // and that has an appropriate message. | |
266 // (Implemented by CDM/Chromium calling completeWithError()). | |
267 | |
268 // 7.8 Add an entry for the value of the sessionId attribute to the | |
269 // list of active session IDs for this object. | |
270 // (Implemented in SessionIdAdapter). | |
271 | |
272 // 7.9 Run the Queue a "message" Event algorithm on the session, | |
273 // providing request and default URL. | |
274 // (Done by the CDM). | |
275 | |
276 // 7.10 Resolve promise with session. | |
277 resolve(session.release()); | |
278 WTF_LOG(Media, "MediaKeySessionInitializer::completeWithSession done w/s
ession"); | |
279 return; | |
280 } | |
281 | |
282 case WebContentDecryptionModuleResult::SessionNotFound: | |
283 // Step 4.7.1 of MediaKeys::loadSession(): If there is no data | |
284 // stored for the sessionId in the origin, resolve promise with | |
285 // undefined. | |
286 resolve(V8UndefinedType()); | |
287 WTF_LOG(Media, "MediaKeySessionInitializer::completeWithSession done w/u
ndefined"); | |
288 return; | |
289 | |
290 case WebContentDecryptionModuleResult::SessionAlreadyExists: | |
291 // If a session already exists, resolve the promise with null. | |
292 resolve(V8NullType()); | |
293 WTF_LOG(Media, "MediaKeySessionInitializer::completeWithSession done w/n
ull"); | |
294 return; | |
295 } | |
296 ASSERT_NOT_REACHED(); | |
297 } | |
298 | |
299 void MediaKeySessionInitializer::completeWithDOMException(ExceptionCode code, co
nst String& errorMessage) | |
300 { | |
301 WTF_LOG(Media, "MediaKeySessionInitializer::completeWithDOMException"); | |
302 reject(DOMException::create(code, errorMessage)); | |
303 } | |
304 | |
305 ScriptPromise MediaKeySession::create(ScriptState* scriptState, MediaKeys* media
Keys, const String& initDataType, PassRefPtr<ArrayBuffer> initData, const String
& sessionType) | |
306 { | |
307 // Since creation is done asynchronously, use MediaKeySessionInitializer | |
308 // to do it. | |
309 return MediaKeySessionInitializer::create(scriptState, mediaKeys, initDataTy
pe, initData, sessionType); | |
310 } | |
311 | |
312 MediaKeySession::MediaKeySession(ExecutionContext* context, MediaKeys* keys, Pas
sOwnPtr<WebContentDecryptionModuleSession> cdmSession) | |
313 : ActiveDOMObject(context) | |
314 , m_keySystem(keys->keySystem()) | |
315 , m_asyncEventQueue(GenericEventQueue::create(this)) | |
316 , m_session(cdmSession) | |
317 , m_keys(keys) | |
318 , m_isClosed(false) | 223 , m_isClosed(false) |
319 , m_closedPromise(new ClosedPromise(context, this, ClosedPromise::Closed)) | 224 , m_closedPromise(new ClosedPromise(scriptState->executionContext(), this, C
losedPromise::Closed)) |
320 , m_actionTimer(this, &MediaKeySession::actionTimerFired) | 225 , m_actionTimer(this, &MediaKeySession::actionTimerFired) |
321 { | 226 { |
322 WTF_LOG(Media, "MediaKeySession(%p)::MediaKeySession", this); | 227 WTF_LOG(Media, "MediaKeySession(%p)::MediaKeySession", this); |
| 228 |
| 229 // Create the matching Chromium object. It will not be usable until |
| 230 // initializeNewSession() is called in response to the user calling |
| 231 // generateRequest(). |
| 232 WebContentDecryptionModule* cdm = mediaKeys->contentDecryptionModule(); |
| 233 m_session = adoptPtr(cdm->createSession()); |
323 m_session->setClientInterface(this); | 234 m_session->setClientInterface(this); |
324 | 235 |
325 // Resume MediaKeys::createSession() at step 7.6. | 236 // MediaKeys::createSession(), step 2. |
326 // 7.6.1 Set the error attribute to null. | 237 // 2.1 Let the sessionId attribute be the empty string. |
327 ASSERT(!m_error); | 238 ASSERT(sessionId().isEmpty()); |
328 | 239 |
329 // 7.6.2 Set the sessionId attribute to session ID. | 240 // 2.2 Let the expiration attribute be NaN. |
330 ASSERT(!sessionId().isEmpty()); | 241 // FIXME: Add expiration property. |
331 | 242 |
332 // 7.6.3 Let expiration be NaN. | 243 // 2.3 Let the closed attribute be a new promise. |
333 // 7.6.4 Let closed be a new promise. | 244 ASSERT(!closed(scriptState).isUndefinedOrNull()); |
334 // 7.6.5 Let the session type be sessionType. | 245 |
335 // FIXME: Implement the previous 3 values. | 246 // 2.4 Let the session type be sessionType. |
| 247 ASSERT(sessionType == m_sessionType); |
| 248 |
| 249 // 2.5 Let uninitialized be true. |
| 250 ASSERT(m_isUninitialized); |
| 251 |
| 252 // 2.6 Let callable be false. |
| 253 ASSERT(!m_isCallable); |
336 } | 254 } |
337 | 255 |
338 MediaKeySession::~MediaKeySession() | 256 MediaKeySession::~MediaKeySession() |
339 { | 257 { |
340 WTF_LOG(Media, "MediaKeySession(%p)::~MediaKeySession", this); | 258 WTF_LOG(Media, "MediaKeySession(%p)::~MediaKeySession", this); |
341 m_session.clear(); | 259 m_session.clear(); |
342 #if !ENABLE(OILPAN) | 260 #if !ENABLE(OILPAN) |
343 // MediaKeySession and m_asyncEventQueue always become unreachable | 261 // MediaKeySession and m_asyncEventQueue always become unreachable |
344 // together. So MediaKeySession and m_asyncEventQueue are destructed in the | 262 // together. So MediaKeySession and m_asyncEventQueue are destructed in the |
345 // same GC. We don't need to call cancelAllEvents explicitly in Oilpan. | 263 // same GC. We don't need to call cancelAllEvents explicitly in Oilpan. |
346 m_asyncEventQueue->cancelAllEvents(); | 264 m_asyncEventQueue->cancelAllEvents(); |
347 #endif | 265 #endif |
348 } | 266 } |
349 | 267 |
350 void MediaKeySession::setError(MediaKeyError* error) | 268 void MediaKeySession::setError(MediaKeyError* error) |
351 { | 269 { |
352 m_error = error; | 270 m_error = error; |
353 } | 271 } |
354 | 272 |
355 String MediaKeySession::sessionId() const | 273 String MediaKeySession::sessionId() const |
356 { | 274 { |
357 return m_session->sessionId(); | 275 return m_session->sessionId(); |
358 } | 276 } |
359 | 277 |
360 ScriptPromise MediaKeySession::closed(ScriptState* scriptState) | 278 ScriptPromise MediaKeySession::closed(ScriptState* scriptState) |
361 { | 279 { |
362 return m_closedPromise->promise(scriptState->world()); | 280 return m_closedPromise->promise(scriptState->world()); |
363 } | 281 } |
364 | 282 |
| 283 ScriptPromise MediaKeySession::generateRequest(ScriptState* scriptState, const S
tring& initDataType, ArrayBuffer* initData) |
| 284 { |
| 285 RefPtr<ArrayBuffer> initDataCopy = ArrayBuffer::create(initData->data(), ini
tData->byteLength()); |
| 286 return generateRequestInternal(scriptState, initDataType, initDataCopy.relea
se()); |
| 287 } |
| 288 |
| 289 ScriptPromise MediaKeySession::generateRequest(ScriptState* scriptState, const S
tring& initDataType, ArrayBufferView* initData) |
| 290 { |
| 291 RefPtr<ArrayBuffer> initDataCopy = ArrayBuffer::create(initData->baseAddress
(), initData->byteLength()); |
| 292 return generateRequestInternal(scriptState, initDataType, initDataCopy.relea
se()); |
| 293 } |
| 294 |
| 295 ScriptPromise MediaKeySession::generateRequestInternal(ScriptState* scriptState,
const String& initDataType, PassRefPtr<ArrayBuffer> initData) |
| 296 { |
| 297 WTF_LOG(Media, "MediaKeySession(%p)::generateRequest %s", this, initDataType
.ascii().data()); |
| 298 |
| 299 // From https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/e
ncrypted-media.html#dom-generaterequest: |
| 300 // The generateRequest(initDataType, initData) method creates a new session |
| 301 // for the specified initData. It must run the following steps: |
| 302 |
| 303 // 1. If this object's uninitialized value is false, return a promise |
| 304 // rejected with a new DOMException whose name is "InvalidStateError". |
| 305 if (!m_isUninitialized) { |
| 306 return ScriptPromise::rejectWithDOMException( |
| 307 scriptState, DOMException::create(InvalidStateError, "The session is
already initialized.")); |
| 308 } |
| 309 |
| 310 // 2. Let this object's uninitialized be false. |
| 311 m_isUninitialized = false; |
| 312 |
| 313 // 3. If initDataType is an empty string, return a promise rejected with a |
| 314 // new DOMException whose name is "InvalidAccessError". |
| 315 if (initDataType.isEmpty()) { |
| 316 return ScriptPromise::rejectWithDOMException( |
| 317 scriptState, DOMException::create(InvalidAccessError, "The initDataT
ype parameter is empty.")); |
| 318 } |
| 319 |
| 320 // 4. If initData is an empty array, return a promise rejected with a new |
| 321 // DOMException whose name is"InvalidAccessError". |
| 322 if (!initData->byteLength()) { |
| 323 return ScriptPromise::rejectWithDOMException( |
| 324 scriptState, DOMException::create(InvalidAccessError, "The initData
parameter is empty.")); |
| 325 } |
| 326 |
| 327 // 5. Let media keys be the MediaKeys object that created this object. |
| 328 // (Use m_mediaKey, which was set in the constructor.) |
| 329 |
| 330 // 6. If the content decryption module corresponding to media keys's |
| 331 // keySystem attribute does not support initDataType as an initialization |
| 332 // data type, return a promise rejected with a new DOMException whose |
| 333 // name is "NotSupportedError". String comparison is case-sensitive. |
| 334 if (!isKeySystemSupportedWithInitDataType(m_keySystem, initDataType)) { |
| 335 return ScriptPromise::rejectWithDOMException( |
| 336 scriptState, DOMException::create(NotSupportedError, "The initializa
tion data type '" + initDataType + "' is not supported by the key system.")); |
| 337 } |
| 338 |
| 339 // 7. Let init data be a copy of the contents of the initData parameter. |
| 340 // (Done before calling this method.) |
| 341 |
| 342 // 8. Let session type be this object's session type. |
| 343 // (Done in constructor.) |
| 344 |
| 345 // 9. Let promise be a new promise. |
| 346 NewSessionResult* result = new NewSessionResult(scriptState, this); |
| 347 ScriptPromise promise = result->promise(); |
| 348 |
| 349 // 10. Run the following steps asynchronously (documented in |
| 350 // actionTimerFired()) |
| 351 m_pendingActions.append(PendingAction::CreatePendingGenerateRequest(result,
initDataType, initData)); |
| 352 ASSERT(!m_actionTimer.isActive()); |
| 353 m_actionTimer.startOneShot(0, FROM_HERE); |
| 354 |
| 355 // 11. Return promise. |
| 356 return promise; |
| 357 } |
| 358 |
365 ScriptPromise MediaKeySession::update(ScriptState* scriptState, ArrayBuffer* res
ponse) | 359 ScriptPromise MediaKeySession::update(ScriptState* scriptState, ArrayBuffer* res
ponse) |
366 { | 360 { |
367 RefPtr<ArrayBuffer> responseCopy = ArrayBuffer::create(response->data(), res
ponse->byteLength()); | 361 RefPtr<ArrayBuffer> responseCopy = ArrayBuffer::create(response->data(), res
ponse->byteLength()); |
368 return updateInternal(scriptState, responseCopy.release()); | 362 return updateInternal(scriptState, responseCopy.release()); |
369 } | 363 } |
370 | 364 |
371 ScriptPromise MediaKeySession::update(ScriptState* scriptState, ArrayBufferView*
response) | 365 ScriptPromise MediaKeySession::update(ScriptState* scriptState, ArrayBufferView*
response) |
372 { | 366 { |
373 RefPtr<ArrayBuffer> responseCopy = ArrayBuffer::create(response->baseAddress
(), response->byteLength()); | 367 RefPtr<ArrayBuffer> responseCopy = ArrayBuffer::create(response->baseAddress
(), response->byteLength()); |
374 return updateInternal(scriptState, responseCopy.release()); | 368 return updateInternal(scriptState, responseCopy.release()); |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
448 // Resolving promises now run synchronously and may result in additional | 442 // Resolving promises now run synchronously and may result in additional |
449 // actions getting added to the queue. As a result, swap the queue to | 443 // actions getting added to the queue. As a result, swap the queue to |
450 // a local copy to avoid problems if this happens. | 444 // a local copy to avoid problems if this happens. |
451 HeapDeque<Member<PendingAction> > pendingActions; | 445 HeapDeque<Member<PendingAction> > pendingActions; |
452 pendingActions.swap(m_pendingActions); | 446 pendingActions.swap(m_pendingActions); |
453 | 447 |
454 while (!pendingActions.isEmpty()) { | 448 while (!pendingActions.isEmpty()) { |
455 PendingAction* action = pendingActions.takeFirst(); | 449 PendingAction* action = pendingActions.takeFirst(); |
456 | 450 |
457 switch (action->type()) { | 451 switch (action->type()) { |
| 452 case PendingAction::GenerateRequest: |
| 453 WTF_LOG(Media, "MediaKeySession(%p)::actionTimerFired: GenerateReque
st", this); |
| 454 |
| 455 // 10.1 Let request be null. |
| 456 // 10.2 Let cdm be the CDM loaded during the initialization of |
| 457 // media keys. |
| 458 // 10.3 Use the cdm to execute the following steps: |
| 459 // 10.3.1 If the init data is not valid for initDataType, reject |
| 460 // promise with a new DOMException whose name is |
| 461 // "InvalidAccessError". |
| 462 // 10.3.2 If the init data is not supported by the cdm, reject |
| 463 // promise with a new DOMException whose name is |
| 464 // "NotSupportedError". |
| 465 // 10.3.3 Let request be a request (e.g. a license request) |
| 466 // generated based on the init data, which is interpreted |
| 467 // per initDataType, and session type. |
| 468 m_session->initializeNewSession(action->initDataType(), static_cast<
unsigned char*>(action->data()->data()), action->data()->byteLength(), m_session
Type, action->result()->result()); |
| 469 |
| 470 // Remainder of steps executed in finishGenerateRequest(), called |
| 471 // when |result| is resolved. |
| 472 break; |
| 473 |
458 case PendingAction::Update: | 474 case PendingAction::Update: |
459 WTF_LOG(Media, "MediaKeySession(%p)::actionTimerFired: Update", this
); | 475 WTF_LOG(Media, "MediaKeySession(%p)::actionTimerFired: Update", this
); |
460 // NOTE: Continued from step 4 of MediaKeySession::update(). | 476 // NOTE: Continued from step 4 of MediaKeySession::update(). |
461 // Continue the update call by passing message to the cdm. Once | 477 // Continue the update call by passing message to the cdm. Once |
462 // completed, it will resolve/reject the promise. | 478 // completed, it will resolve/reject the promise. |
463 m_session->update(static_cast<unsigned char*>(action->data()->data()
), action->data()->byteLength(), action->result()->result()); | 479 m_session->update(static_cast<unsigned char*>(action->data()->data()
), action->data()->byteLength(), action->result()->result()); |
464 break; | 480 break; |
| 481 |
465 case PendingAction::Release: | 482 case PendingAction::Release: |
466 WTF_LOG(Media, "MediaKeySession(%p)::actionTimerFired: Release", thi
s); | 483 WTF_LOG(Media, "MediaKeySession(%p)::actionTimerFired: Release", thi
s); |
467 // NOTE: Continued from step 3 of MediaKeySession::release(). | 484 // NOTE: Continued from step 3 of MediaKeySession::release(). |
468 // 3.1 Let cdm be the cdm loaded in create(). | 485 // 3.1 Let cdm be the cdm loaded in create(). |
469 // 3.2 Use the cdm to execute the following steps: | 486 // 3.2 Use the cdm to execute the following steps: |
470 // 3.2.1 Process the close request. Do not remove stored session dat
a. | 487 // 3.2.1 Process the close request. Do not remove stored session dat
a. |
471 // 3.2.2 If the previous step caused the session to be closed, run t
he | 488 // 3.2.2 If the previous step caused the session to be closed, run t
he |
472 // Session Close algorithm on this object. | 489 // Session Close algorithm on this object. |
473 // 3.3 Resolve promise with undefined. | 490 // 3.3 Resolve promise with undefined. |
474 m_session->release(action->result()->result()); | 491 m_session->release(action->result()->result()); |
475 break; | 492 break; |
476 case PendingAction::Message: | |
477 WTF_LOG(Media, "MediaKeySession(%p)::actionTimerFired: Message", thi
s); | |
478 m_asyncEventQueue->enqueueEvent(action->event().release()); | |
479 break; | |
480 } | 493 } |
481 } | 494 } |
482 } | 495 } |
483 | 496 |
484 // Queue a task to fire a simple event named keymessage at the new object | 497 void MediaKeySession::finishGenerateRequest() |
| 498 { |
| 499 // 10.4 Set the sessionId attribute to a unique Session ID string. |
| 500 // It may be obtained from cdm. |
| 501 ASSERT(!sessionId().isEmpty()); |
| 502 |
| 503 // 10.5 If any of the preceding steps failed, reject promise with a new |
| 504 // DOMException whose name is the appropriate error name. |
| 505 // (Done by call to completeWithError()). |
| 506 |
| 507 // 10.6 Add an entry for the value of the sessionId attribute to |
| 508 // media keys's list of active session IDs. |
| 509 // FIXME: Is this required? |
| 510 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=26758 |
| 511 |
| 512 // 10.7 Run the Queue a "message" Event algorithm on the session, |
| 513 // providing request and null. |
| 514 // (Done by the CDM). |
| 515 |
| 516 // 10.8 Let this object's callable be true. |
| 517 m_isCallable = true; |
| 518 } |
| 519 |
| 520 // Queue a task to fire a simple event named keymessage at the new object. |
485 void MediaKeySession::message(const unsigned char* message, size_t messageLength
, const WebURL& destinationURL) | 521 void MediaKeySession::message(const unsigned char* message, size_t messageLength
, const WebURL& destinationURL) |
486 { | 522 { |
487 WTF_LOG(Media, "MediaKeySession(%p)::message", this); | 523 WTF_LOG(Media, "MediaKeySession(%p)::message", this); |
488 | 524 |
| 525 // Verify that 'message' not fired before session initialization is complete
. |
| 526 ASSERT(m_isCallable); |
| 527 |
489 MediaKeyMessageEventInit init; | 528 MediaKeyMessageEventInit init; |
490 init.bubbles = false; | 529 init.bubbles = false; |
491 init.cancelable = false; | 530 init.cancelable = false; |
492 init.message = ArrayBuffer::create(static_cast<const void*>(message), messag
eLength); | 531 init.message = ArrayBuffer::create(static_cast<const void*>(message), messag
eLength); |
493 init.destinationURL = destinationURL.string(); | 532 init.destinationURL = destinationURL.string(); |
494 | 533 |
495 RefPtrWillBeRawPtr<MediaKeyMessageEvent> event = MediaKeyMessageEvent::creat
e(EventTypeNames::message, init); | 534 RefPtrWillBeRawPtr<MediaKeyMessageEvent> event = MediaKeyMessageEvent::creat
e(EventTypeNames::message, init); |
496 event->setTarget(this); | 535 event->setTarget(this); |
497 | |
498 if (!hasEventListeners()) { | |
499 // Since this event may be generated immediately after resolving the | |
500 // CreateSession() promise, it is possible that the JavaScript hasn't | |
501 // had time to run the .then() action and bind any necessary event | |
502 // handlers. If there are no event handlers connected, delay enqueuing | |
503 // this message to provide time for the JavaScript to run. This will | |
504 // also affect the (rare) case where there is no message handler | |
505 // attched during normal operation. | |
506 m_pendingActions.append(PendingAction::CreatePendingMessage(event.releas
e())); | |
507 if (!m_actionTimer.isActive()) | |
508 m_actionTimer.startOneShot(0, FROM_HERE); | |
509 return; | |
510 } | |
511 | |
512 m_asyncEventQueue->enqueueEvent(event.release()); | 536 m_asyncEventQueue->enqueueEvent(event.release()); |
513 } | 537 } |
514 | 538 |
515 void MediaKeySession::ready() | 539 void MediaKeySession::ready() |
516 { | 540 { |
517 WTF_LOG(Media, "MediaKeySession(%p)::ready", this); | 541 WTF_LOG(Media, "MediaKeySession(%p)::ready", this); |
518 | 542 |
519 RefPtrWillBeRawPtr<Event> event = Event::create(EventTypeNames::ready); | 543 RefPtrWillBeRawPtr<Event> event = Event::create(EventTypeNames::ready); |
520 event->setTarget(this); | 544 event->setTarget(this); |
521 m_asyncEventQueue->enqueueEvent(event.release()); | 545 m_asyncEventQueue->enqueueEvent(event.release()); |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
592 } | 616 } |
593 | 617 |
594 bool MediaKeySession::hasPendingActivity() const | 618 bool MediaKeySession::hasPendingActivity() const |
595 { | 619 { |
596 // Remain around if there are pending events or MediaKeys is still around | 620 // Remain around if there are pending events or MediaKeys is still around |
597 // and we're not closed. | 621 // and we're not closed. |
598 WTF_LOG(Media, "MediaKeySession(%p)::hasPendingActivity %s%s%s%s", this, | 622 WTF_LOG(Media, "MediaKeySession(%p)::hasPendingActivity %s%s%s%s", this, |
599 ActiveDOMObject::hasPendingActivity() ? " ActiveDOMObject::hasPendingAct
ivity()" : "", | 623 ActiveDOMObject::hasPendingActivity() ? " ActiveDOMObject::hasPendingAct
ivity()" : "", |
600 !m_pendingActions.isEmpty() ? " !m_pendingActions.isEmpty()" : "", | 624 !m_pendingActions.isEmpty() ? " !m_pendingActions.isEmpty()" : "", |
601 m_asyncEventQueue->hasPendingEvents() ? " m_asyncEventQueue->hasPendingE
vents()" : "", | 625 m_asyncEventQueue->hasPendingEvents() ? " m_asyncEventQueue->hasPendingE
vents()" : "", |
602 (m_keys && !m_isClosed) ? " m_keys && !m_isClosed" : ""); | 626 (m_mediaKeys && !m_isClosed) ? " m_mediaKeys && !m_isClosed" : ""); |
603 | 627 |
604 return ActiveDOMObject::hasPendingActivity() | 628 return ActiveDOMObject::hasPendingActivity() |
605 || !m_pendingActions.isEmpty() | 629 || !m_pendingActions.isEmpty() |
606 || m_asyncEventQueue->hasPendingEvents() | 630 || m_asyncEventQueue->hasPendingEvents() |
607 || (m_keys && !m_isClosed); | 631 || (m_mediaKeys && !m_isClosed); |
608 } | 632 } |
609 | 633 |
610 void MediaKeySession::stop() | 634 void MediaKeySession::stop() |
611 { | 635 { |
612 // Stop the CDM from firing any more events for this session. | 636 // Stop the CDM from firing any more events for this session. |
613 m_session.clear(); | 637 m_session.clear(); |
614 m_isClosed = true; | 638 m_isClosed = true; |
615 | 639 |
616 if (m_actionTimer.isActive()) | 640 if (m_actionTimer.isActive()) |
617 m_actionTimer.stop(); | 641 m_actionTimer.stop(); |
618 m_pendingActions.clear(); | 642 m_pendingActions.clear(); |
619 m_asyncEventQueue->close(); | 643 m_asyncEventQueue->close(); |
620 } | 644 } |
621 | 645 |
622 void MediaKeySession::trace(Visitor* visitor) | 646 void MediaKeySession::trace(Visitor* visitor) |
623 { | 647 { |
624 visitor->trace(m_error); | 648 visitor->trace(m_error); |
625 visitor->trace(m_asyncEventQueue); | 649 visitor->trace(m_asyncEventQueue); |
626 visitor->trace(m_pendingActions); | 650 visitor->trace(m_pendingActions); |
627 visitor->trace(m_keys); | 651 visitor->trace(m_mediaKeys); |
628 visitor->trace(m_closedPromise); | 652 visitor->trace(m_closedPromise); |
629 EventTargetWithInlineData::trace(visitor); | 653 EventTargetWithInlineData::trace(visitor); |
630 } | 654 } |
631 | 655 |
632 } // namespace blink | 656 } // namespace blink |
OLD | NEW |