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 17 matching lines...) Expand all Loading... | |
28 | 28 |
29 #include "bindings/core/v8/ScriptPromiseResolver.h" | 29 #include "bindings/core/v8/ScriptPromiseResolver.h" |
30 #include "bindings/core/v8/ScriptState.h" | 30 #include "bindings/core/v8/ScriptState.h" |
31 #include "core/dom/DOMException.h" | 31 #include "core/dom/DOMException.h" |
32 #include "core/dom/Document.h" | 32 #include "core/dom/Document.h" |
33 #include "core/dom/ExceptionCode.h" | 33 #include "core/dom/ExceptionCode.h" |
34 #include "core/dom/ExecutionContext.h" | 34 #include "core/dom/ExecutionContext.h" |
35 #include "modules/encryptedmedia/MediaKeyMessageEvent.h" | 35 #include "modules/encryptedmedia/MediaKeyMessageEvent.h" |
36 #include "modules/encryptedmedia/MediaKeySession.h" | 36 #include "modules/encryptedmedia/MediaKeySession.h" |
37 #include "modules/encryptedmedia/MediaKeysController.h" | 37 #include "modules/encryptedmedia/MediaKeysController.h" |
38 #include "modules/encryptedmedia/SimpleContentDecryptionModuleResult.h" | |
38 #include "platform/ContentType.h" | 39 #include "platform/ContentType.h" |
39 #include "platform/Logging.h" | 40 #include "platform/Logging.h" |
40 #include "platform/MIMETypeRegistry.h" | 41 #include "platform/MIMETypeRegistry.h" |
41 #include "platform/Timer.h" | 42 #include "platform/Timer.h" |
42 #include "platform/UUID.h" | 43 #include "platform/UUID.h" |
43 #include "public/platform/Platform.h" | 44 #include "public/platform/Platform.h" |
44 #include "public/platform/WebContentDecryptionModule.h" | 45 #include "public/platform/WebContentDecryptionModule.h" |
45 #include "wtf/ArrayBuffer.h" | 46 #include "wtf/ArrayBuffer.h" |
46 #include "wtf/ArrayBufferView.h" | 47 #include "wtf/ArrayBufferView.h" |
47 #include "wtf/RefPtr.h" | 48 #include "wtf/RefPtr.h" |
(...skipping 17 matching lines...) Expand all Loading... | |
65 ContentType type(contentType); | 66 ContentType type(contentType); |
66 String codecs = type.parameter("codecs"); | 67 String codecs = type.parameter("codecs"); |
67 return MIMETypeRegistry::isSupportedEncryptedMediaMIMEType(keySystem, type.t ype(), codecs); | 68 return MIMETypeRegistry::isSupportedEncryptedMediaMIMEType(keySystem, type.t ype(), codecs); |
68 } | 69 } |
69 | 70 |
70 static ScriptPromise createRejectedPromise(ScriptState* scriptState, ExceptionCo de error, const String& errorMessage) | 71 static ScriptPromise createRejectedPromise(ScriptState* scriptState, ExceptionCo de error, const String& errorMessage) |
71 { | 72 { |
72 return ScriptPromise::rejectWithDOMException(scriptState, DOMException::crea te(error, errorMessage)); | 73 return ScriptPromise::rejectWithDOMException(scriptState, DOMException::crea te(error, errorMessage)); |
73 } | 74 } |
74 | 75 |
76 // A class holding a pending action. | |
77 class MediaKeys::PendingAction : public GarbageCollectedFinalized<MediaKeys::Pen dingAction> { | |
78 public: | |
79 const Persistent<ContentDecryptionModuleResult> result() const | |
80 { | |
81 return m_result; | |
82 } | |
83 | |
84 const RefPtr<ArrayBuffer> data() const | |
85 { | |
86 return m_data; | |
87 } | |
88 | |
89 static PendingAction* CreatePendingSetServerCertificate(ContentDecryptionMod uleResult* result, PassRefPtr<ArrayBuffer> serverCertificate) | |
90 { | |
91 ASSERT(result); | |
92 ASSERT(serverCertificate); | |
93 return new PendingAction(result, serverCertificate); | |
94 } | |
95 | |
96 ~PendingAction() | |
97 { | |
98 } | |
99 | |
100 void trace(Visitor* visitor) | |
101 { | |
102 visitor->trace(m_result); | |
103 } | |
104 | |
105 private: | |
106 PendingAction(ContentDecryptionModuleResult* result, PassRefPtr<ArrayBuffer> data) | |
107 : m_result(result) | |
108 , m_data(data) | |
109 { | |
110 } | |
111 | |
112 const Member<ContentDecryptionModuleResult> m_result; | |
113 const RefPtr<ArrayBuffer> m_data; | |
114 }; | |
115 | |
75 // This class allows a MediaKeys object to be created asynchronously. | 116 // This class allows a MediaKeys object to be created asynchronously. |
76 class MediaKeysInitializer : public ScriptPromiseResolver { | 117 class MediaKeysInitializer : public ScriptPromiseResolver { |
77 WTF_MAKE_NONCOPYABLE(MediaKeysInitializer); | 118 WTF_MAKE_NONCOPYABLE(MediaKeysInitializer); |
78 | 119 |
79 public: | 120 public: |
80 static ScriptPromise create(ScriptState*, const String& keySystem); | 121 static ScriptPromise create(ScriptState*, const String& keySystem); |
81 virtual ~MediaKeysInitializer(); | 122 virtual ~MediaKeysInitializer(); |
82 | 123 |
83 private: | 124 private: |
84 MediaKeysInitializer(ScriptState*, const String& keySystem); | 125 MediaKeysInitializer(ScriptState*, const String& keySystem); |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
170 // 3. Let promise be a new promise. | 211 // 3. Let promise be a new promise. |
171 // 4. Asynchronously create and initialize the MediaKeys. | 212 // 4. Asynchronously create and initialize the MediaKeys. |
172 // 5. Return promise. | 213 // 5. Return promise. |
173 return MediaKeysInitializer::create(scriptState, keySystem); | 214 return MediaKeysInitializer::create(scriptState, keySystem); |
174 } | 215 } |
175 | 216 |
176 MediaKeys::MediaKeys(ExecutionContext* context, const String& keySystem, PassOwn Ptr<WebContentDecryptionModule> cdm) | 217 MediaKeys::MediaKeys(ExecutionContext* context, const String& keySystem, PassOwn Ptr<WebContentDecryptionModule> cdm) |
177 : ContextLifecycleObserver(context) | 218 : ContextLifecycleObserver(context) |
178 , m_keySystem(keySystem) | 219 , m_keySystem(keySystem) |
179 , m_cdm(cdm) | 220 , m_cdm(cdm) |
221 , m_timer(this, &MediaKeys::timerFired) | |
180 { | 222 { |
181 WTF_LOG(Media, "MediaKeys(%p)::MediaKeys", this); | 223 WTF_LOG(Media, "MediaKeys(%p)::MediaKeys", this); |
182 | 224 |
183 // Step 4.4 of MediaKeys::create(): | 225 // Step 4.4 of MediaKeys::create(): |
184 // 4.4.1 Set the keySystem attribute to keySystem. | 226 // 4.4.1 Set the keySystem attribute to keySystem. |
185 ASSERT(!m_keySystem.isEmpty()); | 227 ASSERT(!m_keySystem.isEmpty()); |
186 } | 228 } |
187 | 229 |
188 MediaKeys::~MediaKeys() | 230 MediaKeys::~MediaKeys() |
189 { | 231 { |
(...skipping 13 matching lines...) Expand all Loading... | |
203 // FIXME: Check whether sessionType is actually supported by the CDM. | 245 // FIXME: Check whether sessionType is actually supported by the CDM. |
204 ASSERT(sessionType == kTemporary || sessionType == kPersistent); | 246 ASSERT(sessionType == kTemporary || sessionType == kPersistent); |
205 | 247 |
206 // 2. Let session be a new MediaKeySession object, and initialize it as | 248 // 2. Let session be a new MediaKeySession object, and initialize it as |
207 // follows: | 249 // follows: |
208 // (Initialization is performed in the constructor.) | 250 // (Initialization is performed in the constructor.) |
209 // 3. Return session. | 251 // 3. Return session. |
210 return MediaKeySession::create(scriptState, this, sessionType); | 252 return MediaKeySession::create(scriptState, this, sessionType); |
211 } | 253 } |
212 | 254 |
255 ScriptPromise MediaKeys::setServerCertificate(ScriptState* scriptState, ArrayBuf fer* serverCertificate) | |
256 { | |
257 RefPtr<ArrayBuffer> serverCertificateCopy = ArrayBuffer::create(serverCertif icate->data(), serverCertificate->byteLength()); | |
258 return setServerCertificateInternal(scriptState, serverCertificateCopy.relea se()); | |
259 } | |
260 | |
261 ScriptPromise MediaKeys::setServerCertificate(ScriptState* scriptState, ArrayBuf ferView* serverCertificate) | |
262 { | |
263 RefPtr<ArrayBuffer> serverCertificateCopy = ArrayBuffer::create(serverCertif icate->baseAddress(), serverCertificate->byteLength()); | |
264 return setServerCertificateInternal(scriptState, serverCertificateCopy.relea se()); | |
265 } | |
266 | |
267 ScriptPromise MediaKeys::setServerCertificateInternal(ScriptState* scriptState, PassRefPtr<ArrayBuffer> serverCertificate) | |
268 { | |
269 // From https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/e ncrypted-media.html#dom-setservercertificate: | |
270 // The setServerCertificate(serverCertificate) method provides a server | |
271 // certificate to be used to encrypt messages to the license server. | |
272 // It must run the following steps: | |
273 // 1. If serverCertificate is an empty array, return a promise rejected | |
274 // with a new DOMException whose name is "InvalidAccessError". | |
275 if (!serverCertificate->byteLength()) { | |
276 return ScriptPromise::rejectWithDOMException( | |
277 scriptState, DOMException::create(InvalidAccessError, "The serverCer tificate parameter is empty.")); | |
278 } | |
279 | |
280 // 2. If the keySystem does not support server certificates, return a | |
281 // promise rejected with a new DOMException whose name is | |
282 // "NotSupportedError". | |
283 // (Let the CDM decide whether to support this or not.) | |
284 | |
285 // 3. Let certificate be a copy of the contents of the serverCertificate | |
286 // parameter. | |
287 // (Done in caller.) | |
288 | |
289 // 4. Let promise be a new promise. | |
290 SimpleContentDecryptionModuleResult* result = new SimpleContentDecryptionMod uleResult(scriptState); | |
291 ScriptPromise promise = result->promise(); | |
292 | |
293 // 5. Run the following steps asynchronously (documented in timerFired()). | |
294 m_pendingActions.append(PendingAction::CreatePendingSetServerCertificate(res ult, serverCertificate)); | |
sandersd (OOO until July 31)
2014/09/11 23:37:13
If the Chromium promises implementation is correct
jrummell
2014/09/12 20:49:03
The problem is that ClearKey will resolve the prom
ddorwin
2014/09/15 23:10:56
As discussed, let's keep it simple, consistent wit
jrummell
2014/09/17 23:39:24
Acknowledged.
| |
295 if (!m_timer.isActive()) | |
296 m_timer.startOneShot(0, FROM_HERE); | |
297 | |
298 // 6. Return promise. | |
299 return promise; | |
300 } | |
301 | |
213 bool MediaKeys::isTypeSupported(const String& keySystem, const String& contentTy pe) | 302 bool MediaKeys::isTypeSupported(const String& keySystem, const String& contentTy pe) |
214 { | 303 { |
215 WTF_LOG(Media, "MediaKeys::isTypeSupported(%s, %s)", keySystem.ascii().data( ), contentType.ascii().data()); | 304 WTF_LOG(Media, "MediaKeys::isTypeSupported(%s, %s)", keySystem.ascii().data( ), contentType.ascii().data()); |
216 | 305 |
217 // 1. If keySystem is an empty string, return false and abort these steps. | 306 // 1. If keySystem is an empty string, return false and abort these steps. |
218 if (keySystem.isEmpty()) | 307 if (keySystem.isEmpty()) |
219 return false; | 308 return false; |
220 | 309 |
221 // 2. If keySystem contains an unrecognized or unsupported Key System, retur n false and abort | 310 // 2. If keySystem contains an unrecognized or unsupported Key System, retur n false and abort |
222 // these steps. Key system string comparison is case-sensitive. | 311 // these steps. Key system string comparison is case-sensitive. |
223 if (!isKeySystemSupportedWithContentType(keySystem, "")) | 312 if (!isKeySystemSupportedWithContentType(keySystem, "")) |
224 return false; | 313 return false; |
225 | 314 |
226 // 3. If contentType is an empty string, return true and abort these steps. | 315 // 3. If contentType is an empty string, return true and abort these steps. |
227 if (contentType.isEmpty()) | 316 if (contentType.isEmpty()) |
228 return true; | 317 return true; |
229 | 318 |
230 // 4. If the Key System specified by keySystem does not support decrypting t he container and/or | 319 // 4. If the Key System specified by keySystem does not support decrypting t he container and/or |
231 // codec specified by contentType, return false and abort these steps. | 320 // codec specified by contentType, return false and abort these steps. |
232 return isKeySystemSupportedWithContentType(keySystem, contentType); | 321 return isKeySystemSupportedWithContentType(keySystem, contentType); |
233 } | 322 } |
234 | 323 |
324 void MediaKeys::timerFired(Timer<MediaKeys>*) | |
325 { | |
326 ASSERT(m_pendingActions.size()); | |
327 | |
328 // Resolving promises now run synchronously and may result in additional | |
ddorwin
2014/09/15 23:10:56
"now"? I assume this was inspired by the change in
jrummell
2014/09/17 23:39:24
Comment removed.
| |
329 // actions getting added to the queue. As a result, swap the queue to | |
ddorwin
2014/09/15 23:10:56
nit: s/As a result,/Therefore,/
jrummell
2014/09/17 23:39:24
Changed.
| |
330 // a local copy to avoid problems if this happens. | |
331 HeapDeque<Member<PendingAction> > pendingActions; | |
332 pendingActions.swap(m_pendingActions); | |
333 | |
334 while (!pendingActions.isEmpty()) { | |
335 PendingAction* action = pendingActions.takeFirst(); | |
336 WTF_LOG(Media, "MediaKeys(%p)::timerFired: Certificate", this); | |
337 | |
338 // 5.1 Let cdm be the cdm during the initialization of this object. | |
339 WebContentDecryptionModule* cdm = contentDecryptionModule(); | |
340 | |
341 // 5.2 Use the cdm to process certificate. | |
342 cdm->setServerCertificate(static_cast<unsigned char*>(action->data()->da ta()), action->data()->byteLength(), action->result()->result()); | |
343 // 5.3 If any of the preceding steps failed, reject promise with a | |
344 // new DOMException whose name is the appropriate error name. | |
345 // 5.4 Resolve promise. | |
346 // These are handled by Chromium and the CDM. | |
ddorwin
2014/09/15 23:10:56
Add () as elsewhere?
jrummell
2014/09/17 23:39:24
Done.
| |
347 } | |
348 } | |
349 | |
235 WebContentDecryptionModule* MediaKeys::contentDecryptionModule() | 350 WebContentDecryptionModule* MediaKeys::contentDecryptionModule() |
236 { | 351 { |
237 return m_cdm.get(); | 352 return m_cdm.get(); |
238 } | 353 } |
239 | 354 |
240 void MediaKeys::trace(Visitor* visitor) | 355 void MediaKeys::trace(Visitor* visitor) |
241 { | 356 { |
357 visitor->trace(m_pendingActions); | |
242 } | 358 } |
243 | 359 |
244 void MediaKeys::contextDestroyed() | 360 void MediaKeys::contextDestroyed() |
245 { | 361 { |
246 ContextLifecycleObserver::contextDestroyed(); | 362 ContextLifecycleObserver::contextDestroyed(); |
247 | 363 |
248 // We don't need the CDM anymore. | 364 // We don't need the CDM anymore. |
249 m_cdm.clear(); | 365 m_cdm.clear(); |
250 } | 366 } |
251 | 367 |
252 } // namespace blink | 368 } // namespace blink |
OLD | NEW |