Coverage Report

Created: 2018-09-25 14:53

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