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 id30376
push usercbook@mozilla.com
push dateTue, 28 Jun 2016 14:09:36 +0000
treeherdermozilla-central@e45890951ce7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersqdot, baku
bugs1221730
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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);