Bug 852944 - Gamepad API IPC; r=ted, r=baku
authorKyle Machulis <kyle@nonpolynomial.com>
Thu, 23 Apr 2015 15:05:29 -0700
changeset 270713 45bbfe5ef9b20adebd5a4306ae0c666fd38b3bcf
parent 270712 afdd76d6dcce66cdc081680571ff4f95987fbb8e
child 270714 12409cbb8d6157fe0665afb9a0fe99e840afde3c
push id4830
push userjlund@mozilla.com
push dateMon, 29 Jun 2015 20:18:48 +0000
treeherdermozilla-beta@4c2175bb0420 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersted, baku
bugs852944
milestone40.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 852944 - Gamepad API IPC; r=ted, r=baku
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/gamepad/GamepadFunctions.cpp
dom/gamepad/GamepadFunctions.h
dom/gamepad/GamepadMonitoring.cpp
dom/gamepad/GamepadMonitoring.h
dom/gamepad/GamepadService.cpp
dom/gamepad/GamepadService.h
dom/gamepad/GamepadServiceTest.cpp
dom/gamepad/GamepadServiceTest.h
dom/gamepad/android/AndroidGamepad.cpp
dom/gamepad/cocoa/CocoaGamepad.cpp
dom/gamepad/fallback/FallbackGamepad.cpp
dom/gamepad/linux/LinuxGamepad.cpp
dom/gamepad/linux/udev.h
dom/gamepad/moz.build
dom/gamepad/windows/WindowsGamepad.cpp
dom/interfaces/gamepad/nsIGamepadServiceTest.idl
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/tests/mochitest/gamepad/mochitest.ini
dom/tests/mochitest/gamepad/test_check_timestamp.html
dom/tests/mochitest/gamepad/test_gamepad.html
dom/tests/mochitest/gamepad/test_navigator_gamepads.html
hal/Hal.cpp
hal/Hal.h
hal/android/AndroidGamepad.cpp
hal/cocoa/CocoaGamepad.cpp
hal/fallback/FallbackGamepad.cpp
hal/linux/LinuxGamepad.cpp
hal/linux/udev.h
hal/moz.build
hal/sandbox/SandboxHal.cpp
hal/windows/WindowsGamepad.cpp
layout/build/nsLayoutModule.cpp
widget/android/nsAppShell.cpp
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -182,16 +182,17 @@
 #include "mozilla/dom/MessagePort.h"
 #include "mozilla/dom/MessagePortBinding.h"
 #include "mozilla/dom/indexedDB/IDBFactory.h"
 #include "mozilla/dom/Promise.h"
 
 #include "mozilla/dom/StructuredCloneTags.h"
 
 #ifdef MOZ_GAMEPAD
+#include "mozilla/dom/Gamepad.h"
 #include "mozilla/dom/GamepadService.h"
 #endif
 
 #include "mozilla/dom/VRDevice.h"
 
 #include "nsRefreshDriver.h"
 
 #include "mozilla/AddonPathService.h"
