Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1215)

Unified Diff: Source/modules/encryptedmedia/HTMLMediaElementEncryptedMedia.cpp

Issue 423633002: Make HTMLMediaElement.setMediaKeys() asynchronous. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: rebase + add comment Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: Source/modules/encryptedmedia/HTMLMediaElementEncryptedMedia.cpp
diff --git a/Source/modules/encryptedmedia/HTMLMediaElementEncryptedMedia.cpp b/Source/modules/encryptedmedia/HTMLMediaElementEncryptedMedia.cpp
index f8a82cc0dbd426d4876ae0a230eae3cd0a139d99..e3e2d6eea141e5501ca34c85eeddd2ae09ef89f2 100644
--- a/Source/modules/encryptedmedia/HTMLMediaElementEncryptedMedia.cpp
+++ b/Source/modules/encryptedmedia/HTMLMediaElementEncryptedMedia.cpp
@@ -6,14 +6,21 @@
#include "modules/encryptedmedia/HTMLMediaElementEncryptedMedia.h"
#include "bindings/core/v8/ExceptionState.h"
+#include "bindings/core/v8/ScriptPromise.h"
+#include "bindings/core/v8/ScriptPromiseResolver.h"
+#include "bindings/core/v8/ScriptState.h"
+#include "core/dom/DOMException.h"
#include "core/dom/ExceptionCode.h"
#include "core/html/HTMLMediaElement.h"
#include "core/html/MediaKeyError.h"
#include "core/html/MediaKeyEvent.h"
#include "modules/encryptedmedia/MediaKeyNeededEvent.h"
#include "modules/encryptedmedia/MediaKeys.h"
+#include "modules/encryptedmedia/SimpleContentDecryptionModuleResult.h"
+#include "platform/ContentDecryptionModuleResult.h"
#include "platform/Logging.h"
#include "platform/RuntimeEnabledFeatures.h"
+#include "wtf/Functional.h"
#include "wtf/Uint8Array.h"
namespace blink {
@@ -38,6 +45,187 @@ static void throwExceptionIfMediaKeyExceptionOccurred(const String& keySystem, c
return;
}
+// This class allows MediaKeys to be set asynchronously.
+class SetMediaKeysHandler : public ScriptPromiseResolver {
+ WTF_MAKE_NONCOPYABLE(SetMediaKeysHandler);
+
+public:
+ static ScriptPromise create(ScriptState*, HTMLMediaElement&, MediaKeys*);
+ virtual ~SetMediaKeysHandler();
+
+private:
+ SetMediaKeysHandler(ScriptState*, HTMLMediaElement&, MediaKeys*);
+ void timerFired(Timer<SetMediaKeysHandler>*);
+
+ void clearExistingMediaKeys();
+ void setNewMediaKeys();
+ void finish();
+
+ void reportSetFailed(ExceptionCode, const String& errorMessage);
+
+ RawPtrWillBeMember<HTMLMediaElement> m_element;
+ Persistent<MediaKeys> m_newMediaKeys;
+ Timer<SetMediaKeysHandler> m_timer;
+};
+
+typedef Function<void()> SuccessCallback;
+typedef Function<void(ExceptionCode, const String&)> FailureCallback;
+
+// Represents the result used when setContentDecryptionModule() is called.
+// Calls |success| if result is resolved, |failure| is result is rejected.
+class SetContentDecryptionModuleResult FINAL : public ContentDecryptionModuleResult {
+public:
+ SetContentDecryptionModuleResult(SuccessCallback success, FailureCallback failure)
+ : m_successCallback(success)
+ , m_failureCallback(failure)
+ {
+ }
+
+ // ContentDecryptionModuleResult implementation.
+ virtual void complete() OVERRIDE
+ {
+ m_successCallback();
+ }
+
+ virtual void completeWithSession(blink::WebContentDecryptionModuleResult::SessionStatus status) OVERRIDE
+ {
+ ASSERT_NOT_REACHED();
+ m_failureCallback(InvalidStateError, "Unexpected completion.");
+ }
+
+ virtual void completeWithError(blink::WebContentDecryptionModuleException code, unsigned long systemCode, const blink::WebString& message) OVERRIDE
+ {
+ m_failureCallback(WebCdmExceptionToExceptionCode(code), message);
+ }
+
+private:
+ SuccessCallback m_successCallback;
+ FailureCallback m_failureCallback;
+};
+
+ScriptPromise SetMediaKeysHandler::create(ScriptState* scriptState, HTMLMediaElement& element, MediaKeys* mediaKeys)
+{
+ RefPtr<SetMediaKeysHandler> handler = adoptRef(new SetMediaKeysHandler(scriptState, element, mediaKeys));
+ handler->suspendIfNeeded();
+ handler->keepAliveWhilePending();
+ return handler->promise();
+}
+
+SetMediaKeysHandler::SetMediaKeysHandler(ScriptState* scriptState, HTMLMediaElement& element, MediaKeys* mediaKeys)
+ : ScriptPromiseResolver(scriptState)
+ , m_element(element)
+ , m_newMediaKeys(mediaKeys)
+ , m_timer(this, &SetMediaKeysHandler::timerFired)
+{
+ WTF_LOG(Media, "SetMediaKeysHandler::SetMediaKeysHandler");
+
+ // 3. Run the remaining steps asynchronously.
+ m_timer.startOneShot(0, FROM_HERE);
+}
+
+SetMediaKeysHandler::~SetMediaKeysHandler()
+{
+}
+
+void SetMediaKeysHandler::timerFired(Timer<SetMediaKeysHandler>*)
+{
+ clearExistingMediaKeys();
+}
+
+void SetMediaKeysHandler::clearExistingMediaKeys()
+{
+ WTF_LOG(Media, "SetMediaKeysHandler::clearExistingMediaKeys");
+ HTMLMediaElementEncryptedMedia& thisElement = HTMLMediaElementEncryptedMedia::from(*m_element);
+
+ // 3.1 If mediaKeys is not null, it is already in use by another media
+ // element, and the user agent is unable to use it with this element,
+ // reject promise with a new DOMException whose name is
+ // "QuotaExceededError".
+ // FIXME: Need to check whether mediaKeys is already in use by another
+ // media element.
+ // 3.2 If the mediaKeys attribute is not null, run the following steps:
+ if (thisElement.m_mediaKeys) {
+ // 3.2.1 If the user agent or CDM do not support removing the
+ // association, return a promise rejected with a new DOMException
+ // whose name is "NotSupportedError".
+ // (supported by blink).
+ // 3.2.2 If the association cannot currently be removed (i.e. during
+ // playback), return a promise rejected with a new DOMException
+ // whose name is "InvalidStateError".
+ if (m_element->webMediaPlayer()) {
+ reject(DOMException::create(InvalidStateError, "The existing MediaKeys object cannot be removed while a media resource is loaded."));
+ return;
+ }
+
+ // (next 2 steps not required as there is no player connected).
+ // 3.2.3 Stop using the CDM instance represented by the mediaKeys
+ // attribute to decrypt media data and remove the association
+ // with the media element.
+ // 3.2.4 If the preceding step failed, reject promise with a new
+ // DOMException whose name is the appropriate error name and
+ // that has an appropriate message.
+ }
+
+ // MediaKeys not currently set or no player connected, so continue on.
+ setNewMediaKeys();
+}
+
+void SetMediaKeysHandler::setNewMediaKeys()
+{
+ WTF_LOG(Media, "SetMediaKeysHandler::setNewMediaKeys");
+
+ // 3.3 If mediaKeys is not null, run the following steps:
+ if (m_newMediaKeys) {
+ // 3.3.1 Associate the CDM instance represented by mediaKeys with the
+ // media element for decrypting media data.
+ // 3.3.2 If the preceding step failed, run the following steps:
+ // (done in reportSetFailed()).
+ // 3.3.3 Run the Attempt to Resume Playback If Necessary algorithm on
+ // the media element. The user agent may choose to skip this
+ // step if it knows resuming will fail (i.e. mediaKeys has no
+ // sessions).
+ // (Handled in Chromium).
+ if (m_element->webMediaPlayer()) {
+ SuccessCallback successCallback = bind(&SetMediaKeysHandler::finish, this);
+ FailureCallback failureCallback = bind<ExceptionCode, const String&>(&SetMediaKeysHandler::reportSetFailed, this);
+ ContentDecryptionModuleResult* result = new SetContentDecryptionModuleResult(successCallback, failureCallback);
+ m_element->webMediaPlayer()->setContentDecryptionModule(m_newMediaKeys->contentDecryptionModule(), result->result());
+
+ // Don't do anything more until |result| is resolved (or rejected).
+ return;
+ }
+ }
+
+ // MediaKeys doesn't need to be set on the player, so continue on.
+ finish();
+}
+
+void SetMediaKeysHandler::finish()
+{
+ WTF_LOG(Media, "SetMediaKeysHandler::finish");
+ HTMLMediaElementEncryptedMedia& thisElement = HTMLMediaElementEncryptedMedia::from(*m_element);
+
+ // 3.4 Set the mediaKeys attribute to mediaKeys.
+ thisElement.m_mediaKeys = m_newMediaKeys;
+
+ // 3.5 Resolve promise with undefined.
+ resolve();
+}
+
+void SetMediaKeysHandler::reportSetFailed(ExceptionCode code, const String& errorMessage)
+{
+ WTF_LOG(Media, "SetMediaKeysHandler::reportSetFailed");
+ HTMLMediaElementEncryptedMedia& thisElement = HTMLMediaElementEncryptedMedia::from(*m_element);
+
+ // 3.3.2 If the preceding step failed, run the following steps:
+ // 3.3.2.1 Set the mediaKeys attribute to null.
+ thisElement.m_mediaKeys.clear();
+
+ // 3.3.2.2 Reject promise with a new DOMException whose name is the
+ // appropriate error name and that has an appropriate message.
+ reject(DOMException::create(code, errorMessage));
+}
+
HTMLMediaElementEncryptedMedia::HTMLMediaElementEncryptedMedia()
: m_emeMode(EmeModeNotSelected)
{
@@ -60,12 +248,11 @@ HTMLMediaElementEncryptedMedia& HTMLMediaElementEncryptedMedia::from(HTMLMediaEl
return *supplement;
}
-bool HTMLMediaElementEncryptedMedia::setEmeMode(EmeMode emeMode, ExceptionState& exceptionState)
+bool HTMLMediaElementEncryptedMedia::setEmeMode(EmeMode emeMode)
{
- if (m_emeMode != EmeModeNotSelected && m_emeMode != emeMode) {
- exceptionState.throwDOMException(InvalidStateError, "Mixed use of EME prefixed and unprefixed API not allowed.");
+ if (m_emeMode != EmeModeNotSelected && m_emeMode != emeMode)
return false;
- }
+
m_emeMode = emeMode;
return true;
}
@@ -81,28 +268,21 @@ MediaKeys* HTMLMediaElementEncryptedMedia::mediaKeys(HTMLMediaElement& element)
return thisElement.m_mediaKeys.get();
}
-void HTMLMediaElementEncryptedMedia::setMediaKeysInternal(HTMLMediaElement& element, MediaKeys* mediaKeys)
+ScriptPromise HTMLMediaElementEncryptedMedia::setMediaKeys(ScriptState* scriptState, HTMLMediaElement& element, MediaKeys* mediaKeys)
{
- if (m_mediaKeys == mediaKeys)
- return;
-
- ASSERT(m_emeMode == EmeModeUnprefixed);
- m_mediaKeys = mediaKeys;
-
- // If a player is connected, tell it that the CDM has changed.
- if (element.webMediaPlayer())
- element.webMediaPlayer()->setContentDecryptionModule(contentDecryptionModule());
-}
-
-void HTMLMediaElementEncryptedMedia::setMediaKeys(HTMLMediaElement& element, MediaKeys* mediaKeys, ExceptionState& exceptionState)
-{
- WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::setMediaKeys");
HTMLMediaElementEncryptedMedia& thisElement = HTMLMediaElementEncryptedMedia::from(element);
+ WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::setMediaKeys current(%p), new(%p)", thisElement.m_mediaKeys.get(), mediaKeys);
- if (!thisElement.setEmeMode(EmeModeUnprefixed, exceptionState))
- return;
+ if (!thisElement.setEmeMode(EmeModeUnprefixed))
+ return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(InvalidStateError, "Mixed use of EME prefixed and unprefixed API not allowed."));
+
+ // 1. If mediaKeys and the mediaKeys attribute are the same object, return
+ // a promise resolved with undefined.
+ if (thisElement.m_mediaKeys == mediaKeys)
+ return ScriptPromise::cast(scriptState, V8ValueTraits<V8UndefinedType>::toV8Value(V8UndefinedType(), scriptState->context()->Global(), scriptState->isolate()));
- thisElement.setMediaKeysInternal(element, mediaKeys);
+ // 2. Let promise be a new promise. Remaining steps done in handler.
+ return SetMediaKeysHandler::create(scriptState, element, mediaKeys);
}
// Create a MediaKeyNeededEvent for WD EME.
@@ -139,8 +319,10 @@ void HTMLMediaElementEncryptedMedia::generateKeyRequest(WebMediaPlayer* webMedia
{
WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::webkitGenerateKeyRequest");
- if (!setEmeMode(EmeModePrefixed, exceptionState))
+ if (!setEmeMode(EmeModePrefixed)) {
+ exceptionState.throwDOMException(InvalidStateError, "Mixed use of EME prefixed and unprefixed API not allowed.");
return;
+ }
if (keySystem.isEmpty()) {
exceptionState.throwDOMException(SyntaxError, "The key system provided is empty.");
@@ -177,8 +359,10 @@ void HTMLMediaElementEncryptedMedia::addKey(WebMediaPlayer* webMediaPlayer, cons
{
WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::webkitAddKey");
- if (!setEmeMode(EmeModePrefixed, exceptionState))
+ if (!setEmeMode(EmeModePrefixed)) {
+ exceptionState.throwDOMException(InvalidStateError, "Mixed use of EME prefixed and unprefixed API not allowed.");
return;
+ }
if (keySystem.isEmpty()) {
exceptionState.throwDOMException(SyntaxError, "The key system provided is empty.");
@@ -225,8 +409,10 @@ void HTMLMediaElementEncryptedMedia::cancelKeyRequest(WebMediaPlayer* webMediaPl
{
WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::webkitCancelKeyRequest");
- if (!setEmeMode(EmeModePrefixed, exceptionState))
+ if (!setEmeMode(EmeModePrefixed)) {
+ exceptionState.throwDOMException(InvalidStateError, "Mixed use of EME prefixed and unprefixed API not allowed.");
return;
+ }
if (keySystem.isEmpty()) {
exceptionState.throwDOMException(SyntaxError, "The key system provided is empty.");
@@ -341,7 +527,11 @@ void HTMLMediaElementEncryptedMedia::playerDestroyed(HTMLMediaElement& element)
#endif
HTMLMediaElementEncryptedMedia& thisElement = HTMLMediaElementEncryptedMedia::from(element);
- thisElement.setMediaKeysInternal(element, 0);
+ if (!thisElement.m_mediaKeys)
+ return;
+
+ ASSERT(thisElement.m_emeMode == EmeModeUnprefixed);
+ thisElement.m_mediaKeys.clear();
}
WebContentDecryptionModule* HTMLMediaElementEncryptedMedia::contentDecryptionModule(HTMLMediaElement& element)

Powered by Google App Engine
This is Rietveld 408576698