bug 690935 - implement navigator.getGamepads(). r=smaug
authorTed Mielczarek <ted.mielczarek@gmail.com>
Wed, 15 Feb 2012 19:47:13 -0500
changeset 147177 7a159e3dad7b2bd0fd3d3ea1cbba55c9ced31348
parent 147176 3af6f05984ae4a9b0fa2b7e04d0dc8172e894571
child 147178 231754e0dbcf5fd4337683bca30f20512dd1e8f2
push id2697
push userbbajaj@mozilla.com
push dateMon, 05 Aug 2013 18:49:53 +0000
treeherdermozilla-beta@dfec938c7b63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs690935
milestone24.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 690935 - implement navigator.getGamepads(). r=smaug
content/events/src/moz.build
content/events/src/nsDOMGamepad.cpp
content/events/src/nsDOMGamepad.h
dom/base/Navigator.cpp
dom/base/Navigator.h
dom/base/nsDOMClassInfo.cpp
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/bindings/Bindings.conf
dom/gamepad/Gamepad.cpp
dom/gamepad/Gamepad.h
dom/gamepad/GamepadService.cpp
dom/gamepad/GamepadService.h
dom/gamepad/Makefile.in
dom/gamepad/moz.build
dom/interfaces/gamepad/moz.build
dom/interfaces/gamepad/nsINavigatorGamepads.idl
dom/moz.build
dom/system/GamepadService.cpp
dom/system/GamepadService.h
dom/system/moz.build
dom/tests/mochitest/gamepad/Makefile.in
dom/tests/mochitest/gamepad/test_navigator_gamepads.html
js/xpconnect/src/event_impl_gen.conf.in
layout/build/Makefile.in
--- a/content/events/src/moz.build
+++ b/content/events/src/moz.build
@@ -4,17 +4,16 @@
 # 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/.
 
 MODULE = 'content'
 
 EXPORTS += [
     'nsDOMEvent.h',
     'nsDOMEventTargetHelper.h',
-    'nsDOMGamepad.h',
     'nsDOMTouchEvent.h',
     'nsDOMUIEvent.h',
     'nsEventListenerManager.h',
     'nsEventStateManager.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'Touch.h',
@@ -56,13 +55,8 @@ CPP_SOURCES += [
     'nsEventDispatcher.cpp',
     'nsEventListenerManager.cpp',
     'nsEventListenerService.cpp',
     'nsEventStateManager.cpp',
     'nsIMEStateManager.cpp',
     'nsPaintRequest.cpp',
     'nsPrivateTextRange.cpp',
 ]
-
-if CONFIG['MOZ_GAMEPAD']:
-    CPP_SOURCES += [
-        'nsDOMGamepad.cpp',
-    ]
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -17,16 +17,17 @@
 #include "nsICachingChannel.h"
 #include "nsIDocShell.h"
 #include "nsIWebContentHandlerRegistrar.h"
 #include "nsICookiePermission.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsContentUtils.h"
 #include "nsUnicharUtils.h"
+#include "nsVariant.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "BatteryManager.h"
 #include "PowerManager.h"
 #include "nsIDOMWakeLock.h"
 #include "nsIPowerManagerService.h"
 #include "mozilla/dom/SmsManager.h"
 #include "mozilla/dom/MobileMessageManager.h"
@@ -122,16 +123,19 @@ NS_INTERFACE_MAP_BEGIN(Navigator)
   NS_INTERFACE_MAP_ENTRY(nsIDOMMozNavigatorMobileMessage)
 #ifdef MOZ_MEDIA_NAVIGATOR
   NS_INTERFACE_MAP_ENTRY(nsINavigatorUserMedia)
   NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorUserMedia)
 #endif
 #ifdef MOZ_B2G_RIL
   NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorTelephony)
 #endif
+#ifdef MOZ_GAMEPAD
+  NS_INTERFACE_MAP_ENTRY(nsINavigatorGamepads)
+#endif
   NS_INTERFACE_MAP_ENTRY(nsIDOMMozNavigatorNetwork)
 #ifdef MOZ_B2G_RIL
   NS_INTERFACE_MAP_ENTRY(nsIMozNavigatorMobileConnection)
   NS_INTERFACE_MAP_ENTRY(nsIMozNavigatorCellBroadcast)
   NS_INTERFACE_MAP_ENTRY(nsIMozNavigatorVoicemail)
   NS_INTERFACE_MAP_ENTRY(nsIMozNavigatorIccManager)
 #endif
 #ifdef MOZ_B2G_BT
@@ -1337,16 +1341,52 @@ Navigator::GetMozIccManager(nsIDOMMozIcc
   }
 
   NS_ADDREF(*aIccManager = mIccManager);
   return NS_OK;
 }
 
 #endif // MOZ_B2G_RIL
 
