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