Bug 1221730 - Move gamepad API to PBackground r=qdot, r=baku
authorChih-Yi Leu <cleu@mozilla.com>
Wed, 01 Jun 2016 17:28:29 +0800
changeset 773948 b7a63d430565c63cf4362dd02c3e462d4e3a894f
parent 773947 5ca3fa35598fcc73642f4e3f8017833ef301accf
child 773949 1e54a2c14f31278e011ebf54e01e6d12fcdc234b
push id127852
push usercleu@mozilla.com
push dateWed, 01 Jun 2016 10:26:22 +0000
treeherdertry@6029b4c6a86a [default view] [failures only]
reviewersqdot, baku
bugs1221730
milestone49.0a1
Bug 1221730 - Move gamepad API to PBackground r=qdot, r=baku
dom/base/nsGlobalWindow.cpp
dom/gamepad/GamepadFunctions.cpp
dom/gamepad/GamepadFunctions.h
dom/gamepad/GamepadManager.cpp
dom/gamepad/GamepadManager.h
dom/gamepad/GamepadMonitoring.cpp
dom/gamepad/GamepadPlatformService.cpp
dom/gamepad/GamepadPlatformService.h
dom/gamepad/GamepadService.cpp
dom/gamepad/GamepadService.h
dom/gamepad/GamepadServiceTest.cpp
dom/gamepad/GamepadServiceTest.h
dom/gamepad/cocoa/CocoaGamepad.cpp
dom/gamepad/ipc/GamepadEventChannelChild.cpp
dom/gamepad/ipc/GamepadEventChannelChild.h
dom/gamepad/ipc/GamepadEventChannelParent.cpp
dom/gamepad/ipc/GamepadEventChannelParent.h
dom/gamepad/ipc/PGamepadEventChannel.ipdl
dom/gamepad/linux/LinuxGamepad.cpp
dom/gamepad/moz.build
dom/gamepad/windows/WindowsGamepad.cpp
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
ipc/glue/BackgroundChildImpl.cpp
ipc/glue/BackgroundChildImpl.h
ipc/glue/BackgroundParentImpl.cpp
ipc/glue/BackgroundParentImpl.h
ipc/glue/PBackground.ipdl
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -185,17 +185,17 @@
 #include "prprf.h"
 
 #include "mozilla/dom/IDBFactory.h"
 #include "mozilla/dom/MessageChannel.h"
 #include "mozilla/dom/Promise.h"
 
 #ifdef MOZ_GAMEPAD
 #include "mozilla/dom/Gamepad.h"
-#include "mozilla/dom/GamepadService.h"
+#include "mozilla/dom/GamepadManager.h"
 #endif
 
 #include "mozilla/dom/VRDevice.h"
 
 #include "nsRefreshDriver.h"
 #include "Layers.h"
 
 #include "mozilla/AddonPathService.h"
