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