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