root/Source/modules/encryptedmedia/HTMLMediaElementEncryptedMedia.cpp

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. throwExceptionIfMediaKeyExceptionOccurred
  2. supplementName
  3. from
  4. setEmeMode
  5. contentDecryptionModule
  6. mediaKeys
  7. setMediaKeysInternal
  8. setMediaKeys
  9. createNeedKeyEvent
  10. createWebkitNeedKeyEvent
  11. webkitGenerateKeyRequest
  12. generateKeyRequest
  13. webkitGenerateKeyRequest
  14. webkitAddKey
  15. addKey
  16. webkitAddKey
  17. webkitCancelKeyRequest
  18. cancelKeyRequest
  19. keyAdded
  20. keyError
  21. keyMessage
  22. keyNeeded
  23. playerDestroyed
  24. contentDecryptionModule

// 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 "config.h"
#include "modules/encryptedmedia/HTMLMediaElementEncryptedMedia.h"

#include "RuntimeEnabledFeatures.h"
#include "bindings/v8/ExceptionState.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 "platform/Logging.h"

namespace WebCore {

static void throwExceptionIfMediaKeyExceptionOccurred(const String& keySystem, const String& sessionId, blink::WebMediaPlayer::MediaKeyException exception, ExceptionState& exceptionState)
{
    switch (exception) {
    case blink::WebMediaPlayer::MediaKeyExceptionNoError:
        return;
    case blink::WebMediaPlayer::MediaKeyExceptionInvalidPlayerState:
        exceptionState.throwDOMException(InvalidStateError, "The player is in an invalid state.");
        return;
    case blink::WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported:
        exceptionState.throwDOMException(NotSupportedError, "The key system provided ('" + keySystem +"') is not supported.");
        return;
    case blink::WebMediaPlayer::MediaKeyExceptionInvalidAccess:
        exceptionState.throwDOMException(InvalidAccessError, "The session ID provided ('" + sessionId + "') is invalid.");
        return;
    }

    ASSERT_NOT_REACHED();
    return;
}

HTMLMediaElementEncryptedMedia::HTMLMediaElementEncryptedMedia()
    : m_emeMode(EmeModeNotSelected)
{
}

HTMLMediaElementEncryptedMedia::~HTMLMediaElementEncryptedMedia()
{
}

const char* HTMLMediaElementEncryptedMedia::supplementName()
{
    return "HTMLMediaElementEncryptedMedia";
}

HTMLMediaElementEncryptedMedia& HTMLMediaElementEncryptedMedia::from(HTMLMediaElement& element)
{
    HTMLMediaElementEncryptedMedia* supplement = static_cast<HTMLMediaElementEncryptedMedia*>(Supplement<HTMLMediaElement>::from(element, supplementName()));
    if (!supplement) {
        supplement = new HTMLMediaElementEncryptedMedia();
        provideTo(element, supplementName(), adoptPtr(supplement));
    }
    return *supplement;
}

bool HTMLMediaElementEncryptedMedia::setEmeMode(EmeMode emeMode, ExceptionState& exceptionState)
{
    if (m_emeMode != EmeModeNotSelected && m_emeMode != emeMode) {
        exceptionState.throwDOMException(InvalidStateError, "Mixed use of EME prefixed and unprefixed API not allowed.");
        return false;
    }
    m_emeMode = emeMode;
    return true;
}

blink::WebContentDecryptionModule* HTMLMediaElementEncryptedMedia::contentDecryptionModule()
{
    return m_mediaKeys ? m_mediaKeys->contentDecryptionModule() : 0;
}

MediaKeys* HTMLMediaElementEncryptedMedia::mediaKeys(HTMLMediaElement& element)
{
    HTMLMediaElementEncryptedMedia& thisElement = HTMLMediaElementEncryptedMedia::from(element);
    return thisElement.m_mediaKeys.get();
}

void HTMLMediaElementEncryptedMedia::setMediaKeysInternal(HTMLMediaElement& element, MediaKeys* mediaKeys)
{
    if (m_mediaKeys == mediaKeys)
        return;

    ASSERT(m_emeMode == EmeModeUnprefixed);

    if (m_mediaKeys)
        m_mediaKeys->setMediaElement(0);
    m_mediaKeys = mediaKeys;
    if (m_mediaKeys)
        m_mediaKeys->setMediaElement(&element);

    // 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);

    if (!thisElement.setEmeMode(EmeModeUnprefixed, exceptionState))
        return;

    thisElement.setMediaKeysInternal(element, mediaKeys);
}

// Create a MediaKeyNeededEvent for WD EME.
static PassRefPtrWillBeRawPtr<Event> createNeedKeyEvent(const String& contentType, const unsigned char* initData, unsigned initDataLength)
{
    MediaKeyNeededEventInit initializer;
    initializer.contentType = contentType;
    initializer.initData = Uint8Array::create(initData, initDataLength);
    initializer.bubbles = false;
    initializer.cancelable = false;

    return MediaKeyNeededEvent::create(EventTypeNames::needkey, initializer);
}

// Create a 'needkey' MediaKeyEvent for v0.1b EME.
static PassRefPtrWillBeRawPtr<Event> createWebkitNeedKeyEvent(const String& contentType, const unsigned char* initData, unsigned initDataLength)
{
    MediaKeyEventInit webkitInitializer;
    webkitInitializer.keySystem = String();
    webkitInitializer.sessionId = String();
    webkitInitializer.initData = Uint8Array::create(initData, initDataLength);
    webkitInitializer.bubbles = false;
    webkitInitializer.cancelable = false;

    return MediaKeyEvent::create(EventTypeNames::webkitneedkey, webkitInitializer);
}

void HTMLMediaElementEncryptedMedia::webkitGenerateKeyRequest(HTMLMediaElement& element, const String& keySystem, PassRefPtr<Uint8Array> initData, ExceptionState& exceptionState)
{
    HTMLMediaElementEncryptedMedia::from(element).generateKeyRequest(element.webMediaPlayer(), keySystem, initData, exceptionState);
}

void HTMLMediaElementEncryptedMedia::generateKeyRequest(blink::WebMediaPlayer* webMediaPlayer, const String& keySystem, PassRefPtr<Uint8Array> initData, ExceptionState& exceptionState)
{
    WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::webkitGenerateKeyRequest");

    if (!setEmeMode(EmeModePrefixed, exceptionState))
        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();
    }

