GamepadServiceTest.cpp (12465B)
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 "GamepadServiceTest.h" 8 9 #include "mozilla/ErrorResult.h" 10 #include "mozilla/dom/GamepadManager.h" 11 #include "mozilla/dom/GamepadPlatformService.h" 12 #include "mozilla/dom/GamepadServiceTestBinding.h" 13 #include "mozilla/dom/GamepadTestChannelChild.h" 14 #include "mozilla/ipc/BackgroundChild.h" 15 #include "mozilla/ipc/PBackgroundChild.h" 16 17 namespace mozilla::dom { 18 19 /* 20 * Implementation of the test service. This is just to provide a simple binding 21 * of the GamepadService to JavaScript via WebIDL so that we can write 22 * Mochitests that add and remove fake gamepads, avoiding the platform-specific 23 * backends. 24 */ 25 26 constexpr uint32_t kMaxButtons = 20; 27 constexpr uint32_t kMaxAxes = 10; 28 constexpr uint32_t kMaxHaptics = 2; 29 constexpr uint32_t kMaxLightIndicator = 2; 30 constexpr uint32_t kMaxTouchEvents = 4; 31 32 NS_IMPL_CYCLE_COLLECTION_INHERITED(GamepadServiceTest, DOMEventTargetHelper, 33 mWindow) 34 35 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GamepadServiceTest) 36 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) 37 38 NS_IMPL_ADDREF_INHERITED(GamepadServiceTest, DOMEventTargetHelper) 39 NS_IMPL_RELEASE_INHERITED(GamepadServiceTest, DOMEventTargetHelper) 40 41 // static 42 already_AddRefed<GamepadServiceTest> GamepadServiceTest::CreateTestService( 43 nsPIDOMWindowInner* aWindow) { 44 MOZ_ASSERT(aWindow); 45 RefPtr<GamepadServiceTest> service = new GamepadServiceTest(aWindow); 46 service->InitPBackgroundActor(); 47 return service.forget(); 48 } 49 50 void GamepadServiceTest::Shutdown() { 51 MOZ_ASSERT(!mShuttingDown); 52 mShuttingDown = true; 53 DestroyPBackgroundActor(); 54 mWindow = nullptr; 55 } 56 57 GamepadServiceTest::GamepadServiceTest(nsPIDOMWindowInner* aWindow) 58 : mService(GamepadManager::GetService()), 59 mWindow(aWindow), 60 mEventNumber(0), 61 mShuttingDown(false), 62 mChild(nullptr) {} 63 64 GamepadServiceTest::~GamepadServiceTest() { 65 MOZ_ASSERT(mPromiseList.IsEmpty()); 66 } 67 68 void GamepadServiceTest::InitPBackgroundActor() { 69 MOZ_ASSERT(!mChild); 70 71 ::mozilla::ipc::PBackgroundChild* actor = 72 ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(); 73 if (NS_WARN_IF(!actor)) { 74 MOZ_CRASH("Failed to create a PBackgroundChild actor!"); 75 } 76 77 mChild = GamepadTestChannelChild::Create(this); 78 PGamepadTestChannelChild* initedChild = 79 actor->SendPGamepadTestChannelConstructor(mChild.get()); 80 if (NS_WARN_IF(!initedChild)) { 81 MOZ_CRASH("Failed to create a PBackgroundChild actor!"); 82 } 83 } 84 85 void GamepadServiceTest::ReplyGamepadHandle(uint32_t aPromiseId, 86 const GamepadHandle& aHandle) { 87 uint32_t handleSlot = AddGamepadHandle(aHandle); 88 89 RefPtr<Promise> p; 90 if (!mPromiseList.Get(aPromiseId, getter_AddRefs(p))) { 91 MOZ_CRASH("We should always have a promise."); 92 } 93 94 p->MaybeResolve(handleSlot); 95 mPromiseList.Remove(aPromiseId); 96 } 97 98 void GamepadServiceTest::DestroyPBackgroundActor() { 99 MOZ_ASSERT(mChild); 100 PGamepadTestChannelChild::Send__delete__(mChild); 101 mChild = nullptr; 102 } 103 104 already_AddRefed<Promise> GamepadServiceTest::AddGamepad( 105 const nsAString& aID, GamepadMappingType aMapping, GamepadHand aHand, 106 uint32_t aNumButtons, uint32_t aNumAxes, uint32_t aNumHaptics, 107 uint32_t aNumLightIndicator, uint32_t aNumTouchEvents, ErrorResult& aRv) { 108 if (aNumButtons > kMaxButtons || aNumAxes > kMaxAxes || 109 aNumHaptics > kMaxHaptics || aNumLightIndicator > kMaxLightIndicator || 110 aNumTouchEvents > kMaxTouchEvents) { 111 aRv.ThrowNotSupportedError("exceeded maximum hardware dimensions"); 112 return nullptr; 113 } 114 115 if (mShuttingDown) { 116 aRv.ThrowInvalidStateError("Shutting down"); 117 return nullptr; 118 } 119 120 // The values here are ignored, the value just can't be zero to avoid an 121 // assertion 122 GamepadHandle gamepadHandle{1, GamepadHandleKind::GamepadPlatformManager}; 123 124 GamepadAdded a(nsString(aID), aMapping, aHand, aNumButtons, aNumAxes, 125 aNumHaptics, aNumLightIndicator, aNumTouchEvents); 126 GamepadChangeEventBody body(a); 127 GamepadChangeEvent e(gamepadHandle, body); 128 129 RefPtr<Promise> p = Promise::Create(mWindow->AsGlobal(), aRv); 130 if (aRv.Failed()) { 131 return nullptr; 132 } 133 134 uint32_t id = ++mEventNumber; 135 136 MOZ_ASSERT(!mPromiseList.Contains(id)); 137 mPromiseList.InsertOrUpdate(id, RefPtr{p}); 138 139 mChild->SendGamepadTestEvent(id, e); 140 141 return p.forget(); 142 } 143 144 already_AddRefed<Promise> GamepadServiceTest::RemoveGamepad( 145 uint32_t aHandleSlot, ErrorResult& aRv) { 146 if (mShuttingDown) { 147 aRv.ThrowInvalidStateError("Shutting down"); 148 return nullptr; 149 } 150 151 GamepadHandle gamepadHandle = GetHandleInSlot(aHandleSlot); 152 153 GamepadRemoved a; 154 GamepadChangeEventBody body(a); 155 GamepadChangeEvent e(gamepadHandle, body); 156 157 uint32_t id = ++mEventNumber; 158 159 RefPtr<Promise> p = Promise::Create(mWindow->AsGlobal(), aRv); 160 if (aRv.Failed()) { 161 return nullptr; 162 } 163 164 MOZ_ASSERT(!mPromiseList.Contains(id)); 165 mPromiseList.InsertOrUpdate(id, RefPtr{p}); 166 167 mChild->SendGamepadTestEvent(id, e); 168 return p.forget(); 169 } 170 171 already_AddRefed<Promise> GamepadServiceTest::NewButtonEvent( 172 uint32_t aHandleSlot, uint32_t aButton, bool aPressed, bool aTouched, 173 ErrorResult& aRv) { 174 if (mShuttingDown) { 175 aRv.ThrowInvalidStateError("Shutting down"); 176 return nullptr; 177 } 178 179 GamepadHandle gamepadHandle = GetHandleInSlot(aHandleSlot); 180 181 GamepadButtonInformation a(aButton, aPressed ? 1.0 : 0, aPressed, aTouched); 182 GamepadChangeEventBody body(a); 183 GamepadChangeEvent e(gamepadHandle, body); 184 185 uint32_t id = ++mEventNumber; 186 RefPtr<Promise> p = Promise::Create(mWindow->AsGlobal(), aRv); 187 if (aRv.Failed()) { 188 return nullptr; 189 } 190 191 MOZ_ASSERT(!mPromiseList.Contains(id)); 192 mPromiseList.InsertOrUpdate(id, RefPtr{p}); 193 mChild->SendGamepadTestEvent(id, e); 194 return p.forget(); 195 } 196 197 already_AddRefed<Promise> GamepadServiceTest::NewButtonValueEvent( 198 uint32_t aHandleSlot, uint32_t aButton, bool aPressed, bool aTouched, 199 double aValue, ErrorResult& aRv) { 200 if (mShuttingDown) { 201 aRv.ThrowInvalidStateError("Shutting down"); 202 return nullptr; 203 } 204 205 GamepadHandle gamepadHandle = GetHandleInSlot(aHandleSlot); 206 207 GamepadButtonInformation a(aButton, aValue, aPressed, aTouched); 208 GamepadChangeEventBody body(a); 209 GamepadChangeEvent e(gamepadHandle, body); 210 211 uint32_t id = ++mEventNumber; 212 RefPtr<Promise> p = Promise::Create(mWindow->AsGlobal(), aRv); 213 if (aRv.Failed()) { 214 return nullptr; 215 } 216 217 MOZ_ASSERT(!mPromiseList.Contains(id)); 218 mPromiseList.InsertOrUpdate(id, RefPtr{p}); 219 mChild->SendGamepadTestEvent(id, e); 220 return p.forget(); 221 } 222 223 already_AddRefed<Promise> GamepadServiceTest::NewAxisMoveEvent( 224 uint32_t aHandleSlot, uint32_t aAxis, double aValue, ErrorResult& aRv) { 225 if (mShuttingDown) { 226 aRv.ThrowInvalidStateError("Shutting down"); 227 return nullptr; 228 } 229 230 GamepadHandle gamepadHandle = GetHandleInSlot(aHandleSlot); 231 232 GamepadAxisInformation a(aAxis, aValue); 233 GamepadChangeEventBody body(a); 234 GamepadChangeEvent e(gamepadHandle, body); 235 236 uint32_t id = ++mEventNumber; 237 RefPtr<Promise> p = Promise::Create(mWindow->AsGlobal(), aRv); 238 if (aRv.Failed()) { 239 return nullptr; 240 } 241 242 MOZ_ASSERT(!mPromiseList.Contains(id)); 243 mPromiseList.InsertOrUpdate(id, RefPtr{p}); 244 mChild->SendGamepadTestEvent(id, e); 245 return p.forget(); 246 } 247 248 already_AddRefed<Promise> GamepadServiceTest::NewPoseMove( 249 uint32_t aHandleSlot, const Nullable<Float32Array>& aOrient, 250 const Nullable<Float32Array>& aPos, 251 const Nullable<Float32Array>& aAngVelocity, 252 const Nullable<Float32Array>& aAngAcceleration, 253 const Nullable<Float32Array>& aLinVelocity, 254 const Nullable<Float32Array>& aLinAcceleration, ErrorResult& aRv) { 255 if (mShuttingDown) { 256 aRv.ThrowInvalidStateError("Shutting down"); 257 return nullptr; 258 } 259 260 GamepadHandle gamepadHandle = GetHandleInSlot(aHandleSlot); 261 262 GamepadPoseState poseState; 263 poseState.flags = GamepadCapabilityFlags::Cap_Orientation | 264 GamepadCapabilityFlags::Cap_Position | 265 GamepadCapabilityFlags::Cap_AngularAcceleration | 266 GamepadCapabilityFlags::Cap_LinearAcceleration; 267 if (!aOrient.IsNull()) { 268 DebugOnly<bool> ok = aOrient.Value().CopyDataTo(poseState.orientation); 269 MOZ_ASSERT(ok, 270 "aOrient.Value().Length() != std::size(poseState.orientation)"); 271 poseState.isOrientationValid = true; 272 } 273 if (!aPos.IsNull()) { 274 DebugOnly<bool> ok = aPos.Value().CopyDataTo(poseState.position); 275 MOZ_ASSERT(ok, "aPos.Value().Length() != std::size(poseState.position)"); 276 poseState.isPositionValid = true; 277 } 278 if (!aAngVelocity.IsNull()) { 279 DebugOnly<bool> ok = 280 aAngVelocity.Value().CopyDataTo(poseState.angularVelocity); 281 MOZ_ASSERT(ok, 282 "aAngVelocity.Value().Length() != " 283 "std::size(poseState.angularVelocity)"); 284 } 285 if (!aAngAcceleration.IsNull()) { 286 DebugOnly<bool> ok = 287 aAngAcceleration.Value().CopyDataTo(poseState.angularAcceleration); 288 MOZ_ASSERT(ok, 289 "aAngAcceleration.Value().Length() != " 290 "std::size(poseState.angularAcceleration)"); 291 } 292 if (!aLinVelocity.IsNull()) { 293 DebugOnly<bool> ok = 294 aLinVelocity.Value().CopyDataTo(poseState.linearVelocity); 295 MOZ_ASSERT(ok, 296 "aLinVelocity.Value().Length() != " 297 "std::size(poseState.linearVelocity)"); 298 } 299 if (!aLinAcceleration.IsNull()) { 300 DebugOnly<bool> ok = 301 aLinAcceleration.Value().CopyDataTo(poseState.linearAcceleration); 302 MOZ_ASSERT(ok, 303 "aLinAcceleration.Value().Length() != " 304 "std::size(poseState.linearAcceleration)"); 305 } 306 307 GamepadPoseInformation a(poseState); 308 GamepadChangeEventBody body(a); 309 GamepadChangeEvent e(gamepadHandle, body); 310 311 uint32_t id = ++mEventNumber; 312 RefPtr<Promise> p = Promise::Create(mWindow->AsGlobal(), aRv); 313 if (aRv.Failed()) { 314 return nullptr; 315 } 316 317 MOZ_ASSERT(!mPromiseList.Contains(id)); 318 mPromiseList.InsertOrUpdate(id, RefPtr{p}); 319 mChild->SendGamepadTestEvent(id, e); 320 return p.forget(); 321 } 322 323 already_AddRefed<Promise> GamepadServiceTest::NewTouch( 324 uint32_t aHandleSlot, uint32_t aTouchArrayIndex, uint32_t aTouchId, 325 uint8_t aSurfaceId, const Float32Array& aPos, 326 const Nullable<Float32Array>& aSurfDim, ErrorResult& aRv) { 327 if (mShuttingDown) { 328 aRv.ThrowInvalidStateError("Shutting down"); 329 return nullptr; 330 } 331 332 GamepadHandle gamepadHandle = GetHandleInSlot(aHandleSlot); 333 334 GamepadTouchState touchState; 335 touchState.touchId = aTouchId; 336 touchState.surfaceId = aSurfaceId; 337 DebugOnly<bool> ok = aPos.CopyDataTo(touchState.position); 338 MOZ_ASSERT(ok, "aPos.Length() != std::size(touchState.position)"); 339 340 if (!aSurfDim.IsNull()) { 341 ok = aSurfDim.Value().CopyDataTo(touchState.surfaceDimensions); 342 MOZ_ASSERT(ok, 343 "aSurfDim.Length() != std::size(touchState.surfaceDimensions)"); 344 touchState.isSurfaceDimensionsValid = true; 345 } 346 347 GamepadTouchInformation a(aTouchArrayIndex, touchState); 348 GamepadChangeEventBody body(a); 349 GamepadChangeEvent e(gamepadHandle, body); 350 351 uint32_t id = ++mEventNumber; 352 RefPtr<Promise> p = Promise::Create(mWindow->AsGlobal(), aRv); 353 if (aRv.Failed()) { 354 return nullptr; 355 } 356 357 MOZ_ASSERT(!mPromiseList.Contains(id)); 358 mPromiseList.InsertOrUpdate(id, RefPtr{p}); 359 mChild->SendGamepadTestEvent(id, e); 360 return p.forget(); 361 } 362 363 JSObject* GamepadServiceTest::WrapObject(JSContext* aCx, 364 JS::Handle<JSObject*> aGivenProto) { 365 return GamepadServiceTest_Binding::Wrap(aCx, this, aGivenProto); 366 } 367 368 uint32_t GamepadServiceTest::AddGamepadHandle(GamepadHandle aHandle) { 369 uint32_t handleSlot = mGamepadHandles.Length(); 370 mGamepadHandles.AppendElement(aHandle); 371 return handleSlot; 372 } 373 374 void GamepadServiceTest::RemoveGamepadHandle(uint32_t aHandleSlot) { 375 MOZ_ASSERT(aHandleSlot < mGamepadHandles.Length()); 376 return mGamepadHandles.RemoveElementAt(aHandleSlot); 377 } 378 379 GamepadHandle GamepadServiceTest::GetHandleInSlot(uint32_t aHandleSlot) const { 380 MOZ_ASSERT(aHandleSlot < mGamepadHandles.Length()); 381 return mGamepadHandles.ElementAt(aHandleSlot); 382 } 383 384 } // namespace mozilla::dom