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.
6 #include "modules/encryptedmedia/HTMLMediaElementEncryptedMedia.h"
8 #include "bindings/core/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 #include "wtf/Uint8Array.h"
21 static void throwExceptionIfMediaKeyExceptionOccurred(const String& keySystem, const String& sessionId, WebMediaPlayer::MediaKeyException exception, ExceptionState& exceptionState)
24 case WebMediaPlayer::MediaKeyExceptionNoError:
26 case WebMediaPlayer::MediaKeyExceptionInvalidPlayerState:
27 exceptionState.throwDOMException(InvalidStateError, "The player is in an invalid state.");
29 case WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported:
30 exceptionState.throwDOMException(NotSupportedError, "The key system provided ('" + keySystem +"') is not supported.");
32 case WebMediaPlayer::MediaKeyExceptionInvalidAccess:
33 exceptionState.throwDOMException(InvalidAccessError, "The session ID provided ('" + sessionId + "') is invalid.");
41 HTMLMediaElementEncryptedMedia::HTMLMediaElementEncryptedMedia()
42 : m_emeMode(EmeModeNotSelected)
46 DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(HTMLMediaElementEncryptedMedia)
48 const char* HTMLMediaElementEncryptedMedia::supplementName()
50 return "HTMLMediaElementEncryptedMedia";
53 HTMLMediaElementEncryptedMedia& HTMLMediaElementEncryptedMedia::from(HTMLMediaElement& element)
55 HTMLMediaElementEncryptedMedia* supplement = static_cast<HTMLMediaElementEncryptedMedia*>(WillBeHeapSupplement<HTMLMediaElement>::from(element, supplementName()));
57 supplement = new HTMLMediaElementEncryptedMedia();
58 provideTo(element, supplementName(), adoptPtrWillBeNoop(supplement));
63 bool HTMLMediaElementEncryptedMedia::setEmeMode(EmeMode emeMode, ExceptionState& exceptionState)
65 if (m_emeMode != EmeModeNotSelected && m_emeMode != emeMode) {
66 exceptionState.throwDOMException(InvalidStateError, "Mixed use of EME prefixed and unprefixed API not allowed.");
73 WebContentDecryptionModule* HTMLMediaElementEncryptedMedia::contentDecryptionModule()
75 return m_mediaKeys ? m_mediaKeys->contentDecryptionModule() : 0;
78 MediaKeys* HTMLMediaElementEncryptedMedia::mediaKeys(HTMLMediaElement& element)
80 HTMLMediaElementEncryptedMedia& thisElement = HTMLMediaElementEncryptedMedia::from(element);
81 return thisElement.m_mediaKeys.get();
84 void HTMLMediaElementEncryptedMedia::setMediaKeysInternal(HTMLMediaElement& element, MediaKeys* mediaKeys)
86 if (m_mediaKeys == mediaKeys)
89 ASSERT(m_emeMode == EmeModeUnprefixed);
90 m_mediaKeys = mediaKeys;
92 // If a player is connected, tell it that the CDM has changed.
93 if (element.webMediaPlayer())
94 element.webMediaPlayer()->setContentDecryptionModule(contentDecryptionModule());
97 void HTMLMediaElementEncryptedMedia::setMediaKeys(HTMLMediaElement& element, MediaKeys* mediaKeys, ExceptionState& exceptionState)
99 WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::setMediaKeys");
100 HTMLMediaElementEncryptedMedia& thisElement = HTMLMediaElementEncryptedMedia::from(element);
102 if (!thisElement.setEmeMode(EmeModeUnprefixed, exceptionState))
105 thisElement.setMediaKeysInternal(element, mediaKeys);
108 // Create a MediaKeyNeededEvent for WD EME.
109 static PassRefPtrWillBeRawPtr<Event> createNeedKeyEvent(const String& contentType, const unsigned char* initData, unsigned initDataLength)
111 MediaKeyNeededEventInit initializer;
112 initializer.contentType = contentType;
113 initializer.initData = Uint8Array::create(initData, initDataLength);
114 initializer.bubbles = false;
115 initializer.cancelable = false;
117 return MediaKeyNeededEvent::create(EventTypeNames::needkey, initializer);
120 // Create a 'needkey' MediaKeyEvent for v0.1b EME.
121 static PassRefPtrWillBeRawPtr<Event> createWebkitNeedKeyEvent(const String& contentType, const unsigned char* initData, unsigned initDataLength)
123 MediaKeyEventInit webkitInitializer;
124 webkitInitializer.keySystem = String();
125 webkitInitializer.sessionId = String();
126 webkitInitializer.initData = Uint8Array::create(initData, initDataLength);
127 webkitInitializer.bubbles = false;
128 webkitInitializer.cancelable = false;
130 return MediaKeyEvent::create(EventTypeNames::webkitneedkey, webkitInitializer);
133 void HTMLMediaElementEncryptedMedia::webkitGenerateKeyRequest(HTMLMediaElement& element, const String& keySystem, PassRefPtr<Uint8Array> initData, ExceptionState& exceptionState)
135 HTMLMediaElementEncryptedMedia::from(element).generateKeyRequest(element.webMediaPlayer(), keySystem, initData, exceptionState);
138 void HTMLMediaElementEncryptedMedia::generateKeyRequest(WebMediaPlayer* webMediaPlayer, const String& keySystem, PassRefPtr<Uint8Array> initData, ExceptionState& exceptionState)
140 WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::webkitGenerateKeyRequest");
142 if (!setEmeMode(EmeModePrefixed, exceptionState))
145 if (keySystem.isEmpty()) {
146 exceptionState.throwDOMException(SyntaxError, "The key system provided is empty.");
150 if (!webMediaPlayer) {
151 exceptionState.throwDOMException(InvalidStateError, "No media has been loaded.");
155 const unsigned char* initDataPointer = 0;
156 unsigned initDataLength = 0;
158 initDataPointer = initData->data();
159 initDataLength = initData->length();
162 WebMediaPlayer::MediaKeyException result = webMediaPlayer->generateKeyRequest(keySystem, initDataPointer, initDataLength);
163 throwExceptionIfMediaKeyExceptionOccurred(keySystem, String(), result, exceptionState);
166 void HTMLMediaElementEncryptedMedia::webkitGenerateKeyRequest(HTMLMediaElement& mediaElement, const String& keySystem, ExceptionState& exceptionState)
168 webkitGenerateKeyRequest(mediaElement, keySystem, Uint8Array::create(0), exceptionState);
171 void HTMLMediaElementEncryptedMedia::webkitAddKey(HTMLMediaElement& element, const String& keySystem, PassRefPtr<Uint8Array> key, PassRefPtr<Uint8Array> initData, const String& sessionId, ExceptionState& exceptionState)
173 HTMLMediaElementEncryptedMedia::from(element).addKey(element.webMediaPlayer(), keySystem, key, initData, sessionId, exceptionState);
176 void HTMLMediaElementEncryptedMedia::addKey(WebMediaPlayer* webMediaPlayer, const String& keySystem, PassRefPtr<Uint8Array> key, PassRefPtr<Uint8Array> initData, const String& sessionId, ExceptionState& exceptionState)
178 WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::webkitAddKey");
180 if (!setEmeMode(EmeModePrefixed, exceptionState))
183 if (keySystem.isEmpty()) {
184 exceptionState.throwDOMException(SyntaxError, "The key system provided is empty.");
189 exceptionState.throwDOMException(SyntaxError, "The key provided is invalid.");
193 if (!key->length()) {
194 exceptionState.throwDOMException(TypeMismatchError, "The key provided is invalid.");
198 if (!webMediaPlayer) {
199 exceptionState.throwDOMException(InvalidStateError, "No media has been loaded.");
203 const unsigned char* initDataPointer = 0;
204 unsigned initDataLength = 0;
206 initDataPointer = initData->data();
207 initDataLength = initData->length();
210 WebMediaPlayer::MediaKeyException result = webMediaPlayer->addKey(keySystem, key->data(), key->length(), initDataPointer, initDataLength, sessionId);
211 throwExceptionIfMediaKeyExceptionOccurred(keySystem, sessionId, result, exceptionState);
214 void HTMLMediaElementEncryptedMedia::webkitAddKey(HTMLMediaElement& mediaElement, const String& keySystem, PassRefPtr<Uint8Array> key, ExceptionState& exceptionState)
216 webkitAddKey(mediaElement, keySystem, key, Uint8Array::create(0), String(), exceptionState);
219 void HTMLMediaElementEncryptedMedia::webkitCancelKeyRequest(HTMLMediaElement& element, const String& keySystem, const String& sessionId, ExceptionState& exceptionState)
221 HTMLMediaElementEncryptedMedia::from(element).cancelKeyRequest(element.webMediaPlayer(), keySystem, sessionId, exceptionState);
224 void HTMLMediaElementEncryptedMedia::cancelKeyRequest(WebMediaPlayer* webMediaPlayer, const String& keySystem, const String& sessionId, ExceptionState& exceptionState)
226 WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::webkitCancelKeyRequest");
228 if (!setEmeMode(EmeModePrefixed, exceptionState))
231 if (keySystem.isEmpty()) {
232 exceptionState.throwDOMException(SyntaxError, "The key system provided is empty.");
236 if (!webMediaPlayer) {
237 exceptionState.throwDOMException(InvalidStateError, "No media has been loaded.");
241 WebMediaPlayer::MediaKeyException result = webMediaPlayer->cancelKeyRequest(keySystem, sessionId);
242 throwExceptionIfMediaKeyExceptionOccurred(keySystem, sessionId, result, exceptionState);
245 void HTMLMediaElementEncryptedMedia::keyAdded(HTMLMediaElement& element, const String& keySystem, const String& sessionId)
247 WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::mediaPlayerKeyAdded");
249 MediaKeyEventInit initializer;
250 initializer.keySystem = keySystem;
251 initializer.sessionId = sessionId;
252 initializer.bubbles = false;
253 initializer.cancelable = false;
255 RefPtrWillBeRawPtr<Event> event = MediaKeyEvent::create(EventTypeNames::webkitkeyadded, initializer);
256 event->setTarget(&element);
257 element.scheduleEvent(event.release());
260 void HTMLMediaElementEncryptedMedia::keyError(HTMLMediaElement& element, const String& keySystem, const String& sessionId, WebMediaPlayerClient::MediaKeyErrorCode errorCode, unsigned short systemCode)
262 WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::mediaPlayerKeyError: sessionID=%s, errorCode=%d, systemCode=%d", sessionId.utf8().data(), errorCode, systemCode);
264 MediaKeyError::Code mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN;
266 case WebMediaPlayerClient::MediaKeyErrorCodeUnknown:
267 mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN;
269 case WebMediaPlayerClient::MediaKeyErrorCodeClient:
270 mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_CLIENT;
272 case WebMediaPlayerClient::MediaKeyErrorCodeService:
273 mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_SERVICE;
275 case WebMediaPlayerClient::MediaKeyErrorCodeOutput:
276 mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_OUTPUT;
278 case WebMediaPlayerClient::MediaKeyErrorCodeHardwareChange:
279 mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_HARDWARECHANGE;
281 case WebMediaPlayerClient::MediaKeyErrorCodeDomain:
282 mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_DOMAIN;
286 MediaKeyEventInit initializer;
287 initializer.keySystem = keySystem;
288 initializer.sessionId = sessionId;
289 initializer.errorCode = MediaKeyError::create(mediaKeyErrorCode);
290 initializer.systemCode = systemCode;
291 initializer.bubbles = false;
292 initializer.cancelable = false;
294 RefPtrWillBeRawPtr<Event> event = MediaKeyEvent::create(EventTypeNames::webkitkeyerror, initializer);
295 event->setTarget(&element);
296 element.scheduleEvent(event.release());
299 void HTMLMediaElementEncryptedMedia::keyMessage(HTMLMediaElement& element, const String& keySystem, const String& sessionId, const unsigned char* message, unsigned messageLength, const WebURL& defaultURL)
301 WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::mediaPlayerKeyMessage: sessionID=%s", sessionId.utf8().data());
303 MediaKeyEventInit initializer;
304 initializer.keySystem = keySystem;
305 initializer.sessionId = sessionId;
306 initializer.message = Uint8Array::create(message, messageLength);
307 initializer.defaultURL = KURL(defaultURL);
308 initializer.bubbles = false;
309 initializer.cancelable = false;
311 RefPtrWillBeRawPtr<Event> event = MediaKeyEvent::create(EventTypeNames::webkitkeymessage, initializer);
312 event->setTarget(&element);
313 element.scheduleEvent(event.release());
316 void HTMLMediaElementEncryptedMedia::keyNeeded(HTMLMediaElement& element, const String& contentType, const unsigned char* initData, unsigned initDataLength)
318 WTF_LOG(Media, "HTMLMediaElementEncryptedMedia::mediaPlayerKeyNeeded: contentType=%s", contentType.utf8().data());
320 if (RuntimeEnabledFeatures::encryptedMediaEnabled()) {
321 // Send event for WD EME.
322 RefPtrWillBeRawPtr<Event> event = createNeedKeyEvent(contentType, initData, initDataLength);
323 event->setTarget(&element);
324 element.scheduleEvent(event.release());
327 if (RuntimeEnabledFeatures::prefixedEncryptedMediaEnabled()) {
328 // Send event for v0.1b EME.
329 RefPtrWillBeRawPtr<Event> event = createWebkitNeedKeyEvent(contentType, initData, initDataLength);
330 event->setTarget(&element);
331 element.scheduleEvent(event.release());
335 void HTMLMediaElementEncryptedMedia::playerDestroyed(HTMLMediaElement& element)
338 // FIXME: Oilpan: remove this once the media player is on the heap. crbug.com/378229
339 if (element.isFinalizing())
343 HTMLMediaElementEncryptedMedia& thisElement = HTMLMediaElementEncryptedMedia::from(element);
344 thisElement.setMediaKeysInternal(element, 0);
347 WebContentDecryptionModule* HTMLMediaElementEncryptedMedia::contentDecryptionModule(HTMLMediaElement& element)
349 HTMLMediaElementEncryptedMedia& thisElement = HTMLMediaElementEncryptedMedia::from(element);
350 return thisElement.contentDecryptionModule();
353 void HTMLMediaElementEncryptedMedia::trace(Visitor* visitor)
355 visitor->trace(m_mediaKeys);
356 WillBeHeapSupplement<HTMLMediaElement>::trace(visitor);