Bug 1221730 - Change GamepadServiceTest into webidl. r=qdot, r=baku
authorChih-Yi Leu <cleu@mozilla.com>
Tue, 28 Jun 2016 00:26:00 +0200
changeset 302841 050df9c81c1e80c03e75905716ec95343d83bf0f
parent 302840 342068569153f7399dbf141c6c2b36bce71888fb
child 302842 8f8875dbdc71478aaf172c2a5c146443f171496d
push idunknown
push userunknown
push dateunknown
reviewersqdot, baku
bugs1221730
milestone50.0a1
Bug 1221730 - Change GamepadServiceTest into webidl. r=qdot, r=baku
browser/installer/package-manifest.in
dom/base/Navigator.cpp
dom/base/Navigator.h
dom/gamepad/GamepadServiceTest.cpp
dom/gamepad/GamepadServiceTest.h
dom/gamepad/ipc/GamepadEventTypes.ipdlh
dom/gamepad/ipc/GamepadTestChannelChild.cpp
dom/gamepad/ipc/GamepadTestChannelChild.h
dom/gamepad/ipc/GamepadTestChannelParent.cpp
dom/gamepad/ipc/GamepadTestChannelParent.h
dom/gamepad/ipc/PGamepadEventChannel.ipdl
dom/gamepad/ipc/PGamepadTestChannel.ipdl
dom/gamepad/moz.build
dom/interfaces/gamepad/moz.build
dom/interfaces/gamepad/nsIGamepadServiceTest.idl
dom/moz.build
dom/tests/mochitest/gamepad/gamepad_service_test_chrome_script.js
dom/tests/mochitest/gamepad/mochitest.ini
dom/tests/mochitest/gamepad/mock_gamepad.js
dom/tests/mochitest/gamepad/test_check_timestamp.html
dom/webidl/GamepadServiceTest.webidl
dom/webidl/Navigator.webidl
dom/webidl/moz.build
ipc/glue/BackgroundChildImpl.cpp
ipc/glue/BackgroundChildImpl.h
ipc/glue/BackgroundParentImpl.cpp
ipc/glue/BackgroundParentImpl.h
ipc/glue/PBackground.ipdl
layout/build/nsLayoutModule.cpp
mobile/android/installer/package-manifest.in
modules/libpref/init/all.js
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -214,19 +214,16 @@
 #ifdef MOZ_WEBSPEECH
 @RESPATH@/components/dom_webspeechrecognition.xpt
 #endif
 @RESPATH@/components/dom_workers.xpt
 @RESPATH@/components/dom_xbl.xpt
 @RESPATH@/components/dom_xhr.xpt
 @RESPATH@/components/dom_xpath.xpt
 @RESPATH@/components/dom_xul.xpt
-#ifdef MOZ_GAMEPAD
-@RESPATH@/components/dom_gamepad.xpt
-#endif
 @RESPATH@/components/dom_payment.xpt
 @RESPATH@/components/dom_presentation.xpt
 @RESPATH@/components/downloads.xpt
 @RESPATH@/components/editor.xpt
 @RESPATH@/components/embed_base.xpt
 @RESPATH@/components/extensions.xpt
 @RESPATH@/components/exthandler.xpt
 @RESPATH@/components/exthelper.xpt
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -27,16 +27,19 @@
 #include "nsIScriptSecurityManager.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsContentUtils.h"
 #include "nsUnicharUtils.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "BatteryManager.h"
 #include "mozilla/dom/DeviceStorageAreaListener.h"
+#ifdef MOZ_GAMEPAD
+#include "mozilla/dom/GamepadServiceTest.h"
+#endif
 #include "mozilla/dom/PowerManager.h"
 #include "mozilla/dom/WakeLock.h"
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "mozilla/dom/CellBroadcast.h"
 #include "mozilla/dom/FlyWebPublishedServer.h"
 #include "mozilla/dom/FlyWebService.h"
 #include "mozilla/dom/IccManager.h"
 #include "mozilla/dom/InputPortManager.h"
@@ -260,16 +263,19 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorkerContainer)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
 #ifdef MOZ_EME
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeySystemAccessManager)
 #endif
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDeviceStorageAreaListener)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPresentation)
+#ifdef MOZ_GAMEPAD
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepadServiceTest)
+#endif
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRGetDevicesPromises)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(Navigator)
 
 void
 Navigator::Invalidate()
@@ -402,16 +408,23 @@ Navigator::Invalidate()
     mMediaKeySystemAccessManager = nullptr;
   }
 #endif
 
   if (mDeviceStorageAreaListener) {
     mDeviceStorageAreaListener = nullptr;
   }
 
+#ifdef MOZ_GAMEPAD
+  if (mGamepadServiceTest) {
+    mGamepadServiceTest->Shutdown();
+    mGamepadServiceTest = nullptr;
+  }
+#endif
+
   mVRGetDevicesPromises.Clear();
 }
 
 //*****************************************************************************
 //    Navigator::nsIDOMNavigator
 //*****************************************************************************
 
 NS_IMETHODIMP
@@ -2015,16 +2028,25 @@ Navigator::GetGamepads(nsTArray<RefPtr<G
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return;
   }
   NS_ENSURE_TRUE_VOID(mWindow->GetDocShell());
   nsGlobalWindow* win = nsGlobalWindow::Cast(mWindow);
   win->SetHasGamepadEventListener(true);
   win->GetGamepads(aGamepads);
 }
