tor-browser

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

ChromiumCDMProxy.cpp (24277B)


      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 "ChromiumCDMProxy.h"
      8 
      9 #include "ChromiumCDMCallbackProxy.h"
     10 #include "GMPService.h"
     11 #include "GMPUtils.h"
     12 #include "MediaResult.h"
     13 #include "content_decryption_module.h"
     14 #include "mozilla/StaticPrefs_media.h"
     15 #include "mozilla/dom/MediaKeySession.h"
     16 #include "mozilla/dom/MediaKeysBinding.h"
     17 #include "nsPrintfCString.h"
     18 
     19 #define NS_DispatchToMainThread(...) CompileError_UseAbstractMainThreadInstead
     20 
     21 namespace mozilla {
     22 
     23 ChromiumCDMProxy::ChromiumCDMProxy(dom::MediaKeys* aKeys,
     24                                   const nsAString& aKeySystem,
     25                                   GMPCrashHelper* aCrashHelper,
     26                                   bool aDistinctiveIdentifierRequired,
     27                                   bool aPersistentStateRequired)
     28    : CDMProxy(aKeys, aKeySystem, aDistinctiveIdentifierRequired,
     29               aPersistentStateRequired),
     30      mCrashHelper(aCrashHelper),
     31      mCDMMutex("ChromiumCDMProxy"),
     32      mGMPThread(GetGMPThread()) {
     33  MOZ_ASSERT(NS_IsMainThread());
     34 }
     35 
     36 ChromiumCDMProxy::~ChromiumCDMProxy() {
     37  EME_LOG("ChromiumCDMProxy::~ChromiumCDMProxy(this=%p)", this);
     38 }
     39 
     40 void ChromiumCDMProxy::Init(PromiseId aPromiseId, const nsAString& aOrigin,
     41                            const nsAString& aTopLevelOrigin,
     42                            const nsAString& aGMPName) {
     43  MOZ_ASSERT(NS_IsMainThread());
     44 
     45  RefPtr<GMPCrashHelper> helper(std::move(mCrashHelper));
     46 
     47  NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
     48 
     49  EME_LOG("ChromiumCDMProxy::Init(this=%p, pid=%" PRIu32
     50          ", origin=%s, topLevelOrigin=%s, "
     51          "gmp=%s)",
     52          this, aPromiseId, NS_ConvertUTF16toUTF8(aOrigin).get(),
     53          NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(),
     54          NS_ConvertUTF16toUTF8(aGMPName).get());
     55 
     56  if (!mGMPThread) {
     57    RejectPromiseWithStateError(
     58        aPromiseId, "Couldn't get GMP thread ChromiumCDMProxy::Init"_ns);
     59    return;
     60  }
     61 
     62  if (aGMPName.IsEmpty()) {
     63    RejectPromiseWithStateError(
     64        aPromiseId, nsPrintfCString("Unknown GMP for keysystem '%s'",
     65                                    NS_ConvertUTF16toUTF8(mKeySystem).get()));
     66    return;
     67  }
     68 
     69  gmp::NodeIdParts nodeIdParts{nsString(aOrigin), nsString(aTopLevelOrigin),
     70                               nsString(aGMPName)};
     71  nsCOMPtr<nsISerialEventTarget> thread = mGMPThread;
     72  RefPtr<ChromiumCDMProxy> self(this);
     73  nsCString keySystem = NS_ConvertUTF16toUTF8(mKeySystem);
     74  RefPtr<Runnable> task(NS_NewRunnableFunction(
     75      "ChromiumCDMProxy::Init",
     76      [self, nodeIdParts, helper, aPromiseId, thread, keySystem]() -> void {
     77        MOZ_ASSERT(self->IsOnOwnerThread());
     78 
     79        RefPtr<gmp::GeckoMediaPluginService> service =
     80            gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
     81        if (!service) {
     82          self->RejectPromiseWithStateError(
     83              aPromiseId,
     84              nsLiteralCString("Couldn't get GeckoMediaPluginService in "
     85                               "ChromiumCDMProxy::Init"));
     86          return;
     87        }
     88        RefPtr<gmp::GetCDMParentPromise> promise =
     89            service->GetCDM(nodeIdParts, keySystem, helper);
     90        promise->Then(
     91            thread, __func__,
     92            [self, aPromiseId, thread](RefPtr<gmp::ChromiumCDMParent> cdm) {
     93              // service->GetCDM succeeded
     94              self->mCallback =
     95                  MakeUnique<ChromiumCDMCallbackProxy>(self, self->mMainThread);
     96              cdm->Init(self->mCallback.get(),
     97                        self->mDistinctiveIdentifierRequired,
     98                        self->mPersistentStateRequired, self->mMainThread)
     99                  ->Then(
    100                      self->mMainThread, __func__,
    101                      [self, aPromiseId, cdm](bool /* unused */) {
    102                        // CDM init succeeded
    103                        {
    104                          MutexAutoLock lock(self->mCDMMutex);
    105                          self->mCDM = cdm;
    106                        }
    107                        if (self->mIsShutdown) {
    108                          self->RejectPromiseWithStateError(
    109                              aPromiseId, nsLiteralCString(
    110                                              "ChromiumCDMProxy shutdown "
    111                                              "during ChromiumCDMProxy::Init"));
    112                          // If shutdown happened while waiting to init, we
    113                          // need to explicitly shutdown the CDM to avoid it
    114                          // referencing this proxy which is on its way out.
    115                          self->ShutdownCDMIfExists();
    116                          return;
    117                        }
    118                        self->OnCDMCreated(aPromiseId);
    119                      },
    120                      [self, aPromiseId](MediaResult aResult) {
    121                        // CDM init failed.
    122                        ErrorResult rv;
    123                        // XXXbz MediaResult should really store a
    124                        // CopyableErrorResult or something.  See
    125                        // <https://bugzilla.mozilla.org/show_bug.cgi?id=1612216>.
    126                        rv.Throw(aResult.Code());
    127                        self->RejectPromise(aPromiseId, std::move(rv),
    128                                            aResult.Message());
    129                      });
    130            },
    131            [self, aPromiseId](MediaResult rv) {
    132              // service->GetCDM failed
    133              ErrorResult result;
    134              // XXXbz MediaResult should really store a CopyableErrorResult or
    135              // something.  See
    136              // <https://bugzilla.mozilla.org/show_bug.cgi?id=1612216>.
    137              result.Throw(rv.Code());
    138              self->RejectPromise(aPromiseId, std::move(result),
    139                                  rv.Description());
    140            });
    141      }));
    142 
    143  mGMPThread->Dispatch(task.forget());
    144 }
    145 
    146 void ChromiumCDMProxy::OnCDMCreated(uint32_t aPromiseId) {
    147  EME_LOG("ChromiumCDMProxy::OnCDMCreated(this=%p, pid=%" PRIu32
    148          ") isMainThread=%d",
    149          this, aPromiseId, NS_IsMainThread());
    150  MOZ_ASSERT(NS_IsMainThread());
    151  if (mKeys.IsNull()) {
    152    return;
    153  }
    154  RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
    155  // This should only be called once the CDM has been created.
    156  MOZ_ASSERT(cdm);
    157  if (cdm) {
    158    mKeys->OnCDMCreated(aPromiseId, cdm->PluginId());
    159  } else {
    160    // No CDM? Shouldn't be possible, but reject the promise anyway...
    161    constexpr auto err = "Null CDM in OnCDMCreated()"_ns;
    162    ErrorResult rv;
    163    rv.ThrowInvalidStateError(err);
    164    mKeys->RejectPromise(aPromiseId, std::move(rv), err);
    165  }
    166 }
    167 
    168 void ChromiumCDMProxy::ShutdownCDMIfExists() {
    169  EME_LOG(
    170      "ChromiumCDMProxy::ShutdownCDMIfExists(this=%p) mCDM=%p, mIsShutdown=%s",
    171      this, mCDM.get(), mIsShutdown ? "true" : "false");
    172  MOZ_ASSERT(NS_IsMainThread());
    173  MOZ_ASSERT(mGMPThread);
    174  MOZ_ASSERT(mIsShutdown,
    175             "Should only shutdown the CDM if the proxy is shutting down");
    176  RefPtr<gmp::ChromiumCDMParent> cdm;
    177  {
    178    MutexAutoLock lock(mCDMMutex);
    179    cdm.swap(mCDM);
    180  }
    181  if (cdm) {
    182    // We need to keep this proxy alive until the parent has finished its
    183    // Shutdown (as it may still try to use the proxy until then).
    184    RefPtr<ChromiumCDMProxy> self(this);
    185    nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction(
    186        "ChromiumCDMProxy::Shutdown", [self, cdm]() { cdm->Shutdown(); });
    187    mGMPThread->Dispatch(task.forget());
    188  }
    189 }
    190 
    191 #ifdef DEBUG
    192 bool ChromiumCDMProxy::IsOnOwnerThread() {
    193  return mGMPThread && mGMPThread->IsOnCurrentThread();
    194 }
    195 #endif
    196 
    197 static uint32_t ToCDMSessionType(dom::MediaKeySessionType aSessionType) {
    198  switch (aSessionType) {
    199    case dom::MediaKeySessionType::Temporary:
    200      return static_cast<uint32_t>(cdm::kTemporary);
    201    case dom::MediaKeySessionType::Persistent_license:
    202      return static_cast<uint32_t>(cdm::kPersistentLicense);
    203    default:
    204      return static_cast<uint32_t>(cdm::kTemporary);
    205  };
    206 };
    207 
    208 static uint32_t ToCDMInitDataType(const nsAString& aInitDataType) {
    209  if (aInitDataType.EqualsLiteral("cenc")) {
    210    return static_cast<uint32_t>(cdm::kCenc);
    211  }
    212  if (aInitDataType.EqualsLiteral("webm")) {
    213    return static_cast<uint32_t>(cdm::kWebM);
    214  }
    215  if (aInitDataType.EqualsLiteral("keyids")) {
    216    return static_cast<uint32_t>(cdm::kKeyIds);
    217  }
    218  return static_cast<uint32_t>(cdm::kCenc);
    219 }
    220 
    221 void ChromiumCDMProxy::CreateSession(uint32_t aCreateSessionToken,
    222                                     dom::MediaKeySessionType aSessionType,
    223                                     PromiseId aPromiseId,
    224                                     const nsAString& aInitDataType,
    225                                     nsTArray<uint8_t>& aInitData) {
    226  MOZ_ASSERT(NS_IsMainThread());
    227  EME_LOG("ChromiumCDMProxy::CreateSession(this=%p, token=%" PRIu32
    228          ", type=%d, pid=%" PRIu32
    229          ") "
    230          "initDataLen=%zu",
    231          this, aCreateSessionToken, (int)aSessionType, aPromiseId,
    232          aInitData.Length());
    233 
    234  uint32_t sessionType = ToCDMSessionType(aSessionType);
    235  uint32_t initDataType = ToCDMInitDataType(aInitDataType);
    236 
    237  RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
    238  if (!cdm) {
    239    RejectPromiseWithStateError(aPromiseId, "Null CDM in CreateSession"_ns);
    240    return;
    241  }
    242 
    243  mGMPThread->Dispatch(NewRunnableMethod<uint32_t, uint32_t, uint32_t, uint32_t,
    244                                         nsTArray<uint8_t>>(
    245      "gmp::ChromiumCDMParent::CreateSession", cdm,
    246      &gmp::ChromiumCDMParent::CreateSession, aCreateSessionToken, sessionType,
    247      initDataType, aPromiseId, std::move(aInitData)));
    248 }
    249 
    250 void ChromiumCDMProxy::LoadSession(PromiseId aPromiseId,
    251                                   dom::MediaKeySessionType aSessionType,
    252                                   const nsAString& aSessionId) {
    253  MOZ_ASSERT(NS_IsMainThread());
    254 
    255  RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
    256  if (!cdm) {
    257    RejectPromiseWithStateError(aPromiseId, "Null CDM in LoadSession"_ns);
    258    return;
    259  }
    260 
    261  mGMPThread->Dispatch(NewRunnableMethod<uint32_t, uint32_t, nsString>(
    262      "gmp::ChromiumCDMParent::LoadSession", cdm,
    263      &gmp::ChromiumCDMParent::LoadSession, aPromiseId,
    264      ToCDMSessionType(aSessionType), aSessionId));
    265 }
    266 
    267 void ChromiumCDMProxy::SetServerCertificate(PromiseId aPromiseId,
    268                                            nsTArray<uint8_t>& aCert) {
    269  MOZ_ASSERT(NS_IsMainThread());
    270  EME_LOG("ChromiumCDMProxy::SetServerCertificate(this=%p, pid=%" PRIu32
    271          ") certLen=%zu",
    272          this, aPromiseId, aCert.Length());
    273 
    274  RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
    275  if (!cdm) {
    276    RejectPromiseWithStateError(aPromiseId,
    277                                "Null CDM in SetServerCertificate"_ns);
    278    return;
    279  }
    280 
    281  mGMPThread->Dispatch(NewRunnableMethod<uint32_t, nsTArray<uint8_t>>(
    282      "gmp::ChromiumCDMParent::SetServerCertificate", cdm,
    283      &gmp::ChromiumCDMParent::SetServerCertificate, aPromiseId,
    284      std::move(aCert)));
    285 }
    286 
    287 void ChromiumCDMProxy::UpdateSession(const nsAString& aSessionId,
    288                                     PromiseId aPromiseId,
    289                                     nsTArray<uint8_t>& aResponse) {
    290  MOZ_ASSERT(NS_IsMainThread());
    291  EME_LOG("ChromiumCDMProxy::UpdateSession(this=%p, sid='%s', pid=%" PRIu32
    292          ") "
    293          "responseLen=%zu",
    294          this, NS_ConvertUTF16toUTF8(aSessionId).get(), aPromiseId,
    295          aResponse.Length());
    296 
    297  RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
    298  if (!cdm) {
    299    RejectPromiseWithStateError(aPromiseId, "Null CDM in UpdateSession"_ns);
    300    return;
    301  }
    302  mGMPThread->Dispatch(
    303      NewRunnableMethod<nsCString, uint32_t, nsTArray<uint8_t>>(
    304          "gmp::ChromiumCDMParent::UpdateSession", cdm,
    305          &gmp::ChromiumCDMParent::UpdateSession,
    306          NS_ConvertUTF16toUTF8(aSessionId), aPromiseId, std::move(aResponse)));
    307 }
    308 
    309 void ChromiumCDMProxy::CloseSession(const nsAString& aSessionId,
    310                                    PromiseId aPromiseId) {
    311  MOZ_ASSERT(NS_IsMainThread());
    312  EME_LOG("ChromiumCDMProxy::CloseSession(this=%p, sid='%s', pid=%" PRIu32 ")",
    313          this, NS_ConvertUTF16toUTF8(aSessionId).get(), aPromiseId);
    314 
    315  RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
    316  if (!cdm) {
    317    RejectPromiseWithStateError(aPromiseId, "Null CDM in CloseSession"_ns);
    318    return;
    319  }
    320  mGMPThread->Dispatch(NewRunnableMethod<nsCString, uint32_t>(
    321      "gmp::ChromiumCDMParent::CloseSession", cdm,
    322      &gmp::ChromiumCDMParent::CloseSession, NS_ConvertUTF16toUTF8(aSessionId),
    323      aPromiseId));
    324 }
    325 
    326 void ChromiumCDMProxy::RemoveSession(const nsAString& aSessionId,
    327                                     PromiseId aPromiseId) {
    328  MOZ_ASSERT(NS_IsMainThread());
    329  EME_LOG("ChromiumCDMProxy::RemoveSession(this=%p, sid='%s', pid=%" PRIu32 ")",
    330          this, NS_ConvertUTF16toUTF8(aSessionId).get(), aPromiseId);
    331 
    332  RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
    333  if (!cdm) {
    334    RejectPromiseWithStateError(aPromiseId, "Null CDM in RemoveSession"_ns);
    335    return;
    336  }
    337  mGMPThread->Dispatch(NewRunnableMethod<nsCString, uint32_t>(
    338      "gmp::ChromiumCDMParent::RemoveSession", cdm,
    339      &gmp::ChromiumCDMParent::RemoveSession, NS_ConvertUTF16toUTF8(aSessionId),
    340      aPromiseId));
    341 }
    342 
    343 void ChromiumCDMProxy::QueryOutputProtectionStatus() {
    344  MOZ_ASSERT(NS_IsMainThread());
    345  EME_LOG("ChromiumCDMProxy::QueryOutputProtectionStatus(this=%p)", this);
    346 
    347  if (mKeys.IsNull()) {
    348    EME_LOG(
    349        "ChromiumCDMProxy::QueryOutputProtectionStatus(this=%p), mKeys "
    350        "missing!",
    351        this);
    352    // If we can't get mKeys, we're probably in shutdown. But do our best to
    353    // respond to the request and indicate the check failed.
    354    NotifyOutputProtectionStatus(OutputProtectionCheckStatus::CheckFailed,
    355                                 OutputProtectionCaptureStatus::Unused);
    356    return;
    357  }
    358  // The keys will call back via `NotifyOutputProtectionStatus` to notify the
    359  // result of the check.
    360  mKeys->CheckIsElementCapturePossible();
    361 }
    362 
    363 void ChromiumCDMProxy::NotifyOutputProtectionStatus(
    364    OutputProtectionCheckStatus aCheckStatus,
    365    OutputProtectionCaptureStatus aCaptureStatus) {
    366  MOZ_ASSERT(NS_IsMainThread());
    367  // If the check failed aCaptureStatus should be unused, otherwise not.
    368  MOZ_ASSERT_IF(aCheckStatus == OutputProtectionCheckStatus::CheckFailed,
    369                aCaptureStatus == OutputProtectionCaptureStatus::Unused);
    370  MOZ_ASSERT_IF(aCheckStatus == OutputProtectionCheckStatus::CheckSuccessful,
    371                aCaptureStatus != OutputProtectionCaptureStatus::Unused);
    372  EME_LOG(
    373      "ChromiumCDMProxy::NotifyOutputProtectionStatus(this=%p) "
    374      "aCheckStatus=%" PRIu8 " aCaptureStatus=%" PRIu8,
    375      this, static_cast<uint8_t>(aCheckStatus),
    376      static_cast<uint8_t>(aCaptureStatus));
    377 
    378  RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
    379  if (!cdm) {
    380    // If we're in shutdown the CDM may have been cleared while a notification
    381    // is in flight. If this happens outside of shutdown we have a bug.
    382    MOZ_ASSERT(mIsShutdown);
    383    return;
    384  }
    385 
    386  uint32_t linkMask{};
    387  uint32_t protectionMask{};
    388  if (aCheckStatus == OutputProtectionCheckStatus::CheckSuccessful &&
    389      aCaptureStatus == OutputProtectionCaptureStatus::CapturePossilbe) {
    390    // The result indicates the capture is possible, so set the mask
    391    // to indicate this.
    392    linkMask |= cdm::OutputLinkTypes::kLinkTypeNetwork;
    393  }
    394  // `kProtectionNone` can cause playback to stop if HDCP_V1 is required. Report
    395  // HDCP protection if there's no potential capturing.
    396  if (linkMask == cdm::OutputLinkTypes::kLinkTypeNone &&
    397      StaticPrefs::media_widevine_hdcp_protection_mask()) {
    398    protectionMask = cdm::OutputProtectionMethods::kProtectionHDCP;
    399  }
    400  mGMPThread->Dispatch(NewRunnableMethod<bool, uint32_t, uint32_t>(
    401      "gmp::ChromiumCDMParent::NotifyOutputProtectionStatus", cdm,
    402      &gmp::ChromiumCDMParent::NotifyOutputProtectionStatus,
    403      aCheckStatus == OutputProtectionCheckStatus::CheckSuccessful, linkMask,
    404      protectionMask));
    405 }
    406 
    407 void ChromiumCDMProxy::Shutdown() {
    408  MOZ_ASSERT(NS_IsMainThread());
    409  EME_LOG("ChromiumCDMProxy::Shutdown(this=%p) mCDM=%p, mIsShutdown=%s", this,
    410          mCDM.get(), mIsShutdown ? "true" : "false");
    411  if (mIsShutdown) {
    412    return;
    413  }
    414  mIsShutdown = true;
    415  mKeys.Clear();
    416  ShutdownCDMIfExists();
    417 }
    418 
    419 void ChromiumCDMProxy::RejectPromise(PromiseId aId, ErrorResult&& aException,
    420                                     const nsCString& aReason) {
    421  if (!NS_IsMainThread()) {
    422    // Use CopyableErrorResult to store our exception in the runnable,
    423    // because ErrorResult is not OK to move across threads.
    424    mMainThread->Dispatch(
    425        NewRunnableMethod<PromiseId, StoreCopyPassByRRef<CopyableErrorResult>,
    426                          nsCString>(
    427            "ChromiumCDMProxy::RejectPromise", this,
    428            &ChromiumCDMProxy::RejectPromiseOnMainThread, aId,
    429            std::move(aException), aReason),
    430        NS_DISPATCH_NORMAL);
    431    return;
    432  }
    433  EME_LOG("ChromiumCDMProxy::RejectPromise(this=%p, pid=%" PRIu32
    434          ", code=0x%x, "
    435          "reason='%s')",
    436          this, aId, aException.ErrorCodeAsInt(), aReason.get());
    437  if (!mKeys.IsNull()) {
    438    mKeys->RejectPromise(aId, std::move(aException), aReason);
    439  } else {
    440    // We don't have a MediaKeys object to pass the exception to, so silence
    441    // the exception to avoid it asserting due to being unused.
    442    aException.SuppressException();
    443  }
    444 }
    445 
    446 void ChromiumCDMProxy::RejectPromiseWithStateError(PromiseId aId,
    447                                                   const nsCString& aReason) {
    448  ErrorResult rv;
    449  rv.ThrowInvalidStateError(aReason);
    450  RejectPromise(aId, std::move(rv), aReason);
    451 }
    452 
    453 void ChromiumCDMProxy::RejectPromiseOnMainThread(
    454    PromiseId aId, CopyableErrorResult&& aException, const nsCString& aReason) {
    455  // Moving into or out of a non-copyable ErrorResult will assert that both
    456  // ErorResults are from our current thread.  Avoid the assertion by moving
    457  // into a current-thread CopyableErrorResult first.  Note that this is safe,
    458  // because CopyableErrorResult never holds state that can't move across
    459  // threads.
    460  CopyableErrorResult rv(std::move(aException));
    461  RejectPromise(aId, std::move(rv), aReason);
    462 }
    463 
    464 void ChromiumCDMProxy::ResolvePromise(PromiseId aId) {
    465  if (!NS_IsMainThread()) {
    466    mMainThread->Dispatch(
    467        NewRunnableMethod<PromiseId>("ChromiumCDMProxy::ResolvePromise", this,
    468                                     &ChromiumCDMProxy::ResolvePromise, aId),
    469        NS_DISPATCH_NORMAL);
    470    return;
    471  }
    472 
    473  EME_LOG("ChromiumCDMProxy::ResolvePromise(this=%p, pid=%" PRIu32 ")", this,
    474          aId);
    475  if (!mKeys.IsNull()) {
    476    mKeys->ResolvePromise(aId);
    477  } else {
    478    NS_WARNING("ChromiumCDMProxy unable to resolve promise!");
    479  }
    480 }
    481 
    482 void ChromiumCDMProxy::OnSetSessionId(uint32_t aCreateSessionToken,
    483                                      const nsAString& aSessionId) {
    484  MOZ_ASSERT(NS_IsMainThread());
    485  EME_LOG("ChromiumCDMProxy::OnSetSessionId(this=%p, token=%" PRIu32
    486          ", sid='%s')",
    487          this, aCreateSessionToken, NS_ConvertUTF16toUTF8(aSessionId).get());
    488 
    489  if (mKeys.IsNull()) {
    490    return;
    491  }
    492  RefPtr<dom::MediaKeySession> session(
    493      mKeys->GetPendingSession(aCreateSessionToken));
    494  if (session) {
    495    session->SetSessionId(aSessionId);
    496  }
    497 }
    498 
    499 void ChromiumCDMProxy::OnResolveLoadSessionPromise(uint32_t aPromiseId,
    500                                                   bool aSuccess) {
    501  MOZ_ASSERT(NS_IsMainThread());
    502  if (mKeys.IsNull()) {
    503    return;
    504  }
    505  mKeys->OnSessionLoaded(aPromiseId, aSuccess);
    506 }
    507 
    508 void ChromiumCDMProxy::OnResolvePromiseWithKeyStatus(
    509    uint32_t aPromiseId, dom::MediaKeyStatus aKeyStatus) {
    510  MOZ_ASSERT(NS_IsMainThread());
    511  if (mKeys.IsNull()) {
    512    return;
    513  }
    514  mKeys->ResolvePromiseWithKeyStatus(aPromiseId, aKeyStatus);
    515 }
    516 
    517 void ChromiumCDMProxy::OnSessionMessage(const nsAString& aSessionId,
    518                                        dom::MediaKeyMessageType aMessageType,
    519                                        const nsTArray<uint8_t>& aMessage) {
    520  MOZ_ASSERT(NS_IsMainThread());
    521  if (mKeys.IsNull()) {
    522    return;
    523  }
    524  RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
    525  if (session) {
    526    session->DispatchKeyMessage(aMessageType, aMessage);
    527  }
    528 }
    529 
    530 void ChromiumCDMProxy::OnKeyStatusesChange(const nsAString& aSessionId) {
    531  MOZ_ASSERT(NS_IsMainThread());
    532  if (mKeys.IsNull()) {
    533    return;
    534  }
    535  RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
    536  if (session) {
    537    session->DispatchKeyStatusesChange();
    538  }
    539 }
    540 
    541 void ChromiumCDMProxy::OnExpirationChange(const nsAString& aSessionId,
    542                                          UnixTime aExpiryTime) {
    543  MOZ_ASSERT(NS_IsMainThread());
    544  if (mKeys.IsNull()) {
    545    return;
    546  }
    547  RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
    548  if (session) {
    549    // Expiry of 0 is interpreted as "never expire". See bug 1345341.
    550    double t = (aExpiryTime == 0) ? std::numeric_limits<double>::quiet_NaN()
    551                                  : static_cast<double>(aExpiryTime);
    552    session->SetExpiration(t);
    553  }
    554 }
    555 
    556 void ChromiumCDMProxy::OnSessionClosed(
    557    const nsAString& aSessionId, dom::MediaKeySessionClosedReason aReason) {
    558  MOZ_ASSERT(NS_IsMainThread());
    559 
    560  bool keyStatusesChange = false;
    561  {
    562    auto caps = Capabilites().Lock();
    563    keyStatusesChange = caps->RemoveKeysForSession(nsString(aSessionId));
    564  }
    565  if (keyStatusesChange) {
    566    OnKeyStatusesChange(aSessionId);
    567  }
    568  if (mKeys.IsNull()) {
    569    return;
    570  }
    571  RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
    572  if (session) {
    573    session->OnClosed(aReason);
    574  }
    575 }
    576 
    577 void ChromiumCDMProxy::OnDecrypted(uint32_t aId, DecryptStatus aResult,
    578                                   const nsTArray<uint8_t>& aDecryptedData) {}
    579 
    580 void ChromiumCDMProxy::OnSessionError(const nsAString& aSessionId,
    581                                      nsresult aException, uint32_t aSystemCode,
    582                                      const nsAString& aMsg) {
    583  MOZ_ASSERT(NS_IsMainThread());
    584  if (mKeys.IsNull()) {
    585    return;
    586  }
    587  RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
    588  if (session) {
    589    session->DispatchKeyError(aSystemCode);
    590  }
    591  LogToConsole(aMsg);
    592 }
    593 
    594 void ChromiumCDMProxy::OnRejectPromise(uint32_t aPromiseId,
    595                                       ErrorResult&& aException,
    596                                       const nsCString& aMsg) {
    597  MOZ_ASSERT(NS_IsMainThread());
    598  RejectPromise(aPromiseId, std::move(aException), aMsg);
    599 }
    600 
    601 RefPtr<DecryptPromise> ChromiumCDMProxy::Decrypt(MediaRawData* aSample) {
    602  RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
    603  if (!cdm) {
    604    return DecryptPromise::CreateAndReject(
    605        DecryptResult(eme::AbortedErr, aSample), __func__);
    606  }
    607  RefPtr<MediaRawData> sample = aSample;
    608  return InvokeAsync(mGMPThread, __func__,
    609                     [cdm, sample]() { return cdm->Decrypt(sample); });
    610 }
    611 
    612 void ChromiumCDMProxy::GetStatusForPolicy(
    613    PromiseId aPromiseId, const dom::HDCPVersion& aMinHdcpVersion) {
    614  MOZ_ASSERT(NS_IsMainThread());
    615  EME_LOG("ChromiumCDMProxy::GetStatusForPolicy(this=%p, pid=%" PRIu32
    616          ") minHdcpVersion=%s",
    617          this, aPromiseId, dom::GetEnumString(aMinHdcpVersion).get());
    618 
    619  RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
    620  if (!cdm) {
    621    RejectPromiseWithStateError(aPromiseId,
    622                                "Null CDM in GetStatusForPolicy"_ns);
    623    return;
    624  }
    625 
    626  mGMPThread->Dispatch(NewRunnableMethod<uint32_t, dom::HDCPVersion>(
    627      "gmp::ChromiumCDMParent::GetStatusForPolicy", cdm,
    628      &gmp::ChromiumCDMParent::GetStatusForPolicy, aPromiseId,
    629      aMinHdcpVersion));
    630 }
    631 
    632 void ChromiumCDMProxy::Terminated() {
    633  if (!mKeys.IsNull()) {
    634    mKeys->Terminated();
    635  }
    636 }
    637 
    638 already_AddRefed<gmp::ChromiumCDMParent> ChromiumCDMProxy::GetCDMParent() {
    639  MutexAutoLock lock(mCDMMutex);
    640  RefPtr<gmp::ChromiumCDMParent> cdm = mCDM;
    641  return cdm.forget();
    642 }
    643 
    644 }  // namespace mozilla
    645 
    646 #undef NS_DispatchToMainThread