@@ -9910,34 +9910,34 @@ void nsGlobalWindow::MaybeUpdateTouchSta
 
 void
 nsGlobalWindow::EnableGamepadUpdates()
 {
   MOZ_ASSERT(IsInnerWindow());
 
   if (mHasGamepad) {
 #ifdef MOZ_GAMEPAD
-    RefPtr<GamepadService> gamepadsvc(GamepadService::GetService());
-    if (gamepadsvc) {
-      gamepadsvc->AddListener(this);
+    RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
+    if (gamepadManager) {
+      gamepadManager->AddListener(this);
     }
 #endif
   }
 }
 
 void
 nsGlobalWindow::DisableGamepadUpdates()
 {
   MOZ_ASSERT(IsInnerWindow());
 
   if (mHasGamepad) {
 #ifdef MOZ_GAMEPAD
-    RefPtr<GamepadService> gamepadsvc(GamepadService::GetService());
-    if (gamepadsvc) {
-      gamepadsvc->RemoveListener(this);
+    RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
+    if (gamepadManager) {
+      gamepadManager->RemoveListener(this);
     }
 #endif
   }
 }
 
 void
 nsGlobalWindow::SetChromeEventHandler(EventTarget* aChromeEventHandler)
 {
@@ -13415,19 +13415,19 @@ nsGlobalWindow::HasSeenGamepadInput()
   return mHasSeenGamepadInput;
 }
 
 void
 nsGlobalWindow::SyncGamepadState()
 {
   MOZ_ASSERT(IsInnerWindow());
   if (mHasSeenGamepadInput) {
-    RefPtr<GamepadService> gamepadsvc(GamepadService::GetService());
+    RefPtr<GamepadManager> gamepadManager(GamepadManager::GetService());
     for (auto iter = mGamepads.Iter(); !iter.Done(); iter.Next()) {
-      gamepadsvc->SyncGamepadState(iter.Key(), iter.UserData());
+      gamepadManager->SyncGamepadState(iter.Key(), iter.UserData());
     }
   }
 }
 #endif // MOZ_GAMEPAD
 
 bool
 nsGlobalWindow::UpdateVRDevices(nsTArray<RefPtr<mozilla::dom::VRDevice>>& aDevices)
 {
deleted file mode 100644
--- a/dom/gamepad/GamepadFunctions.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "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_IsParentProcess());
-  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()) {
-    RefPtr<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_IsParentProcess());
-
-  int index = gGamepadIndex;
-  gGamepadIndex++;
-  GamepadAdded a(NS_ConvertUTF8toUTF16(nsDependentCString(aID)), index,
-                 (uint32_t)aMapping, aNumButtons, aNumAxes);
-  NotifyGamepadChange<GamepadAdded>(a);
-  return index;
-}
-
-void
-RemoveGamepad(uint32_t aIndex)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(XRE_IsParentProcess());
-  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_IsParentProcess());
-  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_IsParentProcess());
-  // 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_IsParentProcess());
-  GamepadAxisInformation a(aIndex, aAxis, aValue);
-  NotifyGamepadChange<GamepadAxisInformation>(a);
-}
-
-void
-ResetGamepadIndexes()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(XRE_IsParentProcess());
-  gGamepadIndex = 0;
-}
-
-} // namespace GamepadFunctions
-} // namespace dom
-} // namespace mozilla
deleted file mode 100644
--- a/dom/gamepad/GamepadFunctions.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#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/GamepadManager.cpp
@@ -0,0 +1,596 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/GamepadManager.h"
+
+#include "mozilla/dom/Gamepad.h"
+#include "mozilla/dom/GamepadAxisMoveEvent.h"
+#include "mozilla/dom/GamepadButtonEvent.h"
+#include "mozilla/dom/GamepadEvent.h"
+#include "mozilla/dom/GamepadEventChannelChild.h"
+#include "mozilla/dom/GamepadMonitoring.h"
+
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/StaticPtr.h"
+
+#include "nsAutoPtr.h"
+#include "nsGlobalWindow.h"
+#include "nsIDOMEvent.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMWindow.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsIServiceManager.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Services.h"
+
+#include <cstddef>
+
+using namespace mozilla::ipc;
+
+namespace mozilla {
+namespace dom {
+
+namespace {
+
+const char* kGamepadEnabledPref = "dom.gamepad.enabled";
+const char* kGamepadEventsEnabledPref =
+  "dom.gamepad.non_standard_events.enabled";
+
+const nsTArray<RefPtr<nsGlobalWindow>>::index_type NoIndex =
+  nsTArray<RefPtr<nsGlobalWindow>>::NoIndex;
+
+bool sShutdown = false;
+
+StaticRefPtr<GamepadManager> gGamepadManagerSingleton;
+
+} // namespace
+
+NS_IMPL_ISUPPORTS(GamepadManager, nsIObserver)
+
+GamepadManager::GamepadManager()
+  : mEnabled(false),
+    mNonstandardEventsEnabled(false),
+    mShuttingDown(false),
+    mChild(nullptr)
+{}
+
+nsresult
+GamepadManager::Init()
+{
+  mEnabled = IsAPIEnabled();
+  mNonstandardEventsEnabled =
+    Preferences::GetBool(kGamepadEventsEnabledPref, false);
+  nsCOMPtr<nsIObserverService> observerService =
+    mozilla::services::GetObserverService();
+
+  if (NS_WARN_IF(!observerService)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsresult rv;
+  rv = observerService->AddObserver(this,
+                                    NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID,
+                                    false);
+
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+GamepadManager::Observe(nsISupports* aSubject,
+                        const char* aTopic,
+                        const char16_t* aData)
+{
+  nsCOMPtr<nsIObserverService> observerService =
+    mozilla::services::GetObserverService();
+  if (observerService) {
+    observerService->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
+  }
+  BeginShutdown();
+  return NS_OK;
+}
+
+void
+GamepadManager::StopMonitoring()
+{
+  if(mChild) {
+    mChild->SendGamepadListenerRemoved();
+    mChild = nullptr;
+  }
+  mGamepads.Clear();
+}
+
+void
+GamepadManager::BeginShutdown()
+{
+  mShuttingDown = true;
+  StopMonitoring();
+  // 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();
+  sShutdown = true;
+}
+
+void
+GamepadManager::AddListener(nsGlobalWindow* aWindow)
+{
+  MOZ_ASSERT(aWindow);
+  MOZ_ASSERT(aWindow->IsInnerWindow());
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!mEnabled || mShuttingDown) {
+    return;
+  }
+
+  if (mListeners.IndexOf(aWindow) != NoIndex) {
+    return; // already exists
+  }
+
+  mListeners.AppendElement(aWindow);
+
+  // IPDL child has been created
+  if (mChild) {
+    return;
+  }
+  PBackgroundChild *actor = BackgroundChild::GetForCurrentThread();
+  //Try to get the PBackground Child actor
+  if (actor) {
+    ActorCreated(actor);
+  } else {
+    bool ok = BackgroundChild::GetOrCreateForCurrentThread(this);
+    MOZ_ASSERT(ok);
+  }
+}
+
+void
+GamepadManager::RemoveListener(nsGlobalWindow* aWindow)
+{
+  MOZ_ASSERT(aWindow);
+  MOZ_ASSERT(aWindow->IsInnerWindow());
+
+  if (mShuttingDown) {
+    // Doesn't matter at this point. It's possible we're being called
+    // as a result of our own destructor here, so just bail out.
+    return;
+  }
+
+  if (mListeners.IndexOf(aWindow) == NoIndex) {
+    return; // doesn't exist
+  }
+
+  mListeners.RemoveElement(aWindow);
+
+  if (mListeners.IsEmpty()) {
+    StopMonitoring();
+  }
+}
+
+already_AddRefed<Gamepad>
+GamepadManager::GetGamepad(uint32_t aIndex) const
+{
+  RefPtr<Gamepad> gamepad;
+  if (mGamepads.Get(aIndex, getter_AddRefs(gamepad))) {
+    return gamepad.forget();
+  }
+
+  return nullptr;
+}
+
+void
+GamepadManager::AddGamepad(uint32_t aIndex,
+                           const nsAString& aId,
+                           GamepadMappingType aMapping,
+                           uint32_t aNumButtons,
+                           uint32_t aNumAxes)
+{
+  //TODO: bug 852258: get initial button/axis state
+  RefPtr<Gamepad> gamepad =
+    new Gamepad(nullptr,
+                aId,
+                0, // index is set by global window
+                aMapping,
+                aNumButtons,
+                aNumAxes);
+
+  // We store the gamepad related to its index given by the parent process,
+  // and no duplicate index is allowed.
+  MOZ_ASSERT(!mGamepads.Get(aIndex, nullptr));
+  mGamepads.Put(aIndex, gamepad);
+  NewConnectionEvent(aIndex, true);
+}
+
+void
+GamepadManager::RemoveGamepad(uint32_t aIndex)
+{
+  RefPtr<Gamepad> gamepad = GetGamepad(aIndex);
+  if (!gamepad) {
+    NS_WARNING("Trying to delete gamepad with invalid index");
+    return;
+  }
+  gamepad->SetConnected(false);
+  NewConnectionEvent(aIndex, false);
+  mGamepads.Remove(aIndex);
+}
+
+void
+GamepadManager::NewButtonEvent(uint32_t aIndex, uint32_t aButton, bool aPressed,
+                               double aValue)
+{
+  if (mShuttingDown) {
+    return;
+  }
+
+  RefPtr<Gamepad> gamepad = GetGamepad(aIndex);
+  if (!gamepad) {
+    return;
+  }
+
+  gamepad->SetButton(aButton, aPressed, aValue);
+
+  // Hold on to listeners in a separate array because firing events
+  // can mutate the mListeners array.
+  nsTArray<RefPtr<nsGlobalWindow>> listeners(mListeners);
+  MOZ_ASSERT(!listeners.IsEmpty());
+
+  for (uint32_t i = 0; i < listeners.Length(); i++) {
+
+    MOZ_ASSERT(listeners[i]->IsInnerWindow());
+
+    // Only send events to non-background windows
+    if (!listeners[i]->AsInner()->IsCurrentInnerWindow() ||
+        listeners[i]->GetOuterWindow()->IsBackground()) {
+      continue;
+    }
+
+    bool firstTime = MaybeWindowHasSeenGamepad(listeners[i], aIndex);
+
+    RefPtr<Gamepad> listenerGamepad = listeners[i]->GetGamepad(aIndex);
+    if (listenerGamepad) {
+      listenerGamepad->SetButton(aButton, aPressed, aValue);
+      if (firstTime) {
+        FireConnectionEvent(listeners[i], listenerGamepad, true);
+      }
+      if (mNonstandardEventsEnabled) {
+        // Fire event
+        FireButtonEvent(listeners[i], listenerGamepad, aButton, aValue);
+      }
+    }
+  }
+}
+
+void
+GamepadManager::FireButtonEvent(EventTarget* aTarget,
+                                Gamepad* aGamepad,
+                                uint32_t aButton,
+                                double aValue)
+{
+  nsString name = aValue == 1.0L ? NS_LITERAL_STRING("gamepadbuttondown") :
+                                   NS_LITERAL_STRING("gamepadbuttonup");
+  GamepadButtonEventInit init;
+  init.mBubbles = false;
+  init.mCancelable = false;
+  init.mGamepad = aGamepad;
+  init.mButton = aButton;
+  RefPtr<GamepadButtonEvent> event =
+    GamepadButtonEvent::Constructor(aTarget, name, init);
+
+  event->SetTrusted(true);
+
+  bool defaultActionEnabled = true;
+  aTarget->DispatchEvent(event, &defaultActionEnabled);
+}
+
+void
+GamepadManager::NewAxisMoveEvent(uint32_t aIndex, uint32_t aAxis, double aValue)
+{
+  if (mShuttingDown) {
+    return;
+  }
+
+  RefPtr<Gamepad> gamepad = GetGamepad(aIndex);
+  if (!gamepad) {
+    return;
+  }
+  gamepad->SetAxis(aAxis, aValue);
+
+  // Hold on to listeners in a separate array because firing events
+  // can mutate the mListeners array.
+  nsTArray<RefPtr<nsGlobalWindow>> listeners(mListeners);
+  MOZ_ASSERT(!listeners.IsEmpty());
+
+  for (uint32_t i = 0; i < listeners.Length(); i++) {
+
+    MOZ_ASSERT(listeners[i]->IsInnerWindow());
+
+    // Only send events to non-background windows
+    if (!listeners[i]->AsInner()->IsCurrentInnerWindow() ||
+        listeners[i]->GetOuterWindow()->IsBackground()) {
+      continue;
+    }
+
+    bool firstTime = MaybeWindowHasSeenGamepad(listeners[i], aIndex);
+
+    RefPtr<Gamepad> listenerGamepad = listeners[i]->GetGamepad(aIndex);
+    if (listenerGamepad) {
+      listenerGamepad->SetAxis(aAxis, aValue);
+      if (firstTime) {
+        FireConnectionEvent(listeners[i], listenerGamepad, true);
+      }
+      if (mNonstandardEventsEnabled) {
+        // Fire event
+        FireAxisMoveEvent(listeners[i], listenerGamepad, aAxis, aValue);
+      }
+    }
+  }
+}
+
+void
+GamepadManager::FireAxisMoveEvent(EventTarget* aTarget,
+                                  Gamepad* aGamepad,
+                                  uint32_t aAxis,
+                                  double aValue)
+{
+  GamepadAxisMoveEventInit init;
+  init.mBubbles = false;
+  init.mCancelable = false;
+  init.mGamepad = aGamepad;
+  init.mAxis = aAxis;
+  init.mValue = aValue;
+  RefPtr<GamepadAxisMoveEvent> event =
+    GamepadAxisMoveEvent::Constructor(aTarget,
+                                      NS_LITERAL_STRING("gamepadaxismove"),
+                                      init);
+
+  event->SetTrusted(true);
+
+  bool defaultActionEnabled = true;
+  aTarget->DispatchEvent(event, &defaultActionEnabled);
+}
+
+void
+GamepadManager::NewConnectionEvent(uint32_t aIndex, bool aConnected)
+{
+  if (mShuttingDown) {
+    return;
+  }
+
+  RefPtr<Gamepad> gamepad = GetGamepad(aIndex);
+  if (!gamepad) {
+    return;
+  }
+
+  // Hold on to listeners in a separate array because firing events
+  // can mutate the mListeners array.
+  nsTArray<RefPtr<nsGlobalWindow>> listeners(mListeners);
+  MOZ_ASSERT(!listeners.IsEmpty());
+
+  if (aConnected) {
+    for (uint32_t i = 0; i < listeners.Length(); i++) {
+
+      MOZ_ASSERT(listeners[i]->IsInnerWindow());
+
+      // Only send events to non-background windows
+      if (!listeners[i]->AsInner()->IsCurrentInnerWindow() ||
+          listeners[i]->GetOuterWindow()->IsBackground()) {
+        continue;
+      }
+
+      // 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);
+
+      RefPtr<Gamepad> listenerGamepad = listeners[i]->GetGamepad(aIndex);
+      if (listenerGamepad) {
+        // Fire event
+        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 = 0; i < listeners.Length(); 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)) {
+        RefPtr<Gamepad> listenerGamepad = listeners[i]->GetGamepad(aIndex);
+        if (listenerGamepad) {
+          listenerGamepad->SetConnected(false);
+          // Fire event
+          FireConnectionEvent(listeners[i], listenerGamepad, false);
+          listeners[i]->RemoveGamepad(aIndex);
+        }
+      }
+    }
+  }
+}
+
+void
+GamepadManager::FireConnectionEvent(EventTarget* aTarget,
+                                    Gamepad* aGamepad,
+                                    bool aConnected)
+{
+  nsString name = aConnected ? NS_LITERAL_STRING("gamepadconnected") :
+                               NS_LITERAL_STRING("gamepaddisconnected");
+  GamepadEventInit init;
+  init.mBubbles = false;
+  init.mCancelable = false;
+  init.mGamepad = aGamepad;
+  RefPtr<GamepadEvent> event =
+    GamepadEvent::Constructor(aTarget, name, init);
+
+  event->SetTrusted(true);
+
+  bool defaultActionEnabled = true;
+  aTarget->DispatchEvent(event, &defaultActionEnabled);
+}
+
+void
+GamepadManager::SyncGamepadState(uint32_t aIndex, Gamepad* aGamepad)
+{
+  if (mShuttingDown || !mEnabled) {
+    return;
+  }
+
+  RefPtr<Gamepad> gamepad = GetGamepad(aIndex);
+  if (!gamepad) {
+    return;
+  }
+
+  aGamepad->SyncState(gamepad);
+}
+
+// static
+bool
+GamepadManager::IsServiceRunning()
+{
+  return !!gGamepadManagerSingleton;
+}
+
+// static
+already_AddRefed<GamepadManager>
+GamepadManager::GetService()
+{
+  if (sShutdown) {
+    return nullptr;
+  }
+
+  if (!gGamepadManagerSingleton) {
+    RefPtr<GamepadManager> manager = new GamepadManager();
+    nsresult rv = manager->Init();
+    if(NS_WARN_IF(NS_FAILED(rv))) {
+      return nullptr;
+    }
+    gGamepadManagerSingleton = manager;
+    ClearOnShutdown(&gGamepadManagerSingleton);
+  }
+
+  RefPtr<GamepadManager> service(gGamepadManagerSingleton);
+  return service.forget();
+}
+
+// static
+bool
+GamepadManager::IsAPIEnabled() {
+  return Preferences::GetBool(kGamepadEnabledPref, false);
+}
+
+bool
+GamepadManager::MaybeWindowHasSeenGamepad(nsGlobalWindow* aWindow, uint32_t aIndex)
+{
+  if (!WindowHasSeenGamepad(aWindow, aIndex)) {
+    // This window hasn't seen this gamepad before, so
+    // send a connection event first.
+    SetWindowHasSeenGamepad(aWindow, aIndex);
+    return true;
+  }
+  return false;
+}
+
+bool
+GamepadManager::WindowHasSeenGamepad(nsGlobalWindow* aWindow, uint32_t aIndex) const
+{
+  RefPtr<Gamepad> gamepad = aWindow->GetGamepad(aIndex);
+  return gamepad != nullptr;
+}
+
+void
+GamepadManager::SetWindowHasSeenGamepad(nsGlobalWindow* aWindow,
+                                        uint32_t aIndex,
+                                        bool aHasSeen)
+{
+  MOZ_ASSERT(aWindow);
+  MOZ_ASSERT(aWindow->IsInnerWindow());
+
+  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);
+    RefPtr<Gamepad> gamepad = GetGamepad(aIndex);
+    if (!gamepad) {
+      return;
+    }
+    RefPtr<Gamepad> clonedGamepad = gamepad->Clone(window);
+    aWindow->AddGamepad(aIndex, clonedGamepad);
+  } else {
+    aWindow->RemoveGamepad(aIndex);
+  }
+}
+
+void
+GamepadManager::Update(const GamepadChangeEvent& aEvent)
+{
+  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());
+    return;
+  }
+  if (aEvent.type() == GamepadChangeEvent::TGamepadRemoved) {
+    const GamepadRemoved& a = aEvent.get_GamepadRemoved();
+    RemoveGamepad(a.index());
+    return;
+  }
+  if (aEvent.type() == GamepadChangeEvent::TGamepadButtonInformation) {
+    const GamepadButtonInformation& a = aEvent.get_GamepadButtonInformation();
+    NewButtonEvent(a.index(), a.button(), a.pressed(), a.value());
+    return;
+  }
+  if (aEvent.type() == GamepadChangeEvent::TGamepadAxisInformation) {
+    const GamepadAxisInformation& a = aEvent.get_GamepadAxisInformation();
+    NewAxisMoveEvent(a.index(), a.axis(), a.value());
+    return;
+  }
+
+  MOZ_CRASH("We shouldn't be here!");
+
+}
+
+//Override nsIIPCBackgroundChildCreateCallback
+void
+GamepadManager::ActorCreated(PBackgroundChild *aActor)
+{
+  MOZ_ASSERT(aActor);
+  GamepadEventChannelChild *child = new GamepadEventChannelChild();
+  PGamepadEventChannelChild *initedChild =
+    aActor->SendPGamepadEventChannelConstructor(child);
+  if (NS_WARN_IF(!initedChild)) {
+    ActorFailed();
+    return;
+  }
+  MOZ_ASSERT(initedChild == child);
+  mChild = child;
+  mChild->SendGamepadListenerAdded();
+}
+
+//Override nsIIPCBackgroundChildCreateCallback
+void
+GamepadManager::ActorFailed()
+{
+  MOZ_CRASH("Gamepad IPC actor create failed!");
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/GamepadManager.h
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_GamepadManager_h_
+#define mozilla_dom_GamepadManager_h_
+
+#include "nsIIPCBackgroundChildCreateCallback.h"
+#include "nsIObserver.h"
+// Needed for GamepadMappingType
+#include "mozilla/dom/GamepadBinding.h"
+
+class nsGlobalWindow;
+
+namespace mozilla {
+namespace dom {
+
+class EventTarget;
+class Gamepad;
+class GamepadChangeEvent;
+class GamepadEventChannelChild;
+
+class GamepadManager final : public nsIObserver,
+                             public nsIIPCBackgroundChildCreateCallback
+{
+ public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+  NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
+
+  // Returns true if we actually have a service up and running
+  static bool IsServiceRunning();
+  // Get the singleton service
+  static already_AddRefed<GamepadManager> GetService();
+  // Return true if the API is preffed on.
+  static bool IsAPIEnabled();
+
+  void BeginShutdown();
+  void StopMonitoring();
+
+  // 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.
+  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);
+
+  // 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) const;
+
+  // Receive GamepadChangeEvent messages from parent process to fire DOM events
+  void Update(const GamepadChangeEvent& aGamepadEvent);
+
+ protected:
+  GamepadManager();
+  ~GamepadManager() {};
+
+  // Fire a gamepadconnected or gamepaddisconnected event for the gamepad
+  // at |aIndex| to all windows that are listening and have received
+  // gamepad input.
+  void NewConnectionEvent(uint32_t aIndex, bool aConnected);
+
+  // Fire a gamepadaxismove event to the window at |aTarget| for |aGamepad|.
+  void FireAxisMoveEvent(EventTarget* aTarget,
+                         Gamepad* aGamepad,
+                         uint32_t axis,
+                         double value);
+
+  // Fire one of gamepadbutton{up,down} event at the window at |aTarget| for
+  // |aGamepad|.
+  void FireButtonEvent(EventTarget* aTarget,
+                       Gamepad* aGamepad,
+                       uint32_t aButton,
+                       double aValue);
+
+  // Fire one of gamepad{connected,disconnected} event at the window at
+  // |aTarget| for |aGamepad|.
+  void FireConnectionEvent(EventTarget* aTarget,
+                           Gamepad* aGamepad,
+                           bool aConnected);
+
+  // true if this feature is enabled in preferences
+  bool mEnabled;
+  // true if non-standard events are enabled in preferences
+  bool mNonstandardEventsEnabled;
+  // true when shutdown has begun
+  bool mShuttingDown;
+
+  // Gamepad IPDL child
+  // This pointer is only used by this singleton instance and
+  // will be destroyed during the IPDL shutdown chain, so we
+  // don't need to refcount it here
+  GamepadEventChannelChild MOZ_NON_OWNING_REF *mChild;
+
+ private:
+
+  nsresult Init();
+
+  bool MaybeWindowHasSeenGamepad(nsGlobalWindow* aWindow, uint32_t aIndex);
+  // Returns true if we have already sent data from this gamepad
+  // to this window. This should only return true if the user
+  // explicitly interacted with a gamepad while this window
+  // was focused, by pressing buttons or similar actions.
+  bool WindowHasSeenGamepad(nsGlobalWindow* aWindow, uint32_t aIndex) const;
+  // Indicate that a window has recieved data from a gamepad.
+  void SetWindowHasSeenGamepad(nsGlobalWindow* aWindow, uint32_t aIndex,
+                               bool aHasSeen = true);
+
+  // Gamepads connected to the system. Copies of these are handed out
+  // to each window.
+  nsRefPtrHashtable<nsUint32HashKey, Gamepad> mGamepads;
+  // Inner windows that are listening for gamepad events.
+  // has been sent to that window.
+  nsTArray<RefPtr<nsGlobalWindow>> mListeners;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_GamepadManager_h_
--- a/dom/gamepad/GamepadMonitoring.cpp
+++ b/dom/gamepad/GamepadMonitoring.cpp
@@ -1,33 +1,32 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/GamepadMonitoring.h"
-#include "mozilla/dom/GamepadFunctions.h"
-#include "mozilla/dom/PContentParent.h"
+#include "mozilla/dom/GamepadPlatformService.h"
+#include "mozilla/ipc/BackgroundParent.h"
+
+using namespace mozilla::ipc;
 
 namespace mozilla {
 namespace dom {
 
-using namespace GamepadFunctions;
-
 void
 MaybeStopGamepadMonitoring()
 {
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(XRE_IsParentProcess());
-  nsTArray<ContentParent*> t;
-  ContentParent::GetAll(t);
-  for(uint32_t i = 0; i < t.Length(); ++i) {
-    if (t[i]->HasGamepadListener()) {
-      return;
-    }
+  AssertIsOnBackgroundThread();
+  GamepadPlatformService* service =
+    GamepadPlatformService::GetParentService();
+  MOZ_ASSERT(service);
+  if(service->HasGamepadListeners()) {
+    return;
   }
   StopGamepadMonitoring();
-  ResetGamepadIndexes();
+  service->ResetGamepadIndexes();
+  service->MaybeShutdown();
 }
 
 } // namespace dom
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/GamepadPlatformService.cpp
@@ -0,0 +1,220 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/GamepadPlatformService.h"
+
+#include "mozilla/dom/GamepadEventChannelParent.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/unused.h"
+
+#include "nsCOMPtr.h"
+#include "nsHashKeys.h"
+#include "nsIThread.h"
+
+using namespace mozilla::ipc;
+
+namespace mozilla {
+namespace dom {
+
+namespace {
+
+// This is the singleton instance of GamepadPlatformService, can be called
+// by both background and monitor thread.
+StaticAutoPtr<GamepadPlatformService> gGamepadPlatformServiceSingleton;
+
+} //namepsace
+
+GamepadPlatformService::GamepadPlatformService()
+  : mGamepadIndex(0),
+    mMutex("mozilla::dom::GamepadPlatformService")
+{}
+
+GamepadPlatformService::~GamepadPlatformService()
+{
+  Cleanup();
+}
+
+// static
+GamepadPlatformService*
+GamepadPlatformService::GetParentService()
+{
+  //GamepadPlatformService can only be accessed in parent process
+  MOZ_ASSERT(XRE_IsParentProcess());
+  if(!gGamepadPlatformServiceSingleton) {
+    gGamepadPlatformServiceSingleton = new GamepadPlatformService();
+  }
+  return gGamepadPlatformServiceSingleton;
+}
+
+template<class T>
+void
+GamepadPlatformService::NotifyGamepadChange(const T& aInfo)
+{
+  // This method is called by monitor populated in
+  // platform-dependent backends
+  MOZ_ASSERT(XRE_IsParentProcess());
+  MOZ_ASSERT(!NS_IsMainThread());
+
+  GamepadChangeEvent e(aInfo);
+
+  // mChannelParents may be accessed by background thread in the
+  // same time, we use mutex to prevent possible race condtion
+  MutexAutoLock autoLock(mMutex);
+  for(uint32_t i = 0; i < mChannelParents.Length(); ++i) {
+    mChannelParents[i]->DispatchUpdateEvent(e);
+  }
+}
+
+uint32_t
+GamepadPlatformService::AddGamepad(const char* aID,
+                                   GamepadMappingType aMapping,
+                                   uint32_t aNumButtons, uint32_t aNumAxes)
+{
+  // This method is called by monitor thread populated in
+  // platform-dependent backends
+  MOZ_ASSERT(XRE_IsParentProcess());
+  MOZ_ASSERT(!NS_IsMainThread());
+
+  uint32_t index = ++mGamepadIndex;
+  GamepadAdded a(NS_ConvertUTF8toUTF16(nsDependentCString(aID)), index,
+                 (uint32_t)aMapping, aNumButtons, aNumAxes);
+  NotifyGamepadChange<GamepadAdded>(a);
+  return index;
+}
+
+void
+GamepadPlatformService::RemoveGamepad(uint32_t aIndex)
+{
+  // This method is called by monitor thread populated in
+  // platform-dependent backends
+  MOZ_ASSERT(XRE_IsParentProcess());
+  MOZ_ASSERT(!NS_IsMainThread());
+  GamepadRemoved a(aIndex);
+  NotifyGamepadChange<GamepadRemoved>(a);
+}
+
+void
+GamepadPlatformService::NewButtonEvent(uint32_t aIndex, uint32_t aButton,
+                                       bool aPressed, double aValue)
+{
+  // This method is called by monitor thread populated in
+  // platform-dependent backends
+  MOZ_ASSERT(XRE_IsParentProcess());
+  MOZ_ASSERT(!NS_IsMainThread());
+  GamepadButtonInformation a(aIndex, aButton, aPressed, aValue);
+  NotifyGamepadChange<GamepadButtonInformation>(a);
+}
+
+void
+GamepadPlatformService::NewButtonEvent(uint32_t aIndex, uint32_t aButton,
+                                       bool aPressed)
+{
+  // This method is called by monitor thread populated in
+  // platform-dependent backends
+  MOZ_ASSERT(XRE_IsParentProcess());
+  MOZ_ASSERT(!NS_IsMainThread());
+  // When only a digital button is available the value will be synthesized.
+  NewButtonEvent(aIndex, aButton, aPressed, aPressed ? 1.0L : 0.0L);
+}
+
+void
+GamepadPlatformService::NewAxisMoveEvent(uint32_t aIndex, uint32_t aAxis,
+                                         double aValue)
+{
+  // This method is called by monitor thread populated in
+  // platform-dependent backends
+  MOZ_ASSERT(XRE_IsParentProcess());
+  MOZ_ASSERT(!NS_IsMainThread());
+  GamepadAxisInformation a(aIndex, aAxis, aValue);
+  NotifyGamepadChange<GamepadAxisInformation>(a);
+}
+
+void
+GamepadPlatformService::ResetGamepadIndexes()
+{
+  // This method is called by monitor thread populated in
+  // platform-dependent backends
+  MOZ_ASSERT(XRE_IsParentProcess());
+  MOZ_ASSERT(!NS_IsMainThread());
+  mGamepadIndex = 0;
+}
+
+void
+GamepadPlatformService::AddChannelParent(GamepadEventChannelParent* aParent)
+{
+  // mChannelParents can only be modified once GamepadEventChannelParent
+  // is created or removed in Background thread
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aParent);
+  MOZ_ASSERT(!mChannelParents.Contains(aParent));
+
+  // We use mutex here to prevent race condition with monitor thread
+  MutexAutoLock autoLock(mMutex);
+  mChannelParents.AppendElement(aParent);
+}
+
+void
+GamepadPlatformService::RemoveChannelParent(GamepadEventChannelParent* aParent)
+{
+  // mChannelParents can only be modified once GamepadEventChannelParent
+  // is created or removed in Background thread
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aParent);
+  MOZ_ASSERT(mChannelParents.Contains(aParent));
+
+  // We use mutex here to prevent race condition with monitor thread
+  MutexAutoLock autoLock(mMutex);
+  mChannelParents.RemoveElement(aParent);
+}
+
+bool
+GamepadPlatformService::HasGamepadListeners()
+{
+  // mChannelParents may be accessed by background thread in the
+  // same time, we use mutex to prevent possible race condtion
+  AssertIsOnBackgroundThread();
+
+  // We use mutex here to prevent race condition with monitor thread
+  MutexAutoLock autoLock(mMutex);
+  for (uint32_t i = 0; i < mChannelParents.Length(); i++) {
+    if(mChannelParents[i]->HasGamepadListener()) {
+      return true;
+    }
+  }
+  return false;
+}
+
+void
+GamepadPlatformService::MaybeShutdown()
+{
+  // This method is invoked in MaybeStopGamepadMonitoring when
+  // an IPDL channel is going to be destroyed
+  AssertIsOnBackgroundThread();
+
+  bool isChannelParentEmpty;
+  {
+    MutexAutoLock autoLock(mMutex);
+    isChannelParentEmpty = mChannelParents.IsEmpty();
+  }
+  if(isChannelParentEmpty) {
+    gGamepadPlatformServiceSingleton = nullptr;
+  }
+}
+
+void
+GamepadPlatformService::Cleanup()
+{
+  // This method is called when GamepadPlatformService is
+  // successfully distructed in background thread
+  AssertIsOnBackgroundThread();
+
+  MutexAutoLock autoLock(mMutex);
+  mChannelParents.Clear();
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/GamepadPlatformService.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_GamepadPlatformService_h_
+#define mozilla_dom_GamepadPlatformService_h_
+
+#include "mozilla/dom/GamepadBinding.h"
+
+#include "mozilla/Mutex.h"
+#include "mozilla/StaticPtr.h"
+
+namespace mozilla {
+namespace dom {
+
+class GamepadEventChannelParent;
+
+// Platform Service for building and transmitting IPDL messages
+// through the HAL sandbox. Used by platform specific
+// Gamepad implementations
+//
+// This class can be accessed by the following 2 threads :
+// 1. Background thread:
+//    This thread takes charge of IPDL communications
+//    between here and DOM side
+//
+// 2. Monitor Thread:
+//    This thread is populated in platform-dependent backends, which
+//    is in charge of processing gamepad hardware events from OS
+class GamepadPlatformService final
+{
+ public:
+  ~GamepadPlatformService();
+  //Get the singleton service
+  static GamepadPlatformService* GetParentService();
+
+  // 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();
+
+  //Add IPDL parent instance
+  void AddChannelParent(GamepadEventChannelParent* aParent);
+
+  //Remove IPDL parent instance
+  void RemoveChannelParent(GamepadEventChannelParent* aParent);
+
+  bool HasGamepadListeners();
+
+  void MaybeShutdown();
+
+ private:
+  GamepadPlatformService();
+  template<class T> void NotifyGamepadChange(const T& aInfo);
+  void Cleanup();
+
+  // mGamepadIndex can only be accessed by monitor thread
+  uint32_t mGamepadIndex;
+
+  // mChannelParents stores all the GamepadEventChannelParent instances
+  // which may be accessed by both background thread and monitor thread
+  // simultaneously, so we have a mutex to prevent race condition
+  nsTArray<RefPtr<GamepadEventChannelParent>> mChannelParents;
+
+  // This mutex protects mChannelParents from race condition
+  // between background and monitor thread
+  Mutex mMutex;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif
deleted file mode 100644
--- a/dom/gamepad/GamepadService.cpp
+++ /dev/null
@@ -1,562 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "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 "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 <cstddef>
-
-namespace mozilla {
-namespace dom {
-
-namespace {
-const char* kGamepadEnabledPref = "dom.gamepad.enabled";
-const char* kGamepadEventsEnabledPref =
-  "dom.gamepad.non_standard_events.enabled";
-// Amount of time to wait before cleaning up gamepad resources
-// when no pages are listening for events.
-const int kCleanupDelayMS = 2000;
-const nsTArray<RefPtr<nsGlobalWindow> >::index_type NoIndex =
-    nsTArray<RefPtr<nsGlobalWindow> >::NoIndex;
-
-StaticRefPtr<GamepadService> gGamepadServiceSingleton;
-
-} // namespace
-
-bool GamepadService::sShutdown = false;
-
-NS_IMPL_ISUPPORTS(GamepadService, nsIObserver)
-
-GamepadService::GamepadService()
-  : mStarted(false),
-    mShuttingDown(false)
-{
-  mEnabled = IsAPIEnabled();
-  mNonstandardEventsEnabled =
-    Preferences::GetBool(kGamepadEventsEnabledPref, false);
-  nsCOMPtr<nsIObserverService> observerService =
-    mozilla::services::GetObserverService();
-  observerService->AddObserver(this,
-                               NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID,
-                               false);
-}
-
-NS_IMETHODIMP
-GamepadService::Observe(nsISupports* aSubject,
-                        const char* aTopic,
-                        const char16_t* aData)
-{
-  nsCOMPtr<nsIObserverService> observerService =
-    mozilla::services::GetObserverService();
-  observerService->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
-
-  BeginShutdown();
-  return NS_OK;
-}
-
-void
-GamepadService::StopMonitoring()
-{
-  if (mStarted) {
-    if (XRE_IsParentProcess()) {
-      MaybeStopGamepadMonitoring();
-    } else {
-      ContentChild::GetSingleton()->SendGamepadListenerRemoved();
-    }
-    mStarted = false;
-  }
-  mGamepads.Clear();
-}
-
-void
-GamepadService::BeginShutdown()
-{
-  mShuttingDown = true;
-  if (mTimer) {
-    mTimer->Cancel();
-  }
-  StopMonitoring();
-  // 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();
-  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) {
-    if (XRE_IsParentProcess()) {
-      StartGamepadMonitoring();
-    } else {
-      ContentChild::GetSingleton()->SendGamepadListenerAdded();
-    }
-    mStarted = true;
-  }
-  mListeners.AppendElement(aWindow);
-}
-
-void
-GamepadService::RemoveListener(nsGlobalWindow* aWindow)
-{
-  MOZ_ASSERT(aWindow);
-  MOZ_ASSERT(aWindow->IsInnerWindow());
-
-  if (mShuttingDown) {
-    // Doesn't matter at this point. It's possible we're being called
-    // as a result of our own destructor here, so just bail out.
-    return;
-  }
-
-  if (mListeners.IndexOf(aWindow) == NoIndex) {
-    return; // doesn't exist
-  }
-
-  mListeners.RemoveElement(aWindow);
-
-  if (mListeners.Length() == 0 && !mShuttingDown && mStarted) {
-    if (XRE_IsParentProcess()) {
-      StartCleanupTimer();
-    } else {
-      StopMonitoring();
-    }
-  }
-}
-
-already_AddRefed<Gamepad>
-GamepadService::GetGamepad(uint32_t aIndex)
-{
-  RefPtr<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
-  RefPtr<Gamepad> gamepad =
-    new Gamepad(nullptr,
-                aId,
-                0, // index is set by global window
-                aMapping,
-                aNumButtons,
-                aNumAxes);
-
-  // 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)
-{
-  RefPtr<Gamepad> gamepad = GetGamepad(aIndex);
-  if (!gamepad) {
-    NS_WARNING("Trying to delete gamepad with invalid index");
-    return;
-  }
-  gamepad->SetConnected(false);
-    NewConnectionEvent(aIndex, false);
-  mGamepads.Remove(aIndex);
-}
-
-void
-GamepadService::NewButtonEvent(uint32_t aIndex, uint32_t aButton, bool aPressed,
-                               double aValue)
-{
-  RefPtr<Gamepad> gamepad = GetGamepad(aIndex);
-  if (mShuttingDown || !gamepad) {
-    return;
-  }
-
-  gamepad->SetButton(aButton, aPressed, aValue);
-
-  // Hold on to listeners in a separate array because firing events
-  // can mutate the mListeners array.
-  nsTArray<RefPtr<nsGlobalWindow>> listeners(mListeners);
-
-  for (uint32_t i = listeners.Length(); i > 0 ; ) {
-    --i;
-
-    MOZ_ASSERT(listeners[i]->IsInnerWindow());
-
-    // Only send events to non-background windows
-    if (!listeners[i]->AsInner()->IsCurrentInnerWindow() ||
-        listeners[i]->GetOuterWindow()->IsBackground()) {
-      continue;
-    }
-
-    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;
-    }
-
-    RefPtr<Gamepad> listenerGamepad = listeners[i]->GetGamepad(aIndex);
-    if (listenerGamepad) {
-      listenerGamepad->SetButton(aButton, aPressed, aValue);
-      if (first_time) {
-        FireConnectionEvent(listeners[i], listenerGamepad, true);
-      }
-      if (mNonstandardEventsEnabled) {
-        // Fire event
-        FireButtonEvent(listeners[i], listenerGamepad, aButton, aValue);
-      }
-    }
-  }
-}
-
-void
-GamepadService::FireButtonEvent(EventTarget* aTarget,
-                                Gamepad* aGamepad,
-                                uint32_t aButton,
-                                double aValue)
-{
-  nsString name = aValue == 1.0L ? NS_LITERAL_STRING("gamepadbuttondown") :
-                                   NS_LITERAL_STRING("gamepadbuttonup");
-  GamepadButtonEventInit init;
-  init.mBubbles = false;
-  init.mCancelable = false;
-  init.mGamepad = aGamepad;
-  init.mButton = aButton;
-  RefPtr<GamepadButtonEvent> event =
-    GamepadButtonEvent::Constructor(aTarget, name, init);
-
-  event->SetTrusted(true);
-
-  bool defaultActionEnabled = true;
-  aTarget->DispatchEvent(event, &defaultActionEnabled);
-}
-
-void
-GamepadService::NewAxisMoveEvent(uint32_t aIndex, uint32_t aAxis, double aValue)
-{
-  RefPtr<Gamepad> gamepad = GetGamepad(aIndex);
-  if (mShuttingDown || !gamepad) {
-    return;
-  }
-  gamepad->SetAxis(aAxis, aValue);
-
-  // Hold on to listeners in a separate array because firing events
-  // can mutate the mListeners array.
-  nsTArray<RefPtr<nsGlobalWindow> > listeners(mListeners);
-
-  for (uint32_t i = listeners.Length(); i > 0 ; ) {
-    --i;
-
-    MOZ_ASSERT(listeners[i]->IsInnerWindow());
-
-    // Only send events to non-background windows
-    if (!listeners[i]->AsInner()->IsCurrentInnerWindow() ||
-        listeners[i]->GetOuterWindow()->IsBackground()) {
-      continue;
-    }
-
-    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;
-    }
-
-    RefPtr<Gamepad> listenerGamepad = listeners[i]->GetGamepad(aIndex);
-    if (listenerGamepad) {
-      listenerGamepad->SetAxis(aAxis, aValue);
-      if (first_time) {
-        FireConnectionEvent(listeners[i], listenerGamepad, true);
-      }
-      if (mNonstandardEventsEnabled) {
-        // Fire event
-        FireAxisMoveEvent(listeners[i], listenerGamepad, aAxis, aValue);
-      }
-    }
-  }
-}
-
-void
-GamepadService::FireAxisMoveEvent(EventTarget* aTarget,
-                                  Gamepad* aGamepad,
-                                  uint32_t aAxis,
-                                  double aValue)
-{
-  GamepadAxisMoveEventInit init;
-  init.mBubbles = false;
-  init.mCancelable = false;
-  init.mGamepad = aGamepad;
-  init.mAxis = aAxis;
-  init.mValue = aValue;
-  RefPtr<GamepadAxisMoveEvent> event =
-    GamepadAxisMoveEvent::Constructor(aTarget,
-                                      NS_LITERAL_STRING("gamepadaxismove"),
-                                      init);
-
-  event->SetTrusted(true);
-
-  bool defaultActionEnabled = true;
-  aTarget->DispatchEvent(event, &defaultActionEnabled);
-}
-
-void
-GamepadService::NewConnectionEvent(uint32_t aIndex, bool aConnected)
-{
-  RefPtr<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<RefPtr<nsGlobalWindow> > listeners(mListeners);
-
-  if (aConnected) {
-    for (uint32_t i = listeners.Length(); i > 0 ; ) {
-      --i;
-
-      MOZ_ASSERT(listeners[i]->IsInnerWindow());
-
-      // Only send events to non-background windows
-      if (!listeners[i]->AsInner()->IsCurrentInnerWindow() ||
-          listeners[i]->GetOuterWindow()->IsBackground()) {
-        continue;
-      }
-
-      // 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);
-
-      RefPtr<Gamepad> listenerGamepad = listeners[i]->GetGamepad(aIndex);
-      if (listenerGamepad) {
-        // Fire event
-        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)) {
-        RefPtr<Gamepad> listenerGamepad = listeners[i]->GetGamepad(aIndex);
-        if (listenerGamepad) {
-          listenerGamepad->SetConnected(false);
-          // Fire event
-          FireConnectionEvent(listeners[i], listenerGamepad, false);
-          listeners[i]->RemoveGamepad(aIndex);
-        }
-      }
-    }
-  }
-}
-
-void
-GamepadService::FireConnectionEvent(EventTarget* aTarget,
-                                    Gamepad* aGamepad,
-                                    bool aConnected)
-{
-  nsString name = aConnected ? NS_LITERAL_STRING("gamepadconnected") :
-                               NS_LITERAL_STRING("gamepaddisconnected");
-  GamepadEventInit init;
-  init.mBubbles = false;
-  init.mCancelable = false;
-  init.mGamepad = aGamepad;
-  RefPtr<GamepadEvent> event =
-    GamepadEvent::Constructor(aTarget, name, init);
-
-  event->SetTrusted(true);
-
-  bool defaultActionEnabled = true;
-  aTarget->DispatchEvent(event, &defaultActionEnabled);
-}
-
-void
-GamepadService::SyncGamepadState(uint32_t aIndex, Gamepad* aGamepad)
-{
-  RefPtr<Gamepad> gamepad = GetGamepad(aIndex);
-  if (mShuttingDown || !mEnabled || !gamepad) {
-    return;
-  }
-
-  aGamepad->SyncState(gamepad);
-}
-
-// static
-bool
-GamepadService::IsServiceRunning()
-{
-  return !!gGamepadServiceSingleton;
-}
-
-// static
-already_AddRefed<GamepadService>
-GamepadService::GetService()
-{
-  if (sShutdown) {
-    return nullptr;
-  }
-
-  if (!gGamepadServiceSingleton) {
-    gGamepadServiceSingleton = new GamepadService();
-    ClearOnShutdown(&gGamepadServiceSingleton);
-  }
-  RefPtr<GamepadService> service(gGamepadServiceSingleton);
-  return service.forget();
-}
-
-// static
-bool
-GamepadService::IsAPIEnabled() {
-  return Preferences::GetBool(kGamepadEnabledPref, false);
-}
-
-bool
-GamepadService::WindowHasSeenGamepad(nsGlobalWindow* aWindow, uint32_t aIndex)
-{
-  RefPtr<Gamepad> gamepad = aWindow->GetGamepad(aIndex);
-  return gamepad != nullptr;
-}
-
-void
-GamepadService::SetWindowHasSeenGamepad(nsGlobalWindow* aWindow,
-                                        uint32_t aIndex,
-                                        bool aHasSeen)
-{
-  MOZ_ASSERT(aWindow);
-  MOZ_ASSERT(aWindow->IsInnerWindow());
-
-  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);
-    RefPtr<Gamepad> gamepad = GetGamepad(aIndex);
-    MOZ_ASSERT(gamepad);
-    if (!gamepad) {
-      return;
-    }
-    RefPtr<Gamepad> clonedGamepad = gamepad->Clone(window);
-    aWindow->AddGamepad(aIndex, clonedGamepad);
-  } else {
-    aWindow->RemoveGamepad(aIndex);
-  }
-}
-
-// static
-void
-GamepadService::TimeoutHandler(nsITimer* aTimer, void* aClosure)
-{
-  // the reason that we use self, instead of just using nsITimerCallback or nsIObserver
-  // is so that subclasses are free to use timers without worry about the base classes's
-  // usage.
-  GamepadService* self = reinterpret_cast<GamepadService*>(aClosure);
-  if (!self) {
-    NS_ERROR("no self");
-    return;
-  }
-
-  if (self->mShuttingDown) {
-    return;
-  }
-
-  if (self->mListeners.Length() == 0) {
-    self->StopMonitoring();
-  }
-}
-
-void
-GamepadService::StartCleanupTimer()
-{
-  if (mTimer) {
-    mTimer->Cancel();
-  }
-
-  mTimer = do_CreateInstance("@mozilla.org/timer;1");
-  if (mTimer) {
-    mTimer->InitWithFuncCallback(TimeoutHandler,
-                                 this,
-                                 kCleanupDelayMS,
-                                 nsITimer::TYPE_ONE_SHOT);
-  }
-}
-
-void
-GamepadService::Update(const GamepadChangeEvent& aEvent)
-{
-  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
deleted file mode 100644
--- a/dom/gamepad/GamepadService.h
+++ /dev/null
@@ -1,139 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef mozilla_dom_GamepadService_h_
-#define mozilla_dom_GamepadService_h_
-
-#include <stdint.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();
-  void StopMonitoring();
-
-  // 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.
-  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);
-
-  // 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.
-  void NewConnectionEvent(uint32_t aIndex, bool aConnected);
-
-  // Fire a gamepadaxismove event to the window at |aTarget| for |aGamepad|.
-  void FireAxisMoveEvent(EventTarget* aTarget,
-                         Gamepad* aGamepad,
-                         uint32_t axis,
-                         double value);
-
-  // Fire one of gamepadbutton{up,down} event at the window at |aTarget| for
-  // |aGamepad|.
-  void FireButtonEvent(EventTarget* aTarget,
-                       Gamepad* aGamepad,
-                       uint32_t aButton,
-                       double aValue);
-
-  // Fire one of gamepad{connected,disconnected} event at the window at
-  // |aTarget| for |aGamepad|.
-  void FireConnectionEvent(EventTarget* aTarget,
-                           Gamepad* aGamepad,
-                           bool aConnected);
-
-  // true if this feature is enabled in preferences
-  bool mEnabled;
-  // true if non-standard events are enabled in preferences
-  bool mNonstandardEventsEnabled;
-  // true if the platform-specific backend has started work
-  bool mStarted;
-  // true when shutdown has begun
-  bool mShuttingDown;
-
- private:
-  // Returns true if we have already sent data from this gamepad
-  // to this window. This should only return true if the user
-  // explicitly interacted with a gamepad while this window
-  // was focused, by pressing buttons or similar actions.
-  bool WindowHasSeenGamepad(nsGlobalWindow* aWindow, uint32_t aIndex);
-  // Indicate that a window has recieved data from a gamepad.
-  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.
-  nsRefPtrHashtable<nsUint32HashKey, Gamepad> mGamepads;
-  // Inner windows that are listening for gamepad events.
-  // has been sent to that window.
-  nsTArray<RefPtr<nsGlobalWindow> > mListeners;
-  nsCOMPtr<nsITimer> mTimer;
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_GamepadService_h_
--- a/dom/gamepad/GamepadServiceTest.cpp
+++ b/dom/gamepad/GamepadServiceTest.cpp
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "GamepadServiceTest.h"
-#include "mozilla/dom/GamepadService.h"
-#include "mozilla/dom/GamepadFunctions.h"
+#include "mozilla/dom/GamepadManager.h"
+#include "mozilla/dom/GamepadPlatformService.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.
  */
@@ -26,61 +26,71 @@ GamepadServiceTest::CreateService()
   if (sSingleton == nullptr) {
     sSingleton = new GamepadServiceTest();
   }
   RefPtr<GamepadServiceTest> service = sSingleton;
   return service.forget();
 }
 
 GamepadServiceTest::GamepadServiceTest() :
-  mService(GamepadService::GetService())
+  mService(GamepadManager::GetService())
 {
 }
 
 GamepadServiceTest::~GamepadServiceTest()
 {
 }
 
 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);
+  Gamepadservice* service = Gamepadservice::GetService();
+  MOZ_ASSERT(service);
+  *aGamepadIndex = service->AddGamepad(aID,
+                                       static_cast<GamepadMappingType>(aMapping),
+                                       aNumButtons,
+                                       aNumAxes);
   return NS_OK;
 }
 
 NS_IMETHODIMP GamepadServiceTest::RemoveGamepad(uint32_t aIndex)
 {
-  GamepadFunctions::RemoveGamepad(aIndex);
+  Gamepadservice* service = Gamepadservice::GetService();
+  MOZ_ASSERT(service);
+  service->RemoveGamepad(aIndex);
   return NS_OK;
 }
 
 NS_IMETHODIMP GamepadServiceTest::NewButtonEvent(uint32_t aIndex,
                                                  uint32_t aButton,
                                                  bool aPressed)
 {
-  GamepadFunctions::NewButtonEvent(aIndex, aButton, aPressed);
+  Gamepadservice* service = Gamepadservice::GetService();
+  MOZ_ASSERT(service);
+  service->NewButtonEvent(aIndex, aButton, aPressed);
   return NS_OK;
 }
 
 NS_IMETHODIMP GamepadServiceTest::NewButtonValueEvent(uint32_t aIndex,
                                                       uint32_t aButton,
                                                       bool aPressed,
                                                       double aValue)
 {
-  GamepadFunctions::NewButtonEvent(aIndex, aButton, aPressed, aValue);
+  Gamepadservice* service = Gamepadservice::GetService();
+  MOZ_ASSERT(service);
+  service->NewButtonEvent(aIndex, aButton, aPressed, aValue);
   return NS_OK;
 }
 
 NS_IMETHODIMP GamepadServiceTest::NewAxisMoveEvent(uint32_t aIndex,
                                                    uint32_t aAxis,
                                                    double aValue)
 {
-  GamepadFunctions::NewAxisMoveEvent(aIndex, aAxis, aValue);
+  Gamepadservice* service = Gamepadservice::GetService();
+  MOZ_ASSERT(service);
+  service->NewAxisMoveEvent(aIndex, aAxis, aValue);
   return NS_OK;
 }
 
--- a/dom/gamepad/GamepadServiceTest.h
+++ b/dom/gamepad/GamepadServiceTest.h
@@ -7,34 +7,34 @@
 #ifndef mozilla_dom_GamepadServiceTest_h_
 #define mozilla_dom_GamepadServiceTest_h_
 
 #include "nsIGamepadServiceTest.h"
 
 namespace mozilla {
 namespace dom {
 
-class GamepadService;
+class GamepadManager;
 
 // 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.
-  RefPtr<GamepadService> mService;
+  RefPtr<GamepadManager> mService;
   virtual ~GamepadServiceTest();
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #define NS_GAMEPAD_TEST_CID \
 { 0xfb1fcb57, 0xebab, 0x4cf4, \
--- a/dom/gamepad/cocoa/CocoaGamepad.cpp
+++ b/dom/gamepad/cocoa/CocoaGamepad.cpp
@@ -2,30 +2,31 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // 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/dom/GamepadPlatformService.h"
 #include "mozilla/ArrayUtils.h"
+#include "nsThreadUtils.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 namespace mozilla::dom;
 using std::vector;
 
 struct Button {
   int id;
   bool analog;
   IOHIDElementRef element;
   CFIndex min;
   CFIndex max;
@@ -195,32 +196,57 @@ void Gamepad::init(IOHIDDeviceRef device
   }
 }
 
 class DarwinGamepadService {
  private:
   IOHIDManagerRef mManager;
   vector<Gamepad> mGamepads;
 
+  //Workaround to support running in background thread
+  CFRunLoopRef mMonitorRunLoop;
+  nsCOMPtr<nsIThread> mMonitorThread;
+
   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);
+  void StartupInternal();
 
  public:
   DarwinGamepadService();
   ~DarwinGamepadService();
   void Startup();
   void Shutdown();
+  friend class DarwinGamepadServiceStartupRunnable;
+};
+
+class DarwinGamepadServiceStartupRunnable final : public Runnable
+{
+ private:
+  ~DarwinGamepadServiceStartupRunnable() {}
+  // This Runnable schedules startup of DarwinGamepadService
+  // in a new thread, pointer to DarwinGamepadService is only
+  // used by this Runnable within its thread.
+  DarwinGamepadService MOZ_NON_OWNING_REF *mService;
+ public:
+  DarwinGamepadServiceStartupRunnable(DarwinGamepadService *service)
+    : mService(service) {}
+  NS_IMETHOD Run() override
+  {
+    MOZ_ASSERT(mService);
+    mService->StartupInternal();
+    return NS_OK;
+  }
 };
 
 void
 DarwinGamepadService::DeviceAdded(IOHIDDeviceRef device)
 {
   size_t slot = size_t(-1);
   for (size_t i = 0; i < mGamepads.size(); i++) {
     if (mGamepads[i] == device)
@@ -245,28 +271,35 @@ DarwinGamepadService::DeviceAdded(IOHIDD
   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());
+  GamepadPlatformService* service =
+    GamepadPlatformService::GetParentService();
+  MOZ_ASSERT(service);
+  uint32_t index = service->AddGamepad(buffer,
+                                       mozilla::dom::GamepadMappingType::_empty,
+                                       (int)mGamepads[slot].numButtons(),
+                                       (int)mGamepads[slot].numAxes());
+  mGamepads[slot].mSuperIndex = index;
 }
 
 void
 DarwinGamepadService::DeviceRemoved(IOHIDDeviceRef device)
 {
+  GamepadPlatformService* service =
+    GamepadPlatformService::GetParentService();
+  MOZ_ASSERT(service);
   for (size_t i = 0; i < mGamepads.size(); i++) {
     if (mGamepads[i] == device) {
-      RemoveGamepad(mGamepads[i].mSuperIndex);
+      service->RemoveGamepad(mGamepads[i].mSuperIndex);
       mGamepads[i].clear();
       return;
     }
   }
 }
 
 /*
  * Given a value from a d-pad (POV hat in USB HID terminology),
@@ -313,49 +346,54 @@ DarwinGamepadService::InputValueChanged(
   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);
+  GamepadPlatformService* service =
+    GamepadPlatformService::GetParentService();
+  MOZ_ASSERT(service);
   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(gamepad.mSuperIndex, numButtons - 4 + b, newState[b]);
+            service->NewButtonEvent(gamepad.mSuperIndex,
+                                    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(gamepad.mSuperIndex, axis->id, v);
+        service->NewAxisMoveEvent(gamepad.mSuperIndex, 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(gamepad.mSuperIndex, button->id, pressed, v);
+        service->NewButtonEvent(gamepad.mSuperIndex, button->id, pressed, v);
       }
       return;
     }
   }
 }
 
 void
 DarwinGamepadService::DeviceAddedCallback(void* data, IOReturn result,
@@ -417,17 +455,18 @@ MatchingDictionary(UInt32 inUsagePage, U
 DarwinGamepadService::DarwinGamepadService() : mManager(nullptr) {}
 
 DarwinGamepadService::~DarwinGamepadService()
 {
   if (mManager != nullptr)
     CFRelease(mManager);
 }
 
-void DarwinGamepadService::Startup()
+void
+DarwinGamepadService::StartupInternal()
 {
   if (mManager != nullptr)
     return;
 
   IOHIDManagerRef manager = IOHIDManagerCreate(kCFAllocatorDefault,
                                                kIOHIDOptionsTypeNone);
 
   CFMutableDictionaryRef criteria_arr[2];
@@ -474,26 +513,44 @@ void DarwinGamepadService::Startup()
                                   kCFRunLoopDefaultMode);
   IOReturn rv = IOHIDManagerOpen(manager, kIOHIDOptionsTypeNone);
   if (rv != kIOReturnSuccess) {
     CFRelease(manager);
     return;
   }
 
   mManager = manager;
+
+  // We held the handle of the CFRunLoop to make sure we
+  // can shut it down explicitly by CFRunLoopStop in another
+  // thread.
+  mMonitorRunLoop = CFRunLoopGetCurrent();
+
+  // CFRunLoopRun() is a blocking message loop when it's called in
+  // non-main thread so this thread cannot receive any other runnables
+  // and nsITimer timeout events after it's called.
+  CFRunLoopRun();
+}
+
+void DarwinGamepadService::Startup()
+{
+  Unused << NS_NewThread(getter_AddRefs(mMonitorThread),
+                         new DarwinGamepadServiceStartupRunnable(this));
 }
 
 void DarwinGamepadService::Shutdown()
 {
   IOHIDManagerRef manager = (IOHIDManagerRef)mManager;
+  CFRunLoopStop(mMonitorRunLoop);
   if (manager) {
     IOHIDManagerClose(manager, 0);
     CFRelease(manager);
     mManager = nullptr;
   }
+  mMonitorThread->Shutdown();
 }
 
 } // namespace
 
 namespace mozilla {
 namespace dom {
 
 DarwinGamepadService* gService = nullptr;
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/ipc/GamepadEventChannelChild.cpp
@@ -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/. */
+#include "GamepadEventChannelChild.h"
+#include "mozilla/dom/GamepadManager.h"
+
+namespace mozilla {
+namespace dom{
+
+namespace {
+
+class GamepadUpdateRunnable final : public Runnable
+{
+ public:
+  explicit GamepadUpdateRunnable(const GamepadChangeEvent& aGamepadEvent)
+             : mEvent(aGamepadEvent) {}
+  NS_IMETHOD Run() override
+  {
+    RefPtr<GamepadManager> svc(GamepadManager::GetService());
+    if (svc) {
+      svc->Update(mEvent);
+    }
+    return NS_OK;
+  }
+ protected:
+  GamepadChangeEvent mEvent;
+};
+
+} // namespace
+
+bool
+GamepadEventChannelChild::RecvGamepadUpdate(
+                                       const GamepadChangeEvent& aGamepadEvent)
+{
+  nsresult rv;
+  rv = NS_DispatchToMainThread(new GamepadUpdateRunnable(aGamepadEvent));
+  NS_WARN_IF(NS_FAILED(rv));
+  return true;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/ipc/GamepadEventChannelChild.h
@@ -0,0 +1,24 @@
+/* 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/PGamepadEventChannelChild.h"
+
+#ifndef mozilla_dom_GamepadEventChannelChild_h_
+#define mozilla_dom_GamepadEventChannelChild_h_
+
+namespace mozilla{
+namespace dom{
+
+class GamepadEventChannelChild final : public PGamepadEventChannelChild
+{
+ public:
+  GamepadEventChannelChild() {}
+  ~GamepadEventChannelChild() {}
+  virtual bool
+  RecvGamepadUpdate(const GamepadChangeEvent& aGamepadEvent) override;
+};
+
+}// namespace dom
+}// namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/ipc/GamepadEventChannelParent.cpp
@@ -0,0 +1,101 @@
+/* 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 "GamepadEventChannelParent.h"
+#include "GamepadPlatformService.h"
+#include "mozilla/dom/GamepadMonitoring.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+using namespace mozilla::ipc;
+
+namespace {
+
+class SendGamepadUpdateRunnable final : public Runnable
+{
+ private:
+  ~SendGamepadUpdateRunnable() {}
+  RefPtr<GamepadEventChannelParent> mParent;
+  GamepadChangeEvent mEvent;
+ public:
+  SendGamepadUpdateRunnable(GamepadEventChannelParent* aParent,
+                            GamepadChangeEvent aEvent)
+    : mEvent(aEvent)
+  {
+    MOZ_ASSERT(aParent);
+    mParent = aParent;
+  }
+  NS_IMETHOD Run() override
+  {
+    AssertIsOnBackgroundThread();
+    if(mParent->HasGamepadListener()) {
+      Unused << mParent->SendGamepadUpdate(mEvent);
+    }
+    return NS_OK;
+  }
+};
+
+} // namespace
+
+GamepadEventChannelParent::GamepadEventChannelParent()
+  : mHasGamepadListener(false)
+{
+  GamepadPlatformService* service =
+    GamepadPlatformService::GetParentService();
+  MOZ_ASSERT(service);
+  service->AddChannelParent(this);
+  mBackgroundThread = NS_GetCurrentThread();
+}
+
+bool
+GamepadEventChannelParent::RecvGamepadListenerAdded()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!mHasGamepadListener);
+  mHasGamepadListener = true;
+  StartGamepadMonitoring();
+  return true;
+}
+
+bool
+GamepadEventChannelParent::RecvGamepadListenerRemoved()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(mHasGamepadListener);
+  mHasGamepadListener = false;
+  GamepadPlatformService* service =
+    GamepadPlatformService::GetParentService();
+  MOZ_ASSERT(service);
+  service->RemoveChannelParent(this);
+  Unused << Send__delete__(this);
+  return true;
+}
+
+void
+GamepadEventChannelParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnBackgroundThread();
+
+  // It may be called because IPDL child side crashed, we'll
+  // not receive RecvGamepadListenerRemoved in that case
+  if (mHasGamepadListener) {
+    mHasGamepadListener = false;
+    GamepadPlatformService* service =
+      GamepadPlatformService::GetParentService();
+    MOZ_ASSERT(service);
+    service->RemoveChannelParent(this);
+  }
+  MaybeStopGamepadMonitoring();
+}
+
+void
+GamepadEventChannelParent::DispatchUpdateEvent(const GamepadChangeEvent& aEvent)
+{
+  mBackgroundThread->Dispatch(new SendGamepadUpdateRunnable(this, aEvent),
+                              NS_DISPATCH_NORMAL);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/ipc/GamepadEventChannelParent.h
@@ -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/PGamepadEventChannelParent.h"
+
+#ifndef mozilla_dom_GamepadEventChannelParent_h_
+#define mozilla_dom_GamepadEventChannelParent_h_
+
+namespace mozilla{
+namespace dom{
+
+class GamepadEventChannelParent final : public PGamepadEventChannelParent
+{
+ public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GamepadEventChannelParent)
+  GamepadEventChannelParent();
+  virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+  virtual bool RecvGamepadListenerAdded() override;
+  virtual bool RecvGamepadListenerRemoved() override;
+  void DispatchUpdateEvent(const GamepadChangeEvent& aEvent);
+  bool HasGamepadListener() const { return mHasGamepadListener; }
+ private:
+  ~GamepadEventChannelParent() {}
+  bool mHasGamepadListener;
+  nsCOMPtr<nsIThread> mBackgroundThread;
+};
+
+}// namespace dom
+}// namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/dom/gamepad/ipc/PGamepadEventChannel.ipdl
@@ -0,0 +1,52 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+include protocol PBackground;
+
+namespace mozilla {
+namespace dom {
+
+struct GamepadAdded {
+  nsString id;
+  uint32_t index;
+  uint32_t mapping;
+  uint32_t num_buttons;
+  uint32_t num_axes;
+};
+
+struct GamepadRemoved {
+  uint32_t index;
+};
+
+struct GamepadAxisInformation {
+  uint32_t index;
+  uint32_t axis;
+  double value;
+};
+
+struct GamepadButtonInformation {
+  uint32_t index;
+  uint32_t button;
+  bool pressed;
+  double value;
+};
+
+union GamepadChangeEvent {
+  GamepadAdded;
+  GamepadRemoved;
+  GamepadAxisInformation;
+  GamepadButtonInformation;
+};
+
+async protocol PGamepadEventChannel {
+  manager PBackground;
+  parent:
+    async GamepadListenerAdded();
+    async GamepadListenerRemoved();
+  child:
+    async __delete__();
+    async GamepadUpdate(GamepadChangeEvent aGamepadEvent);
+};
+
+}
+}
--- a/dom/gamepad/linux/LinuxGamepad.cpp
+++ b/dom/gamepad/linux/LinuxGamepad.cpp
@@ -14,22 +14,22 @@
 
 #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 "mozilla/dom/GamepadPlatformService.h"
 #include "udev.h"
 
 namespace {
 
-using namespace mozilla::dom::GamepadFunctions;
+using namespace mozilla::dom;
 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";
@@ -129,25 +129,28 @@ LinuxGamepadService::AddDevice(struct ud
   }
   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;
+  GamepadPlatformService* service =
+    GamepadPlatformService::GetParentService();
+  MOZ_ASSERT(service);
   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.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);
 
@@ -156,21 +159,23 @@ LinuxGamepadService::AddDevice(struct ud
 
 void
 LinuxGamepadService::RemoveDevice(struct udev_device* dev)
 {
   const char* devpath = mUdev.udev_device_get_devnode(dev);
   if (!devpath) {
     return;
   }
-
+  GamepadPlatformService* service =
+    GamepadPlatformService::GetParentService();
+  MOZ_ASSERT(service);
   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);
+      service->RemoveGamepad(mGamepads[i].index);
       mGamepads.RemoveElementAt(i);
       break;
     }
   }
 }
 
 void
 LinuxGamepadService::ScanForDevices()
@@ -291,16 +296,19 @@ LinuxGamepadService::ReadUdevChange()
 
 // static
 gboolean
 LinuxGamepadService::OnGamepadData(GIOChannel* source,
                                    GIOCondition condition,
                                    gpointer data)
 {
   int index = GPOINTER_TO_INT(data);
+  GamepadPlatformService* service =
+    GamepadPlatformService::GetParentService();
+  MOZ_ASSERT(service);
   //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;
@@ -315,21 +323,21 @@ LinuxGamepadService::OnGamepadData(GIOCh
 
     //TODO: store device state?
     if (event.type & JS_EVENT_INIT) {
       continue;
     }
 
     switch (event.type) {
     case JS_EVENT_BUTTON:
-      NewButtonEvent(index, event.number, !!event.value);
+      service->NewButtonEvent(index, event.number, !!event.value);
       break;
     case JS_EVENT_AXIS:
-      NewAxisMoveEvent(index, event.number,
-                       ((float)event.value) / kMaxAxisValue);
+      service->NewAxisMoveEvent(index, event.number,
+                                ((float)event.value) / kMaxAxisValue);
       break;
     }
   }
 
   return TRUE;
 }
 
 // static
--- a/dom/gamepad/moz.build
+++ b/dom/gamepad/moz.build
@@ -2,29 +2,33 @@
 # 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',
+    'GamepadManager.h',
     'GamepadMonitoring.h',
-    'GamepadService.h',
-    'GamepadServiceTest.h'
+    'GamepadPlatformService.h',
+    'GamepadServiceTest.h',
+    'ipc/GamepadEventChannelChild.h',
+    'ipc/GamepadEventChannelParent.h'
     ]
 
 UNIFIED_SOURCES = [
     'Gamepad.cpp',
     'GamepadButton.cpp',
-    'GamepadFunctions.cpp',
+    'GamepadManager.cpp',
     'GamepadMonitoring.cpp',
-    'GamepadService.cpp',
-    'GamepadServiceTest.cpp'
+    'GamepadPlatformService.cpp',
+    'GamepadServiceTest.cpp',
+    'ipc/GamepadEventChannelChild.cpp',
+    'ipc/GamepadEventChannelParent.cpp'
     ]
 
 if CONFIG['MOZ_GAMEPAD_BACKEND'] == 'stub':
     UNIFIED_SOURCES += [
         'fallback/FallbackGamepad.cpp'
     ]
 elif CONFIG['MOZ_GAMEPAD_BACKEND'] == 'cocoa':
     UNIFIED_SOURCES += [
@@ -38,16 +42,24 @@ elif CONFIG['MOZ_GAMEPAD_BACKEND'] == 'l
     UNIFIED_SOURCES += [
         'linux/LinuxGamepad.cpp'
     ]
 elif CONFIG['MOZ_GAMEPAD_BACKEND'] == 'android':
     UNIFIED_SOURCES += [
         'android/AndroidGamepad.cpp'
     ]
 
+LOCAL_INCLUDES += [
+    'ipc',
+]
+
+IPDL_SOURCES += [
+    'ipc/PGamepadEventChannel.ipdl'
+]
+
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '/dom/base',
 ]
 
 CFLAGS += CONFIG['GLIB_CFLAGS']
--- a/dom/gamepad/windows/WindowsGamepad.cpp
+++ b/dom/gamepad/windows/WindowsGamepad.cpp
@@ -14,25 +14,26 @@
 #include <hidsdi.h>
 #include <stdio.h>
 #include <xinput.h>
 
 #include "nsIComponentManager.h"
 #include "nsITimer.h"
 #include "nsTArray.h"
 #include "nsThreadUtils.h"
+
 #include "mozilla/ArrayUtils.h"
+#include "mozilla/Services.h"
+
 #include "mozilla/ipc/BackgroundParent.h"
-#include "mozilla/dom/GamepadFunctions.h"
-#include "mozilla/Services.h"
+#include "mozilla/dom/GamepadPlatformService.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
@@ -436,27 +437,30 @@ WindowsGamepadService::ScanForXInputDevi
     }
     found = true;
     // See if this device is already present in our list.
     if (HaveXInputGamepad(i)) {
       continue;
     }
 
     // Not already present, add it.
+    GamepadPlatformService* service =
+      GamepadPlatformService::GetParentService();
+    MOZ_ASSERT(service);
     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);
+    gamepad.id = service->AddGamepad("xinput",
+                                     GamepadMappingType::Standard,
+                                     kStandardGamepadButtons,
+                                     kStandardGamepadAxes);
     mGamepads.AppendElement(gamepad);
   }
 
   return found;
 }
 
 void
 WindowsGamepadService::ScanForDevices()
@@ -476,19 +480,22 @@ WindowsGamepadService::ScanForDevices()
                                          kXInputPollInterval,
                                          nsITimer::TYPE_ONE_SHOT);
     } else {
       mIsXInputMonitoring = false;
     }
   }
 
   // Look for devices that are no longer present and remove them.
+  GamepadPlatformService* service =
+    GamepadPlatformService::GetParentService();
+  MOZ_ASSERT(service);
   for (int i = mGamepads.Length() - 1; i >= 0; i--) {
     if (!mGamepads[i].present) {
-      RemoveGamepad(mGamepads[i].id);
+      service->RemoveGamepad(mGamepads[i].id);
       mGamepads.RemoveElementAt(i);
     }
   }
 }
 
 void
 WindowsGamepadService::PollXInput()
 {
@@ -503,60 +510,63 @@ WindowsGamepadService::PollXInput()
         && state.dwPacketNumber != mGamepads[i].state.dwPacketNumber) {
         CheckXInputChanges(mGamepads[i], state);
     }
   }
 }
 
 void WindowsGamepadService::CheckXInputChanges(Gamepad& gamepad,
                                                XINPUT_STATE& state) {
+  GamepadPlatformService* service =
+    GamepadPlatformService::GetParentService();
+  MOZ_ASSERT(service);
   // 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);
+      service->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);
+      service->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);
+    service->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);
+    service->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);
+    service->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);
+    service->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);
+    service->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);
+    service->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
@@ -707,20 +717,23 @@ WindowsGamepadService::GetRawGamepad(HAN
       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);
+  GamepadPlatformService* service =
+    GamepadPlatformService::GetParentService();
+  MOZ_ASSERT(service);
+  gamepad.id = service->AddGamepad(gamepad_id,
+                                   GamepadMappingType::_empty,
+                                   gamepad.numButtons,
+                                   gamepad.numAxes);
   mGamepads.AppendElement(gamepad);
   return true;
 }
 
 bool
 WindowsGamepadService::HandleRawInput(HRAWINPUT handle)
 {
   if (!mHID) {
@@ -754,16 +767,19 @@ WindowsGamepadService::HandleRawInput(HR
   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.
+  GamepadPlatformService* service =
+    GamepadPlatformService::GetParentService();
+  MOZ_ASSERT(service);
   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;
   }
@@ -779,17 +795,17 @@ WindowsGamepadService::HandleRawInput(HR
     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]);
+      service->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) {
@@ -812,17 +828,17 @@ LONG value;
                              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);
+      service->NewAxisMoveEvent(gamepad->id, i, new_value);
       gamepad->axes[i].value = new_value;
     }
   }
 
   return true;
 }
 
 void
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -166,20 +166,16 @@
 #include "nsIAccessibilityService.h"
 #endif
 
 #ifdef MOZ_NUWA_PROCESS
 #include "ipc/Nuwa.h"
 #endif
 #include "NuwaChild.h"
 
-#ifdef MOZ_GAMEPAD
-#include "mozilla/dom/GamepadService.h"
-#endif
-
 #ifndef MOZ_SIMPLEPUSH
 #include "nsIPushNotifier.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"
@@ -3145,28 +3141,16 @@ ContentChild::RecvPWebBrowserPersistDocu
 bool
 ContentChild::DeallocPWebBrowserPersistDocumentChild(PWebBrowserPersistDocumentChild* aActor)
 {
   delete aActor;
   return true;
 }
 
 bool
-ContentChild::RecvGamepadUpdate(const GamepadChangeEvent& aGamepadEvent)
-{
-#ifdef MOZ_GAMEPAD
-  RefPtr<GamepadService> svc(GamepadService::GetService());
-  if (svc) {
-    svc->Update(aGamepadEvent);
-  }
-#endif
-  return true;
-}
-
-bool
 ContentChild::RecvSetAudioSessionData(const nsID& aId,
                                       const nsString& aDisplayName,
                                       const nsString& aIconPath)
 {
 #if defined(XP_WIN)
     if (NS_FAILED(mozilla::widget::RecvAudioSessionData(aId, aDisplayName,
                                                         aIconPath))) {
       return true;
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -597,18 +597,16 @@ 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;
-
   // Windows specific - set up audio session
   virtual bool
   RecvSetAudioSessionData(const nsID& aId,
                           const nsString& aDisplayName,
                           const nsString& aIconPath) override;
 
 private:
   virtual void ActorDestroy(ActorDestroyReason why) override;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -257,20 +257,16 @@ 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
-
 #ifndef MOZ_SIMPLEPUSH
 #include "nsIPushNotifier.h"
 #endif
 
 #ifdef XP_WIN
 #include "mozilla/widget/AudioSession.h"
 #endif
 
@@ -2316,17 +2312,16 @@ ContentParent::InitializeMembers()
   mSendPermissionUpdates = false;
   mCalledClose = false;
   mCalledCloseWithError = false;
   mCalledKillHard = false;
   mCreatedPairedMinidumps = false;
   mShutdownPending = false;
   mIPCOpen = true;
   mHangMonitorActor = nullptr;
-  mHasGamepadListener = false;
 }
 
 bool
 ContentParent::LaunchSubprocess(ProcessPriority aInitialPriority /* = PROCESS_PRIORITY_FOREGROUND */)
 {
   PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
 
   std::vector<std::string> extraArgs;
@@ -5644,44 +5639,16 @@ ContentParent::GetBrowserConfiguration(c
     swr->GetRegistrations(aConfig.serviceWorkerRegistrations());
     return true;
   }
 
   return ContentChild::GetSingleton()->SendGetBrowserConfiguration(aURI, &aConfig);
 }
 
 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;
-}
-
-bool
 ContentParent::RecvProfile(const nsCString& aProfile)
 {
 #ifdef MOZ_ENABLE_PROFILER_SPS
   if (NS_WARN_IF(!mGatherer)) {
     return true;
   }
   mProfile = aProfile;
   mGatherer->GatheredOOPProfile();
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -486,18 +486,16 @@ public:
                                        const IPC::Principal& aPrincipal,
                                        const TabId& aTabId) override;
 
   virtual bool
   DeallocPContentPermissionRequestParent(PContentPermissionRequestParent* actor) override;
 
   virtual bool HandleWindowsMessages(const Message& aMsg) const override;
 
-  bool HasGamepadListener() const { return mHasGamepadListener; }
-
   void SetNuwaParent(NuwaParent* aNuwaParent) { mNuwaParent = aNuwaParent; }
 
   void ForkNewProcess(bool aBlocking);
 
   virtual bool RecvCreateWindow(PBrowserParent* aThisTabParent,
                                 PBrowserParent* aOpener,
                                 layout::PRenderFrameParent* aRenderFrame,
                                 const uint32_t& aChromeFlags,
@@ -1076,20 +1074,16 @@ private:
 
 
   virtual bool RecvUpdateDropEffect(const uint32_t& aDragAction,
                                     const uint32_t& aDropEffect) override;
 
   virtual bool RecvGetBrowserConfiguration(const nsCString& aURI,
                                            BrowserConfiguration* aConfig) override;
 
-  virtual bool RecvGamepadListenerAdded() override;
-
-  virtual bool RecvGamepadListenerRemoved() override;
-
   virtual bool RecvProfile(const nsCString& aProfile) override;
 
   virtual bool RecvGetGraphicsDeviceInitData(DeviceInitData* aOut) override;
 
   void StartProfiler(nsIProfilerStartParams* aParams);
 
   virtual bool RecvGetDeviceStorageLocation(const nsString& aType,
                                             nsString* aPath) override;
@@ -1154,17 +1148,16 @@ private:
 
   // True only the if process is already a browser or app or has
   // been transformed into one.
   bool mMetamorphosed;
 
   bool mSendPermissionUpdates;
   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
@@ -332,47 +332,17 @@ 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;
-};
 
 struct FrameScriptInfo
 {
     nsString url;
     bool runInGlobalScope;
 };
 
 struct AndroidSystemInfo
@@ -627,21 +597,16 @@ child:
 
     /**
      * 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.
-     */
-    async GamepadUpdate(GamepadChangeEvent aGamepadEvent);
-
-    /**
      * Notify the child that presentation receiver has been launched with the
      * correspondent iframe.
      */
     async NotifyPresentationReceiverLaunched(PBrowser aIframe, nsString aSessionId);
 
     /**
      * Notify the child that the info about a presentation receiver needs to be
      * cleaned up.
@@ -1113,26 +1078,16 @@ parent:
                                     TabId tabId);
 
     /**
      * Send ServiceWorkerRegistrationData to child process.
      */
     sync GetBrowserConfiguration(nsCString aUri)
         returns (BrowserConfiguration aConfig);
 
-    /*
-     * Tells the parent to start the gamepad listening service if it hasn't already.
-     */
-    async GamepadListenerAdded();
-
-    /**
-     * Tells the parent to stop the gamepad listening service if it hasn't already.
-     */
-    async GamepadListenerRemoved();
-
     async Profile(nsCString aProfile);
 
     /**
      * Request graphics initialization information from the parent.
      */
     sync GetGraphicsDeviceInitData()
         returns (DeviceInitData aData);
 
--- a/ipc/glue/BackgroundChildImpl.cpp
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -19,16 +19,17 @@
 #include "mozilla/dom/PFileSystemRequestChild.h"
 #include "mozilla/dom/FileSystemTaskBase.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsChild.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/quota/PQuotaChild.h"
+#include "mozilla/dom/GamepadEventChannelChild.h"
 #include "mozilla/dom/MessagePortChild.h"
 #include "mozilla/dom/NuwaChild.h"
 #include "mozilla/ipc/PBackgroundTestChild.h"
 #include "mozilla/ipc/PSendStreamChild.h"
 #include "mozilla/layout/VsyncChild.h"
 #include "mozilla/net/PUDPSocketChild.h"
 #include "mozilla/dom/network/UDPSocketChild.h"
 #include "nsID.h"
@@ -479,16 +480,32 @@ BackgroundChildImpl::DeallocPFileSystemR
 {
   // The reference is increased in FileSystemTaskBase::Start of
   // FileSystemTaskBase.cpp. We should decrease it after IPC.
   RefPtr<dom::FileSystemTaskChildBase> child =
     dont_AddRef(static_cast<dom::FileSystemTaskChildBase*>(aActor));
   return true;
 }
 
+// Gamepad API Background IPC
+dom::PGamepadEventChannelChild*
+BackgroundChildImpl::AllocPGamepadEventChannelChild()
+{
+  MOZ_CRASH("PGamepadEventChannelChild actor should be manually constructed!");
+  return nullptr;
+}
+
+bool
+BackgroundChildImpl::DeallocPGamepadEventChannelChild(PGamepadEventChannelChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+  delete static_cast<dom::GamepadEventChannelChild*>(aActor);
+  return true;
+}
+
 } // namespace ipc
 } // namespace mozilla
 
 bool
 TestChild::Recv__delete__(const nsCString& aTestArg)
 {
   MOZ_RELEASE_ASSERT(aTestArg == mTestArg,
                      "BackgroundTest message was corrupted!");
--- a/ipc/glue/BackgroundChildImpl.h
+++ b/ipc/glue/BackgroundChildImpl.h
@@ -168,16 +168,22 @@ protected:
   DeallocPQuotaChild(PQuotaChild* aActor) override;
 
   virtual PFileSystemRequestChild*
   AllocPFileSystemRequestChild(const FileSystemParams&) override;
 
   virtual bool
   DeallocPFileSystemRequestChild(PFileSystemRequestChild*) override;
 
+  // Gamepad API Background IPC
+  virtual PGamepadEventChannelChild*
+  AllocPGamepadEventChannelChild() override;
+
+  virtual bool
+  DeallocPGamepadEventChannelChild(PGamepadEventChannelChild* aActor) override;
 };
 
 class BackgroundChildImpl::ThreadLocal final
 {
   friend class nsAutoPtr<ThreadLocal>;
 
 public:
   nsAutoPtr<mozilla::dom::indexedDB::ThreadLocal> mIndexedDBThreadLocal;
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -13,29 +13,31 @@
 #endif
 #include "mozilla/media/MediaParent.h"
 #include "mozilla/AppProcessChecker.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/DOMTypes.h"
 #include "mozilla/dom/FileSystemBase.h"
 #include "mozilla/dom/FileSystemRequestParent.h"
+#include "mozilla/dom/GamepadEventChannelParent.h"
 #include "mozilla/dom/NuwaParent.h"
 #include "mozilla/dom/PBlobParent.h"
 #include "mozilla/dom/MessagePortParent.h"
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/dom/quota/ActorsParent.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/ipc/PBackgroundTestParent.h"
+#include "mozilla/dom/PGamepadEventChannelParent.h"
 #include "mozilla/ipc/PSendStreamParent.h"
 #include "mozilla/ipc/SendStreamAlloc.h"
 #include "mozilla/layout/VsyncParent.h"
 #include "mozilla/dom/network/UDPSocketParent.h"
 #include "mozilla/Preferences.h"
 #include "nsIAppsService.h"
 #include "nsNetUtil.h"
 #include "nsIScriptSecurityManager.h"
@@ -917,16 +919,34 @@ BackgroundParentImpl::DeallocPFileSystem
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
 
   RefPtr<FileSystemRequestParent> parent =
     dont_AddRef(static_cast<FileSystemRequestParent*>(aDoomed));
   return true;
 }
 
+// Gamepad API Background IPC
+dom::PGamepadEventChannelParent*
+BackgroundParentImpl::AllocPGamepadEventChannelParent()
+{
+  RefPtr<dom::GamepadEventChannelParent> parent =
+    new dom::GamepadEventChannelParent();
+
+  return parent.forget().take();
+}
+
+bool
+BackgroundParentImpl::DeallocPGamepadEventChannelParent(dom::PGamepadEventChannelParent *aActor)
+{
+  MOZ_ASSERT(aActor);
+  RefPtr<dom::GamepadEventChannelParent> parent =
+    dont_AddRef(static_cast<dom::GamepadEventChannelParent*>(aActor));
+  return true;
+}
 } // namespace ipc
 } // namespace mozilla
 
 void
 TestParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   mozilla::ipc::AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -200,14 +200,20 @@ protected:
 
   virtual bool
   RecvPFileSystemRequestConstructor(PFileSystemRequestParent* aActor,
                                     const FileSystemParams& aParams) override;
 
   virtual bool
   DeallocPFileSystemRequestParent(PFileSystemRequestParent*) override;
 
+  // Gamepad API Background IPC
+  virtual PGamepadEventChannelParent*
+  AllocPGamepadEventChannelParent() override;
+
+  virtual bool
+  DeallocPGamepadEventChannelParent(PGamepadEventChannelParent *aActor) override;
 };
 
 } // namespace ipc
 } // namespace mozilla
 
 #endif // mozilla_ipc_backgroundparentimpl_h__