+#ifdef MOZ_GAMEPAD
+//*****************************************************************************
+//    Navigator::nsINavigatorGamepads
+//*****************************************************************************
+
+NS_IMETHODIMP
+Navigator::GetGamepads(nsIVariant** aRetVal)
+{
+  NS_ENSURE_ARG_POINTER(aRetVal);
+  *aRetVal = nullptr;
+
+  nsCOMPtr<nsPIDOMWindow> pwin(do_QueryReferent(mWindow));
+  NS_ENSURE_TRUE(pwin && pwin->GetDocShell(), NS_OK);
+  nsGlobalWindow* win = static_cast<nsGlobalWindow*>(pwin.get());
+
+  nsAutoTArray<nsRefPtr<Gamepad>, 2> gamepads;
+  win->GetGamepads(gamepads);
+
+  nsRefPtr<nsVariant> out = new nsVariant();
+  NS_ENSURE_STATE(out);
+
+  if (gamepads.Length() == 0) {
+    nsresult rv = out->SetAsEmptyArray();
+    NS_ENSURE_SUCCESS(rv, rv);
+  } else {
+    out->SetAsArray(nsIDataType::VTYPE_INTERFACE,
+                    &NS_GET_IID(nsISupports),
+                    gamepads.Length(),
+                    const_cast<void*>(static_cast<const void*>(gamepads.Elements())));
+  }
+  out.forget(aRetVal);
+
+  return NS_OK;
+}
+#endif
+
 //*****************************************************************************
 //    Navigator::nsIDOMNavigatorNetwork
 //*****************************************************************************
 
 NS_IMETHODIMP
 Navigator::GetMozConnection(nsIDOMMozConnection** aConnection)
 {
   *aConnection = nullptr;
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -54,16 +54,20 @@ class nsIDOMTelephony;
 #include "nsIDOMNavigatorBluetooth.h"
 #endif
 
 #include "nsIDOMNavigatorSystemMessages.h"
 
 #include "nsIDOMNavigatorCamera.h"
 #include "DOMCameraManager.h"
 
+#ifdef MOZ_GAMEPAD
+#include "nsINavigatorGamepads.h"
+#endif
+
 //*****************************************************************************
 // Navigator: Script "navigator" object
 //*****************************************************************************
 
 namespace mozilla {
 namespace dom {
 
 namespace battery {
@@ -111,16 +115,19 @@ class Navigator : public nsIDOMNavigator
                 , public nsIDOMMozNavigatorMobileMessage
 #ifdef MOZ_MEDIA_NAVIGATOR
                 , public nsINavigatorUserMedia
                 , public nsIDOMNavigatorUserMedia
 #endif
 #ifdef MOZ_B2G_RIL
                 , public nsIDOMNavigatorTelephony
 #endif
+#ifdef MOZ_GAMEPAD
+                , public nsINavigatorGamepads
+#endif
                 , public nsIDOMMozNavigatorNetwork
 #ifdef MOZ_B2G_RIL
                 , public nsIMozNavigatorMobileConnection
                 , public nsIMozNavigatorCellBroadcast
                 , public nsIMozNavigatorVoicemail
                 , public nsIMozNavigatorIccManager
 #endif
 #ifdef MOZ_B2G_BT
@@ -150,16 +157,19 @@ public:
   NS_DECL_NSIDOMMOZNAVIGATORMOBILEMESSAGE
 #ifdef MOZ_MEDIA_NAVIGATOR
   NS_DECL_NSINAVIGATORUSERMEDIA
   NS_DECL_NSIDOMNAVIGATORUSERMEDIA
 #endif
 #ifdef MOZ_B2G_RIL
   NS_DECL_NSIDOMNAVIGATORTELEPHONY
 #endif
+#ifdef MOZ_GAMEPAD
+  NS_DECL_NSINAVIGATORGAMEPADS
+#endif
   NS_DECL_NSIDOMMOZNAVIGATORNETWORK
 #ifdef MOZ_B2G_RIL
   NS_DECL_NSIMOZNAVIGATORMOBILECONNECTION
   NS_DECL_NSIMOZNAVIGATORCELLBROADCAST
   NS_DECL_NSIMOZNAVIGATORVOICEMAIL
   NS_DECL_NSIMOZNAVIGATORICCMANAGER
 #endif
 
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -170,16 +170,20 @@
 #include "nsIDOMCRMFObject.h"
 #include "nsIDOMCryptoLegacy.h"
 #else
 #include "nsIDOMCrypto.h"
 #endif
 #include "nsIControllers.h"
 #include "nsISelection.h"
 #include "nsIBoxObject.h"
+#ifdef MOZ_GAMEPAD
+#include "nsINavigatorGamepads.h"
+#include "mozilla/dom/GamepadService.h"
+#endif
 #ifdef MOZ_XUL
 #include "nsITreeSelection.h"
 #include "nsITreeContentView.h"
 #include "nsITreeView.h"
 #include "nsIXULTemplateBuilder.h"
 #include "nsTreeColumns.h"
 #endif
 #include "nsIDOMXPathExpression.h"
@@ -1402,16 +1406,20 @@ nsDOMClassInfo::Init()
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozNavigatorMobileMessage)
 #ifdef MOZ_MEDIA_NAVIGATOR
     DOM_CLASSINFO_MAP_ENTRY(nsINavigatorUserMedia)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorUserMedia)
 #endif
 #ifdef MOZ_B2G_RIL
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorTelephony)
 #endif
+#ifdef MOZ_GAMEPAD
+    DOM_CLASSINFO_MAP_CONDITIONAL_ENTRY(nsINavigatorGamepads,
+                                        GamepadService::IsAPIEnabled())
+#endif
     DOM_CLASSINFO_MAP_CONDITIONAL_ENTRY(nsIDOMMozNavigatorNetwork,
                                         network::IsAPIEnabled())
 #ifdef MOZ_B2G_RIL
     DOM_CLASSINFO_MAP_ENTRY(nsIMozNavigatorMobileConnection)
     DOM_CLASSINFO_MAP_ENTRY(nsIMozNavigatorCellBroadcast)
     DOM_CLASSINFO_MAP_ENTRY(nsIMozNavigatorVoicemail)
     DOM_CLASSINFO_MAP_ENTRY(nsIMozNavigatorIccManager)
 #endif
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -11259,34 +11259,56 @@ nsGlobalWindow::SizeOfIncludingThis(nsWi
     mEventTargetObjects.SizeOfExcludingThis(
       SizeOfEventTargetObjectsEntryExcludingThisFun,
       aWindowSizes->mMallocSizeOf);
 }
 
 
 #ifdef MOZ_GAMEPAD
 void
-nsGlobalWindow::AddGamepad(uint32_t aIndex, nsDOMGamepad* aGamepad)
+nsGlobalWindow::AddGamepad(uint32_t aIndex, Gamepad* aGamepad)
 {
   FORWARD_TO_INNER_VOID(AddGamepad, (aIndex, aGamepad));
   mGamepads.Put(aIndex, aGamepad);
 }
 
 void
 nsGlobalWindow::RemoveGamepad(uint32_t aIndex)
 {
   FORWARD_TO_INNER_VOID(RemoveGamepad, (aIndex));
   mGamepads.Remove(aIndex);
 }
 
-already_AddRefed<nsDOMGamepad>
+// 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;
+  return PL_DHASH_NEXT;
+}
+
+void
+nsGlobalWindow::GetGamepads(nsTArray<nsRefPtr<Gamepad> >& aGamepads)
+{
+  FORWARD_TO_INNER_VOID(GetGamepads, (aGamepads));
+  aGamepads.Clear();
+  // mGamepads.Count() may not be sufficient, but it's not harmful.
+  aGamepads.SetCapacity(mGamepads.Count());
+  mGamepads.EnumerateRead(EnumGamepadsForGet, &aGamepads);
+}
+
+already_AddRefed<Gamepad>
 nsGlobalWindow::GetGamepad(uint32_t aIndex)
 {
   FORWARD_TO_INNER(GetGamepad, (aIndex), nullptr);
-  nsRefPtr<nsDOMGamepad> gamepad;
+  nsRefPtr<Gamepad> gamepad;
   if (mGamepads.Get(aIndex, getter_AddRefs(gamepad))) {
     return gamepad.forget();
   }
 
   return nullptr;
 }
 
 void
