tor-browser

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

GamepadPlatformService.cpp (11527B)


      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/dom/GamepadPlatformService.h"
      8 
      9 #include "mozilla/Mutex.h"
     10 #include "mozilla/dom/GamepadEventChannelParent.h"
     11 #include "mozilla/dom/GamepadMonitoring.h"
     12 #include "mozilla/dom/GamepadTestChannelParent.h"
     13 #include "mozilla/ipc/BackgroundParent.h"
     14 #include "nsCOMPtr.h"
     15 #include "nsHashKeys.h"
     16 
     17 using namespace mozilla::ipc;
     18 
     19 namespace mozilla::dom {
     20 
     21 namespace {
     22 
     23 // This is the singleton instance of GamepadPlatformService, can be called
     24 // by both background and monitor thread.
     25 StaticRefPtr<GamepadPlatformService> gGamepadPlatformServiceSingleton;
     26 
     27 }  // namespace
     28 
     29 // static
     30 GamepadMonitoringState& GamepadMonitoringState::GetSingleton() {
     31  static GamepadMonitoringState sInstance{};
     32  return sInstance;
     33 }
     34 
     35 void GamepadMonitoringState::AddObserver(GamepadTestChannelParent* aParent) {
     36  AssertIsOnBackgroundThread();
     37  MOZ_ASSERT(aParent);
     38  MOZ_ALWAYS_TRUE(mObservers.append(aParent));
     39 }
     40 
     41 void GamepadMonitoringState::RemoveObserver(GamepadTestChannelParent* aParent) {
     42  AssertIsOnBackgroundThread();
     43  MOZ_ASSERT(aParent);
     44 
     45  WeakPtr<GamepadTestChannelParent>* observer = nullptr;
     46 
     47  for (auto& item : mObservers) {
     48    if (item == aParent) {
     49      observer = &item;
     50    }
     51  }
     52 
     53  MOZ_ASSERT(
     54      observer,
     55      "Attempted to remove a GamepadTestChannelParent that was never added");
     56 
     57  std::swap(*observer, mObservers.back());
     58  mObservers.popBack();
     59 }
     60 
     61 bool GamepadMonitoringState::IsMonitoring() const {
     62  AssertIsOnBackgroundThread();
     63  return mIsMonitoring;
     64 }
     65 
     66 void GamepadMonitoringState::Set(bool aIsMonitoring) {
     67  AssertIsOnBackgroundThread();
     68 
     69  if (mIsMonitoring != aIsMonitoring) {
     70    mIsMonitoring = aIsMonitoring;
     71    for (auto& observer : mObservers) {
     72      // Since each GamepadTestChannelParent removes itself in its dtor, this
     73      // should never be nullptr
     74      MOZ_RELEASE_ASSERT(observer);
     75      observer->OnMonitoringStateChanged(aIsMonitoring);
     76    }
     77  }
     78 }
     79 
     80 GamepadPlatformService::GamepadPlatformService()
     81    : mNextGamepadHandleValue(1),
     82      mMutex("mozilla::dom::GamepadPlatformService") {}
     83 
     84 GamepadPlatformService::~GamepadPlatformService() { Cleanup(); }
     85 
     86 // static
     87 already_AddRefed<GamepadPlatformService>
     88 GamepadPlatformService::GetParentService() {
     89  // GamepadPlatformService can only be accessed in parent process
     90  MOZ_ASSERT(XRE_IsParentProcess());
     91  if (!gGamepadPlatformServiceSingleton) {
     92    // Only Background Thread can create new GamepadPlatformService instance.
     93    if (IsOnBackgroundThread()) {
     94      gGamepadPlatformServiceSingleton = new GamepadPlatformService();
     95    } else {
     96      return nullptr;
     97    }
     98  }
     99  RefPtr<GamepadPlatformService> service(gGamepadPlatformServiceSingleton);
    100  return service.forget();
    101 }
    102 
    103 template <class T>
    104 void GamepadPlatformService::NotifyGamepadChange(GamepadHandle aHandle,
    105                                                 const T& aInfo) {
    106  // This method is called by monitor populated in
    107  // platform-dependent backends
    108  MOZ_ASSERT(XRE_IsParentProcess());
    109  MOZ_ASSERT(!NS_IsMainThread());
    110 
    111  GamepadChangeEventBody body(aInfo);
    112  GamepadChangeEvent e(aHandle, body);
    113 
    114  // mChannelParents may be accessed by background thread in the
    115  // same time, we use mutex to prevent possible race condtion
    116  MutexAutoLock autoLock(mMutex);
    117 
    118  for (uint32_t i = 0; i < mChannelParents.Length(); ++i) {
    119    mChannelParents[i]->DispatchUpdateEvent(e);
    120  }
    121 }
    122 
    123 GamepadHandle GamepadPlatformService::AddGamepad(
    124    const char* aID, GamepadMappingType aMapping, GamepadHand aHand,
    125    uint32_t aNumButtons, uint32_t aNumAxes, uint32_t aHaptics,
    126    uint32_t aNumLightIndicator, uint32_t aNumTouchEvents) {
    127  // This method is called by monitor thread populated in
    128  // platform-dependent backends
    129  MOZ_ASSERT(XRE_IsParentProcess());
    130  MOZ_ASSERT(!NS_IsMainThread());
    131 
    132  GamepadHandle gamepadHandle{mNextGamepadHandleValue++,
    133                              GamepadHandleKind::GamepadPlatformManager};
    134 
    135  GamepadAdded a(NS_ConvertUTF8toUTF16(nsDependentCString(aID)), aMapping,
    136                 aHand, aNumButtons, aNumAxes, aHaptics, aNumLightIndicator,
    137                 aNumTouchEvents);
    138 
    139  mGamepadAdded.emplace(gamepadHandle, a);
    140  NotifyGamepadChange<GamepadAdded>(gamepadHandle, a);
    141  return gamepadHandle;
    142 }
    143 
    144 void GamepadPlatformService::RemoveGamepad(GamepadHandle aHandle) {
    145  // This method is called by monitor thread populated in
    146  // platform-dependent backends
    147  MOZ_ASSERT(XRE_IsParentProcess());
    148  MOZ_ASSERT(!NS_IsMainThread());
    149  GamepadRemoved a;
    150  NotifyGamepadChange<GamepadRemoved>(aHandle, a);
    151  mGamepadAdded.erase(aHandle);
    152 }
    153 
    154 void GamepadPlatformService::NewButtonEvent(GamepadHandle aHandle,
    155                                            uint32_t aButton, bool aPressed,
    156                                            bool aTouched, double aValue) {
    157  // This method is called by monitor thread populated in
    158  // platform-dependent backends
    159  MOZ_ASSERT(XRE_IsParentProcess());
    160  MOZ_ASSERT(!NS_IsMainThread());
    161  GamepadButtonInformation a(aButton, aValue, aPressed, aTouched);
    162  NotifyGamepadChange<GamepadButtonInformation>(aHandle, a);
    163 }
    164 
    165 void GamepadPlatformService::NewButtonEvent(GamepadHandle aHandle,
    166                                            uint32_t aButton, bool aPressed,
    167                                            double aValue) {
    168  // This method is called by monitor thread populated in
    169  // platform-dependent backends
    170  MOZ_ASSERT(XRE_IsParentProcess());
    171  MOZ_ASSERT(!NS_IsMainThread());
    172  // When only a digital button is available the value will be synthesized.
    173  NewButtonEvent(aHandle, aButton, aPressed, aPressed, aValue);
    174 }
    175 
    176 void GamepadPlatformService::NewButtonEvent(GamepadHandle aHandle,
    177                                            uint32_t aButton, bool aPressed,
    178                                            bool aTouched) {
    179  // This method is called by monitor thread populated in
    180  // platform-dependent backends
    181  MOZ_ASSERT(XRE_IsParentProcess());
    182  MOZ_ASSERT(!NS_IsMainThread());
    183  // When only a digital button is available the value will be synthesized.
    184  NewButtonEvent(aHandle, aButton, aPressed, aTouched, aPressed ? 1.0L : 0.0L);
    185 }
    186 
    187 void GamepadPlatformService::NewButtonEvent(GamepadHandle aHandle,
    188                                            uint32_t aButton, bool aPressed) {
    189  // This method is called by monitor thread populated in
    190  // platform-dependent backends
    191  MOZ_ASSERT(XRE_IsParentProcess());
    192  MOZ_ASSERT(!NS_IsMainThread());
    193  // When only a digital button is available the value will be synthesized.
    194  NewButtonEvent(aHandle, aButton, aPressed, aPressed, aPressed ? 1.0L : 0.0L);
    195 }
    196 
    197 void GamepadPlatformService::NewAxisMoveEvent(GamepadHandle aHandle,
    198                                              uint32_t aAxis, double aValue) {
    199  // This method is called by monitor thread populated in
    200  // platform-dependent backends
    201  MOZ_ASSERT(XRE_IsParentProcess());
    202  MOZ_ASSERT(!NS_IsMainThread());
    203  GamepadAxisInformation a(aAxis, aValue);
    204  NotifyGamepadChange<GamepadAxisInformation>(aHandle, a);
    205 }
    206 
    207 void GamepadPlatformService::NewLightIndicatorTypeEvent(
    208    GamepadHandle aHandle, uint32_t aLight, GamepadLightIndicatorType aType) {
    209  // This method is called by monitor thread populated in
    210  // platform-dependent backends
    211  MOZ_ASSERT(XRE_IsParentProcess());
    212  MOZ_ASSERT(!NS_IsMainThread());
    213  GamepadLightIndicatorTypeInformation a(aLight, aType);
    214  NotifyGamepadChange<GamepadLightIndicatorTypeInformation>(aHandle, a);
    215 }
    216 
    217 void GamepadPlatformService::NewPoseEvent(GamepadHandle aHandle,
    218                                          const GamepadPoseState& aState) {
    219  // This method is called by monitor thread populated in
    220  // platform-dependent backends
    221  MOZ_ASSERT(XRE_IsParentProcess());
    222  MOZ_ASSERT(!NS_IsMainThread());
    223  GamepadPoseInformation a(aState);
    224  NotifyGamepadChange<GamepadPoseInformation>(aHandle, a);
    225 }
    226 
    227 void GamepadPlatformService::NewMultiTouchEvent(
    228    GamepadHandle aHandle, uint32_t aTouchArrayIndex,
    229    const GamepadTouchState& aState) {
    230  // This method is called by monitor thread populated in
    231  // platform-dependent backends
    232  MOZ_ASSERT(XRE_IsParentProcess());
    233  MOZ_ASSERT(!NS_IsMainThread());
    234 
    235  GamepadTouchInformation a(aTouchArrayIndex, aState);
    236  NotifyGamepadChange<GamepadTouchInformation>(aHandle, a);
    237 }
    238 
    239 void GamepadPlatformService::ResetGamepadIndexes() {
    240  // This method is called by monitor thread populated in
    241  // platform-dependent backends
    242  MOZ_ASSERT(XRE_IsParentProcess());
    243  MOZ_ASSERT(!NS_IsMainThread());
    244  mNextGamepadHandleValue = 1;
    245 }
    246 
    247 void GamepadPlatformService::AddChannelParent(
    248    GamepadEventChannelParent* aParent) {
    249  // mChannelParents can only be modified once GamepadEventChannelParent
    250  // is created or removed in Background thread
    251  AssertIsOnBackgroundThread();
    252  MOZ_ASSERT(aParent);
    253  MOZ_ASSERT(!mChannelParents.Contains(aParent));
    254 
    255  // We use mutex here to prevent race condition with monitor thread
    256  {
    257    MutexAutoLock autoLock(mMutex);
    258    mChannelParents.AppendElement(aParent);
    259 
    260    // For a new GamepadEventChannel, we have to send the exising GamepadAdded
    261    // to it to make it can have the same amount of gamepads with others.
    262    if (mChannelParents.Length() > 1) {
    263      for (const auto& evt : mGamepadAdded) {
    264        GamepadChangeEventBody body(evt.second);
    265        GamepadChangeEvent e(evt.first, body);
    266        aParent->DispatchUpdateEvent(e);
    267      }
    268    }
    269  }
    270 
    271  StartGamepadMonitoring();
    272 
    273  GamepadMonitoringState::GetSingleton().Set(true);
    274 }
    275 
    276 void GamepadPlatformService::RemoveChannelParent(
    277    GamepadEventChannelParent* aParent) {
    278  // mChannelParents can only be modified once GamepadEventChannelParent
    279  // is created or removed in Background thread
    280  AssertIsOnBackgroundThread();
    281  MOZ_ASSERT(aParent);
    282  MOZ_ASSERT(mChannelParents.Contains(aParent));
    283 
    284  // We use mutex here to prevent race condition with monitor thread
    285  {
    286    MutexAutoLock autoLock(mMutex);
    287    mChannelParents.RemoveElement(aParent);
    288    if (!mChannelParents.IsEmpty()) {
    289      return;
    290    }
    291  }
    292 
    293  GamepadMonitoringState::GetSingleton().Set(false);
    294 
    295  StopGamepadMonitoring();
    296  ResetGamepadIndexes();
    297  MaybeShutdown();
    298 }
    299 
    300 void GamepadPlatformService::MaybeShutdown() {
    301  // This method is invoked in MaybeStopGamepadMonitoring when
    302  // an IPDL channel is going to be destroyed
    303  AssertIsOnBackgroundThread();
    304 
    305  // We have to release gGamepadPlatformServiceSingleton ouside
    306  // the mutex as well as making upcoming GetParentService() call
    307  // recreate new singleton, so we use this RefPtr to temporarily
    308  // hold the reference, postponing the release process until this
    309  // method ends.
    310  RefPtr<GamepadPlatformService> kungFuDeathGrip;
    311 
    312  bool isChannelParentEmpty;
    313  {
    314    MutexAutoLock autoLock(mMutex);
    315    isChannelParentEmpty = mChannelParents.IsEmpty();
    316    if (isChannelParentEmpty) {
    317      kungFuDeathGrip = gGamepadPlatformServiceSingleton;
    318      gGamepadPlatformServiceSingleton = nullptr;
    319      mGamepadAdded.clear();
    320    }
    321  }
    322 }
    323 
    324 void GamepadPlatformService::Cleanup() {
    325  // This method is called when GamepadPlatformService is
    326  // successfully distructed in background thread
    327  AssertIsOnBackgroundThread();
    328 
    329  MutexAutoLock autoLock(mMutex);
    330  mChannelParents.Clear();
    331 }
    332 
    333 }  // namespace mozilla::dom