--- a/ipc/glue/PBackground.ipdl
+++ b/ipc/glue/PBackground.ipdl
@@ -8,16 +8,17 @@ include protocol PBackgroundIndexedDBUti
 include protocol PBackgroundTest;
 include protocol PBlob;
 include protocol PBroadcastChannel;
 include protocol PCache;
 include protocol PCacheStorage;
 include protocol PCacheStreamControl;
 include protocol PFileDescriptorSet;
 include protocol PFileSystemRequest;
+include protocol PGamepadEventChannel;
 include protocol PMessagePort;
 include protocol PCameras;
 include protocol PNuwa;
 include protocol PQuota;
 include protocol PSendStream;
 include protocol PServiceWorkerManager;
 include protocol PUDPSocket;
 include protocol PVsync;
@@ -49,16 +50,17 @@ sync protocol PBackground
   manages PBackgroundTest;
   manages PBlob;
   manages PBroadcastChannel;
   manages PCache;
   manages PCacheStorage;
   manages PCacheStreamControl;
   manages PFileDescriptorSet;
   manages PFileSystemRequest;
+  manages PGamepadEventChannel;
   manages PMessagePort;
   manages PCameras;
   manages PNuwa;
   manages PQuota;
   manages PSendStream;
   manages PServiceWorkerManager;
   manages PUDPSocket;
   manages PVsync;
@@ -99,16 +101,18 @@ parent:
   async PAsmJSCacheEntry(OpenMode openMode,
                          WriteParams write,
                          PrincipalInfo principalInfo);
 
   async PQuota();
 
   async PFileSystemRequest(FileSystemParams params);
 
+  async PGamepadEventChannel();
+
 child:
   async PCache();
   async PCacheStreamControl();
 
 both:
   async PBlob(BlobConstructorParams params);
 
   async PFileDescriptorSet(FileDescriptor fd);