tor-browser

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

GamepadManager.cpp (21857B)


      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/GamepadManager.h"
      8 
      9 #include "VRManagerChild.h"
     10 #include "mozilla/ClearOnShutdown.h"
     11 #include "mozilla/Preferences.h"
     12 #include "mozilla/Services.h"
     13 #include "mozilla/StaticPrefs_dom.h"
     14 #include "mozilla/StaticPtr.h"
     15 #include "mozilla/dom/Gamepad.h"
     16 #include "mozilla/dom/GamepadAxisMoveEvent.h"
     17 #include "mozilla/dom/GamepadButtonEvent.h"
     18 #include "mozilla/dom/GamepadEvent.h"
     19 #include "mozilla/dom/GamepadEventChannelChild.h"
     20 #include "mozilla/dom/GamepadMonitoring.h"
     21 #include "mozilla/dom/Promise.h"
     22 #include "mozilla/ipc/BackgroundChild.h"
     23 #include "mozilla/ipc/PBackgroundChild.h"
     24 #include "nsContentUtils.h"
     25 #include "nsGlobalWindowInner.h"
     26 #include "nsIObserver.h"
     27 #include "nsIObserverService.h"
     28 #include "nsThreadUtils.h"
     29 
     30 using namespace mozilla::ipc;
     31 
     32 namespace mozilla::dom {
     33 
     34 namespace {
     35 
     36 const nsTArray<RefPtr<nsGlobalWindowInner>>::index_type NoIndex =
     37    nsTArray<RefPtr<nsGlobalWindowInner>>::NoIndex;
     38 
     39 bool sShutdown = false;
     40 
     41 StaticRefPtr<GamepadManager> gGamepadManagerSingleton;
     42 
     43 // A threshold value of axis move to determine the first
     44 // intent.
     45 const float AXIS_FIRST_INTENT_THRESHOLD_VALUE = 0.1f;
     46 
     47 }  // namespace
     48 
     49 NS_IMPL_ISUPPORTS(GamepadManager, nsIObserver)
     50 
     51 GamepadManager::GamepadManager()
     52    : mEnabled(false),
     53      mNonstandardEventsEnabled(false),
     54      mShuttingDown(false),
     55      mPromiseID(0) {}
     56 
     57 nsresult GamepadManager::Init() {
     58  mEnabled = StaticPrefs::dom_gamepad_enabled();
     59  mNonstandardEventsEnabled =
     60      StaticPrefs::dom_gamepad_non_standard_events_enabled();
     61  nsCOMPtr<nsIObserverService> observerService =
     62      mozilla::services::GetObserverService();
     63 
     64  if (NS_WARN_IF(!observerService)) {
     65    return NS_ERROR_FAILURE;
     66  }
     67 
     68  nsresult rv;
     69  rv = observerService->AddObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID,
     70                                    false);
     71 
     72  if (NS_WARN_IF(NS_FAILED(rv))) {
     73    return rv;
     74  }
     75 
     76  return NS_OK;
     77 }
     78 
     79 NS_IMETHODIMP
     80 GamepadManager::Observe(nsISupports* aSubject, const char* aTopic,
     81                        const char16_t* aData) {
     82  nsCOMPtr<nsIObserverService> observerService =
     83      mozilla::services::GetObserverService();
     84  if (observerService) {
     85    observerService->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
     86  }
     87  BeginShutdown();
     88  return NS_OK;
     89 }
     90 
     91 void GamepadManager::StopMonitoring() {
     92  if (mChannelChild) {
     93    PGamepadEventChannelChild::Send__delete__(mChannelChild);
     94    mChannelChild = nullptr;
     95  }
     96  if (gfx::VRManagerChild::IsCreated()) {
     97    gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
     98    vm->SendControllerListenerRemoved();
     99  }
    100  mGamepads.Clear();
    101 }
    102 
    103 void GamepadManager::BeginShutdown() {
    104  mShuttingDown = true;
    105  StopMonitoring();
    106  // Don't let windows call back to unregister during shutdown
    107  for (uint32_t i = 0; i < mListeners.Length(); i++) {
    108    mListeners[i]->SetHasGamepadEventListener(false);
    109  }
    110  mListeners.Clear();
    111  sShutdown = true;
    112 }
    113 
    114 void GamepadManager::AddListener(nsGlobalWindowInner* aWindow) {
    115  MOZ_ASSERT(aWindow);
    116  MOZ_ASSERT(NS_IsMainThread());
    117 
    118  // IPDL child has not been created
    119  if (!mChannelChild) {
    120    PBackgroundChild* actor = BackgroundChild::GetOrCreateForCurrentThread();
    121    if (NS_WARN_IF(!actor)) {
    122      // We are probably shutting down.
    123      return;
    124    }
    125 
    126    RefPtr<GamepadEventChannelChild> child(GamepadEventChannelChild::Create());
    127    if (!actor->SendPGamepadEventChannelConstructor(child.get())) {
    128      // We are probably shutting down.
    129      return;
    130    }
    131 
    132    mChannelChild = child;
    133 
    134    if (gfx::VRManagerChild::IsCreated()) {
    135      // Construct VRManagerChannel and ask adding the connected
    136      // VR controllers to GamepadManager
    137      gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
    138      vm->SendControllerListenerAdded();
    139    }
    140  }
    141 
    142  if (!mEnabled || mShuttingDown ||
    143      aWindow->ShouldResistFingerprinting(RFPTarget::Gamepad)) {
    144    return;
    145  }
    146 
    147  if (mListeners.IndexOf(aWindow) != NoIndex) {
    148    return;  // already exists
    149  }
    150 
    151  mListeners.AppendElement(aWindow);
    152 }
    153 
    154 void GamepadManager::RemoveListener(nsGlobalWindowInner* aWindow) {
    155  MOZ_ASSERT(aWindow);
    156 
    157  if (mShuttingDown) {
    158    // Doesn't matter at this point. It's possible we're being called
    159    // as a result of our own destructor here, so just bail out.
    160    return;
    161  }
    162 
    163  if (mListeners.IndexOf(aWindow) == NoIndex) {
    164    return;  // doesn't exist
    165  }
    166 
    167  for (const auto& key : mGamepads.Keys()) {
    168    aWindow->RemoveGamepad(key);
    169  }
    170 
    171  mListeners.RemoveElement(aWindow);
    172 
    173  if (mListeners.IsEmpty()) {
    174    StopMonitoring();
    175  }
    176 }
    177 
    178 already_AddRefed<Gamepad> GamepadManager::GetGamepad(
    179    GamepadHandle aHandle) const {
    180  RefPtr<Gamepad> gamepad;
    181  if (mGamepads.Get(aHandle, getter_AddRefs(gamepad))) {
    182    return gamepad.forget();
    183  }
    184 
    185  return nullptr;
    186 }
    187 
    188 void GamepadManager::AddGamepad(GamepadHandle aHandle, const nsAString& aId,
    189                                GamepadMappingType aMapping, GamepadHand aHand,
    190                                uint32_t aNumButtons, uint32_t aNumAxes,
    191                                uint32_t aNumHaptics,
    192                                uint32_t aNumLightIndicator,
    193                                uint32_t aNumTouchEvents) {
    194  // TODO: bug 852258: get initial button/axis state
    195  RefPtr<Gamepad> newGamepad =
    196      new Gamepad(nullptr, aId,
    197                  0,  // index is set by global window
    198                  aHandle, aMapping, aHand, aNumButtons, aNumAxes, aNumHaptics,
    199                  aNumLightIndicator, aNumTouchEvents);
    200 
    201  // We store the gamepad related to its index given by the parent process,
    202  // and no duplicate index is allowed.
    203  MOZ_ASSERT(!mGamepads.Contains(aHandle));
    204  mGamepads.InsertOrUpdate(aHandle, std::move(newGamepad));
    205  NewConnectionEvent(aHandle, true);
    206 }
    207 
    208 void GamepadManager::RemoveGamepad(GamepadHandle aHandle) {
    209  RefPtr<Gamepad> gamepad = GetGamepad(aHandle);
    210  if (!gamepad) {
    211    NS_WARNING("Trying to delete gamepad with invalid index");
    212    return;
    213  }
    214  gamepad->SetConnected(false);
    215  NewConnectionEvent(aHandle, false);
    216  mGamepads.Remove(aHandle);
    217 }
    218 
    219 void GamepadManager::FireButtonEvent(EventTarget* aTarget, Gamepad* aGamepad,
    220                                     uint32_t aButton, double aValue) {
    221  nsString name =
    222      aValue == 1.0L ? u"gamepadbuttondown"_ns : u"gamepadbuttonup"_ns;
    223  GamepadButtonEventInit init;
    224  init.mBubbles = false;
    225  init.mCancelable = false;
    226  init.mGamepad = aGamepad;
    227  init.mButton = aButton;
    228  RefPtr<GamepadButtonEvent> event =
    229      GamepadButtonEvent::Constructor(aTarget, name, init);
    230 
    231  event->SetTrusted(true);
    232 
    233  aTarget->DispatchEvent(*event);
    234 }
    235 
    236 void GamepadManager::FireAxisMoveEvent(EventTarget* aTarget, Gamepad* aGamepad,
    237                                       uint32_t aAxis, double aValue) {
    238  GamepadAxisMoveEventInit init;
    239  init.mBubbles = false;
    240  init.mCancelable = false;
    241  init.mGamepad = aGamepad;
    242  init.mAxis = aAxis;
    243  init.mValue = aValue;
    244  RefPtr<GamepadAxisMoveEvent> event =
    245      GamepadAxisMoveEvent::Constructor(aTarget, u"gamepadaxismove"_ns, init);
    246 
    247  event->SetTrusted(true);
    248 
    249  aTarget->DispatchEvent(*event);
    250 }
    251 
    252 void GamepadManager::NewConnectionEvent(GamepadHandle aHandle,
    253                                        bool aConnected) {
    254  if (mShuttingDown) {
    255    return;
    256  }
    257 
    258  RefPtr<Gamepad> gamepad = GetGamepad(aHandle);
    259  if (!gamepad) {
    260    return;
    261  }
    262 
    263  // Hold on to listeners in a separate array because firing events
    264  // can mutate the mListeners array.
    265  nsTArray<RefPtr<nsGlobalWindowInner>> listeners(mListeners.Clone());
    266 
    267  if (aConnected) {
    268    for (uint32_t i = 0; i < listeners.Length(); i++) {
    269 #ifdef NIGHTLY_BUILD
    270      // Don't fire a gamepadconnected event unless it's a secure context
    271      if (!listeners[i]->IsSecureContext()) {
    272        continue;
    273      }
    274 #endif
    275 
    276      // Do not fire gamepadconnected and gamepaddisconnected events when
    277      // privacy.resistFingerprinting is true.
    278      if (listeners[i]->ShouldResistFingerprinting(RFPTarget::Gamepad)) {
    279        continue;
    280      }
    281 
    282      // Only send events to non-background windows
    283      if (!listeners[i]->IsCurrentInnerWindow() ||
    284          listeners[i]->GetOuterWindow()->IsBackground()) {
    285        continue;
    286      }
    287 
    288      // We don't fire a connected event here unless the window
    289      // has seen input from at least one device.
    290      if (!listeners[i]->HasSeenGamepadInput()) {
    291        continue;
    292      }
    293 
    294      SetWindowHasSeenGamepad(listeners[i], aHandle);
    295 
    296      RefPtr<Gamepad> listenerGamepad = listeners[i]->GetGamepad(aHandle);
    297      if (listenerGamepad) {
    298        // Fire event
    299        FireConnectionEvent(listeners[i], listenerGamepad, aConnected);
    300      }
    301    }
    302  } else {
    303    // For disconnection events, fire one at every window that has received
    304    // data from this gamepad.
    305    for (uint32_t i = 0; i < listeners.Length(); i++) {
    306      // Even background windows get these events, so we don't have to
    307      // deal with the hassle of syncing the state of removed gamepads.
    308 
    309      // Do not fire gamepadconnected and gamepaddisconnected events when
    310      // privacy.resistFingerprinting is true.
    311      if (listeners[i]->ShouldResistFingerprinting(RFPTarget::Gamepad)) {
    312        continue;
    313      }
    314 
    315      if (WindowHasSeenGamepad(listeners[i], aHandle)) {
    316        RefPtr<Gamepad> listenerGamepad = listeners[i]->GetGamepad(aHandle);
    317        if (listenerGamepad) {
    318          listenerGamepad->SetConnected(false);
    319          // Fire event
    320          FireConnectionEvent(listeners[i], listenerGamepad, false);
    321          listeners[i]->RemoveGamepad(aHandle);
    322        }
    323      }
    324    }
    325  }
    326 }
    327 
    328 void GamepadManager::FireConnectionEvent(EventTarget* aTarget,
    329                                         Gamepad* aGamepad, bool aConnected) {
    330  nsString name =
    331      aConnected ? u"gamepadconnected"_ns : u"gamepaddisconnected"_ns;
    332  GamepadEventInit init;
    333  init.mBubbles = false;
    334  init.mCancelable = false;
    335  init.mGamepad = aGamepad;
    336  RefPtr<GamepadEvent> event = GamepadEvent::Constructor(aTarget, name, init);
    337 
    338  event->SetTrusted(true);
    339 
    340  aTarget->DispatchEvent(*event);
    341 }
    342 
    343 void GamepadManager::SyncGamepadState(GamepadHandle aHandle,
    344                                      nsGlobalWindowInner* aWindow,
    345                                      Gamepad* aGamepad) {
    346  if (mShuttingDown || !mEnabled ||
    347      aWindow->ShouldResistFingerprinting(RFPTarget::Gamepad)) {
    348    return;
    349  }
    350 
    351  RefPtr<Gamepad> gamepad = GetGamepad(aHandle);
    352  if (!gamepad) {
    353    return;
    354  }
    355 
    356  aGamepad->SyncState(gamepad);
    357 }
    358 
    359 // static
    360 bool GamepadManager::IsServiceRunning() { return !!gGamepadManagerSingleton; }
    361 
    362 // static
    363 already_AddRefed<GamepadManager> GamepadManager::GetService() {
    364  if (sShutdown) {
    365    return nullptr;
    366  }
    367 
    368  if (!gGamepadManagerSingleton) {
    369    RefPtr<GamepadManager> manager = new GamepadManager();
    370    nsresult rv = manager->Init();
    371    if (NS_WARN_IF(NS_FAILED(rv))) {
    372      return nullptr;
    373    }
    374    gGamepadManagerSingleton = manager;
    375    ClearOnShutdown(&gGamepadManagerSingleton);
    376  }
    377 
    378  RefPtr<GamepadManager> service(gGamepadManagerSingleton);
    379  return service.forget();
    380 }
    381 
    382 bool GamepadManager::AxisMoveIsFirstIntent(nsGlobalWindowInner* aWindow,
    383                                           GamepadHandle aHandle,
    384                                           const GamepadChangeEvent& aEvent) {
    385  const GamepadChangeEventBody& body = aEvent.body();
    386  if (!WindowHasSeenGamepad(aWindow, aHandle) &&
    387      body.type() == GamepadChangeEventBody::TGamepadAxisInformation) {
    388    // Some controllers would send small axis values even they are just idle.
    389    // To avoid controllers be activated without its first intent.
    390    const GamepadAxisInformation& a = body.get_GamepadAxisInformation();
    391    if (abs(a.value()) < AXIS_FIRST_INTENT_THRESHOLD_VALUE) {
    392      return false;
    393    }
    394  }
    395  return true;
    396 }
    397 
    398 bool GamepadManager::MaybeWindowHasSeenGamepad(nsGlobalWindowInner* aWindow,
    399                                               GamepadHandle aHandle) {
    400  if (!WindowHasSeenGamepad(aWindow, aHandle)) {
    401    // This window hasn't seen this gamepad before, so
    402    // send a connection event first.
    403    SetWindowHasSeenGamepad(aWindow, aHandle);
    404    return false;
    405  }
    406  return true;
    407 }
    408 
    409 bool GamepadManager::WindowHasSeenGamepad(nsGlobalWindowInner* aWindow,
    410                                          GamepadHandle aHandle) const {
    411  RefPtr<Gamepad> gamepad = aWindow->GetGamepad(aHandle);
    412  return gamepad != nullptr;
    413 }
    414 
    415 void GamepadManager::SetWindowHasSeenGamepad(nsGlobalWindowInner* aWindow,
    416                                             GamepadHandle aHandle,
    417                                             bool aHasSeen) {
    418  MOZ_ASSERT(aWindow);
    419 
    420  if (mListeners.IndexOf(aWindow) == NoIndex) {
    421    // This window isn't even listening for gamepad events.
    422    return;
    423  }
    424 
    425  if (aHasSeen) {
    426    aWindow->SetHasSeenGamepadInput(true);
    427    nsCOMPtr<nsISupports> window = ToSupports(aWindow);
    428    RefPtr<Gamepad> gamepad = GetGamepad(aHandle);
    429    if (!gamepad) {
    430      return;
    431    }
    432    RefPtr<Gamepad> clonedGamepad = gamepad->Clone(window);
    433    aWindow->AddGamepad(aHandle, clonedGamepad);
    434  } else {
    435    aWindow->RemoveGamepad(aHandle);
    436  }
    437 }
    438 
    439 void GamepadManager::Update(const GamepadChangeEvent& aEvent) {
    440  if (!mEnabled || mShuttingDown) {
    441    return;
    442  }
    443 
    444  const GamepadHandle handle = aEvent.handle();
    445 
    446  GamepadChangeEventBody body = aEvent.body();
    447 
    448  if (body.type() == GamepadChangeEventBody::TGamepadAdded) {
    449    const GamepadAdded& a = body.get_GamepadAdded();
    450    AddGamepad(handle, a.id(), static_cast<GamepadMappingType>(a.mapping()),
    451               static_cast<GamepadHand>(a.hand()), a.num_buttons(),
    452               a.num_axes(), a.num_haptics(), a.num_lights(), a.num_touches());
    453    return;
    454  }
    455  if (body.type() == GamepadChangeEventBody::TGamepadRemoved) {
    456    RemoveGamepad(handle);
    457    return;
    458  }
    459 
    460  if (!SetGamepadByEvent(aEvent)) {
    461    return;
    462  }
    463 
    464  // Hold on to listeners in a separate array because firing events
    465  // can mutate the mListeners array.
    466  nsTArray<RefPtr<nsGlobalWindowInner>> listeners(mListeners.Clone());
    467 
    468  for (uint32_t i = 0; i < listeners.Length(); i++) {
    469    // Only send events to non-background windows
    470    if (!listeners[i]->IsCurrentInnerWindow() ||
    471        listeners[i]->GetOuterWindow()->IsBackground() ||
    472        listeners[i]->ShouldResistFingerprinting(RFPTarget::Gamepad)) {
    473      continue;
    474    }
    475 
    476    SetGamepadByEvent(aEvent, listeners[i]);
    477    MaybeConvertToNonstandardGamepadEvent(aEvent, listeners[i]);
    478  }
    479 }
    480 
    481 void GamepadManager::MaybeConvertToNonstandardGamepadEvent(
    482    const GamepadChangeEvent& aEvent, nsGlobalWindowInner* aWindow) {
    483  MOZ_ASSERT(aWindow);
    484 
    485  if (!mNonstandardEventsEnabled) {
    486    return;
    487  }
    488 
    489  GamepadHandle handle = aEvent.handle();
    490 
    491  RefPtr<Gamepad> gamepad = aWindow->GetGamepad(handle);
    492  const GamepadChangeEventBody& body = aEvent.body();
    493 
    494  if (gamepad) {
    495    switch (body.type()) {
    496      case GamepadChangeEventBody::TGamepadButtonInformation: {
    497        const GamepadButtonInformation& a = body.get_GamepadButtonInformation();
    498        FireButtonEvent(aWindow, gamepad, a.button(), a.value());
    499        break;
    500      }
    501      case GamepadChangeEventBody::TGamepadAxisInformation: {
    502        const GamepadAxisInformation& a = body.get_GamepadAxisInformation();
    503        FireAxisMoveEvent(aWindow, gamepad, a.axis(), a.value());
    504        break;
    505      }
    506      default:
    507        break;
    508    }
    509  }
    510 }
    511 
    512 bool GamepadManager::SetGamepadByEvent(const GamepadChangeEvent& aEvent,
    513                                       nsGlobalWindowInner* aWindow) {
    514  bool ret = false;
    515  bool firstTime = false;
    516 
    517  GamepadHandle handle = aEvent.handle();
    518 
    519  if (aWindow) {
    520    if (!AxisMoveIsFirstIntent(aWindow, handle, aEvent)) {
    521      return false;
    522    }
    523    firstTime = !MaybeWindowHasSeenGamepad(aWindow, handle);
    524  }
    525 
    526  RefPtr<Gamepad> gamepad =
    527      aWindow ? aWindow->GetGamepad(handle) : GetGamepad(handle);
    528  const GamepadChangeEventBody& body = aEvent.body();
    529 
    530  if (gamepad) {
    531    switch (body.type()) {
    532      case GamepadChangeEventBody::TGamepadButtonInformation: {
    533        const GamepadButtonInformation& a = body.get_GamepadButtonInformation();
    534        gamepad->SetButton(a.button(), a.pressed(), a.touched(), a.value());
    535        break;
    536      }
    537      case GamepadChangeEventBody::TGamepadAxisInformation: {
    538        const GamepadAxisInformation& a = body.get_GamepadAxisInformation();
    539        gamepad->SetAxis(a.axis(), a.value());
    540        break;
    541      }
    542      case GamepadChangeEventBody::TGamepadPoseInformation: {
    543        const GamepadPoseInformation& a = body.get_GamepadPoseInformation();
    544        gamepad->SetPose(a.pose_state());
    545        break;
    546      }
    547      case GamepadChangeEventBody::TGamepadLightIndicatorTypeInformation: {
    548        const GamepadLightIndicatorTypeInformation& a =
    549            body.get_GamepadLightIndicatorTypeInformation();
    550        gamepad->SetLightIndicatorType(a.light(), a.type());
    551        break;
    552      }
    553      case GamepadChangeEventBody::TGamepadTouchInformation: {
    554        // Avoid GamepadTouch's touchId be accessed in cross-origin tracking.
    555        for (uint32_t i = 0; i < mListeners.Length(); i++) {
    556          RefPtr<Gamepad> listenerGamepad = mListeners[i]->GetGamepad(handle);
    557          if (listenerGamepad && mListeners[i]->IsCurrentInnerWindow() &&
    558              !mListeners[i]->GetOuterWindow()->IsBackground()) {
    559            const GamepadTouchInformation& a =
    560                body.get_GamepadTouchInformation();
    561            listenerGamepad->SetTouchEvent(a.index(), a.touch_state());
    562          }
    563        }
    564        break;
    565      }
    566      case GamepadChangeEventBody::TGamepadHandInformation: {
    567        const GamepadHandInformation& a = body.get_GamepadHandInformation();
    568        gamepad->SetHand(a.hand());
    569        break;
    570      }
    571      default:
    572        MOZ_ASSERT(false);
    573        break;
    574    }
    575    ret = true;
    576  }
    577 
    578  if (aWindow && firstTime) {
    579    FireConnectionEvent(aWindow, gamepad, true);
    580  }
    581 
    582  return ret;
    583 }
    584 
    585 already_AddRefed<Promise> GamepadManager::VibrateHaptic(
    586    GamepadHandle aHandle, uint32_t aHapticIndex, double aIntensity,
    587    double aDuration, nsIGlobalObject* aGlobal, ErrorResult& aRv) {
    588  RefPtr<Promise> promise = Promise::Create(aGlobal, aRv);
    589  if (NS_WARN_IF(aRv.Failed())) {
    590    aRv.Throw(NS_ERROR_FAILURE);
    591    return nullptr;
    592  }
    593  if (StaticPrefs::dom_gamepad_haptic_feedback_enabled()) {
    594    if (aHandle.GetKind() == GamepadHandleKind::VR) {
    595      if (gfx::VRManagerChild::IsCreated()) {
    596        gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
    597        vm->AddPromise(mPromiseID, promise);
    598        vm->SendVibrateHaptic(aHandle, aHapticIndex, aIntensity, aDuration,
    599                              mPromiseID);
    600      }
    601    } else {
    602      if (mChannelChild) {
    603        mChannelChild->AddPromise(mPromiseID, promise);
    604        mChannelChild->SendVibrateHaptic(aHandle, aHapticIndex, aIntensity,
    605                                         aDuration, mPromiseID);
    606      }
    607    }
    608  }
    609 
    610  ++mPromiseID;
    611  return promise.forget();
    612 }
    613 
    614 void GamepadManager::StopHaptics() {
    615  if (!StaticPrefs::dom_gamepad_haptic_feedback_enabled()) {
    616    return;
    617  }
    618 
    619  for (const auto& entry : mGamepads) {
    620    const GamepadHandle handle = entry.GetWeak()->GetHandle();
    621    if (handle.GetKind() == GamepadHandleKind::VR) {
    622      if (gfx::VRManagerChild::IsCreated()) {
    623        gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
    624        vm->SendStopVibrateHaptic(handle);
    625      }
    626    } else {
    627      if (mChannelChild) {
    628        mChannelChild->SendStopVibrateHaptic(handle);
    629      }
    630    }
    631  }
    632 }
    633 
    634 already_AddRefed<Promise> GamepadManager::SetLightIndicatorColor(
    635    GamepadHandle aHandle, uint32_t aLightColorIndex, uint8_t aRed,
    636    uint8_t aGreen, uint8_t aBlue, nsIGlobalObject* aGlobal, ErrorResult& aRv) {
    637  RefPtr<Promise> promise = Promise::Create(aGlobal, aRv);
    638  if (NS_WARN_IF(aRv.Failed())) {
    639    aRv.Throw(NS_ERROR_FAILURE);
    640    return nullptr;
    641  }
    642  if (StaticPrefs::dom_gamepad_extensions_lightindicator()) {
    643    MOZ_RELEASE_ASSERT(aHandle.GetKind() != GamepadHandleKind::VR,
    644                       "We don't support light indicator in VR.");
    645 
    646    if (mChannelChild) {
    647      mChannelChild->AddPromise(mPromiseID, promise);
    648      mChannelChild->SendLightIndicatorColor(aHandle, aLightColorIndex, aRed,
    649                                             aGreen, aBlue, mPromiseID);
    650    }
    651  }
    652 
    653  ++mPromiseID;
    654  return promise.forget();
    655 }
    656 
    657 already_AddRefed<Promise> GamepadManager::RequestAllGamepads(
    658    nsIGlobalObject* aGlobal, ErrorResult& aRv) {
    659  RefPtr<Promise> promise = Promise::Create(aGlobal, aRv);
    660  if (NS_WARN_IF(aRv.Failed())) {
    661    return nullptr;
    662  }
    663 
    664  if (!mChannelChild) {
    665    aRv.Throw(NS_ERROR_FAILURE);
    666    return nullptr;
    667  }
    668 
    669  mChannelChild->SendRequestAllGamepads(
    670      [promise](const nsTArray<GamepadAdded>& aAddedGamepads) {
    671        nsTArray<RefPtr<Gamepad>> gamepads;
    672 
    673        for (const auto& addedGamepad : aAddedGamepads) {
    674          RefPtr<Gamepad> gamepad =
    675              new Gamepad(nullptr, addedGamepad.id(), 0, GamepadHandle(),
    676                          addedGamepad.mapping(), addedGamepad.hand(),
    677                          addedGamepad.num_buttons(), addedGamepad.num_axes(),
    678                          addedGamepad.num_haptics(), addedGamepad.num_lights(),
    679                          addedGamepad.num_touches());
    680          gamepads.AppendElement(gamepad);
    681        }
    682        promise->MaybeResolve(gamepads);
    683      },
    684      [promise](mozilla::ipc::ResponseRejectReason) {
    685        promise->MaybeReject(NS_ERROR_UNEXPECTED);
    686      });
    687 
    688  return promise.forget();
    689 }
    690 }  // namespace mozilla::dom