    blink::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, Uint8Array::create(0), exceptionState);
}

void HTMLMediaElementEncryptedMedia::webkitAddKey(HTMLMediaElement& element, const String& keySystem, PassRefPtr<Uint8Array> key, PassRefPtr<Uint8Array> initData, const String& sessionId, ExceptionState& exceptionState)
{
    HTMLMediaElementEncryptedMedia::from(element).addKey(element.webMediaPlayer(), keySystem, key, initData, sessionId, exceptionState);
}

void HTMLMediaElementEncryptedMedia::addKey(blink::WebMediaPlayer* webMediaPlayer, const String& keySystem, PassRefPtr<Uint8Array> key, PassRefPtr<Uint8Array> initData, const String& sessionId, ExceptionState& exceptionState)
{
    WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::webkitAddKey");

    if (!setEmeMode(EmeModePrefixed, exceptionState))
        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();
    }

    blink::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<Uint8Array> key, ExceptionState& exceptionState)
{
    webkitAddKey(mediaElement, keySystem, key, Uint8Array::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(blink::WebMediaPlayer* webMediaPlayer, const String& keySystem, const String& sessionId, ExceptionState& exceptionState)
{
    WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::webkitCancelKeyRequest");

    if (!setEmeMode(EmeModePrefixed, exceptionState))
        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;
    }

    blink::WebMediaPlayer::MediaKeyException result = webMediaPlayer->cancelKeyRequest(keySystem, sessionId);
    throwExceptionIfMediaKeyExceptionOccurred(keySystem, sessionId, result, exceptionState);
}

void HTMLMediaElementEncryptedMedia::keyAdded(HTMLMediaElement& element, const String& keySystem, const String& sessionId)
{
    WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::mediaPlayerKeyAdded");

    MediaKeyEventInit initializer;
    initializer.keySystem = keySystem;
    initializer.sessionId = sessionId;
    initializer.bubbles = false;
    initializer.cancelable = false;

    RefPtrWillBeRawPtr<Event> event = MediaKeyEvent::create(EventTypeNames::webkitkeyadded, initializer);
    event->setTarget(&element);
    element.scheduleEvent(event.release());
}

void HTMLMediaElementEncryptedMedia::keyError(HTMLMediaElement& element, const String& keySystem, const String& sessionId, blink::WebMediaPlayerClient::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 blink::WebMediaPlayerClient::MediaKeyErrorCodeUnknown:
        mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN;
        break;
    case blink::WebMediaPlayerClient::MediaKeyErrorCodeClient:
        mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_CLIENT;
        break;
    case blink::WebMediaPlayerClient::MediaKeyErrorCodeService:
        mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_SERVICE;
        break;
    case blink::WebMediaPlayerClient::MediaKeyErrorCodeOutput:
        mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_OUTPUT;
        break;
    case blink::WebMediaPlayerClient::MediaKeyErrorCodeHardwareChange:
        mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_HARDWARECHANGE;
        break;
    case blink::WebMediaPlayerClient::MediaKeyErrorCodeDomain:
        mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_DOMAIN;
        break;
    }

    MediaKeyEventInit initializer;
    initializer.keySystem = keySystem;
    initializer.sessionId = sessionId;
    initializer.errorCode = MediaKeyError::create(mediaKeyErrorCode);
    initializer.systemCode = systemCode;
    initializer.bubbles = false;
    initializer.cancelable = false;

    RefPtrWillBeRawPtr<Event> event = MediaKeyEvent::create(EventTypeNames::webkitkeyerror, initializer);
    event->setTarget(&element);
    element.scheduleEvent(event.release());
}

