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 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
43 #include "platform/MIMETypeRegistry.h" | 43 #include "platform/MIMETypeRegistry.h" |
44 #include "platform/Timer.h" | 44 #include "platform/Timer.h" |
45 #include "public/platform/WebContentDecryptionModule.h" | 45 #include "public/platform/WebContentDecryptionModule.h" |
46 #include "public/platform/WebContentDecryptionModuleException.h" | 46 #include "public/platform/WebContentDecryptionModuleException.h" |
47 #include "public/platform/WebContentDecryptionModuleSession.h" | 47 #include "public/platform/WebContentDecryptionModuleSession.h" |
48 #include "public/platform/WebString.h" | 48 #include "public/platform/WebString.h" |
49 #include "public/platform/WebURL.h" | 49 #include "public/platform/WebURL.h" |
50 #include "wtf/ArrayBuffer.h" | 50 #include "wtf/ArrayBuffer.h" |
51 #include "wtf/ArrayBufferView.h" | 51 #include "wtf/ArrayBufferView.h" |
52 | 52 |
| 53 namespace { |
| 54 |
| 55 // The list of possible values for |sessionType| passed to createSession(). |
| 56 #if ENABLE(ASSERT) |
| 57 const char* kTemporary = "temporary"; |
| 58 #endif |
| 59 const char* kPersistent = "persistent"; |
| 60 |
| 61 } // namespace |
| 62 |
53 namespace blink { | 63 namespace blink { |
54 | 64 |
55 static bool isKeySystemSupportedWithInitDataType(const String& keySystem, const
String& initDataType) | 65 static bool isKeySystemSupportedWithInitDataType(const String& keySystem, const
String& initDataType) |
56 { | 66 { |
57 ASSERT(!keySystem.isEmpty()); | 67 ASSERT(!keySystem.isEmpty()); |
58 | 68 |
59 // FIXME: initDataType != contentType. Implement this properly. | 69 // FIXME: initDataType != contentType. Implement this properly. |
60 // http://crbug.com/385874. | 70 // http://crbug.com/385874. |
61 String contentType = initDataType; | 71 String contentType = initDataType; |
62 if (initDataType == "webm") { | 72 if (initDataType == "webm") { |
63 contentType = "video/webm"; | 73 contentType = "video/webm"; |
64 } else if (initDataType == "cenc") { | 74 } else if (initDataType == "cenc") { |
65 contentType = "video/mp4"; | 75 contentType = "video/mp4"; |
66 } | 76 } |
67 | 77 |
68 ContentType type(contentType); | 78 ContentType type(contentType); |
69 return MIMETypeRegistry::isSupportedEncryptedMediaMIMEType(keySystem, type.t
ype(), type.parameter("codecs")); | 79 return MIMETypeRegistry::isSupportedEncryptedMediaMIMEType(keySystem, type.t
ype(), type.parameter("codecs")); |
70 } | 80 } |
71 | 81 |
72 // A class holding a pending action. | 82 // A class holding a pending action. |
73 class MediaKeySession::PendingAction : public GarbageCollectedFinalized<MediaKey
Session::PendingAction> { | 83 class MediaKeySession::PendingAction : public GarbageCollectedFinalized<MediaKey
Session::PendingAction> { |
74 public: | 84 public: |
75 enum Type { | 85 enum Type { |
76 GenerateRequest, | 86 GenerateRequest, |
77 Update, | 87 Update, |
78 Release | 88 Close, |
| 89 Remove |
79 }; | 90 }; |
80 | 91 |
81 Type type() const { return m_type; } | 92 Type type() const { return m_type; } |
82 | 93 |
83 const Persistent<ContentDecryptionModuleResult> result() const | 94 const Persistent<ContentDecryptionModuleResult> result() const |
84 { | 95 { |
85 return m_result; | 96 return m_result; |
86 } | 97 } |
87 | 98 |
88 const RefPtr<ArrayBuffer> data() const | 99 const RefPtr<ArrayBuffer> data() const |
(...skipping 15 matching lines...) Expand all Loading... |
104 return new PendingAction(GenerateRequest, result, initDataType, initData
); | 115 return new PendingAction(GenerateRequest, result, initDataType, initData
); |
105 } | 116 } |
106 | 117 |
107 static PendingAction* CreatePendingUpdate(ContentDecryptionModuleResult* res
ult, PassRefPtr<ArrayBuffer> data) | 118 static PendingAction* CreatePendingUpdate(ContentDecryptionModuleResult* res
ult, PassRefPtr<ArrayBuffer> data) |
108 { | 119 { |
109 ASSERT(result); | 120 ASSERT(result); |
110 ASSERT(data); | 121 ASSERT(data); |
111 return new PendingAction(Update, result, String(), data); | 122 return new PendingAction(Update, result, String(), data); |
112 } | 123 } |
113 | 124 |
114 static PendingAction* CreatePendingRelease(ContentDecryptionModuleResult* re
sult) | 125 static PendingAction* CreatePendingClose(ContentDecryptionModuleResult* resu
lt) |
115 { | 126 { |
116 ASSERT(result); | 127 ASSERT(result); |
117 return new PendingAction(Release, result, String(), PassRefPtr<ArrayBuff
er>()); | 128 return new PendingAction(Close, result, String(), PassRefPtr<ArrayBuffer
>()); |
| 129 } |
| 130 |
| 131 static PendingAction* CreatePendingRemove(ContentDecryptionModuleResult* res
ult) |
| 132 { |
| 133 ASSERT(result); |
| 134 return new PendingAction(Remove, result, String(), PassRefPtr<ArrayBuffe
r>()); |
118 } | 135 } |
119 | 136 |
120 ~PendingAction() | 137 ~PendingAction() |
121 { | 138 { |
122 } | 139 } |
123 | 140 |
124 void trace(Visitor* visitor) | 141 void trace(Visitor* visitor) |
125 { | 142 { |
126 visitor->trace(m_result); | 143 visitor->trace(m_result); |
127 } | 144 } |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
200 m_resolver->reject(DOMException::create(code, errorMessage)); | 217 m_resolver->reject(DOMException::create(code, errorMessage)); |
201 m_resolver.clear(); | 218 m_resolver.clear(); |
202 } | 219 } |
203 | 220 |
204 RefPtr<ScriptPromiseResolver> m_resolver; | 221 RefPtr<ScriptPromiseResolver> m_resolver; |
205 Member<MediaKeySession> m_session; | 222 Member<MediaKeySession> m_session; |
206 }; | 223 }; |
207 | 224 |
208 MediaKeySession* MediaKeySession::create(ScriptState* scriptState, MediaKeys* me
diaKeys, const String& sessionType) | 225 MediaKeySession* MediaKeySession::create(ScriptState* scriptState, MediaKeys* me
diaKeys, const String& sessionType) |
209 { | 226 { |
| 227 ASSERT(sessionType == kTemporary || sessionType == kPersistent); |
210 RefPtrWillBeRawPtr<MediaKeySession> session = new MediaKeySession(scriptStat
e, mediaKeys, sessionType); | 228 RefPtrWillBeRawPtr<MediaKeySession> session = new MediaKeySession(scriptStat
e, mediaKeys, sessionType); |
211 session->suspendIfNeeded(); | 229 session->suspendIfNeeded(); |
212 return session.get(); | 230 return session.get(); |
213 } | 231 } |
214 | 232 |
215 MediaKeySession::MediaKeySession(ScriptState* scriptState, MediaKeys* mediaKeys,
const String& sessionType) | 233 MediaKeySession::MediaKeySession(ScriptState* scriptState, MediaKeys* mediaKeys,
const String& sessionType) |
216 : ActiveDOMObject(scriptState->executionContext()) | 234 : ActiveDOMObject(scriptState->executionContext()) |
217 , m_keySystem(mediaKeys->keySystem()) | 235 , m_keySystem(mediaKeys->keySystem()) |
218 , m_asyncEventQueue(GenericEventQueue::create(this)) | 236 , m_asyncEventQueue(GenericEventQueue::create(this)) |
219 , m_mediaKeys(mediaKeys) | 237 , m_mediaKeys(mediaKeys) |
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
395 // 4. Run the following steps asynchronously (documented in | 413 // 4. Run the following steps asynchronously (documented in |
396 // actionTimerFired()) | 414 // actionTimerFired()) |
397 m_pendingActions.append(PendingAction::CreatePendingUpdate(result, response)
); | 415 m_pendingActions.append(PendingAction::CreatePendingUpdate(result, response)
); |
398 if (!m_actionTimer.isActive()) | 416 if (!m_actionTimer.isActive()) |
399 m_actionTimer.startOneShot(0, FROM_HERE); | 417 m_actionTimer.startOneShot(0, FROM_HERE); |
400 | 418 |
401 // 5. Return promise. | 419 // 5. Return promise. |
402 return promise; | 420 return promise; |
403 } | 421 } |
404 | 422 |
405 ScriptPromise MediaKeySession::release(ScriptState* scriptState) | 423 ScriptPromise MediaKeySession::close(ScriptState* scriptState) |
406 { | 424 { |
407 WTF_LOG(Media, "MediaKeySession(%p)::release", this); | 425 WTF_LOG(Media, "MediaKeySession(%p)::close", this); |
408 SimpleContentDecryptionModuleResult* result = new SimpleContentDecryptionMod
uleResult(scriptState); | |
409 ScriptPromise promise = result->promise(); | |
410 | 426 |
411 // From <https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/
encrypted-media.html#dom-close>: | 427 // From https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/e
ncrypted-media.html#dom-close: |
412 // The close() method allows an application to indicate that it no longer | 428 // The close() method allows an application to indicate that it no longer |
413 // needs the session and the CDM should release any resources associated | 429 // needs the session and the CDM should release any resources associated |
414 // with this object and close it. The returned promise is resolved when the | 430 // with this object and close it. The returned promise is resolved when the |
415 // request has been processed, and the closed attribute promise is resolved | 431 // request has been processed, and the closed attribute promise is resolved |
416 // when the session is closed. It must run the following steps: | 432 // when the session is closed. It must run the following steps: |
417 // | 433 // |
418 // 1. If the Session Close algorithm has been run on this object, return a | 434 // 1. If this object's callable value is false, return a promise rejected |
419 // promise fulfilled with undefined. | 435 // with a new DOMException whose name is "InvalidStateError". |
420 if (m_isClosed) { | 436 if (!m_isCallable) { |
421 result->complete(); | 437 return ScriptPromise::rejectWithDOMException( |
422 return promise; | 438 scriptState, DOMException::create(InvalidStateError, "The session is
not callable.")); |
423 } | 439 } |
424 | 440 |
425 // 2. Let promise be a new promise. | 441 // 2. If the Session Close algorithm has been run on this object, |
426 // (Created earlier so it was available in step 1.) | 442 // return a resolved promise. |
| 443 if (m_isClosed) |
| 444 return ScriptPromise::cast(scriptState, ScriptValue()); |
427 | 445 |
428 // 3. Run the following steps asynchronously (documented in | 446 // 3. Let promise be a new promise. |
| 447 SimpleContentDecryptionModuleResult* result = new SimpleContentDecryptionMod
uleResult(scriptState); |
| 448 ScriptPromise promise = result->promise(); |
| 449 |
| 450 // 4. Run the following steps asynchronously (documented in |
429 // actionTimerFired()). | 451 // actionTimerFired()). |
430 m_pendingActions.append(PendingAction::CreatePendingRelease(result)); | 452 m_pendingActions.append(PendingAction::CreatePendingClose(result)); |
431 if (!m_actionTimer.isActive()) | 453 if (!m_actionTimer.isActive()) |
432 m_actionTimer.startOneShot(0, FROM_HERE); | 454 m_actionTimer.startOneShot(0, FROM_HERE); |
433 | 455 |
434 // 4. Return promise. | 456 // 5. Return promise. |
435 return promise; | 457 return promise; |
436 } | 458 } |
437 | 459 |
| 460 ScriptPromise MediaKeySession::remove(ScriptState* scriptState) |
| 461 { |
| 462 WTF_LOG(Media, "MediaKeySession(%p)::remove", this); |
| 463 |
| 464 // From https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/e
ncrypted-media.html#dom-remove: |
| 465 // The remove() method allows an application to remove stored session data |
| 466 // associated with this object. It must run the following steps: |
| 467 |
| 468 // 1. If this object's callable value is false, return a promise rejected |
| 469 // with a new DOMException whose name is "InvalidStateError". |
| 470 if (!m_isCallable) { |
| 471 return ScriptPromise::rejectWithDOMException( |
| 472 scriptState, DOMException::create(InvalidStateError, "The session is
not callable.")); |
| 473 } |
| 474 |
| 475 // 2. If this object's session type is not "persistent", return a promise |
| 476 // rejected with a new DOMException whose name is "InvalidAccessError". |
| 477 if (m_sessionType != kPersistent) { |
| 478 return ScriptPromise::rejectWithDOMException( |
| 479 scriptState, DOMException::create(InvalidAccessError, "The session t
ype is not 'persistent'.")); |
| 480 } |
| 481 |
| 482 // 3. If the Session Close algorithm has been run on this object, return a |
| 483 // promise rejected with a new DOMException whose name is |
| 484 // "InvalidStateError". |
| 485 if (m_isClosed) { |
| 486 return ScriptPromise::rejectWithDOMException( |
| 487 scriptState, DOMException::create(InvalidStateError, "The session is
already closed.")); |
| 488 } |
| 489 |
| 490 // 4. Let promise be a new promise. |
| 491 SimpleContentDecryptionModuleResult* result = new SimpleContentDecryptionMod
uleResult(scriptState); |
| 492 ScriptPromise promise = result->promise(); |
| 493 |
| 494 // 5. Run the following steps asynchronously (documented in |
| 495 // actionTimerFired()). |
| 496 m_pendingActions.append(PendingAction::CreatePendingRemove(result)); |
| 497 if (!m_actionTimer.isActive()) |
| 498 m_actionTimer.startOneShot(0, FROM_HERE); |
| 499 |
| 500 // 6. Return promise. |
| 501 return promise; |
| 502 } |
| 503 |
438 void MediaKeySession::actionTimerFired(Timer<MediaKeySession>*) | 504 void MediaKeySession::actionTimerFired(Timer<MediaKeySession>*) |
439 { | 505 { |
440 ASSERT(m_pendingActions.size()); | 506 ASSERT(m_pendingActions.size()); |
441 | 507 |
442 // Resolving promises now run synchronously and may result in additional | 508 // Resolving promises now run synchronously and may result in additional |
443 // actions getting added to the queue. As a result, swap the queue to | 509 // actions getting added to the queue. As a result, swap the queue to |
444 // a local copy to avoid problems if this happens. | 510 // a local copy to avoid problems if this happens. |
445 HeapDeque<Member<PendingAction> > pendingActions; | 511 HeapDeque<Member<PendingAction> > pendingActions; |
446 pendingActions.swap(m_pendingActions); | 512 pendingActions.swap(m_pendingActions); |
447 | 513 |
(...skipping 24 matching lines...) Expand all Loading... |
472 break; | 538 break; |
473 | 539 |
474 case PendingAction::Update: | 540 case PendingAction::Update: |
475 WTF_LOG(Media, "MediaKeySession(%p)::actionTimerFired: Update", this
); | 541 WTF_LOG(Media, "MediaKeySession(%p)::actionTimerFired: Update", this
); |
476 // NOTE: Continued from step 4 of MediaKeySession::update(). | 542 // NOTE: Continued from step 4 of MediaKeySession::update(). |
477 // Continue the update call by passing message to the cdm. Once | 543 // Continue the update call by passing message to the cdm. Once |
478 // completed, it will resolve/reject the promise. | 544 // completed, it will resolve/reject the promise. |
479 m_session->update(static_cast<unsigned char*>(action->data()->data()
), action->data()->byteLength(), action->result()->result()); | 545 m_session->update(static_cast<unsigned char*>(action->data()->data()
), action->data()->byteLength(), action->result()->result()); |
480 break; | 546 break; |
481 | 547 |
482 case PendingAction::Release: | 548 case PendingAction::Close: |
483 WTF_LOG(Media, "MediaKeySession(%p)::actionTimerFired: Release", thi
s); | 549 WTF_LOG(Media, "MediaKeySession(%p)::actionTimerFired: Close", this)
; |
484 // NOTE: Continued from step 3 of MediaKeySession::release(). | 550 // NOTE: Continued from step 4 of MediaKeySession::close(). |
485 // 3.1 Let cdm be the cdm loaded in create(). | 551 // 4.1 Let cdm be the CDM loaded during the initialization of the |
486 // 3.2 Use the cdm to execute the following steps: | 552 // MediaKeys object that created this object. |
487 // 3.2.1 Process the close request. Do not remove stored session dat
a. | 553 // (Already captured when creating m_session). |
488 // 3.2.2 If the previous step caused the session to be closed, run t
he | 554 // 4.2 Use the cdm to execute the following steps: |
489 // Session Close algorithm on this object. | 555 // 4.2.1 Process the close request. Do not remove stored session |
490 // 3.3 Resolve promise with undefined. | 556 // data. |
491 m_session->release(action->result()->result()); | 557 // 4.2.3 If the previous step caused the session to be closed, |
| 558 // run the Session Close algorithm on this object. |
| 559 // 4.3 Resolve promise. |
| 560 m_session->close(action->result()->result()); |
| 561 break; |
| 562 |
| 563 case PendingAction::Remove: |
| 564 WTF_LOG(Media, "MediaKeySession(%p)::actionTimerFired: Remove", this
); |
| 565 // NOTE: Continued from step 5 of MediaKeySession::remove(). |
| 566 // 5.1 Let cdm be the CDM loaded during the initialization of the |
| 567 // MediaKeys object that created this object. |
| 568 // (Already captured when creating m_session). |
| 569 // 5.2 Use the cdm to execute the following steps: |
| 570 // 5.2.1 Process the remove request. This may involve exchanging |
| 571 // message(s) with the application. Unless this step fails, |
| 572 // the CDM must have cleared all stored session data |
| 573 // associated with this object, including the sessionId, |
| 574 // before proceeding to the next step. (A subsequent call |
| 575 // to load() with sessionId would fail because there is no |
| 576 // data stored for the sessionId.) |
| 577 // 5.3 Run the following steps asynchronously once the above step |
| 578 // has completed: |
| 579 // 5.3.1 If any of the preceding steps failed, reject promise |
| 580 // with a new DOMException whose name is the appropriate |
| 581 // error name. |
| 582 // 5.3.2 Run the Session Close algorithm on this object. |
| 583 // 5.3.3 Resolve promise. |
| 584 m_session->remove(action->result()->result()); |
492 break; | 585 break; |
493 } | 586 } |
494 } | 587 } |
495 } | 588 } |
496 | 589 |
497 void MediaKeySession::finishGenerateRequest() | 590 void MediaKeySession::finishGenerateRequest() |
498 { | 591 { |
499 // 10.4 Set the sessionId attribute to a unique Session ID string. | 592 // 10.4 Set the sessionId attribute to a unique Session ID string. |
500 // It may be obtained from cdm. | 593 // It may be obtained from cdm. |
501 ASSERT(!sessionId().isEmpty()); | 594 ASSERT(!sessionId().isEmpty()); |
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
652 { | 745 { |
653 visitor->trace(m_error); | 746 visitor->trace(m_error); |
654 visitor->trace(m_asyncEventQueue); | 747 visitor->trace(m_asyncEventQueue); |
655 visitor->trace(m_pendingActions); | 748 visitor->trace(m_pendingActions); |
656 visitor->trace(m_mediaKeys); | 749 visitor->trace(m_mediaKeys); |
657 visitor->trace(m_closedPromise); | 750 visitor->trace(m_closedPromise); |
658 EventTargetWithInlineData::trace(visitor); | 751 EventTargetWithInlineData::trace(visitor); |
659 } | 752 } |
660 | 753 |
661 } // namespace blink | 754 } // namespace blink |
OLD | NEW |