@@ -13362,35 +13363,50 @@ nsGlobalWindow::AddSizeOfIncludingThis(n
 }
 
 
 #ifdef MOZ_GAMEPAD
 void
 nsGlobalWindow::AddGamepad(uint32_t aIndex, Gamepad* aGamepad)
 {
   MOZ_ASSERT(IsInnerWindow());
+  // Create the index we will present to content based on which indices are
+  // already taken, as required by the spec.
+  // https://w3c.github.io/gamepad/gamepad.html#widl-Gamepad-index
+  int index = 0;
+  while(mGamepadIndexSet.Contains(index)) {
+    ++index;
+  }
+  mGamepadIndexSet.Put(index);
+  aGamepad->SetIndex(index);
   mGamepads.Put(aIndex, aGamepad);
 }
 
 void
 nsGlobalWindow::RemoveGamepad(uint32_t aIndex)
 {
   MOZ_ASSERT(IsInnerWindow());
+  nsRefPtr<Gamepad> gamepad;
+  if (!mGamepads.Get(aIndex, getter_AddRefs(gamepad))) {
+    return;
+  }
+  // Free up the index we were using so it can be reused
+  mGamepadIndexSet.Remove(gamepad->Index());
   mGamepads.Remove(aIndex);
 }
 
 // static
 PLDHashOperator
 nsGlobalWindow::EnumGamepadsForGet(const uint32_t& aKey, Gamepad* aData,
                                    void* aUserArg)
 {
   nsTArray<nsRefPtr<Gamepad> >* array =
     static_cast<nsTArray<nsRefPtr<Gamepad> >*>(aUserArg);
-  array->EnsureLengthAtLeast(aKey + 1);
-  (*array)[aKey] = aData;
+  array->EnsureLengthAtLeast(aData->Index() + 1);
+  (*array)[aData->Index()] = aData;
   return PL_DHASH_NEXT;
 }
 
 void
 nsGlobalWindow::GetGamepads(nsTArray<nsRefPtr<Gamepad> >& aGamepads)
 {
   MOZ_ASSERT(IsInnerWindow());
   aGamepads.Clear();
@@ -13399,16 +13415,17 @@ nsGlobalWindow::GetGamepads(nsTArray<nsR
   mGamepads.EnumerateRead(EnumGamepadsForGet, &aGamepads);
 }
 
 already_AddRefed<Gamepad>
 nsGlobalWindow::GetGamepad(uint32_t aIndex)
 {
   MOZ_ASSERT(IsInnerWindow());
   nsRefPtr<Gamepad> gamepad;
+
   if (mGamepads.Get(aIndex, getter_AddRefs(gamepad))) {
     return gamepad.forget();
   }
 
   return nullptr;
 }
 
 void
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -47,16 +47,17 @@
 #include "nsWrapperCacheInlines.h"
 #include "nsIIdleObserver.h"
 #include "nsIDocument.h"
 #include "mozilla/dom/EventTarget.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "Units.h"
 #include "nsComponentManagerUtils.h"
 #include "nsSize.h"
+#include "nsCheapSets.h"
 
 #define DEFAULT_HOME_PAGE "www.mozilla.org"
 #define PREF_BROWSER_STARTUP_HOMEPAGE "browser.startup.homepage"
 
 // Amount of time allowed between alert/prompt/confirm before enabling
 // the stop dialog checkbox.
 #define DEFAULT_SUCCESSIVE_DIALOG_TIME_LIMIT 3 // 3 sec
 
@@ -1585,16 +1586,17 @@ protected:
   // Ensure that a call to ResumeTimeouts() after FreeInnerObjects() does nothing.
   // This member is only used by inner windows.
   bool                   mInnerObjectsFreed : 1;
 
   // Inner windows only.
   // Indicates whether this window wants gamepad input events
   bool                   mHasGamepad : 1;
 #ifdef MOZ_GAMEPAD
+  nsCheapSet<nsUint32HashKey> mGamepadIndexSet;
   nsRefPtrHashtable<nsUint32HashKey, mozilla::dom::Gamepad> mGamepads;
   bool mHasSeenGamepadInput;
 #endif
 
   // whether we've sent the destroy notification for our window id
   bool                   mNotifiedIDDestroyed : 1;
   // whether scripts may close the window,
   // even if "dom.allow_scripts_to_close_windows" is false.
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/GamepadFunctions.cpp
@@ -0,0 +1,105 @@
+/* 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/GamepadFunctions.h"
+#include "mozilla/dom/GamepadService.h"
+#include "nsHashKeys.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/unused.h"
+
+namespace mozilla {
+namespace dom {
+namespace GamepadFunctions {
+
+namespace {
+// Increments as gamepads are added
+uint32_t gGamepadIndex = 0;
+}
+
+template<class T>
+void
+NotifyGamepadChange(const T& aInfo)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+  GamepadChangeEvent e(aInfo);
+  nsTArray<ContentParent*> t;
+  ContentParent::GetAll(t);
+  for(uint32_t i = 0; i < t.Length(); ++i) {
+    unused << t[i]->SendGamepadUpdate(e);
+  }
+  // If we have a GamepadService in the main process, send directly to it.
+  if (GamepadService::IsServiceRunning()) {
+    nsRefPtr<GamepadService> svc = GamepadService::GetService();
+    svc->Update(e);
+  }
+}
+
+uint32_t
+AddGamepad(const char* aID,
+           GamepadMappingType aMapping,
+           uint32_t aNumButtons, uint32_t aNumAxes)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
+  int index = gGamepadIndex;
+  gGamepadIndex++;
+  GamepadAdded a(NS_ConvertUTF8toUTF16(nsDependentCString(aID)), index,
+                 (uint32_t)aMapping, aNumButtons, aNumAxes);
+  gGamepadIndex++;
+  NotifyGamepadChange<GamepadAdded>(a);
+  return index;
+}
+
+void
+RemoveGamepad(uint32_t aIndex)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+  GamepadRemoved a(aIndex);
+  NotifyGamepadChange<GamepadRemoved>(a);
+}
+
+void
+NewButtonEvent(uint32_t aIndex, uint32_t aButton,
+               bool aPressed, double aValue)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+  GamepadButtonInformation a(aIndex, aButton, aPressed, aValue);
+  NotifyGamepadChange<GamepadButtonInformation>(a);
+}
+
+void
+NewButtonEvent(uint32_t aIndex, uint32_t aButton,
+               bool aPressed)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+  // When only a digital button is available the value will be synthesized.
+  NewButtonEvent(aIndex, aButton, aPressed, aPressed ? 1.0L : 0.0L);
+}
+
+void
+NewAxisMoveEvent(uint32_t aIndex, uint32_t aAxis,
+                 double aValue)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+  GamepadAxisInformation a(aIndex, aAxis, aValue);
+  NotifyGamepadChange<GamepadAxisInformation>(a);
+}
+
+void
+ResetGamepadIndexes()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+  gGamepadIndex = 0;
+}
+
+} // namespace GamepadFunctions
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/GamepadFunctions.h
@@ -0,0 +1,45 @@
+/* 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_GamepadFunctions_h_
+#define mozilla_dom_GamepadFunctions_h_
+
+#include "mozilla/dom/GamepadBinding.h"
+
+namespace mozilla {
+namespace dom {
+namespace GamepadFunctions {
+
+// Functions for building and transmitting IPDL messages through the HAL
+// sandbox. Used by platform specific Gamepad implementations
+
+// Add a gamepad to the list of known gamepads, and return its index.
+uint32_t AddGamepad(const char* aID, GamepadMappingType aMapping,
+                    uint32_t aNumButtons, uint32_t aNumAxes);
+// Remove the gamepad at |aIndex| from the list of known gamepads.
+void RemoveGamepad(uint32_t aIndex);
+
+// Update the state of |aButton| for the gamepad at |aIndex| for all
+// windows that are listening and visible, and fire one of
+// a gamepadbutton{up,down} event at them as well.
+// aPressed is used for digital buttons, aValue is for analog buttons.
+void NewButtonEvent(uint32_t aIndex, uint32_t aButton, bool aPressed,
+                    double aValue);
+// When only a digital button is available the value will be synthesized.
+void NewButtonEvent(uint32_t aIndex, uint32_t aButton, bool aPressed);
+
+// Update the state of |aAxis| for the gamepad at |aIndex| for all
+// windows that are listening and visible, and fire a gamepadaxismove
+// event at them as well.
+void NewAxisMoveEvent(uint32_t aIndex, uint32_t aAxis, double aValue);
+
+// When shutting down the platform communications for gamepad, also reset the
+// indexes.
+void ResetGamepadIndexes();
+
+} // namespace GamepadFunctions
+} // namespace dom
+} // namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/GamepadMonitoring.cpp
@@ -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/. */
+
+#include "mozilla/dom/GamepadMonitoring.h"
+#include "mozilla/dom/GamepadFunctions.h"
+#include "mozilla/dom/PContentParent.h"
+
+namespace mozilla {
+namespace dom {
+
+using namespace GamepadFunctions;
+
+void
+MaybeStopGamepadMonitoring()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+  nsTArray<ContentParent*> t;
+  ContentParent::GetAll(t);
+  for(uint32_t i = 0; i < t.Length(); ++i) {
+    if (t[i]->HasGamepadListener()) {
+      return;
+    }
+  }
+  StopGamepadMonitoring();
+  ResetGamepadIndexes();
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/GamepadMonitoring.h
@@ -0,0 +1,22 @@
+/* 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_GamepadMonitoring_h_
+#define mozilla_dom_GamepadMonitoring_h_
+
+namespace mozilla {
+namespace dom {
+// Functions for platform specific gamepad monitoring.
+
+void MaybeStopGamepadMonitoring();
+
+// These two functions are implemented in the platform specific service files
+// (linux/LinuxGamepad.cpp, cocoa/CocoaGamepad.cpp, etc)
+void StartGamepadMonitoring();
+void StopGamepadMonitoring();
+
+} // namespace dom
+} // namespace mozilla
+
+#endif
--- a/dom/gamepad/GamepadService.cpp
+++ b/dom/gamepad/GamepadService.cpp
@@ -1,34 +1,35 @@
 /* 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/Hal.h"
+#include "mozilla/dom/GamepadService.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/Gamepad.h"
+#include "mozilla/dom/GamepadAxisMoveEvent.h"
+#include "mozilla/dom/GamepadButtonEvent.h"
+#include "mozilla/dom/GamepadEvent.h"
+#include "mozilla/dom/GamepadMonitoring.h"
+
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/StaticPtr.h"
 
-#include "GamepadService.h"
-#include "Gamepad.h"
 #include "nsAutoPtr.h"
 #include "nsIDOMEvent.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMWindow.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "nsIServiceManager.h"
 #include "nsITimer.h"
 #include "nsThreadUtils.h"
 #include "mozilla/Services.h"
 
-#include "mozilla/dom/GamepadAxisMoveEvent.h"
-#include "mozilla/dom/GamepadButtonEvent.h"
-#include "mozilla/dom/GamepadEvent.h"
-
 #include <cstddef>
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 const char* kGamepadEnabledPref = "dom.gamepad.enabled";
 const char* kGamepadEventsEnabledPref =
@@ -77,47 +78,53 @@ GamepadService::Observe(nsISupports* aSu
 void
 GamepadService::BeginShutdown()
 {
   mShuttingDown = true;
   if (mTimer) {
     mTimer->Cancel();
   }
   if (mStarted) {
-    mozilla::hal::StopMonitoringGamepadStatus();
+    if (XRE_GetProcessType() == GeckoProcessType_Default) {
+      MaybeStopGamepadMonitoring();
+    } else {
+      ContentChild::GetSingleton()->SendGamepadListenerRemoved();
+    }
     mStarted = false;
   }
   // Don't let windows call back to unregister during shutdown
   for (uint32_t i = 0; i < mListeners.Length(); i++) {
     mListeners[i]->SetHasGamepadEventListener(false);
   }
   mListeners.Clear();
   mGamepads.Clear();
   sShutdown = true;
 }
 
 void
 GamepadService::AddListener(nsGlobalWindow* aWindow)
 {
   MOZ_ASSERT(aWindow);
   MOZ_ASSERT(aWindow->IsInnerWindow());
-
   if (mShuttingDown) {
     return;
   }
 
   if (mListeners.IndexOf(aWindow) != NoIndex) {
     return; // already exists
   }
 
   if (!mStarted && mEnabled) {
-    mozilla::hal::StartMonitoringGamepadStatus();
+    if (XRE_GetProcessType() == GeckoProcessType_Default) {
+      StartGamepadMonitoring();
+    } else {
+      ContentChild::GetSingleton()->SendGamepadListenerAdded();
+    }
     mStarted = true;
   }
-
   mListeners.AppendElement(aWindow);
 }
 
 void
 GamepadService::RemoveListener(nsGlobalWindow* aWindow)
 {
   MOZ_ASSERT(aWindow);
   MOZ_ASSERT(aWindow->IsInnerWindow());
@@ -134,82 +141,71 @@ GamepadService::RemoveListener(nsGlobalW
 
   mListeners.RemoveElement(aWindow);
 
   if (mListeners.Length() == 0 && !mShuttingDown && mStarted) {
     StartCleanupTimer();
   }
 }
 
-uint32_t
-GamepadService::AddGamepad(const char* aId,
+already_AddRefed<Gamepad>
+GamepadService::GetGamepad(uint32_t aIndex)
+{
+  nsRefPtr<Gamepad> gamepad;
+  if (mGamepads.Get(aIndex, getter_AddRefs(gamepad))) {
+    return gamepad.forget();
+  }
+
+  return nullptr;
+}
+
+void
+GamepadService::AddGamepad(uint32_t aIndex,
+                           const nsAString& aId,
                            GamepadMappingType aMapping,
                            uint32_t aNumButtons,
                            uint32_t aNumAxes)
 {
   //TODO: bug 852258: get initial button/axis state
   nsRefPtr<Gamepad> gamepad =
     new Gamepad(nullptr,
-                NS_ConvertUTF8toUTF16(nsDependentCString(aId)),
-                0,
+                aId,
+                0, // index is set by global window
                 aMapping,
                 aNumButtons,
                 aNumAxes);
-  int index = -1;
-  for (uint32_t i = 0; i < mGamepads.Length(); i++) {
-    if (!mGamepads[i]) {
-      mGamepads[i] = gamepad;
-      index = i;
-      break;
-    }
-  }
-  if (index == -1) {
-    mGamepads.AppendElement(gamepad);
-    index = mGamepads.Length() - 1;
-  }
 
-  gamepad->SetIndex(index);
-  NewConnectionEvent(index, true);
-
-  return index;
+  // We store the gamepad related to its index given by the parent process.
+  mGamepads.Put(aIndex, gamepad);
+  NewConnectionEvent(aIndex, true);
 }
 
 void
 GamepadService::RemoveGamepad(uint32_t aIndex)
 {
-  if (aIndex < mGamepads.Length()) {
-    mGamepads[aIndex]->SetConnected(false);
+  nsRefPtr<Gamepad> gamepad = GetGamepad(aIndex);
+  if (!gamepad) {
+    NS_WARNING("Trying to delete gamepad with invalid index");
+    return;
+  }
+  gamepad->SetConnected(false);
     NewConnectionEvent(aIndex, false);
-    // If this is the last entry in the list, just remove it.
-    if (aIndex == mGamepads.Length() - 1) {
-      mGamepads.RemoveElementAt(aIndex);
-    } else {
-      // Otherwise just null it out and leave it, so the
-      // indices of the following entries remain valid.
-      mGamepads[aIndex] = nullptr;
-    }
-  }
-}
-
-void
-GamepadService::NewButtonEvent(uint32_t aIndex, uint32_t aButton, bool aPressed)
-{
-  // Synthesize a value: 1.0 for pressed, 0.0 for unpressed.
-  NewButtonEvent(aIndex, aButton, aPressed, aPressed ? 1.0L : 0.0L);
+  mGamepads.Remove(aIndex);
 }
 
 void
 GamepadService::NewButtonEvent(uint32_t aIndex, uint32_t aButton, bool aPressed,
                                double aValue)
 {
-  if (mShuttingDown || aIndex >= mGamepads.Length()) {
+  nsRefPtr<Gamepad> gamepad = GetGamepad(aIndex);
+  if (mShuttingDown || !gamepad) {
     return;
   }
 
-  mGamepads[aIndex]->SetButton(aButton, aPressed, aValue);
+  gamepad->SetButton(aButton, aPressed, aValue);
 
   // Hold on to listeners in a separate array because firing events
   // can mutate the mListeners array.
   nsTArray<nsRefPtr<nsGlobalWindow> > listeners(mListeners);
 
   for (uint32_t i = listeners.Length(); i > 0 ; ) {
     --i;
 
@@ -222,25 +218,25 @@ GamepadService::NewButtonEvent(uint32_t 
     bool first_time = false;
     if (!WindowHasSeenGamepad(listeners[i], aIndex)) {
       // This window hasn't seen this gamepad before, so
       // send a connection event first.
       SetWindowHasSeenGamepad(listeners[i], aIndex);
       first_time = true;
     }
 
-    nsRefPtr<Gamepad> gamepad = listeners[i]->GetGamepad(aIndex);
-    if (gamepad) {
-      gamepad->SetButton(aButton, aPressed, aValue);
+    nsRefPtr<Gamepad> listenerGamepad = listeners[i]->GetGamepad(aIndex);
+    if (listenerGamepad) {
+      listenerGamepad->SetButton(aButton, aPressed, aValue);
       if (first_time) {
-        FireConnectionEvent(listeners[i], gamepad, true);
+        FireConnectionEvent(listeners[i], listenerGamepad, true);
       }
       if (mNonstandardEventsEnabled) {
         // Fire event
-        FireButtonEvent(listeners[i], gamepad, aButton, aValue);
+        FireButtonEvent(listeners[i], listenerGamepad, aButton, aValue);
       }
     }
   }
 }
 
 void
 GamepadService::FireButtonEvent(EventTarget* aTarget,
                                 Gamepad* aGamepad,
@@ -261,20 +257,21 @@ GamepadService::FireButtonEvent(EventTar
 
   bool defaultActionEnabled = true;
   aTarget->DispatchEvent(event, &defaultActionEnabled);
 }
 
 void
 GamepadService::NewAxisMoveEvent(uint32_t aIndex, uint32_t aAxis, double aValue)
 {
-  if (mShuttingDown || aIndex >= mGamepads.Length()) {
+  nsRefPtr<Gamepad> gamepad = GetGamepad(aIndex);
+  if (mShuttingDown || !gamepad) {
     return;
   }
-  mGamepads[aIndex]->SetAxis(aAxis, aValue);
+  gamepad->SetAxis(aAxis, aValue);
 
   // Hold on to listeners in a separate array because firing events
   // can mutate the mListeners array.
   nsTArray<nsRefPtr<nsGlobalWindow> > listeners(mListeners);
 
   for (uint32_t i = listeners.Length(); i > 0 ; ) {
     --i;
 
@@ -287,25 +284,25 @@ GamepadService::NewAxisMoveEvent(uint32_
     bool first_time = false;
     if (!WindowHasSeenGamepad(listeners[i], aIndex)) {
       // This window hasn't seen this gamepad before, so
       // send a connection event first.
       SetWindowHasSeenGamepad(listeners[i], aIndex);
       first_time = true;
     }
 
-    nsRefPtr<Gamepad> gamepad = listeners[i]->GetGamepad(aIndex);
-    if (gamepad) {
-      gamepad->SetAxis(aAxis, aValue);
+    nsRefPtr<Gamepad> listenerGamepad = listeners[i]->GetGamepad(aIndex);
+    if (listenerGamepad) {
+      listenerGamepad->SetAxis(aAxis, aValue);
       if (first_time) {
-        FireConnectionEvent(listeners[i], gamepad, true);
+        FireConnectionEvent(listeners[i], listenerGamepad, true);
       }
       if (mNonstandardEventsEnabled) {
         // Fire event
-        FireAxisMoveEvent(listeners[i], gamepad, aAxis, aValue);
+        FireAxisMoveEvent(listeners[i], listenerGamepad, aAxis, aValue);
       }
     }
   }
 }
 
 void
 GamepadService::FireAxisMoveEvent(EventTarget* aTarget,
                                   Gamepad* aGamepad,
@@ -327,17 +324,19 @@ GamepadService::FireAxisMoveEvent(EventT
 
   bool defaultActionEnabled = true;
   aTarget->DispatchEvent(event, &defaultActionEnabled);
 }
 
 void
 GamepadService::NewConnectionEvent(uint32_t aIndex, bool aConnected)
 {
-  if (mShuttingDown || aIndex >= mGamepads.Length()) {
+  nsRefPtr<Gamepad> gamepad = GetGamepad(aIndex);
+
+  if (mShuttingDown || !gamepad) {
     return;
   }
 
   // Hold on to listeners in a separate array because firing events
   // can mutate the mListeners array.
   nsTArray<nsRefPtr<nsGlobalWindow> > listeners(mListeners);
 
   if (aConnected) {
@@ -353,37 +352,37 @@ GamepadService::NewConnectionEvent(uint3
       // We don't fire a connected event here unless the window
       // has seen input from at least one device.
       if (!listeners[i]->HasSeenGamepadInput()) {
         continue;
       }
 
       SetWindowHasSeenGamepad(listeners[i], aIndex);
 
-      nsRefPtr<Gamepad> gamepad = listeners[i]->GetGamepad(aIndex);
-      if (gamepad) {
+      nsRefPtr<Gamepad> listenerGamepad = listeners[i]->GetGamepad(aIndex);
+      if (listenerGamepad) {
         // Fire event
-        FireConnectionEvent(listeners[i], gamepad, aConnected);
+        FireConnectionEvent(listeners[i], listenerGamepad, aConnected);
       }
     }
   } else {
     // For disconnection events, fire one at every window that has received
     // data from this gamepad.
     for (uint32_t i = listeners.Length(); i > 0 ; ) {
       --i;
 
       // Even background windows get these events, so we don't have to
       // deal with the hassle of syncing the state of removed gamepads.
 
       if (WindowHasSeenGamepad(listeners[i], aIndex)) {
-        nsRefPtr<Gamepad> gamepad = listeners[i]->GetGamepad(aIndex);
-        if (gamepad) {
-          gamepad->SetConnected(false);
+        nsRefPtr<Gamepad> listenerGamepad = listeners[i]->GetGamepad(aIndex);
+        if (listenerGamepad) {
+          listenerGamepad->SetConnected(false);
           // Fire event
-          FireConnectionEvent(listeners[i], gamepad, false);
+          FireConnectionEvent(listeners[i], listenerGamepad, false);
           listeners[i]->RemoveGamepad(aIndex);
         }
       }
     }
   }
 }
 
 void
@@ -404,21 +403,29 @@ GamepadService::FireConnectionEvent(Even
 
   bool defaultActionEnabled = true;
   aTarget->DispatchEvent(event, &defaultActionEnabled);
 }
 
 void
 GamepadService::SyncGamepadState(uint32_t aIndex, Gamepad* aGamepad)
 {
-  if (mShuttingDown || !mEnabled || aIndex > mGamepads.Length()) {
+  nsRefPtr<Gamepad> gamepad = GetGamepad(aIndex);
+  if (mShuttingDown || !mEnabled || !gamepad) {
     return;
   }
 
-  aGamepad->SyncState(mGamepads[aIndex]);
+  aGamepad->SyncState(gamepad);
+}
+
+// static
+bool
+GamepadService::IsServiceRunning()
+{
+  return !!gGamepadServiceSingleton;
 }
 
 // static
 already_AddRefed<GamepadService>
 GamepadService::GetService()
 {
   if (sShutdown) {
     return nullptr;
@@ -456,18 +463,23 @@ GamepadService::SetWindowHasSeenGamepad(
   if (mListeners.IndexOf(aWindow) == NoIndex) {
     // This window isn't even listening for gamepad events.
     return;
   }
 
   if (aHasSeen) {
     aWindow->SetHasSeenGamepadInput(true);
     nsCOMPtr<nsISupports> window = ToSupports(aWindow);
-    nsRefPtr<Gamepad> gamepad = mGamepads[aIndex]->Clone(window);
-    aWindow->AddGamepad(aIndex, gamepad);
+    nsRefPtr<Gamepad> gamepad = GetGamepad(aIndex);
+    MOZ_ASSERT(gamepad);
+    if (!gamepad) {
+      return;
+    }
+    nsRefPtr<Gamepad> clonedGamepad = gamepad->Clone(window);
+    aWindow->AddGamepad(aIndex, clonedGamepad);
   } else {
     aWindow->RemoveGamepad(aIndex);
   }
 }
 
 // static
 void
 GamepadService::TimeoutHandler(nsITimer* aTimer, void* aClosure)
@@ -481,23 +493,26 @@ GamepadService::TimeoutHandler(nsITimer*
     return;
   }
 
   if (self->mShuttingDown) {
     return;
   }
 
   if (self->mListeners.Length() == 0) {
-    mozilla::hal::StopMonitoringGamepadStatus();
+    if (XRE_GetProcessType() == GeckoProcessType_Default) {
+      MaybeStopGamepadMonitoring();
+    } else {
+      ContentChild::GetSingleton()->SendGamepadListenerRemoved();
+    }
+
     self->mStarted = false;
-    if (!self->mGamepads.IsEmpty()) {
       self->mGamepads.Clear();
     }
   }
-}
 
 void
 GamepadService::StartCleanupTimer()
 {
   if (mTimer) {
     mTimer->Cancel();
   }
 
@@ -505,77 +520,32 @@ GamepadService::StartCleanupTimer()
   if (mTimer) {
     mTimer->InitWithFuncCallback(TimeoutHandler,
                                  this,
                                  kCleanupDelayMS,
                                  nsITimer::TYPE_ONE_SHOT);
   }
 }
 
-/*
- * 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
- * that add and remove fake gamepads, avoiding the platform-specific backends.
- */
-NS_IMPL_ISUPPORTS(GamepadServiceTest, nsIGamepadServiceTest)
-
-GamepadServiceTest* GamepadServiceTest::sSingleton = nullptr;
-
-// static
-already_AddRefed<GamepadServiceTest>
-GamepadServiceTest::CreateService()
-{
-  if (sSingleton == nullptr) {
-    sSingleton = new GamepadServiceTest();
-  }
-  nsRefPtr<GamepadServiceTest> service = sSingleton;
-  return service.forget();
-}
-
-GamepadServiceTest::GamepadServiceTest()
-{
-  /* member initializers and constructor code */
-  nsRefPtr<GamepadService> service = GamepadService::GetService();
-}
-
-/* uint32_t addGamepad (in string id, in unsigned long mapping, in unsigned long numButtons, in unsigned long numAxes); */
-NS_IMETHODIMP GamepadServiceTest::AddGamepad(const char* aID,
-                                             uint32_t aMapping,
-                                             uint32_t aNumButtons,
-                                             uint32_t aNumAxes,
-                                             uint32_t* aRetval)
+void
+GamepadService::Update(const GamepadChangeEvent& aEvent)
 {
-  *aRetval = gGamepadServiceSingleton->AddGamepad(aID,
-                                                  static_cast<GamepadMappingType>(aMapping),
-                                                  aNumButtons,
-                                                  aNumAxes);
-  return NS_OK;
-}
-
-/* void removeGamepad (in uint32_t index); */
-NS_IMETHODIMP GamepadServiceTest::RemoveGamepad(uint32_t aIndex)
-{
-  gGamepadServiceSingleton->RemoveGamepad(aIndex);
-  return NS_OK;
-}
-
-/* void newButtonEvent (in uint32_t index, in uint32_t button,
-   in boolean pressed); */
-NS_IMETHODIMP GamepadServiceTest::NewButtonEvent(uint32_t aIndex,
-                                                 uint32_t aButton,
-                                                 bool aPressed)
-{
-  gGamepadServiceSingleton->NewButtonEvent(aIndex, aButton, aPressed);
-  return NS_OK;
-}
-
-/* void newAxisMoveEvent (in uint32_t index, in uint32_t axis,
-   in double value); */
-NS_IMETHODIMP GamepadServiceTest::NewAxisMoveEvent(uint32_t aIndex,
-                                                   uint32_t aAxis,
-                                                   double aValue)
-{
-  gGamepadServiceSingleton->NewAxisMoveEvent(aIndex, aAxis, aValue);
-  return NS_OK;
+  if (aEvent.type() == GamepadChangeEvent::TGamepadAdded) {
+    const GamepadAdded& a = aEvent.get_GamepadAdded();
+    AddGamepad(a.index(), a.id(),
+               static_cast<GamepadMappingType>(a.mapping()),
+               a.num_buttons(), a.num_axes());
+  } else if (aEvent.type() == GamepadChangeEvent::TGamepadRemoved) {
+    const GamepadRemoved& a = aEvent.get_GamepadRemoved();
+    RemoveGamepad(a.index());
+  } else if (aEvent.type() == GamepadChangeEvent::TGamepadButtonInformation) {
+    const GamepadButtonInformation& a = aEvent.get_GamepadButtonInformation();
+    NewButtonEvent(a.index(), a.button(), a.pressed(), a.value());
+  } else if (aEvent.type() == GamepadChangeEvent::TGamepadAxisInformation) {
+    const GamepadAxisInformation& a = aEvent.get_GamepadAxisInformation();
+    NewAxisMoveEvent(a.index(), a.axis(), a.value());
+  } else {
+    MOZ_CRASH("We shouldn't be here!");
+  }
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/gamepad/GamepadService.h
+++ b/dom/gamepad/GamepadService.h
@@ -1,72 +1,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_GamepadService_h_
 #define mozilla_dom_GamepadService_h_
 
 #include <stdint.h>
-#include "Gamepad.h"
 #include "nsAutoPtr.h"
 #include "nsCOMArray.h"
 #include "nsIGamepadServiceTest.h"
 #include "nsGlobalWindow.h"
 #include "nsIFocusManager.h"
 #include "nsIObserver.h"
 #include "nsITimer.h"
 #include "nsTArray.h"
-
+// Needed for GamepadMappingType
+#include "mozilla/dom/GamepadBinding.h"
 namespace mozilla {
 namespace dom {
 
 class EventTarget;
+class GamepadChangeEvent;
+class Gamepad;
 
 class GamepadService : public nsIObserver
 {
  public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
+  // Returns true if we actually have a service up and running
+  static bool IsServiceRunning();
   // Get the singleton service
   static already_AddRefed<GamepadService> GetService();
   // Return true if the API is preffed on.
   static bool IsAPIEnabled();
 
   void BeginShutdown();
 
   // Indicate that |aWindow| wants to receive gamepad events.
   void AddListener(nsGlobalWindow* aWindow);
   // Indicate that |aWindow| should no longer receive gamepad events.
   void RemoveListener(nsGlobalWindow* aWindow);
 
-  // Add a gamepad to the list of known gamepads, and return its index.
-  uint32_t AddGamepad(const char* aID, GamepadMappingType aMapping,
+  // Add a gamepad to the list of known gamepads.
+  void AddGamepad(uint32_t aIndex, const nsAString& aID, GamepadMappingType aMapping,
                       uint32_t aNumButtons, uint32_t aNumAxes);
+
   // Remove the gamepad at |aIndex| from the list of known gamepads.
   void RemoveGamepad(uint32_t aIndex);
 
   // Update the state of |aButton| for the gamepad at |aIndex| for all
   // windows that are listening and visible, and fire one of
   // a gamepadbutton{up,down} event at them as well.
   // aPressed is used for digital buttons, aValue is for analog buttons.
   void NewButtonEvent(uint32_t aIndex, uint32_t aButton, bool aPressed,
                       double aValue);
-  // When only a digital button is available the value will be synthesized.
-  void NewButtonEvent(uint32_t aIndex, uint32_t aButton, bool aPressed);
 
   // Update the state of |aAxis| for the gamepad at |aIndex| for all
   // windows that are listening and visible, and fire a gamepadaxismove
   // event at them as well.
   void NewAxisMoveEvent(uint32_t aIndex, uint32_t aAxis, double aValue);
 
   // Synchronize the state of |aGamepad| to match the gamepad stored at |aIndex|
   void SyncGamepadState(uint32_t aIndex, Gamepad* aGamepad);
 
+  // Returns gamepad object if index exists, null otherwise
+  already_AddRefed<Gamepad> GetGamepad(uint32_t aIndex);
+
+  // Receive GamepadChangeEvent messages from parent process to fire DOM events
+  void Update(const GamepadChangeEvent& aGamepadEvent);
  protected:
   GamepadService();
   virtual ~GamepadService() {};
   void StartCleanupTimer();
 
   // Fire a gamepadconnected or gamepaddisconnected event for the gamepad
   // at |aIndex| to all windows that are listening and have received
   // gamepad input.
@@ -110,42 +118,18 @@ class GamepadService : public nsIObserve
   void SetWindowHasSeenGamepad(nsGlobalWindow* aWindow, uint32_t aIndex,
                                bool aHasSeen = true);
 
   static void TimeoutHandler(nsITimer* aTimer, void* aClosure);
   static bool sShutdown;
 
   // Gamepads connected to the system. Copies of these are handed out
   // to each window.
-  nsTArray<nsRefPtr<Gamepad> > mGamepads;
+  nsRefPtrHashtable<nsUint32HashKey, Gamepad> mGamepads;
   // Inner windows that are listening for gamepad events.
   // has been sent to that window.
   nsTArray<nsRefPtr<nsGlobalWindow> > mListeners;
   nsCOMPtr<nsITimer> mTimer;
-  nsCOMPtr<nsIFocusManager> mFocusManager;
-  nsCOMPtr<nsIObserver> mObserver;
-};
-
-// Service for testing purposes
-class GamepadServiceTest : public nsIGamepadServiceTest
-{
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIGAMEPADSERVICETEST
-
-  GamepadServiceTest();
-
-  static already_AddRefed<GamepadServiceTest> CreateService();
-
-private:
-  static GamepadServiceTest* sSingleton;
-  virtual ~GamepadServiceTest() {};
 };
 
 }  // 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 // mozilla_dom_GamepadService_h_
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/GamepadServiceTest.cpp
@@ -0,0 +1,92 @@
+/* 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/dom/GamepadService.h"
+#include "mozilla/dom/GamepadFunctions.h"
+
+using namespace mozilla::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
+ * that add and remove fake gamepads, avoiding the platform-specific backends.
+ */
+NS_IMPL_ISUPPORTS(GamepadServiceTest, nsIGamepadServiceTest)
+
+GamepadServiceTest* GamepadServiceTest::sSingleton = nullptr;
+
+// static
+already_AddRefed<GamepadServiceTest>
+GamepadServiceTest::CreateService()
+{
+  if (sSingleton == nullptr) {
+    sSingleton = new GamepadServiceTest();
+  }
+  nsRefPtr<GamepadServiceTest> service = sSingleton;
+  return service.forget();
+}
+
+GamepadServiceTest::GamepadServiceTest() :
+  mService(GamepadService::GetService())
+{
+}
+
+GamepadServiceTest::~GamepadServiceTest()
+{
+}
+
+/* uint32_t addGamepad(in unsigned long index, in string id, in unsigned long mapping, in unsigned long numButtons, in unsigned long numAxes); */
+NS_IMETHODIMP
+GamepadServiceTest::AddGamepad(const char* aID,
+                               uint32_t aMapping,
+                               uint32_t aNumButtons,
+                               uint32_t aNumAxes,
+                               uint32_t* aGamepadIndex)
+{
+  *aGamepadIndex = GamepadFunctions::AddGamepad(aID,
+                                                static_cast<GamepadMappingType>(aMapping),
+                                                aNumButtons,
+                                                aNumAxes);
+  return NS_OK;
+}
+
+/* void removeGamepad (in uint32_t index); */
+NS_IMETHODIMP GamepadServiceTest::RemoveGamepad(uint32_t aIndex)
+{
+  GamepadFunctions::RemoveGamepad(aIndex);
+  return NS_OK;
+}
+
+/* void newButtonEvent (in uint32_t index, in uint32_t button,
+   in boolean pressed); */
+NS_IMETHODIMP GamepadServiceTest::NewButtonEvent(uint32_t aIndex,
+                                                 uint32_t aButton,
+                                                 bool aPressed)
+{
+  GamepadFunctions::NewButtonEvent(aIndex, aButton, aPressed);
+  return NS_OK;
+}
+
+/* void newButtonEvent (in uint32_t index, in uint32_t button,
+   in boolean pressed, double value); */
+NS_IMETHODIMP GamepadServiceTest::NewButtonValueEvent(uint32_t aIndex,
+                                                      uint32_t aButton,
+                                                      bool aPressed,
+                                                      double aValue)
+{
+  GamepadFunctions::NewButtonEvent(aIndex, aButton, aPressed, aValue);
+  return NS_OK;
+}
+
+/* void newAxisMoveEvent (in uint32_t index, in uint32_t axis,
+   in double value); */
+NS_IMETHODIMP GamepadServiceTest::NewAxisMoveEvent(uint32_t aIndex,
+                                                   uint32_t aAxis,
+                                                   double aValue)
+{
+  GamepadFunctions::NewAxisMoveEvent(aIndex, aAxis, aValue);
+  return NS_OK;
+}
+
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/GamepadServiceTest.h
@@ -0,0 +1,42 @@
+/* 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"
+
+namespace mozilla {
+namespace dom {
+
+class GamepadService;
+
+// Service for testing purposes
+class GamepadServiceTest : public nsIGamepadServiceTest
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIGAMEPADSERVICETEST
+
+  GamepadServiceTest();
+
+  static already_AddRefed<GamepadServiceTest> CreateService();
+
+private:
+  static GamepadServiceTest* sSingleton;
+  // Hold a reference to the gamepad service so we don't have to worry about
+  // execution order in tests.
+  nsRefPtr<GamepadService> mService;
+  virtual ~GamepadServiceTest();
+};
+
+}  // 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/android/AndroidGamepad.cpp
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "AndroidBridge.h"
+
+namespace mozilla {
+namespace dom {
+
+void StartGamepadMonitoring()
+{
+  widget::GeckoAppShell::StartMonitoringGamepad();
+}
+
+void StopGamepadMonitoring()
+{
+  widget::GeckoAppShell::StopMonitoringGamepad();
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/cocoa/CocoaGamepad.cpp
@@ -0,0 +1,522 @@
+/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+// mostly derived from the Allegro source code at:
+// http://alleg.svn.sourceforge.net/viewvc/alleg/allegro/branches/4.9/src/macosx/hidjoy.m?revision=13760&view=markup
+
+#include "mozilla/dom/GamepadFunctions.h"
+#include "mozilla/ArrayUtils.h"
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/hid/IOHIDBase.h>
+#include <IOKit/hid/IOHIDKeys.h>
+#include <IOKit/hid/IOHIDManager.h>
+
+#include <stdio.h>
+#include <vector>
+
+namespace {
+
+using namespace mozilla;
+using namespace mozilla::dom::GamepadFunctions;
+using std::vector;
+
+struct Button {
+  int id;
+  bool analog;
+  IOHIDElementRef element;
+  CFIndex min;
+  CFIndex max;
+
+  Button(int aId, IOHIDElementRef aElement, CFIndex aMin, CFIndex aMax) :
+    id(aId),
+    analog((aMax - aMin) > 1),
+    element(aElement),
+    min(aMin),
+    max(aMax) {}
+};
+
+struct Axis {
+  int id;
+  IOHIDElementRef element;
+  uint32_t usagePage;
+  uint32_t usage;
+  CFIndex min;
+  CFIndex max;
+};
+
+typedef bool dpad_buttons[4];
+
+// These values can be found in the USB HID Usage Tables:
+// http://www.usb.org/developers/hidpage
+const unsigned kDesktopUsagePage = 0x01;
+const unsigned kSimUsagePage = 0x02;
+const unsigned kAcceleratorUsage = 0xC4;
+const unsigned kBrakeUsage = 0xC5;
+const unsigned kJoystickUsage = 0x04;
+const unsigned kGamepadUsage = 0x05;
+const unsigned kAxisUsageMin = 0x30;
+const unsigned kAxisUsageMax = 0x35;
+const unsigned kDpadUsage = 0x39;
+const unsigned kButtonUsagePage = 0x09;
+const unsigned kConsumerPage = 0x0C;
+const unsigned kHomeUsage = 0x223;
+const unsigned kBackUsage = 0x224;
+
+
+class Gamepad {
+ private:
+  IOHIDDeviceRef mDevice;
+  nsTArray<Button> buttons;
+  nsTArray<Axis> axes;
+  IOHIDElementRef mDpad;
+  dpad_buttons mDpadState;
+
+ public:
+  Gamepad() : mDevice(nullptr), mDpad(nullptr), mSuperIndex(-1) {}
+  bool operator==(IOHIDDeviceRef device) const { return mDevice == device; }
+  bool empty() const { return mDevice == nullptr; }
+  void clear()
+  {
+    mDevice = nullptr;
+    buttons.Clear();
+    axes.Clear();
+    mDpad = nullptr;
+    mSuperIndex = -1;
+  }
+  void init(IOHIDDeviceRef device);
+  size_t numButtons() { return buttons.Length() + (mDpad ? 4 : 0); }
+  size_t numAxes() { return axes.Length(); }
+
+  // Index given by our superclass.
+  uint32_t mSuperIndex;
+
+  const bool isDpad(IOHIDElementRef element) const
+  {
+    return element == mDpad;
+  }
+
+  const dpad_buttons& getDpadState() const
+  {
+    return mDpadState;
+  }
+
+  void setDpadState(const dpad_buttons& dpadState)
+  {
+    for (unsigned i = 0; i < ArrayLength(mDpadState); i++) {
+      mDpadState[i] = dpadState[i];
+    }
+  }
+
+  const Button* lookupButton(IOHIDElementRef element) const
+  {
+    for (unsigned i = 0; i < buttons.Length(); i++) {
+      if (buttons[i].element == element)
+        return &buttons[i];
+    }
+    return nullptr;
+  }
+
+  const Axis* lookupAxis(IOHIDElementRef element) const
+  {
+    for (unsigned i = 0; i < axes.Length(); i++) {
+      if (axes[i].element == element)
+        return &axes[i];
+    }
+    return nullptr;
+  }
+};
+
+class AxisComparator {
+public:
+  bool Equals(const Axis& a1, const Axis& a2) const
+  {
+    return a1.usagePage == a2.usagePage && a1.usage == a2.usage;
+  }
+  bool LessThan(const Axis& a1, const Axis& a2) const
+  {
+    if (a1.usagePage == a2.usagePage) {
+      return a1.usage < a2.usage;
+    }
+    return a1.usagePage < a2.usagePage;
+  }
+};
+
+void Gamepad::init(IOHIDDeviceRef device)
+{
+  clear();
+  mDevice = device;
+
+  CFArrayRef elements = IOHIDDeviceCopyMatchingElements(device,
+                                                        nullptr,
+                                                        kIOHIDOptionsTypeNone);
+  CFIndex n = CFArrayGetCount(elements);
+  for (CFIndex i = 0; i < n; i++) {
+    IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements,
+                                                                      i);
+    uint32_t usagePage = IOHIDElementGetUsagePage(element);
+    uint32_t usage = IOHIDElementGetUsage(element);
+
+    if (usagePage == kDesktopUsagePage &&
+        usage >= kAxisUsageMin &&
+        usage <= kAxisUsageMax)
+    {
+      Axis axis = { int(axes.Length()),
+                    element,
+                    usagePage,
+                    usage,
+                    IOHIDElementGetLogicalMin(element),
+                    IOHIDElementGetLogicalMax(element) };
+      axes.AppendElement(axis);
+    } else if (usagePage == kDesktopUsagePage && usage == kDpadUsage &&
+               // Don't know how to handle d-pads that return weird values.
+               IOHIDElementGetLogicalMax(element) - IOHIDElementGetLogicalMin(element) == 7) {
+      mDpad = element;
+    } else if ((usagePage == kSimUsagePage &&
+                 (usage == kAcceleratorUsage ||
+                  usage == kBrakeUsage)) ||
+               (usagePage == kButtonUsagePage) ||
+               (usagePage == kConsumerPage &&
+                 (usage == kHomeUsage ||
+                  usage == kBackUsage))) {
+      Button button(int(buttons.Length()), element, IOHIDElementGetLogicalMin(element), IOHIDElementGetLogicalMax(element));
+      buttons.AppendElement(button);
+    } else {
+      //TODO: handle other usage pages
+    }
+  }
+
+  AxisComparator comparator;
+  axes.Sort(comparator);
+  for (unsigned i = 0; i < axes.Length(); i++) {
+    axes[i].id = i;
+  }
+}
+
+class DarwinGamepadService {
+ private:
+  IOHIDManagerRef mManager;
+  vector<Gamepad> mGamepads;
+
+  static void DeviceAddedCallback(void* data, IOReturn result,
+                                  void* sender, IOHIDDeviceRef device);
+  static void DeviceRemovedCallback(void* data, IOReturn result,
+                                    void* sender, IOHIDDeviceRef device);
+  static void InputValueChangedCallback(void* data, IOReturn result,
+                                        void* sender, IOHIDValueRef newValue);
+
+  void DeviceAdded(IOHIDDeviceRef device);
+  void DeviceRemoved(IOHIDDeviceRef device);
+  void InputValueChanged(IOHIDValueRef value);
+
+ public:
+  DarwinGamepadService();
+  ~DarwinGamepadService();
+  void Startup();
+  void Shutdown();
+};
+
+void
+DarwinGamepadService::DeviceAdded(IOHIDDeviceRef device)
+{
+  size_t slot = size_t(-1);
+  for (size_t i = 0; i < mGamepads.size(); i++) {
+    if (mGamepads[i] == device)
+      return;
+    if (slot == size_t(-1) && mGamepads[i].empty())
+      slot = i;
+  }
+
+  if (slot == size_t(-1)) {
+    slot = mGamepads.size();
+    mGamepads.push_back(Gamepad());
+  }
+  mGamepads[slot].init(device);
+
+  // Gather some identifying information
+  CFNumberRef vendorIdRef =
+    (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey));
+  CFNumberRef productIdRef =
+    (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey));
+  CFStringRef productRef =
+    (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
+  int vendorId, productId;
+  CFNumberGetValue(vendorIdRef, kCFNumberIntType, &vendorId);
+  CFNumberGetValue(productIdRef, kCFNumberIntType, &productId);
+  char product_name[128];
+  CFStringGetCString(productRef, product_name,
+                     sizeof(product_name), kCFStringEncodingASCII);
+  char buffer[256];
+  sprintf(buffer, "%x-%x-%s", vendorId, productId, product_name);
+  mGamepads[slot].mSuperIndex = AddGamepad(buffer,
+                                           mozilla::dom::GamepadMappingType::_empty,
+                                           (int)mGamepads[slot].numButtons(),
+                                           (int)mGamepads[slot].numAxes());
+}
+
+void
+DarwinGamepadService::DeviceRemoved(IOHIDDeviceRef device)
+{
+  for (size_t i = 0; i < mGamepads.size(); i++) {
+    if (mGamepads[i] == device) {
+      RemoveGamepad(mGamepads[i].mSuperIndex);
+      mGamepads[i].clear();
+      return;
+    }
+  }
+}
+
+/*
+ * Given a value from a d-pad (POV hat in USB HID terminology),
+ * represent it as 4 buttons, one for each cardinal direction.
+ */
+static void
+UnpackDpad(int dpad_value, int min, int max, dpad_buttons& buttons)
+{
+  const unsigned kUp = 0;
+  const unsigned kDown = 1;
+  const unsigned kLeft = 2;
+  const unsigned kRight = 3;
+
+  // Different controllers have different ways of representing
+  // "nothing is pressed", but they're all outside the range of values.
+  if (dpad_value < min || dpad_value > max) {
+    // Nothing is pressed.
+    return;
+  }
+
+  // Normalize value to start at 0.
+  int value = dpad_value - min;
+
+  // Value will be in the range 0-7. The value represents the
+  // position of the d-pad around a circle, with 0 being straight up,
+  // 2 being right, 4 being straight down, and 6 being left.
+  if (value < 2 || value > 6) {
+    buttons[kUp] = true;
+  }
+  if (value > 2 && value < 6) {
+    buttons[kDown] = true;
+  }
+  if (value > 4) {
+    buttons[kLeft] = true;
+  }
+  if (value > 0 && value < 4) {
+    buttons[kRight] = true;
+  }
+}
+
+void
+DarwinGamepadService::InputValueChanged(IOHIDValueRef value)
+{
+  uint32_t value_length = IOHIDValueGetLength(value);
+  if (value_length > 4) {
+    // Workaround for bizarre issue with PS3 controllers that try to return
+    // massive (30+ byte) values and crash IOHIDValueGetIntegerValue
+    return;
+  }
+  IOHIDElementRef element = IOHIDValueGetElement(value);
+  IOHIDDeviceRef device = IOHIDElementGetDevice(element);
+  for (unsigned i = 0; i < mGamepads.size(); i++) {
+    Gamepad &gamepad = mGamepads[i];
+    if (gamepad == device) {
+      if (gamepad.isDpad(element)) {
+        const dpad_buttons& oldState = gamepad.getDpadState();
+        dpad_buttons newState = { false, false, false, false };
+        UnpackDpad(IOHIDValueGetIntegerValue(value),
+                   IOHIDElementGetLogicalMin(element),
+                   IOHIDElementGetLogicalMax(element),
+                   newState);
+        const int numButtons = gamepad.numButtons();
+        for (unsigned b = 0; b < ArrayLength(newState); b++) {
+          if (newState[b] != oldState[b]) {
+            NewButtonEvent(i, numButtons - 4 + b, newState[b]);
+          }
+        }
+        gamepad.setDpadState(newState);
+      } else if (const Axis* axis = gamepad.lookupAxis(element)) {
+        double d = IOHIDValueGetIntegerValue(value);
+        double v = 2.0f * (d - axis->min) /
+          (double)(axis->max - axis->min) - 1.0f;
+        NewAxisMoveEvent(i, axis->id, v);
+      } else if (const Button* button = gamepad.lookupButton(element)) {
+        int iv = IOHIDValueGetIntegerValue(value);
+        bool pressed = iv != 0;
+        double v = 0;
+        if (button->analog) {
+          double dv = iv;
+          v = (dv - button->min) / (double)(button->max - button->min);
+        } else {
+          v = pressed ? 1.0 : 0.0;
+        }
+        NewButtonEvent(i, button->id, pressed, v);
+      }
+      return;
+    }
+  }
+}
+
+void
+DarwinGamepadService::DeviceAddedCallback(void* data, IOReturn result,
+                                         void* sender, IOHIDDeviceRef device)
+{
+  DarwinGamepadService* service = (DarwinGamepadService*)data;
+  service->DeviceAdded(device);
+}
+
+void
+DarwinGamepadService::DeviceRemovedCallback(void* data, IOReturn result,
+                                           void* sender, IOHIDDeviceRef device)
+{
+  DarwinGamepadService* service = (DarwinGamepadService*)data;
+  service->DeviceRemoved(device);
+}
+
+void
+DarwinGamepadService::InputValueChangedCallback(void* data,
+                                               IOReturn result,
+                                               void* sender,
+                                               IOHIDValueRef newValue)
+{
+  DarwinGamepadService* service = (DarwinGamepadService*)data;
+  service->InputValueChanged(newValue);
+}
+
+static CFMutableDictionaryRef
+MatchingDictionary(UInt32 inUsagePage, UInt32 inUsage)
+{
+  CFMutableDictionaryRef dict =
+    CFDictionaryCreateMutable(kCFAllocatorDefault,
+                              0,
+                              &kCFTypeDictionaryKeyCallBacks,
+                              &kCFTypeDictionaryValueCallBacks);
+  if (!dict)
+    return nullptr;
+  CFNumberRef number = CFNumberCreate(kCFAllocatorDefault,
+                                      kCFNumberIntType,
+                                      &inUsagePage);
+  if (!number) {
+    CFRelease(dict);
+    return nullptr;
+  }
+  CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsagePageKey), number);
+  CFRelease(number);
+
+  number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsage);
+  if (!number) {
+    CFRelease(dict);
+    return nullptr;
+  }
+  CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsageKey), number);
+  CFRelease(number);
+
+  return dict;
+}
+
+DarwinGamepadService::DarwinGamepadService() : mManager(nullptr) {}
+
+DarwinGamepadService::~DarwinGamepadService()
+{
+  if (mManager != nullptr)
+    CFRelease(mManager);
+}
+
+void DarwinGamepadService::Startup()
+{
+  if (mManager != nullptr)
+    return;
+
+  IOHIDManagerRef manager = IOHIDManagerCreate(kCFAllocatorDefault,
+                                               kIOHIDOptionsTypeNone);
+
+  CFMutableDictionaryRef criteria_arr[2];
+  criteria_arr[0] = MatchingDictionary(kDesktopUsagePage,
+                                       kJoystickUsage);
+  if (!criteria_arr[0]) {
+    CFRelease(manager);
+    return;
+  }
+
+  criteria_arr[1] = MatchingDictionary(kDesktopUsagePage,
+                                       kGamepadUsage);
+  if (!criteria_arr[1]) {
+    CFRelease(criteria_arr[0]);
+    CFRelease(manager);
+    return;
+  }
+
+  CFArrayRef criteria =
+    CFArrayCreate(kCFAllocatorDefault, (const void**)criteria_arr, 2, nullptr);
+  if (!criteria) {
+    CFRelease(criteria_arr[1]);
+    CFRelease(criteria_arr[0]);
+    CFRelease(manager);
+    return;
+  }
+
+  IOHIDManagerSetDeviceMatchingMultiple(manager, criteria);
+  CFRelease(criteria);
+  CFRelease(criteria_arr[1]);
+  CFRelease(criteria_arr[0]);
+
+  IOHIDManagerRegisterDeviceMatchingCallback(manager,
+                                             DeviceAddedCallback,
+                                             this);
+  IOHIDManagerRegisterDeviceRemovalCallback(manager,
+                                            DeviceRemovedCallback,
+                                            this);
+  IOHIDManagerRegisterInputValueCallback(manager,
+                                         InputValueChangedCallback,
+                                         this);
+  IOHIDManagerScheduleWithRunLoop(manager,
+                                  CFRunLoopGetCurrent(),
+                                  kCFRunLoopDefaultMode);
+  IOReturn rv = IOHIDManagerOpen(manager, kIOHIDOptionsTypeNone);
+  if (rv != kIOReturnSuccess) {
+    CFRelease(manager);
+    return;
+  }
+
+  mManager = manager;
+}
+
+void DarwinGamepadService::Shutdown()
+{
+  IOHIDManagerRef manager = (IOHIDManagerRef)mManager;
+  if (manager) {
+    IOHIDManagerClose(manager, 0);
+    CFRelease(manager);
+    mManager = nullptr;
+  }
+}
+
+} // namespace
+
+namespace mozilla {
+namespace dom {
+
+DarwinGamepadService* gService = nullptr;
+
+void StartGamepadMonitoring()
+{
+  if (gService) {
+    return;
+  }
+
+  gService = new DarwinGamepadService();
+  gService->Startup();
+}
+
+void StopGamepadMonitoring()
+{
+  if (!gService) {
+    return;
+  }
+
+  gService->Shutdown();
+  delete gService;
+  gService = nullptr;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/fallback/FallbackGamepad.cpp
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et ft=cpp : */
+/* 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 {
+
+void StartGamepadMonitoring()
+{
+}
+
+void StopGamepadMonitoring()
+{
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/linux/LinuxGamepad.cpp
@@ -0,0 +1,371 @@
+/* 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/. */
+
+/*
+ * LinuxGamepadService: A Linux backend for the GamepadService.
+ * Derived from the kernel documentation at
+ * http://www.kernel.org/doc/Documentation/input/joystick-api.txt
+ */
+#include <algorithm>
+#include <cstddef>
+
+#include <glib.h>
+#include <linux/joystick.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include "nscore.h"
+#include "mozilla/dom/GamepadFunctions.h"
+#include "udev.h"
+
+namespace {
+
+using namespace mozilla::dom::GamepadFunctions;
+using mozilla::udev_lib;
+using mozilla::udev_device;
+using mozilla::udev_list_entry;
+using mozilla::udev_enumerate;
+using mozilla::udev_monitor;
+
+static const float kMaxAxisValue = 32767.0;
+static const char kJoystickPath[] = "/dev/input/js";
+
+//TODO: should find a USB identifier for each device so we can
+// provide something that persists across connect/disconnect cycles.
+typedef struct {
+  int index;
+  guint source_id;
+  int numAxes;
+  int numButtons;
+  char idstring[128];
+  char devpath[PATH_MAX];
+} Gamepad;
+
+class LinuxGamepadService {
+public:
+  LinuxGamepadService() : mMonitor(nullptr),
+                          mMonitorSourceID(0) {
+  }
+
+  void Startup();
+  void Shutdown();
+
+private:
+  void AddDevice(struct udev_device* dev);
+  void RemoveDevice(struct udev_device* dev);
+  void ScanForDevices();
+  void AddMonitor();
+  void RemoveMonitor();
+  bool is_gamepad(struct udev_device* dev);
+  void ReadUdevChange();
+
+  // handler for data from /dev/input/jsN
+  static gboolean OnGamepadData(GIOChannel *source,
+                                GIOCondition condition,
+                                gpointer data);
+
+  // handler for data from udev monitor
+  static gboolean OnUdevMonitor(GIOChannel *source,
+                                GIOCondition condition,
+                                gpointer data);
+
+  udev_lib mUdev;
+  struct udev_monitor* mMonitor;
+  guint mMonitorSourceID;
+  // Information about currently connected gamepads.
+  nsAutoTArray<Gamepad,4> mGamepads;
+};
+
+// singleton instance
+LinuxGamepadService* gService = nullptr;
+
+void
+LinuxGamepadService::AddDevice(struct udev_device* dev)
+{
+  const char* devpath = mUdev.udev_device_get_devnode(dev);
+  if (!devpath) {
+    return;
+  }
+
+  // Ensure that this device hasn't already been added.
+  for (unsigned int i = 0; i < mGamepads.Length(); i++) {
+    if (strcmp(mGamepads[i].devpath, devpath) == 0) {
+      return;
+    }
+  }
+
+  Gamepad gamepad;
+  snprintf(gamepad.devpath, sizeof(gamepad.devpath), "%s", devpath);
+  GIOChannel* channel = g_io_channel_new_file(devpath, "r", nullptr);
+  if (!channel) {
+    return;
+  }
+
+  g_io_channel_set_flags(channel, G_IO_FLAG_NONBLOCK, nullptr);
+  g_io_channel_set_encoding(channel, nullptr, nullptr);
+  g_io_channel_set_buffered(channel, FALSE);
+  int fd = g_io_channel_unix_get_fd(channel);
+  char name[128];
+  if (ioctl(fd, JSIOCGNAME(sizeof(name)), &name) == -1) {
+    strcpy(name, "unknown");
+  }
+  const char* vendor_id =
+    mUdev.udev_device_get_property_value(dev, "ID_VENDOR_ID");
+  const char* model_id =
+    mUdev.udev_device_get_property_value(dev, "ID_MODEL_ID");
+  if (!vendor_id || !model_id) {
+    struct udev_device* parent =
+      mUdev.udev_device_get_parent_with_subsystem_devtype(dev,
+                                                          "input",
+                                                          nullptr);
+    if (parent) {
+      vendor_id = mUdev.udev_device_get_sysattr_value(parent, "id/vendor");
+      model_id = mUdev.udev_device_get_sysattr_value(parent, "id/product");
+    }
+  }
+  snprintf(gamepad.idstring, sizeof(gamepad.idstring),
+           "%s-%s-%s",
+           vendor_id ? vendor_id : "unknown",
+           model_id ? model_id : "unknown",
+           name);
+
+  char numAxes = 0, numButtons = 0;
+  ioctl(fd, JSIOCGAXES, &numAxes);
+  gamepad.numAxes = numAxes;
+  ioctl(fd, JSIOCGBUTTONS, &numButtons);
+  gamepad.numButtons = numButtons;
+
+  gamepad.index = AddGamepad(gamepad.idstring,
+                             mozilla::dom::GamepadMappingType::_empty,
+                             gamepad.numButtons,
+                             gamepad.numAxes);
+
+  gamepad.source_id =
+    g_io_add_watch(channel,
+                   GIOCondition(G_IO_IN | G_IO_ERR | G_IO_HUP),
+                   OnGamepadData,
+                   GINT_TO_POINTER(gamepad.index));
+  g_io_channel_unref(channel);
+
+  mGamepads.AppendElement(gamepad);
+}
+
+void
+LinuxGamepadService::RemoveDevice(struct udev_device* dev)
+{
+  const char* devpath = mUdev.udev_device_get_devnode(dev);
+  if (!devpath) {
+    return;
+  }
+
+  for (unsigned int i = 0; i < mGamepads.Length(); i++) {
+    if (strcmp(mGamepads[i].devpath, devpath) == 0) {
+      g_source_remove(mGamepads[i].source_id);
+      RemoveGamepad(mGamepads[i].index);
+      mGamepads.RemoveElementAt(i);
+      break;
+    }
+  }
+}
+
+void
+LinuxGamepadService::ScanForDevices()
+{
+  struct udev_enumerate* en = mUdev.udev_enumerate_new(mUdev.udev);
+  mUdev.udev_enumerate_add_match_subsystem(en, "input");
+  mUdev.udev_enumerate_scan_devices(en);
+
+  struct udev_list_entry* dev_list_entry;
+  for (dev_list_entry = mUdev.udev_enumerate_get_list_entry(en);
+       dev_list_entry != nullptr;
+       dev_list_entry = mUdev.udev_list_entry_get_next(dev_list_entry)) {
+    const char* path = mUdev.udev_list_entry_get_name(dev_list_entry);
+    struct udev_device* dev = mUdev.udev_device_new_from_syspath(mUdev.udev,
+                                                                 path);
+    if (is_gamepad(dev)) {
+      AddDevice(dev);
+    }
+
+    mUdev.udev_device_unref(dev);
+  }
+
+  mUdev.udev_enumerate_unref(en);
+}
+
+void
+LinuxGamepadService::AddMonitor()
+{
+  // Add a monitor to watch for device changes
+  mMonitor =
+    mUdev.udev_monitor_new_from_netlink(mUdev.udev, "udev");
+  if (!mMonitor) {
+    // Not much we can do here.
+    return;
+  }
+  mUdev.udev_monitor_filter_add_match_subsystem_devtype(mMonitor,
+                                                        "input",
+							nullptr);
+
+  int monitor_fd = mUdev.udev_monitor_get_fd(mMonitor);
+  GIOChannel* monitor_channel = g_io_channel_unix_new(monitor_fd);
+  mMonitorSourceID =
+    g_io_add_watch(monitor_channel,
+                   GIOCondition(G_IO_IN | G_IO_ERR | G_IO_HUP),
+                   OnUdevMonitor,
+                   nullptr);
+  g_io_channel_unref(monitor_channel);
+
+  mUdev.udev_monitor_enable_receiving(mMonitor);
+}
+
+void
+LinuxGamepadService::RemoveMonitor()
+{
+  if (mMonitorSourceID) {
+    g_source_remove(mMonitorSourceID);
+    mMonitorSourceID = 0;
+  }
+  if (mMonitor) {
+    mUdev.udev_monitor_unref(mMonitor);
+    mMonitor = nullptr;
+  }
+}
+
+void
+LinuxGamepadService::Startup()
+{
+  // Don't bother starting up if libudev couldn't be loaded or initialized.
+  if (!mUdev)
+    return;
+
+  AddMonitor();
+  ScanForDevices();
+}
+
+void
+LinuxGamepadService::Shutdown()
+{
+  for (unsigned int i = 0; i < mGamepads.Length(); i++) {
+    g_source_remove(mGamepads[i].source_id);
+  }
+  mGamepads.Clear();
+  RemoveMonitor();
+}
+
+bool
+LinuxGamepadService::is_gamepad(struct udev_device* dev)
+{
+  if (!mUdev.udev_device_get_property_value(dev, "ID_INPUT_JOYSTICK"))
+    return false;
+
+  const char* devpath = mUdev.udev_device_get_devnode(dev);
+  if (!devpath) {
+    return false;
+  }
+  if (strncmp(kJoystickPath, devpath, sizeof(kJoystickPath) - 1) != 0) {
+    return false;
+  }
+
+  return true;
+}
+
+void
+LinuxGamepadService::ReadUdevChange()
+{
+  struct udev_device* dev =
+    mUdev.udev_monitor_receive_device(mMonitor);
+  const char* action = mUdev.udev_device_get_action(dev);
+  if (is_gamepad(dev)) {
+    if (strcmp(action, "add") == 0) {
+      AddDevice(dev);
+    } else if (strcmp(action, "remove") == 0) {
+      RemoveDevice(dev);
+    }
+  }
+  mUdev.udev_device_unref(dev);
+}
+
+// static
+gboolean
+LinuxGamepadService::OnGamepadData(GIOChannel* source,
+                                   GIOCondition condition,
+                                   gpointer data)
+{
+  int index = GPOINTER_TO_INT(data);
+  //TODO: remove gamepad?
+  if (condition & G_IO_ERR || condition & G_IO_HUP)
+    return FALSE;
+
+  while (true) {
+    struct js_event event;
+    gsize count;
+    GError* err = nullptr;
+    if (g_io_channel_read_chars(source,
+				(gchar*)&event,
+				sizeof(event),
+				&count,
+				&err) != G_IO_STATUS_NORMAL ||
+	count == 0) {
+      break;
+    }
+
+    //TODO: store device state?
+    if (event.type & JS_EVENT_INIT) {
+      continue;
+    }
+
+    switch (event.type) {
+    case JS_EVENT_BUTTON:
+      NewButtonEvent(index, event.number, !!event.value);
+      break;
+    case JS_EVENT_AXIS:
+      NewAxisMoveEvent(index, event.number,
+                       ((float)event.value) / kMaxAxisValue);
+      break;
+    }
+  }
+
+  return TRUE;
+}
+
+// static
+gboolean
+LinuxGamepadService::OnUdevMonitor(GIOChannel* source,
+                                   GIOCondition condition,
+                                   gpointer data)
+{
+  if (condition & G_IO_ERR || condition & G_IO_HUP)
+    return FALSE;
+
+  gService->ReadUdevChange();
+  return TRUE;
+}
+
+} // namespace
+
+namespace mozilla {
+namespace dom {
+
+void StartGamepadMonitoring()
+{
+  if (gService) {
+    return;
+  }
+  gService = new LinuxGamepadService();
+  gService->Startup();
+}
+
+void StopGamepadMonitoring()
+{
+  if (!gService) {
+    return;
+  }
+  gService->Shutdown();
+  delete gService;
+  gService = nullptr;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/linux/udev.h
@@ -0,0 +1,148 @@
+/* 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/. */
+
+/*
+ * This file defines a wrapper around libudev so we can avoid
+ * linking directly to it and use dlopen instead.
+ */
+
+#ifndef HAL_LINUX_UDEV_H_
+#define HAL_LINUX_UDEV_H_
+
+#include <dlfcn.h>
+
+#include "mozilla/ArrayUtils.h"
+
+namespace mozilla {
+
+struct udev;
+struct udev_device;
+struct udev_enumerate;
+struct udev_list_entry;
+struct udev_monitor;
+
+class udev_lib {
+ public:
+  udev_lib() : lib(nullptr),
+               udev(nullptr) {
+    // Be careful about ABI compat! 0 -> 1 didn't change any
+    // symbols this code relies on, per:
+    // https://lists.fedoraproject.org/pipermail/devel/2012-June/168227.html
+    const char* lib_names[] = {"libudev.so.0", "libudev.so.1"};
+    // Check whether a library is already loaded so we don't load two
+    // conflicting libs.
+    for (unsigned i = 0; i < ArrayLength(lib_names); i++) {
+      lib = dlopen(lib_names[i], RTLD_NOLOAD | RTLD_LAZY | RTLD_GLOBAL);
+      if (lib) {
+        break;
+      }
+    }
+    // If nothing loads the first time through, it means no version of libudev
+    // was already loaded.
+    if (!lib) {
+      for (unsigned i = 0; i < ArrayLength(lib_names); i++) {
+        lib = dlopen(lib_names[i], RTLD_LAZY | RTLD_GLOBAL);
+        if (lib) {
+          break;
+        }
+      }
+    }
+    if (lib && LoadSymbols()) {
+      udev = udev_new();
+    }
+  }
+
+  ~udev_lib() {
+    if (udev) {
+      udev_unref(udev);
+    }
+
+    if (lib) {
+      dlclose(lib);
+    }
+  }
+
+  explicit operator bool() {
+    return udev;
+  }
+
+ private:
+  bool LoadSymbols() {
+#define DLSYM(s) \
+  do { \
+    s = (typeof(s))dlsym(lib, #s); \
+    if (!s) return false; \
+  } while (0)
+
+    DLSYM(udev_new);
+    DLSYM(udev_unref);
+    DLSYM(udev_device_unref);
+    DLSYM(udev_device_new_from_syspath);
+    DLSYM(udev_device_get_devnode);
+    DLSYM(udev_device_get_parent_with_subsystem_devtype);
+    DLSYM(udev_device_get_property_value);
+    DLSYM(udev_device_get_action);
+    DLSYM(udev_device_get_sysattr_value);
+    DLSYM(udev_enumerate_new);
+    DLSYM(udev_enumerate_unref);
+    DLSYM(udev_enumerate_add_match_subsystem);
+    DLSYM(udev_enumerate_scan_devices);
+    DLSYM(udev_enumerate_get_list_entry);
+    DLSYM(udev_list_entry_get_next);
+    DLSYM(udev_list_entry_get_name);
+    DLSYM(udev_monitor_new_from_netlink);
+    DLSYM(udev_monitor_filter_add_match_subsystem_devtype);
+    DLSYM(udev_monitor_enable_receiving);
+    DLSYM(udev_monitor_get_fd);
+    DLSYM(udev_monitor_receive_device);
+    DLSYM(udev_monitor_unref);
+#undef DLSYM
+    return true;
+  }
+
+  void* lib;
+
+ public:
+  struct udev* udev;
+
+  // Function pointers returned from dlsym.
+  struct udev* (*udev_new)(void);
+  void (*udev_unref)(struct udev*);
+
+  void (*udev_device_unref)(struct udev_device*);
+  struct udev_device* (*udev_device_new_from_syspath)(struct udev*,
+                                                      const char*);
+  const char* (*udev_device_get_devnode)(struct udev_device*);
+  struct udev_device* (*udev_device_get_parent_with_subsystem_devtype)
+    (struct udev_device*, const char*, const char*);
+  const char* (*udev_device_get_property_value)(struct udev_device*,
+                                                const char*);
+  const char* (*udev_device_get_action)(struct udev_device*);
+  const char* (*udev_device_get_sysattr_value)(struct udev_device*,
+                                               const char*);
+
+  struct udev_enumerate* (*udev_enumerate_new)(struct udev*);
+  void (*udev_enumerate_unref)(struct udev_enumerate*);
+  int (*udev_enumerate_add_match_subsystem)(struct udev_enumerate*,
+                                            const char*);
+  int (*udev_enumerate_scan_devices)(struct udev_enumerate*);
+  struct udev_list_entry* (*udev_enumerate_get_list_entry)
+    (struct udev_enumerate*);
+
+  struct udev_list_entry* (*udev_list_entry_get_next)(struct udev_list_entry *);
+  const char* (*udev_list_entry_get_name)(struct udev_list_entry*);
+
+  struct udev_monitor* (*udev_monitor_new_from_netlink)(struct udev*,
+                                                        const char*);
+  int (*udev_monitor_filter_add_match_subsystem_devtype)
+    (struct udev_monitor*, const char*, const char*);
+  int (*udev_monitor_enable_receiving)(struct udev_monitor*);
+  int (*udev_monitor_get_fd)(struct udev_monitor*);
+  struct udev_device* (*udev_monitor_receive_device)(struct udev_monitor*);
+  void (*udev_monitor_unref)(struct udev_monitor*);
+};
+
+} // namespace mozilla
+
+#endif // HAL_LINUX_UDEV_H_
--- a/dom/gamepad/moz.build
+++ b/dom/gamepad/moz.build
@@ -2,26 +2,57 @@
 # 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/.
 
 EXPORTS.mozilla.dom += [
     'Gamepad.h',
     'GamepadButton.h',
+    'GamepadFunctions.h',
+    'GamepadMonitoring.h',
     'GamepadService.h',
+    'GamepadServiceTest.h'
     ]
 
 UNIFIED_SOURCES = [
     'Gamepad.cpp',
     'GamepadButton.cpp',
+    'GamepadFunctions.cpp',
+    'GamepadMonitoring.cpp',
     'GamepadService.cpp',
+    'GamepadServiceTest.cpp'
+    ]
+
+if CONFIG['MOZ_GAMEPAD_BACKEND'] == 'stub':
+    UNIFIED_SOURCES += [
+        'fallback/FallbackGamepad.cpp'
+    ]
+elif CONFIG['MOZ_GAMEPAD_BACKEND'] == 'cocoa':
+    UNIFIED_SOURCES += [
+        'cocoa/CocoaGamepad.cpp'
+    ]
+elif CONFIG['MOZ_GAMEPAD_BACKEND'] == 'windows':
+    UNIFIED_SOURCES += [
+        'windows/WindowsGamepad.cpp'
+    ]
+elif CONFIG['MOZ_GAMEPAD_BACKEND'] == 'linux':
+    UNIFIED_SOURCES += [
+        'linux/LinuxGamepad.cpp'
+    ]
+elif CONFIG['MOZ_GAMEPAD_BACKEND'] == 'android':
+    UNIFIED_SOURCES += [
+        'android/AndroidGamepad.cpp'
     ]
 
 FAIL_ON_WARNINGS = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '/dom/base',
 ]
 
+CFLAGS += CONFIG['GLIB_CFLAGS']
+CFLAGS += CONFIG['MOZ_DBUS_GLIB_CFLAGS']
+CXXFLAGS += CONFIG['GLIB_CFLAGS']
+CXXFLAGS += CONFIG['MOZ_DBUS_GLIB_CFLAGS']
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/windows/WindowsGamepad.cpp
@@ -0,0 +1,1011 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 <algorithm>
+#include <cstddef>
+
+#ifndef UNICODE
+#define UNICODE
+#endif
+#include <windows.h>
+#include <hidsdi.h>
+#include <stdio.h>
+#include <xinput.h>
+
+#include "nsIComponentManager.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsITimer.h"
+#include "nsTArray.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/dom/GamepadFunctions.h"
+#include "mozilla/Services.h"
+
+namespace {
+
+using namespace mozilla::dom;
+using namespace mozilla::dom::GamepadFunctions;
+using mozilla::ArrayLength;
+
+// USB HID usage tables, page 1 (Hat switch)
+const unsigned kUsageDpad = 0x39;
+// USB HID usage tables, page 1, 0x30 = X
+const unsigned kFirstAxis = 0x30;
+
+// USB HID usage tables
+const unsigned kDesktopUsagePage = 0x1;
+const unsigned kButtonUsagePage = 0x9;
+
+// Arbitrary. In practice 10 buttons/6 axes is the near maximum.
+const unsigned kMaxButtons = 32;
+const unsigned kMaxAxes = 32;
+
+// Multiple devices-changed notifications can be sent when a device
+// is connected, because USB devices consist of multiple logical devices.
+// Therefore, we wait a bit after receiving one before looking for
+// device changes.
+const uint32_t kDevicesChangedStableDelay = 200;
+// XInput is a purely polling-driven API, so we need to
+// poll it periodically. 50ms is arbitrarily chosen.
+const uint32_t kXInputPollInterval = 50;
+
+const UINT kRawInputError = (UINT)-1;
+
+#ifndef XUSER_MAX_COUNT
+#define XUSER_MAX_COUNT 4
+#endif
+
+const struct {
+  int usagePage;
+  int usage;
+} kUsagePages[] = {
+  // USB HID usage tables, page 1
+  { kDesktopUsagePage, 4 },  // Joystick
+  { kDesktopUsagePage, 5 }   // Gamepad
+};
+
+const struct {
+  WORD button;
+  int mapped;
+} kXIButtonMap[] = {
+  { XINPUT_GAMEPAD_DPAD_UP, 12 },
+  { XINPUT_GAMEPAD_DPAD_DOWN, 13 },
+  { XINPUT_GAMEPAD_DPAD_LEFT, 14 },
+  { XINPUT_GAMEPAD_DPAD_RIGHT, 15 },
+  { XINPUT_GAMEPAD_START, 9 },
+  { XINPUT_GAMEPAD_BACK, 8 },
+  { XINPUT_GAMEPAD_LEFT_THUMB, 10 },
+  { XINPUT_GAMEPAD_RIGHT_THUMB, 11 },
+  { XINPUT_GAMEPAD_LEFT_SHOULDER, 4 },
+  { XINPUT_GAMEPAD_RIGHT_SHOULDER, 5 },
+  { XINPUT_GAMEPAD_A, 0 },
+  { XINPUT_GAMEPAD_B, 1 },
+  { XINPUT_GAMEPAD_X, 2 },
+  { XINPUT_GAMEPAD_Y, 3 }
+};
+const size_t kNumMappings = ArrayLength(kXIButtonMap);
+
+enum GamepadType {
+  kNoGamepad = 0,
+  kRawInputGamepad,
+  kXInputGamepad
+};
+
+class WindowsGamepadService;
+WindowsGamepadService* gService = nullptr;
+
+struct Gamepad {
+  GamepadType type;
+
+  // Handle to raw input device
+  HANDLE handle;
+
+  // XInput Index of the user's controller. Passed to XInputGetState.
+  DWORD userIndex;
+
+  // Last-known state of the controller.
+  XINPUT_STATE state;
+
+  // ID from the GamepadService, also used as the index into
+  // WindowsGamepadService::mGamepads.
+  int id;
+
+  // Information about the physical device.
+  unsigned numAxes;
+  unsigned numButtons;
+  bool hasDpad;
+  HIDP_VALUE_CAPS dpadCaps;
+
+  bool buttons[kMaxButtons];
+  struct {
+    HIDP_VALUE_CAPS caps;
+    double value;
+  } axes[kMaxAxes];
+
+  // Used during rescan to find devices that were disconnected.
+  bool present;
+};
+
+// Drop this in favor of decltype when we require a new enough SDK.
+typedef void (WINAPI *XInputEnable_func)(BOOL);
+
+// RAII class to wrap loading the XInput DLL
+class XInputLoader {
+public:
+  XInputLoader() : module(nullptr),
+                   mXInputEnable(nullptr),
+                   mXInputGetState(nullptr) {
+    // xinput1_4.dll exists on Windows 8
+    // xinput9_1_0.dll exists on Windows 7 and Vista
+    // xinput1_3.dll shipped with the DirectX SDK
+    const wchar_t* dlls[] = {L"xinput1_4.dll",
+                             L"xinput9_1_0.dll",
+                             L"xinput1_3.dll"};
+    const size_t kNumDLLs = ArrayLength(dlls);
+    for (size_t i = 0; i < kNumDLLs; ++i) {
+      module = LoadLibraryW(dlls[i]);
+      if (module) {
+        mXInputEnable = reinterpret_cast<XInputEnable_func>(
+         GetProcAddress(module, "XInputEnable"));
+        mXInputGetState = reinterpret_cast<decltype(XInputGetState)*>(
+         GetProcAddress(module, "XInputGetState"));
+        if (mXInputEnable) {
+          mXInputEnable(TRUE);
+        }
+        break;
+      }
+    }
+  }
+
+  ~XInputLoader() {
+    //mXInputEnable = nullptr;
+    mXInputGetState = nullptr;
+
+    if (module) {
+      FreeLibrary(module);
+    }
+  }
+
+  operator bool() {
+    return module && mXInputGetState;
+  }
+
+  HMODULE module;
+  decltype(XInputGetState) *mXInputGetState;
+  XInputEnable_func mXInputEnable;
+};
+
+bool
+GetPreparsedData(HANDLE handle, nsTArray<uint8_t>& data)
+{
+  UINT size;
+  if (GetRawInputDeviceInfo(handle, RIDI_PREPARSEDDATA, nullptr, &size) == kRawInputError) {
+    return false;
+  }
+  data.SetLength(size);
+  return GetRawInputDeviceInfo(handle, RIDI_PREPARSEDDATA,
+                               data.Elements(), &size) > 0;
+}
+
+/*
+ * Given an axis value and a minimum and maximum range,
+ * scale it to be in the range -1.0 .. 1.0.
+ */
+double
+ScaleAxis(ULONG value, LONG min, LONG max)
+{
+  return  2.0 * (value - min) / (max - min) - 1.0;
+}
+
+/*
+ * Given a value from a d-pad (POV hat in USB HID terminology),
+ * represent it as 4 buttons, one for each cardinal direction.
+ */
+void
+UnpackDpad(LONG dpad_value, const Gamepad* gamepad, bool buttons[kMaxButtons])
+{
+  const unsigned kUp = gamepad->numButtons - 4;
+  const unsigned kDown = gamepad->numButtons - 3;
+  const unsigned kLeft = gamepad->numButtons - 2;
+  const unsigned kRight = gamepad->numButtons - 1;
+
+  // Different controllers have different ways of representing
+  // "nothing is pressed", but they're all outside the range of values.
+  if (dpad_value < gamepad->dpadCaps.LogicalMin
+      || dpad_value > gamepad->dpadCaps.LogicalMax) {
+    // Nothing is pressed.
+    return;
+  }
+
+  // Normalize value to start at 0.
+  int value = dpad_value - gamepad->dpadCaps.LogicalMin;
+
+  // Value will be in the range 0-7. The value represents the
+  // position of the d-pad around a circle, with 0 being straight up,
+  // 2 being right, 4 being straight down, and 6 being left.
+  if (value < 2 || value > 6) {
+    buttons[kUp] = true;
+  }
+  if (value > 2 && value < 6) {
+    buttons[kDown] = true;
+  }
+  if (value > 4) {
+    buttons[kLeft] = true;
+  }
+  if (value > 0 && value < 4) {
+    buttons[kRight] = true;
+  }
+}
+
+/*
+ * Return true if this USB HID usage page and usage are of a type we
+ * know how to handle.
+ */
+bool
+SupportedUsage(USHORT page, USHORT usage)
+{
+  for (unsigned i = 0; i < ArrayLength(kUsagePages); i++) {
+    if (page == kUsagePages[i].usagePage && usage == kUsagePages[i].usage) {
+      return true;
+    }
+  }
+  return false;
+}
+
+class Observer : public nsIObserver {
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+  Observer(WindowsGamepadService& svc) : mSvc(svc),
+                                         mObserving(true)
+  {
+    nsresult rv;
+    mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
+    nsCOMPtr<nsIObserverService> observerService =
+      mozilla::services::GetObserverService();
+    observerService->AddObserver(this,
+                                 NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID,
+                                 false);
+  }
+
+  void Stop()
+  {
+    if (mTimer) {
+      mTimer->Cancel();
+    }
+    if (mObserving) {
+      nsCOMPtr<nsIObserverService> observerService =
+        mozilla::services::GetObserverService();
+      observerService->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
+      mObserving = false;
+    }
+  }
+
+  void SetDeviceChangeTimer()
+  {
+    // Set stable timer, since we will get multiple devices-changed
+    // notifications at once
+    if (mTimer) {
+      mTimer->Cancel();
+      mTimer->Init(this, kDevicesChangedStableDelay, nsITimer::TYPE_ONE_SHOT);
+    }
+  }
+
+private:
+  virtual ~Observer()
+  {
+    Stop();
+  }
+
+  // Gamepad service owns us, we just hold a reference back to it.
+  WindowsGamepadService& mSvc;
+  nsCOMPtr<nsITimer> mTimer;
+  bool mObserving;
+};
+
+NS_IMPL_ISUPPORTS(Observer, nsIObserver);
+
+class HIDLoader {
+public:
+  HIDLoader() : mModule(LoadLibraryW(L"hid.dll")),
+                mHidD_GetProductString(nullptr),
+                mHidP_GetCaps(nullptr),
+                mHidP_GetButtonCaps(nullptr),
+                mHidP_GetValueCaps(nullptr),
+                mHidP_GetUsages(nullptr),
+                mHidP_GetUsageValue(nullptr),
+                mHidP_GetScaledUsageValue(nullptr)
+  {
+    if (mModule) {
+      mHidD_GetProductString = reinterpret_cast<decltype(HidD_GetProductString)*>(GetProcAddress(mModule, "HidD_GetProductString"));
+      mHidP_GetCaps = reinterpret_cast<decltype(HidP_GetCaps)*>(GetProcAddress(mModule, "HidP_GetCaps"));
+      mHidP_GetButtonCaps = reinterpret_cast<decltype(HidP_GetButtonCaps)*>(GetProcAddress(mModule, "HidP_GetButtonCaps"));
+      mHidP_GetValueCaps = reinterpret_cast<decltype(HidP_GetValueCaps)*>(GetProcAddress(mModule, "HidP_GetValueCaps"));
+      mHidP_GetUsages = reinterpret_cast<decltype(HidP_GetUsages)*>(GetProcAddress(mModule, "HidP_GetUsages"));
+      mHidP_GetUsageValue = reinterpret_cast<decltype(HidP_GetUsageValue)*>(GetProcAddress(mModule, "HidP_GetUsageValue"));
+      mHidP_GetScaledUsageValue = reinterpret_cast<decltype(HidP_GetScaledUsageValue)*>(GetProcAddress(mModule, "HidP_GetScaledUsageValue"));
+    }
+  }
+
+  ~HIDLoader() {
+    if (mModule) {
+      FreeLibrary(mModule);
+    }
+  }
+
+  operator bool() {
+    return mModule &&
+      mHidD_GetProductString &&
+      mHidP_GetCaps &&
+      mHidP_GetButtonCaps &&
+      mHidP_GetValueCaps &&
+      mHidP_GetUsages &&
+      mHidP_GetUsageValue &&
+      mHidP_GetScaledUsageValue;
+  }
+
+  decltype(HidD_GetProductString) *mHidD_GetProductString;
+  decltype(HidP_GetCaps) *mHidP_GetCaps;
+  decltype(HidP_GetButtonCaps) *mHidP_GetButtonCaps;
+  decltype(HidP_GetValueCaps) *mHidP_GetValueCaps;
+  decltype(HidP_GetUsages) *mHidP_GetUsages;
+  decltype(HidP_GetUsageValue) *mHidP_GetUsageValue;
+  decltype(HidP_GetScaledUsageValue) *mHidP_GetScaledUsageValue;
+
+private:
+  HMODULE mModule;
+};
+
+class WindowsGamepadService {
+public:
+  WindowsGamepadService();
+  virtual ~WindowsGamepadService()
+  {
+    Cleanup();
+  }
+
+  enum DeviceChangeType {
+    DeviceChangeNotification,
+    DeviceChangeStable
+  };
+  void DevicesChanged(DeviceChangeType type);
+  void Startup();
+  void Shutdown();
+  // Parse gamepad input from a WM_INPUT message.
+  bool HandleRawInput(HRAWINPUT handle);
+
+private:
+  void ScanForDevices();
+  // Look for connected raw input devices.
+  void ScanForRawInputDevices();
+  // Look for connected XInput devices.
+  bool ScanForXInputDevices();
+  bool HaveXInputGamepad(int userIndex);
+
+  // Timer callback for XInput polling
+  static void XInputPollTimerCallback(nsITimer* aTimer, void* aClosure);
+  void PollXInput();
+  void CheckXInputChanges(Gamepad& gamepad, XINPUT_STATE& state);
+
+  // Get information about a raw input gamepad.
+  bool GetRawGamepad(HANDLE handle);
+  void Cleanup();
+
+  // List of connected devices.
+  nsTArray<Gamepad> mGamepads;
+
+  nsRefPtr<Observer> mObserver;
+  nsCOMPtr<nsITimer> mXInputPollTimer;
+
+  HIDLoader mHID;
+  XInputLoader mXInput;
+};
+
+
+WindowsGamepadService::WindowsGamepadService()
+{
+  nsresult rv;
+  mXInputPollTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
+  mObserver = new Observer(*this);
+}
+
+void
+WindowsGamepadService::ScanForRawInputDevices()
+{
+  if (!mHID) {
+    return;
+  }
+
+  UINT numDevices;
+  if (GetRawInputDeviceList(nullptr, &numDevices, sizeof(RAWINPUTDEVICELIST))
+      == kRawInputError) {
+    return;
+  }
+  nsTArray<RAWINPUTDEVICELIST> devices(numDevices);
+  devices.SetLength(numDevices);
+  if (GetRawInputDeviceList(devices.Elements(), &numDevices,
+                            sizeof(RAWINPUTDEVICELIST)) == kRawInputError) {
+    return;
+  }
+
+  for (unsigned i = 0; i < devices.Length(); i++) {
+    if (devices[i].dwType == RIM_TYPEHID) {
+      GetRawGamepad(devices[i].hDevice);
+    }
+  }
+}
+
+bool
+WindowsGamepadService::HaveXInputGamepad(int userIndex)
+{
+  for (unsigned int i = 0; i < mGamepads.Length(); i++) {
+    if (mGamepads[i].type == kXInputGamepad
+        && mGamepads[i].userIndex == userIndex) {
+      mGamepads[i].present = true;
+      return true;
+    }
+  }
+  return false;
+}
+
+bool
+WindowsGamepadService::ScanForXInputDevices()
+{
+  MOZ_ASSERT(mXInput, "XInput should be present!");
+
+  bool found = false;
+  for (int i = 0; i < XUSER_MAX_COUNT; i++) {
+    XINPUT_STATE state = {};
+    if (mXInput.mXInputGetState(i, &state) != ERROR_SUCCESS) {
+      continue;
+    }
+    found = true;
+    // See if this device is already present in our list.
+    if (HaveXInputGamepad(i)) {
+      continue;
+    }
+
+    // Not already present, add it.
+    Gamepad gamepad = {};
+    gamepad.type = kXInputGamepad;
+    gamepad.present = true;
+    gamepad.state = state;
+    gamepad.userIndex = i;
+    gamepad.numButtons = kStandardGamepadButtons;
+    gamepad.numAxes = kStandardGamepadAxes;
+    gamepad.id = AddGamepad("xinput",
+                            GamepadMappingType::Standard,
+                            kStandardGamepadButtons,
+                            kStandardGamepadAxes);
+    mGamepads.AppendElement(gamepad);
+  }
+
+  return found;
+}
+
+void
+WindowsGamepadService::ScanForDevices()
+{
+  for (int i = mGamepads.Length() - 1; i >= 0; i--) {
+    mGamepads[i].present = false;
+  }
+
+  if (mHID) {
+    ScanForRawInputDevices();
+  }
+  if (mXInput) {
+    mXInputPollTimer->Cancel();
+    if (ScanForXInputDevices()) {
+      mXInputPollTimer->InitWithFuncCallback(XInputPollTimerCallback,
+                                             this,
+                                             kXInputPollInterval,
+                                             nsITimer::TYPE_REPEATING_SLACK);
+    }
+  }
+
+  // Look for devices that are no longer present and remove them.
+  for (int i = mGamepads.Length() - 1; i >= 0; i--) {
+    if (!mGamepads[i].present) {
+      RemoveGamepad(mGamepads[i].id);
+      mGamepads.RemoveElementAt(i);
+    }
+  }
+}
+
+// static
+void
+WindowsGamepadService::XInputPollTimerCallback(nsITimer* aTimer,
+                                               void* aClosure)
+{
+  WindowsGamepadService* self =
+    reinterpret_cast<WindowsGamepadService*>(aClosure);
+  self->PollXInput();
+}
+
+void
+WindowsGamepadService::PollXInput()
+{
+  for (unsigned int i = 0; i < mGamepads.Length(); i++) {
+    if (mGamepads[i].type != kXInputGamepad) {
+      continue;
+    }
+
+    XINPUT_STATE state = {};
+    DWORD res = mXInput.mXInputGetState(mGamepads[i].userIndex, &state);
+    if (res == ERROR_SUCCESS
+        && state.dwPacketNumber != mGamepads[i].state.dwPacketNumber) {
+        CheckXInputChanges(mGamepads[i], state);
+    }
+  }
+}
+
+void WindowsGamepadService::CheckXInputChanges(Gamepad& gamepad,
+                                               XINPUT_STATE& state) {
+  // Handle digital buttons first
+  for (size_t b = 0; b < kNumMappings; b++) {
+    if (state.Gamepad.wButtons & kXIButtonMap[b].button &&
+        !(gamepad.state.Gamepad.wButtons & kXIButtonMap[b].button)) {
+      // Button pressed
+      NewButtonEvent(gamepad.id, kXIButtonMap[b].mapped, true);
+    } else if (!(state.Gamepad.wButtons & kXIButtonMap[b].button) &&
+               gamepad.state.Gamepad.wButtons & kXIButtonMap[b].button) {
+      // Button released
+      NewButtonEvent(gamepad.id, kXIButtonMap[b].mapped, false);
+    }
+  }
+
+  // Then triggers
+  if (state.Gamepad.bLeftTrigger != gamepad.state.Gamepad.bLeftTrigger) {
+    bool pressed =
+      state.Gamepad.bLeftTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
+    NewButtonEvent(gamepad.id, kButtonLeftTrigger,
+                   pressed, state.Gamepad.bLeftTrigger / 255.0);
+  }
+  if (state.Gamepad.bRightTrigger != gamepad.state.Gamepad.bRightTrigger) {
+    bool pressed =
+      state.Gamepad.bRightTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
+    NewButtonEvent(gamepad.id, kButtonRightTrigger,
+                   pressed, state.Gamepad.bRightTrigger / 255.0);
+  }
+
+  // Finally deal with analog sticks
+  // TODO: bug 1001955 - Support deadzones.
+  if (state.Gamepad.sThumbLX != gamepad.state.Gamepad.sThumbLX) {
+    NewAxisMoveEvent(gamepad.id, kLeftStickXAxis,
+                     state.Gamepad.sThumbLX / 32767.0);
+  }
+  if (state.Gamepad.sThumbLY != gamepad.state.Gamepad.sThumbLY) {
+    NewAxisMoveEvent(gamepad.id, kLeftStickYAxis,
+                     -1.0 * state.Gamepad.sThumbLY / 32767.0);
+  }
+  if (state.Gamepad.sThumbRX != gamepad.state.Gamepad.sThumbRX) {
+    NewAxisMoveEvent(gamepad.id, kRightStickXAxis,
+                     state.Gamepad.sThumbRX / 32767.0);
+  }
+  if (state.Gamepad.sThumbRY != gamepad.state.Gamepad.sThumbRY) {
+    NewAxisMoveEvent(gamepad.id, kRightStickYAxis,
+                     -1.0 * state.Gamepad.sThumbRY / 32767.0);
+  }
+  gamepad.state = state;
+}
+
+// Used to sort a list of axes by HID usage.
+class HidValueComparator {
+public:
+  bool Equals(const HIDP_VALUE_CAPS& c1, const HIDP_VALUE_CAPS& c2) const
+  {
+    return c1.UsagePage == c2.UsagePage && c1.Range.UsageMin == c2.Range.UsageMin;
+  }
+  bool LessThan(const HIDP_VALUE_CAPS& c1, const HIDP_VALUE_CAPS& c2) const
+  {
+    if (c1.UsagePage == c2.UsagePage) {
+      return c1.Range.UsageMin < c2.Range.UsageMin;
+    }
+    return c1.UsagePage < c2.UsagePage;
+  }
+};
+
+bool
+WindowsGamepadService::GetRawGamepad(HANDLE handle)
+{
+  if (!mHID) {
+    return false;
+  }
+
+  for (unsigned i = 0; i < mGamepads.Length(); i++) {
+    if (mGamepads[i].type == kRawInputGamepad && mGamepads[i].handle == handle) {
+      mGamepads[i].present = true;
+      return true;
+    }
+  }
+
+  RID_DEVICE_INFO rdi = {};
+  UINT size = rdi.cbSize = sizeof(RID_DEVICE_INFO);
+  if (GetRawInputDeviceInfo(handle, RIDI_DEVICEINFO, &rdi, &size) == kRawInputError) {
+    return false;
+  }
+  // Ensure that this is a device we care about
+  if (!SupportedUsage(rdi.hid.usUsagePage, rdi.hid.usUsage)) {
+    return false;
+  }
+
+  Gamepad gamepad = {};
+
+  // Device name is a mostly-opaque string.
+  if (GetRawInputDeviceInfo(handle, RIDI_DEVICENAME, nullptr, &size) == kRawInputError) {
+    return false;
+  }
+
+  nsTArray<wchar_t> devname(size);
+  devname.SetLength(size);
+  if (GetRawInputDeviceInfo(handle, RIDI_DEVICENAME, devname.Elements(), &size) == kRawInputError) {
+    return false;
+  }
+
+  // Per http://msdn.microsoft.com/en-us/library/windows/desktop/ee417014.aspx
+  // device names containing "IG_" are XInput controllers. Ignore those
+  // devices since we'll handle them with XInput.
+  if (wcsstr(devname.Elements(), L"IG_")) {
+    return false;
+  }
+
+  // Product string is a human-readable name.
+  // Per http://msdn.microsoft.com/en-us/library/windows/hardware/ff539681%28v=vs.85%29.aspx
+  // "For USB devices, the maximum string length is 126 wide characters (not including the terminating NULL character)."
+  wchar_t name[128] = { 0 };
+  size = sizeof(name);
+  nsTArray<char> gamepad_name;
+  HANDLE hid_handle = CreateFile(devname.Elements(), GENERIC_READ | GENERIC_WRITE,
+    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+  if (hid_handle) {
+    if (mHID.mHidD_GetProductString(hid_handle, &name, size)) {
+      int bytes = WideCharToMultiByte(CP_UTF8, 0, name, -1, nullptr, 0, nullptr,
+                                      nullptr);
+      gamepad_name.SetLength(bytes);
+      WideCharToMultiByte(CP_UTF8, 0, name, -1, gamepad_name.Elements(),
+                          bytes, nullptr, nullptr);
+    }
+    CloseHandle(hid_handle);
+  }
+  if (gamepad_name.Length() == 0 || !gamepad_name[0]) {
+    const char kUnknown[] = "Unknown Gamepad";
+    gamepad_name.SetLength(ArrayLength(kUnknown));
+    strcpy_s(gamepad_name.Elements(), gamepad_name.Length(), kUnknown);
+  }
+
+  char gamepad_id[256] = { 0 };
+  _snprintf_s(gamepad_id, _TRUNCATE, "%04x-%04x-%s", rdi.hid.dwVendorId,
+              rdi.hid.dwProductId, gamepad_name.Elements());
+
+  nsTArray<uint8_t> preparsedbytes;
+  if (!GetPreparsedData(handle, preparsedbytes)) {
+    return false;
+  }
+
+  PHIDP_PREPARSED_DATA parsed =
+    reinterpret_cast<PHIDP_PREPARSED_DATA>(preparsedbytes.Elements());
+  HIDP_CAPS caps;
+  if (mHID.mHidP_GetCaps(parsed, &caps) != HIDP_STATUS_SUCCESS) {
+    return false;
+  }
+
+  // Enumerate buttons.
+  USHORT count = caps.NumberInputButtonCaps;
+  nsTArray<HIDP_BUTTON_CAPS> buttonCaps(count);
+  buttonCaps.SetLength(count);
+  if (mHID.mHidP_GetButtonCaps(HidP_Input, buttonCaps.Elements(), &count, parsed)
+      != HIDP_STATUS_SUCCESS) {
+    return false;
+  }
+  for (unsigned i = 0; i < count; i++) {
+    // Each buttonCaps is typically a range of buttons.
+    gamepad.numButtons +=
+      buttonCaps[i].Range.UsageMax - buttonCaps[i].Range.UsageMin + 1;
+  }
+  gamepad.numButtons = std::min(gamepad.numButtons, kMaxButtons);
+
+  // Enumerate value caps, which represent axes and d-pads.
+  count = caps.NumberInputValueCaps;
+  nsTArray<HIDP_VALUE_CAPS> valueCaps(count);
+  valueCaps.SetLength(count);
+  if (mHID.mHidP_GetValueCaps(HidP_Input, valueCaps.Elements(), &count, parsed)
+      != HIDP_STATUS_SUCCESS) {
+    return false;
+  }
+  nsTArray<HIDP_VALUE_CAPS> axes;
+  // Sort the axes by usagePage and usage to expose a consistent ordering.
+  HidValueComparator comparator;
+  for (unsigned i = 0; i < count; i++) {
+    if (valueCaps[i].UsagePage == kDesktopUsagePage
+        && valueCaps[i].Range.UsageMin == kUsageDpad
+        // Don't know how to handle d-pads that return weird values.
+        && valueCaps[i].LogicalMax - valueCaps[i].LogicalMin == 7
+        // Can't overflow buttons
+        && gamepad.numButtons + 4 < kMaxButtons) {
+      // d-pad gets special handling.
+      // Ostensibly HID devices can expose multiple d-pads, but this
+      // doesn't happen in practice.
+      gamepad.hasDpad = true;
+      gamepad.dpadCaps = valueCaps[i];
+      // Expose d-pad as 4 additional buttons.
+      gamepad.numButtons += 4;
+    } else {
+      axes.InsertElementSorted(valueCaps[i], comparator);
+    }
+  }
+
+  gamepad.numAxes = std::min<size_t>(axes.Length(), kMaxAxes);
+  for (unsigned i = 0; i < gamepad.numAxes; i++) {
+    if (i >= kMaxAxes) {
+      break;
+    }
+    gamepad.axes[i].caps = axes[i];
+  }
+  gamepad.type = kRawInputGamepad;
+  gamepad.handle = handle;
+  gamepad.present = true;
+
+  gamepad.id = GamepadFunctions::AddGamepad(gamepad_id,
+                                            GamepadMappingType::_empty,
+                                            gamepad.numButtons,
+                                            gamepad.numAxes);
+  mGamepads.AppendElement(gamepad);
+  return true;
+}
+
+bool
+WindowsGamepadService::HandleRawInput(HRAWINPUT handle)
+{
+  if (!mHID) {
+    return false;
+  }
+
+  // First, get data from the handle
+  UINT size;
+  GetRawInputData(handle, RID_INPUT, nullptr, &size, sizeof(RAWINPUTHEADER));
+  nsTArray<uint8_t> data(size);
+  data.SetLength(size);
+  if (GetRawInputData(handle, RID_INPUT, data.Elements(), &size,
+                      sizeof(RAWINPUTHEADER)) == kRawInputError) {
+    return false;
+  }
+  PRAWINPUT raw = reinterpret_cast<PRAWINPUT>(data.Elements());
+
+  Gamepad* gamepad = nullptr;
+  for (unsigned i = 0; i < mGamepads.Length(); i++) {
+    if (mGamepads[i].type == kRawInputGamepad
+        && mGamepads[i].handle == raw->header.hDevice) {
+      gamepad = &mGamepads[i];
+      break;
+    }
+  }
+  if (gamepad == nullptr) {
+    return false;
+  }
+
+  // Second, get the preparsed data
+  nsTArray<uint8_t> parsedbytes;
+  if (!GetPreparsedData(raw->header.hDevice, parsedbytes)) {
+    return false;
+  }
+  PHIDP_PREPARSED_DATA parsed =
+    reinterpret_cast<PHIDP_PREPARSED_DATA>(parsedbytes.Elements());
+
+  // Get all the pressed buttons.
+  nsTArray<USAGE> usages(gamepad->numButtons);
+  usages.SetLength(gamepad->numButtons);
+  ULONG usageLength = gamepad->numButtons;
+  if (mHID.mHidP_GetUsages(HidP_Input, kButtonUsagePage, 0, usages.Elements(),
+                     &usageLength, parsed, (PCHAR)raw->data.hid.bRawData,
+                     raw->data.hid.dwSizeHid) != HIDP_STATUS_SUCCESS) {
+    return false;
+  }
+
+  bool buttons[kMaxButtons] = { false };
+  usageLength = std::min<ULONG>(usageLength, kMaxButtons);
+  for (unsigned i = 0; i < usageLength; i++) {
+    buttons[usages[i] - 1] = true;
+  }
+
+  if (gamepad->hasDpad) {
+    // Get d-pad position as 4 buttons.
+    ULONG value;
+    if (mHID.mHidP_GetUsageValue(HidP_Input, gamepad->dpadCaps.UsagePage, 0, gamepad->dpadCaps.Range.UsageMin, &value, parsed, (PCHAR)raw->data.hid.bRawData, raw->data.hid.dwSizeHid) == HIDP_STATUS_SUCCESS) {
+      UnpackDpad(static_cast<LONG>(value), gamepad, buttons);
+    }
+  }
+
+  for (unsigned i = 0; i < gamepad->numButtons; i++) {
+    if (gamepad->buttons[i] != buttons[i]) {
+      NewButtonEvent(gamepad->id, i, buttons[i]);
+      gamepad->buttons[i] = buttons[i];
+    }
+  }
+
+  // Get all axis values.
+  for (unsigned i = 0; i < gamepad->numAxes; i++) {
+    double new_value;
+    if (gamepad->axes[i].caps.LogicalMin < 0) {
+LONG value;
+      if (mHID.mHidP_GetScaledUsageValue(HidP_Input, gamepad->axes[i].caps.UsagePage,
+                                   0, gamepad->axes[i].caps.Range.UsageMin,
+                                   &value, parsed,
+                                   (PCHAR)raw->data.hid.bRawData,
+                                   raw->data.hid.dwSizeHid)
+          != HIDP_STATUS_SUCCESS) {
+        continue;
+      }
+      new_value = ScaleAxis(value, gamepad->axes[i].caps.LogicalMin,
+                            gamepad->axes[i].caps.LogicalMax);
+    }
+    else {
+      ULONG value;
+      if (mHID.mHidP_GetUsageValue(HidP_Input, gamepad->axes[i].caps.UsagePage, 0,
+                             gamepad->axes[i].caps.Range.UsageMin, &value,
+                             parsed, (PCHAR)raw->data.hid.bRawData,
+                             raw->data.hid.dwSizeHid) != HIDP_STATUS_SUCCESS) {
+        continue;
+      }
+
+      new_value = ScaleAxis(value, gamepad->axes[i].caps.LogicalMin,
+                            gamepad->axes[i].caps.LogicalMax);
+    }
+    if (gamepad->axes[i].value != new_value) {
+      NewAxisMoveEvent(gamepad->id, i, new_value);
+      gamepad->axes[i].value = new_value;
+    }
+  }
+
+  return true;
+}
+
+void
+WindowsGamepadService::Startup()
+{
+  ScanForDevices();
+}
+
+void
+WindowsGamepadService::Shutdown()
+{
+  Cleanup();
+}
+
+void
+WindowsGamepadService::Cleanup()
+{
+  if (mXInputPollTimer) {
+    mXInputPollTimer->Cancel();
+  }
+  mGamepads.Clear();
+}
+
+void
+WindowsGamepadService::DevicesChanged(DeviceChangeType type)
+{
+  if (type == DeviceChangeNotification) {
+    mObserver->SetDeviceChangeTimer();
+  } else if (type == DeviceChangeStable) {
+    ScanForDevices();
+  }
+}
+
+NS_IMETHODIMP
+Observer::Observe(nsISupports* aSubject,
+                  const char* aTopic,
+                  const char16_t* aData)
+{
+  if (strcmp(aTopic, "timer-callback") == 0) {
+    mSvc.DevicesChanged(WindowsGamepadService::DeviceChangeStable);
+  } else if (strcmp(aTopic, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID) == 0) {
+    Stop();
+  }
+  return NS_OK;
+}
+
+HWND sHWnd = nullptr;
+
+bool
+RegisterRawInput(HWND hwnd, bool enable)
+{
+  nsTArray<RAWINPUTDEVICE> rid(ArrayLength(kUsagePages));
+  rid.SetLength(ArrayLength(kUsagePages));
+
+  for (unsigned i = 0; i < rid.Length(); i++) {
+    rid[i].usUsagePage = kUsagePages[i].usagePage;
+    rid[i].usUsage = kUsagePages[i].usage;
+    rid[i].dwFlags =
+      enable ? RIDEV_EXINPUTSINK | RIDEV_DEVNOTIFY : RIDEV_REMOVE;
+    rid[i].hwndTarget = hwnd;
+  }
+
+  if (!RegisterRawInputDevices(rid.Elements(), rid.Length(),
+                               sizeof(RAWINPUTDEVICE))) {
+    return false;
+  }
+  return true;
+}
+
+static
+LRESULT CALLBACK
+GamepadWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+  const unsigned int DBT_DEVICEARRIVAL        = 0x8000;
+  const unsigned int DBT_DEVICEREMOVECOMPLETE = 0x8004;
+  const unsigned int DBT_DEVNODES_CHANGED     = 0x7;
+
+  switch (msg) {
+  case WM_DEVICECHANGE:
+    if (wParam == DBT_DEVICEARRIVAL ||
+        wParam == DBT_DEVICEREMOVECOMPLETE ||
+        wParam == DBT_DEVNODES_CHANGED) {
+      if (gService) {
+        gService->DevicesChanged(WindowsGamepadService::DeviceChangeNotification);
+      }
+    }
+    break;
+  case WM_INPUT:
+    if (gService) {
+      gService->HandleRawInput(reinterpret_cast<HRAWINPUT>(lParam));
+    }
+    break;
+  }
+  return DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
+} // namespace
+
+namespace mozilla {
+namespace dom {
+
+void StartGamepadMonitoring()
+{
+  if (gService) {
+    return;
+  }
+
+  gService = new WindowsGamepadService();
+  gService->Startup();
+
+  if (sHWnd == nullptr) {
+    WNDCLASSW wc;
+    HMODULE hSelf = GetModuleHandle(nullptr);
+
+    if (!GetClassInfoW(hSelf, L"MozillaGamepadClass", &wc)) {
+      ZeroMemory(&wc, sizeof(WNDCLASSW));
+      wc.hInstance = hSelf;
+      wc.lpfnWndProc = GamepadWindowProc;
+      wc.lpszClassName = L"MozillaGamepadClass";
+      RegisterClassW(&wc);
+    }
+
+    sHWnd = CreateWindowW(L"MozillaGamepadClass", L"Gamepad Watcher",
+                          0, 0, 0, 0, 0,
+                          nullptr, nullptr, hSelf, nullptr);
+    RegisterRawInput(sHWnd, true);
+  }
+}
+
+void StopGamepadMonitoring()
+{
+  if (!gService) {
+    return;
+  }
+
+  if (sHWnd) {
+    RegisterRawInput(sHWnd, false);
+    DestroyWindow(sHWnd);
+    sHWnd = nullptr;
+  }
+
+  gService->Shutdown();
+  delete gService;
+  gService = nullptr;
+}
+
+} // namespace dom
+} // namespace mozilla
+
--- a/dom/interfaces/gamepad/nsIGamepadServiceTest.idl
+++ b/dom/interfaces/gamepad/nsIGamepadServiceTest.idl
@@ -4,23 +4,26 @@
 
 #include "nsISupports.idl"
 
 interface nsIVariant;
 
 /*
  * This interface is intended only for use in tests.
  */
-[scriptable, uuid(b6ed093c-6ea0-4141-a8eb-f99645162651)]
+[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,
+  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/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -150,16 +150,20 @@
 #include "nsIAccessibilityService.h"
 #endif
 
 #ifdef MOZ_NUWA_PROCESS
 #include <setjmp.h>
 #include "ipc/Nuwa.h"
 #endif
 
+#ifdef MOZ_GAMEPAD
+#include "mozilla/dom/GamepadService.h"
+#endif
+
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/cellbroadcast/CellBroadcastIPCService.h"
 #include "mozilla/dom/icc/IccChild.h"
 #include "mozilla/dom/mobileconnection/MobileConnectionChild.h"
 #include "mozilla/dom/mobilemessage/SmsChild.h"
 #include "mozilla/dom/devicestorage/DeviceStorageRequestChild.h"
 #include "mozilla/dom/PFileSystemRequestChild.h"
 #include "mozilla/dom/FileSystemTaskBase.h"
@@ -2770,16 +2774,28 @@ ContentChild::AllocPContentPermissionReq
 bool
 ContentChild::DeallocPContentPermissionRequestChild(PContentPermissionRequestChild* actor)
 {
     auto child = static_cast<RemotePermissionRequest*>(actor);
     child->IPDLRelease();
     return true;
 }
 
+bool
+ContentChild::RecvGamepadUpdate(const GamepadChangeEvent& aGamepadEvent)
+{
+#ifdef MOZ_GAMEPAD
+    nsRefPtr<GamepadService> svc(GamepadService::GetService());
+    if (svc) {
+        svc->Update(aGamepadEvent);
+    }
+#endif
+    return true;
+}
+
 // This code goes here rather than nsGlobalWindow.cpp because nsGlobalWindow.cpp
 // can't include ContentChild.h since it includes windows.h.
 
 static uint64_t gNextWindowID = 0;
 
 // We use only 53 bits for the window ID so that it can be converted to and from
 // a JS value without loss of precision. The upper bits of the window ID hold the
 // process ID. The lower bits identify the window.
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -459,16 +459,18 @@ public:
 
     virtual PContentPermissionRequestChild*
     AllocPContentPermissionRequestChild(const InfallibleTArray<PermissionRequest>& aRequests,
                                         const IPC::Principal& aPrincipal,
                                         const TabId& aTabId) override;
     virtual bool
     DeallocPContentPermissionRequestChild(PContentPermissionRequestChild* actor) override;
 
+    virtual bool RecvGamepadUpdate(const GamepadChangeEvent& aGamepadEvent) override;
+
 private:
     virtual void ActorDestroy(ActorDestroyReason why) override;
 
     virtual void ProcessingError(Result aCode, const char* aReason) override;
 
     /**
      * Exit *now*.  Do not shut down XPCOM, do not pass Go, do not run
      * static destructors, do not collect $200.
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -221,16 +221,20 @@ using namespace mozilla::system;
 #include "nsIBrowserSearchService.h"
 #endif
 
 #ifdef MOZ_ENABLE_PROFILER_SPS
 #include "nsIProfiler.h"
 #include "nsIProfileSaveEvent.h"
 #endif
 
+#ifdef MOZ_GAMEPAD
+#include "mozilla/dom/GamepadMonitoring.h"
+#endif
+
 static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
 
 using base::ChildPrivileges;
 using base::KillProcess;
 
 #ifdef MOZ_CRASHREPORTER
 using namespace CrashReporter;
 #endif
@@ -2152,16 +2156,17 @@ ContentParent::ContentParent(mozIApplica
                              bool aIsForBrowser,
                              bool aIsForPreallocated,
                              ProcessPriority aInitialPriority /* = PROCESS_PRIORITY_FOREGROUND */,
                              bool aIsNuwaProcess /* = false */)
     : nsIContentParent()
     , mOpener(aOpener)
     , mIsForBrowser(aIsForBrowser)
     , mIsNuwaProcess(aIsNuwaProcess)
+    , mHasGamepadListener(false)
 {
     InitializeMembers();  // Perform common initialization.
 
     // No more than one of !!aApp, aIsForBrowser, aIsForPreallocated should be
     // true.
     MOZ_ASSERT(!!aApp + aIsForBrowser + aIsForPreallocated <= 1);
 
     // Only the preallocated process uses Nuwa.
@@ -4985,16 +4990,44 @@ ContentParent::AllocPContentPermissionRe
 bool
 ContentParent::DeallocPContentPermissionRequestParent(PContentPermissionRequestParent* actor)
 {
     nsContentPermissionUtils::NotifyRemoveContentPermissionRequestParent(actor);
     delete actor;
     return true;
 }
 
+bool
+ContentParent::RecvGamepadListenerAdded()
+{
+#ifdef MOZ_GAMEPAD
+    if (mHasGamepadListener) {
+        NS_WARNING("Gamepad listener already started, cannot start again!");
+        return false;
+    }
+    mHasGamepadListener = true;
+    StartGamepadMonitoring();
+#endif
+    return true;
+}
+
+bool
+ContentParent::RecvGamepadListenerRemoved()
+{
+#ifdef MOZ_GAMEPAD
+    if (!mHasGamepadListener) {
+        NS_WARNING("Gamepad listener already stopped, cannot stop again!");
+        return false;
+    }
+    mHasGamepadListener = false;
+    MaybeStopGamepadMonitoring();
+#endif
+    return true;
+}
+
 } // namespace dom
 } // namespace mozilla
 
 NS_IMPL_ISUPPORTS(ParentIdleListener, nsIObserver)
 
 NS_IMETHODIMP
 ParentIdleListener::Observe(nsISupports*, const char* aTopic, const char16_t* aData) {
     mozilla::unused << mParent->SendNotifyIdleObserver(mObserver,
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -372,16 +372,18 @@ public:
 
     virtual PContentPermissionRequestParent*
     AllocPContentPermissionRequestParent(const InfallibleTArray<PermissionRequest>& aRequests,
                                          const IPC::Principal& aPrincipal,
                                          const TabId& aTabId) override;
     virtual bool
     DeallocPContentPermissionRequestParent(PContentPermissionRequestParent* actor) override;
 
+    bool HasGamepadListener() const { return mHasGamepadListener; }
+
 protected:
     void OnChannelConnected(int32_t pid) override;
     virtual void ActorDestroy(ActorDestroyReason why) override;
     void OnNuwaForkTimeout();
 
     bool ShouldContinueFromReplyTimeout() override;
 
 private:
@@ -835,16 +837,20 @@ private:
 
     virtual PDocAccessibleParent* AllocPDocAccessibleParent(PDocAccessibleParent*, const uint64_t&) override;
     virtual bool DeallocPDocAccessibleParent(PDocAccessibleParent*) override;
     virtual bool RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc,
                                                PDocAccessibleParent* aParentDoc, const uint64_t& aParentID) override;
 
     virtual bool RecvUpdateDropEffect(const uint32_t& aDragAction,
                                       const uint32_t& aDropEffect) override;
+
+    virtual bool RecvGamepadListenerAdded() override;
+    virtual bool RecvGamepadListenerRemoved() override;
+
     // If you add strong pointers to cycle collected objects here, be sure to
     // release these objects in ShutDownProcess.  See the comment there for more
     // details.
 
     GeckoChildProcessHost* mSubprocess;
     ContentParent* mOpener;
 
     ContentParentId mChildID;
@@ -879,16 +885,17 @@ private:
     // True only the if process is already a browser or app or has
     // been transformed into one.
     bool mMetamorphosed;
 
     bool mSendPermissionUpdates;
     bool mSendDataStoreInfos;
     bool mIsForBrowser;
     bool mIsNuwaProcess;
+    bool mHasGamepadListener;
 
     // These variables track whether we've called Close(), CloseWithError()
     // and KillHard() on our channel.
     bool mCalledClose;
     bool mCalledCloseWithError;
     bool mCalledKillHard;
     bool mCreatedPairedMinidumps;
     bool mShutdownPending;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -363,16 +363,48 @@ struct DomainPolicyClone
 {
     bool        active;
     URIParams[] blacklist;
     URIParams[] whitelist;
     URIParams[] superBlacklist;
     URIParams[] superWhitelist;
 };
 
+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;
+};
+
 prio(normal upto urgent) sync protocol PContent
 {
     parent spawns PPluginModule;
 
     parent opens PCompositor;
     parent opens PProcessHangMonitor;
     parent opens PSharedBufferManager;
     parent opens PImageBridge;
@@ -600,16 +632,20 @@ child:
     async LoadProcessScript(nsString url);
 
     /**
      * Requests a full native update of a native plugin child window. This is
      * a Windows specific call.
      */
     async UpdateWindow(uintptr_t aChildId);
 
+    /**
+     * Send gamepad status update to child.
+     */
+    GamepadUpdate(GamepadChangeEvent aGamepadEvent);
 parent:
     /**
      * Tell the parent process a new accessible document has been created.
      * aParentDoc is the accessible document it was created in if any, and
      * aParentAcc is the id of the accessible in that document the new document
      * is a child of.
      */
     PDocAccessible(nullable PDocAccessible aParentDoc, uint64_t aParentAcc);
@@ -987,15 +1023,25 @@ parent:
      *
      * NOTE: The principal is untrusted in the parent process. Only
      *       principals that can live in the content process should
      *       provided.
      */
     PContentPermissionRequest(PermissionRequest[] aRequests, Principal aPrincipal,
                               TabId tabId);
 
+    /*
+     * Tells the parent to start the gamepad listening service if it hasn't already.
+     */
+    GamepadListenerAdded();
+
+    /**
+     * Tells the parent to stop the gamepad listening service if it hasn't already.
+     */
+    GamepadListenerRemoved();
+
 both:
      AsyncMessage(nsString aMessage, ClonedMessageData aData,
                   CpowEntry[] aCpows, Principal aPrincipal);
 };
 
 }
 }
--- a/dom/tests/mochitest/gamepad/mochitest.ini
+++ b/dom/tests/mochitest/gamepad/mochitest.ini
@@ -1,18 +1,13 @@
 [DEFAULT]
+skip-if=e10s || (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 support-files =
   gamepad_frame.html
   gamepad_frame_state.html
   mock_gamepad.js
 
 [test_check_timestamp.html]
-skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_gamepad.html]
-skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_gamepad_connect_events.html]
-skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_gamepad_frame_state_sync.html]
-skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_gamepad_hidden_frame.html]
-skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_navigator_gamepads.html]
-skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
--- a/dom/tests/mochitest/gamepad/test_check_timestamp.html
+++ b/dom/tests/mochitest/gamepad/test_check_timestamp.html
@@ -6,16 +6,17 @@
   <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();
