tor-browser

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

GMPPlatform.cpp (9200B)


      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 "GMPPlatform.h"
      7 
      8 #include "GMPChild.h"
      9 #include "GMPStorageChild.h"
     10 #include "GMPTimerChild.h"
     11 #include "base/task.h"
     12 #include "base/thread.h"
     13 #include "base/time.h"
     14 #include "mozilla/Monitor.h"
     15 #include "mozilla/Mutex.h"
     16 #include "mozilla/ReentrantMonitor.h"
     17 #include "mozilla/StaticMonitor.h"
     18 #include "nsTArray.h"
     19 #include "nsThreadUtils.h"
     20 
     21 #ifdef XP_WIN
     22 #  include "mozilla/UntrustedModulesProcessor.h"
     23 #endif
     24 
     25 #include <ctime>
     26 
     27 namespace mozilla::gmp {
     28 
     29 static MessageLoop* sMainLoop = nullptr;
     30 static GMPChild* sChild = nullptr;
     31 
     32 static StaticMonitor sMainLoopMonitor;
     33 static nsTArray<RefPtr<Runnable>>* sMainLoopPendingEvents
     34    MOZ_GUARDED_BY(sMainLoopMonitor) = nullptr;
     35 static bool sMainLoopHasPendingProcess MOZ_GUARDED_BY(sMainLoopMonitor) = false;
     36 
     37 static bool IsOnChildMainThread() {
     38  return sMainLoop && sMainLoop == MessageLoop::current();
     39 }
     40 
     41 // We just need a refcounted wrapper for GMPTask objects.
     42 class GMPRunnable final : public Runnable {
     43 public:
     44  explicit GMPRunnable(GMPTask* aTask)
     45      : Runnable("mozilla::gmp::GMPRunnable"), mTask(aTask) {
     46    MOZ_ASSERT(mTask);
     47  }
     48 
     49  NS_IMETHOD Run() override {
     50    mTask->Run();
     51    mTask->Destroy();
     52    mTask = nullptr;
     53    return NS_OK;
     54  }
     55 
     56 private:
     57  GMPTask* mTask;
     58 };
     59 
     60 class GMPSyncRunnable final : public Runnable {
     61 public:
     62  GMPSyncRunnable(GMPTask* aTask, MessageLoop* aMessageLoop)
     63      : Runnable("mozilla::gmp::GMPSyncRunnable"),
     64        mDone(false),
     65        mTask(aTask),
     66        mMonitor("GMPSyncRunnable") {
     67    MOZ_ASSERT(mTask);
     68  }
     69 
     70  void WaitUntilDone() {
     71    // We assert here for two reasons.
     72    // 1) Nobody should be blocking the main thread.
     73    // 2) This prevents deadlocks when doing sync calls to main which if the
     74    //    main thread tries to do a sync call back to the calling thread.
     75    MOZ_ASSERT(!IsOnChildMainThread());
     76 
     77    MonitorAutoLock lock(mMonitor);
     78    while (!mDone) {
     79      lock.Wait();
     80    }
     81  }
     82 
     83  NS_IMETHOD Run() override {
     84    mTask->Run();
     85    mTask->Destroy();
     86    mTask = nullptr;
     87    MonitorAutoLock lock(mMonitor);
     88    mDone = true;
     89    lock.Notify();
     90    return NS_OK;
     91  }
     92 
     93 private:
     94  bool mDone MOZ_GUARDED_BY(mMonitor);
     95  GMPTask* mTask;
     96  Monitor mMonitor;
     97 };
     98 
     99 class GMPThreadImpl final : public GMPThread {
    100 public:
    101  GMPThreadImpl();
    102  virtual ~GMPThreadImpl();
    103 
    104  // GMPThread
    105  void Post(GMPTask* aTask) override;
    106  void Join() override;
    107 
    108 private:
    109  Mutex mMutex MOZ_UNANNOTATED;
    110  base::Thread mThread MOZ_GUARDED_BY(mMutex);
    111 };
    112 
    113 GMPErr CreateThread(GMPThread** aThread) {
    114  if (!aThread) {
    115    return GMPGenericErr;
    116  }
    117 
    118  *aThread = new GMPThreadImpl();
    119 
    120  return GMPNoErr;
    121 }
    122 
    123 bool SpinPendingGmpEventsUntil(const SpinPendingPredicate& aPred,
    124                               uint32_t aTimeoutMs) {
    125  MOZ_ASSERT(IsOnChildMainThread());
    126 
    127  auto timeout = TimeDuration::FromMilliseconds(aTimeoutMs);
    128 
    129  while (!aPred()) {
    130    nsTArray<RefPtr<Runnable>> pendingEvents;
    131    {
    132      StaticMonitorAutoLock lock(sMainLoopMonitor);
    133      while (sMainLoopPendingEvents->IsEmpty()) {
    134        if (lock.Wait(timeout) == CVStatus::Timeout) {
    135          return false;
    136        }
    137      }
    138      pendingEvents = std::move(*sMainLoopPendingEvents);
    139    }
    140 
    141    for (auto& event : pendingEvents) {
    142      event->Run();
    143    }
    144  }
    145 
    146  return true;
    147 }
    148 
    149 static void ProcessPendingGmpEvents() {
    150  MOZ_ASSERT(IsOnChildMainThread());
    151 
    152  nsTArray<RefPtr<Runnable>> pendingEvents;
    153  {
    154    StaticMonitorAutoLock lock(sMainLoopMonitor);
    155    pendingEvents = std::move(*sMainLoopPendingEvents);
    156    sMainLoopHasPendingProcess = false;
    157  }
    158 
    159  for (auto& event : pendingEvents) {
    160    event->Run();
    161  }
    162 }
    163 
    164 static void QueueForMainThread(RefPtr<Runnable>&& aRunnable) {
    165  StaticMonitorAutoLock lock(sMainLoopMonitor);
    166  sMainLoopPendingEvents->AppendElement(std::move(aRunnable));
    167  if (!sMainLoopHasPendingProcess) {
    168    sMainLoop->PostTask(NewRunnableFunction(
    169        "mozilla::gmp::ProcessPendingGmpEvents", &ProcessPendingGmpEvents));
    170    sMainLoopHasPendingProcess = true;
    171  }
    172  lock.Notify();
    173 }
    174 
    175 GMPErr RunOnMainThread(GMPTask* aTask) {
    176  if (!aTask || !sMainLoop) {
    177    return GMPGenericErr;
    178  }
    179 
    180  RefPtr<GMPRunnable> r = new GMPRunnable(aTask);
    181  QueueForMainThread(std::move(r));
    182  return GMPNoErr;
    183 }
    184 
    185 GMPErr SyncRunOnMainThread(GMPTask* aTask) {
    186  if (!aTask || !sMainLoop || IsOnChildMainThread()) {
    187    return GMPGenericErr;
    188  }
    189 
    190  RefPtr<GMPSyncRunnable> r = new GMPSyncRunnable(aTask, sMainLoop);
    191  QueueForMainThread(RefPtr{r});
    192  r->WaitUntilDone();
    193 
    194  return GMPNoErr;
    195 }
    196 
    197 class MOZ_CAPABILITY("mutex") GMPMutexImpl final : public GMPMutex {
    198 public:
    199  GMPMutexImpl();
    200  virtual ~GMPMutexImpl();
    201 
    202  // GMPMutex
    203  void Acquire() override MOZ_CAPABILITY_ACQUIRE();
    204  void Release() override MOZ_CAPABILITY_RELEASE();
    205  void Destroy() override;
    206 
    207 private:
    208  ReentrantMonitor mMonitor MOZ_UNANNOTATED;
    209 };
    210 
    211 GMPErr CreateMutex(GMPMutex** aMutex) {
    212  if (!aMutex) {
    213    return GMPGenericErr;
    214  }
    215 
    216  *aMutex = new GMPMutexImpl();
    217 
    218  return GMPNoErr;
    219 }
    220 
    221 GMPErr CreateRecord(const char* aRecordName, uint32_t aRecordNameSize,
    222                    GMPRecord** aOutRecord, GMPRecordClient* aClient) {
    223  if (aRecordNameSize > GMP_MAX_RECORD_NAME_SIZE || aRecordNameSize == 0) {
    224    NS_WARNING("GMP tried to CreateRecord with too long or 0 record name");
    225    return GMPGenericErr;
    226  }
    227  GMPStorageChild* storage = sChild->GetGMPStorage();
    228  if (!storage) {
    229    return GMPGenericErr;
    230  }
    231  MOZ_ASSERT(storage);
    232  return storage->CreateRecord(nsDependentCString(aRecordName, aRecordNameSize),
    233                               aOutRecord, aClient);
    234 }
    235 
    236 GMPErr SetTimerOnMainThread(GMPTask* aTask, int64_t aTimeoutMS) {
    237  if (!aTask || !sMainLoop || !IsOnChildMainThread()) {
    238    return GMPGenericErr;
    239  }
    240  GMPTimerChild* timers = sChild->GetGMPTimers();
    241  NS_ENSURE_TRUE(timers, GMPGenericErr);
    242  return timers->SetTimer(aTask, aTimeoutMS);
    243 }
    244 
    245 GMPErr GetClock(GMPTimestamp* aOutTime) {
    246  if (!aOutTime) {
    247    return GMPGenericErr;
    248  }
    249  *aOutTime = base::Time::Now().ToDoubleT() * 1000.0;
    250  return GMPNoErr;
    251 }
    252 
    253 void InitPlatformAPI(GMPPlatformAPI& aPlatformAPI, GMPChild* aChild) {
    254  if (!sMainLoop) {
    255    sMainLoop = MessageLoop::current();
    256  }
    257  if (!sChild) {
    258    sChild = aChild;
    259  }
    260 
    261  {
    262    StaticMonitorAutoLock lock(sMainLoopMonitor);
    263    if (!sMainLoopPendingEvents) {
    264      sMainLoopPendingEvents = new nsTArray<RefPtr<Runnable>>();
    265    }
    266  }
    267 
    268  aPlatformAPI.version = 0;
    269  aPlatformAPI.createthread = &CreateThread;
    270  aPlatformAPI.runonmainthread = &RunOnMainThread;
    271  aPlatformAPI.syncrunonmainthread = &SyncRunOnMainThread;
    272  aPlatformAPI.createmutex = &CreateMutex;
    273  aPlatformAPI.createrecord = &CreateRecord;
    274  aPlatformAPI.settimer = &SetTimerOnMainThread;
    275  aPlatformAPI.getcurrenttime = &GetClock;
    276 }
    277 
    278 void ShutdownPlatformAPI() {
    279  StaticMonitorAutoLock lock(sMainLoopMonitor);
    280  if (sMainLoopPendingEvents) {
    281    delete sMainLoopPendingEvents;
    282    sMainLoopPendingEvents = nullptr;
    283  }
    284 }
    285 
    286 void SendFOGData(ipc::ByteBuf&& buf) {
    287  if (sChild) {
    288    sChild->SendFOGData(std::move(buf));
    289  }
    290 }
    291 
    292 #ifdef XP_WIN
    293 RefPtr<PGMPChild::GetModulesTrustPromise> SendGetModulesTrust(
    294    ModulePaths&& aModules, bool aRunAtNormalPriority) {
    295  if (!sChild) {
    296    return PGMPChild::GetModulesTrustPromise::CreateAndReject(
    297        ipc::ResponseRejectReason::SendError, __func__);
    298  }
    299  return sChild->SendGetModulesTrust(std::move(aModules), aRunAtNormalPriority);
    300 }
    301 #endif
    302 
    303 GMPThreadImpl::GMPThreadImpl() : mMutex("GMPThreadImpl"), mThread("GMPThread") {
    304  MOZ_COUNT_CTOR(GMPThread);
    305 }
    306 
    307 GMPThreadImpl::~GMPThreadImpl() { MOZ_COUNT_DTOR(GMPThread); }
    308 
    309 void GMPThreadImpl::Post(GMPTask* aTask) {
    310  MutexAutoLock lock(mMutex);
    311 
    312  if (!mThread.IsRunning()) {
    313    bool started = mThread.Start();
    314    if (!started) {
    315      NS_WARNING("Unable to start GMPThread!");
    316      return;
    317    }
    318  }
    319 
    320  RefPtr<GMPRunnable> r = new GMPRunnable(aTask);
    321  mThread.message_loop()->PostTask(
    322      NewRunnableMethod("gmp::GMPRunnable::Run", r.get(), &GMPRunnable::Run));
    323 }
    324 
    325 void GMPThreadImpl::Join() {
    326  {
    327    MutexAutoLock lock(mMutex);
    328    if (mThread.IsRunning()) {
    329      mThread.Stop();
    330    }
    331  }
    332  delete this;
    333 }
    334 
    335 GMPMutexImpl::GMPMutexImpl() : mMonitor("gmp-mutex") {
    336  MOZ_COUNT_CTOR(GMPMutexImpl);
    337 }
    338 
    339 GMPMutexImpl::~GMPMutexImpl() { MOZ_COUNT_DTOR(GMPMutexImpl); }
    340 
    341 void GMPMutexImpl::Destroy() { delete this; }
    342 
    343 MOZ_PUSH_IGNORE_THREAD_SAFETY
    344 void GMPMutexImpl::Acquire() { mMonitor.Enter(); }
    345 
    346 void GMPMutexImpl::Release() { mMonitor.Exit(); }
    347 MOZ_POP_THREAD_SAFETY
    348 
    349 GMPTask* NewGMPTask(std::function<void()>&& aFunction) {
    350  class Task : public GMPTask {
    351   public:
    352    explicit Task(std::function<void()>&& aFunction)
    353        : mFunction(std::move(aFunction)) {}
    354    void Destroy() override { delete this; }
    355    ~Task() override = default;
    356    void Run() override { mFunction(); }
    357 
    358   private:
    359    std::function<void()> mFunction;
    360  };
    361  return new Task(std::move(aFunction));
    362 }
    363 
    364 }  // namespace mozilla::gmp