void HTMLMediaElementEncryptedMedia::keyMessage(HTMLMediaElement& element, const String& keySystem, const String& sessionId, const unsigned char* message, unsigned messageLength, const blink::WebURL& defaultURL)
{
    WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::mediaPlayerKeyMessage: sessionID=%s", sessionId.utf8().data());

    MediaKeyEventInit initializer;
    initializer.keySystem = keySystem;
    initializer.sessionId = sessionId;
    initializer.message = Uint8Array::create(message, messageLength);
    initializer.defaultURL = KURL(defaultURL);
    initializer.bubbles = false;
    initializer.cancelable = false;

    RefPtrWillBeRawPtr<Event> event = MediaKeyEvent::create(EventTypeNames::webkitkeymessage, initializer);
    event->setTarget(&element);
    element.scheduleEvent(event.release());
}

void HTMLMediaElementEncryptedMedia::keyNeeded(HTMLMediaElement& element, const String& contentType, const unsigned char* initData, unsigned initDataLength)
{
    WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::mediaPlayerKeyNeeded: contentType=%s", contentType.utf8().data());

    if (RuntimeEnabledFeatures::encryptedMediaEnabled()) {
        // Send event for WD EME.
        RefPtrWillBeRawPtr<Event> event = createNeedKeyEvent(contentType, initData, initDataLength);
        event->setTarget(&element);
        element.scheduleEvent(event.release());
    }

    if (RuntimeEnabledFeatures::prefixedEncryptedMediaEnabled()) {
        // Send event for v0.1b EME.
        RefPtrWillBeRawPtr<Event> event = createWebkitNeedKeyEvent(contentType, initData, initDataLength);
        event->setTarget(&element);
        element.scheduleEvent(event.release());
    }
}

void HTMLMediaElementEncryptedMedia::playerDestroyed(HTMLMediaElement& element)
{
    HTMLMediaElementEncryptedMedia& thisElement = HTMLMediaElementEncryptedMedia::from(element);
    thisElement.setMediaKeysInternal(element, 0);
}

blink::WebContentDecryptionModule* HTMLMediaElementEncryptedMedia::contentDecryptionModule(HTMLMediaElement& element)
{
    HTMLMediaElementEncryptedMedia& thisElement = HTMLMediaElementEncryptedMedia::from(element);
    return thisElement.contentDecryptionModule();
}

} // namespace WebCore

/* [<][>][^][v][top][bottom][index][help] */