@@ -11300,17 +11322,18 @@ bool
 nsGlobalWindow::HasSeenGamepadInput()
 {
   FORWARD_TO_INNER(HasSeenGamepadInput, (), false);
   return mHasSeenGamepadInput;
 }
 
 // static
 PLDHashOperator
-nsGlobalWindow::EnumGamepadsForSync(const uint32_t& aKey, nsDOMGamepad* aData, void* userArg)
+nsGlobalWindow::EnumGamepadsForSync(const uint32_t& aKey, Gamepad* aData,
+                                    void* aUserArg)
 {
   nsRefPtr<GamepadService> gamepadsvc(GamepadService::GetService());
   gamepadsvc->SyncGamepadState(aKey, aData);
   return PL_DHASH_NEXT;
 }
 
 void
 nsGlobalWindow::SyncGamepadState()
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -55,17 +55,17 @@
 #include "mozilla/LinkedList.h"
 #include "mozilla/TimeStamp.h"
 #include "nsIDOMTouchEvent.h"
 #include "nsIInlineEventHandlers.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsIIdleObserver.h"
 #include "nsIDOMWakeLock.h"
 #ifdef MOZ_GAMEPAD
-#include "nsDOMGamepad.h"
+#include "mozilla/dom/Gamepad.h"
 #endif
 #include "nsIDocument.h"
 
 #include "mozilla/dom/EventTarget.h"
 
 // JS includes
 #include "jsapi.h"
 
@@ -686,25 +686,29 @@ public:
   void HandleIdleObserverCallback();
 
   void AllowScriptsToClose()
   {
     mAllowScriptsToClose = true;
   }
 
 #ifdef MOZ_GAMEPAD
-  void AddGamepad(uint32_t aIndex, nsDOMGamepad* aGamepad);
+  void AddGamepad(uint32_t aIndex, mozilla::dom::Gamepad* aGamepad);
   void RemoveGamepad(uint32_t aIndex);
-  already_AddRefed<nsDOMGamepad> GetGamepad(uint32_t aIndex);
+  void GetGamepads(nsTArray<nsRefPtr<mozilla::dom::Gamepad> >& aGamepads);
+  already_AddRefed<mozilla::dom::Gamepad> GetGamepad(uint32_t aIndex);
   void SetHasSeenGamepadInput(bool aHasSeen);
   bool HasSeenGamepadInput();
   void SyncGamepadState();
   static PLDHashOperator EnumGamepadsForSync(const uint32_t& aKey,
-                                             nsDOMGamepad* aData,
-                                             void* userArg);
+                                             mozilla::dom::Gamepad* aData,
+                                             void* aUserArg);
+  static PLDHashOperator EnumGamepadsForGet(const PRUint32& aKey,
+                                            mozilla::dom::Gamepad* aData,
+                                            void* aUserArg);
 #endif
 
   // Enable/disable updates for gamepad input.
   void EnableGamepadUpdates();
   void DisableGamepadUpdates();
 
 
 #define EVENT(name_, id_, type_, struct_)                                     \
@@ -1151,17 +1155,17 @@ protected:
 
   // true if tab navigation has occurred for this window. Focus rings
   // should be displayed.
   bool                   mFocusByKeyOccurred : 1;
 
   // Indicates whether this window wants gamepad input events
   bool                   mHasGamepad : 1;
 #ifdef MOZ_GAMEPAD
