• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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