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