+
+GamepadServiceTest*
+Navigator::RequestGamepadServiceTest()
+{
+  if (!mGamepadServiceTest) {
+    mGamepadServiceTest = GamepadServiceTest::CreateTestService(mWindow);
+  }
+  return mGamepadServiceTest;
+}
 #endif
 
 already_AddRefed<Promise>
 Navigator::GetVRDevices(ErrorResult& aRv)
 {
   if (!mWindow || !mWindow->GetDocShell()) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -66,16 +66,17 @@ class FMRadio;
 
 class Promise;
 
 class DesktopNotificationCenter;
 class MobileMessageManager;
 class MozIdleObserver;
 #ifdef MOZ_GAMEPAD
 class Gamepad;
+class GamepadServiceTest;
 #endif // MOZ_GAMEPAD
 class NavigatorUserMediaSuccessCallback;
 class NavigatorUserMediaErrorCallback;
 class MozGetUserMediaDevicesSuccessCallback;
 
 namespace network {
 class Connection;
 } // namespace network
@@ -260,16 +261,17 @@ public:
   already_AddRefed<Promise> GetMobileIdAssertion(const MobileIdOptions& options,
                                                  ErrorResult& aRv);
 #endif
 #ifdef MOZ_B2G_RIL
   MobileConnectionArray* GetMozMobileConnections(ErrorResult& aRv);
 #endif // MOZ_B2G_RIL
 #ifdef MOZ_GAMEPAD
   void GetGamepads(nsTArray<RefPtr<Gamepad> >& aGamepads, ErrorResult& aRv);
+  GamepadServiceTest* RequestGamepadServiceTest();
 #endif // MOZ_GAMEPAD
   already_AddRefed<Promise> GetVRDevices(ErrorResult& aRv);
   void NotifyVRDevicesUpdated();
 #ifdef MOZ_B2G_FM
   FMRadio* GetMozFMRadio(ErrorResult& aRv);
 #endif
 #ifdef MOZ_B2G_BT
   bluetooth::BluetoothManager* GetMozBluetooth(ErrorResult& aRv);
@@ -393,17 +395,19 @@ private:
   RefPtr<MediaDevices> mMediaDevices;
   nsCOMPtr<nsIDOMNavigatorSystemMessages> mMessagesManager;
   nsTArray<nsWeakPtr> mDeviceStorageStores;
   RefPtr<time::TimeManager> mTimeManager;
   RefPtr<ServiceWorkerContainer> mServiceWorkerContainer;
   nsCOMPtr<nsPIDOMWindowInner> mWindow;
   RefPtr<DeviceStorageAreaListener> mDeviceStorageAreaListener;
   RefPtr<Presentation> mPresentation;
-
+#ifdef MOZ_GAMEPAD
+  RefPtr<GamepadServiceTest> mGamepadServiceTest;
+#endif
   nsTArray<RefPtr<Promise> > mVRGetDevicesPromises;
   nsTArray<uint32_t> mRequestedVibrationPattern;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Navigator_h
--- a/dom/gamepad/GamepadServiceTest.cpp
+++ b/dom/gamepad/GamepadServiceTest.cpp
@@ -1,96 +1,278 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "GamepadServiceTest.h"
+
+#include "mozilla/ErrorResult.h"
+#include "mozilla/unused.h"
+
 #include "mozilla/dom/GamepadManager.h"
 #include "mozilla/dom/GamepadPlatformService.h"
+#include "mozilla/dom/GamepadServiceTestBinding.h"
+#include "mozilla/dom/GamepadTestChannelChild.h"
 
-using namespace mozilla::dom;
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+
+#include "mozilla/unused.h"
+
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+
+namespace mozilla {
+namespace dom {
 
 /*
  * Implementation of the test service. This is just to provide a simple binding
- * of the GamepadService to JavaScript via XPCOM so that we can write Mochitests
+ * of the GamepadService to JavaScript via WebIDL so that we can write Mochitests
  * that add and remove fake gamepads, avoiding the platform-specific backends.
  */
-NS_IMPL_ISUPPORTS(GamepadServiceTest, nsIGamepadServiceTest)
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(GamepadServiceTest)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(GamepadServiceTest,
+                                                  DOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
-GamepadServiceTest* GamepadServiceTest::sSingleton = nullptr;
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(GamepadServiceTest,
+                                                DOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(GamepadServiceTest)
+  NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback)
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+
+NS_IMPL_ADDREF_INHERITED(GamepadServiceTest, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(GamepadServiceTest, DOMEventTargetHelper)
 
 // static
 already_AddRefed<GamepadServiceTest>
-GamepadServiceTest::CreateService()
+GamepadServiceTest::CreateTestService(nsPIDOMWindowInner* aWindow)
 {
-  if (sSingleton == nullptr) {
-    sSingleton = new GamepadServiceTest();
-  }
-  RefPtr<GamepadServiceTest> service = sSingleton;
+  MOZ_ASSERT(aWindow);
+  RefPtr<GamepadServiceTest> service = new GamepadServiceTest(aWindow);
+  service->InitPBackgroundActor();
   return service.forget();
 }
 
-GamepadServiceTest::GamepadServiceTest() :
-  mService(GamepadManager::GetService())
+void
+GamepadServiceTest::Shutdown()
 {
+  MOZ_ASSERT(!mShuttingDown);
+  mShuttingDown = true;
+  DestroyPBackgroundActor();
+  mWindow = nullptr;
 }
 
-GamepadServiceTest::~GamepadServiceTest()
+GamepadServiceTest::GamepadServiceTest(nsPIDOMWindowInner* aWindow)
+  : mService(GamepadManager::GetService()),
+    mWindow(aWindow),
+    mEventNumber(0),
+    mShuttingDown(false),
+    mChild(nullptr)
+{}
+
+GamepadServiceTest::~GamepadServiceTest() {}
+
+void
+GamepadServiceTest::InitPBackgroundActor()
 {
+  MOZ_ASSERT(!mChild);
+  PBackgroundChild *actor = BackgroundChild::GetForCurrentThread();
+  //Try to get the PBackground Child actor
+  if (actor) {
+    ActorCreated(actor);
+  } else {
+    Unused << BackgroundChild::GetOrCreateForCurrentThread(this);
+  }
 }
 
-NS_IMETHODIMP
-GamepadServiceTest::AddGamepad(const char* aID,
+void
+GamepadServiceTest::DestroyPBackgroundActor()
+{
+  if (mChild) {
+    // If mChild exists, which means that IPDL channel
+    // has been created, our pending operations should
+    // be empty.
+    MOZ_ASSERT(mPendingOperations.IsEmpty());
+    mChild->SendShutdownChannel();
+    mChild = nullptr;
+  } else {
+    // If the IPDL channel has not been created and we
+    // want to destroy it now, just cancel all pending
+    // operations.
+    mPendingOperations.Clear();
+  }
+}
+
+already_AddRefed<Promise>
+GamepadServiceTest::AddGamepad(const nsAString& aID,
                                uint32_t aMapping,
                                uint32_t aNumButtons,
                                uint32_t aNumAxes,
-                               uint32_t* aGamepadIndex)
+                               ErrorResult& aRv)
 {
-  GamepadService* service = GamepadService::GetService();
-  MOZ_ASSERT(service);
-  *aGamepadIndex = service->AddGamepad(aID,
-                                       static_cast<GamepadMappingType>(aMapping),
-                                       aNumButtons,
-                                       aNumAxes);
-  return NS_OK;
+  if (mShuttingDown) {
+    return nullptr;
+  }
+
+  GamepadAdded a(nsString(aID), 0,
+                (uint32_t)aMapping, aNumButtons, aNumAxes);
+  GamepadChangeEvent e(a);
+  nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
+
+  RefPtr<Promise> p = Promise::Create(go, aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  uint32_t id = ++mEventNumber;
+  if (mChild) {
+    mChild->AddPromise(id, p);
+    mChild->SendGamepadTestEvent(id, e);
+  } else {
+    PendingOperation op(id, e, p);
+    mPendingOperations.AppendElement(op);
+  }
+  return p.forget();
 }
 
-NS_IMETHODIMP GamepadServiceTest::RemoveGamepad(uint32_t aIndex)
+void
+GamepadServiceTest::RemoveGamepad(uint32_t aIndex)
 {
-  GamepadService* service = GamepadService::GetService();
-  MOZ_ASSERT(service);
-  service->RemoveGamepad(aIndex);
-  return NS_OK;
+  if (mShuttingDown) {
+    return;
+  }
+
+  GamepadRemoved a(aIndex);
+  GamepadChangeEvent e(a);
+
+  uint32_t id = ++mEventNumber;
+  if (mChild) {
+    mChild->SendGamepadTestEvent(id, e);
+  } else {
+    PendingOperation op(id, e);
+    mPendingOperations.AppendElement(op);
+  }
+}
+
+void
+GamepadServiceTest::NewButtonEvent(uint32_t aIndex,
+                                   uint32_t aButton,
+                                   bool aPressed)
+{
+  if (mShuttingDown) {
+    return;
+  }
+
+  GamepadButtonInformation a(aIndex, aButton, aPressed, aPressed ? 1.0 : 0);
+  GamepadChangeEvent e(a);
+
+  uint32_t id = ++mEventNumber;
+  if (mChild) {
+    mChild->SendGamepadTestEvent(id, e);
+  } else {
+    PendingOperation op(id, e);
+    mPendingOperations.AppendElement(op);
+  }
 }
 
-NS_IMETHODIMP GamepadServiceTest::NewButtonEvent(uint32_t aIndex,
-                                                 uint32_t aButton,
-                                                 bool aPressed)
+void
+GamepadServiceTest::NewButtonValueEvent(uint32_t aIndex,
+                                        uint32_t aButton,
+                                        bool aPressed,
+                                        double aValue)
 {
-  GamepadService* service = GamepadService::GetService();
-  MOZ_ASSERT(service);
-  service->NewButtonEvent(aIndex, aButton, aPressed);
-  return NS_OK;
+  if (mShuttingDown) {
+    return;
+  }
+
+  GamepadButtonInformation a(aIndex, aButton, aPressed, aValue);
+  GamepadChangeEvent e(a);
+
+  uint32_t id = ++mEventNumber;
+  if (mChild) {
+    mChild->SendGamepadTestEvent(id, e);
+  } else {
+    PendingOperation op(id, e);
+    mPendingOperations.AppendElement(op);
+  }
+}
+
+void
+GamepadServiceTest::NewAxisMoveEvent(uint32_t aIndex,
+                                     uint32_t aAxis,
+                                     double aValue)
+{
+  if (mShuttingDown) {
+    return;
+  }
+
+  GamepadAxisInformation a(aIndex, aAxis, aValue);
+  GamepadChangeEvent e(a);
+
+  uint32_t id = ++mEventNumber;
+  if (mChild) {
+    mChild->SendGamepadTestEvent(id, e);
+  } else {
+    PendingOperation op(id, e);
+    mPendingOperations.AppendElement(op);
+  }
 }
 
-NS_IMETHODIMP GamepadServiceTest::NewButtonValueEvent(uint32_t aIndex,
-                                                      uint32_t aButton,
-                                                      bool aPressed,
-                                                      double aValue)
+void
+GamepadServiceTest::FlushPendingOperations()
 {
-  GamepadService* service = GamepadService::GetService();
-  MOZ_ASSERT(service);
-  service->NewButtonEvent(aIndex, aButton, aPressed, aValue);
-  return NS_OK;
+  for (uint32_t i=0; i < mPendingOperations.Length(); ++i) {
+    PendingOperation op = mPendingOperations[i];
+    if (op.mPromise) {
+      mChild->AddPromise(op.mID, op.mPromise);
+    }
+    mChild->SendGamepadTestEvent(op.mID, op.mEvent);
+  }
+  mPendingOperations.Clear();
 }
 
-NS_IMETHODIMP GamepadServiceTest::NewAxisMoveEvent(uint32_t aIndex,
-                                                   uint32_t aAxis,
-                                                   double aValue)
+void
+GamepadServiceTest::ActorCreated(PBackgroundChild* aActor)
 {
-  GamepadService* service = GamepadService::GetService();
-  MOZ_ASSERT(service);
-  service->NewAxisMoveEvent(aIndex, aAxis, aValue);
-  return NS_OK;
+  MOZ_ASSERT(aActor);
+  // If we are shutting down, we don't need to create the
+  // IPDL child/parent pair anymore.
+  if (mShuttingDown) {
+    // mPendingOperations should be cleared in
+    // DestroyPBackgroundActor()
+    MOZ_ASSERT(mPendingOperations.IsEmpty());
+    return;
+  }
+
+  mChild = new GamepadTestChannelChild();
+  PGamepadTestChannelChild* initedChild =
+    aActor->SendPGamepadTestChannelConstructor(mChild);
+  if (NS_WARN_IF(!initedChild)) {
+    ActorFailed();
+    return;
+  }
+  FlushPendingOperations();
 }
 
+void
+GamepadServiceTest::ActorFailed()
+{
+  MOZ_CRASH("Failed to create background child actor!");
+}
+
+JSObject*
+GamepadServiceTest::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto)
+{
+  return GamepadServiceTestBinding::Wrap(aCx, this, aGivenProto);
+}
+
+} // dom
+} // mozilla
--- a/dom/gamepad/GamepadServiceTest.h
+++ b/dom/gamepad/GamepadServiceTest.h
@@ -2,43 +2,86 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_GamepadServiceTest_h_
 #define mozilla_dom_GamepadServiceTest_h_
 
-#include "nsIGamepadServiceTest.h"
+#include "nsIIPCBackgroundChildCreateCallback.h"
 
 namespace mozilla {
 namespace dom {
 
+class GamepadChangeEvent;
 class GamepadManager;
+class GamepadTestChannelChild;
+class Promise;
 
 // Service for testing purposes
-class GamepadServiceTest : public nsIGamepadServiceTest
+class GamepadServiceTest final : public DOMEventTargetHelper,
+                                 public nsIIPCBackgroundChildCreateCallback
 {
 public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIGAMEPADSERVICETEST
+  NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(GamepadServiceTest,
+                                           DOMEventTargetHelper)
+
+  uint32_t NoMapping() const { return 0; }
+  uint32_t StandardMapping() const { return 1; }
 
-  GamepadServiceTest();
+  already_AddRefed<Promise> AddGamepad(const nsAString& aID,
+                                       uint32_t aMapping,
+                                       uint32_t aNumButtons,
+                                       uint32_t aNumAxes,
+                                       ErrorResult& aRv);
+  void RemoveGamepad(uint32_t aIndex);
+  void NewButtonEvent(uint32_t aIndex, uint32_t aButton, bool aPressed);
+  void NewButtonValueEvent(uint32_t aIndex, uint32_t aButton, bool aPressed, double aValue);
+  void NewAxisMoveEvent(uint32_t aIndex, uint32_t aAxis, double aValue);
+  void Shutdown();
 
-  static already_AddRefed<GamepadServiceTest> CreateService();
+  static already_AddRefed<GamepadServiceTest> CreateTestService(nsPIDOMWindowInner* aWindow);
+  nsPIDOMWindowInner* GetParentObject() const { return mWindow; }
+  JSObject* WrapObject(JSContext* aCx, JS::HandleObject aGivenProto) override;
 
 private:
-  static GamepadServiceTest* sSingleton;
+
+  // We need to asynchronously create IPDL channel, it is possible that
+  // we send commands before the channel is created, so we have to buffer
+  // them until the channel is created in that case.
+  struct PendingOperation {
+    explicit PendingOperation(const uint32_t& aID,
+                              const GamepadChangeEvent& aEvent,
+                              Promise* aPromise = nullptr)
+               : mID(aID), mEvent(aEvent), mPromise(aPromise) {}
+    uint32_t mID;
+    const GamepadChangeEvent& mEvent;
+    RefPtr<Promise> mPromise;
+  };
+
   // Hold a reference to the gamepad service so we don't have to worry about
   // execution order in tests.
   RefPtr<GamepadManager> mService;
-  virtual ~GamepadServiceTest();
+  nsCOMPtr<nsPIDOMWindowInner> mWindow;
+  nsTArray<PendingOperation> mPendingOperations;
+  uint32_t mEventNumber;
+  bool mShuttingDown;
+
+  // IPDL Channel for us to send test events to GamepadPlatformService, it
+  // will only be used in this singleton class and deleted during the IPDL
+  // shutdown chain
+  GamepadTestChannelChild* MOZ_NON_OWNING_REF mChild;
+
+  explicit GamepadServiceTest(nsPIDOMWindowInner* aWindow);
+  ~GamepadServiceTest();
+  void InitPBackgroundActor();
+  void DestroyPBackgroundActor();
+  void FlushPendingOperations();
+
 };
 
 } // namespace dom
 } // namespace mozilla
 
-#define NS_GAMEPAD_TEST_CID \
-{ 0xfb1fcb57, 0xebab, 0x4cf4, \
-{ 0x96, 0x3b, 0x1e, 0x4d, 0xb8, 0x52, 0x16, 0x96 } }
-#define NS_GAMEPAD_TEST_CONTRACTID "@mozilla.org/gamepad-test;1"
-
 #endif
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/ipc/GamepadEventTypes.ipdlh
@@ -0,0 +1,41 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+namespace mozilla {
+namespace dom {
+
+struct GamepadAdded {
+  nsString id;
+  uint32_t index;
+  uint32_t mapping;
+  uint32_t num_buttons;
+  uint32_t num_axes;
+};
+
+struct GamepadRemoved {
+  uint32_t index;
+};
+
+struct GamepadAxisInformation {
+  uint32_t index;
+  uint32_t axis;
+  double value;
+};
+
+struct GamepadButtonInformation {
+  uint32_t index;
+  uint32_t button;
+  bool pressed;
+  double value;
+};
+
+union GamepadChangeEvent {
+  GamepadAdded;
+  GamepadRemoved;
+  GamepadAxisInformation;
+  GamepadButtonInformation;
+};
+
+} // namespace dom
+} // namespace mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/ipc/GamepadTestChannelChild.cpp
@@ -0,0 +1,32 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GamepadTestChannelChild.h"
+
+namespace mozilla {
+namespace dom {
+
+void
+GamepadTestChannelChild::AddPromise(const uint32_t& aID, Promise* aPromise)
+{
+  MOZ_ASSERT(!mPromiseList.Get(aID, nullptr));
+  mPromiseList.Put(aID, aPromise);
+}
+
+bool
+GamepadTestChannelChild::RecvReplyGamepadIndex(const uint32_t& aID,
+                                               const uint32_t& aIndex)
+{
+  RefPtr<Promise> p;
+  if (!mPromiseList.Get(aID, getter_AddRefs(p))) {
+    MOZ_CRASH("We should always have a promise.");
+  }
+
+  p->MaybeResolve(aIndex);
+  mPromiseList.Remove(aID);
+  return true;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/ipc/GamepadTestChannelChild.h
@@ -0,0 +1,29 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/PGamepadTestChannelChild.h"
+#include "mozilla/dom/Promise.h"
+
+#ifndef mozilla_dom_GamepadTestChannelChild_h_
+#define mozilla_dom_GamepadTestChannelChild_h_
+
+namespace mozilla {
+namespace dom {
+
+class GamepadTestChannelChild final : public PGamepadTestChannelChild
+{
+ public:
+  GamepadTestChannelChild() {}
+  ~GamepadTestChannelChild() {}
+  void AddPromise(const uint32_t& aID, Promise* aPromise);
+ private:
+  virtual bool RecvReplyGamepadIndex(const uint32_t& aID,
+                                     const uint32_t& aIndex) override;
+  nsRefPtrHashtable<nsUint32HashKey, Promise> mPromiseList;
+};
+
+}// namespace dom
+}// namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/ipc/GamepadTestChannelParent.cpp
@@ -0,0 +1,63 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GamepadTestChannelParent.h"
+
+#include "mozilla/dom/GamepadPlatformService.h"
+#include "mozilla/unused.h"
+
+namespace mozilla {
+namespace dom {
+
+bool
+GamepadTestChannelParent::RecvGamepadTestEvent(const uint32_t& aID,
+                                               const GamepadChangeEvent& aEvent)
+{
+  mozilla::ipc::AssertIsOnBackgroundThread();
+  GamepadPlatformService*  service =
+    GamepadPlatformService::GetParentService();
+  MOZ_ASSERT(service);
+  if (aEvent.type() == GamepadChangeEvent::TGamepadAdded) {
+    const GamepadAdded& a = aEvent.get_GamepadAdded();
+    nsCString gamepadID;
+    LossyCopyUTF16toASCII(a.id(), gamepadID);
+    uint32_t index = service->AddGamepad(gamepadID.get(),
+                                         (GamepadMappingType)a.mapping(),
+                                         a.num_buttons(),
+                                         a.num_axes());
+    if (!mShuttingdown) {
+      Unused << SendReplyGamepadIndex(aID, index);
+    }
+    return true;
+  }
+  if (aEvent.type() == GamepadChangeEvent::TGamepadRemoved) {
+    const GamepadRemoved& a = aEvent.get_GamepadRemoved();
+    service->RemoveGamepad(a.index());
+    return true;
+  }
+  if (aEvent.type() == GamepadChangeEvent::TGamepadButtonInformation) {
+    const GamepadButtonInformation& a = aEvent.get_GamepadButtonInformation();
+    service->NewButtonEvent(a.index(), a.button(), a.pressed(), a.value());
+    return true;
+  }
+  if (aEvent.type() == GamepadChangeEvent::TGamepadAxisInformation) {
+    const GamepadAxisInformation& a = aEvent.get_GamepadAxisInformation();
+    service->NewAxisMoveEvent(a.index(), a.axis(), a.value());
+    return true;
+  }
+
+  NS_WARNING("Unknown event type.");
+  return false;
+}
+
+bool
+GamepadTestChannelParent::RecvShutdownChannel()
+{
+  mShuttingdown = true;
+  Unused << Send__delete__(this);
+  return true;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/ipc/GamepadTestChannelParent.h
@@ -0,0 +1,33 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/PGamepadTestChannelParent.h"
+
+#ifndef mozilla_dom_GamepadTestChannelParent_h_
+#define mozilla_dom_GamepadTestChannelParent_h_
+
+namespace mozilla {
+namespace dom {
+
+class GamepadTestChannelParent final : public PGamepadTestChannelParent
+{
+ public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GamepadTestChannelParent)
+  GamepadTestChannelParent()
+    : mShuttingdown(false) {}
+  virtual void ActorDestroy(ActorDestroyReason aWhy) override {}
+  virtual bool
+  RecvGamepadTestEvent(const uint32_t& aID,
+                       const GamepadChangeEvent& aGamepadEvent) override;
+  virtual bool
+  RecvShutdownChannel() override;
+ private:
+  ~GamepadTestChannelParent() {}
+  bool mShuttingdown;
+};
+
+}// namespace dom
+}// namespace mozilla
+
+#endif
--- a/dom/gamepad/ipc/PGamepadEventChannel.ipdl
+++ b/dom/gamepad/ipc/PGamepadEventChannel.ipdl
@@ -1,48 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 include protocol PBackground;
+include GamepadEventTypes;
 
 namespace mozilla {
 namespace dom {
 
-struct GamepadAdded {
-  nsString id;
-  uint32_t index;
-  uint32_t mapping;
-  uint32_t num_buttons;
-  uint32_t num_axes;
-};
-
-struct GamepadRemoved {
-  uint32_t index;
-};
-
-struct GamepadAxisInformation {
-  uint32_t index;
-  uint32_t axis;
-  double value;
-};
-
-struct GamepadButtonInformation {
-  uint32_t index;
-  uint32_t button;
-  bool pressed;
-  double value;
-};
-
-union GamepadChangeEvent {
-  GamepadAdded;
-  GamepadRemoved;
-  GamepadAxisInformation;
-  GamepadButtonInformation;
-};
-
 async protocol PGamepadEventChannel {
   manager PBackground;
   parent:
     async GamepadListenerAdded();
     async GamepadListenerRemoved();
   child:
     async __delete__();
     async GamepadUpdate(GamepadChangeEvent aGamepadEvent);
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/ipc/PGamepadTestChannel.ipdl
@@ -0,0 +1,21 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+include protocol PBackground;
+include GamepadEventTypes;
+
+namespace mozilla {
+namespace dom {
+
+async protocol PGamepadTestChannel {
+  manager PBackground;
+  parent:
+    async GamepadTestEvent(uint32_t aID, GamepadChangeEvent aGamepadEvent);
+    async ShutdownChannel();
+  child:
+    async __delete__();
+    async ReplyGamepadIndex(uint32_t aID, uint32_t aIndex);
+};
+
+}
+}
--- a/dom/gamepad/moz.build
+++ b/dom/gamepad/moz.build
@@ -7,28 +7,32 @@
 EXPORTS.mozilla.dom += [
     'Gamepad.h',
     'GamepadButton.h',
     'GamepadManager.h',
     'GamepadMonitoring.h',
     'GamepadPlatformService.h',
     'GamepadServiceTest.h',
     'ipc/GamepadEventChannelChild.h',
-    'ipc/GamepadEventChannelParent.h'
+    'ipc/GamepadEventChannelParent.h',
+    'ipc/GamepadTestChannelChild.h',
+    'ipc/GamepadTestChannelParent.h'
     ]
 
 UNIFIED_SOURCES = [
     'Gamepad.cpp',
     'GamepadButton.cpp',
     'GamepadManager.cpp',
     'GamepadMonitoring.cpp',
     'GamepadPlatformService.cpp',
     'GamepadServiceTest.cpp',
     'ipc/GamepadEventChannelChild.cpp',
-    'ipc/GamepadEventChannelParent.cpp'
+    'ipc/GamepadEventChannelParent.cpp',
+    'ipc/GamepadTestChannelChild.cpp',
+    'ipc/GamepadTestChannelParent.cpp'
     ]
 
 if CONFIG['MOZ_GAMEPAD_BACKEND'] == 'stub':
     UNIFIED_SOURCES += [
         'fallback/FallbackGamepad.cpp'
     ]
 elif CONFIG['MOZ_GAMEPAD_BACKEND'] == 'cocoa':
     UNIFIED_SOURCES += [
@@ -47,17 +51,19 @@ elif CONFIG['MOZ_GAMEPAD_BACKEND'] == 'a
         'android/AndroidGamepad.cpp'
     ]
 
 LOCAL_INCLUDES += [
     'ipc',
 ]
 
 IPDL_SOURCES += [
-    'ipc/PGamepadEventChannel.ipdl'
+    'ipc/GamepadEventTypes.ipdlh',
+    'ipc/PGamepadEventChannel.ipdl',
+    'ipc/PGamepadTestChannel.ipdl'
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '/dom/base',
 ]
deleted file mode 100644
--- a/dom/interfaces/gamepad/moz.build
+++ /dev/null
@@ -1,11 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-XPIDL_MODULE = 'dom_gamepad'
-
-XPIDL_SOURCES = [
-  'nsIGamepadServiceTest.idl',
-  ]
deleted file mode 100644
--- a/dom/interfaces/gamepad/nsIGamepadServiceTest.idl
+++ /dev/null
@@ -1,29 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsISupports.idl"
-
-interface nsIVariant;
-
-/*
- * This interface is intended only for use in tests.
- */
-[scriptable, uuid(c03ec4ed-8a7e-40e7-99da-c609f1760d0c)]
-interface nsIGamepadServiceTest : nsISupports
-{
-  const unsigned long NO_MAPPING = 0;
-  const unsigned long STANDARD_MAPPING = 1;
-
-  unsigned long addGamepad(in string id,
-                           in unsigned long mapping,
-                           in unsigned long numButtons,
-			   in unsigned long numAxes);
-  void removeGamepad(in unsigned long index);
-  void newButtonEvent(in unsigned long index, in unsigned long button,
-		      in boolean pressed);
-  void newButtonValueEvent(in unsigned long index, in unsigned long button,
-                           in boolean pressed, in double value);
-  void newAxisMoveEvent(in unsigned long index, in unsigned long axis,
-			in double value);
-};
--- a/dom/moz.build
+++ b/dom/moz.build
@@ -26,17 +26,16 @@ interfaces = [
     'json',
     'offline',
     'geolocation',
     'notification',
     'permission',
     'svg',
     'smil',
     'apps',
-    'gamepad',
     'push',
 ]
 
 DIRS += ['interfaces/' + i for i in interfaces]
 
 DIRS += [
     'animation',
     'apps',
deleted file mode 100644
--- a/dom/tests/mochitest/gamepad/gamepad_service_test_chrome_script.js
+++ /dev/null
@@ -1,36 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-var { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
-const { Services } = Cu.import('resource://gre/modules/Services.jsm');
-
-var GamepadService = (function() {
-  return Cc["@mozilla.org/gamepad-test;1"].getService(Ci.nsIGamepadServiceTest);
-})();
-
-
-addMessageListener('add-gamepad', function(message) {
-  var index = GamepadService.addGamepad(message.name,
-                                        message.mapping,
-                                        message.buttons,
-                                        message.axes);
-  sendAsyncMessage('new-gamepad-index', index);
-});
-
-addMessageListener('new-button-event', function(message) {
-  GamepadService.newButtonEvent(message.index,
-                                message.button,
-                                message.status);
-});
-
-addMessageListener('new-button-value-event', function(message) {
-  GamepadService.newButtonValueEvent(message.index,
-                                     message.button,
-                                     message.status,
-                                     message.value);
-});
-
-addMessageListener('remove-gamepad', function(message) {
-  GamepadService.removeGamepad(message.index);
-});
-
--- a/dom/tests/mochitest/gamepad/mochitest.ini
+++ b/dom/tests/mochitest/gamepad/mochitest.ini
@@ -1,14 +1,13 @@
 [DEFAULT]
 skip-if=(buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 support-files =
   gamepad_frame.html
   gamepad_frame_state.html
   mock_gamepad.js
-  gamepad_service_test_chrome_script.js
 
 [test_check_timestamp.html]
 [test_gamepad.html]
 [test_gamepad_connect_events.html]
 [test_gamepad_frame_state_sync.html]
 [test_gamepad_hidden_frame.html]
 [test_navigator_gamepads.html]
--- a/dom/tests/mochitest/gamepad/mock_gamepad.js
+++ b/dom/tests/mochitest/gamepad/mock_gamepad.js
@@ -1,71 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-// Determine whether this process is a parent or child process.
-let appInfo = SpecialPowers.Cc["@mozilla.org/xre/app-info;1"];
-let isParentProcess =
-      !appInfo || appInfo.getService(SpecialPowers.Ci.nsIXULRuntime)
-  .processType == SpecialPowers.Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
-
 var GamepadService;
-var GamepadScript;
-
-// sendAsyncMessage fails when running in process, so if we're not e10s, just
-// use GamepadServiceTest directly.
-if (isParentProcess) {
-  GamepadService = SpecialPowers.Cc["@mozilla.org/gamepad-test;1"].getService(SpecialPowers.Ci.nsIGamepadServiceTest);
-} else {
-  // If we're e10s, use the proxy script to access GamepadServiceTest in the
-  // parent process.
-  let url = SimpleTest.getTestFileURL("gamepad_service_test_chrome_script.js");
-  GamepadScript = SpecialPowers.loadChromeScript(url);
 
-  GamepadService = {
-    addGamepad: function (name, mapping, buttons, axes) {
-      GamepadScript.sendAsyncMessage("add-gamepad", {
-        name: name,
-        mapping: mapping,
-        buttons: buttons,
-        axes: axes
-      });
-    },
-    newButtonEvent: function (index, button, status) {
-      GamepadScript.sendAsyncMessage("new-button-event", {
-        index: index,
-        button: button,
-        status: status
-      });
-    },
-    newButtonValueEvent: function (index, button, status, value) {
-      GamepadScript.sendAsyncMessage("new-button-value-event", {
-        index: index,
-        button: button,
-        status: status,
-        value: value
-      });
-    },
-    removeGamepad: function (index) {
-      GamepadScript.sendAsyncMessage("remove-gamepad", {
-        index: index
-      });
-    },
-    finish: function () {
-      GamepadScript.destroy();
-    }
-  };
-}
+GamepadService = SpecialPowers.Cc["@mozilla.org/gamepad-test;1"].getService(SpecialPowers.Ci.nsIGamepadServiceTest);
+
 
 var addGamepad = function(name, mapping, buttons, axes) {
   return new Promise((resolve, reject) => {
-    if (isParentProcess) {
-      resolve(GamepadService.addGamepad(name, mapping, buttons, axes));
-    } else {
-      var listener = (index) => {
-        GamepadScript.removeMessageListener('new-gamepad-index', listener);
-        resolve(index);
-      };
-      GamepadScript.addMessageListener('new-gamepad-index', listener);
-      GamepadService.addGamepad(name, mapping, buttons, axes);
-    }
+    resolve(GamepadService.addGamepad(name, mapping, buttons, axes));
   });
 }
--- a/dom/tests/mochitest/gamepad/test_check_timestamp.html
+++ b/dom/tests/mochitest/gamepad/test_check_timestamp.html
@@ -6,30 +6,30 @@
   <title>Test Gamepad.timestamp</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <script type="text/javascript" src="mock_gamepad.js"></script>
 <script class="testbody" type="text/javascript">
 SimpleTest.waitForExplicitFinish();
-
+window.addEventListener("gamepadbuttondown", buttonpresshandler);
 var index;
 addGamepad("test gamepad 1", // id
            SpecialPowers.Ci.nsIGamepadServiceTest.NO_MAPPING,
            4, // buttons
            2).then(function(i) {
              index = i;
              // Press a button to make the gamepad visible to the page.
              GamepadService.newButtonEvent(index, 0, true);
              GamepadService.newButtonEvent(index, 0, true);
            });
 
 var timea=0;
-window.addEventListener("gamepadbuttondown", buttonpresshandler);
+
 
 var firstPress = true;
 var testOver = false;
 
 function cleanup(){
   SpecialPowers.executeSoon(function() {
     GamepadService.removeGamepad(index);
     SimpleTest.finish();
new file mode 100644
--- /dev/null
+++ b/dom/webidl/GamepadServiceTest.webidl
@@ -0,0 +1,31 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+[Pref="dom.gamepad.test.enabled"]
+interface GamepadServiceTest
+{
+  readonly attribute unsigned long noMapping;
+  readonly attribute unsigned long standardMapping;
+
+  [Throws]
+  Promise<unsigned long> addGamepad(DOMString id,
+                                    unsigned long mapping,
+                                    unsigned long numButtons,
+                                    unsigned long numAxes);
+
+  void removeGamepad(unsigned long index);
+
+  void newButtonEvent(unsigned long index,
+                      unsigned long button,
+                      boolean pressed);
+
+  void newButtonValueEvent(unsigned long index,
+                           unsigned long button,
+                           boolean pressed,
+                           double value);
+
+  void newAxisMoveEvent(unsigned long index,
+                        unsigned long axis,
+                        double value);
+};
\ No newline at end of file
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -344,16 +344,20 @@ partial interface Navigator {
 #endif // MOZ_B2G_RIL
 
 #ifdef MOZ_GAMEPAD
 // https://dvcs.w3.org/hg/gamepad/raw-file/default/gamepad.html#navigator-interface-extension
 partial interface Navigator {
   [Throws, Pref="dom.gamepad.enabled"]
   sequence<Gamepad?> getGamepads();
 };
+partial interface Navigator {
+  [Pref="dom.gamepad.test.enabled"]
+  GamepadServiceTest requestGamepadServiceTest();
+};
 #endif // MOZ_GAMEPAD
 
 partial interface Navigator {
   [Throws, Pref="dom.vr.enabled"]
   Promise<sequence<VRDevice>> getVRDevices();
 };
 
 #ifdef MOZ_B2G_BT
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -167,16 +167,17 @@ WEBIDL_FILES = [
     'FlyWebWebSocketEvent.webidl',
     'FocusEvent.webidl',
     'FontFace.webidl',
     'FontFaceSet.webidl',
     'FontFaceSource.webidl',
     'FormData.webidl',
     'Function.webidl',
     'GainNode.webidl',
+    'GamepadServiceTest.webidl',
     'Geolocation.webidl',
     'GeometryUtils.webidl',
     'GetUserMediaRequest.webidl',
     'HDMIInputPort.webidl',
     'Headers.webidl',
     'HeapSnapshot.webidl',
     'History.webidl',
     'HTMLAllCollection.webidl',
--- a/ipc/glue/BackgroundChildImpl.cpp
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -20,16 +20,17 @@
 #include "mozilla/dom/FileSystemTaskBase.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsChild.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/quota/PQuotaChild.h"
 #include "mozilla/dom/GamepadEventChannelChild.h"
+#include "mozilla/dom/GamepadTestChannelChild.h"
 #include "mozilla/dom/MessagePortChild.h"
 #include "mozilla/dom/NuwaChild.h"
 #include "mozilla/ipc/PBackgroundTestChild.h"
 #include "mozilla/ipc/PSendStreamChild.h"
 #include "mozilla/layout/VsyncChild.h"
 #include "mozilla/net/PUDPSocketChild.h"
 #include "mozilla/dom/network/UDPSocketChild.h"
 #include "nsID.h"
@@ -495,16 +496,31 @@ BackgroundChildImpl::AllocPGamepadEventC
 bool
 BackgroundChildImpl::DeallocPGamepadEventChannelChild(PGamepadEventChannelChild* aActor)
 {
   MOZ_ASSERT(aActor);
   delete static_cast<dom::GamepadEventChannelChild*>(aActor);
   return true;
 }
 
+dom::PGamepadTestChannelChild*
+BackgroundChildImpl::AllocPGamepadTestChannelChild()
+{
+  MOZ_CRASH("PGamepadTestChannelChild actor should be manually constructed!");
+  return nullptr;
+}
+
+bool
+BackgroundChildImpl::DeallocPGamepadTestChannelChild(PGamepadTestChannelChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+  delete static_cast<dom::GamepadTestChannelChild*>(aActor);
+  return true;
+}
+
 } // namespace ipc
 } // namespace mozilla
 
 bool
 TestChild::Recv__delete__(const nsCString& aTestArg)
 {
   MOZ_RELEASE_ASSERT(aTestArg == mTestArg,
                      "BackgroundTest message was corrupted!");
--- a/ipc/glue/BackgroundChildImpl.h
+++ b/ipc/glue/BackgroundChildImpl.h
@@ -173,16 +173,22 @@ protected:
   DeallocPFileSystemRequestChild(PFileSystemRequestChild*) override;
 
   // Gamepad API Background IPC
   virtual PGamepadEventChannelChild*
   AllocPGamepadEventChannelChild() override;
 
   virtual bool
   DeallocPGamepadEventChannelChild(PGamepadEventChannelChild* aActor) override;
+
+  virtual PGamepadTestChannelChild*
+  AllocPGamepadTestChannelChild() override;
+
+  virtual bool
+  DeallocPGamepadTestChannelChild(PGamepadTestChannelChild* aActor) override;
 };
 
 class BackgroundChildImpl::ThreadLocal final
 {
   friend class nsAutoPtr<ThreadLocal>;
 
 public:
   nsAutoPtr<mozilla::dom::indexedDB::ThreadLocal> mIndexedDBThreadLocal;
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -14,19 +14,21 @@
 #include "mozilla/media/MediaParent.h"
 #include "mozilla/AppProcessChecker.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/DOMTypes.h"
 #include "mozilla/dom/FileSystemBase.h"
 #include "mozilla/dom/FileSystemRequestParent.h"
 #include "mozilla/dom/GamepadEventChannelParent.h"
+#include "mozilla/dom/GamepadTestChannelParent.h"
 #include "mozilla/dom/NuwaParent.h"
 #include "mozilla/dom/PBlobParent.h"
 #include "mozilla/dom/PGamepadEventChannelParent.h"
+#include "mozilla/dom/PGamepadTestChannelParent.h"
 #include "mozilla/dom/MessagePortParent.h"
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/dom/quota/ActorsParent.h"
 #include "mozilla/ipc/BackgroundParent.h"
@@ -931,16 +933,35 @@ BackgroundParentImpl::AllocPGamepadEvent
 bool
 BackgroundParentImpl::DeallocPGamepadEventChannelParent(dom::PGamepadEventChannelParent *aActor)
 {
   MOZ_ASSERT(aActor);
   RefPtr<dom::GamepadEventChannelParent> parent =
     dont_AddRef(static_cast<dom::GamepadEventChannelParent*>(aActor));
   return true;
 }
+
+dom::PGamepadTestChannelParent*
+BackgroundParentImpl::AllocPGamepadTestChannelParent()
+{
+  RefPtr<dom::GamepadTestChannelParent> parent =
+    new dom::GamepadTestChannelParent();
+
+  return parent.forget().take();
+}
+
+bool
+BackgroundParentImpl::DeallocPGamepadTestChannelParent(dom::PGamepadTestChannelParent *aActor)
+{
+  MOZ_ASSERT(aActor);
+  RefPtr<dom::GamepadTestChannelParent> parent =
+    dont_AddRef(static_cast<dom::GamepadTestChannelParent*>(aActor));
+  return true;
+}
+
 } // namespace ipc
 } // namespace mozilla
 
 void
 TestParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   mozilla::ipc::AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -204,14 +204,20 @@ protected:
   DeallocPFileSystemRequestParent(PFileSystemRequestParent*) override;
 
   // Gamepad API Background IPC
   virtual PGamepadEventChannelParent*
   AllocPGamepadEventChannelParent() override;
 
   virtual bool
   DeallocPGamepadEventChannelParent(PGamepadEventChannelParent *aActor) override;
+
+  virtual PGamepadTestChannelParent*
+  AllocPGamepadTestChannelParent() override;
+
+  virtual bool
+  DeallocPGamepadTestChannelParent(PGamepadTestChannelParent* aActor) override;
 };
 
 } // namespace ipc
 } // namespace mozilla
 
 #endif // mozilla_ipc_backgroundparentimpl_h__
--- a/ipc/glue/PBackground.ipdl
+++ b/ipc/glue/PBackground.ipdl
@@ -9,16 +9,17 @@ include protocol PBackgroundTest;
 include protocol PBlob;
 include protocol PBroadcastChannel;
 include protocol PCache;
 include protocol PCacheStorage;
 include protocol PCacheStreamControl;
 include protocol PFileDescriptorSet;
 include protocol PFileSystemRequest;
 include protocol PGamepadEventChannel;
+include protocol PGamepadTestChannel;
 include protocol PMessagePort;
 include protocol PCameras;
 include protocol PNuwa;
 include protocol PQuota;
 include protocol PSendStream;
 include protocol PServiceWorkerManager;
 include protocol PUDPSocket;
 include protocol PVsync;
@@ -51,16 +52,17 @@ sync protocol PBackground
   manages PBlob;
   manages PBroadcastChannel;
   manages PCache;
   manages PCacheStorage;
   manages PCacheStreamControl;
   manages PFileDescriptorSet;
   manages PFileSystemRequest;
   manages PGamepadEventChannel;
+  manages PGamepadTestChannel;
   manages PMessagePort;
   manages PCameras;
   manages PNuwa;
   manages PQuota;
   manages PSendStream;
   manages PServiceWorkerManager;
   manages PUDPSocket;
   manages PVsync;
@@ -102,16 +104,18 @@ parent:
                          PrincipalInfo principalInfo);
 
   async PQuota();
 
   async PFileSystemRequest(FileSystemParams params);
 
   async PGamepadEventChannel();
 
+  async PGamepadTestChannel();
+
 child:
   async PCache();
   async PCacheStreamControl();
 
 both:
   async PBlob(BlobConstructorParams params);
 
   async PFileDescriptorSet(FileDescriptor fd);
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -209,19 +209,16 @@ NS_NewXULContentBuilder(nsISupports* aOu
 nsresult
 NS_NewXULTreeBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult);
 #endif
 
 static void Shutdown();
 
 #include "nsGeolocation.h"
 #include "nsDeviceSensors.h"
-#ifdef MOZ_GAMEPAD
-#include "mozilla/dom/GamepadServiceTest.h"
-#endif
 #include "mozilla/dom/nsContentSecurityManager.h"
 #include "mozilla/dom/nsCSPService.h"
 #include "mozilla/dom/nsCSPContext.h"
 #include "nsICellBroadcastService.h"
 #include "nsIIccService.h"
 #include "nsISmsService.h"
 #include "nsIMmsService.h"
 #include "nsIMobileConnectionService.h"
@@ -366,22 +363,16 @@ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR
                                          PowerManagerService::GetInstance)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIAlarmHalService,
                                          AlarmHalService::GetInstance)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsITimeService,
                                          TimeService::GetInstance)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIStreamingProtocolControllerService,
                                          StreamingProtocolControllerService::GetInstance)
 
-#ifdef MOZ_GAMEPAD
-using mozilla::dom::GamepadServiceTest;
-NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(GamepadServiceTest,
-                                         GamepadServiceTest::CreateService)
-#endif
-
 #ifdef MOZ_WIDGET_GONK
 #ifndef DISABLE_MOZ_RIL_GEOLOC
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIGeolocationProvider,
                                          GonkGPSGeolocationProvider::GetSingleton)
 #endif
 // Since the nsVolumeService constructor calls into nsIPowerManagerService,
 // we need it to be constructed sometime after nsIPowerManagerService.
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsVolumeService,
@@ -840,19 +831,16 @@ NS_DEFINE_NAMED_CID(MOBILE_MESSAGE_SERVI
 NS_DEFINE_NAMED_CID(MOBILE_MESSAGE_DATABASE_SERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_POWERMANAGERSERVICE_CID);
 NS_DEFINE_NAMED_CID(OSFILECONSTANTSSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_ALARMHALSERVICE_CID);
 NS_DEFINE_NAMED_CID(UDPSOCKETCHILD_CID);
 NS_DEFINE_NAMED_CID(NS_TIMESERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_MEDIASTREAMCONTROLLERSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_MEDIAMANAGERSERVICE_CID);
-#ifdef MOZ_GAMEPAD
-NS_DEFINE_NAMED_CID(NS_GAMEPAD_TEST_CID);
-#endif
 #ifdef MOZ_WEBSPEECH_TEST_BACKEND
 NS_DEFINE_NAMED_CID(NS_FAKE_SPEECH_RECOGNITION_SERVICE_CID);
 #endif
 #ifdef MOZ_WEBSPEECH_POCKETSPHINX
 NS_DEFINE_NAMED_CID(NS_POCKETSPHINX_SPEECH_RECOGNITION_SERVICE_CID);
 #endif
 #ifdef MOZ_WEBSPEECH
 NS_DEFINE_NAMED_CID(NS_SYNTHVOICEREGISTRY_CID);
@@ -1152,19 +1140,16 @@ static const mozilla::Module::CIDEntry k
   { &kUDPSOCKETCHILD_CID, false, nullptr, UDPSocketChildConstructor },
   { &kGECKO_MEDIA_PLUGIN_SERVICE_CID, true, nullptr, GeckoMediaPluginServiceConstructor },
   { &kNS_TIMESERVICE_CID, false, nullptr, nsITimeServiceConstructor },
   { &kNS_MEDIASTREAMCONTROLLERSERVICE_CID, false, nullptr, nsIStreamingProtocolControllerServiceConstructor },
 #if defined(MOZ_WIDGET_GONK) && !defined(DISABLE_MOZ_RIL_GEOLOC)
   { &kGONK_GPS_GEOLOCATION_PROVIDER_CID, false, nullptr, nsIGeolocationProviderConstructor },
 #endif
   { &kNS_MEDIAMANAGERSERVICE_CID, false, nullptr, nsIMediaManagerServiceConstructor },
-#ifdef MOZ_GAMEPAD
-  { &kNS_GAMEPAD_TEST_CID, false, nullptr, GamepadServiceTestConstructor },
-#endif
 #ifdef ACCESSIBILITY
   { &kNS_ACCESSIBILITY_SERVICE_CID, false, nullptr, CreateA11yService },
 #endif
   { &kTELEPHONY_SERVICE_CID, false, nullptr, nsITelephonyServiceConstructor },
   { &kNS_MOBILE_CONNECTION_SERVICE_CID, false, NULL, nsIMobileConnectionServiceConstructor },
   { &kNS_VOICEMAIL_SERVICE_CID, false, nullptr, nsIVoicemailServiceConstructor },
   { &kTV_TUNER_DATA_CID, false, nullptr, TVTunerDataConstructor },
   { &kTV_CHANNEL_DATA_CID, false, nullptr, TVChannelDataConstructor },
@@ -1319,19 +1304,16 @@ static const mozilla::Module::ContractID
   { OSFILECONSTANTSSERVICE_CONTRACTID, &kOSFILECONSTANTSSERVICE_CID },
   { ALARMHALSERVICE_CONTRACTID, &kNS_ALARMHALSERVICE_CID },
   { "@mozilla.org/udp-socket-child;1", &kUDPSOCKETCHILD_CID },
   { TIMESERVICE_CONTRACTID, &kNS_TIMESERVICE_CID },
   { MEDIASTREAMCONTROLLERSERVICE_CONTRACTID, &kNS_MEDIASTREAMCONTROLLERSERVICE_CID },
 #if defined(MOZ_WIDGET_GONK) && !defined(DISABLE_MOZ_RIL_GEOLOC)
   { GONK_GPS_GEOLOCATION_PROVIDER_CONTRACTID, &kGONK_GPS_GEOLOCATION_PROVIDER_CID },
 #endif
-#ifdef MOZ_GAMEPAD
-  { NS_GAMEPAD_TEST_CONTRACTID, &kNS_GAMEPAD_TEST_CID },
-#endif
   { MEDIAMANAGERSERVICE_CONTRACTID, &kNS_MEDIAMANAGERSERVICE_CID },
 #ifdef ACCESSIBILITY
   { "@mozilla.org/accessibilityService;1", &kNS_ACCESSIBILITY_SERVICE_CID },
   { "@mozilla.org/accessibleRetrieval;1", &kNS_ACCESSIBILITY_SERVICE_CID },
 #endif
   { TELEPHONY_SERVICE_CONTRACTID, &kTELEPHONY_SERVICE_CID },
   { TV_TUNER_DATA_CONTRACTID, &kTV_TUNER_DATA_CID },
   { TV_CHANNEL_DATA_CONTRACTID, &kTV_CHANNEL_DATA_CID },
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -144,19 +144,16 @@
 @BINPATH@/components/dom_tv.xpt
 #ifdef MOZ_WEBSPEECH
 @BINPATH@/components/dom_webspeechrecognition.xpt
 #endif
 @BINPATH@/components/dom_xbl.xpt
 @BINPATH@/components/dom_xhr.xpt
 @BINPATH@/components/dom_xpath.xpt
 @BINPATH@/components/dom_xul.xpt
-#ifdef MOZ_GAMEPAD
-@BINPATH@/components/dom_gamepad.xpt
-#endif
 @BINPATH@/components/dom_presentation.xpt
 @BINPATH@/components/downloads.xpt
 @BINPATH@/components/editor.xpt
 @BINPATH@/components/embed_base.xpt
 @BINPATH@/components/extensions.xpt
 @BINPATH@/components/exthandler.xpt
 @BINPATH@/components/exthelper.xpt
 @BINPATH@/components/fastfind.xpt
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -169,16 +169,17 @@ pref("dom.performance.enable_notify_perf
 #ifdef NIGHTLY_BUILD
 pref("dom.enable_performance_observer", true);
 #else
 pref("dom.enable_performance_observer", false);
 #endif
 
 // Whether the Gamepad API is enabled
 pref("dom.gamepad.enabled", true);
+pref("dom.gamepad.test.enabled", false);
 #ifdef RELEASE_BUILD
 pref("dom.gamepad.non_standard_events.enabled", false);
 #else
 pref("dom.gamepad.non_standard_events.enabled", true);
 #endif
 
 // Whether the KeyboardEvent.code is enabled
 pref("dom.keyboardevent.code.enabled", true);