tor-browser

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

RemoteCDMChild.cpp (19865B)


      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
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "RemoteCDMChild.h"
      8 
      9 #include "mozilla/RemoteDecodeUtils.h"
     10 #include "mozilla/dom/MediaKeySession.h"
     11 
     12 #ifdef MOZ_WIDGET_ANDROID
     13 #  include "mozilla/MediaDrmProvisioningHelper.h"
     14 #endif
     15 
     16 namespace mozilla {
     17 
     18 #define LOGD(msg, ...) \
     19  MOZ_LOG_FMT(gRemoteDecodeLog, LogLevel::Debug, msg, ##__VA_ARGS__)
     20 
     21 RemoteCDMChild::RemoteCDMChild(
     22    nsCOMPtr<nsISerialEventTarget>&& aThread,
     23    RefPtr<GenericNonExclusivePromise>&& aIPDLPromise, RemoteMediaIn aLocation,
     24    dom::MediaKeys* aKeys, const nsAString& aKeySystem,
     25    bool aDistinctiveIdentifierRequired, bool aPersistentStateRequired)
     26    : CDMProxy(aKeys, aKeySystem, aDistinctiveIdentifierRequired,
     27               aPersistentStateRequired),
     28      mThread(std::move(aThread)),
     29      mIPDLPromise(std::move(aIPDLPromise)),
     30      mLocation(aLocation) {}
     31 
     32 RemoteCDMChild::~RemoteCDMChild() = default;
     33 
     34 void RemoteCDMChild::ActorDestroy(ActorDestroyReason aWhy) {
     35  LOGD("[{}] RemoteCDMChild::ActorDestroy", fmt::ptr(this));
     36  mNeedsShutdown = false;
     37 }
     38 
     39 mozilla::ipc::IPCResult RemoteCDMChild::RecvProvision(
     40    const RemoteCDMProvisionRequestIPDL& aRequest,
     41    ProvisionResolver&& aResolver) {
     42  LOGD("[{}] RemoteCDMChild::RecvProvision", fmt::ptr(this));
     43 #ifdef MOZ_WIDGET_ANDROID
     44  auto helper =
     45      MakeRefPtr<MediaDrmProvisioningHelper>(aRequest, std::move(aResolver));
     46  helper->Provision();
     47 #else
     48  aResolver(MediaResult(NS_ERROR_DOM_MEDIA_NOT_SUPPORTED_ERR));
     49 #endif
     50  return IPC_OK();
     51 }
     52 
     53 mozilla::ipc::IPCResult RemoteCDMChild::RecvOnSessionKeyStatus(
     54    const RemoteCDMKeyStatusIPDL& aMsg) {
     55  LOGD("[{}] RemoteCDMChild::RecvOnSessionKeyStatus", fmt::ptr(this));
     56  bool changed = false;
     57  {
     58    auto caps = mCapabilites.Lock();
     59    for (const auto& keyInfo : aMsg.keyInfo()) {
     60      changed |=
     61          caps->SetKeyStatus(keyInfo.mKeyId, aMsg.sessionId(), keyInfo.mStatus);
     62    }
     63  }
     64 
     65  if (!changed) {
     66    return IPC_OK();
     67  }
     68 
     69  NS_DispatchToMainThread(NS_NewRunnableFunction(
     70      __func__, [self = RefPtr{this}, sessionId = aMsg.sessionId()]() {
     71        if (self->mKeys.IsNull()) {
     72          return;
     73        }
     74        if (RefPtr<dom::MediaKeySession> session =
     75                self->mKeys->GetSession(sessionId)) {
     76          session->DispatchKeyStatusesChange();
     77        }
     78      }));
     79  return IPC_OK();
     80 }
     81 
     82 mozilla::ipc::IPCResult RemoteCDMChild::RecvOnSessionKeyExpiration(
     83    RemoteCDMKeyExpirationIPDL&& aMsg) {
     84  LOGD("[{}] RemoteCDMChild::RecvOnSessionKeyExpiration", fmt::ptr(this));
     85  NS_DispatchToMainThread(NS_NewRunnableFunction(
     86      __func__, [self = RefPtr{this}, msg = std::move(aMsg)]() {
     87        if (self->mKeys.IsNull()) {
     88          return;
     89        }
     90        if (RefPtr<dom::MediaKeySession> session =
     91                self->mKeys->GetSession(msg.sessionId())) {
     92          session->SetExpiration(msg.expiredTimeMilliSecondsSinceEpoch());
     93        }
     94      }));
     95  return IPC_OK();
     96 }
     97 
     98 mozilla::ipc::IPCResult RemoteCDMChild::RecvOnSessionKeyMessage(
     99    RemoteCDMKeyMessageIPDL&& aMsg) {
    100  LOGD("[{}] RemoteCDMChild::RecvOnSessionKeyMessage", fmt::ptr(this));
    101  NS_DispatchToMainThread(NS_NewRunnableFunction(
    102      __func__, [self = RefPtr{this}, msg = std::move(aMsg)]() {
    103        if (self->mKeys.IsNull()) {
    104          return;
    105        }
    106        if (RefPtr<dom::MediaKeySession> session =
    107                self->mKeys->GetSession(msg.sessionId())) {
    108          session->DispatchKeyMessage(msg.type(), msg.message());
    109        }
    110      }));
    111  return IPC_OK();
    112 }
    113 
    114 void RemoteCDMChild::Init(PromiseId aPromiseId, const nsAString& aOrigin,
    115                          const nsAString& aTopLevelOrigin,
    116                          const nsAString& aName) {
    117  MOZ_ASSERT(NS_IsMainThread());
    118 
    119  if (mKeys.IsNull()) {
    120    return;
    121  }
    122 
    123  LOGD("[{}] RemoteCDMChild::Init -- promise {}", fmt::ptr(this), aPromiseId);
    124  if (!mIPDLPromise) {
    125    RejectPromise(aPromiseId,
    126                  MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR,
    127                              "PRemoteCDMChild already initialized"_ns));
    128    return;
    129  }
    130 
    131  mIPDLPromise->Then(
    132      mThread, __func__,
    133      [self = RefPtr{this}, aPromiseId](
    134          const GenericNonExclusivePromise::ResolveOrRejectValue& aValue) {
    135        LOGD("[{}] RemoteCDMChild::Init -- promise {} resolved {}",
    136             fmt::ptr(self.get()), aPromiseId, aValue.IsResolve());
    137 
    138        if (aValue.IsReject()) {
    139          self->RejectPromise(
    140              aPromiseId,
    141              MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR,
    142                          "PRemoteCDMChild ensure process fail"_ns));
    143          return;
    144        }
    145 
    146        self->InitInternal(aPromiseId);
    147      });
    148  mIPDLPromise = nullptr;
    149 }
    150 
    151 void RemoteCDMChild::InitInternal(PromiseId aPromiseId) {
    152  LOGD("[{}] RemoteCDMChild::InitInternal -- promise {}", fmt::ptr(this),
    153       aPromiseId);
    154  RefPtr<RemoteMediaManagerChild> manager =
    155      RemoteMediaManagerChild::GetSingleton(mLocation);
    156  if (!manager) {
    157    RejectPromise(aPromiseId,
    158                  MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR,
    159                              "PRemoteCDMChild manager is not available"_ns));
    160    return;
    161  }
    162 
    163  LOGD("[{}] RemoteCDMChild::InitInternal -- send constructor", fmt::ptr(this));
    164  if (!manager->SendPRemoteCDMConstructor(this, mKeySystem)) {
    165    RejectPromise(aPromiseId,
    166                  MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR,
    167                              "PRemoteCDMChild manager is unable to send"_ns));
    168    return;
    169  }
    170 
    171  LOGD("[{}] RemoteCDMChild::InitInternal -- send init", fmt::ptr(this));
    172  SendInit(RemoteCDMInitRequestIPDL(mDistinctiveIdentifierRequired,
    173                                    mPersistentStateRequired))
    174      ->Then(
    175          GetMainThreadSerialEventTarget(), __func__,
    176          [self = RefPtr{this},
    177           aPromiseId](const InitPromise::ResolveOrRejectValue& aValue) {
    178            LOGD("[{}] RemoteCDMChild::InitInternal -- promise {} resolved {}",
    179                 fmt::ptr(self.get()), aPromiseId, aValue.IsResolve());
    180 
    181            if (self->mKeys.IsNull()) {
    182              return;
    183            }
    184 
    185            if (aValue.IsReject()) {
    186              self->RejectPromise(
    187                  aPromiseId,
    188                  MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR,
    189                              "PRemoteCDMChild::SendInit IPC fail"_ns));
    190              return;
    191            }
    192 
    193            self->mKeys->OnCDMCreated(aPromiseId, 0);
    194          });
    195 }
    196 
    197 void RemoteCDMChild::CreateSession(uint32_t aCreateSessionToken,
    198                                   MediaKeySessionType aSessionType,
    199                                   PromiseId aPromiseId,
    200                                   const nsAString& aInitDataType,
    201                                   nsTArray<uint8_t>& aInitData) {
    202  MOZ_ALWAYS_SUCCEEDS(mThread->Dispatch(NS_NewRunnableFunction(
    203      __func__, [self = RefPtr{this}, aCreateSessionToken, aSessionType,
    204                 aPromiseId, initDataType = nsString(aInitDataType),
    205                 initData = std::move(aInitData)]() mutable {
    206        LOGD("[{}] RemoteCDMChild::CreateSession -- promise {}",
    207             fmt::ptr(self.get()), aPromiseId);
    208        self
    209            ->SendCreateSession(RemoteCDMCreateSessionRequestIPDL(
    210                aSessionType, std::move(initDataType), std::move(initData)))
    211            ->Then(
    212                GetMainThreadSerialEventTarget(), __func__,
    213                [self, aCreateSessionToken, aPromiseId](
    214                    const CreateSessionPromise::ResolveOrRejectValue& aValue) {
    215                  if (self->mKeys.IsNull()) {
    216                    return;
    217                  }
    218 
    219                  if (aValue.IsReject()) {
    220                    self->RejectPromise(
    221                        aPromiseId,
    222                        MediaResult(
    223                            NS_ERROR_DOM_INVALID_STATE_ERR,
    224                            "PRemoteCDMChild::SendCreateSession IPC fail"_ns));
    225                    return;
    226                  }
    227 
    228                  const auto& response = aValue.ResolveValue();
    229                  if (response.type() ==
    230                      RemoteCDMSessionResponseIPDL::TMediaResult) {
    231                    self->RejectPromise(aPromiseId, response.get_MediaResult());
    232                    return;
    233                  }
    234 
    235                  const auto& msg = response.get_RemoteCDMKeyMessageIPDL();
    236                  const auto& sessionId = msg.sessionId();
    237                  if (RefPtr<dom::MediaKeySession> session =
    238                          self->mKeys->GetPendingSession(aCreateSessionToken)) {
    239                    session->SetSessionId(sessionId);
    240                    session->DispatchKeyMessage(msg.type(), msg.message());
    241                  }
    242 
    243                  self->ResolvePromise(aPromiseId);
    244                });
    245      })));
    246 }
    247 
    248 void RemoteCDMChild::LoadSession(PromiseId aPromiseId,
    249                                 dom::MediaKeySessionType aSessionType,
    250                                 const nsAString& aSessionId) {
    251  MOZ_ALWAYS_SUCCEEDS(mThread->Dispatch(NS_NewRunnableFunction(
    252      __func__, [self = RefPtr{this}, aPromiseId, aSessionType,
    253                 sessionId = nsString(aSessionId)]() mutable {
    254        LOGD("[{}] RemoteCDMChild::LoadSession -- promise {}",
    255             fmt::ptr(self.get()), aPromiseId);
    256        self->SendLoadSession(RemoteCDMLoadSessionRequestIPDL(
    257                                  aSessionType, std::move(sessionId)))
    258            ->Then(GetMainThreadSerialEventTarget(), __func__,
    259                   [self, aPromiseId](
    260                       const LoadSessionPromise::ResolveOrRejectValue& aValue) {
    261                     if (self->mKeys.IsNull()) {
    262                       return;
    263                     }
    264 
    265                     self->mKeys->OnSessionLoaded(
    266                         aPromiseId, aValue.IsResolve() &&
    267                                         NS_SUCCEEDED(aValue.ResolveValue()));
    268                   });
    269      })));
    270 }
    271 
    272 void RemoteCDMChild::SetServerCertificate(PromiseId aPromiseId,
    273                                          nsTArray<uint8_t>& aCert) {
    274  MOZ_ALWAYS_SUCCEEDS(mThread->Dispatch(NS_NewRunnableFunction(
    275      __func__,
    276      [self = RefPtr{this}, aPromiseId, cert = std::move(aCert)]() mutable {
    277        LOGD("[{}] RemoteCDMChild::SetServerCertificate -- promise {}",
    278             fmt::ptr(self.get()), aPromiseId);
    279        self->SendSetServerCertificate(std::move(cert))
    280            ->Then(
    281                GetMainThreadSerialEventTarget(), __func__,
    282                [self, aPromiseId](
    283                    const SetServerCertificatePromise::ResolveOrRejectValue&
    284                        aValue) {
    285                  if (self->mKeys.IsNull()) {
    286                    return;
    287                  }
    288 
    289                  if (aValue.IsReject()) {
    290                    self->RejectPromise(
    291                        aPromiseId,
    292                        MediaResult(
    293                            NS_ERROR_DOM_INVALID_STATE_ERR,
    294                            "PRemoteCDMChild::SendSetServerCertificate IPC fail"_ns));
    295                    return;
    296                  }
    297 
    298                  self->ResolveOrRejectPromise(aPromiseId,
    299                                               aValue.ResolveValue());
    300                });
    301      })));
    302 }
    303 
    304 void RemoteCDMChild::UpdateSession(const nsAString& aSessionId,
    305                                   PromiseId aPromiseId,
    306                                   nsTArray<uint8_t>& aResponse) {
    307  MOZ_ALWAYS_SUCCEEDS(mThread->Dispatch(NS_NewRunnableFunction(
    308      __func__, [self = RefPtr{this}, sessionId = nsString(aSessionId),
    309                 aPromiseId, response = std::move(aResponse)]() mutable {
    310        LOGD("[{}] RemoteCDMChild::UpdateSession -- promise {}",
    311             fmt::ptr(self.get()), aPromiseId);
    312        self->SendUpdateSession(RemoteCDMUpdateSessionRequestIPDL(
    313                                    std::move(sessionId), std::move(response)))
    314            ->Then(
    315                GetMainThreadSerialEventTarget(), __func__,
    316                [self, aPromiseId](
    317                    const UpdateSessionPromise::ResolveOrRejectValue& aValue) {
    318                  if (self->mKeys.IsNull()) {
    319                    return;
    320                  }
    321 
    322                  if (aValue.IsReject()) {
    323                    self->RejectPromise(
    324                        aPromiseId,
    325                        MediaResult(
    326                            NS_ERROR_DOM_INVALID_STATE_ERR,
    327                            "PRemoteCDMChild::SendUpdateSession IPC fail"_ns));
    328                    return;
    329                  }
    330 
    331                  self->ResolveOrRejectPromise(aPromiseId,
    332                                               aValue.ResolveValue());
    333                });
    334      })));
    335 }
    336 
    337 void RemoteCDMChild::CloseSession(const nsAString& aSessionId,
    338                                  PromiseId aPromiseId) {
    339  MOZ_ALWAYS_SUCCEEDS(mThread->Dispatch(NS_NewRunnableFunction(
    340      __func__, [self = RefPtr{this}, sessionId = nsString(aSessionId),
    341                 aPromiseId]() mutable {
    342        LOGD("[{}] RemoteCDMChild::CloseSession -- promise {}",
    343             fmt::ptr(self.get()), aPromiseId);
    344        self->SendCloseSession(std::move(sessionId))
    345            ->Then(
    346                GetMainThreadSerialEventTarget(), __func__,
    347                [self, aPromiseId](
    348                    const CloseSessionPromise::ResolveOrRejectValue& aValue) {
    349                  if (self->mKeys.IsNull()) {
    350                    return;
    351                  }
    352 
    353                  if (aValue.IsReject()) {
    354                    self->RejectPromise(
    355                        aPromiseId,
    356                        MediaResult(
    357                            NS_ERROR_DOM_INVALID_STATE_ERR,
    358                            "PRemoteCDMChild::SendCloseSession IPC fail"_ns));
    359                    return;
    360                  }
    361 
    362                  self->ResolveOrRejectPromise(aPromiseId,
    363                                               aValue.ResolveValue());
    364                });
    365      })));
    366 }
    367 
    368 void RemoteCDMChild::RemoveSession(const nsAString& aSessionId,
    369                                   PromiseId aPromiseId) {
    370  MOZ_ALWAYS_SUCCEEDS(mThread->Dispatch(NS_NewRunnableFunction(
    371      __func__, [self = RefPtr{this}, sessionId = nsString(aSessionId),
    372                 aPromiseId]() mutable {
    373        LOGD("[{}] RemoteCDMChild::RemoveSession -- promise {}",
    374             fmt::ptr(self.get()), aPromiseId);
    375        self->SendRemoveSession(std::move(sessionId))
    376            ->Then(
    377                GetMainThreadSerialEventTarget(), __func__,
    378                [self, aPromiseId](
    379                    const RemoveSessionPromise::ResolveOrRejectValue& aValue) {
    380                  if (self->mKeys.IsNull()) {
    381                    return;
    382                  }
    383 
    384                  if (aValue.IsReject()) {
    385                    self->RejectPromise(
    386                        aPromiseId,
    387                        MediaResult(
    388                            NS_ERROR_DOM_INVALID_STATE_ERR,
    389                            "PRemoteCDMChild::SendRemoveSession IPC fail"_ns));
    390                    return;
    391                  }
    392 
    393                  self->ResolveOrRejectPromise(aPromiseId,
    394                                               aValue.ResolveValue());
    395                });
    396      })));
    397 }
    398 
    399 void RemoteCDMChild::QueryOutputProtectionStatus() {}
    400 
    401 void RemoteCDMChild::NotifyOutputProtectionStatus(
    402    OutputProtectionCheckStatus aCheckStatus,
    403    OutputProtectionCaptureStatus aCaptureStatus) {}
    404 
    405 void RemoteCDMChild::Shutdown() {
    406  LOGD("[{}] RemoteCDMChild::Shutdown", fmt::ptr(this));
    407  // If this is the last reference, and we still have an actor, then we know
    408  // that the last reference is solely due to the IPDL reference. Dispatch to
    409  // the owning thread to delete that so that we can clean up.
    410  //
    411  // It is an atomic because ActorDestroy will be called on the IPDL / manager
    412  // thread, and Shutdown is likely called on the main thread.
    413  if (mNeedsShutdown.exchange(false)) {
    414    mThread->Dispatch(NS_NewRunnableFunction(__func__, [self = RefPtr{this}]() {
    415      if (self->CanSend()) {
    416        self->Send__delete__(self);
    417      }
    418    }));
    419  }
    420 }
    421 
    422 void RemoteCDMChild::Terminated() {
    423  MOZ_ASSERT_UNREACHABLE("Unexpected to be called!");
    424 }
    425 
    426 void RemoteCDMChild::OnSetSessionId(uint32_t aCreateSessionToken,
    427                                    const nsAString& aSessionId) {
    428  MOZ_ASSERT_UNREACHABLE("Unexpected to be called!");
    429 }
    430 
    431 void RemoteCDMChild::OnResolveLoadSessionPromise(uint32_t aPromiseId,
    432                                                 bool aSuccess) {
    433  MOZ_ASSERT_UNREACHABLE("Unexpected to be called!");
    434 }
    435 
    436 void RemoteCDMChild::OnSessionMessage(const nsAString& aSessionId,
    437                                      dom::MediaKeyMessageType aMessageType,
    438                                      const nsTArray<uint8_t>& aMessage) {
    439  MOZ_ASSERT_UNREACHABLE("Unexpected to be called!");
    440 }
    441 
    442 void RemoteCDMChild::OnExpirationChange(const nsAString& aSessionId,
    443                                        UnixTime aExpiryTime) {
    444  MOZ_ASSERT_UNREACHABLE("Unexpected to be called!");
    445 }
    446 
    447 void RemoteCDMChild::OnSessionClosed(const nsAString& aSessionId,
    448                                     dom::MediaKeySessionClosedReason aReason) {
    449  MOZ_ASSERT_UNREACHABLE("Unexpected to be called!");
    450 }
    451 
    452 void RemoteCDMChild::OnSessionError(const nsAString& aSessionId,
    453                                    nsresult aException, uint32_t aSystemCode,
    454                                    const nsAString& aMsg) {
    455  MOZ_ASSERT_UNREACHABLE("Unexpected to be called!");
    456 }
    457 
    458 void RemoteCDMChild::OnRejectPromise(uint32_t aPromiseId,
    459                                     ErrorResult&& aException,
    460                                     const nsCString& aMsg) {
    461  MOZ_ASSERT_UNREACHABLE("Unexpected to be called!");
    462 }
    463 
    464 RefPtr<DecryptPromise> RemoteCDMChild::Decrypt(MediaRawData* aSample) {
    465  MOZ_ASSERT_UNREACHABLE("Unexpected to be called!");
    466  return nullptr;
    467 }
    468 
    469 void RemoteCDMChild::OnDecrypted(uint32_t aId, DecryptStatus aResult,
    470                                 const nsTArray<uint8_t>& aDecryptedData) {
    471  MOZ_ASSERT_UNREACHABLE("Unexpected to be called!");
    472 }
    473 
    474 void RemoteCDMChild::RejectPromise(PromiseId aId, ErrorResult&& aException,
    475                                   const nsCString& aReason) {
    476  LOGD("[{}] RemoteCDMChild::RejectPromise -- {}", fmt::ptr(this), aId);
    477  MOZ_ASSERT(NS_IsMainThread());
    478  MOZ_ASSERT(!mKeys.IsNull());
    479  mKeys->RejectPromise(aId, std::move(aException), aReason);
    480 }
    481 
    482 void RemoteCDMChild::ResolvePromise(PromiseId aId) {
    483  LOGD("[{}] RemoteCDMChild::ResolvePromise -- {}", fmt::ptr(this), aId);
    484  MOZ_ASSERT(NS_IsMainThread());
    485  MOZ_ASSERT(!mKeys.IsNull());
    486  mKeys->ResolvePromise(aId);
    487 }
    488 
    489 void RemoteCDMChild::RejectPromise(PromiseId aId, const MediaResult& aResult) {
    490  MOZ_ASSERT(NS_FAILED(aResult.Code()));
    491 
    492  ErrorResult rv;
    493  aResult.ThrowTo(rv);
    494  RejectPromise(aId, std::move(rv), aResult.Message());
    495 }
    496 
    497 void RemoteCDMChild::ResolveOrRejectPromise(PromiseId aId,
    498                                            const MediaResult& aResult) {
    499  if (aResult.Code() == NS_OK) {
    500    ResolvePromise(aId);
    501    return;
    502  }
    503 
    504  RejectPromise(aId, aResult);
    505 }
    506 
    507 void RemoteCDMChild::OnKeyStatusesChange(const nsAString& aSessionId) {}
    508 
    509 void RemoteCDMChild::GetStatusForPolicy(
    510    PromiseId aPromiseId, const dom::HDCPVersion& aMinHdcpVersion) {
    511  RejectPromise(
    512      aPromiseId,
    513      MediaResult(NS_ERROR_DOM_MEDIA_NOT_SUPPORTED_ERR,
    514                  "Currently Fennec does not support GetStatusForPolicy"));
    515 }
    516 
    517 #ifdef DEBUG
    518 bool RemoteCDMChild::IsOnOwnerThread() { return mThread->IsOnCurrentThread(); }
    519 #endif
    520 
    521 #undef LOGD
    522 
    523 }  // namespace mozilla