blob: bab33a8f6364bc624608704914371f5c3416cda8 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#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 "bindings/core/v8/V8Binding.h"
#include "core/dom/DOMException.h"
#include "core/dom/DOMTypedArray.h"
#include "core/dom/ExceptionCode.h"
#include "core/html/HTMLMediaElement.h"
#include "core/html/MediaKeyError.h"
#include "core/html/MediaKeyEvent.h"
#include "modules/encryptedmedia/ContentDecryptionModuleResultPromise.h"
#include "modules/encryptedmedia/EncryptedMediaUtils.h"
#include "modules/encryptedmedia/MediaEncryptedEvent.h"
#include "modules/encryptedmedia/MediaKeys.h"
#include "platform/ContentDecryptionModuleResult.h"
#include "platform/Logging.h"
#include "platform/RuntimeEnabledFeatures.h"
#include "wtf/Functional.h"
namespace blink {
static void throwExceptionIfMediaKeyExceptionOccurred(const String& keySystem, const String& sessionId, WebMediaPlayer::MediaKeyException exception, ExceptionState& exceptionState)
{
switch (exception) {
case WebMediaPlayer::MediaKeyExceptionNoError:
return;
case WebMediaPlayer::MediaKeyExceptionInvalidPlayerState:
exceptionState.throwDOMException(InvalidStateError, "The player is in an invalid state.");
return;
case WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported:
exceptionState.throwDOMException(NotSupportedError, "The key system provided ('" + keySystem +"') is not supported.");
return;
case WebMediaPlayer::MediaKeyExceptionInvalidAccess:
exceptionState.throwDOMException(InvalidAccessError, "The session ID provided ('" + sessionId + "') is invalid.");
return;
}
ASSERT_NOT_REACHED();
return;
}
// This class allows MediaKeys to be set asynchronously.
class SetMediaKeysHandler : public ScriptPromiseResolver {
WTF_MAKE_NONCOPYABLE(SetMediaKeysHandler);
public:
static ScriptPromise create(ScriptState*, HTMLMediaElement&, MediaKeys*);
~SetMediaKeysHandler() override;
DECLARE_VIRTUAL_TRACE();
private:
SetMediaKeysHandler(ScriptState*, HTMLMediaElement&, MediaKeys*);
void timerFired(Timer<SetMediaKeysHandler>*);
void clearExistingMediaKeys();
void setNewMediaKeys();
void finish();
void fail(ExceptionCode, const String& errorMessage);
void clearFailed(ExceptionCode, const String& errorMessage);
void setFailed(ExceptionCode, const String& errorMessage);
// Keep media element alive until promise is fulfilled
RefPtrWillBeMember<HTMLMediaElement> m_element;
Member<MediaKeys> m_newMediaKeys;
bool m_madeReservation;
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(PassOwnPtr<SuccessCallback> success, PassOwnPtr<FailureCallback> failure)
: m_successCallback(success)
, m_failureCallback(failure)
{
}
// ContentDecryptionModuleResult implementation.
void complete() override
{
(*m_successCallback)();
}
void completeWithContentDecryptionModule(WebContentDecryptionModule*) override
{
ASSERT_NOT_REACHED();
(*m_failureCallback)(InvalidStateError, "Unexpected completion.");
}
void completeWithSession(WebContentDecryptionModuleResult::SessionStatus status) override
{
ASSERT_NOT_REACHED();
(*m_failureCallback)(InvalidStateError, "Unexpected completion.");
}
void completeWithError(WebContentDecryptionModuleException code, unsigned long systemCode, const WebString& message) override
{
// Non-zero |systemCode| is appended to the |message|. If the |message|
// is empty, we'll report "Rejected with system code (systemCode)".
String errorString = message;
if (systemCode != 0) {
if (errorString.isEmpty())
errorString.append("Rejected with system code");
errorString.append(" (" + String::number(systemCode) + ")");
}
(*m_failureCallback)(WebCdmExceptionToExceptionCode(code), errorString);
}
private:
OwnPtr<SuccessCallback> m_successCallback;
OwnPtr<FailureCallback> m_failureCallback;
};
ScriptPromise SetMediaKeysHandler::create(ScriptState* scriptState, HTMLMediaElement& element, MediaKeys* mediaKeys)
{
SetMediaKeysHandler* handler = 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_madeReservation(false)
, m_timer(this, &SetMediaKeysHandler::timerFired)
{
WTF_LOG(Media, "SetMediaKeysHandler::SetMediaKeysHandler");
// 3. Run the remaining steps asynchronously.
m_timer.startOneShot(0, BLINK_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".
if (m_newMediaKeys) {
if (!m_newMediaKeys->reserveForMediaElement(m_element.get())) {
fail(QuotaExceededError, "The MediaKeys object is already in use by another media element.");
return;
}
// Note that |m_newMediaKeys| is now considered reserved for
// |m_element|, so it needs to be accepted or cancelled.
m_madeReservation = true;
}
// 3.2 If the mediaKeys attribute is not null, run the following steps:
if (thisElement.m_mediaKeys) {
WebMediaPlayer* mediaPlayer = m_element->webMediaPlayer();
if (mediaPlayer) {
// 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".
// 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".
// 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.
// (All 3 steps handled as needed in Chromium.)
OwnPtr<SuccessCallback> successCallback = bind(&SetMediaKeysHandler::setNewMediaKeys, this);
OwnPtr<FailureCallback> failureCallback = bind<ExceptionCode, const String&>(&SetMediaKeysHandler::clearFailed, this);
ContentDecryptionModuleResult* result = new SetContentDecryptionModuleResult(successCallback.release(), failureCallback.release());
mediaPlayer->setContentDecryptionModule(nullptr, result->result());
// Don't do anything more until |result| is resolved (or rejected).
return;
}
}
// 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 setFailed()).
// 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()) {
OwnPtr<SuccessCallback> successCallback = bind(&SetMediaKeysHandler::finish, this);
OwnPtr<FailureCallback> failureCallback = bind<ExceptionCode, const String&>(&SetMediaKeysHandler::setFailed, this);
ContentDecryptionModuleResult* result = new SetContentDecryptionModuleResult(successCallback.release(), failureCallback.release());
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.
if (thisElement.m_mediaKeys)
thisElement.m_mediaKeys->clearMediaElement();
thisElement.m_mediaKeys = m_newMediaKeys;
if (m_madeReservation)
m_newMediaKeys->acceptReservation();
// 3.5 Resolve promise with undefined.
resolve();
}
void SetMediaKeysHandler::fail(ExceptionCode code, const String& errorMessage)
{
// Reset ownership of |m_newMediaKeys|.
if (m_madeReservation)
m_newMediaKeys->cancelReservation();
// Reject promise with an appropriate error.
reject(DOMException::create(code, errorMessage));
}
void SetMediaKeysHandler::clearFailed(ExceptionCode code, const String& errorMessage)
{
WTF_LOG(Media, "SetMediaKeysHandler::clearFailed (%d, %s)", code, errorMessage.ascii().data());
// 3.2.4 If the preceding step failed (in setContentDecryptionModule()
// called from clearExistingMediaKeys()), reject promise with a new
// DOMException whose name is the appropriate error name and that
// has an appropriate message.
fail(code, errorMessage);
}
void SetMediaKeysHandler::setFailed(ExceptionCode code, const String& errorMessage)
{
WTF_LOG(Media, "SetMediaKeysHandler::setFailed (%d, %s)", code, errorMessage.ascii().data());
HTMLMediaElementEncryptedMedia& thisElement = HTMLMediaElementEncryptedMedia::from(*m_element);
// 3.3.2 If the preceding step failed (in setContentDecryptionModule()
// called from setNewMediaKeys()), 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.
fail(code, errorMessage);
}
DEFINE_TRACE(SetMediaKeysHandler)
{
visitor->trace(m_element);
visitor->trace(m_newMediaKeys);
ScriptPromiseResolver::trace(visitor);
}
HTMLMediaElementEncryptedMedia::HTMLMediaElementEncryptedMedia(HTMLMediaElement& element)
: m_mediaElement(&element)
, m_emeMode(EmeModeNotSelected)
, m_isWaitingForKey(false)
{
}
HTMLMediaElementEncryptedMedia::~HTMLMediaElementEncryptedMedia()
{
#if !ENABLE(OILPAN)
WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::~HTMLMediaElementEncryptedMedia");
if (m_mediaKeys)
m_mediaKeys->clearMediaElement();
#endif
}
const char* HTMLMediaElementEncryptedMedia::supplementName()
{
return "HTMLMediaElementEncryptedMedia";
}
HTMLMediaElementEncryptedMedia& HTMLMediaElementEncryptedMedia::from(HTMLMediaElement& element)
{
HTMLMediaElementEncryptedMedia* supplement = static_cast<HTMLMediaElementEncryptedMedia*>(WillBeHeapSupplement<HTMLMediaElement>::from(element, supplementName()));
if (!supplement) {
supplement = new HTMLMediaElementEncryptedMedia(element);
provideTo(element, supplementName(), adoptPtrWillBeNoop(supplement));
}
return *supplement;
}
bool HTMLMediaElementEncryptedMedia::setEmeMode(EmeMode emeMode)
{
if (m_emeMode != EmeModeNotSelected && m_emeMode != emeMode)
return false;
m_emeMode = emeMode;
return true;
}
MediaKeys* HTMLMediaElementEncryptedMedia::mediaKeys(HTMLMediaElement& element)
{
HTMLMediaElementEncryptedMedia& thisElement = HTMLMediaElementEncryptedMedia::from(element);
return thisElement.m_mediaKeys.get();
}
ScriptPromise HTMLMediaElementEncryptedMedia::setMediaKeys(ScriptState* scriptState, HTMLMediaElement& element, MediaKeys* mediaKeys)
{
HTMLMediaElementEncryptedMedia& thisElement = HTMLMediaElementEncryptedMedia::from(element);
WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::setMediaKeys current(%p), new(%p)", thisElement.m_mediaKeys.get(), mediaKeys);
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, v8::Undefined(scriptState->isolate()));
// 2. Let promise be a new promise. Remaining steps done in handler.
return SetMediaKeysHandler::create(scriptState, element, mediaKeys);
}
// Create a MediaEncryptedEvent for WD EME.
static PassRefPtrWillBeRawPtr<Event> createEncryptedEvent(WebEncryptedMediaInitDataType initDataType, const unsigned char* initData, unsigned initDataLength)
{
MediaEncryptedEventInit initializer;
initializer.setInitDataType(EncryptedMediaUtils::convertFromInitDataType(initDataType));
initializer.setInitData(DOMArrayBuffer::create(initData, initDataLength));
initializer.setBubbles(false);
initializer.setCancelable(false);
return MediaEncryptedEvent::create(EventTypeNames::encrypted, initializer);
}
// Create a 'needkey' MediaKeyEvent for v0.1b EME.
static PassRefPtrWillBeRawPtr<Event> createWebkitNeedKeyEvent(const unsigned char* initData, unsigned initDataLength)
{
MediaKeyEventInit webkitInitializer;
webkitInitializer.setInitData(DOMUint8Array::create(initData, initDataLength));
return MediaKeyEvent::create(EventTypeNames::webkitneedkey, webkitInitializer);
}
void HTMLMediaElementEncryptedMedia::webkitGenerateKeyRequest(HTMLMediaElement& element, const String& keySystem, PassRefPtr<DOMUint8Array> initData, ExceptionState& exceptionState)
{
HTMLMediaElementEncryptedMedia::from(element).generateKeyRequest(element.webMediaPlayer(), keySystem, initData, exceptionState);
}
void HTMLMediaElementEncryptedMedia::generateKeyRequest(WebMediaPlayer* webMediaPlayer, const String& keySystem, PassRefPtr<DOMUint8Array> initData, ExceptionState& exceptionState)
{
WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::webkitGenerateKeyRequest");
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.");
return;
}
if (!webMediaPlayer) {
exceptionState.throwDOMException(InvalidStateError, "No media has been loaded.");
return;
}
const unsigned char* initDataPointer = 0;
unsigned initDataLength = 0;
if (initData) {
initDataPointer = initData->data();
initDataLength = initData->length();
}
WebMediaPlayer::MediaKeyException result = webMediaPlayer->generateKeyRequest(keySystem, initDataPointer, initDataLength);
throwExceptionIfMediaKeyExceptionOccurred(keySystem, String(), result, exceptionState);
}
void HTMLMediaElementEncryptedMedia::webkitGenerateKeyRequest(HTMLMediaElement& mediaElement, const String& keySystem, ExceptionState& exceptionState)
{
webkitGenerateKeyRequest(mediaElement, keySystem, DOMUint8Array::create(0), exceptionState);
}
void HTMLMediaElementEncryptedMedia::webkitAddKey(HTMLMediaElement& element, const String& keySystem, PassRefPtr<DOMUint8Array> key, PassRefPtr<DOMUint8Array> initData, const String& sessionId, ExceptionState& exceptionState)
{
HTMLMediaElementEncryptedMedia::from(element).addKey(element.webMediaPlayer(), keySystem, key, initData, sessionId, exceptionState);
}
void HTMLMediaElementEncryptedMedia::addKey(WebMediaPlayer* webMediaPlayer, const String& keySystem, PassRefPtr<DOMUint8Array> key, PassRefPtr<DOMUint8Array> initData, const String& sessionId, ExceptionState& exceptionState)
{
WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::webkitAddKey");
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.");
return;
}
if (!key) {
exceptionState.throwDOMException(SyntaxError, "The key provided is invalid.");
return;
}
if (!key->length()) {
exceptionState.throwDOMException(TypeMismatchError, "The key provided is invalid.");
return;
}
if (!webMediaPlayer) {
exceptionState.throwDOMException(InvalidStateError, "No media has been loaded.");
return;
}
const unsigned char* initDataPointer = 0;
unsigned initDataLength = 0;
if (initData) {
initDataPointer = initData->data();
initDataLength = initData->length();
}
WebMediaPlayer::MediaKeyException result = webMediaPlayer->addKey(keySystem, key->data(), key->length(), initDataPointer, initDataLength, sessionId);
throwExceptionIfMediaKeyExceptionOccurred(keySystem, sessionId, result, exceptionState);
}
void HTMLMediaElementEncryptedMedia::webkitAddKey(HTMLMediaElement& mediaElement, const String& keySystem, PassRefPtr<DOMUint8Array> key, ExceptionState& exceptionState)
{
webkitAddKey(mediaElement, keySystem, key, DOMUint8Array::create(0), String(), exceptionState);
}
void HTMLMediaElementEncryptedMedia::webkitCancelKeyRequest(HTMLMediaElement& element, const String& keySystem, const String& sessionId, ExceptionState& exceptionState)
{
HTMLMediaElementEncryptedMedia::from(element).cancelKeyRequest(element.webMediaPlayer(), keySystem, sessionId, exceptionState);
}
void HTMLMediaElementEncryptedMedia::cancelKeyRequest(WebMediaPlayer* webMediaPlayer, const String& keySystem, const String& sessionId, ExceptionState& exceptionState)
{
WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::webkitCancelKeyRequest");
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.");
return;
}
if (!webMediaPlayer) {
exceptionState.throwDOMException(InvalidStateError, "No media has been loaded.");
return;
}
WebMediaPlayer::MediaKeyException result = webMediaPlayer->cancelKeyRequest(keySystem, sessionId);
throwExceptionIfMediaKeyExceptionOccurred(keySystem, sessionId, result, exceptionState);
}
void HTMLMediaElementEncryptedMedia::keyAdded(const WebString& keySystem, const WebString& sessionId)
{
WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::mediaPlayerKeyAdded");
MediaKeyEventInit initializer;
initializer.setKeySystem(keySystem);
initializer.setSessionId(sessionId);
RefPtrWillBeRawPtr<Event> event = MediaKeyEvent::create(EventTypeNames::webkitkeyadded, initializer);
event->setTarget(m_mediaElement);
m_mediaElement->scheduleEvent(event.release());
}
void HTMLMediaElementEncryptedMedia::keyError(const WebString& keySystem, const WebString& sessionId, WebMediaPlayerEncryptedMediaClient::MediaKeyErrorCode errorCode, unsigned short systemCode)
{
WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::mediaPlayerKeyError: sessionID=%s, errorCode=%d, systemCode=%d", sessionId.utf8().data(), errorCode, systemCode);
MediaKeyError::Code mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN;
switch (errorCode) {
case WebMediaPlayerEncryptedMediaClient::MediaKeyErrorCodeUnknown:
mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN;
break;
case WebMediaPlayerEncryptedMediaClient::MediaKeyErrorCodeClient:
mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_CLIENT;
break;
case WebMediaPlayerEncryptedMediaClient::MediaKeyErrorCodeService:
mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_SERVICE;
break;
case WebMediaPlayerEncryptedMediaClient::MediaKeyErrorCodeOutput:
mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_OUTPUT;
break;
case WebMediaPlayerEncryptedMediaClient::MediaKeyErrorCodeHardwareChange:
mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_HARDWARECHANGE;
break;
case WebMediaPlayerEncryptedMediaClient::MediaKeyErrorCodeDomain:
mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_DOMAIN;
break;
}
MediaKeyEventInit initializer;
initializer.setKeySystem(keySystem);
initializer.setSessionId(sessionId);
initializer.setErrorCode(MediaKeyError::create(mediaKeyErrorCode));
initializer.setSystemCode(systemCode);
RefPtrWillBeRawPtr<Event> event = MediaKeyEvent::create(EventTypeNames::webkitkeyerror, initializer);
event->setTarget(m_mediaElement);
m_mediaElement->scheduleEvent(event.release());
}
void HTMLMediaElementEncryptedMedia::keyMessage(const WebString& keySystem, const WebString& sessionId, const unsigned char* message, unsigned messageLength, const WebURL& defaultURL)
{
WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::mediaPlayerKeyMessage: sessionID=%s", sessionId.utf8().data());
MediaKeyEventInit initializer;
initializer.setKeySystem(keySystem);
initializer.setSessionId(sessionId);
initializer.setMessage(DOMUint8Array::create(message, messageLength));
initializer.setDefaultURL(KURL(defaultURL));
RefPtrWillBeRawPtr<Event> event = MediaKeyEvent::create(EventTypeNames::webkitkeymessage, initializer);
event->setTarget(m_mediaElement);
m_mediaElement->scheduleEvent(event.release());
}
void HTMLMediaElementEncryptedMedia::encrypted(WebEncryptedMediaInitDataType initDataType, const unsigned char* initData, unsigned initDataLength)
{
WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::encrypted");
if (RuntimeEnabledFeatures::encryptedMediaEnabled()) {
// Send event for WD EME.
RefPtrWillBeRawPtr<Event> event;
if (m_mediaElement->isMediaDataCORSSameOrigin(m_mediaElement->executionContext()->securityOrigin())) {
event = createEncryptedEvent(initDataType, initData, initDataLength);
} else {
// Current page is not allowed to see content from the media file,
// so don't return the initData. However, they still get an event.
event = createEncryptedEvent(WebEncryptedMediaInitDataType::Unknown, nullptr, 0);
}
event->setTarget(m_mediaElement);
m_mediaElement->scheduleEvent(event.release());
}
if (RuntimeEnabledFeatures::prefixedEncryptedMediaEnabled()) {
// Send event for v0.1b EME.
RefPtrWillBeRawPtr<Event> event = createWebkitNeedKeyEvent(initData, initDataLength);
event->setTarget(m_mediaElement);
m_mediaElement->scheduleEvent(event.release());
}
}
void HTMLMediaElementEncryptedMedia::didBlockPlaybackWaitingForKey()
{
WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::didBlockPlaybackWaitingForKey");
// From https://w3c.github.io/encrypted-media/#queue-waitingforkey:
// It should only be called when the HTMLMediaElement object is potentially
// playing and its readyState is equal to HAVE_FUTURE_DATA or greater.
// FIXME: Is this really required?
// 1. Let the media element be the specified HTMLMediaElement object.
// 2. If the media element's waiting for key value is false, queue a task
// to fire a simple event named waitingforkey at the media element.
if (!m_isWaitingForKey) {
RefPtrWillBeRawPtr<Event> event = Event::create(EventTypeNames::waitingforkey);
event->setTarget(m_mediaElement);
m_mediaElement->scheduleEvent(event.release());
}
// 3. Set the media element's waiting for key value to true.
m_isWaitingForKey = true;
// 4. Suspend playback.
// (Already done on the Chromium side by the decryptors.)
}
void HTMLMediaElementEncryptedMedia::didResumePlaybackBlockedForKey()
{
WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::didResumePlaybackBlockedForKey");
// Logic is on the Chromium side to attempt to resume playback when a new
// key is available. However, |m_isWaitingForKey| needs to be cleared so
// that a later waitingForKey() call can generate the event.
m_isWaitingForKey = false;
}
WebContentDecryptionModule* HTMLMediaElementEncryptedMedia::contentDecryptionModule()
{
return m_mediaKeys ? m_mediaKeys->contentDecryptionModule() : 0;
}
DEFINE_TRACE(HTMLMediaElementEncryptedMedia)
{
visitor->trace(m_mediaElement);
visitor->trace(m_mediaKeys);
WillBeHeapSupplement<HTMLMediaElement>::trace(visitor);
}
} // namespace blink