+
 var index = GamepadService.addGamepad("test gamepad", // id
                                       SpecialPowers.Ci.nsIGamepadServiceTest.NO_MAPPING,
                                       4, // buttons
                                       2);// axes
 
 var timea=0;
 window.addEventListener("gamepadbuttondown", buttonpresshandler);
 
--- a/dom/tests/mochitest/gamepad/test_gamepad.html
+++ b/dom/tests/mochitest/gamepad/test_gamepad.html
@@ -12,25 +12,35 @@
 <script class="testbody" type="text/javascript">
 SimpleTest.waitForExplicitFinish();
 window.addEventListener("gamepadconnected", connecthandler);
 // Add a gamepad
 var index = GamepadService.addGamepad("test gamepad", // id
                                       SpecialPowers.Ci.nsIGamepadServiceTest.STANDARD_MAPPING,
                                       4, // buttons
                                       2);// axes
-// Press a button
 GamepadService.newButtonEvent(index, 0, true);
 function connecthandler(e) {
   ok(e.gamepad.timestamp <= performance.now());
+  is(e.gamepad.index, 0, "correct gamepad index");
   is(e.gamepad.id, "test gamepad", "correct gamepad name");
   is(e.gamepad.mapping, "standard", "standard mapping");
   is(e.gamepad.buttons.length, 4, "correct number of buttons");
   is(e.gamepad.axes.length, 2, "correct number of axes");
+  // Press a button
+  GamepadService.newButtonEvent(index, 0, true);
+  gamepads = navigator.getGamepads();
+  is(gamepads[0].buttons[0].pressed, true, "gamepad button should register as pressed")
+  GamepadService.newButtonValueEvent(index, 1, true, 0.5);
+  gamepads = navigator.getGamepads();
+  is(gamepads[0].buttons[1].pressed, true, "gamepad button should register as pressed")
+  is(gamepads[0].buttons[1].value, 0.5, "gamepad button value should be 0.5")
+
   SimpleTest.executeSoon(function() {
     GamepadService.removeGamepad(index);
     SimpleTest.finish();
   });
 }
