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 "GamepadServiceTest.h"
9 #include "mozilla/ErrorResult.h"
10 #include "mozilla/Unused.h"
12 #include "mozilla/dom/GamepadManager.h"
13 #include "mozilla/dom/GamepadPlatformService.h"
14 #include "mozilla/dom/GamepadServiceTestBinding.h"
15 #include "mozilla/dom/GamepadTestChannelChild.h"
17 #include "mozilla/ipc/BackgroundChild.h"
18 #include "mozilla/ipc/PBackgroundChild.h"
20 namespace mozilla::dom
{
23 * Implementation of the test service. This is just to provide a simple binding
24 * of the GamepadService to JavaScript via WebIDL so that we can write
25 * Mochitests that add and remove fake gamepads, avoiding the platform-specific
29 constexpr uint32_t kMaxButtons
= 20;
30 constexpr uint32_t kMaxAxes
= 10;
31 constexpr uint32_t kMaxHaptics
= 2;
32 constexpr uint32_t kMaxLightIndicator
= 2;
33 constexpr uint32_t kMaxTouchEvents
= 4;
35 NS_IMPL_CYCLE_COLLECTION_INHERITED(GamepadServiceTest
, DOMEventTargetHelper
,
38 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GamepadServiceTest
)
39 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
41 NS_IMPL_ADDREF_INHERITED(GamepadServiceTest
, DOMEventTargetHelper
)
42 NS_IMPL_RELEASE_INHERITED(GamepadServiceTest
, DOMEventTargetHelper
)
45 already_AddRefed
<GamepadServiceTest
> GamepadServiceTest::CreateTestService(
46 nsPIDOMWindowInner
* aWindow
) {
48 RefPtr
<GamepadServiceTest
> service
= new GamepadServiceTest(aWindow
);
49 service
->InitPBackgroundActor();
50 return service
.forget();
53 void GamepadServiceTest::Shutdown() {
54 MOZ_ASSERT(!mShuttingDown
);
56 DestroyPBackgroundActor();
60 GamepadServiceTest::GamepadServiceTest(nsPIDOMWindowInner
* aWindow
)
61 : mService(GamepadManager::GetService()),
67 GamepadServiceTest::~GamepadServiceTest() {
68 MOZ_ASSERT(mPromiseList
.IsEmpty());
71 void GamepadServiceTest::InitPBackgroundActor() {
74 ::mozilla::ipc::PBackgroundChild
* actor
=
75 ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
76 if (NS_WARN_IF(!actor
)) {
77 MOZ_CRASH("Failed to create a PBackgroundChild actor!");
80 mChild
= GamepadTestChannelChild::Create(this);
81 PGamepadTestChannelChild
* initedChild
=
82 actor
->SendPGamepadTestChannelConstructor(mChild
.get());
83 if (NS_WARN_IF(!initedChild
)) {
84 MOZ_CRASH("Failed to create a PBackgroundChild actor!");
88 void GamepadServiceTest::ReplyGamepadHandle(uint32_t aPromiseId
,
89 const GamepadHandle
& aHandle
) {
90 uint32_t handleSlot
= AddGamepadHandle(aHandle
);
93 if (!mPromiseList
.Get(aPromiseId
, getter_AddRefs(p
))) {
94 MOZ_CRASH("We should always have a promise.");
97 p
->MaybeResolve(handleSlot
);
98 mPromiseList
.Remove(aPromiseId
);
101 void GamepadServiceTest::DestroyPBackgroundActor() {
103 PGamepadTestChannelChild::Send__delete__(mChild
);
107 already_AddRefed
<Promise
> GamepadServiceTest::AddGamepad(
108 const nsAString
& aID
, GamepadMappingType aMapping
, GamepadHand aHand
,
109 uint32_t aNumButtons
, uint32_t aNumAxes
, uint32_t aNumHaptics
,
110 uint32_t aNumLightIndicator
, uint32_t aNumTouchEvents
, ErrorResult
& aRv
) {
111 if (aNumButtons
> kMaxButtons
|| aNumAxes
> kMaxAxes
||
112 aNumHaptics
> kMaxHaptics
|| aNumLightIndicator
> kMaxLightIndicator
||
113 aNumTouchEvents
> kMaxTouchEvents
) {
114 aRv
.ThrowNotSupportedError("exceeded maximum hardware dimensions");
119 aRv
.ThrowInvalidStateError("Shutting down");
123 // The values here are ignored, the value just can't be zero to avoid an
125 GamepadHandle gamepadHandle
{1, GamepadHandleKind::GamepadPlatformManager
};
127 // Only VR controllers has displayID, we give 0 to the general gamepads.
128 GamepadAdded
a(nsString(aID
), aMapping
, aHand
, 0, aNumButtons
, aNumAxes
,
129 aNumHaptics
, aNumLightIndicator
, aNumTouchEvents
);
130 GamepadChangeEventBody
body(a
);
131 GamepadChangeEvent
e(gamepadHandle
, body
);
133 RefPtr
<Promise
> p
= Promise::Create(mWindow
->AsGlobal(), aRv
);
138 uint32_t id
= ++mEventNumber
;
140 MOZ_ASSERT(!mPromiseList
.Contains(id
));
141 mPromiseList
.InsertOrUpdate(id
, RefPtr
{p
});
143 mChild
->SendGamepadTestEvent(id
, e
);
148 already_AddRefed
<Promise
> GamepadServiceTest::RemoveGamepad(
149 uint32_t aHandleSlot
, ErrorResult
& aRv
) {
151 aRv
.ThrowInvalidStateError("Shutting down");
155 GamepadHandle gamepadHandle
= GetHandleInSlot(aHandleSlot
);
158 GamepadChangeEventBody
body(a
);
159 GamepadChangeEvent
e(gamepadHandle
, body
);
161 uint32_t id
= ++mEventNumber
;
163 RefPtr
<Promise
> p
= Promise::Create(mWindow
->AsGlobal(), aRv
);
168 MOZ_ASSERT(!mPromiseList
.Contains(id
));
169 mPromiseList
.InsertOrUpdate(id
, RefPtr
{p
});
171 mChild
->SendGamepadTestEvent(id
, e
);
175 already_AddRefed
<Promise
> GamepadServiceTest::NewButtonEvent(
176 uint32_t aHandleSlot
, uint32_t aButton
, bool aPressed
, bool aTouched
,
179 aRv
.ThrowInvalidStateError("Shutting down");
183 GamepadHandle gamepadHandle
= GetHandleInSlot(aHandleSlot
);
185 GamepadButtonInformation
a(aButton
, aPressed
? 1.0 : 0, aPressed
, aTouched
);
186 GamepadChangeEventBody
body(a
);
187 GamepadChangeEvent
e(gamepadHandle
, body
);
189 uint32_t id
= ++mEventNumber
;
190 RefPtr
<Promise
> p
= Promise::Create(mWindow
->AsGlobal(), aRv
);
195 MOZ_ASSERT(!mPromiseList
.Contains(id
));
196 mPromiseList
.InsertOrUpdate(id
, RefPtr
{p
});
197 mChild
->SendGamepadTestEvent(id
, e
);
201 already_AddRefed
<Promise
> GamepadServiceTest::NewButtonValueEvent(
202 uint32_t aHandleSlot
, uint32_t aButton
, bool aPressed
, bool aTouched
,
203 double aValue
, ErrorResult
& aRv
) {
205 aRv
.ThrowInvalidStateError("Shutting down");
209 GamepadHandle gamepadHandle
= GetHandleInSlot(aHandleSlot
);
211 GamepadButtonInformation
a(aButton
, aValue
, aPressed
, aTouched
);
212 GamepadChangeEventBody
body(a
);
213 GamepadChangeEvent
e(gamepadHandle
, body
);
215 uint32_t id
= ++mEventNumber
;
216 RefPtr
<Promise
> p
= Promise::Create(mWindow
->AsGlobal(), aRv
);
221 MOZ_ASSERT(!mPromiseList
.Contains(id
));
222 mPromiseList
.InsertOrUpdate(id
, RefPtr
{p
});
223 mChild
->SendGamepadTestEvent(id
, e
);
227 already_AddRefed
<Promise
> GamepadServiceTest::NewAxisMoveEvent(
228 uint32_t aHandleSlot
, uint32_t aAxis
, double aValue
, ErrorResult
& aRv
) {
230 aRv
.ThrowInvalidStateError("Shutting down");
234 GamepadHandle gamepadHandle
= GetHandleInSlot(aHandleSlot
);
236 GamepadAxisInformation
a(aAxis
, aValue
);
237 GamepadChangeEventBody
body(a
);
238 GamepadChangeEvent
e(gamepadHandle
, body
);
240 uint32_t id
= ++mEventNumber
;
241 RefPtr
<Promise
> p
= Promise::Create(mWindow
->AsGlobal(), aRv
);
246 MOZ_ASSERT(!mPromiseList
.Contains(id
));
247 mPromiseList
.InsertOrUpdate(id
, RefPtr
{p
});
248 mChild
->SendGamepadTestEvent(id
, e
);
252 already_AddRefed
<Promise
> GamepadServiceTest::NewPoseMove(
253 uint32_t aHandleSlot
, const Nullable
<Float32Array
>& aOrient
,
254 const Nullable
<Float32Array
>& aPos
,
255 const Nullable
<Float32Array
>& aAngVelocity
,
256 const Nullable
<Float32Array
>& aAngAcceleration
,
257 const Nullable
<Float32Array
>& aLinVelocity
,
258 const Nullable
<Float32Array
>& aLinAcceleration
, ErrorResult
& aRv
) {
260 aRv
.ThrowInvalidStateError("Shutting down");
264 GamepadHandle gamepadHandle
= GetHandleInSlot(aHandleSlot
);
266 GamepadPoseState poseState
;
267 poseState
.flags
= GamepadCapabilityFlags::Cap_Orientation
|
268 GamepadCapabilityFlags::Cap_Position
|
269 GamepadCapabilityFlags::Cap_AngularAcceleration
|
270 GamepadCapabilityFlags::Cap_LinearAcceleration
;
271 if (!aOrient
.IsNull()) {
272 DebugOnly
<bool> ok
= aOrient
.Value().CopyDataTo(poseState
.orientation
);
274 "aOrient.Value().Length() != std::size(poseState.orientation)");
275 poseState
.isOrientationValid
= true;
277 if (!aPos
.IsNull()) {
278 DebugOnly
<bool> ok
= aPos
.Value().CopyDataTo(poseState
.position
);
279 MOZ_ASSERT(ok
, "aPos.Value().Length() != std::size(poseState.position)");
280 poseState
.isPositionValid
= true;
282 if (!aAngVelocity
.IsNull()) {
284 aAngVelocity
.Value().CopyDataTo(poseState
.angularVelocity
);
286 "aAngVelocity.Value().Length() != "
287 "std::size(poseState.angularVelocity)");
289 if (!aAngAcceleration
.IsNull()) {
291 aAngAcceleration
.Value().CopyDataTo(poseState
.angularAcceleration
);
293 "aAngAcceleration.Value().Length() != "
294 "std::size(poseState.angularAcceleration)");
296 if (!aLinVelocity
.IsNull()) {
298 aLinVelocity
.Value().CopyDataTo(poseState
.linearVelocity
);
300 "aLinVelocity.Value().Length() != "
301 "std::size(poseState.linearVelocity)");
303 if (!aLinAcceleration
.IsNull()) {
305 aLinAcceleration
.Value().CopyDataTo(poseState
.linearAcceleration
);
307 "aLinAcceleration.Value().Length() != "
308 "std::size(poseState.linearAcceleration)");
311 GamepadPoseInformation
a(poseState
);
312 GamepadChangeEventBody
body(a
);
313 GamepadChangeEvent
e(gamepadHandle
, body
);
315 uint32_t id
= ++mEventNumber
;
316 RefPtr
<Promise
> p
= Promise::Create(mWindow
->AsGlobal(), aRv
);
321 MOZ_ASSERT(!mPromiseList
.Contains(id
));
322 mPromiseList
.InsertOrUpdate(id
, RefPtr
{p
});
323 mChild
->SendGamepadTestEvent(id
, e
);
327 already_AddRefed
<Promise
> GamepadServiceTest::NewTouch(
328 uint32_t aHandleSlot
, uint32_t aTouchArrayIndex
, uint32_t aTouchId
,
329 uint8_t aSurfaceId
, const Float32Array
& aPos
,
330 const Nullable
<Float32Array
>& aSurfDim
, ErrorResult
& aRv
) {
332 aRv
.ThrowInvalidStateError("Shutting down");
336 GamepadHandle gamepadHandle
= GetHandleInSlot(aHandleSlot
);
338 GamepadTouchState touchState
;
339 touchState
.touchId
= aTouchId
;
340 touchState
.surfaceId
= aSurfaceId
;
341 DebugOnly
<bool> ok
= aPos
.CopyDataTo(touchState
.position
);
342 MOZ_ASSERT(ok
, "aPos.Length() != std::size(touchState.position)");
344 if (!aSurfDim
.IsNull()) {
345 ok
= aSurfDim
.Value().CopyDataTo(touchState
.surfaceDimensions
);
347 "aSurfDim.Length() != std::size(touchState.surfaceDimensions)");
348 touchState
.isSurfaceDimensionsValid
= true;
351 GamepadTouchInformation
a(aTouchArrayIndex
, touchState
);
352 GamepadChangeEventBody
body(a
);
353 GamepadChangeEvent
e(gamepadHandle
, body
);
355 uint32_t id
= ++mEventNumber
;
356 RefPtr
<Promise
> p
= Promise::Create(mWindow
->AsGlobal(), aRv
);
361 MOZ_ASSERT(!mPromiseList
.Contains(id
));
362 mPromiseList
.InsertOrUpdate(id
, RefPtr
{p
});
363 mChild
->SendGamepadTestEvent(id
, e
);
367 JSObject
* GamepadServiceTest::WrapObject(JSContext
* aCx
,
368 JS::Handle
<JSObject
*> aGivenProto
) {
369 return GamepadServiceTest_Binding::Wrap(aCx
, this, aGivenProto
);
372 uint32_t GamepadServiceTest::AddGamepadHandle(GamepadHandle aHandle
) {
373 uint32_t handleSlot
= mGamepadHandles
.Length();
374 mGamepadHandles
.AppendElement(aHandle
);
378 void GamepadServiceTest::RemoveGamepadHandle(uint32_t aHandleSlot
) {
379 MOZ_ASSERT(aHandleSlot
< mGamepadHandles
.Length());
380 return mGamepadHandles
.RemoveElementAt(aHandleSlot
);
383 GamepadHandle
GamepadServiceTest::GetHandleInSlot(uint32_t aHandleSlot
) const {
384 MOZ_ASSERT(aHandleSlot
< mGamepadHandles
.Length());
385 return mGamepadHandles
.ElementAt(aHandleSlot
);
388 } // namespace mozilla::dom