-  nsRefPtrHashtable<nsUint32HashKey, nsDOMGamepad> mGamepads;
+  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.
   bool                   mAllowScriptsToClose : 1;
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -406,20 +406,16 @@ DOMInterfaces = {
 'Future': {
     'implicitJSContext': [ 'constructor' ]
 },
 
 'GainNode': {
     'resultNotAddRefed': [ 'gain' ],
 },
 
-'Gamepad': {
-    'nativeType': 'nsDOMGamepad',
-},
-
 'Geolocation': {
     'headerFile': 'nsGeolocation.h'
 },
 
 'HTMLAppletElement': {
     'nativeType': 'mozilla::dom::HTMLSharedObjectElement'
 },
 
rename from content/events/src/nsDOMGamepad.cpp
rename to dom/gamepad/Gamepad.cpp
--- a/content/events/src/nsDOMGamepad.cpp
+++ b/dom/gamepad/Gamepad.cpp
@@ -1,74 +1,74 @@
 /* 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 "nsDOMGamepad.h"
+#include "Gamepad.h"
 #include "nsAutoPtr.h"
 #include "nsTArray.h"
 #include "nsContentUtils.h"
 #include "nsVariant.h"
 #include "mozilla/dom/GamepadBinding.h"
 
-using namespace mozilla;
-using namespace mozilla::dom;
+namespace mozilla {
+namespace dom {
 
-NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMGamepad)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMGamepad)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(Gamepad)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(Gamepad)
 
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMGamepad)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Gamepad)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsIDOMGamepad)
 NS_INTERFACE_MAP_END
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(nsDOMGamepad, mParent)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(Gamepad, mParent)
 
-nsDOMGamepad::nsDOMGamepad(nsISupports* aParent,
-                           const nsAString& aID, uint32_t aIndex,
-                           uint32_t aNumButtons, uint32_t aNumAxes)
+Gamepad::Gamepad(nsISupports* aParent,
+                 const nsAString& aID, uint32_t aIndex,
+                 uint32_t aNumButtons, uint32_t aNumAxes)
   : mParent(aParent),
     mID(aID),
     mIndex(aIndex),
     mConnected(true)
 {
   SetIsDOMBinding();
   mButtons.InsertElementsAt(0, aNumButtons, 0);
   mAxes.InsertElementsAt(0, aNumAxes, 0.0f);
 }
 
 void
-nsDOMGamepad::SetIndex(uint32_t aIndex)
+Gamepad::SetIndex(uint32_t aIndex)
 {
   mIndex = aIndex;
 }
 
 void
-nsDOMGamepad::SetConnected(bool aConnected)
+Gamepad::SetConnected(bool aConnected)
 {
   mConnected = aConnected;
 }
 
 void
-nsDOMGamepad::SetButton(uint32_t aButton, double aValue)
+Gamepad::SetButton(uint32_t aButton, double aValue)
 {
   MOZ_ASSERT(aButton < mButtons.Length());
   mButtons[aButton] = aValue;
 }
 
 void
-nsDOMGamepad::SetAxis(uint32_t aAxis, double aValue)
+Gamepad::SetAxis(uint32_t aAxis, double aValue)
 {
   MOZ_ASSERT(aAxis < mAxes.Length());
   mAxes[aAxis] = aValue;
 }
 
 nsresult
-nsDOMGamepad::GetButtons(nsIVariant** aButtons)
+Gamepad::GetButtons(nsIVariant** aButtons)
 {
   nsRefPtr<nsVariant> out = new nsVariant();
   NS_ENSURE_STATE(out);
 
   if (mButtons.Length() == 0) {
     nsresult rv = out->SetAsEmptyArray();
     NS_ENSURE_SUCCESS(rv, rv);
   } else {
@@ -89,17 +89,17 @@ nsDOMGamepad::GetButtons(nsIVariant** aB
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   *aButtons = out.forget().get();
   return NS_OK;
 }
 
 nsresult
-nsDOMGamepad::GetAxes(nsIVariant** aAxes)
+Gamepad::GetAxes(nsIVariant** aAxes)
 {
   nsRefPtr<nsVariant> out = new nsVariant();
   NS_ENSURE_STATE(out);
 
   if (mAxes.Length() == 0) {
     nsresult rv = out->SetAsEmptyArray();
     NS_ENSURE_SUCCESS(rv, rv);
   } else {
@@ -120,38 +120,41 @@ nsDOMGamepad::GetAxes(nsIVariant** aAxes
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   *aAxes = out.forget().get();
   return NS_OK;
 }
 
 void
-nsDOMGamepad::SyncState(nsDOMGamepad* aOther)
+Gamepad::SyncState(Gamepad* aOther)
 {
   if (mButtons.Length() != aOther->mButtons.Length() ||
       mAxes.Length() != aOther->mAxes.Length()) {
     return;
   }
 
   mConnected = aOther->mConnected;
   for (uint32_t i = 0; i < mButtons.Length(); ++i) {
     mButtons[i] = aOther->mButtons[i];
   }
   for (uint32_t i = 0; i < mAxes.Length(); ++i) {
     mAxes[i] = aOther->mAxes[i];
   }
 }
 
-already_AddRefed<nsDOMGamepad>
-nsDOMGamepad::Clone(nsISupports* aParent)
+already_AddRefed<Gamepad>
+Gamepad::Clone(nsISupports* aParent)
 {
-  nsRefPtr<nsDOMGamepad> out =
-    new nsDOMGamepad(aParent, mID, mIndex, mButtons.Length(), mAxes.Length());
+  nsRefPtr<Gamepad> out =
+    new Gamepad(aParent, mID, mIndex, mButtons.Length(), mAxes.Length());
   out->SyncState(this);
   return out.forget();
 }
 
 /* virtual */ JSObject*
-nsDOMGamepad::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
+Gamepad::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return GamepadBinding::Wrap(aCx, aScope, this);
 }