+
 </script>
 </body>
 </html>
 
--- a/dom/tests/mochitest/gamepad/test_navigator_gamepads.html
+++ b/dom/tests/mochitest/gamepad/test_navigator_gamepads.html
@@ -32,59 +32,71 @@ function connecthandler(e) {
 }
 
 function disconnecthandler(e) {
   run_next_test(e);
 }
 window.addEventListener("gamepadconnected", connecthandler);
 window.addEventListener("gamepaddisconnected", disconnecthandler);
 // Add a gamepad
-var index1 = GamepadService.addGamepad("test gamepad 1", // id
+var internal_index1 = GamepadService.addGamepad("test gamepad 1", // id
                                        SpecialPowers.Ci.nsIGamepadServiceTest.NO_MAPPING,
                                        4, // buttons
                                        2);// axes
-var index2;
+var content_index1 = 0;
+var internal_index2;
+var content_index2 = 1;
 
 // Press a button to make the gamepad visible to the page.
-GamepadService.newButtonEvent(index1, 0, true);
+GamepadService.newButtonEvent(internal_index1, 0, true);
 
 function check_first_gamepad(e) {
+  ok(true, "Checking first gamepad");
   // First gamepad gets added.
   is(e.gamepad.id, "test gamepad 1", "correct gamepad name");
   var gamepads = navigator.getGamepads();
   is(gamepads.length, 1, "should have one gamepad exposed");
   is(gamepads[e.gamepad.index], e.gamepad, "right gamepad exposed at index");
+  is(gamepads[content_index1], e.gamepad, "gamepad counter working correctly");
   // Add a second gamepad, should automatically show up.
-  index2 = GamepadService.addGamepad("test gamepad 2", // id
+  internal_index2 = GamepadService.addGamepad("test gamepad 2", // id
                                      SpecialPowers.Ci.nsIGamepadServiceTest.NO_MAPPING,
                                      4, // buttons
                                      2);// axes
+  ok(true, "Done checking first gamepad");
 }
 
 function check_second_gamepad(e) {
+  ok(true, "Checking seceond gamepad");
   // Second gamepad gets added.
+  is(e.gamepad.index, 1, "gamepad index should be 1")
   is(e.gamepad.id, "test gamepad 2", "correct gamepad name");
   var gamepads = navigator.getGamepads();
   is(gamepads.length, 2, "should have two gamepads exposed");
   is(gamepads[e.gamepad.index], e.gamepad, "right gamepad exposed at index");
+  is(gamepads[content_index2], e.gamepad, "gamepad counter working correctly");
   // Now remove the first one.
-  GamepadService.removeGamepad(index1);
+  GamepadService.removeGamepad(internal_index1);
+  ok(true, "Done checking second gamepad");
 }
 
 function check_gamepad_hole(e) {
+  ok(true, "Checking gamepad hole");
   // First gamepad gets removed.
   var gamepads = navigator.getGamepads();
   is(gamepads.length, 2, "gamepads should have two entries");
-  is(gamepads[index1], null, "should be a hole in the gamepad list");
-  isnot(gamepads[index2], null, "second gamepad should exist");
+  is(gamepads[content_index1], null, "should be a hole in the gamepad list");
+  isnot(gamepads[content_index2], null, "second gamepad should exist");
   // Now remove the second one.
-  GamepadService.removeGamepad(index2);
+  GamepadService.removeGamepad(internal_index2);
+  ok(true, "Done checking gamepad hole");
 }
 
 function check_no_gamepads(e) {
+  ok(true, "Checking no gamepads");
   // Second gamepad gets removed.
   var gamepads = navigator.getGamepads();
   is(gamepads.length, 0, "gamepads should be empty");
   SimpleTest.finish();
 }
 </script>
 </body>
 </html>
--- a/hal/Hal.cpp
+++ b/hal/Hal.cpp
@@ -631,26 +631,16 @@ void PowerOff()
 
 void StartForceQuitWatchdog(ShutdownMode aMode, int32_t aTimeoutSecs)
 {
   AssertMainProcess();
   AssertMainThread();
   PROXY_IF_SANDBOXED(StartForceQuitWatchdog(aMode, aTimeoutSecs));
 }
 
-void StartMonitoringGamepadStatus()
-{
-  PROXY_IF_SANDBOXED(StartMonitoringGamepadStatus());
-}
-
-void StopMonitoringGamepadStatus()
-{
-  PROXY_IF_SANDBOXED(StopMonitoringGamepadStatus());
-}
-
 void
 RegisterWakeLockObserver(WakeLockObserver* aObserver)
 {
   AssertMainThread();
   sWakeLockObservers.AddObserver(aObserver);
 }
 
 void
--- a/hal/Hal.h
+++ b/hal/Hal.h
@@ -12,17 +12,16 @@
 #include "base/basictypes.h"
 #include "mozilla/Observer.h"
 #include "mozilla/Types.h"
 #include "nsTArray.h"
 #include "mozilla/dom/MozPowerManagerBinding.h"
 #include "mozilla/dom/battery/Types.h"
 #include "mozilla/dom/network/Types.h"
 #include "mozilla/dom/power/Types.h"
-#include "mozilla/hal_sandbox/PHal.h"
 #include "mozilla/dom/ScreenOrientation.h"
 #include "mozilla/HalScreenConfiguration.h"
 
 /*
  * Hal.h contains the public Hal API.
  *
  * By default, this file defines its functions in the hal namespace, but if
  * MOZ_HAL_NAMESPACE is defined, we'll define our functions in that namespace.
@@ -594,26 +593,16 @@ void DisableRDS();
 void StartForceQuitWatchdog(hal::ShutdownMode aMode, int32_t aTimeoutSecs);
 
 /**
  * Perform Factory Reset to wipe out all user data.
  */
 void FactoryReset(mozilla::dom::FactoryResetReason& aReason);
 
 /**
- * Start monitoring the status of gamepads attached to the system.
- */
-void StartMonitoringGamepadStatus();
-
-/**
- * Stop monitoring the status of gamepads attached to the system.
- */
-void StopMonitoringGamepadStatus();
-
-/**
  * Start monitoring disk space for low space situations.
  *
  * This API is currently only allowed to be used from the main process.
  */
 void StartDiskSpaceWatcher();
 
 /**
  * Stop monitoring disk space for low space situations.
deleted file mode 100644
--- a/hal/android/AndroidGamepad.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 "Hal.h"
-#include "AndroidBridge.h"
-
-using namespace mozilla::hal;
-
-namespace mozilla {
-namespace hal_impl {
-
-void
-StartMonitoringGamepadStatus()
-{
-  widget::GeckoAppShell::StartMonitoringGamepad();
-}
-
-void
-StopMonitoringGamepadStatus()
-{
-  widget::GeckoAppShell::StopMonitoringGamepad();
-}
-
-} // hal_impl
-} // mozilla
deleted file mode 100644
--- a/hal/cocoa/CocoaGamepad.cpp
+++ /dev/null
@@ -1,522 +0,0 @@
-/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-// mostly derived from the Allegro source code at:
-// http://alleg.svn.sourceforge.net/viewvc/alleg/allegro/branches/4.9/src/macosx/hidjoy.m?revision=13760&view=markup
-
-#include "mozilla/dom/GamepadService.h"
-#include <CoreFoundation/CoreFoundation.h>
-#include <IOKit/hid/IOHIDBase.h>
-#include <IOKit/hid/IOHIDKeys.h>
-#include <IOKit/hid/IOHIDManager.h>
-
-#include <stdio.h>
-#include <vector>
-
-namespace {
-
-using mozilla::dom::GamepadService;
-
-using std::vector;
-
-struct Button {
-  int id;
-  bool analog;
-  IOHIDElementRef element;
-  CFIndex min;
-  CFIndex max;
-
-  Button(int aId, IOHIDElementRef aElement, CFIndex aMin, CFIndex aMax) :
-    id(aId),
-    analog((aMax - aMin) > 1),
-    element(aElement),
-    min(aMin),
-    max(aMax) {}
-};
-
-struct Axis {
-  int id;
-  IOHIDElementRef element;
-  uint32_t usagePage;
-  uint32_t usage;
-  CFIndex min;
-  CFIndex max;
-};
-
-typedef bool dpad_buttons[4];
-
-// These values can be found in the USB HID Usage Tables:
-// http://www.usb.org/developers/hidpage
-const unsigned kDesktopUsagePage = 0x01;
-const unsigned kSimUsagePage = 0x02;
-const unsigned kAcceleratorUsage = 0xC4;
-const unsigned kBrakeUsage = 0xC5;
-const unsigned kJoystickUsage = 0x04;
-const unsigned kGamepadUsage = 0x05;
-const unsigned kAxisUsageMin = 0x30;
-const unsigned kAxisUsageMax = 0x35;
-const unsigned kDpadUsage = 0x39;
-const unsigned kButtonUsagePage = 0x09;
-const unsigned kConsumerPage = 0x0C;
-const unsigned kHomeUsage = 0x223;
-const unsigned kBackUsage = 0x224;
-
-
-class Gamepad {
- private:
-  IOHIDDeviceRef mDevice;
-  nsTArray<Button> buttons;
-  nsTArray<Axis> axes;
-  IOHIDElementRef mDpad;
-  dpad_buttons mDpadState;
-
- public:
-  Gamepad() : mDevice(nullptr), mDpad(nullptr), mSuperIndex(-1) {}
-  bool operator==(IOHIDDeviceRef device) const { return mDevice == device; }
-  bool empty() const { return mDevice == nullptr; }
-  void clear()
-  {
-    mDevice = nullptr;
-    buttons.Clear();
-    axes.Clear();
-    mDpad = nullptr;
-    mSuperIndex = -1;
-  }
-  void init(IOHIDDeviceRef device);
-  size_t numButtons() { return buttons.Length() + (mDpad ? 4 : 0); }
-  size_t numAxes() { return axes.Length(); }
-
-  // Index given by our superclass.
-  uint32_t mSuperIndex;
-
-  const bool isDpad(IOHIDElementRef element) const
-  {
-    return element == mDpad;
-  }
-
-  const dpad_buttons& getDpadState() const
-  {
-    return mDpadState;
-  }
-
-  void setDpadState(const dpad_buttons& dpadState)
-  {
-    for (unsigned i = 0; i < ArrayLength(mDpadState); i++) {
-      mDpadState[i] = dpadState[i];
-    }
-  }
-
-  const Button* lookupButton(IOHIDElementRef element) const
-  {
-    for (unsigned i = 0; i < buttons.Length(); i++) {
-      if (buttons[i].element == element)
-        return &buttons[i];
-    }
-    return nullptr;
-  }
-
-  const Axis* lookupAxis(IOHIDElementRef element) const
-  {
-    for (unsigned i = 0; i < axes.Length(); i++) {
-      if (axes[i].element == element)
-        return &axes[i];
-    }
-    return nullptr;
-  }
-};
-
-class AxisComparator {
-public:
-  bool Equals(const Axis& a1, const Axis& a2) const
-  {
-    return a1.usagePage == a2.usagePage && a1.usage == a2.usage;
-  }
-  bool LessThan(const Axis& a1, const Axis& a2) const
-  {
-    if (a1.usagePage == a2.usagePage) {
-      return a1.usage < a2.usage;
-    }
-    return a1.usagePage < a2.usagePage;
-  }
-};
-
-void Gamepad::init(IOHIDDeviceRef device)
-{
-  clear();
-  mDevice = device;
-
-  CFArrayRef elements = IOHIDDeviceCopyMatchingElements(device,
-                                                        nullptr,
-                                                        kIOHIDOptionsTypeNone);
-  CFIndex n = CFArrayGetCount(elements);
-  for (CFIndex i = 0; i < n; i++) {
-    IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements,
-                                                                      i);
-    uint32_t usagePage = IOHIDElementGetUsagePage(element);
-    uint32_t usage = IOHIDElementGetUsage(element);
-
-    if (usagePage == kDesktopUsagePage &&
-        usage >= kAxisUsageMin &&
-        usage <= kAxisUsageMax)
-    {
-      Axis axis = { int(axes.Length()),
-                    element,
-                    usagePage,
-                    usage,
-                    IOHIDElementGetLogicalMin(element),
-                    IOHIDElementGetLogicalMax(element) };
-      axes.AppendElement(axis);
-    } else if (usagePage == kDesktopUsagePage && usage == kDpadUsage &&
-               // Don't know how to handle d-pads that return weird values.
-               IOHIDElementGetLogicalMax(element) - IOHIDElementGetLogicalMin(element) == 7) {
-      mDpad = element;
-    } else if ((usagePage == kSimUsagePage &&
-                 (usage == kAcceleratorUsage ||
-                  usage == kBrakeUsage)) ||
-               (usagePage == kButtonUsagePage) ||
-               (usagePage == kConsumerPage &&
-                 (usage == kHomeUsage ||
-                  usage == kBackUsage))) {
-      Button button(int(buttons.Length()), element, IOHIDElementGetLogicalMin(element), IOHIDElementGetLogicalMax(element));
-      buttons.AppendElement(button);
-    } else {
-      //TODO: handle other usage pages
-    }
-  }
-
-  AxisComparator comparator;
-  axes.Sort(comparator);
-  for (unsigned i = 0; i < axes.Length(); i++) {
-    axes[i].id = i;
-  }
-}
-
-class DarwinGamepadService {
- private:
-  IOHIDManagerRef mManager;
-  vector<Gamepad> mGamepads;
-
-  static void DeviceAddedCallback(void* data, IOReturn result,
-                                  void* sender, IOHIDDeviceRef device);
-  static void DeviceRemovedCallback(void* data, IOReturn result,
-                                    void* sender, IOHIDDeviceRef device);
-  static void InputValueChangedCallback(void* data, IOReturn result,
-                                        void* sender, IOHIDValueRef newValue);
-
-  void DeviceAdded(IOHIDDeviceRef device);
-  void DeviceRemoved(IOHIDDeviceRef device);
-  void InputValueChanged(IOHIDValueRef value);
-
- public:
-  DarwinGamepadService();
-  ~DarwinGamepadService();
-  void Startup();
-  void Shutdown();
-};
-
-void
-DarwinGamepadService::DeviceAdded(IOHIDDeviceRef device)
-{
-  size_t slot = size_t(-1);
-  for (size_t i = 0; i < mGamepads.size(); i++) {
-    if (mGamepads[i] == device)
-      return;
-    if (slot == size_t(-1) && mGamepads[i].empty())
-      slot = i;
-  }
-
-  if (slot == size_t(-1)) {
-    slot = mGamepads.size();
-    mGamepads.push_back(Gamepad());
-  }
-  mGamepads[slot].init(device);
-
-  // Gather some identifying information
-  CFNumberRef vendorIdRef =
-    (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey));
-  CFNumberRef productIdRef =
-    (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey));
-  CFStringRef productRef =
-    (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
-  int vendorId, productId;
-  CFNumberGetValue(vendorIdRef, kCFNumberIntType, &vendorId);
-  CFNumberGetValue(productIdRef, kCFNumberIntType, &productId);
-  char product_name[128];
-  CFStringGetCString(productRef, product_name,
-                     sizeof(product_name), kCFStringEncodingASCII);
-  char buffer[256];
-  sprintf(buffer, "%x-%x-%s", vendorId, productId, product_name);
-  nsRefPtr<GamepadService> service(GamepadService::GetService());
-  mGamepads[slot].mSuperIndex = service->AddGamepad(buffer,
-                                                    mozilla::dom::GamepadMappingType::_empty,
-                                                    (int)mGamepads[slot].numButtons(),
-                                                    (int)mGamepads[slot].numAxes());
-}
-
-void
-DarwinGamepadService::DeviceRemoved(IOHIDDeviceRef device)
-{
-  nsRefPtr<GamepadService> service(GamepadService::GetService());
-  for (size_t i = 0; i < mGamepads.size(); i++) {
-    if (mGamepads[i] == device) {
-      service->RemoveGamepad(mGamepads[i].mSuperIndex);
-      mGamepads[i].clear();
-      return;
-    }
-  }
-}
-
-/*
- * Given a value from a d-pad (POV hat in USB HID terminology),
- * represent it as 4 buttons, one for each cardinal direction.
- */
-static void
-UnpackDpad(int dpad_value, int min, int max, dpad_buttons& buttons)
-{
-  const unsigned kUp = 0;
-  const unsigned kDown = 1;
-  const unsigned kLeft = 2;
-  const unsigned kRight = 3;
-
-  // Different controllers have different ways of representing
-  // "nothing is pressed", but they're all outside the range of values.
-  if (dpad_value < min || dpad_value > max) {
-    // Nothing is pressed.
-    return;
-  }
-
-  // Normalize value to start at 0.
-  int value = dpad_value - min;
-
-  // Value will be in the range 0-7. The value represents the
-  // position of the d-pad around a circle, with 0 being straight up,
-  // 2 being right, 4 being straight down, and 6 being left.
-  if (value < 2 || value > 6) {
-    buttons[kUp] = true;
-  }
-  if (value > 2 && value < 6) {
-    buttons[kDown] = true;
-  }
-  if (value > 4) {
-    buttons[kLeft] = true;
-  }
-  if (value > 0 && value < 4) {
-    buttons[kRight] = true;
-  }
-}
-
-void
-DarwinGamepadService::InputValueChanged(IOHIDValueRef value)
-{
-  uint32_t value_length = IOHIDValueGetLength(value);
-  if (value_length > 4) {
-    // Workaround for bizarre issue with PS3 controllers that try to return
-    // massive (30+ byte) values and crash IOHIDValueGetIntegerValue
-    return;
-  }
-  nsRefPtr<GamepadService> service(GamepadService::GetService());
-  IOHIDElementRef element = IOHIDValueGetElement(value);
-  IOHIDDeviceRef device = IOHIDElementGetDevice(element);
-  for (unsigned i = 0; i < mGamepads.size(); i++) {
-    Gamepad &gamepad = mGamepads[i];
-    if (gamepad == device) {
-      if (gamepad.isDpad(element)) {
-        const dpad_buttons& oldState = gamepad.getDpadState();
-        dpad_buttons newState = { false, false, false, false };
-        UnpackDpad(IOHIDValueGetIntegerValue(value),
-                   IOHIDElementGetLogicalMin(element),
-                   IOHIDElementGetLogicalMax(element),
-                   newState);
-        const int numButtons = gamepad.numButtons();
-        for (unsigned b = 0; b < ArrayLength(newState); b++) {
-          if (newState[b] != oldState[b]) {
-            service->NewButtonEvent(i, numButtons - 4 + b, newState[b]);
-          }
-        }
-        gamepad.setDpadState(newState);
-      } else if (const Axis* axis = gamepad.lookupAxis(element)) {
-        double d = IOHIDValueGetIntegerValue(value);
-        double v = 2.0f * (d - axis->min) /
-          (double)(axis->max - axis->min) - 1.0f;
-        service->NewAxisMoveEvent(i, axis->id, v);
-      } else if (const Button* button = gamepad.lookupButton(element)) {
-        int iv = IOHIDValueGetIntegerValue(value);
-        bool pressed = iv != 0;
-        double v = 0;
-        if (button->analog) {
-          double dv = iv;
-          v = (dv - button->min) / (double)(button->max - button->min);
-        } else {
-          v = pressed ? 1.0 : 0.0;
-        }
-        service->NewButtonEvent(i, button->id, pressed, v);
-      }
-      return;
-    }
-  }
-}
-
-void
-DarwinGamepadService::DeviceAddedCallback(void* data, IOReturn result,
-                                         void* sender, IOHIDDeviceRef device)
-{
-  DarwinGamepadService* service = (DarwinGamepadService*)data;
-  service->DeviceAdded(device);
-}
-
-void
-DarwinGamepadService::DeviceRemovedCallback(void* data, IOReturn result,
-                                           void* sender, IOHIDDeviceRef device)
-{
-  DarwinGamepadService* service = (DarwinGamepadService*)data;
-  service->DeviceRemoved(device);
-}
-
-void
-DarwinGamepadService::InputValueChangedCallback(void* data,
-                                               IOReturn result,
-                                               void* sender,
-                                               IOHIDValueRef newValue)
-{
-  DarwinGamepadService* service = (DarwinGamepadService*)data;
-  service->InputValueChanged(newValue);
-}
-
-static CFMutableDictionaryRef
-MatchingDictionary(UInt32 inUsagePage, UInt32 inUsage)
-{
-  CFMutableDictionaryRef dict =
-    CFDictionaryCreateMutable(kCFAllocatorDefault,
-                              0,
-                              &kCFTypeDictionaryKeyCallBacks,
-                              &kCFTypeDictionaryValueCallBacks);
-  if (!dict)
-    return nullptr;
-  CFNumberRef number = CFNumberCreate(kCFAllocatorDefault,
-                                      kCFNumberIntType,
-                                      &inUsagePage);
-  if (!number) {
-    CFRelease(dict);
-    return nullptr;
-  }
-  CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsagePageKey), number);
-  CFRelease(number);
-
-  number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsage);
-  if (!number) {
-    CFRelease(dict);
-    return nullptr;
-  }
-  CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsageKey), number);
-  CFRelease(number);
-
-  return dict;
-}
-
-DarwinGamepadService::DarwinGamepadService() : mManager(nullptr) {}
-
-DarwinGamepadService::~DarwinGamepadService()
-{
-  if (mManager != nullptr)
-    CFRelease(mManager);
-}
-
-void DarwinGamepadService::Startup()
-{
-  if (mManager != nullptr)
-    return;
-
-  IOHIDManagerRef manager = IOHIDManagerCreate(kCFAllocatorDefault,
-                                               kIOHIDOptionsTypeNone);
-
-  CFMutableDictionaryRef criteria_arr[2];
-  criteria_arr[0] = MatchingDictionary(kDesktopUsagePage,
-                                       kJoystickUsage);
-  if (!criteria_arr[0]) {
-    CFRelease(manager);
-    return;
-  }
-
-  criteria_arr[1] = MatchingDictionary(kDesktopUsagePage,
-                                       kGamepadUsage);
-  if (!criteria_arr[1]) {
-    CFRelease(criteria_arr[0]);
-    CFRelease(manager);
-    return;
-  }
-
-  CFArrayRef criteria =
-    CFArrayCreate(kCFAllocatorDefault, (const void**)criteria_arr, 2, nullptr);
-  if (!criteria) {
-    CFRelease(criteria_arr[1]);
-    CFRelease(criteria_arr[0]);
-    CFRelease(manager);
-    return;
-  }
-
-  IOHIDManagerSetDeviceMatchingMultiple(manager, criteria);
-  CFRelease(criteria);
-  CFRelease(criteria_arr[1]);
-  CFRelease(criteria_arr[0]);
-
-  IOHIDManagerRegisterDeviceMatchingCallback(manager,
-                                             DeviceAddedCallback,
-                                             this);
-  IOHIDManagerRegisterDeviceRemovalCallback(manager,
-                                            DeviceRemovedCallback,
-                                            this);
-  IOHIDManagerRegisterInputValueCallback(manager,
-                                         InputValueChangedCallback,
-                                         this);
-  IOHIDManagerScheduleWithRunLoop(manager,
-                                  CFRunLoopGetCurrent(),
-                                  kCFRunLoopDefaultMode);
-  IOReturn rv = IOHIDManagerOpen(manager, kIOHIDOptionsTypeNone);
-  if (rv != kIOReturnSuccess) {
-    CFRelease(manager);
-    return;
-  }
-
-  mManager = manager;
-}
-
-void DarwinGamepadService::Shutdown()
-{
-  IOHIDManagerRef manager = (IOHIDManagerRef)mManager;
-  if (manager) {
-    IOHIDManagerClose(manager, 0);
-    CFRelease(manager);
-    mManager = nullptr;
-  }
-}
-
-} // namespace
-
-namespace mozilla {
-namespace hal_impl {
-
-DarwinGamepadService* gService = nullptr;
-
-void StartMonitoringGamepadStatus()
-{
-  if (gService)
-    return;
-
-  gService = new DarwinGamepadService();
-  gService->Startup();
-}
-
-void StopMonitoringGamepadStatus()
-{
-  if (!gService)
-    return;
-
-  gService->Shutdown();
-  delete gService;
-  gService = nullptr;
-}
-
-} // namespace hal_impl
-} // namespace mozilla
deleted file mode 100644
--- a/hal/fallback/FallbackGamepad.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set sw=2 ts=8 et ft=cpp : */
-/* 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 "Hal.h"
-
-namespace mozilla {
-namespace hal_impl {
-
-void StartMonitoringGamepadStatus()
-{}
-
-void StopMonitoringGamepadStatus()
-{}
-
-} // hal_impl
-} // namespace mozilla
deleted file mode 100644
--- a/hal/linux/LinuxGamepad.cpp
+++ /dev/null
@@ -1,373 +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/. */
-
-/*
- * LinuxGamepadService: A Linux backend for the GamepadService.
- * Derived from the kernel documentation at
- * http://www.kernel.org/doc/Documentation/input/joystick-api.txt
- */
-#include <algorithm>
-#include <cstddef>
-
-#include <glib.h>
-#include <linux/joystick.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <sys/ioctl.h>
-#include <unistd.h>
-
-#include "nscore.h"
-#include "mozilla/dom/GamepadService.h"
-#include "udev.h"
-
-namespace {
-
-using mozilla::dom::GamepadService;
-using mozilla::udev_lib;
-using mozilla::udev_device;
-using mozilla::udev_list_entry;
-using mozilla::udev_enumerate;
-using mozilla::udev_monitor;
-
-static const float kMaxAxisValue = 32767.0;
-static const char kJoystickPath[] = "/dev/input/js";
-
-//TODO: should find a USB identifier for each device so we can
-// provide something that persists across connect/disconnect cycles.
-typedef struct {
-  int index;
-  guint source_id;
-  int numAxes;
-  int numButtons;
-  char idstring[128];
-  char devpath[PATH_MAX];
-} Gamepad;
-
-class LinuxGamepadService {
-public:
-  LinuxGamepadService() : mMonitor(nullptr),
-                          mMonitorSourceID(0) {
-  }
-
-  void Startup();
-  void Shutdown();
-
-private:
-  void AddDevice(struct udev_device* dev);
-  void RemoveDevice(struct udev_device* dev);
-  void ScanForDevices();
-  void AddMonitor();
-  void RemoveMonitor();
-  bool is_gamepad(struct udev_device* dev);
-  void ReadUdevChange();
-
-  // handler for data from /dev/input/jsN
-  static gboolean OnGamepadData(GIOChannel *source,
-                                GIOCondition condition,
-                                gpointer data);
-
-  // handler for data from udev monitor
-  static gboolean OnUdevMonitor(GIOChannel *source,
-                                GIOCondition condition,
-                                gpointer data);
-
-  udev_lib mUdev;
-  struct udev_monitor* mMonitor;
-  guint mMonitorSourceID;
-  // Information about currently connected gamepads.
-  nsAutoTArray<Gamepad,4> mGamepads;
-};
-
-// singleton instance
-LinuxGamepadService* gService = nullptr;
-
-void
-LinuxGamepadService::AddDevice(struct udev_device* dev)
-{
-  const char* devpath = mUdev.udev_device_get_devnode(dev);
-  if (!devpath) {
-    return;
-  }
-
-  // Ensure that this device hasn't already been added.
-  for (unsigned int i = 0; i < mGamepads.Length(); i++) {
-    if (strcmp(mGamepads[i].devpath, devpath) == 0) {
-      return;
-    }
-  }
-
-  Gamepad gamepad;
-  snprintf(gamepad.devpath, sizeof(gamepad.devpath), "%s", devpath);
-  GIOChannel* channel = g_io_channel_new_file(devpath, "r", nullptr);
-  if (!channel) {
-    return;
-  }
-
-  g_io_channel_set_flags(channel, G_IO_FLAG_NONBLOCK, nullptr);
-  g_io_channel_set_encoding(channel, nullptr, nullptr);
-  g_io_channel_set_buffered(channel, FALSE);
-  int fd = g_io_channel_unix_get_fd(channel);
-  char name[128];
-  if (ioctl(fd, JSIOCGNAME(sizeof(name)), &name) == -1) {
-    strcpy(name, "unknown");
-  }
-  const char* vendor_id =
-    mUdev.udev_device_get_property_value(dev, "ID_VENDOR_ID");
-  const char* model_id =
-    mUdev.udev_device_get_property_value(dev, "ID_MODEL_ID");
-  if (!vendor_id || !model_id) {
-    struct udev_device* parent =
-      mUdev.udev_device_get_parent_with_subsystem_devtype(dev,
-                                                          "input",
-                                                          nullptr);
-    if (parent) {
-      vendor_id = mUdev.udev_device_get_sysattr_value(parent, "id/vendor");
-      model_id = mUdev.udev_device_get_sysattr_value(parent, "id/product");
-    }
-  }
-  snprintf(gamepad.idstring, sizeof(gamepad.idstring),
-           "%s-%s-%s",
-           vendor_id ? vendor_id : "unknown",
-           model_id ? model_id : "unknown",
-           name);
-
-  char numAxes = 0, numButtons = 0;
-  ioctl(fd, JSIOCGAXES, &numAxes);
-  gamepad.numAxes = numAxes;
-  ioctl(fd, JSIOCGBUTTONS, &numButtons);
-  gamepad.numButtons = numButtons;
-
-  nsRefPtr<GamepadService> service(GamepadService::GetService());
-  gamepad.index = service->AddGamepad(gamepad.idstring,
-                                      mozilla::dom::GamepadMappingType::_empty,
-                                      gamepad.numButtons,
-                                      gamepad.numAxes);
-
-  gamepad.source_id =
-    g_io_add_watch(channel,
-                   GIOCondition(G_IO_IN | G_IO_ERR | G_IO_HUP),
-                   OnGamepadData,
-                   GINT_TO_POINTER(gamepad.index));
-  g_io_channel_unref(channel);
-
-  mGamepads.AppendElement(gamepad);
-}
-
-void
-LinuxGamepadService::RemoveDevice(struct udev_device* dev)
-{
-  const char* devpath = mUdev.udev_device_get_devnode(dev);
-  if (!devpath) {
-    return;
-  }
-
-  nsRefPtr<GamepadService> service(GamepadService::GetService());
-  for (unsigned int i = 0; i < mGamepads.Length(); i++) {
-    if (strcmp(mGamepads[i].devpath, devpath) == 0) {
-      g_source_remove(mGamepads[i].source_id);
-      service->RemoveGamepad(mGamepads[i].index);
-      mGamepads.RemoveElementAt(i);
-      break;
-    }
-  }
-}
-
-void
-LinuxGamepadService::ScanForDevices()
-{
-  struct udev_enumerate* en = mUdev.udev_enumerate_new(mUdev.udev);
-  mUdev.udev_enumerate_add_match_subsystem(en, "input");
-  mUdev.udev_enumerate_scan_devices(en);
-
-  struct udev_list_entry* dev_list_entry;
-  for (dev_list_entry = mUdev.udev_enumerate_get_list_entry(en);
-       dev_list_entry != nullptr;
-       dev_list_entry = mUdev.udev_list_entry_get_next(dev_list_entry)) {
-    const char* path = mUdev.udev_list_entry_get_name(dev_list_entry);
-    struct udev_device* dev = mUdev.udev_device_new_from_syspath(mUdev.udev,
-                                                                 path);
-    if (is_gamepad(dev)) {
-      AddDevice(dev);
-    }
-
-    mUdev.udev_device_unref(dev);
-  }
-
-  mUdev.udev_enumerate_unref(en);
-}
-
-void
-LinuxGamepadService::AddMonitor()
-{
-  // Add a monitor to watch for device changes
-  mMonitor =
-    mUdev.udev_monitor_new_from_netlink(mUdev.udev, "udev");
-  if (!mMonitor) {
-    // Not much we can do here.
-    return;
-  }
-  mUdev.udev_monitor_filter_add_match_subsystem_devtype(mMonitor,
-                                                        "input",
-							nullptr);
-
-  int monitor_fd = mUdev.udev_monitor_get_fd(mMonitor);
-  GIOChannel* monitor_channel = g_io_channel_unix_new(monitor_fd);
-  mMonitorSourceID =
-    g_io_add_watch(monitor_channel,
-                   GIOCondition(G_IO_IN | G_IO_ERR | G_IO_HUP),
-                   OnUdevMonitor,
-                   nullptr);
-  g_io_channel_unref(monitor_channel);
-
-  mUdev.udev_monitor_enable_receiving(mMonitor);
-}
-
-void
-LinuxGamepadService::RemoveMonitor()
-{
-  if (mMonitorSourceID) {
-    g_source_remove(mMonitorSourceID);
-    mMonitorSourceID = 0;
-  }
-  if (mMonitor) {
-    mUdev.udev_monitor_unref(mMonitor);
-    mMonitor = nullptr;
-  }
-}
-
-void
-LinuxGamepadService::Startup()
-{
-  // Don't bother starting up if libudev couldn't be loaded or initialized.
-  if (!mUdev)
-    return;
-
-  AddMonitor();
-  ScanForDevices();
-}
-
-void
-LinuxGamepadService::Shutdown()
-{
-  for (unsigned int i = 0; i < mGamepads.Length(); i++) {
-    g_source_remove(mGamepads[i].source_id);
-  }
-  mGamepads.Clear();
-  RemoveMonitor();
-}
-
-bool
-LinuxGamepadService::is_gamepad(struct udev_device* dev)
-{
-  if (!mUdev.udev_device_get_property_value(dev, "ID_INPUT_JOYSTICK"))
-    return false;
-
-  const char* devpath = mUdev.udev_device_get_devnode(dev);
-  if (!devpath) {
-    return false;
-  }
-  if (strncmp(kJoystickPath, devpath, sizeof(kJoystickPath) - 1) != 0) {
-    return false;
-  }
-
-  return true;
-}
-
-void
-LinuxGamepadService::ReadUdevChange()
-{
-  struct udev_device* dev =
-    mUdev.udev_monitor_receive_device(mMonitor);
-  const char* action = mUdev.udev_device_get_action(dev);
-  if (is_gamepad(dev)) {
-    if (strcmp(action, "add") == 0) {
-      AddDevice(dev);
-    } else if (strcmp(action, "remove") == 0) {
-      RemoveDevice(dev);
-    }
-  }
-  mUdev.udev_device_unref(dev);
-}
-
-// static
-gboolean
-LinuxGamepadService::OnGamepadData(GIOChannel* source,
-                                   GIOCondition condition,
-                                   gpointer data)
-{
-  int index = GPOINTER_TO_INT(data);
-  //TODO: remove gamepad?
-  if (condition & G_IO_ERR || condition & G_IO_HUP)
-    return FALSE;
-
-  while (true) {
-    struct js_event event;
-    gsize count;
-    GError* err = nullptr;
-    if (g_io_channel_read_chars(source,
-				(gchar*)&event,
-				sizeof(event),
-				&count,
-				&err) != G_IO_STATUS_NORMAL ||
-	count == 0) {
-      break;
-    }
-
-    //TODO: store device state?
-    if (event.type & JS_EVENT_INIT) {
-      continue;
-    }
-
-    nsRefPtr<GamepadService> service(GamepadService::GetService());
-    switch (event.type) {
-    case JS_EVENT_BUTTON:
-      service->NewButtonEvent(index, event.number, !!event.value);
-      break;
-    case JS_EVENT_AXIS:
-      service->NewAxisMoveEvent(index, event.number,
-                                ((float)event.value) / kMaxAxisValue);
-      break;
-    }
-  }
-
-  return TRUE;
-}
-
-// static
-gboolean
-LinuxGamepadService::OnUdevMonitor(GIOChannel* source,
-                                   GIOCondition condition,
-                                   gpointer data)
-{
-  if (condition & G_IO_ERR || condition & G_IO_HUP)
-    return FALSE;
-
-  gService->ReadUdevChange();
-  return TRUE;
-}
-
-} // namespace
-
-namespace mozilla {
-namespace hal_impl {
-
-void StartMonitoringGamepadStatus()
-{
-  if (!gService) {
-    gService = new LinuxGamepadService();
-    gService->Startup();
-  }
-}
-
-void StopMonitoringGamepadStatus()
-{
-  if (gService) {
-    gService->Shutdown();
-    delete gService;
-    gService = nullptr;
-  }
-}
-
-} // namespace hal_impl
-} // namespace mozilla
deleted file mode 100644
--- a/hal/linux/udev.h
+++ /dev/null
@@ -1,148 +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/. */
-
-/*
- * This file defines a wrapper around libudev so we can avoid
- * linking directly to it and use dlopen instead.
- */
-
-#ifndef HAL_LINUX_UDEV_H_
-#define HAL_LINUX_UDEV_H_
-
-#include <dlfcn.h>
-
-#include "mozilla/ArrayUtils.h"
-
-namespace mozilla {
-
-struct udev;
-struct udev_device;
-struct udev_enumerate;
-struct udev_list_entry;
-struct udev_monitor;
-
-class udev_lib {
- public:
-  udev_lib() : lib(nullptr),
-               udev(nullptr) {
-    // Be careful about ABI compat! 0 -> 1 didn't change any
-    // symbols this code relies on, per:
-    // https://lists.fedoraproject.org/pipermail/devel/2012-June/168227.html
-    const char* lib_names[] = {"libudev.so.0", "libudev.so.1"};
-    // Check whether a library is already loaded so we don't load two
-    // conflicting libs.
-    for (unsigned i = 0; i < ArrayLength(lib_names); i++) {
-      lib = dlopen(lib_names[i], RTLD_NOLOAD | RTLD_LAZY | RTLD_GLOBAL);
-      if (lib) {
-        break;
-      }
-    }
-    // If nothing loads the first time through, it means no version of libudev
-    // was already loaded.
-    if (!lib) {
-      for (unsigned i = 0; i < ArrayLength(lib_names); i++) {
-        lib = dlopen(lib_names[i], RTLD_LAZY | RTLD_GLOBAL);
-        if (lib) {
-          break;
-        }
-      }
-    }
-    if (lib && LoadSymbols()) {
-      udev = udev_new();
-    }
-  }
-
-  ~udev_lib() {
-    if (udev) {
-      udev_unref(udev);
-    }
-
-    if (lib) {
-      dlclose(lib);
-    }
-  }
-
-  explicit operator bool() {
-    return udev;
-  }
-
- private:
-  bool LoadSymbols() {
-#define DLSYM(s) \
-  do { \
-    s = (typeof(s))dlsym(lib, #s); \
-    if (!s) return false; \
-  } while (0)
-
-    DLSYM(udev_new);
-    DLSYM(udev_unref);
-    DLSYM(udev_device_unref);
-    DLSYM(udev_device_new_from_syspath);
-    DLSYM(udev_device_get_devnode);
-    DLSYM(udev_device_get_parent_with_subsystem_devtype);
-    DLSYM(udev_device_get_property_value);
-    DLSYM(udev_device_get_action);
-    DLSYM(udev_device_get_sysattr_value);
-    DLSYM(udev_enumerate_new);
-    DLSYM(udev_enumerate_unref);
-    DLSYM(udev_enumerate_add_match_subsystem);
-    DLSYM(udev_enumerate_scan_devices);
-    DLSYM(udev_enumerate_get_list_entry);
-    DLSYM(udev_list_entry_get_next);
-    DLSYM(udev_list_entry_get_name);
-    DLSYM(udev_monitor_new_from_netlink);
-    DLSYM(udev_monitor_filter_add_match_subsystem_devtype);
-    DLSYM(udev_monitor_enable_receiving);
-    DLSYM(udev_monitor_get_fd);
-    DLSYM(udev_monitor_receive_device);
-    DLSYM(udev_monitor_unref);
-#undef DLSYM
-    return true;
-  }
-
-  void* lib;
-
- public:
-  struct udev* udev;
-
-  // Function pointers returned from dlsym.
-  struct udev* (*udev_new)(void);
-  void (*udev_unref)(struct udev*);
-
-  void (*udev_device_unref)(struct udev_device*);
-  struct udev_device* (*udev_device_new_from_syspath)(struct udev*,
-                                                      const char*);
-  const char* (*udev_device_get_devnode)(struct udev_device*);
-  struct udev_device* (*udev_device_get_parent_with_subsystem_devtype)
-    (struct udev_device*, const char*, const char*);
-  const char* (*udev_device_get_property_value)(struct udev_device*,
-                                                const char*);
-  const char* (*udev_device_get_action)(struct udev_device*);
-  const char* (*udev_device_get_sysattr_value)(struct udev_device*,
-                                               const char*);
-
-  struct udev_enumerate* (*udev_enumerate_new)(struct udev*);
-  void (*udev_enumerate_unref)(struct udev_enumerate*);
-  int (*udev_enumerate_add_match_subsystem)(struct udev_enumerate*,
-                                            const char*);
-  int (*udev_enumerate_scan_devices)(struct udev_enumerate*);
-  struct udev_list_entry* (*udev_enumerate_get_list_entry)
-    (struct udev_enumerate*);
-
-  struct udev_list_entry* (*udev_list_entry_get_next)(struct udev_list_entry *);
-  const char* (*udev_list_entry_get_name)(struct udev_list_entry*);
-
-  struct udev_monitor* (*udev_monitor_new_from_netlink)(struct udev*,
-                                                        const char*);
-  int (*udev_monitor_filter_add_match_subsystem_devtype)
-    (struct udev_monitor*, const char*, const char*);
-  int (*udev_monitor_enable_receiving)(struct udev_monitor*);
-  int (*udev_monitor_get_fd)(struct udev_monitor*);
-  struct udev_device* (*udev_monitor_receive_device)(struct udev_monitor*);
-  void (*udev_monitor_unref)(struct udev_monitor*);
-};
-
-} // namespace mozilla
-
-#endif // HAL_LINUX_UDEV_H_
--- a/hal/moz.build
+++ b/hal/moz.build
@@ -27,37 +27,16 @@ UNIFIED_SOURCES += [
     'WindowIdentifier.cpp',
 ]
 
 # Hal.cpp cannot be built in unified mode because it relies on HalImpl.h.
 SOURCES += [
     'Hal.cpp',
 ]
 
-if CONFIG['MOZ_GAMEPAD_BACKEND'] == 'stub':
-    UNIFIED_SOURCES += [
-        'fallback/FallbackGamepad.cpp'
-    ]
-elif CONFIG['MOZ_GAMEPAD_BACKEND'] == 'cocoa':
-    UNIFIED_SOURCES += [
-        'cocoa/CocoaGamepad.cpp'
-    ]
-elif CONFIG['MOZ_GAMEPAD_BACKEND'] == 'windows':
-    UNIFIED_SOURCES += [
-        'windows/WindowsGamepad.cpp'
-    ]
-elif CONFIG['MOZ_GAMEPAD_BACKEND'] == 'linux':
-    UNIFIED_SOURCES += [
-        'linux/LinuxGamepad.cpp'
-    ]
-elif CONFIG['MOZ_GAMEPAD_BACKEND'] == 'android':
-    UNIFIED_SOURCES += [
-        'android/AndroidGamepad.cpp'
-    ]
-
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
     UNIFIED_SOURCES += [
         'android/AndroidSensor.cpp',
         'fallback/FallbackAlarm.cpp',
         'fallback/FallbackPower.cpp',
         'linux/LinuxMemory.cpp',
     ]
     # AndroidHal.cpp cannot be built in unified mode because it relies on HalImpl.h.
--- a/hal/sandbox/SandboxHal.cpp
+++ b/hal/sandbox/SandboxHal.cpp
@@ -266,23 +266,16 @@ EnableSensorNotifications(SensorType aSe
   Hal()->SendEnableSensorNotifications(aSensor);
 }
 
 void
 DisableSensorNotifications(SensorType aSensor) {
   Hal()->SendDisableSensorNotifications(aSensor);
 }
 
-//TODO: bug 852944 - IPC implementations of these
-void StartMonitoringGamepadStatus()
-{}
-
-void StopMonitoringGamepadStatus()
-{}
-
 void
 EnableWakeLockNotifications()
 {
   Hal()->SendEnableWakeLockNotifications();
 }
 
 void
 DisableWakeLockNotifications()
deleted file mode 100644
--- a/hal/windows/WindowsGamepad.cpp
+++ /dev/null
@@ -1,1029 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 <algorithm>
-#include <cstddef>
-
-#ifndef UNICODE
-#define UNICODE
-#endif
-#include <windows.h>
-#include <hidsdi.h>
-#include <stdio.h>
-#include <xinput.h>
-
-#include "nsIComponentManager.h"
-#include "nsIObserver.h"
-#include "nsIObserverService.h"
-#include "nsITimer.h"
-#include "nsTArray.h"
-#include "mozilla/ArrayUtils.h"
-#include "mozilla/dom/GamepadService.h"
-#include "mozilla/Services.h"
-
-namespace {
-
-using namespace mozilla::dom;
-using mozilla::ArrayLength;
-
-// USB HID usage tables, page 1 (Hat switch)
-const unsigned kUsageDpad = 0x39;
-// USB HID usage tables, page 1, 0x30 = X
-const unsigned kFirstAxis = 0x30;
-
-// USB HID usage tables
-const unsigned kDesktopUsagePage = 0x1;
-const unsigned kButtonUsagePage = 0x9;
-
-// Arbitrary. In practice 10 buttons/6 axes is the near maximum.
-const unsigned kMaxButtons = 32;
-const unsigned kMaxAxes = 32;
-
-// Multiple devices-changed notifications can be sent when a device
-// is connected, because USB devices consist of multiple logical devices.
-// Therefore, we wait a bit after receiving one before looking for
-// device changes.
-const uint32_t kDevicesChangedStableDelay = 200;
-// XInput is a purely polling-driven API, so we need to
-// poll it periodically. 50ms is arbitrarily chosen.
-const uint32_t kXInputPollInterval = 50;
-
-const UINT kRawInputError = (UINT)-1;
-
-#ifndef XUSER_MAX_COUNT
-#define XUSER_MAX_COUNT 4
-#endif
-
-const struct {
-  int usagePage;
-  int usage;
-} kUsagePages[] = {
-  // USB HID usage tables, page 1
-  { kDesktopUsagePage, 4 },  // Joystick
-  { kDesktopUsagePage, 5 }   // Gamepad
-};
-
-const struct {
-  WORD button;
-  int mapped;
-} kXIButtonMap[] = {
-  { XINPUT_GAMEPAD_DPAD_UP, 12 },
-  { XINPUT_GAMEPAD_DPAD_DOWN, 13 },
-  { XINPUT_GAMEPAD_DPAD_LEFT, 14 },
-  { XINPUT_GAMEPAD_DPAD_RIGHT, 15 },
-  { XINPUT_GAMEPAD_START, 9 },
-  { XINPUT_GAMEPAD_BACK, 8 },
-  { XINPUT_GAMEPAD_LEFT_THUMB, 10 },
-  { XINPUT_GAMEPAD_RIGHT_THUMB, 11 },
-  { XINPUT_GAMEPAD_LEFT_SHOULDER, 4 },
-  { XINPUT_GAMEPAD_RIGHT_SHOULDER, 5 },
-  { XINPUT_GAMEPAD_A, 0 },
-  { XINPUT_GAMEPAD_B, 1 },
-  { XINPUT_GAMEPAD_X, 2 },
-  { XINPUT_GAMEPAD_Y, 3 }
-};
-const size_t kNumMappings = ArrayLength(kXIButtonMap);
-
-enum GamepadType {
-  kNoGamepad = 0,
-  kRawInputGamepad,
-  kXInputGamepad
-};
-
-class WindowsGamepadService;
-WindowsGamepadService* gService = nullptr;
-
-struct Gamepad {
-  GamepadType type;
-
-  // Handle to raw input device
-  HANDLE handle;
-
-  // XInput Index of the user's controller. Passed to XInputGetState.
-  DWORD userIndex;
-
-  // Last-known state of the controller.
-  XINPUT_STATE state;
-
-  // ID from the GamepadService, also used as the index into
-  // WindowsGamepadService::mGamepads.
-  int id;
-
-  // Information about the physical device.
-  unsigned numAxes;
-  unsigned numButtons;
-  bool hasDpad;
-  HIDP_VALUE_CAPS dpadCaps;
-
-  bool buttons[kMaxButtons];
-  struct {
-    HIDP_VALUE_CAPS caps;
-    double value;
-  } axes[kMaxAxes];
-
-  // Used during rescan to find devices that were disconnected.
-  bool present;
-};
-
-// Drop this in favor of decltype when we require a new enough SDK.
-typedef void (WINAPI *XInputEnable_func)(BOOL);
-
-// RAII class to wrap loading the XInput DLL
-class XInputLoader {
-public:
-  XInputLoader() : module(nullptr),
-                   mXInputEnable(nullptr),
-                   mXInputGetState(nullptr) {
-    // xinput1_4.dll exists on Windows 8
-    // xinput9_1_0.dll exists on Windows 7 and Vista
-    // xinput1_3.dll shipped with the DirectX SDK
-    const wchar_t* dlls[] = {L"xinput1_4.dll",
-                             L"xinput9_1_0.dll",
-                             L"xinput1_3.dll"};
-    const size_t kNumDLLs = ArrayLength(dlls);
-    for (size_t i = 0; i < kNumDLLs; ++i) {
-      module = LoadLibraryW(dlls[i]);
-      if (module) {
-        mXInputEnable = reinterpret_cast<XInputEnable_func>(
-         GetProcAddress(module, "XInputEnable"));
-        mXInputGetState = reinterpret_cast<decltype(XInputGetState)*>(
-         GetProcAddress(module, "XInputGetState"));
-        if (mXInputEnable) {
-          mXInputEnable(TRUE);
-        }
-        break;
-      }
-    }
-  }
-
-  ~XInputLoader() {
-    //mXInputEnable = nullptr;
-    mXInputGetState = nullptr;
-
-    if (module) {
-      FreeLibrary(module);
-    }
-  }
-
-  operator bool() {
-    return module && mXInputGetState;
-  }
-
-  HMODULE module;
-  decltype(XInputGetState) *mXInputGetState;
-  XInputEnable_func mXInputEnable;
-};
-
-bool
-GetPreparsedData(HANDLE handle, nsTArray<uint8_t>& data)
-{
-  UINT size;
-  if (GetRawInputDeviceInfo(handle, RIDI_PREPARSEDDATA, nullptr, &size) == kRawInputError) {
-    return false;
-  }
-  data.SetLength(size);
-  return GetRawInputDeviceInfo(handle, RIDI_PREPARSEDDATA,
-                               data.Elements(), &size) > 0;
-}
-
-/*
- * Given an axis value and a minimum and maximum range,
- * scale it to be in the range -1.0 .. 1.0.
- */
-double
-ScaleAxis(ULONG value, LONG min, LONG max)
-{
-  return  2.0 * (value - min) / (max - min) - 1.0;
-}
-
-/*
- * Given a value from a d-pad (POV hat in USB HID terminology),
- * represent it as 4 buttons, one for each cardinal direction.
- */
-void
-UnpackDpad(LONG dpad_value, const Gamepad* gamepad, bool buttons[kMaxButtons])
-{
-  const unsigned kUp = gamepad->numButtons - 4;
-  const unsigned kDown = gamepad->numButtons - 3;
-  const unsigned kLeft = gamepad->numButtons - 2;
-  const unsigned kRight = gamepad->numButtons - 1;
-
-  // Different controllers have different ways of representing
-  // "nothing is pressed", but they're all outside the range of values.
-  if (dpad_value < gamepad->dpadCaps.LogicalMin
-      || dpad_value > gamepad->dpadCaps.LogicalMax) {
-    // Nothing is pressed.
-    return;
-  }
-
-  // Normalize value to start at 0.
-  int value = dpad_value - gamepad->dpadCaps.LogicalMin;
-
-  // Value will be in the range 0-7. The value represents the
-  // position of the d-pad around a circle, with 0 being straight up,
-  // 2 being right, 4 being straight down, and 6 being left.
-  if (value < 2 || value > 6) {
-    buttons[kUp] = true;
-  }
-  if (value > 2 && value < 6) {
-    buttons[kDown] = true;
-  }
-  if (value > 4) {
-    buttons[kLeft] = true;
-  }
-  if (value > 0 && value < 4) {
-    buttons[kRight] = true;
-  }
-}
-
-/*
- * Return true if this USB HID usage page and usage are of a type we
- * know how to handle.
- */
-bool
-SupportedUsage(USHORT page, USHORT usage)
-{
-  for (unsigned i = 0; i < ArrayLength(kUsagePages); i++) {
-    if (page == kUsagePages[i].usagePage && usage == kUsagePages[i].usage) {
-      return true;
-    }
-  }
-  return false;
-}
-
-class Observer : public nsIObserver {
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIOBSERVER
-
-  Observer(WindowsGamepadService& svc) : mSvc(svc),
-                                         mObserving(true)
-  {
-    nsresult rv;
-    mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
-    nsCOMPtr<nsIObserverService> observerService =
-      mozilla::services::GetObserverService();
-    observerService->AddObserver(this,
-                                 NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID,
-                                 false);
-  }
-
-  void Stop()
-  {
-    if (mTimer) {
-      mTimer->Cancel();
-    }
-    if (mObserving) {
-      nsCOMPtr<nsIObserverService> observerService =
-        mozilla::services::GetObserverService();
-      observerService->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
-      mObserving = false;
-    }
-  }
-
-  void SetDeviceChangeTimer()
-  {
-    // Set stable timer, since we will get multiple devices-changed
-    // notifications at once
-    if (mTimer) {
-      mTimer->Cancel();
-      mTimer->Init(this, kDevicesChangedStableDelay, nsITimer::TYPE_ONE_SHOT);
-    }
-  }
-
-private:
-  virtual ~Observer()
-  {
-    Stop();
-  }
-
-  // Gamepad service owns us, we just hold a reference back to it.
-  WindowsGamepadService& mSvc;
-  nsCOMPtr<nsITimer> mTimer;
-  bool mObserving;
-};
-
-NS_IMPL_ISUPPORTS(Observer, nsIObserver);
-
-class HIDLoader {
-public:
-  HIDLoader() : mModule(LoadLibraryW(L"hid.dll")),
-                mHidD_GetProductString(nullptr),
-                mHidP_GetCaps(nullptr),
-                mHidP_GetButtonCaps(nullptr),
-                mHidP_GetValueCaps(nullptr),
-                mHidP_GetUsages(nullptr),
-                mHidP_GetUsageValue(nullptr),
-                mHidP_GetScaledUsageValue(nullptr)
-  {
-    if (mModule) {
-      mHidD_GetProductString = reinterpret_cast<decltype(HidD_GetProductString)*>(GetProcAddress(mModule, "HidD_GetProductString"));
-      mHidP_GetCaps = reinterpret_cast<decltype(HidP_GetCaps)*>(GetProcAddress(mModule, "HidP_GetCaps"));
-      mHidP_GetButtonCaps = reinterpret_cast<decltype(HidP_GetButtonCaps)*>(GetProcAddress(mModule, "HidP_GetButtonCaps"));
-      mHidP_GetValueCaps = reinterpret_cast<decltype(HidP_GetValueCaps)*>(GetProcAddress(mModule, "HidP_GetValueCaps"));
-      mHidP_GetUsages = reinterpret_cast<decltype(HidP_GetUsages)*>(GetProcAddress(mModule, "HidP_GetUsages"));
-      mHidP_GetUsageValue = reinterpret_cast<decltype(HidP_GetUsageValue)*>(GetProcAddress(mModule, "HidP_GetUsageValue"));
-      mHidP_GetScaledUsageValue = reinterpret_cast<decltype(HidP_GetScaledUsageValue)*>(GetProcAddress(mModule, "HidP_GetScaledUsageValue"));
-    }
-  }
-
-  ~HIDLoader() {
-    if (mModule) {
-      FreeLibrary(mModule);
-    }
-  }
-
-  operator bool() {
-    return mModule &&
-      mHidD_GetProductString &&
-      mHidP_GetCaps &&
-      mHidP_GetButtonCaps &&
-      mHidP_GetValueCaps &&
-      mHidP_GetUsages &&
-      mHidP_GetUsageValue &&
-      mHidP_GetScaledUsageValue;
-  }
-
-  decltype(HidD_GetProductString) *mHidD_GetProductString;
-  decltype(HidP_GetCaps) *mHidP_GetCaps;
-  decltype(HidP_GetButtonCaps) *mHidP_GetButtonCaps;
-  decltype(HidP_GetValueCaps) *mHidP_GetValueCaps;
-  decltype(HidP_GetUsages) *mHidP_GetUsages;
-  decltype(HidP_GetUsageValue) *mHidP_GetUsageValue;
-  decltype(HidP_GetScaledUsageValue) *mHidP_GetScaledUsageValue;
-
-private:
-  HMODULE mModule;
-};
-
-class WindowsGamepadService {
-public:
-  WindowsGamepadService();
-  virtual ~WindowsGamepadService()
-  {
-    Cleanup();
-  }
-
-  enum DeviceChangeType {
-    DeviceChangeNotification,
-    DeviceChangeStable
-  };
-  void DevicesChanged(DeviceChangeType type);
-  void Startup();
-  void Shutdown();
-  // Parse gamepad input from a WM_INPUT message.
-  bool HandleRawInput(HRAWINPUT handle);
-
-private:
-  void ScanForDevices();
-  // Look for connected raw input devices.
-  void ScanForRawInputDevices();
-  // Look for connected XInput devices.
-  bool ScanForXInputDevices();
-  bool HaveXInputGamepad(int userIndex);
-
-  // Timer callback for XInput polling
-  static void XInputPollTimerCallback(nsITimer* aTimer, void* aClosure);
-  void PollXInput();
-  void CheckXInputChanges(Gamepad& gamepad, XINPUT_STATE& state);
-
-  // Get information about a raw input gamepad.
-  bool GetRawGamepad(HANDLE handle);
-  void Cleanup();
-
-  // List of connected devices.
-  nsTArray<Gamepad> mGamepads;
-
-  nsRefPtr<Observer> mObserver;
-  nsCOMPtr<nsITimer> mXInputPollTimer;
-
-  HIDLoader mHID;
-  XInputLoader mXInput;
-};
-
-
-WindowsGamepadService::WindowsGamepadService()
-{
-  nsresult rv;
-  mXInputPollTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
-  mObserver = new Observer(*this);
-}
-
-void
-WindowsGamepadService::ScanForRawInputDevices()
-{
-  if (!mHID) {
-    return;
-  }
-
-  UINT numDevices;
-  if (GetRawInputDeviceList(nullptr, &numDevices, sizeof(RAWINPUTDEVICELIST))
-      == kRawInputError) {
-    return;
-  }
-  nsTArray<RAWINPUTDEVICELIST> devices(numDevices);
-  devices.SetLength(numDevices);
-  if (GetRawInputDeviceList(devices.Elements(), &numDevices,
-                            sizeof(RAWINPUTDEVICELIST)) == kRawInputError) {
-    return;
-  }
-
-  for (unsigned i = 0; i < devices.Length(); i++) {
-    if (devices[i].dwType == RIM_TYPEHID) {
-      GetRawGamepad(devices[i].hDevice);
-    }
-  }
-}
-
-bool
-WindowsGamepadService::HaveXInputGamepad(int userIndex)
-{
-  for (unsigned int i = 0; i < mGamepads.Length(); i++) {
-    if (mGamepads[i].type == kXInputGamepad
-        && mGamepads[i].userIndex == userIndex) {
-      mGamepads[i].present = true;
-      return true;
-    }
-  }
-  return false;
-}
-
-bool
-WindowsGamepadService::ScanForXInputDevices()
-{
-  MOZ_ASSERT(mXInput, "XInput should be present!");
-
-  nsRefPtr<GamepadService> gamepadsvc(GamepadService::GetService());
-  if (!gamepadsvc) {
-    return false;
-  }
-
-  bool found = false;
-  for (int i = 0; i < XUSER_MAX_COUNT; i++) {
-    XINPUT_STATE state = {};
-    if (mXInput.mXInputGetState(i, &state) != ERROR_SUCCESS) {
-      continue;
-    }
-    found = true;
-    // See if this device is already present in our list.
-    if (HaveXInputGamepad(i)) {
-      continue;
-    }
-
-    // Not already present, add it.
-    Gamepad gamepad = {};
-    gamepad.type = kXInputGamepad;
-    gamepad.present = true;
-    gamepad.state = state;
-    gamepad.userIndex = i;
-    gamepad.numButtons = kStandardGamepadButtons;
-    gamepad.numAxes = kStandardGamepadAxes;
-    gamepad.id = gamepadsvc->AddGamepad("xinput",
-                                        GamepadMappingType::Standard,
-                                        kStandardGamepadButtons,
-                                        kStandardGamepadAxes);
-    mGamepads.AppendElement(gamepad);
-  }
-
-  return found;
-}
-
-void
-WindowsGamepadService::ScanForDevices()
-{
-  for (int i = mGamepads.Length() - 1; i >= 0; i--) {
-    mGamepads[i].present = false;
-  }
-
-  if (mHID) {
-    ScanForRawInputDevices();
-  }
-  if (mXInput) {
-    mXInputPollTimer->Cancel();
-    if (ScanForXInputDevices()) {
-      mXInputPollTimer->InitWithFuncCallback(XInputPollTimerCallback,
-                                             this,
-                                             kXInputPollInterval,
-                                             nsITimer::TYPE_REPEATING_SLACK);
-    }
-  }
-
-  nsRefPtr<GamepadService> gamepadsvc(GamepadService::GetService());
-  if (!gamepadsvc) {
-    return;
-  }
-  // Look for devices that are no longer present and remove them.
-  for (int i = mGamepads.Length() - 1; i >= 0; i--) {
-    if (!mGamepads[i].present) {
-      gamepadsvc->RemoveGamepad(mGamepads[i].id);
-      mGamepads.RemoveElementAt(i);
-    }
-  }
-}
-
-// static
-void
-WindowsGamepadService::XInputPollTimerCallback(nsITimer* aTimer,
-                                               void* aClosure)
-{
-  WindowsGamepadService* self =
-    reinterpret_cast<WindowsGamepadService*>(aClosure);
-  self->PollXInput();
-}
-
-void
-WindowsGamepadService::PollXInput()
-{
-  for (unsigned int i = 0; i < mGamepads.Length(); i++) {
-    if (mGamepads[i].type != kXInputGamepad) {
-      continue;
-    }
-
-    XINPUT_STATE state = {};
-    DWORD res = mXInput.mXInputGetState(mGamepads[i].userIndex, &state);
-    if (res == ERROR_SUCCESS
-        && state.dwPacketNumber != mGamepads[i].state.dwPacketNumber) {
-        CheckXInputChanges(mGamepads[i], state);
-    }
-  }
-}
-
-void WindowsGamepadService::CheckXInputChanges(Gamepad& gamepad,
-                                               XINPUT_STATE& state) {
-  nsRefPtr<GamepadService> gamepadsvc(GamepadService::GetService());
-  // Handle digital buttons first
-  for (size_t b = 0; b < kNumMappings; b++) {
-    if (state.Gamepad.wButtons & kXIButtonMap[b].button &&
-        !(gamepad.state.Gamepad.wButtons & kXIButtonMap[b].button)) {
-      // Button pressed
-      gamepadsvc->NewButtonEvent(gamepad.id, kXIButtonMap[b].mapped, true);
-    } else if (!(state.Gamepad.wButtons & kXIButtonMap[b].button) &&
-               gamepad.state.Gamepad.wButtons & kXIButtonMap[b].button) {
-      // Button released
-      gamepadsvc->NewButtonEvent(gamepad.id, kXIButtonMap[b].mapped, false);
-    }
-  }
-
-  // Then triggers
-  if (state.Gamepad.bLeftTrigger != gamepad.state.Gamepad.bLeftTrigger) {
-    bool pressed =
-      state.Gamepad.bLeftTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
-    gamepadsvc->NewButtonEvent(gamepad.id, kButtonLeftTrigger,
-                               pressed, state.Gamepad.bLeftTrigger / 255.0);
-  }
-  if (state.Gamepad.bRightTrigger != gamepad.state.Gamepad.bRightTrigger) {
-    bool pressed =
-      state.Gamepad.bRightTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
-    gamepadsvc->NewButtonEvent(gamepad.id, kButtonRightTrigger,
-                               pressed, state.Gamepad.bRightTrigger / 255.0);
-  }
-
-  // Finally deal with analog sticks
-  // TODO: bug 1001955 - Support deadzones.
-  if (state.Gamepad.sThumbLX != gamepad.state.Gamepad.sThumbLX) {
-    gamepadsvc->NewAxisMoveEvent(gamepad.id, kLeftStickXAxis,
-                                 state.Gamepad.sThumbLX / 32767.0);
-  }
-  if (state.Gamepad.sThumbLY != gamepad.state.Gamepad.sThumbLY) {
-    gamepadsvc->NewAxisMoveEvent(gamepad.id, kLeftStickYAxis,
-                                 -1.0 * state.Gamepad.sThumbLY / 32767.0);
-  }
-  if (state.Gamepad.sThumbRX != gamepad.state.Gamepad.sThumbRX) {
-    gamepadsvc->NewAxisMoveEvent(gamepad.id, kRightStickXAxis,
-                                 state.Gamepad.sThumbRX / 32767.0);
-  }
-  if (state.Gamepad.sThumbRY != gamepad.state.Gamepad.sThumbRY) {
-    gamepadsvc->NewAxisMoveEvent(gamepad.id, kRightStickYAxis,
-                                 -1.0 * state.Gamepad.sThumbRY / 32767.0);
-  }
-  gamepad.state = state;
-}
-
-// Used to sort a list of axes by HID usage.
-class HidValueComparator {
-public:
-  bool Equals(const HIDP_VALUE_CAPS& c1, const HIDP_VALUE_CAPS& c2) const
-  {
-    return c1.UsagePage == c2.UsagePage && c1.Range.UsageMin == c2.Range.UsageMin;
-  }
-  bool LessThan(const HIDP_VALUE_CAPS& c1, const HIDP_VALUE_CAPS& c2) const
-  {
-    if (c1.UsagePage == c2.UsagePage) {
-      return c1.Range.UsageMin < c2.Range.UsageMin;
-    }
-    return c1.UsagePage < c2.UsagePage;
-  }
-};
-
-bool
-WindowsGamepadService::GetRawGamepad(HANDLE handle)
-{
-  if (!mHID) {
-    return false;
-  }
-
-  for (unsigned i = 0; i < mGamepads.Length(); i++) {
-    if (mGamepads[i].type == kRawInputGamepad && mGamepads[i].handle == handle) {
-      mGamepads[i].present = true;
-      return true;
-    }
-  }
-
-  RID_DEVICE_INFO rdi = {};
-  UINT size = rdi.cbSize = sizeof(RID_DEVICE_INFO);
-  if (GetRawInputDeviceInfo(handle, RIDI_DEVICEINFO, &rdi, &size) == kRawInputError) {
-    return false;
-  }
-  // Ensure that this is a device we care about
-  if (!SupportedUsage(rdi.hid.usUsagePage, rdi.hid.usUsage)) {
-    return false;
-  }
-
-  Gamepad gamepad = {};
-
-  // Device name is a mostly-opaque string.
-  if (GetRawInputDeviceInfo(handle, RIDI_DEVICENAME, nullptr, &size) == kRawInputError) {
-    return false;
-  }
-
-  nsTArray<wchar_t> devname(size);
-  devname.SetLength(size);
-  if (GetRawInputDeviceInfo(handle, RIDI_DEVICENAME, devname.Elements(), &size) == kRawInputError) {
-    return false;
-  }
-
-  // Per http://msdn.microsoft.com/en-us/library/windows/desktop/ee417014.aspx
-  // device names containing "IG_" are XInput controllers. Ignore those
-  // devices since we'll handle them with XInput.
-  if (wcsstr(devname.Elements(), L"IG_")) {
-    return false;
-  }
-
-  // Product string is a human-readable name.
-  // Per http://msdn.microsoft.com/en-us/library/windows/hardware/ff539681%28v=vs.85%29.aspx
-  // "For USB devices, the maximum string length is 126 wide characters (not including the terminating NULL character)."
-  wchar_t name[128] = { 0 };
-  size = sizeof(name);
-  nsTArray<char> gamepad_name;
-  HANDLE hid_handle = CreateFile(devname.Elements(), GENERIC_READ | GENERIC_WRITE,
-    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
-  if (hid_handle) {
-    if (mHID.mHidD_GetProductString(hid_handle, &name, size)) {
-      int bytes = WideCharToMultiByte(CP_UTF8, 0, name, -1, nullptr, 0, nullptr,
-                                      nullptr);
-      gamepad_name.SetLength(bytes);
-      WideCharToMultiByte(CP_UTF8, 0, name, -1, gamepad_name.Elements(),
-                          bytes, nullptr, nullptr);
-    }
-    CloseHandle(hid_handle);
-  }
-  if (gamepad_name.Length() == 0 || !gamepad_name[0]) {
-    const char kUnknown[] = "Unknown Gamepad";
-    gamepad_name.SetLength(ArrayLength(kUnknown));
-    strcpy_s(gamepad_name.Elements(), gamepad_name.Length(), kUnknown);
-  }
-
-  char gamepad_id[256] = { 0 };
-  _snprintf_s(gamepad_id, _TRUNCATE, "%04x-%04x-%s", rdi.hid.dwVendorId,
-              rdi.hid.dwProductId, gamepad_name.Elements());
-
-  nsTArray<uint8_t> preparsedbytes;
-  if (!GetPreparsedData(handle, preparsedbytes)) {
-    return false;
-  }
-
-  PHIDP_PREPARSED_DATA parsed =
-    reinterpret_cast<PHIDP_PREPARSED_DATA>(preparsedbytes.Elements());
-  HIDP_CAPS caps;
-  if (mHID.mHidP_GetCaps(parsed, &caps) != HIDP_STATUS_SUCCESS) {
-    return false;
-  }
-
-  // Enumerate buttons.
-  USHORT count = caps.NumberInputButtonCaps;
-  nsTArray<HIDP_BUTTON_CAPS> buttonCaps(count);
-  buttonCaps.SetLength(count);
-  if (mHID.mHidP_GetButtonCaps(HidP_Input, buttonCaps.Elements(), &count, parsed)
-      != HIDP_STATUS_SUCCESS) {
-    return false;
-  }
-  for (unsigned i = 0; i < count; i++) {
-    // Each buttonCaps is typically a range of buttons.
-    gamepad.numButtons +=
-      buttonCaps[i].Range.UsageMax - buttonCaps[i].Range.UsageMin + 1;
-  }
-  gamepad.numButtons = std::min(gamepad.numButtons, kMaxButtons);
-
-  // Enumerate value caps, which represent axes and d-pads.
-  count = caps.NumberInputValueCaps;
-  nsTArray<HIDP_VALUE_CAPS> valueCaps(count);
-  valueCaps.SetLength(count);
-  if (mHID.mHidP_GetValueCaps(HidP_Input, valueCaps.Elements(), &count, parsed)
-      != HIDP_STATUS_SUCCESS) {
-    return false;
-  }
-  nsTArray<HIDP_VALUE_CAPS> axes;
-  // Sort the axes by usagePage and usage to expose a consistent ordering.
-  HidValueComparator comparator;
-  for (unsigned i = 0; i < count; i++) {
-    if (valueCaps[i].UsagePage == kDesktopUsagePage
-        && valueCaps[i].Range.UsageMin == kUsageDpad
-        // Don't know how to handle d-pads that return weird values.
-        && valueCaps[i].LogicalMax - valueCaps[i].LogicalMin == 7
-        // Can't overflow buttons
-        && gamepad.numButtons + 4 < kMaxButtons) {
-      // d-pad gets special handling.
-      // Ostensibly HID devices can expose multiple d-pads, but this
-      // doesn't happen in practice.
-      gamepad.hasDpad = true;
-      gamepad.dpadCaps = valueCaps[i];
-      // Expose d-pad as 4 additional buttons.
-      gamepad.numButtons += 4;
-    } else {
-      axes.InsertElementSorted(valueCaps[i], comparator);
-    }
-  }
-
-  gamepad.numAxes = std::min<size_t>(axes.Length(), kMaxAxes);
-  for (unsigned i = 0; i < gamepad.numAxes; i++) {
-    if (i >= kMaxAxes) {
-      break;
-    }
-    gamepad.axes[i].caps = axes[i];
-  }
-  gamepad.type = kRawInputGamepad;
-  gamepad.handle = handle;
-  gamepad.present = true;
-
-  nsRefPtr<GamepadService> gamepadsvc(GamepadService::GetService());
-  if (!gamepadsvc) {
-    return false;
-  }
-
-  gamepad.id = gamepadsvc->AddGamepad(gamepad_id,
-                                      GamepadMappingType::_empty,
-                                      gamepad.numButtons,
-                                      gamepad.numAxes);
-  mGamepads.AppendElement(gamepad);
-  return true;
-}
-
-bool
-WindowsGamepadService::HandleRawInput(HRAWINPUT handle)
-{
-  if (!mHID) {
-    return false;
-  }
-  nsRefPtr<GamepadService> gamepadsvc(GamepadService::GetService());
-  if (!gamepadsvc) {
-    return false;
-  }
-
-  // First, get data from the handle
-  UINT size;
-  GetRawInputData(handle, RID_INPUT, nullptr, &size, sizeof(RAWINPUTHEADER));
-  nsTArray<uint8_t> data(size);
-  data.SetLength(size);
-  if (GetRawInputData(handle, RID_INPUT, data.Elements(), &size,
-                      sizeof(RAWINPUTHEADER)) == kRawInputError) {
-    return false;
-  }
-  PRAWINPUT raw = reinterpret_cast<PRAWINPUT>(data.Elements());
-
-  Gamepad* gamepad = nullptr;
-  for (unsigned i = 0; i < mGamepads.Length(); i++) {
-    if (mGamepads[i].type == kRawInputGamepad
-        && mGamepads[i].handle == raw->header.hDevice) {
-      gamepad = &mGamepads[i];
-      break;
-    }
-  }
-  if (gamepad == nullptr) {
-    return false;
-  }
-
-  // Second, get the preparsed data
-  nsTArray<uint8_t> parsedbytes;
-  if (!GetPreparsedData(raw->header.hDevice, parsedbytes)) {
-    return false;
-  }
-  PHIDP_PREPARSED_DATA parsed =
-    reinterpret_cast<PHIDP_PREPARSED_DATA>(parsedbytes.Elements());
-
-  // Get all the pressed buttons.
-  nsTArray<USAGE> usages(gamepad->numButtons);
-  usages.SetLength(gamepad->numButtons);
-  ULONG usageLength = gamepad->numButtons;
-  if (mHID.mHidP_GetUsages(HidP_Input, kButtonUsagePage, 0, usages.Elements(),
-                     &usageLength, parsed, (PCHAR)raw->data.hid.bRawData,
-                     raw->data.hid.dwSizeHid) != HIDP_STATUS_SUCCESS) {
-    return false;
-  }
-
-  bool buttons[kMaxButtons] = { false };
-  usageLength = std::min<ULONG>(usageLength, kMaxButtons);
-  for (unsigned i = 0; i < usageLength; i++) {
-    buttons[usages[i] - 1] = true;
-  }
-
-  if (gamepad->hasDpad) {
-    // Get d-pad position as 4 buttons.
-    ULONG value;
-    if (mHID.mHidP_GetUsageValue(HidP_Input, gamepad->dpadCaps.UsagePage, 0, gamepad->dpadCaps.Range.UsageMin, &value, parsed, (PCHAR)raw->data.hid.bRawData, raw->data.hid.dwSizeHid) == HIDP_STATUS_SUCCESS) {
-      UnpackDpad(static_cast<LONG>(value), gamepad, buttons);
-    }
-  }
-
-  for (unsigned i = 0; i < gamepad->numButtons; i++) {
-    if (gamepad->buttons[i] != buttons[i]) {
-      gamepadsvc->NewButtonEvent(gamepad->id, i, buttons[i]);
-      gamepad->buttons[i] = buttons[i];
-    }
-  }
-
-  // Get all axis values.
-  for (unsigned i = 0; i < gamepad->numAxes; i++) {
-    double new_value;
-    if (gamepad->axes[i].caps.LogicalMin < 0) {
-LONG value;
-      if (mHID.mHidP_GetScaledUsageValue(HidP_Input, gamepad->axes[i].caps.UsagePage,
-                                   0, gamepad->axes[i].caps.Range.UsageMin,
-                                   &value, parsed,
-                                   (PCHAR)raw->data.hid.bRawData,
-                                   raw->data.hid.dwSizeHid)
-          != HIDP_STATUS_SUCCESS) {
-        continue;
-      }
-      new_value = ScaleAxis(value, gamepad->axes[i].caps.LogicalMin,
-                            gamepad->axes[i].caps.LogicalMax);
-    }
-    else {
-      ULONG value;
-      if (mHID.mHidP_GetUsageValue(HidP_Input, gamepad->axes[i].caps.UsagePage, 0,
-                             gamepad->axes[i].caps.Range.UsageMin, &value,
-                             parsed, (PCHAR)raw->data.hid.bRawData,
-                             raw->data.hid.dwSizeHid) != HIDP_STATUS_SUCCESS) {
-        continue;
-      }
-
-      new_value = ScaleAxis(value, gamepad->axes[i].caps.LogicalMin,
-                            gamepad->axes[i].caps.LogicalMax);
-    }
-    if (gamepad->axes[i].value != new_value) {
-      gamepadsvc->NewAxisMoveEvent(gamepad->id, i, new_value);
-      gamepad->axes[i].value = new_value;
-    }
-  }
-
-  return true;
-}
-
-void
-WindowsGamepadService::Startup()
-{
-  ScanForDevices();
-}
-
-void
-WindowsGamepadService::Shutdown()
-{
-  Cleanup();
-}
-
-void
-WindowsGamepadService::Cleanup()
-{
-  if (mXInputPollTimer) {
-    mXInputPollTimer->Cancel();
-  }
-  mGamepads.Clear();
-}
-
-void
-WindowsGamepadService::DevicesChanged(DeviceChangeType type)
-{
-  if (type == DeviceChangeNotification) {
-    mObserver->SetDeviceChangeTimer();
-  } else if (type == DeviceChangeStable) {
-    ScanForDevices();
-  }
-}
-
-NS_IMETHODIMP
-Observer::Observe(nsISupports* aSubject,
-                  const char* aTopic,
-                  const char16_t* aData)
-{
-  if (strcmp(aTopic, "timer-callback") == 0) {
-    mSvc.DevicesChanged(WindowsGamepadService::DeviceChangeStable);
-  } else if (strcmp(aTopic, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID) == 0) {
-    Stop();
-  }
-  return NS_OK;
-}
-
-HWND sHWnd = nullptr;
-
-bool
-RegisterRawInput(HWND hwnd, bool enable)
-{
-  nsTArray<RAWINPUTDEVICE> rid(ArrayLength(kUsagePages));
-  rid.SetLength(ArrayLength(kUsagePages));
-
-  for (unsigned i = 0; i < rid.Length(); i++) {
-    rid[i].usUsagePage = kUsagePages[i].usagePage;
-    rid[i].usUsage = kUsagePages[i].usage;
-    rid[i].dwFlags =
-      enable ? RIDEV_EXINPUTSINK | RIDEV_DEVNOTIFY : RIDEV_REMOVE;
-    rid[i].hwndTarget = hwnd;
-  }
-
-  if (!RegisterRawInputDevices(rid.Elements(), rid.Length(),
-                               sizeof(RAWINPUTDEVICE))) {
-    return false;
-  }
-  return true;
-}
-
-static
-LRESULT CALLBACK
-GamepadWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
-  const unsigned int DBT_DEVICEARRIVAL        = 0x8000;
-  const unsigned int DBT_DEVICEREMOVECOMPLETE = 0x8004;
-  const unsigned int DBT_DEVNODES_CHANGED     = 0x7;
-
-  switch (msg) {
-  case WM_DEVICECHANGE:
-    if (wParam == DBT_DEVICEARRIVAL ||
-        wParam == DBT_DEVICEREMOVECOMPLETE ||
-        wParam == DBT_DEVNODES_CHANGED) {
-      if (gService) {
-        gService->DevicesChanged(WindowsGamepadService::DeviceChangeNotification);
-      }
-    }
-    break;
-  case WM_INPUT:
-    if (gService) {
-      gService->HandleRawInput(reinterpret_cast<HRAWINPUT>(lParam));
-    }
-    break;
-  }
-  return DefWindowProc(hwnd, msg, wParam, lParam);
-}
-
-} // namespace
-
-namespace mozilla {
-namespace hal_impl {
-
-void StartMonitoringGamepadStatus()
-{
-  if (gService) {
-    return;
-  }
-
-  gService = new WindowsGamepadService();
-  gService->Startup();
-
-  if (sHWnd == nullptr) {
-    WNDCLASSW wc;
-    HMODULE hSelf = GetModuleHandle(nullptr);
-
-    if (!GetClassInfoW(hSelf, L"MozillaGamepadClass", &wc)) {
-      ZeroMemory(&wc, sizeof(WNDCLASSW));
-      wc.hInstance = hSelf;
-      wc.lpfnWndProc = GamepadWindowProc;
-      wc.lpszClassName = L"MozillaGamepadClass";
-      RegisterClassW(&wc);
-    }
-
-    sHWnd = CreateWindowW(L"MozillaGamepadClass", L"Gamepad Watcher",
-                          0, 0, 0, 0, 0,
-                          nullptr, nullptr, hSelf, nullptr);
-    RegisterRawInput(sHWnd, true);
-  }
-}
-
-void StopMonitoringGamepadStatus()
-{
-  if (!gService) {
-    return;
-  }
-
-  if (sHWnd) {
-    RegisterRawInput(sHWnd, false);
-    DestroyWindow(sHWnd);
-    sHWnd = nullptr;
-  }
-
-  gService->Shutdown();
-  delete gService;
-  gService = nullptr;
-}
-
-} // namespace hal_impl
-} // namespace mozilla
-
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -204,17 +204,17 @@ 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/GamepadService.h"
+#include "mozilla/dom/GamepadServiceTest.h"
 #endif
 #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"
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -36,17 +36,18 @@
 #include "AndroidBridge.h"
 #include "AndroidBridgeUtilities.h"
 #include <android/log.h>
 #include <pthread.h>
 #include <wchar.h>
 
 #include "mozilla/dom/ScreenOrientation.h"
 #ifdef MOZ_GAMEPAD
-#include "mozilla/dom/GamepadService.h"
+#include "mozilla/dom/GamepadFunctions.h"
+#include "mozilla/dom/Gamepad.h"
 #endif
 
 #include "GeckoProfiler.h"
 #ifdef MOZ_ANDROID_HISTORY
 #include "nsNetUtil.h"
 #include "IHistory.h"
 #endif
 
@@ -651,51 +652,43 @@ nsAppShell::ProcessNextNativeEvent(bool 
 
     case AndroidGeckoEvent::TELEMETRY_HISTOGRAM_ADD:
         Telemetry::Accumulate(NS_ConvertUTF16toUTF8(curEvent->Characters()).get(),
                               curEvent->Count());
         break;
 
     case AndroidGeckoEvent::GAMEPAD_ADDREMOVE: {
 #ifdef MOZ_GAMEPAD
-        nsRefPtr<mozilla::dom::GamepadService> svc =
-            mozilla::dom::GamepadService::GetService();
-        if (svc) {
             if (curEvent->Action() == AndroidGeckoEvent::ACTION_GAMEPAD_ADDED) {
-                int svc_id = svc->AddGamepad("android",
-                                             mozilla::dom::GamepadMappingType::Standard,
-                                             mozilla::dom::kStandardGamepadButtons,
-                                             mozilla::dom::kStandardGamepadAxes);
+            int svc_id = dom::GamepadFunctions::AddGamepad("android",
+                                                           dom::GamepadMappingType::Standard,
+                                                           dom::kStandardGamepadButtons,
+                                                           dom::kStandardGamepadAxes);
                 widget::GeckoAppShell::GamepadAdded(curEvent->ID(),
                                                     svc_id);
             } else if (curEvent->Action() == AndroidGeckoEvent::ACTION_GAMEPAD_REMOVED) {
-                svc->RemoveGamepad(curEvent->ID());
-            }
+            dom::GamepadFunctions::RemoveGamepad(curEvent->ID());
         }
 #endif
         break;
     }
 
     case AndroidGeckoEvent::GAMEPAD_DATA: {
 #ifdef MOZ_GAMEPAD
-        nsRefPtr<mozilla::dom::GamepadService> svc =
-            mozilla::dom::GamepadService::GetService();
-        if (svc) {
             int id = curEvent->ID();
             if (curEvent->Action() == AndroidGeckoEvent::ACTION_GAMEPAD_BUTTON) {
-                 svc->NewButtonEvent(id, curEvent->GamepadButton(),
+            dom::GamepadFunctions::NewButtonEvent(id, curEvent->GamepadButton(),
                                      curEvent->GamepadButtonPressed(),
                                      curEvent->GamepadButtonValue());
             } else if (curEvent->Action() == AndroidGeckoEvent::ACTION_GAMEPAD_AXES) {
                 int valid = curEvent->Flags();
                 const nsTArray<float>& values = curEvent->GamepadValues();
                 for (unsigned i = 0; i < values.Length(); i++) {
                     if (valid & (1<<i)) {
-                        svc->NewAxisMoveEvent(id, i, values[i]);
-                    }
+                    dom::GamepadFunctions::NewAxisMoveEvent(id, i, values[i]);
                 }
             }
         }
 #endif
         break;
     }
     case AndroidGeckoEvent::NOOP:
         break;