Bug 852944 - Gamepad API IPC; r=ted, r=baku
authorKyle Machulis <kyle@nonpolynomial.com>
Thu, 23 Apr 2015 15:05:29 -0700
changeset 240877 45bbfe5ef9b20adebd5a4306ae0c666fd38b3bcf
parent 240876 afdd76d6dcce66cdc081680571ff4f95987fbb8e
child 240878 12409cbb8d6157fe0665afb9a0fe99e840afde3c
push id12562
push usercbook@mozilla.com
push dateFri, 24 Apr 2015 13:02:09 +0000
treeherderfx-team@237b579d8a26 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersted, baku
bugs852944
milestone40.0a1
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;