tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

MediaDrmCDMProxy.cpp (15245B)


      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/MediaDrmCDMProxy.h"
      8 
      9 #include "MediaDrmCDMCallbackProxy.h"
     10 #include "mozilla/dom/MediaKeySession.h"
     11 
     12 namespace mozilla {
     13 
     14 MediaDrmSessionType ToMediaDrmSessionType(
     15    dom::MediaKeySessionType aSessionType) {
     16  switch (aSessionType) {
     17    case dom::MediaKeySessionType::Temporary:
     18      return kKeyStreaming;
     19    case dom::MediaKeySessionType::Persistent_license:
     20      return kKeyOffline;
     21    default:
     22      return kKeyStreaming;
     23  };
     24 }
     25 
     26 MediaDrmCDMProxy::MediaDrmCDMProxy(dom::MediaKeys* aKeys,
     27                                   const nsAString& aKeySystem,
     28                                   bool aDistinctiveIdentifierRequired,
     29                                   bool aPersistentStateRequired)
     30    : CDMProxy(aKeys, aKeySystem, aDistinctiveIdentifierRequired,
     31               aPersistentStateRequired),
     32      mCDM(nullptr),
     33      mShutdownCalled(false) {
     34  MOZ_ASSERT(NS_IsMainThread());
     35  MOZ_COUNT_CTOR(MediaDrmCDMProxy);
     36 }
     37 
     38 MediaDrmCDMProxy::~MediaDrmCDMProxy() { MOZ_COUNT_DTOR(MediaDrmCDMProxy); }
     39 
     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, nsLiteralCString(
     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 
     70 void MediaDrmCDMProxy::CreateSession(uint32_t aCreateSessionToken,
     71                                     dom::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 
     91 void MediaDrmCDMProxy::LoadSession(PromiseId aPromiseId,
     92                                   dom::MediaKeySessionType aSessionType,
     93                                   const nsAString& aSessionId) {
     94  // TODO: Implement LoadSession.
     95  RejectPromiseWithStateError(
     96      aPromiseId, "Currently Fennec does not support LoadSession"_ns);
     97 }
     98 
     99 void MediaDrmCDMProxy::SetServerCertificate(PromiseId aPromiseId,
    100                                            nsTArray<uint8_t>& aCert) {
    101  MOZ_ASSERT(NS_IsMainThread());
    102  MOZ_ASSERT(mOwnerThread);
    103 
    104  mOwnerThread->Dispatch(NewRunnableMethod<PromiseId, const nsTArray<uint8_t>>(
    105                             "MediaDrmCDMProxy::md_SetServerCertificate", this,
    106                             &MediaDrmCDMProxy::md_SetServerCertificate,
    107                             aPromiseId, std::move(aCert)),
    108                         NS_DISPATCH_NORMAL);
    109 }
    110 
    111 void MediaDrmCDMProxy::UpdateSession(const nsAString& aSessionId,
    112                                     PromiseId aPromiseId,
    113                                     nsTArray<uint8_t>& aResponse) {
    114  MOZ_ASSERT(NS_IsMainThread());
    115  MOZ_ASSERT(mOwnerThread);
    116  NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
    117 
    118  UniquePtr<UpdateSessionData> data(new UpdateSessionData());
    119  data->mPromiseId = aPromiseId;
    120  data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
    121  data->mResponse = std::move(aResponse);
    122 
    123  nsCOMPtr<nsIRunnable> task(NewRunnableMethod<UniquePtr<UpdateSessionData>&&>(
    124      "MediaDrmCDMProxy::md_UpdateSession", this,
    125      &MediaDrmCDMProxy::md_UpdateSession, std::move(data)));
    126  mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
    127 }
    128 
    129 void MediaDrmCDMProxy::CloseSession(const nsAString& aSessionId,
    130                                    PromiseId aPromiseId) {
    131  MOZ_ASSERT(NS_IsMainThread());
    132  MOZ_ASSERT(mOwnerThread);
    133  NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
    134 
    135  UniquePtr<SessionOpData> data(new SessionOpData());
    136  data->mPromiseId = aPromiseId;
    137  data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
    138 
    139  nsCOMPtr<nsIRunnable> task(NewRunnableMethod<UniquePtr<SessionOpData>&&>(
    140      "MediaDrmCDMProxy::md_CloseSession", this,
    141      &MediaDrmCDMProxy::md_CloseSession, std::move(data)));
    142  mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
    143 }
    144 
    145 void MediaDrmCDMProxy::RemoveSession(const nsAString& aSessionId,
    146                                     PromiseId aPromiseId) {
    147  // TODO: Implement RemoveSession.
    148  RejectPromiseWithStateError(
    149      aPromiseId, "Currently Fennec does not support RemoveSession"_ns);
    150 }
    151 
    152 void MediaDrmCDMProxy::QueryOutputProtectionStatus() {
    153  // TODO(bryce): determine if this is needed for Android and implement as
    154  // needed. See also `NotifyOutputProtectionStatus`.
    155 }
    156 
    157 void MediaDrmCDMProxy::NotifyOutputProtectionStatus(
    158    OutputProtectionCheckStatus aCheckStatus,
    159    OutputProtectionCaptureStatus aCaptureStatus) {
    160  // TODO(bryce): determine if this is needed for Android and implement as
    161  // needed. See also `QueryOutputProtectionStatus`.
    162 }
    163 
    164 void MediaDrmCDMProxy::Shutdown() {
    165  MOZ_ASSERT(NS_IsMainThread());
    166  MOZ_ASSERT(mOwnerThread);
    167  nsCOMPtr<nsIRunnable> task(NewRunnableMethod(
    168      "MediaDrmCDMProxy::md_Shutdown", this, &MediaDrmCDMProxy::md_Shutdown));
    169 
    170  mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
    171  mOwnerThread->Shutdown();
    172  mOwnerThread = nullptr;
    173  mKeys.Clear();
    174 }
    175 
    176 void MediaDrmCDMProxy::Terminated() {
    177  // TODO: Implement Terminated.
    178  // Should find a way to handle the case when remote side MediaDrm crashed.
    179 }
    180 
    181 void MediaDrmCDMProxy::OnSetSessionId(uint32_t aCreateSessionToken,
    182                                      const nsAString& aSessionId) {
    183  MOZ_ASSERT(NS_IsMainThread());
    184  if (mKeys.IsNull()) {
    185    return;
    186  }
    187 
    188  RefPtr<dom::MediaKeySession> session(
    189      mKeys->GetPendingSession(aCreateSessionToken));
    190  if (session) {
    191    session->SetSessionId(aSessionId);
    192  }
    193 }
    194 
    195 void MediaDrmCDMProxy::OnResolveLoadSessionPromise(uint32_t aPromiseId,
    196                                                   bool aSuccess) {
    197  MOZ_ASSERT(NS_IsMainThread());
    198  if (mKeys.IsNull()) {
    199    return;
    200  }
    201  mKeys->OnSessionLoaded(aPromiseId, aSuccess);
    202 }
    203 
    204 void MediaDrmCDMProxy::OnSessionMessage(const nsAString& aSessionId,
    205                                        dom::MediaKeyMessageType aMessageType,
    206                                        const nsTArray<uint8_t>& aMessage) {
    207  MOZ_ASSERT(NS_IsMainThread());
    208  if (mKeys.IsNull()) {
    209    return;
    210  }
    211  RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
    212  if (session) {
    213    session->DispatchKeyMessage(aMessageType, aMessage);
    214  }
    215 }
    216 
    217 void MediaDrmCDMProxy::OnExpirationChange(const nsAString& aSessionId,
    218                                          UnixTime aExpiryTime) {
    219  MOZ_ASSERT(NS_IsMainThread());
    220  if (mKeys.IsNull()) {
    221    return;
    222  }
    223  RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
    224  if (session) {
    225    session->SetExpiration(static_cast<double>(aExpiryTime));
    226  }
    227 }
    228 
    229 void MediaDrmCDMProxy::OnSessionClosed(
    230    const nsAString& aSessionId, dom::MediaKeySessionClosedReason aReason) {
    231  MOZ_ASSERT(NS_IsMainThread());
    232  if (mKeys.IsNull()) {
    233    return;
    234  }
    235  RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
    236  if (session) {
    237    session->OnClosed(aReason);
    238  }
    239 }
    240 
    241 void MediaDrmCDMProxy::OnSessionError(const nsAString& aSessionId,
    242                                      nsresult aException, uint32_t aSystemCode,
    243                                      const nsAString& aMsg) {
    244  MOZ_ASSERT(NS_IsMainThread());
    245  if (mKeys.IsNull()) {
    246    return;
    247  }
    248  RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
    249  if (session) {
    250    session->DispatchKeyError(aSystemCode);
    251  }
    252 }
    253 
    254 void MediaDrmCDMProxy::OnRejectPromise(uint32_t aPromiseId,
    255                                       ErrorResult&& aException,
    256                                       const nsCString& aMsg) {
    257  MOZ_ASSERT(NS_IsMainThread());
    258  RejectPromise(aPromiseId, std::move(aException), aMsg);
    259 }
    260 
    261 RefPtr<DecryptPromise> MediaDrmCDMProxy::Decrypt(MediaRawData* aSample) {
    262  MOZ_ASSERT_UNREACHABLE("Fennec could not handle decrypting individually");
    263  return nullptr;
    264 }
    265 
    266 void MediaDrmCDMProxy::OnDecrypted(uint32_t aId, DecryptStatus aResult,
    267                                   const nsTArray<uint8_t>& aDecryptedData) {
    268  MOZ_ASSERT_UNREACHABLE("Fennec could not handle decrypted event");
    269 }
    270 
    271 void MediaDrmCDMProxy::RejectPromise(PromiseId aId, ErrorResult&& aException,
    272                                     const nsCString& aReason) {
    273  if (NS_IsMainThread()) {
    274    if (!mKeys.IsNull()) {
    275      mKeys->RejectPromise(aId, std::move(aException), aReason);
    276    }
    277  } else {
    278    nsCOMPtr<nsIRunnable> task(
    279        new RejectPromiseTask(this, aId, std::move(aException), aReason));
    280    mMainThread->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
    281  }
    282 }
    283 
    284 void MediaDrmCDMProxy::RejectPromiseWithStateError(PromiseId aId,
    285                                                   const nsCString& aReason) {
    286  ErrorResult rv;
    287  rv.ThrowInvalidStateError(aReason);
    288  RejectPromise(aId, std::move(rv), aReason);
    289 }
    290 
    291 void MediaDrmCDMProxy::ResolvePromise(PromiseId aId) {
    292  if (NS_IsMainThread()) {
    293    if (!mKeys.IsNull()) {
    294      mKeys->ResolvePromise(aId);
    295    } else {
    296      NS_WARNING("MediaDrmCDMProxy unable to resolve promise!");
    297    }
    298  } else {
    299    nsCOMPtr<nsIRunnable> task;
    300    task =
    301        NewRunnableMethod<PromiseId>("MediaDrmCDMProxy::ResolvePromise", this,
    302                                     &MediaDrmCDMProxy::ResolvePromise, aId);
    303    mMainThread->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
    304  }
    305 }
    306 
    307 template <typename T>
    308 void MediaDrmCDMProxy::ResolvePromiseWithResult(PromiseId aId,
    309                                                const T& aResult) {
    310  if (NS_IsMainThread()) {
    311    if (!mKeys.IsNull()) {
    312      mKeys->ResolvePromiseWithResult(aId, aResult);
    313    } else {
    314      NS_WARNING("MediaDrmCDMProxy unable to resolve promise!");
    315    }
    316    return;
    317  }
    318 
    319  nsCOMPtr<nsIRunnable> task;
    320  task = NewRunnableMethod<PromiseId, T>(
    321      "MediaDrmCDMProxy::ResolvePromiseWithResult", this,
    322      &MediaDrmCDMProxy::ResolvePromiseWithResult<T>, aId, aResult);
    323  mMainThread->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
    324 }
    325 
    326 void MediaDrmCDMProxy::OnKeyStatusesChange(const nsAString& aSessionId) {
    327  MOZ_ASSERT(NS_IsMainThread());
    328  if (mKeys.IsNull()) {
    329    return;
    330  }
    331  RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
    332  if (session) {
    333    session->DispatchKeyStatusesChange();
    334  }
    335 }
    336 
    337 void MediaDrmCDMProxy::GetStatusForPolicy(
    338    PromiseId aPromiseId, const dom::HDCPVersion& aMinHdcpVersion) {
    339  // TODO: Implement GetStatusForPolicy.
    340  constexpr auto err =
    341      "Currently Fennec does not support GetStatusForPolicy"_ns;
    342 
    343  ErrorResult rv;
    344  rv.ThrowNotSupportedError(err);
    345  RejectPromise(aPromiseId, std::move(rv), err);
    346 }
    347 
    348 #ifdef DEBUG
    349 bool MediaDrmCDMProxy::IsOnOwnerThread() {
    350  return NS_GetCurrentThread() == mOwnerThread;
    351 }
    352 #endif
    353 
    354 const nsString& MediaDrmCDMProxy::GetMediaDrmStubId() const {
    355  MOZ_ASSERT(mCDM);
    356  return mCDM->GetMediaDrmStubId();
    357 }
    358 
    359 void MediaDrmCDMProxy::OnCDMCreated(uint32_t aPromiseId) {
    360  MOZ_ASSERT(NS_IsMainThread());
    361  if (mKeys.IsNull()) {
    362    return;
    363  }
    364 
    365  if (mCDM) {
    366    mKeys->OnCDMCreated(aPromiseId, 0);
    367    return;
    368  }
    369 
    370  // No CDM? Just reject the promise.
    371  constexpr auto err = "Null CDM in OnCDMCreated()"_ns;
    372  ErrorResult rv;
    373  rv.ThrowInvalidStateError(err);
    374  mKeys->RejectPromise(aPromiseId, std::move(rv), err);
    375 }
    376 
    377 void MediaDrmCDMProxy::md_Init(uint32_t aPromiseId) {
    378  MOZ_ASSERT(IsOnOwnerThread());
    379  MOZ_ASSERT(mCDM);
    380 
    381  UniquePtr<MediaDrmCDMCallbackProxy> callback(
    382      new MediaDrmCDMCallbackProxy(this));
    383  mCDM->Init(std::move(callback));
    384  nsCOMPtr<nsIRunnable> task(
    385      NewRunnableMethod<uint32_t>("MediaDrmCDMProxy::OnCDMCreated", this,
    386                                  &MediaDrmCDMProxy::OnCDMCreated, aPromiseId));
    387  mMainThread->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
    388 }
    389 
    390 void MediaDrmCDMProxy::md_CreateSession(UniquePtr<CreateSessionData>&& aData) {
    391  MOZ_ASSERT(IsOnOwnerThread());
    392 
    393  if (!mCDM) {
    394    RejectPromiseWithStateError(aData->mPromiseId,
    395                                "Null CDM in md_CreateSession"_ns);
    396    return;
    397  }
    398 
    399  mCDM->CreateSession(aData->mCreateSessionToken, aData->mPromiseId,
    400                      aData->mInitDataType, aData->mInitData,
    401                      ToMediaDrmSessionType(aData->mSessionType));
    402 }
    403 
    404 void MediaDrmCDMProxy::md_SetServerCertificate(PromiseId aPromiseId,
    405                                               const nsTArray<uint8_t>& aCert) {
    406  MOZ_ASSERT(IsOnOwnerThread());
    407 
    408  if (!mCDM) {
    409    RejectPromiseWithStateError(aPromiseId,
    410                                "Null CDM in md_SetServerCertificate"_ns);
    411    return;
    412  }
    413 
    414  if (mCDM->SetServerCertificate(aCert)) {
    415    ResolvePromiseWithResult(aPromiseId, true);
    416  } else {
    417    RejectPromiseWithStateError(
    418        aPromiseId, "MediaDrmCDMProxy unable to set server certificate"_ns);
    419  }
    420 }
    421 
    422 void MediaDrmCDMProxy::md_UpdateSession(UniquePtr<UpdateSessionData>&& aData) {
    423  MOZ_ASSERT(IsOnOwnerThread());
    424 
    425  if (!mCDM) {
    426    RejectPromiseWithStateError(aData->mPromiseId,
    427                                "Null CDM in md_UpdateSession"_ns);
    428    return;
    429  }
    430  mCDM->UpdateSession(aData->mPromiseId, aData->mSessionId, aData->mResponse);
    431 }
    432 
    433 void MediaDrmCDMProxy::md_CloseSession(UniquePtr<SessionOpData>&& aData) {
    434  MOZ_ASSERT(IsOnOwnerThread());
    435 
    436  if (!mCDM) {
    437    RejectPromiseWithStateError(aData->mPromiseId,
    438                                "Null CDM in md_CloseSession"_ns);
    439    return;
    440  }
    441  mCDM->CloseSession(aData->mPromiseId, aData->mSessionId);
    442 }
    443 
    444 void MediaDrmCDMProxy::md_Shutdown() {
    445  MOZ_ASSERT(IsOnOwnerThread());
    446  MOZ_ASSERT(mCDM);
    447  if (mShutdownCalled) {
    448    return;
    449  }
    450  mShutdownCalled = true;
    451  mCDM->Shutdown();
    452  mCDM = nullptr;
    453 }
    454 
    455 }  // namespace mozilla