tor-browser

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

GMPContentParent.cpp (7415B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "GMPContentParent.h"
      7 
      8 #include "ChromiumCDMParent.h"
      9 #include "GMPLog.h"
     10 #include "GMPParent.h"
     11 #include "GMPServiceChild.h"
     12 #include "GMPVideoDecoderParent.h"
     13 #include "GMPVideoEncoderParent.h"
     14 #include "base/task.h"
     15 #include "mozIGeckoMediaPluginService.h"
     16 #include "mozilla/Logging.h"
     17 
     18 namespace mozilla::gmp {
     19 
     20 static const char* GetBoolString(bool aBool) {
     21  return aBool ? "true" : "false";
     22 }
     23 
     24 GMPContentParent::GMPContentParent(GMPParent* aParent)
     25    : mParent(aParent), mPluginId(0) {
     26  GMP_LOG_DEBUG("GMPContentParent::GMPContentParent(this=%p), aParent=%p", this,
     27                aParent);
     28  if (mParent) {
     29    SetDisplayName(mParent->GetDisplayName());
     30    SetPluginId(mParent->GetPluginId());
     31    SetPluginType(mParent->GetPluginType());
     32  }
     33 }
     34 
     35 GMPContentParent::~GMPContentParent() {
     36  GMP_LOG_DEBUG(
     37      "GMPContentParent::~GMPContentParent(this=%p) mVideoDecoders.IsEmpty=%s, "
     38      "mVideoEncoders.IsEmpty=%s, mChromiumCDMs.IsEmpty=%s, "
     39      "mCloseBlockerCount=%" PRIu32,
     40      this, GetBoolString(mVideoDecoders.IsEmpty()),
     41      GetBoolString(mVideoEncoders.IsEmpty()),
     42      GetBoolString(mChromiumCDMs.IsEmpty()), mCloseBlockerCount);
     43 }
     44 
     45 void GMPContentParent::ActorDestroy(ActorDestroyReason aWhy) {
     46  GMP_LOG_DEBUG("GMPContentParent::ActorDestroy(this=%p, aWhy=%d)", this,
     47                static_cast<int>(aWhy));
     48  MOZ_ASSERT(mVideoDecoders.IsEmpty() && mVideoEncoders.IsEmpty() &&
     49             mChromiumCDMs.IsEmpty());
     50 }
     51 
     52 void GMPContentParent::ChromiumCDMDestroyed(ChromiumCDMParent* aCDM) {
     53  GMP_LOG_DEBUG("GMPContentParent::ChromiumCDMDestroyed(this=%p, aCDM=%p)",
     54                this, aCDM);
     55  MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread());
     56 
     57  MOZ_ALWAYS_TRUE(mChromiumCDMs.RemoveElement(aCDM));
     58  CloseIfUnused();
     59 }
     60 
     61 void GMPContentParent::VideoDecoderDestroyed(GMPVideoDecoderParent* aDecoder) {
     62  GMP_LOG_DEBUG("GMPContentParent::VideoDecoderDestroyed(this=%p, aDecoder=%p)",
     63                this, aDecoder);
     64  MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread());
     65 
     66  // If the constructor fails, we'll get called before it's added
     67  (void)NS_WARN_IF(!mVideoDecoders.RemoveElement(aDecoder));
     68  CloseIfUnused();
     69 }
     70 
     71 void GMPContentParent::VideoEncoderDestroyed(GMPVideoEncoderParent* aEncoder) {
     72  GMP_LOG_DEBUG("GMPContentParent::VideoEncoderDestroyed(this=%p, aEncoder=%p)",
     73                this, aEncoder);
     74  MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread());
     75 
     76  // If the constructor fails, we'll get called before it's added
     77  (void)NS_WARN_IF(!mVideoEncoders.RemoveElement(aEncoder));
     78  CloseIfUnused();
     79 }
     80 
     81 void GMPContentParent::AddCloseBlocker() {
     82  MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread());
     83  ++mCloseBlockerCount;
     84  GMP_LOG_DEBUG(
     85      "GMPContentParent::AddCloseBlocker(this=%p) mCloseBlockerCount=%" PRIu32,
     86      this, mCloseBlockerCount);
     87 }
     88 
     89 void GMPContentParent::RemoveCloseBlocker() {
     90  MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread());
     91  --mCloseBlockerCount;
     92  GMP_LOG_DEBUG(
     93      "GMPContentParent::RemoveCloseBlocker(this=%p) "
     94      "mCloseBlockerCount=%" PRIu32,
     95      this, mCloseBlockerCount);
     96  CloseIfUnused();
     97 }
     98 
     99 void GMPContentParent::CloseIfUnused() {
    100  MOZ_ASSERT(GMPEventTarget()->IsOnCurrentThread());
    101  GMP_LOG_DEBUG(
    102      "GMPContentParent::CloseIfUnused(this=%p) mVideoDecoders.IsEmpty=%s, "
    103      "mVideoEncoders.IsEmpty=%s, mChromiumCDMs.IsEmpty=%s, "
    104      "mCloseBlockerCount=%" PRIu32,
    105      this, GetBoolString(mVideoDecoders.IsEmpty()),
    106      GetBoolString(mVideoEncoders.IsEmpty()),
    107      GetBoolString(mChromiumCDMs.IsEmpty()), mCloseBlockerCount);
    108  if (mVideoDecoders.IsEmpty() && mVideoEncoders.IsEmpty() &&
    109      mChromiumCDMs.IsEmpty() && mCloseBlockerCount == 0) {
    110    RefPtr<GMPContentParent> toClose;
    111    if (mParent) {
    112      toClose = mParent->ForgetGMPContentParent();
    113    } else {
    114      toClose = this;
    115      RefPtr<GeckoMediaPluginServiceChild> gmp(
    116          GeckoMediaPluginServiceChild::GetSingleton());
    117      if (gmp) {
    118        gmp->RemoveGMPContentParent(toClose);
    119      }
    120    }
    121    NS_DispatchToCurrentThread(NewRunnableMethod(
    122        "gmp::GMPContentParent::Close", toClose, &GMPContentParent::Close));
    123  }
    124 }
    125 
    126 nsCOMPtr<nsISerialEventTarget> GMPContentParent::GMPEventTarget() {
    127  if (!mGMPEventTarget) {
    128    GMP_LOG_DEBUG("GMPContentParent::GMPEventTarget(this=%p)", this);
    129    nsCOMPtr<mozIGeckoMediaPluginService> mps =
    130        do_GetService("@mozilla.org/gecko-media-plugin-service;1");
    131    MOZ_ASSERT(mps);
    132    if (!mps) {
    133      return nullptr;
    134    }
    135    // Not really safe if we just grab to the mGMPEventTarget, as we don't know
    136    // what thread we're running on and other threads may be trying to
    137    // access this without locks!  However, debug only, and primary failure
    138    // mode outside of compiler-helped TSAN is a leak.  But better would be
    139    // to use swap() under a lock.
    140    nsCOMPtr<nsIThread> gmpThread;
    141    mps->GetThread(getter_AddRefs(gmpThread));
    142    MOZ_ASSERT(gmpThread);
    143 
    144    mGMPEventTarget = gmpThread;
    145  }
    146 
    147  return mGMPEventTarget;
    148 }
    149 
    150 already_AddRefed<ChromiumCDMParent> GMPContentParent::GetChromiumCDM(
    151    const nsCString& aKeySystem) {
    152  GMP_LOG_DEBUG("GMPContentParent::GetChromiumCDM(this=%p aKeySystem=%s)", this,
    153                aKeySystem.get());
    154 
    155  RefPtr<ChromiumCDMParent> parent = new ChromiumCDMParent(this, GetPluginId());
    156  // TODO: Remove parent from mChromiumCDMs in ChromiumCDMParent::Destroy().
    157  mChromiumCDMs.AppendElement(parent);
    158 
    159  if (!SendPChromiumCDMConstructor(parent, aKeySystem)) {
    160    MOZ_ASSERT(!mChromiumCDMs.Contains(parent));
    161    return nullptr;
    162  }
    163 
    164  return parent.forget();
    165 }
    166 
    167 nsresult GMPContentParent::GetGMPVideoDecoder(GMPVideoDecoderParent** aGMPVD) {
    168  GMP_LOG_DEBUG("GMPContentParent::GetGMPVideoDecoder(this=%p)", this);
    169 
    170  RefPtr<GMPVideoDecoderParent> vdp = new GMPVideoDecoderParent(this);
    171  if (!SendPGMPVideoDecoderConstructor(vdp)) {
    172    return NS_ERROR_FAILURE;
    173  }
    174 
    175  // This addref corresponds to the Proxy pointer the consumer is returned.
    176  // It's dropped by calling Close() on the interface.
    177  vdp.get()->AddRef();
    178  *aGMPVD = vdp;
    179  mVideoDecoders.AppendElement(vdp);
    180 
    181  return NS_OK;
    182 }
    183 
    184 nsresult GMPContentParent::GetGMPVideoEncoder(GMPVideoEncoderParent** aGMPVE) {
    185  GMP_LOG_DEBUG("GMPContentParent::GetGMPVideoEncoder(this=%p)", this);
    186 
    187  RefPtr<GMPVideoEncoderParent> vep = new GMPVideoEncoderParent(this);
    188  if (!SendPGMPVideoEncoderConstructor(vep)) {
    189    return NS_ERROR_FAILURE;
    190  }
    191 
    192  // This addref corresponds to the Proxy pointer the consumer is returned.
    193  // It's dropped by calling Close() on the interface.
    194  vep.get()->AddRef();
    195  *aGMPVE = vep;
    196  mVideoEncoders.AppendElement(vep);
    197 
    198  return NS_OK;
    199 }
    200 
    201 void GMPContentParentCloseBlocker::Destroy() {
    202  MOZ_ASSERT(mParent);
    203  MOZ_ASSERT(mEventTarget);
    204 
    205  if (!mEventTarget->IsOnCurrentThread()) {
    206    mEventTarget->Dispatch(NS_NewRunnableFunction(
    207        __func__, [parent = std::move(mParent), eventTarget = mEventTarget]() {
    208          parent->RemoveCloseBlocker();
    209        }));
    210    mEventTarget = nullptr;
    211    return;
    212  }
    213 
    214  mParent->RemoveCloseBlocker();
    215  mParent = nullptr;
    216  mEventTarget = nullptr;
    217 }
    218 
    219 }  // namespace mozilla::gmp