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
throwExceptionIfMediaKeyExceptionOccurred(const String & keySystem,const String & sessionId,blink::WebMediaPlayer::MediaKeyException exception,ExceptionState & exceptionState)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
HTMLMediaElementEncryptedMedia()40 HTMLMediaElementEncryptedMedia::HTMLMediaElementEncryptedMedia()
41 : m_emeMode(EmeModeNotSelected)
42 {
43 }
44
DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(HTMLMediaElementEncryptedMedia) const45 DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(HTMLMediaElementEncryptedMedia)
46
47 const char* HTMLMediaElementEncryptedMedia::supplementName()
48 {
49 return "HTMLMediaElementEncryptedMedia";
50 }
51
from(HTMLMediaElement & element)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
setEmeMode(EmeMode emeMode,ExceptionState & exceptionState)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
contentDecryptionModule()72 blink::WebContentDecryptionModule* HTMLMediaElementEncryptedMedia::contentDecryptionModule()
73 {
74 return m_mediaKeys ? m_mediaKeys->contentDecryptionModule() : 0;
75 }
76
mediaKeys(HTMLMediaElement & element)77 MediaKeys* HTMLMediaElementEncryptedMedia::mediaKeys(HTMLMediaElement& element)
78 {
79 HTMLMediaElementEncryptedMedia& thisElement = HTMLMediaElementEncryptedMedia::from(element);
80 return thisElement.m_mediaKeys.get();
81 }
82
setMediaKeysInternal(HTMLMediaElement & element,MediaKeys * mediaKeys)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
setMediaKeys(HTMLMediaElement & element,MediaKeys * mediaKeys,ExceptionState & exceptionState)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.
createNeedKeyEvent(const String & contentType,const unsigned char * initData,unsigned initDataLength)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.
createWebkitNeedKeyEvent(const String & contentType,const unsigned char * initData,unsigned initDataLength)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
webkitGenerateKeyRequest(HTMLMediaElement & element,const String & keySystem,PassRefPtr<Uint8Array> initData,ExceptionState & exceptionState)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
generateKeyRequest(blink::WebMediaPlayer * webMediaPlayer,const String & keySystem,PassRefPtr<Uint8Array> initData,ExceptionState & exceptionState)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
webkitGenerateKeyRequest(HTMLMediaElement & mediaElement,const String & keySystem,ExceptionState & exceptionState)165 void HTMLMediaElementEncryptedMedia::webkitGenerateKeyRequest(HTMLMediaElement& mediaElement, const String& keySystem, ExceptionState& exceptionState)
166 {
167 webkitGenerateKeyRequest(mediaElement, keySystem, Uint8Array::create(0), exceptionState);
168 }
169
webkitAddKey(HTMLMediaElement & element,const String & keySystem,PassRefPtr<Uint8Array> key,PassRefPtr<Uint8Array> initData,const String & sessionId,ExceptionState & exceptionState)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
addKey(blink::WebMediaPlayer * webMediaPlayer,const String & keySystem,PassRefPtr<Uint8Array> key,PassRefPtr<Uint8Array> initData,const String & sessionId,ExceptionState & exceptionState)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
webkitAddKey(HTMLMediaElement & mediaElement,const String & keySystem,PassRefPtr<Uint8Array> key,ExceptionState & exceptionState)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
webkitCancelKeyRequest(HTMLMediaElement & element,const String & keySystem,const String & sessionId,ExceptionState & exceptionState)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
cancelKeyRequest(blink::WebMediaPlayer * webMediaPlayer,const String & keySystem,const String & sessionId,ExceptionState & exceptionState)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
keyAdded(HTMLMediaElement & element,const String & keySystem,const String & sessionId)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
keyError(HTMLMediaElement & element,const String & keySystem,const String & sessionId,blink::WebMediaPlayerClient::MediaKeyErrorCode errorCode,unsigned short systemCode)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
keyMessage(HTMLMediaElement & element,const String & keySystem,const String & sessionId,const unsigned char * message,unsigned messageLength,const blink::WebURL & defaultURL)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
keyNeeded(HTMLMediaElement & element,const String & contentType,const unsigned char * initData,unsigned initDataLength)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
playerDestroyed(HTMLMediaElement & element)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
contentDecryptionModule(HTMLMediaElement & element)346 blink::WebContentDecryptionModule* HTMLMediaElementEncryptedMedia::contentDecryptionModule(HTMLMediaElement& element)
347 {
348 HTMLMediaElementEncryptedMedia& thisElement = HTMLMediaElementEncryptedMedia::from(element);
349 return thisElement.contentDecryptionModule();
350 }
351
trace(Visitor * visitor)352 void HTMLMediaElementEncryptedMedia::trace(Visitor* visitor)
353 {
354 visitor->trace(m_mediaKeys);
355 WillBeHeapSupplement<HTMLMediaElement>::trace(visitor);
356 }
357
358 } // namespace WebCore
359