+
+} // namespace dom
+} // namespace mozilla
rename from content/events/src/nsDOMGamepad.h
rename to dom/gamepad/Gamepad.h
--- a/content/events/src/nsDOMGamepad.h
+++ b/dom/gamepad/Gamepad.h
@@ -1,45 +1,48 @@
 /* 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 nsDomGamepad_h
-#define nsDomGamepad_h
+#ifndef mozilla_dom_gamepad_Gamepad_h
+#define mozilla_dom_gamepad_Gamepad_h
 
 #include "mozilla/ErrorResult.h"
 #include "mozilla/StandardInteger.h"
 #include "nsCOMPtr.h"
 #include "nsIDOMGamepad.h"
+#include "nsIVariant.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsWrapperCache.h"
 
-class nsDOMGamepad : public nsIDOMGamepad
-                   , public nsWrapperCache
+namespace mozilla {
+namespace dom {
+
+class Gamepad : public nsIDOMGamepad
+              , public nsWrapperCache
 {
 public:
-  nsDOMGamepad(nsISupports* aParent,
-               const nsAString& aID, uint32_t aIndex,
-               uint32_t aNumButtons, uint32_t aNumAxes);
+  Gamepad(nsISupports* aParent,
+          const nsAString& aID, uint32_t aIndex,
+          uint32_t aNumButtons, uint32_t aNumAxes);
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMGamepad)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Gamepad)
 
-  nsDOMGamepad();
   void SetConnected(bool aConnected);
   void SetButton(uint32_t aButton, double aValue);
   void SetAxis(uint32_t aAxis, double aValue);
   void SetIndex(uint32_t aIndex);
 
   // Make the state of this gamepad equivalent to other.
-  void SyncState(nsDOMGamepad* other);
+  void SyncState(Gamepad* aOther);
 
-  // Return a new nsDOMGamepad containing the same data as this object,
+  // Return a new Gamepad containing the same data as this object,
   // parented to aParent.
-  already_AddRefed<nsDOMGamepad> Clone(nsISupports* aParent);
+  already_AddRefed<Gamepad> Clone(nsISupports* aParent);
 
   nsISupports* GetParentObject() const
   {
     return mParent;
   }
 
   virtual JSObject* WrapObject(JSContext* aCx,
 			       JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
@@ -69,17 +72,17 @@ public:
   already_AddRefed<nsIVariant> GetAxes(mozilla::ErrorResult& aRv)
   {
     nsCOMPtr<nsIVariant> axes;
     aRv = GetAxes(getter_AddRefs(axes));
     return axes.forget();
   }
 
 private:
-  virtual ~nsDOMGamepad() {}
+  virtual ~Gamepad() {}
 
   nsresult GetButtons(nsIVariant** aButtons);
   nsresult GetAxes(nsIVariant** aAxes);
 
 protected:
   nsCOMPtr<nsISupports> mParent;
   nsString mID;
   uint32_t mIndex;
@@ -87,9 +90,12 @@ protected:
   // true if this gamepad is currently connected.
   bool mConnected;
 
   // Current state of buttons, axes.
   nsTArray<double> mButtons;
   nsTArray<double> mAxes;
 };
 
-#endif // nsDomGamepad_h
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_gamepad_Gamepad_h
rename from dom/system/GamepadService.cpp
rename to dom/gamepad/GamepadService.cpp
--- a/dom/system/GamepadService.cpp
+++ b/dom/gamepad/GamepadService.cpp
@@ -3,20 +3,20 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Hal.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 "nsDOMGamepad.h"
 #include "nsIDOMGamepadButtonEvent.h"
 #include "nsIDOMGamepadAxisMoveEvent.h"
 #include "nsIDOMGamepadEvent.h"
 #include "GeneratedEvents.h"
 #include "nsIDOMWindow.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "nsIServiceManager.h"
@@ -44,17 +44,17 @@ StaticRefPtr<GamepadService> gGamepadSer
 bool GamepadService::sShutdown = false;
 
 NS_IMPL_ISUPPORTS1(GamepadService, nsIObserver)
 
 GamepadService::GamepadService()
   : mStarted(false),
     mShuttingDown(false)
 {
-  mEnabled = Preferences::GetBool(kGamepadEnabledPref, false);
+  mEnabled = IsAPIEnabled();
   nsCOMPtr<nsIObserverService> observerService =
     mozilla::services::GetObserverService();
   observerService->AddObserver(this,
                                NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID,
                                false);
 }
 
 NS_IMETHODIMP
@@ -130,22 +130,22 @@ GamepadService::RemoveListener(nsGlobalW
 }
 
 uint32_t
 GamepadService::AddGamepad(const char* aId,
                            uint32_t aNumButtons,
                            uint32_t aNumAxes)
 {
   //TODO: bug 852258: get initial button/axis state
-  nsRefPtr<nsDOMGamepad> gamepad =
-    new nsDOMGamepad(nullptr,
-                     NS_ConvertUTF8toUTF16(nsDependentCString(aId)),
-                     0,
-                     aNumButtons,
-                     aNumAxes);
+  nsRefPtr<Gamepad> gamepad =
+    new Gamepad(nullptr,
+                NS_ConvertUTF8toUTF16(nsDependentCString(aId)),
+                0,
+                aNumButtons,
+                aNumAxes);
   int index = -1;
   for (uint32_t i = 0; i < mGamepads.Length(); i++) {
     if (!mGamepads[i]) {
       mGamepads[i] = gamepad;
       index = i;
       break;
     }
   }
@@ -202,28 +202,28 @@ GamepadService::NewButtonEvent(uint32_t 
 
     if (!WindowHasSeenGamepad(listeners[i], aIndex)) {
       SetWindowHasSeenGamepad(listeners[i], aIndex);
       // This window hasn't seen this gamepad before, so
       // send a connection event first.
       NewConnectionEvent(aIndex, true);
     }
 
-    nsRefPtr<nsDOMGamepad> gamepad = listeners[i]->GetGamepad(aIndex);
+    nsRefPtr<Gamepad> gamepad = listeners[i]->GetGamepad(aIndex);
     if (gamepad) {
       gamepad->SetButton(aButton, value);
       // Fire event
       FireButtonEvent(listeners[i], gamepad, aButton, value);
     }
   }
 }
 
 void
 GamepadService::FireButtonEvent(EventTarget* aTarget,
-                                nsDOMGamepad* aGamepad,
+                                Gamepad* aGamepad,
                                 uint32_t aButton,
                                 double aValue)
 {
   nsCOMPtr<nsIDOMEvent> event;
   bool defaultActionEnabled = true;
   NS_NewDOMGamepadButtonEvent(getter_AddRefs(event), aTarget, nullptr, nullptr);
   nsCOMPtr<nsIDOMGamepadButtonEvent> je = do_QueryInterface(event);
   MOZ_ASSERT(je, "QI should not fail");
@@ -260,28 +260,28 @@ GamepadService::NewAxisMoveEvent(uint32_
 
     if (!WindowHasSeenGamepad(listeners[i], aIndex)) {
       SetWindowHasSeenGamepad(listeners[i], aIndex);
       // This window hasn't seen this gamepad before, so
       // send a connection event first.
       NewConnectionEvent(aIndex, true);
     }
 
-    nsRefPtr<nsDOMGamepad> gamepad = listeners[i]->GetGamepad(aIndex);
+    nsRefPtr<Gamepad> gamepad = listeners[i]->GetGamepad(aIndex);
     if (gamepad) {
       gamepad->SetAxis(aAxis, aValue);
       // Fire event
       FireAxisMoveEvent(listeners[i], gamepad, aAxis, aValue);
     }
   }
 }
 
 void
 GamepadService::FireAxisMoveEvent(EventTarget* aTarget,
-                                  nsDOMGamepad* aGamepad,
+                                  Gamepad* aGamepad,
                                   uint32_t aAxis,
                                   double aValue)
 {
   nsCOMPtr<nsIDOMEvent> event;
   bool defaultActionEnabled = true;
   NS_NewDOMGamepadAxisMoveEvent(getter_AddRefs(event), aTarget, nullptr,
                                 nullptr);
   nsCOMPtr<nsIDOMGamepadAxisMoveEvent> je = do_QueryInterface(event);
@@ -317,50 +317,50 @@ GamepadService::NewConnectionEvent(uint3
       // We don't fire a connected event here unless the window
       // has seen input from at least one device.
       if (aConnected && !listeners[i]->HasSeenGamepadInput()) {
         return;
       }
 
       SetWindowHasSeenGamepad(listeners[i], aIndex);
 
-      nsRefPtr<nsDOMGamepad> gamepad = listeners[i]->GetGamepad(aIndex);
+      nsRefPtr<Gamepad> gamepad = listeners[i]->GetGamepad(aIndex);
       if (gamepad) {
         // Fire event
         FireConnectionEvent(listeners[i], gamepad, 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<nsDOMGamepad> gamepad = listeners[i]->GetGamepad(aIndex);
+        nsRefPtr<Gamepad> gamepad = listeners[i]->GetGamepad(aIndex);
         if (gamepad) {
           gamepad->SetConnected(false);
           // Fire event
           FireConnectionEvent(listeners[i], gamepad, false);
         }
 
         if (gamepad) {
           listeners[i]->RemoveGamepad(aIndex);
         }
       }
     }
   }
 }
 
 void
 GamepadService::FireConnectionEvent(EventTarget* aTarget,
-                                    nsDOMGamepad* aGamepad,
+                                    Gamepad* aGamepad,
                                     bool aConnected)
 {
   nsCOMPtr<nsIDOMEvent> event;
   bool defaultActionEnabled = true;
   NS_NewDOMGamepadEvent(getter_AddRefs(event), aTarget, nullptr, nullptr);
   nsCOMPtr<nsIDOMGamepadEvent> je = do_QueryInterface(event);
   MOZ_ASSERT(je, "QI should not fail");
 
@@ -368,17 +368,17 @@ GamepadService::FireConnectionEvent(Even
                                NS_LITERAL_STRING("gamepaddisconnected");
   je->InitGamepadEvent(name, false, false, aGamepad);
   je->SetTrusted(true);
 
   aTarget->DispatchEvent(event, &defaultActionEnabled);
 }
 
 void
-GamepadService::SyncGamepadState(uint32_t aIndex, nsDOMGamepad* aGamepad)
+GamepadService::SyncGamepadState(uint32_t aIndex, Gamepad* aGamepad)
 {
   if (mShuttingDown || !mEnabled || aIndex > mGamepads.Length()) {
     return;
   }
 
   aGamepad->SyncState(mGamepads[aIndex]);
 }
 
@@ -393,37 +393,43 @@ GamepadService::GetService()
   if (!gGamepadServiceSingleton) {
     gGamepadServiceSingleton = new GamepadService();
     ClearOnShutdown(&gGamepadServiceSingleton);
   }
   nsRefPtr<GamepadService> service(gGamepadServiceSingleton);
   return service.forget();
 }
 
+// static
+bool
+GamepadService::IsAPIEnabled() {
+  return Preferences::GetBool(kGamepadEnabledPref, false);
+}
+
 bool
 GamepadService::WindowHasSeenGamepad(nsGlobalWindow* aWindow, uint32_t aIndex)
 {
-  nsRefPtr<nsDOMGamepad> gamepad = aWindow->GetGamepad(aIndex);
+  nsRefPtr<Gamepad> gamepad = aWindow->GetGamepad(aIndex);
   return gamepad != nullptr;
 }
 
 void
 GamepadService::SetWindowHasSeenGamepad(nsGlobalWindow* aWindow,
                                         uint32_t aIndex,
                                         bool aHasSeen)
 {
   if (mListeners.IndexOf(aWindow) == NoIndex) {
     // This window isn't even listening for gamepad events.
     return;
   }
 
   if (aHasSeen) {
     aWindow->SetHasSeenGamepadInput(true);
     nsCOMPtr<nsISupports> window = nsGlobalWindow::ToSupports(aWindow);
-    nsRefPtr<nsDOMGamepad> gamepad = mGamepads[aIndex]->Clone(window);
+    nsRefPtr<Gamepad> gamepad = mGamepads[aIndex]->Clone(window);
     aWindow->AddGamepad(aIndex, gamepad);
   } else {
     aWindow->RemoveGamepad(aIndex);
   }
 }
 
 // static
 void
rename from dom/system/GamepadService.h
rename to dom/gamepad/GamepadService.h
--- a/dom/system/GamepadService.h
+++ b/dom/gamepad/GamepadService.h
@@ -1,19 +1,19 @@
 /* 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 "mozilla/StandardInteger.h"
+#include "Gamepad.h"
 #include "nsAutoPtr.h"
 #include "nsCOMArray.h"
-#include "nsDOMGamepad.h"
 #include "nsIGamepadServiceTest.h"
 #include "nsGlobalWindow.h"
 #include "nsIFocusManager.h"
 #include "nsIObserver.h"
 #include "nsITimer.h"
 #include "nsTArray.h"
 
 class nsIDOMDocument;
@@ -26,16 +26,18 @@ class EventTarget;
 class GamepadService : public nsIObserver
 {
  public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
   // 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);
 
@@ -45,34 +47,34 @@ class GamepadService : public nsIObserve
   void RemoveGamepad(uint32_t aIndex);
 
   //TODO: the spec uses double values for buttons, to allow for analog
   // buttons.
   void NewButtonEvent(uint32_t aIndex, uint32_t aButton, bool aPressed);
   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, nsDOMGamepad* aGamepad);
+  void SyncGamepadState(uint32_t aIndex, Gamepad* aGamepad);
 
  protected:
   GamepadService();
   virtual ~GamepadService() {};
   void StartCleanupTimer();
 
   void NewConnectionEvent(uint32_t aIndex, bool aConnected);
   void FireAxisMoveEvent(EventTarget* aTarget,
-                         nsDOMGamepad* aGamepad,
+                         Gamepad* aGamepad,
                          uint32_t axis,
                          double value);
   void FireButtonEvent(EventTarget* aTarget,
-                       nsDOMGamepad* aGamepad,
+                       Gamepad* aGamepad,
                        uint32_t aButton,
                        double aValue);
   void FireConnectionEvent(EventTarget* aTarget,
-                           nsDOMGamepad* aGamepad,
+                           Gamepad* aGamepad,
                            bool aConnected);
 
   // true if this feature is enabled in preferences
   bool mEnabled;
   // true if the platform-specific backend has started work
   bool mStarted;
   // true when shutdown has begun
   bool mShuttingDown;
@@ -87,17 +89,17 @@ 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<nsDOMGamepad> > mGamepads;
+  nsTArray<nsRefPtr<Gamepad> > mGamepads;
   // nsGlobalWindows 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;
 };
 
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/Makefile.in
@@ -0,0 +1,21 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEPTH       = @DEPTH@
+topsrcdir   = @top_srcdir@
+srcdir      = @srcdir@
+VPATH       = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+LIBRARY_NAME    = domgamepad_s
+LIBXUL_LIBRARY  = 1
+FAIL_ON_WARNINGS := 1
+
+LOCAL_INCLUDES += \
+  -I$(topsrcdir)/dom/base \
+  $(NULL)
+
+include $(topsrcdir)/config/rules.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXPORTS.mozilla.dom += [
+    'Gamepad.h',
+    'GamepadService.h',
+    ]
+
+CPP_SOURCES = [
+    'Gamepad.cpp',
+    'GamepadService.cpp',
+    ]
--- a/dom/interfaces/gamepad/moz.build
+++ b/dom/interfaces/gamepad/moz.build
@@ -4,9 +4,10 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 XPIDL_MODULE = 'dom_gamepad'
 
 XPIDL_SOURCES = [
   'nsIDOMGamepad.idl',
   'nsIGamepadServiceTest.idl',
+  'nsINavigatorGamepads.idl',
   ]
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/gamepad/nsINavigatorGamepads.idl
@@ -0,0 +1,13 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIVariant;
+
+[scriptable, uuid(bd2593a6-7df9-4dc7-8abe-20171c36a7a7)]
+interface nsINavigatorGamepads : nsISupports
+{
+  nsIVariant getGamepads();
+};
--- a/dom/moz.build
+++ b/dom/moz.build
@@ -88,16 +88,19 @@ if CONFIG['MOZ_B2G_RIL']:
     ]
 
 if CONFIG['MOZ_B2G_FM']:
     PARALLEL_DIRS += ['fm']
 
 if CONFIG['MOZ_PAY']:
     PARALLEL_DIRS += ['payment']
 
+if CONFIG['MOZ_GAMEPAD']:
+    PARALLEL_DIRS += ['gamepad']
+
 # bindings/test is here, because it needs to build after bindings/, and
 # we build subdirectories before ourselves.
 TEST_DIRS += [
     'tests',
     'imptests',
     'bindings/test',
 ]
 
--- a/dom/system/moz.build
+++ b/dom/system/moz.build
@@ -25,24 +25,16 @@ XPIDL_SOURCES += [
 XPIDL_MODULE = 'dom_system'
 
 MODULE = 'dom'
 
 EXPORTS += [
     'nsDeviceSensors.h',
 ]
 
-if CONFIG['MOZ_GAMEPAD']:
-    EXPORTS.mozilla.dom = [
-        'GamepadService.h',
-    ]
-    CPP_SOURCES += [
-        'GamepadService.cpp',
-    ]
-
 EXPORTS.mozilla += [
     'OSFileConstants.h',
 ]
 
 CPP_SOURCES += [
     'OSFileConstants.cpp',
     'nsDeviceSensors.cpp',
 ]
--- a/dom/tests/mochitest/gamepad/Makefile.in
+++ b/dom/tests/mochitest/gamepad/Makefile.in
@@ -11,12 +11,13 @@ relativesrcdir	= dom/tests/mochitest/gam
 include $(DEPTH)/config/autoconf.mk
 
 MOCHITEST_FILES = \
   test_gamepad.html \
   test_gamepad_frame_state_sync.html \
   gamepad_frame_state.html \
   test_gamepad_hidden_frame.html \
   gamepad_frame.html \
+  test_navigator_gamepads.html \
   mock_gamepad.js \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/gamepad/test_navigator_gamepads.html
@@ -0,0 +1,89 @@
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test gamepad</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 testNum = 0;
+var tests = [
+  check_first_gamepad,
+  check_second_gamepad,
+  check_gamepad_hole,
+  check_no_gamepads,
+];
+
+function run_next_test(event) {
+  SpecialPowers.executeSoon(function() { tests[testNum++](event); });
+}
+
+// gamepads should be empty first
+is(navigator.getGamepads().length, 0, "should be zero gamepads exposed");
+
+function connecthandler(e) {
+ run_next_test(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
+                                       4, // buttons
+                                       2);// axes
+var index2;
+
+// Press a button to make the gamepad visible to the page.
+GamepadService.newButtonEvent(index1, 0, true);
+
+function check_first_gamepad(e) {
+  // 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");
+  // Add a second gamepad, should automatically show up.
+  index2 = GamepadService.addGamepad("test gamepad 2", // id
+                                     4, // buttons
+                                     2);// axes
+}
+
+function check_second_gamepad(e) {
+  // Second gamepad gets added.
+  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");
+  // Now remove the first one.
+  GamepadService.removeGamepad(index1);
+}
+
+function check_gamepad_hole(e) {
+  // 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");
+  // Now remove the second one.
+  GamepadService.removeGamepad(index2);
+}
+
+function check_no_gamepads(e) {
+  // Second gamepad gets removed.
+  var gamepads = navigator.getGamepads();
+  is(gamepads.length, 0, "gamepads should be empty");
+  SimpleTest.finish();
+}
+</script>
+</body>
+</html>
+
--- a/js/xpconnect/src/event_impl_gen.conf.in
+++ b/js/xpconnect/src/event_impl_gen.conf.in
@@ -76,11 +76,11 @@ exclude_automatic_type_include = [
     'nsIDOMBlob'
   ]
 
 """ Map xpidl interface names to implementation classes. The third column is the canonical interface. """
 xpidl_to_native = [
     ['nsIDOMDocument', 'nsIDocument', 'nsIDocument'],
     ['nsIDOMElement', 'mozilla::dom::Element', 'mozilla::dom::Element'],
     ['nsIDOMCSSStyleSheet', 'nsCSSStyleSheet', 'nsIStyleSheet'],
-    ['nsIDOMGamepad', 'nsDOMGamepad', 'nsIDOMGamepad']
+    ['nsIDOMGamepad', 'Gamepad', 'nsIDOMGamepad']
   ]
 
--- a/layout/build/Makefile.in
+++ b/layout/build/Makefile.in
@@ -252,16 +252,21 @@ SHARED_LIBRARY_LIBS += \
 	$(DEPTH)/js/xpconnect/src/$(LIB_PREFIX)xpconnect_s.$(LIB_SUFFIX)
 
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 ifdef MOZ_GSTREAMER
 EXTRA_DSO_LDOPTS += $(GSTREAMER_LIBS)
 endif
 endif
 
+ifdef MOZ_GAMEPAD
+SHARED_LIBRARY_LIBS += \
+  $(DEPTH)/dom/gamepad/$(LIB_PREFIX)domgamepad_s.$(LIB_SUFFIX)
+endif
+
 include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 
 include $(topsrcdir)/config/rules.mk
 
 LOCAL_INCLUDES	+= -I$(srcdir)/../base \
 		   -I$(srcdir)/../generic \
 		   -I$(srcdir)/../forms \