Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/gamepad/GamepadPlatformService.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/GamepadPlatformService.h"
8
9
#include "mozilla/dom/GamepadEventChannelParent.h"
10
#include "mozilla/ipc/BackgroundParent.h"
11
#include "mozilla/Mutex.h"
12
#include "mozilla/Unused.h"
13
14
#include "nsCOMPtr.h"
15
#include "nsHashKeys.h"
16
#include "nsIThread.h"
17
18
using namespace mozilla::ipc;
19
20
namespace mozilla {
21
namespace dom {
22
23
namespace {
24
25
// This is the singleton instance of GamepadPlatformService, can be called
26
// by both background and monitor thread.
27
StaticRefPtr<GamepadPlatformService> gGamepadPlatformServiceSingleton;
28
29
} //namepsace
30
31
GamepadPlatformService::GamepadPlatformService()
32
  : mGamepadIndex(0),
33
    mMutex("mozilla::dom::GamepadPlatformService")
34
0
{}
35
36
GamepadPlatformService::~GamepadPlatformService()
37
0
{
38
0
  Cleanup();
39
0
}
40
41
// static
42
already_AddRefed<GamepadPlatformService>
43
GamepadPlatformService::GetParentService()
44
0
{
45
0
  //GamepadPlatformService can only be accessed in parent process
46
0
  MOZ_ASSERT(XRE_IsParentProcess());
47
0
  if (!gGamepadPlatformServiceSingleton) {
48
0
    // Only Background Thread can create new GamepadPlatformService instance.
49
0
    if (IsOnBackgroundThread()) {
50
0
      gGamepadPlatformServiceSingleton = new GamepadPlatformService();
51
0
    } else {
52
0
      return nullptr;
53
0
    }
54
0
  }
55
0
  RefPtr<GamepadPlatformService> service(gGamepadPlatformServiceSingleton);
56
0
  return service.forget();
57
0
}
58
59
template<class T>
60
void
61
GamepadPlatformService::NotifyGamepadChange(uint32_t aIndex, const T& aInfo)
62
0
{
63
0
  // This method is called by monitor populated in
64
0
  // platform-dependent backends
65
0
  MOZ_ASSERT(XRE_IsParentProcess());
66
0
  MOZ_ASSERT(!NS_IsMainThread());
67
0
68
0
  GamepadChangeEventBody body(aInfo);
69
0
  GamepadChangeEvent e(aIndex, GamepadServiceType::Standard, body);
70
0
71
0
  // mChannelParents may be accessed by background thread in the
72
0
  // same time, we use mutex to prevent possible race condtion
73
0
  MutexAutoLock autoLock(mMutex);
74
0
75
0
  // Buffer all events if we have no Channel to dispatch, which
76
0
  // may happen when performing Mochitest.
77
0
  if (mChannelParents.IsEmpty()) {
78
0
    mPendingEvents.AppendElement(e);
79
0
    return;
80
0
  }
81
0
82
0
  for(uint32_t i = 0; i < mChannelParents.Length(); ++i) {
83
0
    mChannelParents[i]->DispatchUpdateEvent(e);
84
0
  }
85
0
}
Unexecuted instantiation: void mozilla::dom::GamepadPlatformService::NotifyGamepadChange<mozilla::dom::GamepadAdded>(unsigned int, mozilla::dom::GamepadAdded const&)
Unexecuted instantiation: void mozilla::dom::GamepadPlatformService::NotifyGamepadChange<mozilla::dom::GamepadRemoved>(unsigned int, mozilla::dom::GamepadRemoved const&)
Unexecuted instantiation: void mozilla::dom::GamepadPlatformService::NotifyGamepadChange<mozilla::dom::GamepadButtonInformation>(unsigned int, mozilla::dom::GamepadButtonInformation const&)
Unexecuted instantiation: void mozilla::dom::GamepadPlatformService::NotifyGamepadChange<mozilla::dom::GamepadAxisInformation>(unsigned int, mozilla::dom::GamepadAxisInformation const&)
Unexecuted instantiation: void mozilla::dom::GamepadPlatformService::NotifyGamepadChange<mozilla::dom::GamepadPoseInformation>(unsigned int, mozilla::dom::GamepadPoseInformation const&)
86
87
uint32_t
88
GamepadPlatformService::AddGamepad(const char* aID,
89
                                   GamepadMappingType aMapping,
90
                                   GamepadHand aHand,
91
                                   uint32_t aNumButtons, uint32_t aNumAxes,
92
                                   uint32_t aHaptics)
93
0
{
94
0
  // This method is called by monitor thread populated in
95
0
  // platform-dependent backends
96
0
  MOZ_ASSERT(XRE_IsParentProcess());
97
0
  MOZ_ASSERT(!NS_IsMainThread());
98
0
99
0
  uint32_t index = ++mGamepadIndex;
100
0
101
0
  // Only VR controllers has displayID, we give 0 to the general gamepads.
102
0
  GamepadAdded a(NS_ConvertUTF8toUTF16(nsDependentCString(aID)),
103
0
                 aMapping, aHand, 0, aNumButtons, aNumAxes, aHaptics);
104
0
105
0
  NotifyGamepadChange<GamepadAdded>(index, a);
106
0
  return index;
107
0
}
108
109
void
110
GamepadPlatformService::RemoveGamepad(uint32_t aIndex)
111
0
{
112
0
  // This method is called by monitor thread populated in
113
0
  // platform-dependent backends
114
0
  MOZ_ASSERT(XRE_IsParentProcess());
115
0
  MOZ_ASSERT(!NS_IsMainThread());
116
0
  GamepadRemoved a;
117
0
  NotifyGamepadChange<GamepadRemoved>(aIndex, a);
118
0
}
119
120
void
121
GamepadPlatformService::NewButtonEvent(uint32_t aIndex, uint32_t aButton,
122
                                       bool aPressed, bool aTouched,
123
                                       double aValue)
124
0
{
125
0
  // This method is called by monitor thread populated in
126
0
  // platform-dependent backends
127
0
  MOZ_ASSERT(XRE_IsParentProcess());
128
0
  MOZ_ASSERT(!NS_IsMainThread());
129
0
  GamepadButtonInformation a(aButton, aValue, aPressed, aTouched);
130
0
  NotifyGamepadChange<GamepadButtonInformation>(aIndex, a);
131
0
}
132
133
void
134
GamepadPlatformService::NewButtonEvent(uint32_t aIndex, uint32_t aButton,
135
                                       bool aPressed, bool aTouched)
136
0
{
137
0
  // This method is called by monitor thread populated in
138
0
  // platform-dependent backends
139
0
  MOZ_ASSERT(XRE_IsParentProcess());
140
0
  MOZ_ASSERT(!NS_IsMainThread());
141
0
  // When only a digital button is available the value will be synthesized.
142
0
  NewButtonEvent(aIndex, aButton, aPressed, aTouched, aPressed ? 1.0L : 0.0L);
143
0
}
144
145
void
146
GamepadPlatformService::NewButtonEvent(uint32_t aIndex, uint32_t aButton,
147
                                       bool aPressed)
148
0
{
149
0
  // This method is called by monitor thread populated in
150
0
  // platform-dependent backends
151
0
  MOZ_ASSERT(XRE_IsParentProcess());
152
0
  MOZ_ASSERT(!NS_IsMainThread());
153
0
  // When only a digital button is available the value will be synthesized.
154
0
  NewButtonEvent(aIndex, aButton, aPressed, aPressed, aPressed ? 1.0L : 0.0L);
155
0
}
156
157
void
158
GamepadPlatformService::NewAxisMoveEvent(uint32_t aIndex, uint32_t aAxis,
159
                                         double aValue)
160
0
{
161
0
  // This method is called by monitor thread populated in
162
0
  // platform-dependent backends
163
0
  MOZ_ASSERT(XRE_IsParentProcess());
164
0
  MOZ_ASSERT(!NS_IsMainThread());
165
0
  GamepadAxisInformation a(aAxis, aValue);
166
0
  NotifyGamepadChange<GamepadAxisInformation>(aIndex, a);
167
0
}
168
169
void
170
GamepadPlatformService::NewPoseEvent(uint32_t aIndex,
171
                                     const GamepadPoseState& aPose)
172
0
{
173
0
  // This method is called by monitor thread populated in
174
0
  // platform-dependent backends
175
0
  MOZ_ASSERT(XRE_IsParentProcess());
176
0
  MOZ_ASSERT(!NS_IsMainThread());
177
0
  GamepadPoseInformation a(aPose);
178
0
  NotifyGamepadChange<GamepadPoseInformation>(aIndex, a);
179
0
}
180
181
void
182
GamepadPlatformService::ResetGamepadIndexes()
183
0
{
184
0
  // This method is called by monitor thread populated in
185
0
  // platform-dependent backends
186
0
  MOZ_ASSERT(XRE_IsParentProcess());
187
0
  MOZ_ASSERT(!NS_IsMainThread());
188
0
  mGamepadIndex = 0;
189
0
}
190
191
void
192
GamepadPlatformService::AddChannelParent(GamepadEventChannelParent* aParent)
193
0
{
194
0
  // mChannelParents can only be modified once GamepadEventChannelParent
195
0
  // is created or removed in Background thread
196
0
  AssertIsOnBackgroundThread();
197
0
  MOZ_ASSERT(aParent);
198
0
  MOZ_ASSERT(!mChannelParents.Contains(aParent));
199
0
200
0
  // We use mutex here to prevent race condition with monitor thread
201
0
  MutexAutoLock autoLock(mMutex);
202
0
  mChannelParents.AppendElement(aParent);
203
0
  FlushPendingEvents();
204
0
}
205
206
void
207
GamepadPlatformService::FlushPendingEvents()
208
0
{
209
0
  AssertIsOnBackgroundThread();
210
0
  MOZ_ASSERT(!mChannelParents.IsEmpty());
211
0
212
0
  if (mPendingEvents.IsEmpty()) {
213
0
    return;
214
0
  }
215
0
216
0
  // NOTE: This method must be called with mMutex held because it accesses
217
0
  // mChannelParents.
218
0
  for (uint32_t i=0; i<mChannelParents.Length(); ++i) {
219
0
    for (uint32_t j=0; j<mPendingEvents.Length();++j) {
220
0
      mChannelParents[i]->DispatchUpdateEvent(mPendingEvents[j]);
221
0
    }
222
0
  }
223
0
  mPendingEvents.Clear();
224
0
}
225
226
void
227
GamepadPlatformService::RemoveChannelParent(GamepadEventChannelParent* aParent)
228
0
{
229
0
  // mChannelParents can only be modified once GamepadEventChannelParent
230
0
  // is created or removed in Background thread
231
0
  AssertIsOnBackgroundThread();
232
0
  MOZ_ASSERT(aParent);
233
0
  MOZ_ASSERT(mChannelParents.Contains(aParent));
234
0
235
0
  // We use mutex here to prevent race condition with monitor thread
236
0
  MutexAutoLock autoLock(mMutex);
237
0
  mChannelParents.RemoveElement(aParent);
238
0
}
239
240
bool
241
GamepadPlatformService::HasGamepadListeners()
242
0
{
243
0
  // mChannelParents may be accessed by background thread in the
244
0
  // same time, we use mutex to prevent possible race condtion
245
0
  AssertIsOnBackgroundThread();
246
0
247
0
  // We use mutex here to prevent race condition with monitor thread
248
0
  MutexAutoLock autoLock(mMutex);
249
0
  for (uint32_t i = 0; i < mChannelParents.Length(); i++) {
250
0
    if(mChannelParents[i]->HasGamepadListener()) {
251
0
      return true;
252
0
    }
253
0
  }
254
0
  return false;
255
0
}
256
257
void
258
GamepadPlatformService::MaybeShutdown()
259
0
{
260
0
  // This method is invoked in MaybeStopGamepadMonitoring when
261
0
  // an IPDL channel is going to be destroyed
262
0
  AssertIsOnBackgroundThread();
263
0
264
0
  // We have to release gGamepadPlatformServiceSingleton ouside
265
0
  // the mutex as well as making upcoming GetParentService() call
266
0
  // recreate new singleton, so we use this RefPtr to temporarily
267
0
  // hold the reference, postponing the release process until this
268
0
  // method ends.
269
0
  RefPtr<GamepadPlatformService> kungFuDeathGrip;
270
0
271
0
  bool isChannelParentEmpty;
272
0
  {
273
0
    MutexAutoLock autoLock(mMutex);
274
0
    isChannelParentEmpty = mChannelParents.IsEmpty();
275
0
    if(isChannelParentEmpty) {
276
0
      kungFuDeathGrip = gGamepadPlatformServiceSingleton;
277
0
      gGamepadPlatformServiceSingleton = nullptr;
278
0
    }
279
0
  }
280
0
}
281
282
void
283
GamepadPlatformService::Cleanup()
284
0
{
285
0
  // This method is called when GamepadPlatformService is
286
0
  // successfully distructed in background thread
287
0
  AssertIsOnBackgroundThread();
288
0
289
0
  MutexAutoLock autoLock(mMutex);
290
0
  mChannelParents.Clear();
291
0
}
292
293
} // namespace dom
294
} // namespace mozilla