1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "mozilla/dom/MediaKeySession.h"
8 #include "mozilla/MediaDrmCDMProxy.h"
9 #include "MediaDrmCDMCallbackProxy.h"
10
11 namespace mozilla {
12
ToMediaDrmSessionType(dom::MediaKeySessionType aSessionType)13 MediaDrmSessionType ToMediaDrmSessionType(
14 dom::MediaKeySessionType aSessionType) {
15 switch (aSessionType) {
16 case dom::MediaKeySessionType::Temporary:
17 return kKeyStreaming;
18 case dom::MediaKeySessionType::Persistent_license:
19 return kKeyOffline;
20 default:
21 return kKeyStreaming;
22 };
23 }
24
MediaDrmCDMProxy(dom::MediaKeys * aKeys,const nsAString & aKeySystem,bool aDistinctiveIdentifierRequired,bool aPersistentStateRequired,nsISerialEventTarget * aMainThread)25 MediaDrmCDMProxy::MediaDrmCDMProxy(dom::MediaKeys* aKeys,
26 const nsAString& aKeySystem,
27 bool aDistinctiveIdentifierRequired,
28 bool aPersistentStateRequired,
29 nsISerialEventTarget* aMainThread)
30 : CDMProxy(aKeys, aKeySystem, aDistinctiveIdentifierRequired,
31 aPersistentStateRequired, aMainThread),
32 mCDM(nullptr),
33 mShutdownCalled(false) {
34 MOZ_ASSERT(NS_IsMainThread());
35 MOZ_COUNT_CTOR(MediaDrmCDMProxy);
36 }
37
~MediaDrmCDMProxy()38 MediaDrmCDMProxy::~MediaDrmCDMProxy() { MOZ_COUNT_DTOR(MediaDrmCDMProxy); }
39
Init(PromiseId aPromiseId,const nsAString & aOrigin,const nsAString & aTopLevelOrigin,const nsAString & aName)40 void MediaDrmCDMProxy::Init(PromiseId aPromiseId, const nsAString& aOrigin,
41 const nsAString& aTopLevelOrigin,
42 const nsAString& aName) {
43 MOZ_ASSERT(NS_IsMainThread());
44 NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
45
46 EME_LOG("MediaDrmCDMProxy::Init (%s, %s) %s",
47 NS_ConvertUTF16toUTF8(aOrigin).get(),
48 NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(),
49 NS_ConvertUTF16toUTF8(aName).get());
50
51 // Create a thread to work with cdm.
52 if (!mOwnerThread) {
53 nsresult rv =
54 NS_NewNamedThread("MDCDMThread", getter_AddRefs(mOwnerThread));
55 if (NS_FAILED(rv)) {
56 RejectPromiseWithStateError(
57 aPromiseId, NS_LITERAL_CSTRING(
58 "Couldn't create CDM thread MediaDrmCDMProxy::Init"));
59 return;
60 }
61 }
62
63 mCDM = mozilla::MakeUnique<MediaDrmProxySupport>(mKeySystem);
64 nsCOMPtr<nsIRunnable> task(
65 NewRunnableMethod<uint32_t>("MediaDrmCDMProxy::md_Init", this,
66 &MediaDrmCDMProxy::md_Init, aPromiseId));
67 mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
68 }
69
CreateSession(uint32_t aCreateSessionToken,MediaKeySessionType aSessionType,PromiseId aPromiseId,const nsAString & aInitDataType,nsTArray<uint8_t> & aInitData)70 void MediaDrmCDMProxy::CreateSession(uint32_t aCreateSessionToken,
71 MediaKeySessionType aSessionType,
72 PromiseId aPromiseId,
73 const nsAString& aInitDataType,
74 nsTArray<uint8_t>& aInitData) {
75 MOZ_ASSERT(NS_IsMainThread());
76 MOZ_ASSERT(mOwnerThread);
77
78 UniquePtr<CreateSessionData> data(new CreateSessionData());
79 data->mSessionType = aSessionType;
80 data->mCreateSessionToken = aCreateSessionToken;
81 data->mPromiseId = aPromiseId;
82 data->mInitDataType = NS_ConvertUTF16toUTF8(aInitDataType);
83 data->mInitData = std::move(aInitData);
84
85 nsCOMPtr<nsIRunnable> task(NewRunnableMethod<UniquePtr<CreateSessionData>&&>(
86 "MediaDrmCDMProxy::md_CreateSession", this,
87 &MediaDrmCDMProxy::md_CreateSession, std::move(data)));
88 mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
89 }
90
LoadSession(PromiseId aPromiseId,dom::MediaKeySessionType aSessionType,const nsAString & aSessionId)91 void MediaDrmCDMProxy::LoadSession(PromiseId aPromiseId,
92 dom::MediaKeySessionType aSessionType,
93 const nsAString& aSessionId) {
94 // TODO: Implement LoadSession.
95 RejectPromiseWithStateError(
96 aPromiseId,
97 NS_LITERAL_CSTRING("Currently Fennec does not support LoadSession"));
98 }
99
SetServerCertificate(PromiseId aPromiseId,nsTArray<uint8_t> & aCert)100 void MediaDrmCDMProxy::SetServerCertificate(PromiseId aPromiseId,
101 nsTArray<uint8_t>& aCert) {
102 MOZ_ASSERT(NS_IsMainThread());
103 MOZ_ASSERT(mOwnerThread);
104
105 mOwnerThread->Dispatch(NewRunnableMethod<PromiseId, const nsTArray<uint8_t>>(
106 "MediaDrmCDMProxy::md_SetServerCertificate", this,
107 &MediaDrmCDMProxy::md_SetServerCertificate,
108 aPromiseId, std::move(aCert)),
109 NS_DISPATCH_NORMAL);
110 }
111
UpdateSession(const nsAString & aSessionId,PromiseId aPromiseId,nsTArray<uint8_t> & aResponse)112 void MediaDrmCDMProxy::UpdateSession(const nsAString& aSessionId,
113 PromiseId aPromiseId,
114 nsTArray<uint8_t>& aResponse) {
115 MOZ_ASSERT(NS_IsMainThread());
116 MOZ_ASSERT(mOwnerThread);
117 NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
118
119 UniquePtr<UpdateSessionData> data(new UpdateSessionData());
120 data->mPromiseId = aPromiseId;
121 data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
122 data->mResponse = std::move(aResponse);
123
124 nsCOMPtr<nsIRunnable> task(NewRunnableMethod<UniquePtr<UpdateSessionData>&&>(
125 "MediaDrmCDMProxy::md_UpdateSession", this,
126 &MediaDrmCDMProxy::md_UpdateSession, std::move(data)));
127 mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
128 }
129
CloseSession(const nsAString & aSessionId,PromiseId aPromiseId)130 void MediaDrmCDMProxy::CloseSession(const nsAString& aSessionId,
131 PromiseId aPromiseId) {
132 MOZ_ASSERT(NS_IsMainThread());
133 MOZ_ASSERT(mOwnerThread);
134 NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
135
136 UniquePtr<SessionOpData> data(new SessionOpData());
137 data->mPromiseId = aPromiseId;
138 data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
139
140 nsCOMPtr<nsIRunnable> task(NewRunnableMethod<UniquePtr<SessionOpData>&&>(
141 "MediaDrmCDMProxy::md_CloseSession", this,
142 &MediaDrmCDMProxy::md_CloseSession, std::move(data)));
143 mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
144 }
145
RemoveSession(const nsAString & aSessionId,PromiseId aPromiseId)146 void MediaDrmCDMProxy::RemoveSession(const nsAString& aSessionId,
147 PromiseId aPromiseId) {
148 // TODO: Implement RemoveSession.
149 RejectPromiseWithStateError(
150 aPromiseId,
151 NS_LITERAL_CSTRING("Currently Fennec does not support RemoveSession"));
152 }
153
Shutdown()154 void MediaDrmCDMProxy::Shutdown() {
155 MOZ_ASSERT(NS_IsMainThread());
156 MOZ_ASSERT(mOwnerThread);
157 nsCOMPtr<nsIRunnable> task(NewRunnableMethod(
158 "MediaDrmCDMProxy::md_Shutdown", this, &MediaDrmCDMProxy::md_Shutdown));
159
160 mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
161 mOwnerThread->Shutdown();
162 mOwnerThread = nullptr;
163 }
164
Terminated()165 void MediaDrmCDMProxy::Terminated() {
166 // TODO: Implement Terminated.
167 // Should find a way to handle the case when remote side MediaDrm crashed.
168 }
169
GetNodeId() const170 const nsCString& MediaDrmCDMProxy::GetNodeId() const { return mNodeId; }
171
OnSetSessionId(uint32_t aCreateSessionToken,const nsAString & aSessionId)172 void MediaDrmCDMProxy::OnSetSessionId(uint32_t aCreateSessionToken,
173 const nsAString& aSessionId) {
174 MOZ_ASSERT(NS_IsMainThread());
175 if (mKeys.IsNull()) {
176 return;
177 }
178
179 RefPtr<dom::MediaKeySession> session(
180 mKeys->GetPendingSession(aCreateSessionToken));
181 if (session) {
182 session->SetSessionId(aSessionId);
183 }
184 }
185
OnResolveLoadSessionPromise(uint32_t aPromiseId,bool aSuccess)186 void MediaDrmCDMProxy::OnResolveLoadSessionPromise(uint32_t aPromiseId,
187 bool aSuccess) {
188 MOZ_ASSERT(NS_IsMainThread());
189 if (mKeys.IsNull()) {
190 return;
191 }
192 mKeys->OnSessionLoaded(aPromiseId, aSuccess);
193 }
194
OnSessionMessage(const nsAString & aSessionId,dom::MediaKeyMessageType aMessageType,const nsTArray<uint8_t> & aMessage)195 void MediaDrmCDMProxy::OnSessionMessage(const nsAString& aSessionId,
196 dom::MediaKeyMessageType aMessageType,
197 const nsTArray<uint8_t>& aMessage) {
198 MOZ_ASSERT(NS_IsMainThread());
199 if (mKeys.IsNull()) {
200 return;
201 }
202 RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
203 if (session) {
204 session->DispatchKeyMessage(aMessageType, aMessage);
205 }
206 }
207
OnExpirationChange(const nsAString & aSessionId,UnixTime aExpiryTime)208 void MediaDrmCDMProxy::OnExpirationChange(const nsAString& aSessionId,
209 UnixTime aExpiryTime) {
210 MOZ_ASSERT(NS_IsMainThread());
211 if (mKeys.IsNull()) {
212 return;
213 }
214 RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
215 if (session) {
216 session->SetExpiration(static_cast<double>(aExpiryTime));
217 }
218 }
219
OnSessionClosed(const nsAString & aSessionId)220 void MediaDrmCDMProxy::OnSessionClosed(const nsAString& aSessionId) {
221 MOZ_ASSERT(NS_IsMainThread());
222 if (mKeys.IsNull()) {
223 return;
224 }
225 RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
226 if (session) {
227 session->OnClosed();
228 }
229 }
230
OnSessionError(const nsAString & aSessionId,nsresult aException,uint32_t aSystemCode,const nsAString & aMsg)231 void MediaDrmCDMProxy::OnSessionError(const nsAString& aSessionId,
232 nsresult aException, uint32_t aSystemCode,
233 const nsAString& aMsg) {
234 MOZ_ASSERT(NS_IsMainThread());
235 if (mKeys.IsNull()) {
236 return;
237 }
238 RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
239 if (session) {
240 session->DispatchKeyError(aSystemCode);
241 }
242 }
243
OnRejectPromise(uint32_t aPromiseId,ErrorResult && aException,const nsCString & aMsg)244 void MediaDrmCDMProxy::OnRejectPromise(uint32_t aPromiseId,
245 ErrorResult&& aException,
246 const nsCString& aMsg) {
247 MOZ_ASSERT(NS_IsMainThread());
248 RejectPromise(aPromiseId, std::move(aException), aMsg);
249 }
250
Decrypt(MediaRawData * aSample)251 RefPtr<DecryptPromise> MediaDrmCDMProxy::Decrypt(MediaRawData* aSample) {
252 MOZ_ASSERT_UNREACHABLE("Fennec could not handle decrypting individually");
253 return nullptr;
254 }
255
OnDecrypted(uint32_t aId,DecryptStatus aResult,const nsTArray<uint8_t> & aDecryptedData)256 void MediaDrmCDMProxy::OnDecrypted(uint32_t aId, DecryptStatus aResult,
257 const nsTArray<uint8_t>& aDecryptedData) {
258 MOZ_ASSERT_UNREACHABLE("Fennec could not handle decrypted event");
259 }
260
RejectPromise(PromiseId aId,ErrorResult && aException,const nsCString & aReason)261 void MediaDrmCDMProxy::RejectPromise(PromiseId aId, ErrorResult&& aException,
262 const nsCString& aReason) {
263 if (NS_IsMainThread()) {
264 if (!mKeys.IsNull()) {
265 mKeys->RejectPromise(aId, std::move(aException), aReason);
266 }
267 } else {
268 nsCOMPtr<nsIRunnable> task(
269 new RejectPromiseTask(this, aId, std::move(aException), aReason));
270 mMainThread->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
271 }
272 }
273
RejectPromiseWithStateError(PromiseId aId,const nsCString & aReason)274 void MediaDrmCDMProxy::RejectPromiseWithStateError(PromiseId aId,
275 const nsCString& aReason) {
276 ErrorResult rv;
277 rv.ThrowInvalidStateError(aReason);
278 RejectPromise(aId, std::move(rv), aReason);
279 }
280
ResolvePromise(PromiseId aId)281 void MediaDrmCDMProxy::ResolvePromise(PromiseId aId) {
282 if (NS_IsMainThread()) {
283 if (!mKeys.IsNull()) {
284 mKeys->ResolvePromise(aId);
285 } else {
286 NS_WARNING("MediaDrmCDMProxy unable to resolve promise!");
287 }
288 } else {
289 nsCOMPtr<nsIRunnable> task;
290 task =
291 NewRunnableMethod<PromiseId>("MediaDrmCDMProxy::ResolvePromise", this,
292 &MediaDrmCDMProxy::ResolvePromise, aId);
293 mMainThread->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
294 }
295 }
296
297 template <typename T>
ResolvePromiseWithResult(PromiseId aId,const T & aResult)298 void MediaDrmCDMProxy::ResolvePromiseWithResult(PromiseId aId,
299 const T& aResult) {
300 if (NS_IsMainThread()) {
301 if (!mKeys.IsNull()) {
302 mKeys->ResolvePromiseWithResult(aId, aResult);
303 } else {
304 NS_WARNING("MediaDrmCDMProxy unable to resolve promise!");
305 }
306 return;
307 }
308
309 nsCOMPtr<nsIRunnable> task;
310 task = NewRunnableMethod<PromiseId, T>(
311 "MediaDrmCDMProxy::ResolvePromiseWithResult", this,
312 &MediaDrmCDMProxy::ResolvePromiseWithResult<T>, aId, aResult);
313 mMainThread->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
314 }
315
KeySystem() const316 const nsString& MediaDrmCDMProxy::KeySystem() const { return mKeySystem; }
317
Capabilites()318 DataMutex<CDMCaps>& MediaDrmCDMProxy::Capabilites() { return mCapabilites; }
319
OnKeyStatusesChange(const nsAString & aSessionId)320 void MediaDrmCDMProxy::OnKeyStatusesChange(const nsAString& aSessionId) {
321 MOZ_ASSERT(NS_IsMainThread());
322 if (mKeys.IsNull()) {
323 return;
324 }
325 RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
326 if (session) {
327 session->DispatchKeyStatusesChange();
328 }
329 }
330
GetStatusForPolicy(PromiseId aPromiseId,const nsAString & aMinHdcpVersion)331 void MediaDrmCDMProxy::GetStatusForPolicy(PromiseId aPromiseId,
332 const nsAString& aMinHdcpVersion) {
333 // TODO: Implement GetStatusForPolicy.
334 NS_NAMED_LITERAL_CSTRING(
335 err, "Currently Fennec does not support GetStatusForPolicy");
336 ErrorResult rv;
337 rv.ThrowNotSupportedError(err);
338 RejectPromise(aPromiseId, std::move(rv), err);
339 }
340
341 #ifdef DEBUG
IsOnOwnerThread()342 bool MediaDrmCDMProxy::IsOnOwnerThread() {
343 return NS_GetCurrentThread() == mOwnerThread;
344 }
345 #endif
346
GetMediaDrmStubId() const347 const nsString& MediaDrmCDMProxy::GetMediaDrmStubId() const {
348 MOZ_ASSERT(mCDM);
349 return mCDM->GetMediaDrmStubId();
350 }
351
OnCDMCreated(uint32_t aPromiseId)352 void MediaDrmCDMProxy::OnCDMCreated(uint32_t aPromiseId) {
353 MOZ_ASSERT(NS_IsMainThread());
354 if (mKeys.IsNull()) {
355 return;
356 }
357
358 if (mCDM) {
359 mKeys->OnCDMCreated(aPromiseId, 0);
360 return;
361 }
362
363 // No CDM? Just reject the promise.
364 NS_NAMED_LITERAL_CSTRING(err, "Null CDM in OnCDMCreated()");
365 ErrorResult rv;
366 rv.ThrowInvalidStateError(err);
367 mKeys->RejectPromise(aPromiseId, std::move(rv), err);
368 }
369
md_Init(uint32_t aPromiseId)370 void MediaDrmCDMProxy::md_Init(uint32_t aPromiseId) {
371 MOZ_ASSERT(IsOnOwnerThread());
372 MOZ_ASSERT(mCDM);
373
374 mCallback.reset(new MediaDrmCDMCallbackProxy(this));
375 mCDM->Init(mCallback.get());
376 nsCOMPtr<nsIRunnable> task(
377 NewRunnableMethod<uint32_t>("MediaDrmCDMProxy::OnCDMCreated", this,
378 &MediaDrmCDMProxy::OnCDMCreated, aPromiseId));
379 mMainThread->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
380 }
381
md_CreateSession(UniquePtr<CreateSessionData> && aData)382 void MediaDrmCDMProxy::md_CreateSession(UniquePtr<CreateSessionData>&& aData) {
383 MOZ_ASSERT(IsOnOwnerThread());
384
385 if (!mCDM) {
386 RejectPromiseWithStateError(
387 aData->mPromiseId, NS_LITERAL_CSTRING("Null CDM in md_CreateSession"));
388 return;
389 }
390
391 mCDM->CreateSession(aData->mCreateSessionToken, aData->mPromiseId,
392 aData->mInitDataType, aData->mInitData,
393 ToMediaDrmSessionType(aData->mSessionType));
394 }
395
md_SetServerCertificate(PromiseId aPromiseId,const nsTArray<uint8_t> & aCert)396 void MediaDrmCDMProxy::md_SetServerCertificate(PromiseId aPromiseId,
397 const nsTArray<uint8_t>& aCert) {
398 MOZ_ASSERT(IsOnOwnerThread());
399
400 if (!mCDM) {
401 RejectPromiseWithStateError(
402 aPromiseId, NS_LITERAL_CSTRING("Null CDM in md_SetServerCertificate"));
403 return;
404 }
405
406 if (mCDM->SetServerCertificate(aCert)) {
407 ResolvePromiseWithResult(aPromiseId, true);
408 } else {
409 RejectPromiseWithStateError(
410 aPromiseId, NS_LITERAL_CSTRING(
411 "MediaDrmCDMProxy unable to set server certificate"));
412 }
413 }
414
md_UpdateSession(UniquePtr<UpdateSessionData> && aData)415 void MediaDrmCDMProxy::md_UpdateSession(UniquePtr<UpdateSessionData>&& aData) {
416 MOZ_ASSERT(IsOnOwnerThread());
417
418 if (!mCDM) {
419 RejectPromiseWithStateError(
420 aData->mPromiseId, NS_LITERAL_CSTRING("Null CDM in md_UpdateSession"));
421 return;
422 }
423 mCDM->UpdateSession(aData->mPromiseId, aData->mSessionId, aData->mResponse);
424 }
425
md_CloseSession(UniquePtr<SessionOpData> && aData)426 void MediaDrmCDMProxy::md_CloseSession(UniquePtr<SessionOpData>&& aData) {
427 MOZ_ASSERT(IsOnOwnerThread());
428
429 if (!mCDM) {
430 RejectPromiseWithStateError(
431 aData->mPromiseId, NS_LITERAL_CSTRING("Null CDM in md_CloseSession"));
432 return;
433 }
434 mCDM->CloseSession(aData->mPromiseId, aData->mSessionId);
435 }
436
md_Shutdown()437 void MediaDrmCDMProxy::md_Shutdown() {
438 MOZ_ASSERT(IsOnOwnerThread());
439 MOZ_ASSERT(mCDM);
440 if (mShutdownCalled) {
441 return;
442 }
443 mShutdownCalled = true;
444 mCDM->Shutdown();
445 mCDM = nullptr;
446 }
447
448 } // namespace mozilla
449