Home | History | Annotate | Download | only in encryptedmedia
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "config.h"
      6 #include "modules/encryptedmedia/HTMLMediaElementEncryptedMedia.h"
      7 
      8 #include "bindings/v8/ExceptionState.h"
      9 #include "core/dom/ExceptionCode.h"
     10 #include "core/html/HTMLMediaElement.h"
     11 #include "core/html/MediaKeyError.h"
     12 #include "core/html/MediaKeyEvent.h"
     13 #include "modules/encryptedmedia/MediaKeyNeededEvent.h"
     14 #include "modules/encryptedmedia/MediaKeys.h"
     15 #include "platform/Logging.h"
     16 #include "platform/RuntimeEnabledFeatures.h"
     17 
     18 namespace WebCore {
     19 
     20 static void throwExceptionIfMediaKeyExceptionOccurred(const String& keySystem, const String& sessionId, blink::WebMediaPlayer::MediaKeyException exception, ExceptionState& exceptionState)
     21 {
     22     switch (exception) {
     23     case blink::WebMediaPlayer::MediaKeyExceptionNoError:
     24         return;
     25     case blink::WebMediaPlayer::MediaKeyExceptionInvalidPlayerState:
     26         exceptionState.throwDOMException(InvalidStateError, "The player is in an invalid state.");
     27         return;
     28     case blink::WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported:
     29         exceptionState.throwDOMException(NotSupportedError, "The key system provided ('" + keySystem +"') is not supported.");
     30         return;
     31     case blink::WebMediaPlayer::MediaKeyExceptionInvalidAccess:
     32         exceptionState.throwDOMException(InvalidAccessError, "The session ID provided ('" + sessionId + "') is invalid.");
     33         return;
     34     }
     35 
     36     ASSERT_NOT_REACHED();
     37     return;
     38 }
     39 
     40 HTMLMediaElementEncryptedMedia::HTMLMediaElementEncryptedMedia()
     41     : m_emeMode(EmeModeNotSelected)
     42 {
     43 }
     44 
     45 DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(HTMLMediaElementEncryptedMedia)
     46 
     47 const char* HTMLMediaElementEncryptedMedia::supplementName()
     48 {
     49     return "HTMLMediaElementEncryptedMedia";
     50 }
     51 
     52 HTMLMediaElementEncryptedMedia& HTMLMediaElementEncryptedMedia::from(HTMLMediaElement& element)
     53 {
     54     HTMLMediaElementEncryptedMedia* supplement = static_cast<HTMLMediaElementEncryptedMedia*>(WillBeHeapSupplement<HTMLMediaElement>::from(element, supplementName()));
     55     if (!supplement) {
     56         supplement = new HTMLMediaElementEncryptedMedia();
     57         provideTo(element, supplementName(), adoptPtrWillBeNoop(supplement));
     58     }
     59     return *supplement;
     60 }
     61 
     62 bool HTMLMediaElementEncryptedMedia::setEmeMode(EmeMode emeMode, ExceptionState& exceptionState)
     63 {
     64     if (m_emeMode != EmeModeNotSelected && m_emeMode != emeMode) {
     65         exceptionState.throwDOMException(InvalidStateError, "Mixed use of EME prefixed and unprefixed API not allowed.");
     66         return false;
     67     }
     68     m_emeMode = emeMode;
     69     return true;
     70 }
     71 
     72 blink::WebContentDecryptionModule* HTMLMediaElementEncryptedMedia::contentDecryptionModule()
     73 {
     74     return m_mediaKeys ? m_mediaKeys->contentDecryptionModule() : 0;
     75 }
     76 
     77 MediaKeys* HTMLMediaElementEncryptedMedia::mediaKeys(HTMLMediaElement& element)
     78 {
     79     HTMLMediaElementEncryptedMedia& thisElement = HTMLMediaElementEncryptedMedia::from(element);
     80     return thisElement.m_mediaKeys.get();
     81 }
     82 
     83 void HTMLMediaElementEncryptedMedia::setMediaKeysInternal(HTMLMediaElement& element, MediaKeys* mediaKeys)
     84 {
     85     if (m_mediaKeys == mediaKeys)
     86         return;
     87 
     88     ASSERT(m_emeMode == EmeModeUnprefixed);
     89     m_mediaKeys = mediaKeys;
     90 
     91     // If a player is connected, tell it that the CDM has changed.
     92     if (element.webMediaPlayer())
     93         element.webMediaPlayer()->setContentDecryptionModule(contentDecryptionModule());
     94 }
     95 
     96 void HTMLMediaElementEncryptedMedia::setMediaKeys(HTMLMediaElement& element, MediaKeys* mediaKeys, ExceptionState& exceptionState)
     97 {
     98     WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::setMediaKeys");
     99     HTMLMediaElementEncryptedMedia& thisElement = HTMLMediaElementEncryptedMedia::from(element);
    100 
    101     if (!thisElement.setEmeMode(EmeModeUnprefixed, exceptionState))
    102         return;
    103 
    104     thisElement.setMediaKeysInternal(element, mediaKeys);
    105 }
    106 
    107 // Create a MediaKeyNeededEvent for WD EME.
    108 static PassRefPtrWillBeRawPtr<Event> createNeedKeyEvent(const String& contentType, const unsigned char* initData, unsigned initDataLength)
    109 {
    110     MediaKeyNeededEventInit initializer;
    111     initializer.contentType = contentType;
    112     initializer.initData = Uint8Array::create(initData, initDataLength);
    113     initializer.bubbles = false;
    114     initializer.cancelable = false;
    115 
    116     return MediaKeyNeededEvent::create(EventTypeNames::needkey, initializer);
    117 }
    118 
    119 // Create a 'needkey' MediaKeyEvent for v0.1b EME.
    120 static PassRefPtrWillBeRawPtr<Event> createWebkitNeedKeyEvent(const String& contentType, const unsigned char* initData, unsigned initDataLength)
    121 {
    122     MediaKeyEventInit webkitInitializer;
    123     webkitInitializer.keySystem = String();
    124     webkitInitializer.sessionId = String();
    125     webkitInitializer.initData = Uint8Array::create(initData, initDataLength);
    126     webkitInitializer.bubbles = false;
    127     webkitInitializer.cancelable = false;
    128 
    129     return MediaKeyEvent::create(EventTypeNames::webkitneedkey, webkitInitializer);
    130 }
    131 
    132 void HTMLMediaElementEncryptedMedia::webkitGenerateKeyRequest(HTMLMediaElement& element, const String& keySystem, PassRefPtr<Uint8Array> initData, ExceptionState& exceptionState)
    133 {
    134     HTMLMediaElementEncryptedMedia::from(element).generateKeyRequest(element.webMediaPlayer(), keySystem, initData, exceptionState);
    135 }
    136 
    137 void HTMLMediaElementEncryptedMedia::generateKeyRequest(blink::WebMediaPlayer* webMediaPlayer, const String& keySystem, PassRefPtr<Uint8Array> initData, ExceptionState& exceptionState)
    138 {
    139     WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::webkitGenerateKeyRequest");
    140 
    141     if (!setEmeMode(EmeModePrefixed, exceptionState))
    142         return;
    143 
    144     if (keySystem.isEmpty()) {
    145         exceptionState.throwDOMException(SyntaxError, "The key system provided is empty.");
    146         return;
    147     }
    148 
    149     if (!webMediaPlayer) {
    150         exceptionState.throwDOMException(InvalidStateError, "No media has been loaded.");
    151         return;
    152     }
    153 
    154     const unsigned char* initDataPointer = 0;
    155     unsigned initDataLength = 0;
    156     if (initData) {
    157         initDataPointer = initData->data();
    158         initDataLength = initData->length();
    159     }
    160 
    161     blink::WebMediaPlayer::MediaKeyException result = webMediaPlayer->generateKeyRequest(keySystem, initDataPointer, initDataLength);
    162     throwExceptionIfMediaKeyExceptionOccurred(keySystem, String(), result, exceptionState);
    163 }
    164 
    165 void HTMLMediaElementEncryptedMedia::webkitGenerateKeyRequest(HTMLMediaElement& mediaElement, const String& keySystem, ExceptionState& exceptionState)
    166 {
    167     webkitGenerateKeyRequest(mediaElement, keySystem, Uint8Array::create(0), exceptionState);
    168 }
    169 
    170 void HTMLMediaElementEncryptedMedia::webkitAddKey(HTMLMediaElement& element, const String& keySystem, PassRefPtr<Uint8Array> key, PassRefPtr<Uint8Array> initData, const String& sessionId, ExceptionState& exceptionState)
    171 {
    172     HTMLMediaElementEncryptedMedia::from(element).addKey(element.webMediaPlayer(), keySystem, key, initData, sessionId, exceptionState);
    173 }
    174 
    175 void HTMLMediaElementEncryptedMedia::addKey(blink::WebMediaPlayer* webMediaPlayer, const String& keySystem, PassRefPtr<Uint8Array> key, PassRefPtr<Uint8Array> initData, const String& sessionId, ExceptionState& exceptionState)
    176 {
    177     WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::webkitAddKey");
    178 
    179     if (!setEmeMode(EmeModePrefixed, exceptionState))
    180         return;
    181 
    182     if (keySystem.isEmpty()) {
    183         exceptionState.throwDOMException(SyntaxError, "The key system provided is empty.");
    184         return;
    185     }
    186 
    187     if (!key) {
    188         exceptionState.throwDOMException(SyntaxError, "The key provided is invalid.");
    189         return;
    190     }
    191 
    192     if (!key->length()) {
    193         exceptionState.throwDOMException(TypeMismatchError, "The key provided is invalid.");
    194         return;
    195     }
    196 
    197     if (!webMediaPlayer) {
    198         exceptionState.throwDOMException(InvalidStateError, "No media has been loaded.");
    199         return;
    200     }
    201 
    202     const unsigned char* initDataPointer = 0;
    203     unsigned initDataLength = 0;
    204     if (initData) {
    205         initDataPointer = initData->data();
    206         initDataLength = initData->length();
    207     }
    208 
    209     blink::WebMediaPlayer::MediaKeyException result = webMediaPlayer->addKey(keySystem, key->data(), key->length(), initDataPointer, initDataLength, sessionId);
    210     throwExceptionIfMediaKeyExceptionOccurred(keySystem, sessionId, result, exceptionState);
    211 }
    212 
    213 void HTMLMediaElementEncryptedMedia::webkitAddKey(HTMLMediaElement& mediaElement, const String& keySystem, PassRefPtr<Uint8Array> key, ExceptionState& exceptionState)
    214 {
    215     webkitAddKey(mediaElement, keySystem, key, Uint8Array::create(0), String(), exceptionState);
    216 }
    217 
    218 void HTMLMediaElementEncryptedMedia::webkitCancelKeyRequest(HTMLMediaElement& element, const String& keySystem, const String& sessionId, ExceptionState& exceptionState)
    219 {
    220     HTMLMediaElementEncryptedMedia::from(element).cancelKeyRequest(element.webMediaPlayer(), keySystem, sessionId, exceptionState);
    221 }
    222 
    223 void HTMLMediaElementEncryptedMedia::cancelKeyRequest(blink::WebMediaPlayer* webMediaPlayer, const String& keySystem, const String& sessionId, ExceptionState& exceptionState)
    224 {
    225     WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::webkitCancelKeyRequest");
    226 
    227     if (!setEmeMode(EmeModePrefixed, exceptionState))
    228         return;
    229 
    230     if (keySystem.isEmpty()) {
    231         exceptionState.throwDOMException(SyntaxError, "The key system provided is empty.");
    232         return;
    233     }
    234 
    235     if (!webMediaPlayer) {
    236         exceptionState.throwDOMException(InvalidStateError, "No media has been loaded.");
    237         return;
    238     }
    239 
    240     blink::WebMediaPlayer::MediaKeyException result = webMediaPlayer->cancelKeyRequest(keySystem, sessionId);
    241     throwExceptionIfMediaKeyExceptionOccurred(keySystem, sessionId, result, exceptionState);
    242 }
    243 
    244 void HTMLMediaElementEncryptedMedia::keyAdded(HTMLMediaElement& element, const String& keySystem, const String& sessionId)
    245 {
    246     WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::mediaPlayerKeyAdded");
    247 
    248     MediaKeyEventInit initializer;
    249     initializer.keySystem = keySystem;
    250     initializer.sessionId = sessionId;
    251     initializer.bubbles = false;
    252     initializer.cancelable = false;
    253 
    254     RefPtrWillBeRawPtr<Event> event = MediaKeyEvent::create(EventTypeNames::webkitkeyadded, initializer);
    255     event->setTarget(&element);
    256     element.scheduleEvent(event.release());
    257 }
    258 
    259 void HTMLMediaElementEncryptedMedia::keyError(HTMLMediaElement& element, const String& keySystem, const String& sessionId, blink::WebMediaPlayerClient::MediaKeyErrorCode errorCode, unsigned short systemCode)
    260 {
    261     WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::mediaPlayerKeyError: sessionID=%s, errorCode=%d, systemCode=%d", sessionId.utf8().data(), errorCode, systemCode);
    262 
    263     MediaKeyError::Code mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN;
    264     switch (errorCode) {
    265     case blink::WebMediaPlayerClient::MediaKeyErrorCodeUnknown:
    266         mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN;
    267         break;
    268     case blink::WebMediaPlayerClient::MediaKeyErrorCodeClient:
    269         mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_CLIENT;
    270         break;
    271     case blink::WebMediaPlayerClient::MediaKeyErrorCodeService:
    272         mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_SERVICE;
    273         break;
    274     case blink::WebMediaPlayerClient::MediaKeyErrorCodeOutput:
    275         mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_OUTPUT;
    276         break;
    277     case blink::WebMediaPlayerClient::MediaKeyErrorCodeHardwareChange:
    278         mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_HARDWARECHANGE;
    279         break;
    280     case blink::WebMediaPlayerClient::MediaKeyErrorCodeDomain:
    281         mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_DOMAIN;
    282         break;
    283     }
    284 
    285     MediaKeyEventInit initializer;
    286     initializer.keySystem = keySystem;
    287     initializer.sessionId = sessionId;
    288     initializer.errorCode = MediaKeyError::create(mediaKeyErrorCode);
    289     initializer.systemCode = systemCode;
    290     initializer.bubbles = false;
    291     initializer.cancelable = false;
    292 
    293     RefPtrWillBeRawPtr<Event> event = MediaKeyEvent::create(EventTypeNames::webkitkeyerror, initializer);
    294     event->setTarget(&element);
    295     element.scheduleEvent(event.release());
    296 }
    297 
    298 void HTMLMediaElementEncryptedMedia::keyMessage(HTMLMediaElement& element, const String& keySystem, const String& sessionId, const unsigned char* message, unsigned messageLength, const blink::WebURL& defaultURL)
    299 {
    300     WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::mediaPlayerKeyMessage: sessionID=%s", sessionId.utf8().data());
    301 
    302     MediaKeyEventInit initializer;
    303     initializer.keySystem = keySystem;
    304     initializer.sessionId = sessionId;
    305     initializer.message = Uint8Array::create(message, messageLength);
    306     initializer.defaultURL = KURL(defaultURL);
    307     initializer.bubbles = false;
    308     initializer.cancelable = false;
    309 
    310     RefPtrWillBeRawPtr<Event> event = MediaKeyEvent::create(EventTypeNames::webkitkeymessage, initializer);
    311     event->setTarget(&element);
    312     element.scheduleEvent(event.release());
    313 }
    314 
    315 void HTMLMediaElementEncryptedMedia::keyNeeded(HTMLMediaElement& element, const String& contentType, const unsigned char* initData, unsigned initDataLength)
    316 {
    317     WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::mediaPlayerKeyNeeded: contentType=%s", contentType.utf8().data());
    318 
    319     if (RuntimeEnabledFeatures::encryptedMediaEnabled()) {
    320         // Send event for WD EME.
    321         RefPtrWillBeRawPtr<Event> event = createNeedKeyEvent(contentType, initData, initDataLength);
    322         event->setTarget(&element);
    323         element.scheduleEvent(event.release());
    324     }
    325 
    326     if (RuntimeEnabledFeatures::prefixedEncryptedMediaEnabled()) {
    327         // Send event for v0.1b EME.
    328         RefPtrWillBeRawPtr<Event> event = createWebkitNeedKeyEvent(contentType, initData, initDataLength);
    329         event->setTarget(&element);
    330         element.scheduleEvent(event.release());
    331     }
    332 }
    333 
    334 void HTMLMediaElementEncryptedMedia::playerDestroyed(HTMLMediaElement& element)
    335 {
    336 #if ENABLE(OILPAN)
    337     // FIXME: Oilpan: remove this once the media player is on the heap. crbug.com/378229
    338     if (element.isFinalizing())
    339         return;
    340 #endif
    341 
    342     HTMLMediaElementEncryptedMedia& thisElement = HTMLMediaElementEncryptedMedia::from(element);
    343     thisElement.setMediaKeysInternal(element, 0);
    344 }
    345 
    346 blink::WebContentDecryptionModule* HTMLMediaElementEncryptedMedia::contentDecryptionModule(HTMLMediaElement& element)
    347 {
    348     HTMLMediaElementEncryptedMedia& thisElement = HTMLMediaElementEncryptedMedia::from(element);
    349     return thisElement.contentDecryptionModule();
    350 }
    351 
    352 void HTMLMediaElementEncryptedMedia::trace(Visitor* visitor)
    353 {
    354     visitor->trace(m_mediaKeys);
    355     WillBeHeapSupplement<HTMLMediaElement>::trace(visitor);
    356 }
    357 
    358 } // namespace WebCore
    359