Bug 870676: Part 1 - Implement mozFMRadio in WebIDL. r=khuey,baku
authorKyle Huey <khuey@kylehuey.com>
Wed, 28 Aug 2013 09:44:33 -0700
changeset 144753 05529a089e45d12e6be364564e14b1f7783cb158
parent 144752 c1017a8b9d5dd33d83e311aff4c2255b164bba51
child 144754 8ed8e41fb68c85c8c6340ee398655aae49aa4fd7
push id25175
push userryanvm@gmail.com
push dateThu, 29 Aug 2013 02:48:09 +0000
treeherdermozilla-central@8451e35a8a50 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey, baku
bugs870676
milestone26.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 870676: Part 1 - Implement mozFMRadio in WebIDL. r=khuey,baku
b2g/chrome/content/shell.js
b2g/installer/package-manifest.in
content/base/src/nsGkAtomList.h
dom/base/Navigator.cpp
dom/base/Navigator.h
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfoClasses.h
dom/dom-config.mk
dom/fm/DOMFMRadio.manifest
dom/fm/DOMFMRadioChild.js
dom/fm/DOMFMRadioParent.jsm
dom/fm/FMRadio.cpp
dom/fm/FMRadio.h
dom/fm/Makefile.in
dom/fm/moz.build
dom/fm/nsFMRadioSettings.cpp
dom/fm/nsFMRadioSettings.h
dom/fm/nsIDOMFMRadio.idl
dom/fm/nsIFMRadio.idl
dom/fmradio/FMRadio.cpp
dom/fmradio/FMRadio.h
dom/fmradio/FMRadioCommon.h
dom/fmradio/FMRadioService.cpp
dom/fmradio/FMRadioService.h
dom/fmradio/Makefile.in
dom/fmradio/moz.build
dom/moz.build
dom/permission/tests/test_fmradio.html
dom/webidl/FMRadio.webidl
dom/webidl/Navigator.webidl
dom/webidl/WebIDL.mk
layout/build/Makefile.in
layout/build/nsLayoutModule.cpp
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -1,19 +1,16 @@
 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
 /* 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/. */
 
 Cu.import('resource://gre/modules/ContactService.jsm');
 Cu.import('resource://gre/modules/SettingsChangeNotifier.jsm');
-#ifdef MOZ_B2G_FM
-Cu.import('resource://gre/modules/DOMFMRadioParent.jsm');
-#endif
 Cu.import('resource://gre/modules/AlarmService.jsm');
 Cu.import('resource://gre/modules/ActivitiesService.jsm');
 Cu.import('resource://gre/modules/PermissionPromptHelper.jsm');
 Cu.import('resource://gre/modules/ObjectWrapper.jsm');
 Cu.import('resource://gre/modules/accessibility/AccessFu.jsm');
 Cu.import('resource://gre/modules/Payment.jsm');
 Cu.import("resource://gre/modules/AppsUtils.jsm");
 Cu.import('resource://gre/modules/UserAgentOverrides.jsm');
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -167,19 +167,16 @@
 @BINPATH@/components/dom_telephony.xpt
 @BINPATH@/components/dom_voicemail.xpt
 @BINPATH@/components/dom_wifi.xpt
 @BINPATH@/components/dom_system_gonk.xpt
 @BINPATH@/components/dom_icc.xpt
 @BINPATH@/components/dom_cellbroadcast.xpt
 @BINPATH@/components/dom_wappush.xpt
 #endif
-#ifdef MOZ_B2G_FM
-@BINPATH@/components/dom_fm.xpt
-#endif
 #ifdef MOZ_B2G_BT
 @BINPATH@/components/dom_bluetooth.xpt
 #endif
 @BINPATH@/components/dom_camera.xpt
 @BINPATH@/components/dom_canvas.xpt
 @BINPATH@/components/dom_contacts.xpt
 @BINPATH@/components/dom_alarm.xpt
 @BINPATH@/components/dom_core.xpt
@@ -469,20 +466,16 @@
 @BINPATH@/components/WifiWorker.manifest
 @BINPATH@/components/DOMWifiManager.js
 @BINPATH@/components/DOMWifiManager.manifest
 @BINPATH@/components/NetworkStatsManager.js
 @BINPATH@/components/NetworkStatsManager.manifest
 @BINPATH@/components/NetworkInterfaceListService.manifest
 @BINPATH@/components/NetworkInterfaceListService.js
 #endif
-#ifdef MOZ_B2G_FM
-@BINPATH@/components/DOMFMRadioChild.js
-@BINPATH@/components/DOMFMRadio.manifest
-#endif
 #ifdef MOZ_ENABLE_DBUS
 @BINPATH@/components/@DLL_PREFIX@dbusservice@DLL_SUFFIX@
 #endif
 @BINPATH@/components/nsINIProcessor.manifest
 @BINPATH@/components/nsINIProcessor.js
 @BINPATH@/components/nsPrompter.manifest
 @BINPATH@/components/nsPrompter.js
 #ifdef MOZ_SERVICES_SYNC
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -633,16 +633,17 @@ GK_ATOM(ona2dpstatuschanged, "ona2dpstat
 GK_ATOM(onabort, "onabort")
 GK_ATOM(onadapteradded, "onadapteradded")
 GK_ATOM(onafterprint, "onafterprint")
 GK_ATOM(onafterscriptexecute, "onafterscriptexecute")
 GK_ATOM(onalerting, "onalerting")
 GK_ATOM(onanimationend, "onanimationend")
 GK_ATOM(onanimationiteration, "onanimationiteration")
 GK_ATOM(onanimationstart, "onanimationstart")
+GK_ATOM(onantennaavailablechange, "onantennaavailablechange")
 GK_ATOM(onAppCommand, "onAppCommand")
 GK_ATOM(onaudioprocess, "onaudioprocess")
 GK_ATOM(onbeforecopy, "onbeforecopy")
 GK_ATOM(onbeforecut, "onbeforecut")
 GK_ATOM(onbeforepaste, "onbeforepaste")
 GK_ATOM(onbeforeprint, "onbeforeprint")
 GK_ATOM(onbeforescriptexecute, "onbeforescriptexecute")
 GK_ATOM(onbeforeunload, "onbeforeunload")
@@ -704,16 +705,17 @@ GK_ATOM(ondragleave, "ondragleave")
 GK_ATOM(ondragover, "ondragover")
 GK_ATOM(ondragstart, "ondragstart")
 GK_ATOM(ondrop, "ondrop")
 GK_ATOM(onenabled, "onenabled")
 GK_ATOM(onemergencycbmodechange, "onemergencycbmodechange")
 GK_ATOM(onerror, "onerror")
 GK_ATOM(onfailed, "onfailed")
 GK_ATOM(onfocus, "onfocus")
+GK_ATOM(onfrequencychange, "onfrequencychange")
 GK_ATOM(onget, "onget")
 GK_ATOM(ongroupchange, "ongroupchange")
 GK_ATOM(onhashchange, "onhashchange")
 GK_ATOM(onheadphoneschange, "onheadphoneschange")
 GK_ATOM(onheld, "onheld")
 GK_ATOM(onhfpstatuschanged, "onhfpstatuschanged")
 GK_ATOM(onholding, "onholding")
 GK_ATOM(oniccinfochange, "oniccinfochange")
@@ -780,18 +782,16 @@ GK_ATOM(onscroll, "onscroll")
 GK_ATOM(onselect, "onselect")
 GK_ATOM(onsending, "onsending")
 GK_ATOM(onsent, "onsent")
 GK_ATOM(onset, "onset")
 GK_ATOM(onshow, "onshow")
 GK_ATOM(onstatechange, "onstatechange")
 GK_ATOM(onstatuschanged, "onstatuschanged")
 GK_ATOM(onstkcommand, "onstkcommand")
-GK_ATOM(onantennastatechange, "onantennastatechange")
-GK_ATOM(onseekcomplete, "onseekcomplete")
 GK_ATOM(onstksessionend, "onstksessionend")
 GK_ATOM(onsubmit, "onsubmit")
 GK_ATOM(onsuccess, "onsuccess")
 GK_ATOM(ontext, "ontext")
 GK_ATOM(ontouchstart, "ontouchstart")
 GK_ATOM(ontouchend, "ontouchend")
 GK_ATOM(ontouchmove, "ontouchmove")
 GK_ATOM(ontouchenter, "ontouchenter")
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -60,16 +60,20 @@
 #include "BluetoothManager.h"
 #endif
 #include "DOMCameraManager.h"
 
 #ifdef MOZ_AUDIO_CHANNEL_MANAGER
 #include "AudioChannelManager.h"
 #endif
 
+#ifdef MOZ_B2G_FM
+#include "mozilla/dom/FMRadio.h"
+#endif
+
 #include "nsIDOMGlobalPropertyInitializer.h"
 #include "nsJSUtils.h"
 
 #include "nsScriptNameSpaceManager.h"
 
 #include "mozilla/dom/NavigatorBinding.h"
 
 using namespace mozilla::dom::power;
@@ -190,16 +194,23 @@ Navigator::Invalidate()
     mNotification = nullptr;
   }
 
   if (mBatteryManager) {
     mBatteryManager->Shutdown();
     mBatteryManager = nullptr;
   }
 
+#ifdef MOZ_B2G_FM
+  if (mFMRadio) {
+    mFMRadio->Shutdown();
+    mFMRadio = nullptr;
+  }
+#endif
+
   if (mPowerManager) {
     mPowerManager->Shutdown();
     mPowerManager = nullptr;
   }
 
   if (mMobileMessageManager) {
     mMobileMessageManager->Shutdown();
     mMobileMessageManager = nullptr;
@@ -1042,16 +1053,44 @@ Navigator::GetMozNotification(ErrorResul
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   mNotification = new DesktopNotificationCenter(mWindow);
   return mNotification;
 }
 
+#ifdef MOZ_B2G_FM
+
+using mozilla::dom::FMRadio;
+
+FMRadio*
+Navigator::GetMozFMRadio(ErrorResult& aRv)
+{
+  if (!mFMRadio) {
+    if (!mWindow) {
+      aRv.Throw(NS_ERROR_UNEXPECTED);
+      return nullptr;
+    }
+
+    NS_ENSURE_TRUE(mWindow->GetDocShell(), nullptr);
+
+    mFMRadio = new FMRadio();
+    mFMRadio->Init(mWindow);
+  }
+
+  return mFMRadio;
+}
+
+#endif  // MOZ_B2G_FM
+
+//*****************************************************************************
+//    Navigator::nsINavigatorBattery
+//*****************************************************************************
+
 battery::BatteryManager*
 Navigator::GetBattery(ErrorResult& aRv)
 {
   if (!mBatteryManager) {
     if (!mWindow) {
       aRv.Throw(NS_ERROR_UNEXPECTED);
       return nullptr;
     }
@@ -1708,16 +1747,26 @@ Navigator::HasIccManagerSupport(JSContex
 bool
 Navigator::HasBluetoothSupport(JSContext* /* unused */, JSObject* aGlobal)
 {
   nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
   return win && bluetooth::BluetoothManager::CheckPermission(win);
 }
 #endif // MOZ_B2G_BT
 
+#ifdef MOZ_B2G_FM
+/* static */
+bool
+Navigator::HasFMRadioSupport(JSContext* /* unused */, JSObject* aGlobal)
+{
+  nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
+  return win && CheckPermission(win, "fmradio");
+}
+#endif // MOZ_B2G_FM
+
 #ifdef MOZ_TIME_MANAGER
 /* static */
 bool
 Navigator::HasTimeSupport(JSContext* /* unused */, JSObject* aGlobal)
 {
   nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
   return win && CheckPermission(win, "time");
 }
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -47,16 +47,20 @@ void NS_GetNavigatorAppName(nsAString& a
 
 namespace mozilla {
 namespace dom {
 
 namespace battery {
 class BatteryManager;
 } // namespace battery
 
+#ifdef MOZ_B2G_FM
+class FMRadio;
+#endif
+
 class DesktopNotificationCenter;
 class MobileMessageManager;
 class MozIdleObserver;
 #ifdef MOZ_GAMEPAD
 class Gamepad;
 #endif // MOZ_GAMEPAD
 #ifdef MOZ_MEDIA_NAVIGATOR
 class MozDOMGetUserMediaSuccessCallback;
@@ -222,16 +226,19 @@ public:
   nsIDOMMozMobileConnection* GetMozMobileConnection(ErrorResult& aRv);
   CellBroadcast* GetMozCellBroadcast(ErrorResult& aRv);
   nsIDOMMozVoicemail* GetMozVoicemail(ErrorResult& aRv);
   nsIDOMMozIccManager* GetMozIccManager(ErrorResult& aRv);
 #endif // MOZ_B2G_RIL
 #ifdef MOZ_GAMEPAD
   void GetGamepads(nsTArray<nsRefPtr<Gamepad> >& aGamepads, ErrorResult& aRv);
 #endif // MOZ_GAMEPAD
+#ifdef MOZ_B2G_FM
+  FMRadio* GetMozFMRadio(ErrorResult& aRv);
+#endif
 #ifdef MOZ_B2G_BT
   bluetooth::BluetoothManager* GetMozBluetooth(ErrorResult& aRv);
 #endif // MOZ_B2G_BT
 #ifdef MOZ_TIME_MANAGER
   time::TimeManager* GetMozTime(ErrorResult& aRv);
 #endif // MOZ_TIME_MANAGER
 #ifdef MOZ_AUDIO_CHANNEL_MANAGER
   system::AudioChannelManager* GetMozAudioChannelManager(ErrorResult& aRv);
@@ -275,16 +282,19 @@ public:
   static bool HasVoicemailSupport(JSContext* /* unused */,
                                   JSObject* aGlobal);
   static bool HasIccManagerSupport(JSContext* /* unused */,
                                    JSObject* aGlobal);
 #endif // MOZ_B2G_RIL
 #ifdef MOZ_B2G_BT
   static bool HasBluetoothSupport(JSContext* /* unused */, JSObject* aGlobal);
 #endif // MOZ_B2G_BT
+#ifdef MOZ_B2G_FM
+  static bool HasFMRadioSupport(JSContext* /* unused */, JSObject* aGlobal);
+#endif // MOZ_B2G_FM
 #ifdef MOZ_TIME_MANAGER
   static bool HasTimeSupport(JSContext* /* unused */, JSObject* aGlobal);
 #endif // MOZ_TIME_MANAGER
 #ifdef MOZ_MEDIA_NAVIGATOR
   static bool HasUserMediaSupport(JSContext* /* unused */,
                                   JSObject* /* unused */);
 #endif // MOZ_MEDIA_NAVIGATOR
 
@@ -306,16 +316,19 @@ private:
   // any, else null.
   static already_AddRefed<nsPIDOMWindow> GetWindowFromGlobal(JSObject* aGlobal);
 
   nsRefPtr<nsMimeTypeArray> mMimeTypes;
   nsRefPtr<nsPluginArray> mPlugins;
   nsRefPtr<Geolocation> mGeolocation;
   nsRefPtr<DesktopNotificationCenter> mNotification;
   nsRefPtr<battery::BatteryManager> mBatteryManager;
+#ifdef MOZ_B2G_FM
+  nsRefPtr<FMRadio> mFMRadio;
+#endif
   nsRefPtr<power::PowerManager> mPowerManager;
   nsRefPtr<MobileMessageManager> mMobileMessageManager;
 #ifdef MOZ_B2G_RIL
   nsRefPtr<telephony::Telephony> mTelephony;
   nsCOMPtr<nsIDOMMozVoicemail> mVoicemail;
 #endif
   nsRefPtr<network::Connection> mConnection;
 #ifdef MOZ_B2G_RIL
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -536,21 +536,16 @@ static nsDOMClassInfoData sClassInfoData
 
 #ifdef MOZ_B2G_RIL
   NS_DEFINE_CLASSINFO_DATA(MozVoicemail, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(MozIccManager, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 #endif
 
-#ifdef MOZ_B2G_FM
-  NS_DEFINE_CLASSINFO_DATA(FMRadio, nsEventTargetSH,
-                           EVENTTARGET_SCRIPTABLE_FLAGS)
-#endif
-
 #ifdef MOZ_B2G_BT
   NS_DEFINE_CLASSINFO_DATA(BluetoothDevice, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
 #endif
 
   NS_DEFINE_CLASSINFO_DATA(CameraCapabilities, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
@@ -1407,23 +1402,16 @@ nsDOMClassInfo::Init()
 
   DOM_CLASSINFO_MAP_BEGIN(MozIccManager, nsIDOMMozIccManager)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozIccManager)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
   DOM_CLASSINFO_MAP_END
 
 #endif
 
-#ifdef MOZ_B2G_FM
-  DOM_CLASSINFO_MAP_BEGIN(FMRadio, nsIFMRadio)
-    DOM_CLASSINFO_MAP_ENTRY(nsIFMRadio)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
-  DOM_CLASSINFO_MAP_END
-#endif
-
 #ifdef MOZ_B2G_BT
   DOM_CLASSINFO_MAP_BEGIN(BluetoothDevice, nsIDOMBluetoothDevice)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMBluetoothDevice)
   DOM_CLASSINFO_MAP_END
 #endif
 
   DOM_CLASSINFO_MAP_BEGIN(CameraCapabilities, nsICameraCapabilities)
     DOM_CLASSINFO_MAP_ENTRY(nsICameraCapabilities)
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -126,20 +126,16 @@ DOMCI_CLASS(CSSPageRule)
 
 DOMCI_CLASS(MediaQueryList)
 
 #ifdef MOZ_B2G_RIL
 DOMCI_CLASS(MozVoicemail)
 DOMCI_CLASS(MozIccManager)
 #endif
 
-#ifdef MOZ_B2G_FM
-DOMCI_CLASS(FMRadio)
-#endif
-
 #ifdef MOZ_B2G_BT
 DOMCI_CLASS(BluetoothDevice)
 #endif
 
 DOMCI_CLASS(CameraCapabilities)
 
 DOMCI_CLASS(DOMWindowResizeEventDetail)
 
--- a/dom/dom-config.mk
+++ b/dom/dom-config.mk
@@ -43,17 +43,17 @@ DOM_SRCDIRS += \
   dom/telephony \
   dom/wifi \
   dom/icc/src \
   $(NULL)
 endif
 
 ifdef MOZ_B2G_FM
 DOM_SRCDIRS += \
-  dom/fm \
+  dom/fmradio \
   $(NULL)
 endif
 
 ifdef MOZ_B2G_BT
 DOM_SRCDIRS += dom/bluetooth
 endif
 
 ifdef MOZ_WEBSPEECH
deleted file mode 100644
--- a/dom/fm/DOMFMRadio.manifest
+++ /dev/null
@@ -1,4 +0,0 @@
-# DOMFMRadio.js
-component {901f8a83-03a6-4be9-bb8f-35387d3849da} DOMFMRadioChild.js
-contract @mozilla.org/domfmradio;1 {901f8a83-03a6-4be9-bb8f-35387d3849da}
-category JavaScript-navigator-property mozFMRadio @mozilla.org/domfmradio;1
deleted file mode 100644
--- a/dom/fm/DOMFMRadioChild.js
+++ /dev/null
@@ -1,423 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict"
-let DEBUG = 0;
-if (DEBUG)
-  debug = function (s) { dump("-*- DOMFMRadioChild: " + s + "\n"); };
-else
-  debug = function (s) { };
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
-
-const DOMFMMANAGER_CONTRACTID = "@mozilla.org/domfmradio;1";
-const DOMFMMANAGER_CID = Components.ID("{901f8a83-03a6-4be9-bb8f-35387d3849da}");
-
-XPCOMUtils.defineLazyGetter(Services, "DOMRequest", function() {
-  return Cc["@mozilla.org/dom/dom-request-service;1"]
-           .getService(Ci.nsIDOMRequestService);
-});
-
-XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
-                                   "@mozilla.org/childprocessmessagemanager;1",
-                                   "nsISyncMessageSender");
-
-function DOMFMRadioChild() { }
-
-DOMFMRadioChild.prototype = {
-  __proto__: DOMRequestIpcHelper.prototype,
-
-  classID: DOMFMMANAGER_CID,
-  classInfo: XPCOMUtils.generateCI({
-               classID: DOMFMMANAGER_CID,
-               contractID: DOMFMMANAGER_CONTRACTID,
-               classDescription: "DOMFMRadio",
-               interfaces: [Ci.nsIDOMFMRadio],
-               flags: Ci.nsIClassInfo.DOM_OBJECT
-             }),
-
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMFMRadio,
-                                         Ci.nsIDOMGlobalPropertyInitializer,
-                                         Ci.nsISupportsWeakReference]),
-
-  // nsIDOMGlobalPropertyInitializer implementation
-  init: function(aWindow) {
-    let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"]
-                   .getService(Ci.nsIScriptSecurityManager);
-
-    let perm = Services.perms.testExactPermissionFromPrincipal(aWindow.document.nodePrincipal, "fmradio");
-    this._hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION;
-
-    if (!this._hasPrivileges) {
-      Cu.reportError("NO FMRADIO PERMISSION FOR: " + aWindow.document.nodePrincipal.origin + "\n");
-      return null;
-    }
-
-    const messages = ["DOMFMRadio:enable:Return:OK",
-                      "DOMFMRadio:enable:Return:NO",
-                      "DOMFMRadio:disable:Return:OK",
-                      "DOMFMRadio:disable:Return:NO",
-                      "DOMFMRadio:setFrequency:Return:OK",
-                      "DOMFMRadio:setFrequency:Return:NO",
-                      "DOMFMRadio:seekUp:Return:OK",
-                      "DOMFMRadio:seekUp:Return:NO",
-                      "DOMFMRadio:seekDown:Return:OK",
-                      "DOMFMRadio:seekDown:Return:NO",
-                      "DOMFMRadio:cancelSeek:Return:OK",
-                      "DOMFMRadio:cancelSeek:Return:NO",
-                      "DOMFMRadio:frequencyChange",
-                      "DOMFMRadio:powerStateChange",
-                      "DOMFMRadio:antennaChange"];
-    this.initDOMRequestHelper(aWindow, messages);
-
-    let els = Cc["@mozilla.org/eventlistenerservice;1"]
-                .getService(Ci.nsIEventListenerService);
-
-    els.addSystemEventListener(aWindow, "visibilitychange",
-                               this._updateVisibility.bind(this),
-                               /* useCapture = */ true);
-
-    this._visibility = aWindow.document.visibilityState;
-    // Unlike the |enabled| getter, this is true if *this* DOM window
-    // has successfully enabled the FM radio more recently than
-    // disabling it.
-    this._haveEnabledRadio = false;
-  },
-
-  // Called from DOMRequestIpcHelper
-  uninit: function() {
-    this._onFrequencyChange = null;
-    this._onAntennaChange = null;
-    this._onDisabled = null;
-    this._onEnabled = null;
-  },
-
-  _createEvent: function(name) {
-    return new this._window.Event(name);
-  },
-
-  _sendMessageForRequest: function(name, data, request) {
-    let id = this.getRequestId(request);
-    cpmm.sendAsyncMessage(name, {
-      data: data,
-      rid: id,
-      mid: this._id
-    });
-  },
-
-  _fireFrequencyChangeEvent: function() {
-    let e = this._createEvent("frequencychange");
-    if (this._onFrequencyChange) {
-      this._onFrequencyChange.handleEvent(e);
-    }
-    this.dispatchEvent(e);
-  },
-
-  _firePowerStateChangeEvent: function() {
-    let _enabled = this.enabled;
-    debug("Current power state: " + _enabled);
-    if (_enabled) {
-      let e = this._createEvent("enabled");
-      if (this._onEnabled) {
-        this._onEnabled.handleEvent(e);
-      }
-      this.dispatchEvent(e);
-    } else {
-      let e = this._createEvent("disabled");
-      if (this._onDisabled) {
-        this._onDisabled.handleEvent(e);
-      }
-      this.dispatchEvent(e);
-    }
-  },
-
-  _fireAntennaAvailableChangeEvent: function() {
-    let e = this._createEvent("antennaavailablechange");
-    if (this._onAntennaChange) {
-      this._onAntennaChange.handleEvent(e);
-    }
-    this.dispatchEvent(e);
-  },
-
-  _updateVisibility: function(evt) {
-    this._visibility = evt.target.visibilityState;
-    // Only notify visibility state when we "own" the radio stream.
-    if (this._haveEnabledRadio) {
-      this._notifyVisibility();
-    }
-  },
-
-  _notifyVisibility: function() {
-    cpmm.sendAsyncMessage("DOMFMRadio:updateVisibility", this._visibility);
-  },
-
-  receiveMessage: function(aMessage) {
-    let msg = aMessage.json;
-    if (msg.mid && msg.mid != this._id) {
-      return;
-    }
-
-    let request;
-    switch (aMessage.name) {
-      case "DOMFMRadio:enable:Return:OK":
-        request = this.takeRequest(msg.rid);
-        if (!request) {
-          return;
-        }
-        Services.DOMRequest.fireSuccess(request, null);
-        break;
-      case "DOMFMRadio:enable:Return:NO":
-        request = this.takeRequest(msg.rid);
-        if (!request) {
-          return;
-        }
-        Services.DOMRequest.fireError(request, "Failed to turn on the FM radio");
-        break;
-      case "DOMFMRadio:disable:Return:OK":
-        this._haveEnabledRadio = false;
-        request = this.takeRequest(msg.rid);
-        if (!request) {
-          return;
-        }
-        Services.DOMRequest.fireSuccess(request, null);
-        break;
-      case "DOMFMRadio:disable:Return:NO":
-        // If disabling the radio failed, but the hardware is still
-        // on, this DOM window is still responsible for the continued
-        // playback.
-        this._haveEnabledRadio = this.enabled;
-        request = this.takeRequest(msg.rid);
-        if (!request) {
-          return;
-        }
-        Services.DOMRequest.fireError(request,
-                                      "Failed to turn off the FM radio");
-        break;
-      case "DOMFMRadio:setFrequency:Return:OK":
-        request = this.takeRequest(msg.rid);
-        if (!request) {
-          return;
-        }
-        Services.DOMRequest.fireSuccess(request, null);
-        break;
-      case "DOMFMRadio:setFrequency:Return:NO":
-        request = this.takeRequest(msg.rid);
-        if (!request) {
-          return;
-        }
-        Services.DOMRequest.fireError(request,
-                                      "Failed to set the FM radio frequency");
-        break;
-      case "DOMFMRadio:seekUp:Return:OK":
-        request = this.takeRequest(msg.rid);
-        if (!request) {
-          return;
-        }
-        Services.DOMRequest.fireSuccess(request, null);
-        break;
-      case "DOMFMRadio:seekUp:Return:NO":
-        request = this.takeRequest(msg.rid);
-        if (!request) {
-          return;
-        }
-        Services.DOMRequest.fireError(request, "FM radio seek-up failed");
-        break;
-      case "DOMFMRadio:seekDown:Return:OK":
-        request = this.takeRequest(msg.rid);
-        if (!request) {
-          return;
-        }
-        Services.DOMRequest.fireSuccess(request, null);
-        break;
-      case "DOMFMRadio:seekDown:Return:NO":
-        request = this.takeRequest(msg.rid);
-        if (!request) {
-          return;
-        }
-        Services.DOMRequest.fireError(request, "FM radio seek-down failed");
-        break;
-      case "DOMFMRadio:cancelSeek:Return:OK":
-        request = this.takeRequest(msg.rid);
-        if (!request) {
-          return;
-        }
-        Services.DOMRequest.fireSuccess(request, null);
-        break;
-      case "DOMFMRadio:cancelSeek:Return:NO":
-        request = this.takeRequest(msg.rid);
-        if (!request) {
-          return;
-        }
-        Services.DOMRequest.fireError(request, "Failed to cancel seek");
-        break;
-      case "DOMFMRadio:powerStateChange":
-        this._firePowerStateChangeEvent();
-        break;
-      case "DOMFMRadio:frequencyChange":
-        this._fireFrequencyChangeEvent();
-        break;
-      case "DOMFMRadio:antennaChange":
-        this._fireAntennaAvailableChangeEvent();
-        break;
-    }
-  },
-
-  _call: function(name, arg) {
-    var request = this.createRequest();
-    this._sendMessageForRequest("DOMFMRadio:" + name, arg, request);
-    return request;
-  },
-
-  // nsIDOMFMRadio
-  get enabled() {
-    return cpmm.sendSyncMessage("DOMFMRadio:getPowerState")[0];
-  },
-
-  get antennaAvailable() {
-    return cpmm.sendSyncMessage("DOMFMRadio:getAntennaState")[0];
-  },
-
-  get frequency() {
-    return cpmm.sendSyncMessage("DOMFMRadio:getFrequency")[0];
-  },
-
-  get frequencyUpperBound() {
-    let range = cpmm.sendSyncMessage("DOMFMRadio:getCurrentBand")[0];
-    return range.upper;
-  },
-
-  get frequencyLowerBound() {
-    let range = cpmm.sendSyncMessage("DOMFMRadio:getCurrentBand")[0];
-    return range.lower;
-  },
-
-  get channelWidth() {
-    let range = cpmm.sendSyncMessage("DOMFMRadio:getCurrentBand")[0];
-    return range.channelWidth;
-  },
-
-  set onantennaavailablechange(callback) {
-    this._onAntennaChange = callback;
-  },
-
-  set onenabled(callback) {
-    this._onEnabled = callback;
-  },
-
-  set ondisabled(callback) {
-    this._onDisabled = callback;
-  },
-
-  set onfrequencychange(callback) {
-    this._onFrequencyChange = callback;
-  },
-
-  disable: function nsIDOMFMRadio_disable() {
-    return this._call("disable", null);
-  },
-
-  enable: function nsIDOMFMRadio_enable(frequency) {
-    // FMRadio::Enable() needs the most recent visibility state
-    // synchronously.
-    this._haveEnabledRadio = true;
-    this._notifyVisibility();
-    return this._call("enable", frequency);
-  },
-
-  setFrequency: function nsIDOMFMRadio_setFreq(frequency) {
-    return this._call("setFrequency", frequency);
-  },
-
-  seekDown: function nsIDOMFMRadio_seekDown() {
-    return this._call("seekDown", null);
-  },
-
-  seekUp: function nsIDOMFMRadio_seekUp() {
-    return this._call("seekUp", null);
-  },
-
-  cancelSeek: function nsIDOMFMRadio_cancelSeek() {
-    return this._call("cancelSeek", null);
-  },
-
-  // These are fake implementations, will be replaced by using
-  // nsJSDOMEventTargetHelper, see bug 731746
-  addEventListener: function(type, listener, useCapture) {
-    if (!this._eventListenersByType) {
-      this._eventListenersByType = {};
-    }
-
-    if (!listener) {
-      return;
-    }
-
-    var listeners = this._eventListenersByType[type];
-    if (!listeners) {
-      listeners = this._eventListenersByType[type] = [];
-    }
-
-    useCapture = !!useCapture;
-    for (let i = 0, len = listeners.length; i < len; i++) {
-      let l = listeners[i];
-      if (l && l.listener === listener && l.useCapture === useCapture) {
-        return;
-      }
-    }
-
-    listeners.push({
-      listener: listener,
-      useCapture: useCapture
-    });
-  },
-
-  removeEventListener: function(type, listener, useCapture) {
-    if (!this._eventListenersByType) {
-      return;
-    }
-
-    useCapture = !!useCapture;
-
-    var listeners = this._eventListenersByType[type];
-    if (listeners) {
-      for (let i = 0, len = listeners.length; i < len; i++) {
-        let l = listeners[i];
-        if (l && l.listener === listener && l.useCapture === useCapture) {
-          listeners.splice(i, 1);
-        }
-      }
-    }
-  },
-
-  dispatchEvent: function(evt) {
-    if (!this._eventListenersByType) {
-      return;
-    }
-
-    let type = evt.type;
-    var listeners = this._eventListenersByType[type];
-    if (listeners) {
-      for (let i = 0, len = listeners.length; i < len; i++) {
-        let listener = listeners[i].listener;
-
-        try {
-          if (typeof listener == "function") {
-            listener.call(this, evt);
-          } else if (listener && listener.handleEvent &&
-                     typeof listener.handleEvent == "function") {
-            listener.handleEvent(evt);
-          }
-        } catch (e) {
-          debug("Exception is caught: " + e);
-        }
-      }
-    }
-  }
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DOMFMRadioChild]);
-
deleted file mode 100644
--- a/dom/fm/DOMFMRadioParent.jsm
+++ /dev/null
@@ -1,472 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict"
-
-let DEBUG = 0;
-if (DEBUG)
-  debug = function(s) { dump("-*- DOMFMRadioParent component: " + s + "\n");  };
-else
-  debug = function(s) {};
-
-const Cu = Components.utils;
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-
-const MOZ_SETTINGS_CHANGED_OBSERVER_TOPIC  = "mozsettings-changed";
-const PROFILE_BEFORE_CHANGE_OBSERVER_TOPIC = "profile-before-change";
-
-const BAND_87500_108000_kHz = 1;
-const BAND_76000_108000_kHz = 2;
-const BAND_76000_90000_kHz  = 3;
-
-const FM_BANDS = { };
-FM_BANDS[BAND_76000_90000_kHz] = {
-  lower: 76000,
-  upper: 90000
-};
-
-FM_BANDS[BAND_87500_108000_kHz] = {
-  lower: 87500,
-  upper: 108000
-};
-
-FM_BANDS[BAND_76000_108000_kHz] = {
-  lower: 76000,
-  upper: 108000
-};
-
-const BAND_SETTING_KEY          = "fmRadio.band";
-const CHANNEL_WIDTH_SETTING_KEY = "fmRadio.channelWidth";
-
-// Hal types
-const CHANNEL_WIDTH_200KHZ = 200;
-const CHANNEL_WIDTH_100KHZ = 100;
-const CHANNEL_WIDTH_50KHZ  = 50;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-
-XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
-                                   "@mozilla.org/parentprocessmessagemanager;1",
-                                   "nsIMessageListenerManager");
-
-XPCOMUtils.defineLazyGetter(this, "FMRadio", function() {
-  return Cc["@mozilla.org/fmradio;1"].getService(Ci.nsIFMRadio);
-});
-
-XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
-                                   "@mozilla.org/settingsService;1",
-                                   "nsISettingsService");
-
-this.EXPORTED_SYMBOLS = ["DOMFMRadioParent"];
-
-this.DOMFMRadioParent = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                         Ci.nsISettingsServiceCallback]),
-
-  _initialized: false,
-
-  /* Indicates if the FM radio is currently enabled */
-  _isEnabled: false,
-
-  /* Indicates if the FM radio is currently being enabled */
-  _enabling: false,
-
-  /* Current frequency in KHz */
-  _currentFrequency: 0,
-
-  /* Current band setting */
-  _currentBand: BAND_87500_108000_kHz,
-
-  /* Current channel width */
-  _currentWidth: CHANNEL_WIDTH_100KHZ,
-
-  /* Indicates if the antenna is currently available */
-  _antennaAvailable: true,
-
-  _seeking: false,
-
-  _seekingCallback: null,
-
-  init: function() {
-    if (this._initialized === true) {
-      return;
-    }
-    this._initialized = true;
-
-    this._messages = ["DOMFMRadio:enable", "DOMFMRadio:disable",
-                      "DOMFMRadio:setFrequency", "DOMFMRadio:getCurrentBand",
-                      "DOMFMRadio:getPowerState", "DOMFMRadio:getFrequency",
-                      "DOMFMRadio:getAntennaState",
-                      "DOMFMRadio:seekUp", "DOMFMRadio:seekDown",
-                      "DOMFMRadio:cancelSeek",
-                      "DOMFMRadio:updateVisibility",
-                     ];
-    this._messages.forEach(function(msgName) {
-      ppmm.addMessageListener(msgName, this);
-    }.bind(this));
-
-    Services.obs.addObserver(this, PROFILE_BEFORE_CHANGE_OBSERVER_TOPIC, false);
-    Services.obs.addObserver(this, MOZ_SETTINGS_CHANGED_OBSERVER_TOPIC, false);
-
-    this._updatePowerState();
-
-    // Get the band setting and channel width setting
-    let lock = gSettingsService.createLock();
-    lock.get(BAND_SETTING_KEY, this);
-    lock.get(CHANNEL_WIDTH_SETTING_KEY, this);
-
-    this._updateAntennaState();
-
-    let self = this;
-    FMRadio.onantennastatechange = function onantennachange() {
-      self._updateAntennaState();
-    };
-
-    debug("Initialized");
-  },
-
-  // nsISettingsServiceCallback
-  handle: function(aName, aResult) {
-    if (aName == BAND_SETTING_KEY) {
-      this._updateBand(aResult);
-    } else if (aName == CHANNEL_WIDTH_SETTING_KEY) {
-      this._updateChannelWidth(aResult);
-    }
-  },
-
-  handleError: function(aErrorMessage) {
-    this._updateBand(BAND_87500_108000_kHz);
-    this._updateChannelWidth(CHANNEL_WIDTH_100KHZ);
-  },
-
-  _updateAntennaState: function() {
-    let antennaState = FMRadio.isAntennaAvailable;
-
-    if (antennaState != this._antennaAvailable) {
-      this._antennaAvailable = antennaState;
-      ppmm.broadcastAsyncMessage("DOMFMRadio:antennaChange", { });
-    }
-  },
-
-  _updateBand: function(band) {
-      switch (parseInt(band)) {
-        case BAND_87500_108000_kHz:
-        case BAND_76000_108000_kHz:
-        case BAND_76000_90000_kHz:
-          this._currentBand = band;
-          break;
-      }
-  },
-
-  _updateChannelWidth: function(channelWidth) {
-    switch (parseInt(channelWidth)) {
-      case CHANNEL_WIDTH_50KHZ:
-      case CHANNEL_WIDTH_100KHZ:
-      case CHANNEL_WIDTH_200KHZ:
-        this._currentWidth = channelWidth;
-        break;
-    }
-  },
-
-  /**
-   * Update and cache the current frequency.
-   * Send frequency change message if the frequency is changed.
-   * The returned boolean value indicates if the frequency is changed.
-   */
-  _updateFrequency: function() {
-    let frequency = FMRadio.frequency;
-
-    if (frequency != this._currentFrequency) {
-      this._currentFrequency = frequency;
-      ppmm.broadcastAsyncMessage("DOMFMRadio:frequencyChange", { });
-      return true;
-    }
-
-    return false;
-  },
-
-  /**
-   * Update and cache the power state of the FM radio.
-   * Send message if the power state is changed.
-   */
-  _updatePowerState: function() {
-    let enabled = FMRadio.enabled;
-
-    if (this._isEnabled != enabled) {
-      this._isEnabled = enabled;
-      ppmm.broadcastAsyncMessage("DOMFMRadio:powerStateChange", { });
-
-      // If the FM radio is enabled, update the current frequency immediately,
-      if (enabled) {
-        this._updateFrequency();
-      }
-    }
-  },
-
-  _onSeekComplete: function(success) {
-    if (this._seeking) {
-      this._seeking = false;
-
-      if (this._seekingCallback) {
-        this._seekingCallback(success);
-        this._seekingCallback = null;
-      }
-    }
-  },
-
-  /**
-
-   * Seek the next channel with given direction.
-   * Only one seek action is allowed at once.
-   */
-  _seekStation: function(direction, aMessage) {
-    let msg = aMessage.json || { };
-    let messageName = aMessage.name + ":Return";
-
-    // If the FM radio is disabled, do not execute the seek action.
-    if(!this._isEnabled) {
-       this._sendMessage(messageName, false, null, msg);
-       return;
-    }
-
-    let self = this;
-    function callback(success) {
-      debug("Seek completed.");
-      if (!success) {
-        self._sendMessage(messageName, false, null, msg);
-      } else {
-        // Make sure the FM app will get the right frequency.
-        self._updateFrequency();
-        self._sendMessage(messageName, true, null, msg);
-      }
-    }
-
-    if (this._seeking) {
-      // Pass a boolean value to the callback which indicates that
-      // the seek action failed.
-      callback(false);
-      return;
-    }
-
-    this._seekingCallback = callback;
-    this._seeking = true;
-
-    let self = this;
-    FMRadio.seek(direction);
-    FMRadio.addEventListener("seekcomplete", function FM_onSeekComplete() {
-      FMRadio.removeEventListener("seekcomplete", FM_onSeekComplete);
-      self._onSeekComplete(true);
-    });
-  },
-
-  /**
-   * Round the frequency to match the range of frequency and the channel width.
-   * If the given frequency is out of range, return null.
-   * For example:
-   *  - lower: 87.5MHz, upper: 108MHz, channel width: 0.2MHz
-   *    87600 is rounded to 87700
-   *    87580 is rounded to 87500
-   *    109000 is not rounded, null will be returned
-   */
-  _roundFrequency: function(frequencyInKHz) {
-    if (frequencyInKHz < FM_BANDS[this._currentBand].lower ||
-        frequencyInKHz > FM_BANDS[this._currentBand].upper) {
-      return null;
-    }
-
-    let partToBeRounded = frequencyInKHz - FM_BANDS[this._currentBand].lower;
-    let roundedPart = Math.round(partToBeRounded / this._currentWidth) *
-                        this._currentWidth;
-    return FM_BANDS[this._currentBand].lower + roundedPart;
-  },
-
-  observe: function(aSubject, aTopic, aData) {
-    switch (aTopic) {
-      case PROFILE_BEFORE_CHANGE_OBSERVER_TOPIC:
-        this._messages.forEach(function(msgName) {
-          ppmm.removeMessageListener(msgName, this);
-        }.bind(this));
-
-        Services.obs.removeObserver(this, PROFILE_BEFORE_CHANGE_OBSERVER_TOPIC);
-        Services.obs.removeObserver(this, MOZ_SETTINGS_CHANGED_OBSERVER_TOPIC);
-
-        ppmm = null;
-        this._messages = null;
-        break;
-      case MOZ_SETTINGS_CHANGED_OBSERVER_TOPIC:
-        let setting = JSON.parse(aData);
-        this.handleMozSettingsChanged(setting);
-        break;
-    }
-  },
-
-  _sendMessage: function(message, success, data, msg) {
-    msg.manager.sendAsyncMessage(message + (success ? ":OK" : ":NO"), {
-      data: data,
-      rid: msg.rid,
-      mid: msg.mid
-    });
-  },
-
-  handleMozSettingsChanged: function(settings) {
-    switch (settings.key) {
-      case BAND_SETTING_KEY:
-        this._updateBand(settings.value);
-        break;
-      case CHANNEL_WIDTH_SETTING_KEY:
-        this._updateChannelWidth(settings.value);
-        break;
-    }
-  },
-
-  _enableFMRadio: function(msg) {
-    let frequencyInKHz = this._roundFrequency(msg.data * 1000);
-
-    // If the FM radio is already enabled or it is currently being enabled
-    // or the given frequency is out of range, return false.
-    if (this._isEnabled || this._enabling || !frequencyInKHz) {
-      this._sendMessage("DOMFMRadio:enable:Return", false, null, msg);
-      return;
-    }
-
-    this._enabling = true;
-    let self = this;
-
-    FMRadio.addEventListener("enabled", function on_enabled() {
-      dump("Perf:FMRadio:Enable " + (Date.now()- timeStart) + " ms.\n");
-      self._enabling = false;
-
-      FMRadio.removeEventListener("enabled", on_enabled);
-
-      // To make sure the FM app will get right frequency after the FM
-      // radio is enabled, we have to set the frequency first.
-      FMRadio.setFrequency(frequencyInKHz);
-
-      // Update the current frequency without sending 'frequencyChange'
-      // msg, to make sure the FM app will get the right frequency when the
-      // 'enabled' event is fired.
-      self._currentFrequency = FMRadio.frequency;
-
-      self._updatePowerState();
-      self._sendMessage("DOMFMRadio:enable:Return", true, null, msg);
-
-      // The frequency is changed from 'null' to some number, so we should
-      // send the 'frequencyChange' message manually.
-      ppmm.broadcastAsyncMessage("DOMFMRadio:frequencyChange", { });
-    });
-
-    let timeStart = Date.now();
-
-    FMRadio.enable({
-      lowerLimit: FM_BANDS[self._currentBand].lower,
-      upperLimit: FM_BANDS[self._currentBand].upper,
-      channelWidth:  self._currentWidth   // 100KHz by default
-    });
-  },
-
-  _disableFMRadio: function(msg) {
-    // If the FM radio is already disabled, return false.
-    if (!this._isEnabled) {
-      this._sendMessage("DOMFMRadio:disable:Return", false, null, msg);
-      return;
-    }
-
-    let self = this;
-    FMRadio.addEventListener("disabled", function on_disabled() {
-      debug("FM Radio is disabled!");
-      FMRadio.removeEventListener("disabled", on_disabled);
-
-      self._updatePowerState();
-      self._sendMessage("DOMFMRadio:disable:Return", true, null, msg);
-
-      // If the FM Radio is currently seeking, no fail-to-seek or similar
-      // event will be fired, execute the seek callback manually.
-      self._onSeekComplete(false);
-    });
-
-    FMRadio.disable();
-  },
-
-  receiveMessage: function(aMessage) {
-    let msg = aMessage.json || {};
-    msg.manager = aMessage.target;
-
-    let ret = 0;
-    let self = this;
-
-    if (!aMessage.target.assertPermission("fmradio")) {
-      Cu.reportError("FMRadio message " + aMessage.name +
-                     " from a content process with no 'fmradio' privileges.");
-      return null;
-    }
-
-    switch (aMessage.name) {
-      case "DOMFMRadio:enable":
-        self._enableFMRadio(msg);
-        break;
-      case "DOMFMRadio:disable":
-        self._disableFMRadio(msg);
-        break;
-      case "DOMFMRadio:setFrequency":
-        let frequencyInKHz = self._roundFrequency(msg.data * 1000);
-
-        // If the FM radio is disabled or the given frequency is out of range,
-        // skip to set frequency and send back the False message immediately.
-        if (!self._isEnabled || !frequencyInKHz) {
-          self._sendMessage("DOMFMRadio:setFrequency:Return", false, null, msg);
-        } else {
-          FMRadio.setFrequency(frequencyInKHz);
-          self._sendMessage("DOMFMRadio:setFrequency:Return", true, null, msg);
-          this._updateFrequency();
-        }
-        break;
-      case "DOMFMRadio:getCurrentBand":
-        // this message is sync
-        return {
-          lower: FM_BANDS[self._currentBand].lower / 1000,   // in MHz
-          upper: FM_BANDS[self._currentBand].upper / 1000,   // in MHz
-          channelWidth: self._currentWidth / 1000            // in MHz
-        };
-      case "DOMFMRadio:getPowerState":
-        // this message is sync
-        return self._isEnabled;
-      case "DOMFMRadio:getFrequency":
-        // this message is sync
-        return self._isEnabled ? this._currentFrequency / 1000 : null; // in MHz
-      case "DOMFMRadio:getAntennaState":
-        // this message is sync
-        return self._antennaAvailable;
-      case "DOMFMRadio:seekUp":
-        self._seekStation(Ci.nsIFMRadio.SEEK_DIRECTION_UP, aMessage);
-        break;
-      case "DOMFMRadio:seekDown":
-        self._seekStation(Ci.nsIFMRadio.SEEK_DIRECTION_DOWN, aMessage);
-        break;
-      case "DOMFMRadio:cancelSeek":
-        // If the FM radio is disabled, or the FM radio is not currently
-        // seeking, do not execute the cancel seek action.
-        if (!self._isEnabled || !self._seeking) {
-          self._sendMessage("DOMFMRadio:cancelSeek:Return", false, null, msg);
-        } else {
-          FMRadio.cancelSeek();
-          // No fail-to-seek or similar event will be fired from the hal part,
-          // so execute the seek callback here manually.
-          this._onSeekComplete(false);
-          // The FM radio will stop at one frequency without any event, so we need to
-          // update the current frequency, make sure the FM app will get the right frequency.
-          this._updateFrequency();
-          self._sendMessage("DOMFMRadio:cancelSeek:Return", true, null, msg);
-        }
-        break;
-      case "DOMFMRadio:updateVisibility":
-        FMRadio.updateVisible(msg == 'visible');
-        break;
-    }
-  }
-};
-
-DOMFMRadioParent.init();
-
deleted file mode 100644
--- a/dom/fm/FMRadio.cpp
+++ /dev/null
@@ -1,264 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "mozilla/Hal.h"
-#include "mozilla/HalTypes.h"
-#include "mozilla/Preferences.h"
-#include "nsIAudioManager.h"
-#include "FMRadio.h"
-#include "nsDOMEvent.h"
-#include "nsDOMClassInfo.h"
-#include "nsFMRadioSettings.h"
-#include "nsCOMPtr.h"
-
-#undef LOG
-#if defined(MOZ_WIDGET_GONK)
-#include <android/log.h>
-#define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "FMRadio" , ## args)
-#else
-#define LOG(args...)
-#endif
-
-// The pref indicates if the device has an internal antenna.
-// If the pref is true, the antanna will be always available.
-#define DOM_FM_ANTENNA_INTERNAL_PREF "dom.fm.antenna.internal"
-
-#define RADIO_SEEK_COMPLETE_EVENT_NAME   NS_LITERAL_STRING("seekcomplete")
-#define RADIO_DISABLED_EVENT_NAME        NS_LITERAL_STRING("disabled")
-#define RADIO_ENABLED_EVENT_NAME         NS_LITERAL_STRING("enabled")
-#define ANTENNA_STATE_CHANGED_EVENT_NAME NS_LITERAL_STRING("antennastatechange")
-
-#define NS_AUDIOMANAGER_CONTRACTID "@mozilla.org/telephony/audiomanager;1"
-
-using namespace mozilla::dom::fm;
-using namespace mozilla::hal;
-using mozilla::Preferences;
-
-FMRadio::FMRadio()
-  : mHeadphoneState(SWITCH_STATE_OFF)
-  , mHasInternalAntenna(false)
-  , mHidden(true)
-{
-  LOG("FMRadio is initialized.");
-
-  mHasInternalAntenna = Preferences::GetBool(DOM_FM_ANTENNA_INTERNAL_PREF,
-                                             /* default = */ false);
-  if (mHasInternalAntenna) {
-    LOG("We have an internal antenna.");
-  } else {
-    RegisterSwitchObserver(SWITCH_HEADPHONES, this);
-    mHeadphoneState = GetCurrentSwitchState(SWITCH_HEADPHONES);
-  }
-
-  RegisterFMRadioObserver(this);
-}
-
-FMRadio::~FMRadio()
-{
-  UnregisterFMRadioObserver(this);
-  if (!mHasInternalAntenna) {
-    UnregisterSwitchObserver(SWITCH_HEADPHONES, this);
-  }
-}
-
-DOMCI_DATA(FMRadio, FMRadio)
-
-NS_INTERFACE_MAP_BEGIN(FMRadio)
-  NS_INTERFACE_MAP_ENTRY(nsIFMRadio)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(FMRadio)
-NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
-
-NS_IMPL_EVENT_HANDLER(FMRadio, seekcomplete)
-NS_IMPL_EVENT_HANDLER(FMRadio, disabled)
-NS_IMPL_EVENT_HANDLER(FMRadio, enabled)
-NS_IMPL_EVENT_HANDLER(FMRadio, antennastatechange)
-
-NS_IMPL_ADDREF_INHERITED(FMRadio, nsDOMEventTargetHelper)
-NS_IMPL_RELEASE_INHERITED(FMRadio, nsDOMEventTargetHelper)
-
-/* readonly attribute boolean isAntennaAvailable; */
-NS_IMETHODIMP FMRadio::GetIsAntennaAvailable(bool *aIsAvailable)
-{
-  if (mHasInternalAntenna) {
-    *aIsAvailable = true;
-  } else {
-    *aIsAvailable = mHeadphoneState != SWITCH_STATE_OFF;
-  }
-  return NS_OK;
-}
-
-/* readonly attribute long frequency; */
-NS_IMETHODIMP FMRadio::GetFrequency(int32_t *aFrequency)
-{
-  *aFrequency = GetFMRadioFrequency();
-  return NS_OK;
-}
-
-/* readonly attribute blean enabled; */
-NS_IMETHODIMP FMRadio::GetEnabled(bool *aEnabled)
-{
-  *aEnabled = IsFMRadioOn();
-  return NS_OK;
-}
-
-/* void enable (in nsIFMRadioSettings settings); */
-NS_IMETHODIMP FMRadio::Enable(nsIFMRadioSettings *settings)
-{
-  hal::FMRadioSettings info;
-
-  int32_t upperLimit, lowerLimit, channelWidth;
-
-  if (!mAudioChannelAgent) {
-    nsresult rv;
-    mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv);
-    if (!mAudioChannelAgent) {
-      return NS_ERROR_FAILURE;
-    }
-    mAudioChannelAgent->Init(AUDIO_CHANNEL_CONTENT, this);
-  }
-
-  bool canPlay;
-  mAudioChannelAgent->SetVisibilityState(!mHidden);
-  mAudioChannelAgent->StartPlaying(&canPlay);
-
-  settings->GetUpperLimit(&upperLimit);
-  settings->GetLowerLimit(&lowerLimit);
-  settings->GetChannelWidth(&channelWidth);
-
-  info.upperLimit() = upperLimit;
-  info.lowerLimit() = lowerLimit;
-  info.spaceType() = channelWidth;
-
-  EnableFMRadio(info);
-
-  nsCOMPtr<nsIAudioManager> audioManager =
-    do_GetService(NS_AUDIOMANAGER_CONTRACTID);
-  NS_ENSURE_TRUE(audioManager, NS_OK);
-
-  audioManager->SetFmRadioAudioEnabled(true);
-  // We enable the hardware, but mute the audio stream, in order to
-  // simplify state handling.  This is simpler but worse for battery
-  // life; followup is bug 820282.
-  // Note: To adjust FM volume is only available after setting up
-  // routing patch.
-  CanPlayChanged(canPlay);
-
-  return NS_OK;
-}
-
-/* void disableRadio (); */
-NS_IMETHODIMP FMRadio::Disable()
-{
-  // Fix Bug 796733. 
-  // DisableFMRadio should be called before SetFmRadioAudioEnabled to prevent
-  // the annoying beep sound.
-  DisableFMRadio();
-
-  nsCOMPtr<nsIAudioManager> audioManager =
-    do_GetService(NS_AUDIOMANAGER_CONTRACTID);
-  NS_ENSURE_TRUE(audioManager, NS_OK);
-
-  audioManager->SetFmRadioAudioEnabled(false);
-
-  if (mAudioChannelAgent) {
-    mAudioChannelAgent->StopPlaying();
-    mAudioChannelAgent = nullptr;
-  }
-  return NS_OK;
-}
-
-/* void cancelSeek */
-NS_IMETHODIMP FMRadio::CancelSeek()
-{
-  CancelFMRadioSeek();
-  return NS_OK;
-}
-
-/* void seek (in long direction); */
-NS_IMETHODIMP FMRadio::Seek(int32_t direction)
-{
-  if (direction == (int)FM_RADIO_SEEK_DIRECTION_UP) {
-    FMRadioSeek(FM_RADIO_SEEK_DIRECTION_UP);
-  } else {
-    FMRadioSeek(FM_RADIO_SEEK_DIRECTION_DOWN);
-  }
-  return NS_OK;
-}
-
-/* nsIFMRadioSettings getSettings (); */
-NS_IMETHODIMP FMRadio::GetSettings(nsIFMRadioSettings * *_retval)
-{
-  hal::FMRadioSettings settings;
-  GetFMRadioSettings(&settings);
-
-  nsCOMPtr<nsIFMRadioSettings> radioSettings(new nsFMRadioSettings(
-                                                   settings.upperLimit(),
-                                                   settings.lowerLimit(),
-                                                   settings.spaceType()));
-  radioSettings.forget(_retval);
-
-  return NS_OK;
-}
-
-/* void setFrequency (in long frequency); */
-NS_IMETHODIMP FMRadio::SetFrequency(int32_t frequency)
-{
-  SetFMRadioFrequency(frequency);
-  return NS_OK;
-}
-
-NS_IMETHODIMP FMRadio::UpdateVisible(bool aVisible)
-{
-  mHidden = !aVisible;
-  if (mAudioChannelAgent) {
-    mAudioChannelAgent->SetVisibilityState(!mHidden);
-  }
-  return NS_OK;
-}
-
-void FMRadio::Notify(const SwitchEvent& aEvent)
-{
-  if (mHeadphoneState != aEvent.status()) {
-    LOG("Antenna state is changed!");
-    mHeadphoneState = aEvent.status();
-    DispatchTrustedEvent(ANTENNA_STATE_CHANGED_EVENT_NAME);
-  }
-}
-
-void FMRadio::Notify(const FMRadioOperationInformation& info)
-{
-  switch (info.operation())
-  {
-    case FM_RADIO_OPERATION_ENABLE:
-      DispatchTrustedEvent(RADIO_ENABLED_EVENT_NAME);
-      break;
-    case FM_RADIO_OPERATION_DISABLE:
-      DispatchTrustedEvent(RADIO_DISABLED_EVENT_NAME);
-      break;
-    case FM_RADIO_OPERATION_SEEK:
-      DispatchTrustedEvent(RADIO_SEEK_COMPLETE_EVENT_NAME);
-      break;
-    default:
-      MOZ_CRASH();
-  }
-}
-
-/* void canPlayChanged (in boolean canPlay); */
-NS_IMETHODIMP FMRadio::CanPlayChanged(bool canPlay)
-{
-  nsCOMPtr<nsIAudioManager> audioManager =
-    do_GetService(NS_AUDIOMANAGER_CONTRACTID);
-  NS_ENSURE_TRUE(audioManager, NS_OK);
-
-  bool AudioEnabled;
-  audioManager->GetFmRadioAudioEnabled(&AudioEnabled);
-  if (AudioEnabled == canPlay) {
-    return NS_OK;
-  }
-
-  /* mute fm first, it should be better to stop&resume fm */
-  audioManager->SetFmRadioAudioEnabled(canPlay);
-  return NS_OK;
-}
-
deleted file mode 100644
--- a/dom/fm/FMRadio.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
-/* vim: set ts=2 et sw=2 tw=40: */
-/* 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_fm_radio_h__
-#define mozilla_dom_fm_radio_h__
-
-#include "nsCOMPtr.h"
-#include "mozilla/HalTypes.h"
-#include "nsDOMEventTargetHelper.h"
-#include "nsIFMRadio.h"
-#include "AudioChannelService.h"
-
-#define NS_FMRADIO_CONTRACTID "@mozilla.org/fmradio;1"
-// 9cb91834-78a9-4029-b644-7806173c5e2d
-#define NS_FMRADIO_CID {0x9cb91834, 0x78a9, 0x4029, \
-      {0xb6, 0x44, 0x78, 0x06, 0x17, 0x3c, 0x5e, 0x2d}}
-
-namespace mozilla {
-namespace dom {
-namespace fm {
-
-/* Header file */
-class FMRadio : public nsDOMEventTargetHelper
-              , public nsIFMRadio
-              , public hal::FMRadioObserver
-              , public hal::SwitchObserver
-              , public nsIAudioChannelAgentCallback
-{
-public:
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_NSIFMRADIO
-  NS_DECL_NSIAUDIOCHANNELAGENTCALLBACK
-
-  NS_REALLY_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper)
-  FMRadio();
-  virtual void Notify(const hal::FMRadioOperationInformation& info);
-  virtual void Notify(const hal::SwitchEvent& aEvent);
-
-private:
-  ~FMRadio();
-
-  nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent;
-  hal::SwitchState mHeadphoneState;
-  bool mHasInternalAntenna;
-  bool mHidden;
-};
-
-} // namespace fm
-} // namespace dom
-} // namespace mozilla
-#endif
-
deleted file mode 100644
--- a/dom/fm/Makefile.in
+++ /dev/null
@@ -1,17 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this file,
-# You can obtain one at http://mozilla.org/MPL/2.0/.
-
-DEPTH            = @DEPTH@
-topsrcdir        = @top_srcdir@
-srcdir           = @srcdir@
-VPATH            = @srcdir@
-
-include $(DEPTH)/config/autoconf.mk
-
-LIBRARY_NAME     = domfm_s
-include $(topsrcdir)/dom/dom-config.mk
-
-include $(topsrcdir)/config/rules.mk
-include $(topsrcdir)/ipc/chromium/chromium-config.mk
-
deleted file mode 100644
--- a/dom/fm/moz.build
+++ /dev/null
@@ -1,31 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-XPIDL_SOURCES += [
-    'nsIDOMFMRadio.idl',
-    'nsIFMRadio.idl',
-]
-
-XPIDL_MODULE = 'dom_fm'
-
-MODULE = 'dom'
-
-CPP_SOURCES += [
-    'FMRadio.cpp',
-    'nsFMRadioSettings.cpp',
-]
-
-EXTRA_JS_MODULES += [
-    'DOMFMRadioParent.jsm',
-]
-
-EXTRA_COMPONENTS += [
-    'DOMFMRadio.manifest',
-    'DOMFMRadioChild.js',
-]
-
-LIBXUL_LIBRARY = True
-
deleted file mode 100644
--- a/dom/fm/nsFMRadioSettings.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsFMRadioSettings.h"
-
-NS_IMPL_ISUPPORTS1(nsFMRadioSettings, nsIFMRadioSettings)
-
-nsFMRadioSettings::nsFMRadioSettings(int32_t aUpperLimit,
-                                     int32_t aLowerLimit,
-                                     int32_t aChannelWidth)
-{
-  mUpperLimit = aUpperLimit;
-  mLowerLimit = aLowerLimit;
-  mChannelWidth  = aChannelWidth;
-}
-
-nsFMRadioSettings::~nsFMRadioSettings()
-{
-
-}
-
-/* attribute long upperLimit; */
-NS_IMETHODIMP nsFMRadioSettings::GetUpperLimit(int32_t *aUpperLimit)
-{
-  *aUpperLimit = mUpperLimit;
-  return NS_OK;
-}
-
-NS_IMETHODIMP nsFMRadioSettings::SetUpperLimit(int32_t aUpperLimit)
-{
-  mUpperLimit = aUpperLimit;
-  return NS_OK;
-}
-
-/* attribute long lowerLimit; */
-NS_IMETHODIMP nsFMRadioSettings::GetLowerLimit(int32_t *aLowerLimit)
-{
-  *aLowerLimit = mLowerLimit;
-  return NS_OK;
-}
-
-NS_IMETHODIMP nsFMRadioSettings::SetLowerLimit(int32_t aLowerLimit)
-{
-  mLowerLimit = aLowerLimit;
-  return NS_OK;
-}
-
-/* attribute long spaceType; */
-NS_IMETHODIMP nsFMRadioSettings::GetChannelWidth(int32_t *aChannelWidth)
-{
-  *aChannelWidth = mChannelWidth;
-  return NS_OK;
-}
-
-NS_IMETHODIMP nsFMRadioSettings::SetChannelWidth(int32_t aChannelWidth)
-{
-  mChannelWidth = aChannelWidth;
-  return NS_OK;
-}
-
deleted file mode 100644
--- a/dom/fm/nsFMRadioSettings.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
-/* vim: set ts=2 et sw=2 tw=40: */
-/* 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_fm_radio_settings_h__
-#define mozilla_dom_fm_radio_settings_h__
-
-#include "nsIFMRadio.h"
-
-class nsFMRadioSettings : public nsIFMRadioSettings
-{
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIFMRADIOSETTINGS
-
-  nsFMRadioSettings(int32_t aUpperLimit, int32_t aLowerLimit, int32_t aChannelWidth);
-private:
-  ~nsFMRadioSettings();
-  int32_t mUpperLimit;
-  int32_t mLowerLimit;
-  int32_t mChannelWidth;
-};
-#endif
-
deleted file mode 100644
--- a/dom/fm/nsIDOMFMRadio.idl
+++ /dev/null
@@ -1,122 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsISupports.idl"
-#include "nsIDOMDOMRequest.idl"
-
-[scriptable, uuid(1d0443f3-ac30-4f9e-a070-002bb20ce1e6)]
-interface nsIDOMFMRadio : nsISupports {
-    /* Indicates if the FM radio is enabled. */
-    readonly attribute boolean enabled;
-
-    /* Indicates if the antenna is plugged and available. */
-    readonly attribute boolean antennaAvailable;
-
-    /**
-     * Current frequency in MHz.
-     * The value will be null if the FM radio is disabled.
-     */
-    readonly attribute jsval frequency;
-
-    /* The upper bound of frequency in MHz. */
-    readonly attribute double frequencyUpperBound;
-
-    /* The lower bound of frequency in MHz. */
-    readonly attribute double frequencyLowerBound;
-
-    /**
-     * The channel width of the ranges of frequency, in MHz.
-     * Usually, the value is one of:
-     *  - 0.05 MHz
-     *  - 0.1  MHz
-     *  - 0.2  MHz
-     */
-    readonly attribute double channelWidth;
-
-    /* Fired when the FM radio is enabled. */
-    attribute nsIDOMEventListener onenabled;
-
-    /* Fired when the FM radio is disabled. */
-    attribute nsIDOMEventListener ondisabled;
-
-    /**
-     * Fired when the antenna becomes available or unavailable, i.e., fired when
-     * the antennaAvailable attribute changes.
-     */
-    attribute nsIDOMEventListener onantennaavailablechange;
-
-    /* Fired when the FM radio's frequency is changed. */
-    attribute nsIDOMEventListener onfrequencychange;
-
-    /**
-     * Power the FM radio off.
-     * The disabled event will be fired if this request completes successfully.
-     */
-    nsIDOMDOMRequest disable();
-
-    /**
-     * Power the FM radio on, and tune the radio to the given frequency in MHz.
-     * This will fail if the given frequency is out of range.
-     * The enabled event and frequencychange event will be fired if this request
-     * completes successfully.
-     */
-    nsIDOMDOMRequest enable(in double frequency);
-
-    /**
-     * Tune the FM radio to the given frequency.
-     * This will fail if the given frequency is out of range.
-     *
-     * Note that the FM radio may not tuned to the exact frequency given. To get
-     * the frequency the radio is actually tuned to, wait for the request to fire
-     * onsucess (or wait for the frequencychange event to fire), and then read the
-     * frequency attribute.
-     */
-    nsIDOMDOMRequest setFrequency(in double frequency);
-
-    /**
-     * Tell the FM radio to seek up to the next channel. If the frequency is
-     * successfully changed, the frequencychange event will be triggered.
-     *
-     * Only one seek is allowed at once:
-     * If the radio is seeking when the seekUp is called, onerror will be fired.
-     */
-    nsIDOMDOMRequest seekUp();
-
-    /**
-     * Tell the FM radio to seek down to the next channel. If the frequency is
-     * successfully changed, the frequencychange event will be triggered.
-     *
-     * Only one seek is allowed at once:
-     * If the radio is seeking when the seekDown is called, onerror will be fired.
-     */
-    nsIDOMDOMRequest seekDown();
-
-    /**
-     * Cancel the seek action.
-     * If the radio is not currently seeking up or down, onerror will be fired.
-     */
-    nsIDOMDOMRequest cancelSeek();
-
-
-    /**
-     * These functions related to EventTarget are temporary hacks:
-     *   - addEventListener
-     *   - removeEventListener
-     *   - handleEvent
-     *
-     * These will be removed by inheriting from nsIJSDOMEventTarget,
-     * see bug 731746.
-     */
-    [optional_argc] void addEventListener(in DOMString type,
-                                          in nsIDOMEventListener listener,
-                                          [optional] in boolean useCapture,
-                                          [optional] in boolean wantsUntrusted);
-
-    void  removeEventListener(in DOMString type,
-                              in nsIDOMEventListener listener,
-                              [optional] in boolean useCapture);
-
-    boolean dispatchEvent(in nsIDOMEvent evt) raises(DOMException);
-};
-
deleted file mode 100644
--- a/dom/fm/nsIFMRadio.idl
+++ /dev/null
@@ -1,107 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsIDOMEventTarget.idl"
-
-[scriptable, uuid(c142387a-5488-454b-8b5a-91f0dbee833b)]
-interface nsIFMRadioSettings : nsISupports
-{
-    /* Upper limit in KHz */
-    attribute long upperLimit;
-    /* Lower limit in KHz */
-    attribute long lowerLimit;
-    /* Channel width in KHz */
-    attribute long channelWidth;
-};
-
-/**
- * This is an interface to expose the FM radio hardware related functions;
- * it's kind of the FM radio hardware wrapper interface.
- *
- * Because the WebFM API (navigator.mozFMRadio) is implemented as a JS component,
- * it can't access our C++ hardware interface directly; instead it must go
- * through this interface.
- * Do not confuse this interface with the WebFM DOM interface (nsIDOMFMRadio).
- *
- * If the WebFM API is re-written in c++ some day, this interface will be useless.
- */
-[scriptable, builtinclass, uuid(2ee7c122-b7aa-4948-9bc5-e4593ed4ac32)]
-interface nsIFMRadio : nsIDOMEventTarget {
-    const long SEEK_DIRECTION_UP   = 0;
-    const long SEEK_DIRECTION_DOWN = 1;
-
-    /**
-     * Indicates if the FM radio hardware is enabled.
-     */
-    readonly attribute boolean enabled;
-
-    /**
-     * Current frequency in KHz
-     */
-    readonly attribute long frequency;
-
-    /**
-     * Indicates if the antenna is plugged in and available.
-     */
-    readonly attribute boolean isAntennaAvailable;
-
-    /**
-     * Enable the FM radio hardware with the given settings.
-     */
-    void enable(in nsIFMRadioSettings settings);
-
-    /**
-     * Disable the FM radio hardware.
-     */
-    void disable();
-
-    /**
-     * Seek the next available channel (up or down).
-     *
-     * @param direction
-     *   The value should be one of SEEK_DIRECTION_DOWN and SEEK_DIRECTION_UP
-     */
-    void seek(in long direction);
-
-    /**
-     * Cancel the seek action.
-     */
-    void cancelSeek();
-
-    /**
-     * Get the current settings.
-     */
-    nsIFMRadioSettings getSettings();
-
-    /**
-     * Set the frequency in KHz
-     */
-    void setFrequency(in long frequency);
-
-    /**
-     * Update the visibility state of our client.
-     */
-    void updateVisible(in boolean visible);
-
-    /**
-     * Fired when the antenna state is changed.
-     */
-    [implicit_jscontext] attribute jsval onantennastatechange;
-
-    /**
-     * Fired when a seek action completes.
-     */
-    [implicit_jscontext] attribute jsval onseekcomplete;
-
-    /**
-     * Fired when the FM radio hardware is enabled.
-     */
-    [implicit_jscontext] attribute jsval onenabled;
-
-    /**
-     * Fired when the FM radio hardware is disabled.
-     */
-    [implicit_jscontext] attribute jsval ondisabled;
-};
-
new file mode 100644
--- /dev/null
+++ b/dom/fmradio/FMRadio.cpp
@@ -0,0 +1,295 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/FMRadio.h"
+#include "nsContentUtils.h"
+#include "mozilla/Hal.h"
+#include "mozilla/HalTypes.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/dom/FMRadioBinding.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/PFMRadioChild.h"
+#include "mozilla/dom/FMRadioService.h"
+#include "DOMRequest.h"
+
+#undef LOG
+#define LOG(args...) FM_LOG("FMRadio", args)
+
+// The pref indicates if the device has an internal antenna.
+// If the pref is true, the antanna will be always available.
+#define DOM_FM_ANTENNA_INTERNAL_PREF "dom.fmradio.antenna.internal"
+
+using namespace mozilla::hal;
+using mozilla::Preferences;
+
+BEGIN_FMRADIO_NAMESPACE
+
+class FMRadioRequest MOZ_FINAL : public ReplyRunnable
+                               , public DOMRequest
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+
+  FMRadioRequest(nsPIDOMWindow* aWindow, FMRadio* aFMRadio)
+    : DOMRequest(aWindow)
+  {
+    // |FMRadio| inherits from |nsIDOMEventTarget| and |nsISupportsWeakReference|
+    // which both inherits from nsISupports, so |nsISupports| is an ambiguous
+    // base of |FMRadio|, we have to cast |aFMRadio| to one of the base classes.
+    mFMRadio = do_GetWeakReference(static_cast<nsIDOMEventTarget*>(aFMRadio));
+  }
+
+  ~FMRadioRequest() { }
+
+  NS_IMETHODIMP
+  Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+    nsCOMPtr<nsIDOMEventTarget> target = do_QueryReferent(mFMRadio);
+    if (!target) {
+      return NS_OK;
+    }
+
+    FMRadio* fmRadio = static_cast<FMRadio*>(
+      static_cast<nsIDOMEventTarget*>(target));
+
+    if (fmRadio->mIsShutdown) {
+      return NS_OK;
+    }
+
+    switch (mResponseType.type()) {
+      case FMRadioResponseType::TErrorResponse:
+        FireError(mResponseType.get_ErrorResponse().error());
+        break;
+      case FMRadioResponseType::TSuccessResponse:
+        FireSuccess(JS::UndefinedHandleValue);
+        break;
+      default:
+        MOZ_CRASH();
+    }
+
+    return NS_OK;
+  }
+
+private:
+  nsWeakPtr mFMRadio;
+};
+
+NS_IMPL_ISUPPORTS_INHERITED0(FMRadioRequest, DOMRequest)
+
+FMRadio::FMRadio()
+  : mHeadphoneState(SWITCH_STATE_OFF)
+  , mHasInternalAntenna(false)
+  , mIsShutdown(false)
+{
+  LOG("FMRadio is initialized.");
+
+  SetIsDOMBinding();
+}
+
+FMRadio::~FMRadio()
+{
+}
+
+void
+FMRadio::Init(nsPIDOMWindow *aWindow)
+{
+  BindToOwner(aWindow);
+
+  IFMRadioService::Singleton()->AddObserver(this);
+
+  mHasInternalAntenna = Preferences::GetBool(DOM_FM_ANTENNA_INTERNAL_PREF,
+                                             /* default = */ false);
+  if (mHasInternalAntenna) {
+    LOG("We have an internal antenna.");
+  } else {
+    mHeadphoneState = GetCurrentSwitchState(SWITCH_HEADPHONES);
+    RegisterSwitchObserver(SWITCH_HEADPHONES, this);
+  }
+}
+
+void
+FMRadio::Shutdown()
+{
+  IFMRadioService::Singleton()->RemoveObserver(this);
+
+  if (!mHasInternalAntenna) {
+    UnregisterSwitchObserver(SWITCH_HEADPHONES, this);
+  }
+
+  mIsShutdown = true;
+}
+
+JSObject*
+FMRadio::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
+{
+  return FMRadioBinding::Wrap(aCx, aScope, this);
+}
+
+void
+FMRadio::Notify(const SwitchEvent& aEvent)
+{
+  MOZ_ASSERT(!mHasInternalAntenna);
+
+  if (mHeadphoneState != aEvent.status()) {
+    mHeadphoneState = aEvent.status();
+
+    DispatchTrustedEvent(NS_LITERAL_STRING("antennaavailablechange"));
+  }
+}
+
+void
+FMRadio::Notify(const FMRadioEventType& aType)
+{
+  switch (aType) {
+    case FrequencyChanged:
+      DispatchTrustedEvent(NS_LITERAL_STRING("frequencychange"));
+      break;
+    case EnabledChanged:
+      if (Enabled()) {
+        DispatchTrustedEvent(NS_LITERAL_STRING("enabled"));
+      } else {
+        DispatchTrustedEvent(NS_LITERAL_STRING("disabled"));
+      }
+      break;
+    default:
+      MOZ_CRASH();
+  }
+}
+
+/* static */
+bool
+FMRadio::Enabled()
+{
+  return IFMRadioService::Singleton()->IsEnabled();
+}
+
+bool
+FMRadio::AntennaAvailable() const
+{
+  return mHasInternalAntenna ? true : mHeadphoneState != SWITCH_STATE_OFF;
+}
+
+Nullable<double>
+FMRadio::GetFrequency() const
+{
+  return Enabled() ?
+    Nullable<double>(IFMRadioService::Singleton()->GetFrequency()) :
+    Nullable<double>();
+}
+
+double
+FMRadio::FrequencyUpperBound() const
+{
+  return IFMRadioService::Singleton()->GetFrequencyUpperBound();
+}
+
+double
+FMRadio::FrequencyLowerBound() const
+{
+  return IFMRadioService::Singleton()->GetFrequencyLowerBound();
+}
+
+double
+FMRadio::ChannelWidth() const
+{
+  return IFMRadioService::Singleton()->GetChannelWidth();
+}
+
+already_AddRefed<DOMRequest>
+FMRadio::Enable(double aFrequency)
+{
+  nsCOMPtr<nsPIDOMWindow> win = GetOwner();
+  if (!win) {
+    return nullptr;
+  }
+
+  nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
+  IFMRadioService::Singleton()->Enable(aFrequency, r);
+
+  return r.forget();
+}
+
+already_AddRefed<DOMRequest>
+FMRadio::Disable()
+{
+  nsCOMPtr<nsPIDOMWindow> win = GetOwner();
+  if (!win) {
+    return nullptr;
+  }
+
+  nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
+  IFMRadioService::Singleton()->Disable(r);
+
+  return r.forget();
+}
+
+already_AddRefed<DOMRequest>
+FMRadio::SetFrequency(double aFrequency)
+{
+  nsCOMPtr<nsPIDOMWindow> win = GetOwner();
+  if (!win) {
+    return nullptr;
+  }
+
+  nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
+  IFMRadioService::Singleton()->SetFrequency(aFrequency, r);
+
+  return r.forget();
+}
+
+already_AddRefed<DOMRequest>
+FMRadio::SeekUp()
+{
+  nsCOMPtr<nsPIDOMWindow> win = GetOwner();
+  if (!win) {
+    return nullptr;
+  }
+
+  nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
+  IFMRadioService::Singleton()->Seek(FM_RADIO_SEEK_DIRECTION_UP, r);
+
+  return r.forget();
+}
+
+already_AddRefed<DOMRequest>
+FMRadio::SeekDown()
+{
+  nsCOMPtr<nsPIDOMWindow> win = GetOwner();
+  if (!win) {
+    return nullptr;
+  }
+
+  nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
+  IFMRadioService::Singleton()->Seek(FM_RADIO_SEEK_DIRECTION_DOWN, r);
+
+  return r.forget();
+}
+
+already_AddRefed<DOMRequest>
+FMRadio::CancelSeek()
+{
+  nsCOMPtr<nsPIDOMWindow> win = GetOwner();
+  if (!win) {
+    return nullptr;
+  }
+
+  nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
+  IFMRadioService::Singleton()->CancelSeek(r);
+
+  return r.forget();
+}
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FMRadio)
+  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
+
+NS_IMPL_ADDREF_INHERITED(FMRadio, nsDOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(FMRadio, nsDOMEventTargetHelper)
+
+END_FMRADIO_NAMESPACE
+
new file mode 100644
--- /dev/null
+++ b/dom/fmradio/FMRadio.h
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_FMRadio_h
+#define mozilla_dom_FMRadio_h
+
+#include "FMRadioCommon.h"
+#include "nsDOMEventTargetHelper.h"
+#include "nsCycleCollectionParticipant.h"
+#include "mozilla/HalTypes.h"
+#include "nsWeakReference.h"
+
+class nsPIDOMWindow;
+class nsIScriptContext;
+
+BEGIN_FMRADIO_NAMESPACE
+
+class DOMRequest;
+
+class FMRadio MOZ_FINAL : public nsDOMEventTargetHelper
+                        , public hal::SwitchObserver
+                        , public FMRadioEventObserver
+                        , public nsSupportsWeakReference
+{
+  friend class FMRadioRequest;
+
+public:
+  FMRadio();
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+  NS_REALLY_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper)
+
+  void Init(nsPIDOMWindow *aWindow);
+  void Shutdown();
+
+  /* hal::SwitchObserver */
+  virtual void Notify(const hal::SwitchEvent& aEvent) MOZ_OVERRIDE;
+  /* FMRadioEventObserver */
+  virtual void Notify(const FMRadioEventType& aType) MOZ_OVERRIDE;
+
+  nsPIDOMWindow* GetParentObject() const
+  {
+    return GetOwner();
+  }
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+
+  static bool Enabled();
+
+  bool AntennaAvailable() const;
+
+  Nullable<double> GetFrequency() const;
+
+  double FrequencyUpperBound() const;
+
+  double FrequencyLowerBound() const;
+
+  double ChannelWidth() const;
+
+  already_AddRefed<DOMRequest> Enable(double aFrequency);
+
+  already_AddRefed<DOMRequest> Disable();
+
+  already_AddRefed<DOMRequest> SetFrequency(double aFrequency);
+
+  already_AddRefed<DOMRequest> SeekUp();
+
+  already_AddRefed<DOMRequest> SeekDown();
+
+  already_AddRefed<DOMRequest> CancelSeek();
+
+  IMPL_EVENT_HANDLER(enabled);
+  IMPL_EVENT_HANDLER(disabled);
+  IMPL_EVENT_HANDLER(antennaavailablechange);
+  IMPL_EVENT_HANDLER(frequencychange);
+
+private:
+  ~FMRadio();
+
+  hal::SwitchState mHeadphoneState;
+  bool mHasInternalAntenna;
+  bool mIsShutdown;
+};
+
+END_FMRADIO_NAMESPACE
+
+#endif // mozilla_dom_FMRadio_h
+
new file mode 100644
--- /dev/null
+++ b/dom/fmradio/FMRadioCommon.h
@@ -0,0 +1,42 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=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 FMRADIOCOMMON_H_
+#define FMRADIOCOMMON_H_
+
+#include "mozilla/Observer.h"
+
+#undef FM_LOG
+#if defined(ANDROID)
+#include <android/log.h>
+#define FM_LOG(FMRADIO_LOG_INFO, args...) \
+  __android_log_print(ANDROID_LOG_INFO, \
+                      FMRADIO_LOG_INFO, \
+                      ## args)
+#else
+#define FM_LOG(args...)
+#endif
+
+#define BEGIN_FMRADIO_NAMESPACE \
+  namespace mozilla { namespace dom {
+#define END_FMRADIO_NAMESPACE \
+  } /* namespace dom */ } /* namespace mozilla */
+
+BEGIN_FMRADIO_NAMESPACE
+
+enum FMRadioEventType
+{
+  FrequencyChanged,
+  EnabledChanged
+};
+
+typedef mozilla::Observer<FMRadioEventType>     FMRadioEventObserver;
+typedef mozilla::ObserverList<FMRadioEventType> FMRadioEventObserverList;
+
+END_FMRADIO_NAMESPACE
+
+#endif /* FMRADIOCOMMON_H_ */
+
new file mode 100644
--- /dev/null
+++ b/dom/fmradio/FMRadioService.cpp
@@ -0,0 +1,800 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=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 "FMRadioService.h"
+#include "mozilla/Hal.h"
+#include "nsIAudioManager.h"
+#include "AudioManager.h"
+#include "nsDOMClassInfo.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/dom/FMRadioChild.h"
+#include "nsIObserverService.h"
+#include "nsISettingsService.h"
+#include "nsJSUtils.h"
+#include "nsCxPusher.h"
+
+#define BAND_87500_108000_kHz 1
+#define BAND_76000_108000_kHz 2
+#define BAND_76000_90000_kHz  3
+
+#define CHANNEL_WIDTH_200KHZ 200
+#define CHANNEL_WIDTH_100KHZ 100
+#define CHANNEL_WIDTH_50KHZ  50
+
+#define MOZSETTINGS_CHANGED_ID "mozsettings-changed"
+#define SETTING_KEY_RIL_RADIO_DISABLED "ril.radio.disabled"
+
+using namespace mozilla::hal;
+using mozilla::Preferences;
+
+BEGIN_FMRADIO_NAMESPACE
+
+// static
+IFMRadioService*
+IFMRadioService::Singleton()
+{
+  if (XRE_GetProcessType() != GeckoProcessType_Default) {
+    return FMRadioChild::Singleton();
+  } else {
+    return FMRadioService::Singleton();
+  }
+}
+
+StaticRefPtr<FMRadioService> FMRadioService::sFMRadioService;
+
+FMRadioService::FMRadioService()
+  : mPendingFrequencyInKHz(0)
+  , mState(Disabled)
+  , mHasReadRilSetting(false)
+  , mRilDisabled(false)
+  , mPendingRequest(nullptr)
+  , mObserverList(FMRadioEventObserverList())
+{
+
+  // Read power state and frequency from Hal.
+  mEnabled = IsFMRadioOn();
+  if (mEnabled) {
+    mPendingFrequencyInKHz = GetFMRadioFrequency();
+    SetState(Enabled);
+  }
+
+  switch (Preferences::GetInt("dom.fmradio.band", BAND_87500_108000_kHz)) {
+    case BAND_76000_90000_kHz:
+      mUpperBoundInKHz = 90000;
+      mLowerBoundInKHz = 76000;
+      break;
+    case BAND_76000_108000_kHz:
+      mUpperBoundInKHz = 108000;
+      mLowerBoundInKHz = 76000;
+      break;
+    case BAND_87500_108000_kHz:
+    default:
+      mUpperBoundInKHz = 108000;
+      mLowerBoundInKHz = 87500;
+      break;
+  }
+
+  switch (Preferences::GetInt("dom.fmradio.channelWidth",
+                              CHANNEL_WIDTH_100KHZ)) {
+    case CHANNEL_WIDTH_200KHZ:
+      mChannelWidthInKHz = 200;
+      break;
+    case CHANNEL_WIDTH_50KHZ:
+      mChannelWidthInKHz = 50;
+      break;
+    case CHANNEL_WIDTH_100KHZ:
+    default:
+      mChannelWidthInKHz = 100;
+      break;
+  }
+
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+
+  if (NS_FAILED(obs->AddObserver(this,
+                                 MOZSETTINGS_CHANGED_ID,
+                                 /* useWeak */ false))) {
+    NS_WARNING("Failed to add settings change observer!");
+  }
+
+  RegisterFMRadioObserver(this);
+}
+
+FMRadioService::~FMRadioService()
+{
+  UnregisterFMRadioObserver(this);
+}
+
+class EnableRunnable MOZ_FINAL : public nsRunnable
+{
+public:
+  EnableRunnable(int32_t aUpperLimit, int32_t aLowerLimit, int32_t aSpaceType)
+    : mUpperLimit(aUpperLimit)
+    , mLowerLimit(aLowerLimit)
+    , mSpaceType(aSpaceType) { }
+
+  NS_IMETHOD Run()
+  {
+    FMRadioSettings info;
+    info.upperLimit() = mUpperLimit;
+    info.lowerLimit() = mLowerLimit;
+    info.spaceType() = mSpaceType;
+
+    EnableFMRadio(info);
+
+    nsCOMPtr<nsIAudioManager> audioManager =
+      do_GetService(NS_AUDIOMANAGER_CONTRACTID);
+    audioManager->SetFmRadioAudioEnabled(true);
+
+    // TODO apply path from bug 862899: AudioChannelAgent per process
+    return NS_OK;
+  }
+
+private:
+  int32_t mUpperLimit;
+  int32_t mLowerLimit;
+  int32_t mSpaceType;
+};
+
+/**
+ * Read the airplane-mode setting, if the airplane-mode is not enabled, we
+ * enable the FM radio.
+ */
+class ReadRilSettingTask MOZ_FINAL : public nsISettingsServiceCallback
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  ReadRilSettingTask(nsRefPtr<ReplyRunnable> aPendingRequest)
+    : mPendingRequest(aPendingRequest) { }
+
+  NS_IMETHOD
+  Handle(const nsAString& aName, const JS::Value& aResult)
+  {
+    FMRadioService* fmRadioService = FMRadioService::Singleton();
+    MOZ_ASSERT(mPendingRequest == fmRadioService->mPendingRequest);
+
+    fmRadioService->mHasReadRilSetting = true;
+
+    if (!aResult.isBoolean()) {
+      // Failed to read the setting value, set the state back to Disabled.
+      fmRadioService->TransitionState(
+        ErrorResponse(NS_LITERAL_STRING("Unexpected error")), Disabled);
+      return NS_OK;
+    }
+
+    fmRadioService->mRilDisabled = aResult.toBoolean();
+    if (!fmRadioService->mRilDisabled) {
+      EnableRunnable* runnable =
+        new EnableRunnable(fmRadioService->mUpperBoundInKHz,
+                           fmRadioService->mLowerBoundInKHz,
+                           fmRadioService->mChannelWidthInKHz);
+      NS_DispatchToMainThread(runnable);
+    } else {
+      // Airplane mode is enabled, set the state back to Disabled.
+      fmRadioService->TransitionState(ErrorResponse(
+        NS_LITERAL_STRING("Airplane mode currently enabled")), Disabled);
+    }
+
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  HandleError(const nsAString& aName)
+  {
+    FMRadioService* fmRadioService = FMRadioService::Singleton();
+    MOZ_ASSERT(mPendingRequest == fmRadioService->mPendingRequest);
+
+    fmRadioService->TransitionState(ErrorResponse(
+      NS_LITERAL_STRING("Unexpected error")), Disabled);
+
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<ReplyRunnable> mPendingRequest;
+};
+
+NS_IMPL_ISUPPORTS1(ReadRilSettingTask, nsISettingsServiceCallback)
+
+class DisableRunnable MOZ_FINAL : public nsRunnable
+{
+public:
+  DisableRunnable() { }
+
+  NS_IMETHOD Run()
+  {
+    // Fix Bug 796733. DisableFMRadio should be called before
+    // SetFmRadioAudioEnabled to prevent the annoying beep sound.
+    DisableFMRadio();
+
+    nsCOMPtr<nsIAudioManager> audioManager =
+      do_GetService(NS_AUDIOMANAGER_CONTRACTID);
+
+    audioManager->SetFmRadioAudioEnabled(false);
+
+    return NS_OK;
+  }
+};
+
+class SetFrequencyRunnable MOZ_FINAL : public nsRunnable
+{
+public:
+  SetFrequencyRunnable(int32_t aFrequency)
+    : mFrequency(aFrequency) { }
+
+  NS_IMETHOD Run()
+  {
+    SetFMRadioFrequency(mFrequency);
+
+    FMRadioService* fmRadioService = FMRadioService::Singleton();
+    fmRadioService->UpdateFrequency();
+
+    return NS_OK;
+  }
+
+private:
+  int32_t mFrequency;
+};
+
+class SeekRunnable MOZ_FINAL : public nsRunnable
+{
+public:
+  SeekRunnable(FMRadioSeekDirection aDirection) : mDirection(aDirection) { }
+
+  NS_IMETHOD Run()
+  {
+    switch (mDirection) {
+      case FM_RADIO_SEEK_DIRECTION_UP:
+      case FM_RADIO_SEEK_DIRECTION_DOWN:
+        FMRadioSeek(mDirection);
+        break;
+      default:
+        MOZ_CRASH();
+    }
+
+    return NS_OK;
+  }
+
+private:
+  FMRadioSeekDirection mDirection;
+};
+
+void
+FMRadioService::TransitionState(const FMRadioResponseType& aResponse,
+                                FMRadioState aState)
+{
+  if (mPendingRequest) {
+    mPendingRequest->SetReply(aResponse);
+    NS_DispatchToMainThread(mPendingRequest);
+  }
+
+  SetState(aState);
+}
+
+void
+FMRadioService::SetState(FMRadioState aState)
+{
+  mState = aState;
+  mPendingRequest = nullptr;
+}
+
+void
+FMRadioService::AddObserver(FMRadioEventObserver* aObserver)
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  mObserverList.AddObserver(aObserver);
+}
+
+void
+FMRadioService::RemoveObserver(FMRadioEventObserver* aObserver)
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  mObserverList.RemoveObserver(aObserver);
+
+  if (mObserverList.Length() == 0)
+  {
+    // Turning off the FM radio HW because observer list is empty.
+    if (IsFMRadioOn()) {
+      NS_DispatchToMainThread(new DisableRunnable());
+    }
+  }
+}
+
+/**
+ * Round the frequency to match the range of frequency and the channel width. If
+ * the given frequency is out of range, return 0. For example:
+ *  - lower: 87500KHz, upper: 108000KHz, channel width: 200KHz
+ *    87.6MHz is rounded to 87700KHz
+ *    87.58MHz is rounded to 87500KHz
+ *    87.49MHz is rounded to 87500KHz
+ *    109MHz is not rounded, 0 will be returned
+ *
+ * We take frequency in MHz to prevent precision losing, and return rounded
+ * value in KHz for Gonk using.
+ */
+int32_t
+FMRadioService::RoundFrequency(double aFrequencyInMHz)
+{
+  double halfChannelWidthInMHz = mChannelWidthInKHz / 1000.0 / 2;
+
+  // Make sure 87.49999MHz would be rounded to the lower bound when
+  // the lower bound is 87500KHz.
+  if (aFrequencyInMHz < mLowerBoundInKHz / 1000.0 - halfChannelWidthInMHz ||
+      aFrequencyInMHz > mUpperBoundInKHz / 1000.0 + halfChannelWidthInMHz) {
+    return 0;
+  }
+
+  int32_t partToBeRounded = round(aFrequencyInMHz * 1000) - mLowerBoundInKHz;
+  int32_t roundedPart = round(partToBeRounded / (double)mChannelWidthInKHz) *
+                        mChannelWidthInKHz;
+
+  return mLowerBoundInKHz + roundedPart;
+}
+
+bool
+FMRadioService::IsEnabled() const
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  return IsFMRadioOn();
+}
+
+double
+FMRadioService::GetFrequency() const
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  if (IsEnabled()) {
+    int32_t frequencyInKHz = GetFMRadioFrequency();
+    return frequencyInKHz / 1000.0;
+  }
+
+  return 0;
+}
+
+double
+FMRadioService::GetFrequencyUpperBound() const
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  return mUpperBoundInKHz / 1000.0;
+}
+
+double
+FMRadioService::GetFrequencyLowerBound() const
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  return mLowerBoundInKHz / 1000.0;
+}
+
+double
+FMRadioService::GetChannelWidth() const
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  return mChannelWidthInKHz / 1000.0;
+}
+
+void
+FMRadioService::Enable(double aFrequencyInMHz, ReplyRunnable* aReplyRunnable)
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(aReplyRunnable);
+
+  switch (mState) {
+    case Seeking:
+    case Enabled:
+      aReplyRunnable->SetReply(
+        ErrorResponse(NS_LITERAL_STRING("FM radio currently enabled")));
+      NS_DispatchToMainThread(aReplyRunnable);
+      return;
+    case Disabling:
+      aReplyRunnable->SetReply(
+        ErrorResponse(NS_LITERAL_STRING("FM radio currently disabling")));
+      NS_DispatchToMainThread(aReplyRunnable);
+      return;
+    case Enabling:
+      aReplyRunnable->SetReply(
+        ErrorResponse(NS_LITERAL_STRING("FM radio currently enabling")));
+      NS_DispatchToMainThread(aReplyRunnable);
+      return;
+    case Disabled:
+      break;
+  }
+
+  int32_t roundedFrequency = RoundFrequency(aFrequencyInMHz);
+
+  if (!roundedFrequency) {
+    aReplyRunnable->SetReply(ErrorResponse(
+      NS_LITERAL_STRING("Frequency is out of range")));
+    NS_DispatchToMainThread(aReplyRunnable);
+    return;
+  }
+
+  if (mHasReadRilSetting && mRilDisabled) {
+    aReplyRunnable->SetReply(ErrorResponse(
+      NS_LITERAL_STRING("Airplane mode currently enabled")));
+    NS_DispatchToMainThread(aReplyRunnable);
+    return;
+  }
+
+  SetState(Enabling);
+  // Cache the enable request just in case disable() is called
+  // while the FM radio HW is being enabled.
+  mPendingRequest = aReplyRunnable;
+
+  // Cache the frequency value, and set it after the FM radio HW is enabled
+  mPendingFrequencyInKHz = roundedFrequency;
+
+  if (!mHasReadRilSetting) {
+    nsCOMPtr<nsISettingsService> settings =
+      do_GetService("@mozilla.org/settingsService;1");
+
+    nsCOMPtr<nsISettingsServiceLock> settingsLock;
+    nsresult rv = settings->CreateLock(getter_AddRefs(settingsLock));
+    if (NS_FAILED(rv)) {
+      TransitionState(ErrorResponse(
+        NS_LITERAL_STRING("Can't create settings lock")), Disabled);
+      return;
+    }
+
+    nsRefPtr<ReadRilSettingTask> callback =
+      new ReadRilSettingTask(mPendingRequest);
+
+    rv = settingsLock->Get(SETTING_KEY_RIL_RADIO_DISABLED, callback);
+    if (NS_FAILED(rv)) {
+      TransitionState(ErrorResponse(
+        NS_LITERAL_STRING("Can't get settings lock")), Disabled);
+    }
+
+    return;
+  }
+
+  NS_DispatchToMainThread(new EnableRunnable(mUpperBoundInKHz,
+                                             mLowerBoundInKHz,
+                                             mChannelWidthInKHz));
+}
+
+void
+FMRadioService::Disable(ReplyRunnable* aReplyRunnable)
+{
+  // When airplane-mode is enabled, we will call this function from
+  // FMRadioService::Observe without passing a ReplyRunnable, so we have to
+  // check if |aReplyRunnable| is null before we dispatch it.
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+  switch (mState) {
+    case Disabling:
+      if (aReplyRunnable) {
+        aReplyRunnable->SetReply(
+          ErrorResponse(NS_LITERAL_STRING("FM radio currently disabling")));
+        NS_DispatchToMainThread(aReplyRunnable);
+      }
+      return;
+    case Disabled:
+      if (aReplyRunnable) {
+        aReplyRunnable->SetReply(
+          ErrorResponse(NS_LITERAL_STRING("FM radio currently disabled")));
+        NS_DispatchToMainThread(aReplyRunnable);
+      }
+      return;
+    case Enabled:
+    case Enabling:
+    case Seeking:
+      break;
+  }
+
+  nsRefPtr<ReplyRunnable> enablingRequest = mPendingRequest;
+
+  // If the FM Radio is currently seeking, no fail-to-seek or similar
+  // event will be fired, execute the seek callback manually.
+  if (mState == Seeking) {
+    TransitionState(ErrorResponse(
+      NS_LITERAL_STRING("Seek action is cancelled")), Disabling);
+  }
+
+  FMRadioState preState = mState;
+  SetState(Disabling);
+  mPendingRequest = aReplyRunnable;
+
+  if (preState == Enabling) {
+    // If the radio is currently enabling, we fire the error callback on the
+    // enable request immediately. When the radio finishes enabling, we'll call
+    // DoDisable and fire the success callback on the disable request.
+    enablingRequest->SetReply(
+      ErrorResponse(NS_LITERAL_STRING("Enable action is cancelled")));
+    NS_DispatchToMainThread(enablingRequest);
+
+    // If we haven't read the ril settings yet we won't enable the FM radio HW,
+    // so fail the disable request immediately.
+    if (!mHasReadRilSetting) {
+      SetState(Disabled);
+
+      if (aReplyRunnable) {
+        aReplyRunnable->SetReply(SuccessResponse());
+        NS_DispatchToMainThread(aReplyRunnable);
+      }
+    }
+
+    return;
+  }
+
+  DoDisable();
+}
+
+void
+FMRadioService::DoDisable()
+{
+  // To make such codes work:
+  //    navigator.mozFMRadio.disable();
+  //    navigator.mozFMRadio.ondisabled = function() {
+  //      console.log("We will catch disabled event ");
+  //    };
+  // we need to call hal::DisableFMRadio() asynchronously. Same reason for
+  // EnableRunnable and SetFrequencyRunnable.
+  NS_DispatchToMainThread(new DisableRunnable());
+}
+
+void
+FMRadioService::SetFrequency(double aFrequencyInMHz,
+                             ReplyRunnable* aReplyRunnable)
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(aReplyRunnable);
+
+  switch (mState) {
+    case Disabled:
+      aReplyRunnable->SetReply(
+        ErrorResponse(NS_LITERAL_STRING("FM radio currently disabled")));
+      NS_DispatchToMainThread(aReplyRunnable);
+      return;
+    case Enabling:
+      aReplyRunnable->SetReply(
+        ErrorResponse(NS_LITERAL_STRING("FM radio currently enabling")));
+      NS_DispatchToMainThread(aReplyRunnable);
+      return;
+    case Disabling:
+      aReplyRunnable->SetReply(
+        ErrorResponse(NS_LITERAL_STRING("FM radio currently disabling")));
+      NS_DispatchToMainThread(aReplyRunnable);
+      return;
+    case Seeking:
+      CancelFMRadioSeek();
+      TransitionState(ErrorResponse(
+        NS_LITERAL_STRING("Seek action is cancelled")), Enabled);
+      break;
+    case Enabled:
+      break;
+  }
+
+  int32_t roundedFrequency = RoundFrequency(aFrequencyInMHz);
+
+  if (!roundedFrequency) {
+    aReplyRunnable->SetReply(ErrorResponse(
+      NS_LITERAL_STRING("Frequency is out of range")));
+    NS_DispatchToMainThread(aReplyRunnable);
+    return;
+  }
+
+  NS_DispatchToMainThread(new SetFrequencyRunnable(roundedFrequency));
+
+  aReplyRunnable->SetReply(SuccessResponse());
+  NS_DispatchToMainThread(aReplyRunnable);
+}
+
+void
+FMRadioService::Seek(FMRadioSeekDirection aDirection,
+                     ReplyRunnable* aReplyRunnable)
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(aReplyRunnable);
+
+  switch (mState) {
+    case Enabling:
+      aReplyRunnable->SetReply(
+        ErrorResponse(NS_LITERAL_STRING("FM radio currently enabling")));
+      NS_DispatchToMainThread(aReplyRunnable);
+      return;
+    case Disabled:
+      aReplyRunnable->SetReply(
+        ErrorResponse(NS_LITERAL_STRING("FM radio currently disabled")));
+      NS_DispatchToMainThread(aReplyRunnable);
+      return;
+    case Seeking:
+      aReplyRunnable->SetReply(
+        ErrorResponse(NS_LITERAL_STRING("FM radio currently seeking")));
+      NS_DispatchToMainThread(aReplyRunnable);
+      return;
+    case Disabling:
+      aReplyRunnable->SetReply(
+        ErrorResponse(NS_LITERAL_STRING("FM radio currently disabling")));
+      NS_DispatchToMainThread(aReplyRunnable);
+      return;
+    case Enabled:
+      break;
+  }
+
+  SetState(Seeking);
+  mPendingRequest = aReplyRunnable;
+
+  NS_DispatchToMainThread(new SeekRunnable(aDirection));
+}
+
+void
+FMRadioService::CancelSeek(ReplyRunnable* aReplyRunnable)
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(aReplyRunnable);
+
+  // We accept canceling seek request only if it's currently seeking.
+  if (mState != Seeking) {
+    aReplyRunnable->SetReply(
+      ErrorResponse(NS_LITERAL_STRING("FM radio currently not seeking")));
+    NS_DispatchToMainThread(aReplyRunnable);
+    return;
+  }
+
+  // Cancel the seek immediately to prevent it from completing.
+  CancelFMRadioSeek();
+
+  TransitionState(
+    ErrorResponse(NS_LITERAL_STRING("Seek action is cancelled")), Enabled);
+
+  aReplyRunnable->SetReply(SuccessResponse());
+  NS_DispatchToMainThread(aReplyRunnable);
+}
+
+NS_IMETHODIMP
+FMRadioService::Observe(nsISupports * aSubject,
+                        const char * aTopic,
+                        const PRUnichar * aData)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(sFMRadioService);
+
+  if (strcmp(aTopic, MOZSETTINGS_CHANGED_ID) != 0) {
+    return NS_OK;
+  }
+
+  // The string that we're interested in will be a JSON string looks like:
+  //  {"key":"ril.radio.disabled","value":true}
+  AutoSafeJSContext cx;
+  const nsDependentString dataStr(aData);
+  JS::Rooted<JS::Value> val(cx);
+  if (!JS_ParseJSON(cx, dataStr.get(), dataStr.Length(), &val) ||
+      !val.isObject()) {
+    return NS_OK;
+  }
+
+  JSObject& obj(val.toObject());
+  JS::Rooted<JS::Value> key(cx);
+  if (!JS_GetProperty(cx, &obj, "key", &key) ||
+      !key.isString()) {
+    return NS_OK;
+  }
+
+  JS::Rooted<JSString*> jsKey(cx, key.toString());
+  nsDependentJSString keyStr;
+  if (!keyStr.init(cx, jsKey)) {
+    return NS_OK;
+  }
+
+  JS::Rooted<JS::Value> value(cx);
+  if (!JS_GetProperty(cx, &obj, "value", &value)) {
+    return NS_OK;
+  }
+
+  if (keyStr.EqualsLiteral(SETTING_KEY_RIL_RADIO_DISABLED)) {
+    if (!value.isBoolean()) {
+      return NS_OK;
+    }
+
+    mRilDisabled = value.toBoolean();
+    mHasReadRilSetting = true;
+
+    // Disable the FM radio HW if Airplane mode is enabled.
+    if (mRilDisabled) {
+      Disable(nullptr);
+    }
+
+    return NS_OK;
+  }
+
+  return NS_OK;
+}
+
+void
+FMRadioService::NotifyFMRadioEvent(FMRadioEventType aType)
+{
+  mObserverList.Broadcast(aType);
+}
+
+void
+FMRadioService::Notify(const FMRadioOperationInformation& aInfo)
+{
+  switch (aInfo.operation()) {
+    case FM_RADIO_OPERATION_ENABLE:
+      MOZ_ASSERT(IsFMRadioOn());
+      MOZ_ASSERT(mState == Disabling || mState == Enabling);
+
+      // If we're disabling, disable the radio right now.
+      if (mState == Disabling) {
+        DoDisable();
+        return;
+      }
+
+      // Fire success callback on the enable request.
+      TransitionState(SuccessResponse(), Enabled);
+
+      // To make sure the FM app will get the right frequency after the FM
+      // radio is enabled, we have to set the frequency first.
+      SetFMRadioFrequency(mPendingFrequencyInKHz);
+
+      // Update the current frequency without sending the`FrequencyChanged`
+      // event, to make sure the FM app will get the right frequency when the
+      // `EnabledChange` event is sent.
+      mPendingFrequencyInKHz = GetFMRadioFrequency();
+      UpdatePowerState();
+
+      // The frequency was changed from '0' to some meaningful number, so we
+      // should send the `FrequencyChanged` event manually.
+      NotifyFMRadioEvent(FrequencyChanged);
+      break;
+    case FM_RADIO_OPERATION_DISABLE:
+      MOZ_ASSERT(mState == Disabling);
+
+      TransitionState(SuccessResponse(), Disabled);
+      UpdatePowerState();
+      break;
+    case FM_RADIO_OPERATION_SEEK:
+
+      // Seek action might be cancelled by SetFrequency(), we need to check if
+      // the current state is Seeking.
+      if (mState == Seeking) {
+        TransitionState(SuccessResponse(), Enabled);
+      }
+
+      UpdateFrequency();
+      break;
+    default:
+      MOZ_CRASH();
+  }
+}
+
+void
+FMRadioService::UpdatePowerState()
+{
+  bool enabled = IsFMRadioOn();
+  if (enabled != mEnabled) {
+    mEnabled = enabled;
+    NotifyFMRadioEvent(EnabledChanged);
+  }
+}
+
+void
+FMRadioService::UpdateFrequency()
+{
+  int32_t frequency = GetFMRadioFrequency();
+  if (mPendingFrequencyInKHz != frequency) {
+    mPendingFrequencyInKHz = frequency;
+    NotifyFMRadioEvent(FrequencyChanged);
+  }
+}
+
+// static
+FMRadioService*
+FMRadioService::Singleton()
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!sFMRadioService) {
+    sFMRadioService = new FMRadioService();
+  }
+
+  return sFMRadioService;
+}
+
+NS_IMPL_ISUPPORTS1(FMRadioService, nsIObserver)
+
+END_FMRADIO_NAMESPACE
+
new file mode 100644
--- /dev/null
+++ b/dom/fmradio/FMRadioService.h
@@ -0,0 +1,203 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=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_fmradioservice_h__
+#define mozilla_dom_fmradioservice_h__
+
+#include "mozilla/dom/PFMRadioRequest.h"
+#include "FMRadioCommon.h"
+#include "mozilla/Hal.h"
+#include "mozilla/StaticPtr.h"
+
+BEGIN_FMRADIO_NAMESPACE
+
+class ReplyRunnable : public nsRunnable
+{
+public:
+  ReplyRunnable() : mResponseType(SuccessResponse()) {}
+  virtual ~ReplyRunnable() {}
+
+  void
+  SetReply(const FMRadioResponseType& aResponseType)
+  {
+    mResponseType = aResponseType;
+  }
+
+protected:
+  FMRadioResponseType mResponseType;
+};
+
+/**
+ * The FMRadio Service Interface for FMRadio.
+ *
+ * There are two concrete classes which implement this interface:
+ *  - FMRadioService
+ *    It's used in the main process, implements all the logics about FM Radio.
+ *
+ *  - FMRadioChild
+ *    It's used in subprocess. It's a kind of proxy which just sends all
+ *    the requests to main process through IPC channel.
+ *
+ * All the requests coming from the content page will be redirected to the
+ * concrete class object.
+ *
+ * Consider navigator.mozFMRadio.enable(). Here is the call sequence:
+ *  - OOP
+ *    Child:
+ *      (1) Call navigator.mozFMRadio.enable().
+ *      (2) Return a DOMRequest object, and call FMRadioChild.Enable() with a
+ *          ReplyRunnable object.
+ *      (3) Send IPC message to main process.
+ *    Parent:
+ *      (4) Call FMRadioService::Enable() with a ReplyRunnable object.
+ *      (5) Call hal::EnableFMRadio().
+ *      (6) Notify FMRadioService object when FM radio HW is enabled.
+ *      (7) Dispatch the ReplyRunnable object created in (4).
+ *      (8) Send IPC message back to child process.
+ *    Child:
+ *      (9) Dispatch the ReplyRunnable object created in (2).
+ *     (10) Fire success callback of the DOMRequest Object created in (2).
+ *                     _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ *                    |            OOP            |
+ *                    |                           |
+ *   Page  FMRadio    |    FMRadioChild       IPC |    FMRadioService   Hal
+ *    | (1)  |        |          |             |  |           |          |
+ *    |----->|    (2) |          |             |  |           |          |
+ *    |      |--------|--------->|      (3)    |  |           |          |
+ *    |      |        |          |-----------> |  |   (4)     |          |
+ *    |      |        |          |             |--|---------->|  (5)     |
+ *    |      |        |          |             |  |           |--------->|
+ *    |      |        |          |             |  |           |  (6)     |
+ *    |      |        |          |             |  |   (7)     |<---------|
+ *    |      |        |          |      (8)    |<-|-----------|          |
+ *    |      |    (9) |          |<----------- |  |           |          |
+ *    | (10) |<-------|----------|             |  |           |          |
+ *    |<-----|        |          |             |  |           |          |
+ *                    |                           |
+ *                    |_ _ _ _ _ _ _ _ _ _ _ _ _ _|
+ *  - non-OOP
+ *    In non-OOP model, we don't need to send messages between processes, so
+ *    the call sequences are much more simpler, it almost just follows the
+ *    sequences presented in OOP model: (1) (2) (5) (6) (9) and (10).
+ *
+ */
+class IFMRadioService
+{
+protected:
+  virtual ~IFMRadioService() { }
+
+public:
+  virtual bool IsEnabled() const = 0;
+  virtual double GetFrequency() const = 0;
+  virtual double GetFrequencyUpperBound() const = 0;
+  virtual double GetFrequencyLowerBound() const = 0;
+  virtual double GetChannelWidth() const = 0;
+
+  virtual void Enable(double aFrequency, ReplyRunnable* aReplyRunnable) = 0;
+  virtual void Disable(ReplyRunnable* aReplyRunnable) = 0;
+  virtual void SetFrequency(double aFrequency, ReplyRunnable* aReplyRunnable) = 0;
+  virtual void Seek(mozilla::hal::FMRadioSeekDirection aDirection,
+                    ReplyRunnable* aReplyRunnable) = 0;
+  virtual void CancelSeek(ReplyRunnable* aReplyRunnable) = 0;
+
+  /**
+   * Register handler to receive the FM Radio events, including:
+   *   - StateChangedEvent
+   *   - FrequencyChangedEvent
+   *
+   * Called by FMRadio and FMRadioParent.
+   */
+  virtual void AddObserver(FMRadioEventObserver* aObserver) = 0;
+  virtual void RemoveObserver(FMRadioEventObserver* aObserver) = 0;
+
+  /**
+   * Static method to return the singleton instance. If it's in the child
+   * process, we will get an object of FMRadioChild.
+   */
+  static IFMRadioService* Singleton();
+};
+
+enum FMRadioState
+{
+  Disabled,
+  Disabling,
+  Enabling,
+  Enabled,
+  Seeking
+};
+
+class FMRadioService MOZ_FINAL : public IFMRadioService
+                               , public hal::FMRadioObserver
+                               , public nsIObserver
+{
+  friend class ReadRilSettingTask;
+  friend class SetFrequencyRunnable;
+
+public:
+  static FMRadioService* Singleton();
+  virtual ~FMRadioService();
+
+  NS_DECL_ISUPPORTS
+
+  virtual bool IsEnabled() const MOZ_OVERRIDE;
+  virtual double GetFrequency() const MOZ_OVERRIDE;
+  virtual double GetFrequencyUpperBound() const MOZ_OVERRIDE;
+  virtual double GetFrequencyLowerBound() const MOZ_OVERRIDE;
+  virtual double GetChannelWidth() const MOZ_OVERRIDE;
+
+  virtual void Enable(double aFrequency, ReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
+  virtual void Disable(ReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
+  virtual void SetFrequency(double aFrequency, ReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
+  virtual void Seek(mozilla::hal::FMRadioSeekDirection aDirection,
+                    ReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
+  virtual void CancelSeek(ReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
+
+  virtual void AddObserver(FMRadioEventObserver* aObserver) MOZ_OVERRIDE;
+  virtual void RemoveObserver(FMRadioEventObserver* aObserver) MOZ_OVERRIDE;
+
+  /* FMRadioObserver */
+  void Notify(const hal::FMRadioOperationInformation& aInfo) MOZ_OVERRIDE;
+
+  NS_DECL_NSIOBSERVER
+
+protected:
+  FMRadioService();
+
+private:
+  int32_t RoundFrequency(double aFrequencyInMHz);
+
+  void NotifyFMRadioEvent(FMRadioEventType aType);
+  void DoDisable();
+  void TransitionState(const FMRadioResponseType& aResponse, FMRadioState aState);
+  void SetState(FMRadioState aState);
+  void UpdatePowerState();
+  void UpdateFrequency();
+
+private:
+  bool mEnabled;
+
+  int32_t mPendingFrequencyInKHz;
+
+  FMRadioState mState;
+
+  bool mHasReadRilSetting;
+  bool mRilDisabled;
+
+  double mUpperBoundInKHz;
+  double mLowerBoundInKHz;
+  double mChannelWidthInKHz;
+
+  nsRefPtr<ReplyRunnable> mPendingRequest;
+
+  FMRadioEventObserverList mObserverList;
+
+  static StaticRefPtr<FMRadioService> sFMRadioService;
+};
+
+END_FMRADIO_NAMESPACE
+
+#endif // mozilla_dom_fmradioservice_h__
+
new file mode 100644
--- /dev/null
+++ b/dom/fmradio/Makefile.in
@@ -0,0 +1,18 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEPTH            = ../..
+topsrcdir        = @top_srcdir@
+srcdir           = @srcdir@
+VPATH            = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+LIBRARY_NAME     = domfmradio_s
+
+include $(topsrcdir)/dom/dom-config.mk
+
+include $(topsrcdir)/config/rules.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
+
new file mode 100644
--- /dev/null
+++ b/dom/fmradio/moz.build
@@ -0,0 +1,33 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+if CONFIG['MOZ_B2G_FM']:
+    DIRS += [
+        'ipc',
+    ]
+
+    MODULE = 'dom'
+
+    EXPORTS.mozilla.dom += [
+        'FMRadio.h',
+        'FMRadioCommon.h',
+        'FMRadioService.h',
+    ]
+
+    CPP_SOURCES += [
+        'FMRadio.cpp',
+        'FMRadioService.cpp',
+    ]
+
+    LIBXUL_LIBRARY = True
+
+IPDL_SOURCES += [
+    'ipc/PFMRadio.ipdl',
+    'ipc/PFMRadioRequest.ipdl',
+]
+
+FAIL_ON_WARNINGS = True
+
--- a/dom/moz.build
+++ b/dom/moz.build
@@ -44,16 +44,17 @@ PARALLEL_DIRS += [
     'bluetooth',
     'browser-element',
     'contacts',
     'phonenumberutils',
     'alarm',
     'devicestorage',
     'encoding',
     'file',
+    'fmradio',
     'media',
     'messages',
     'power',
     'push',
     'quota',
     'settings',
     'mobilemessage',
     'src',
@@ -81,19 +82,16 @@ if CONFIG['MOZ_B2G_RIL']:
     PARALLEL_DIRS += [
         'telephony',
         'wifi',
         'icc',
         'cellbroadcast',
         'voicemail',
     ]
 
-if CONFIG['MOZ_B2G_FM']:
-    PARALLEL_DIRS += ['fm']
-
 if CONFIG['MOZ_PAY']:
     PARALLEL_DIRS += ['payment']
 
 if CONFIG['MOZ_GAMEPAD']:
     PARALLEL_DIRS += ['gamepad']
 
 # bindings/test is here, because it needs to build after bindings/, and
 # we build subdirectories before ourselves.
--- a/dom/permission/tests/test_fmradio.html
+++ b/dom/permission/tests/test_fmradio.html
@@ -14,17 +14,18 @@ https://bugzilla.mozilla.org/show_bug.cg
 <p id="display"></p>
 <div id="content" style="display: none"></div>
 <pre id="test">
 <script type="application/javascript;version=1.8" src="file_framework.js"></script>
 <script type="application/javascript;version=1.8">
 var gData = [
   {
     perm: ["fmradio"],
+    needParentPerm: true,
     obj: "mozFMRadio",
-    idl: "nsIDOMFMRadio",
+    webidl: "FMRadio",
   },
 ]
 </script>
 </pre>
 </body>
 </html>
 
new file mode 100644
--- /dev/null
+++ b/dom/webidl/FMRadio.webidl
@@ -0,0 +1,102 @@
+/* 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/. */
+
+interface FMRadio : EventTarget {
+  /* Indicates if the FM radio is enabled. */
+  readonly attribute boolean enabled;
+
+  /* Indicates if the antenna is plugged and available. */
+  readonly attribute boolean antennaAvailable;
+
+  /**
+   * Current frequency in MHz. The value will be null if the FM radio is
+   * disabled.
+   */
+  readonly attribute double? frequency;
+
+  /* The upper bound of frequency in MHz. */
+  readonly attribute double frequencyUpperBound;
+
+  /* The lower bound of frequency in MHz. */
+  readonly attribute double frequencyLowerBound;
+
+  /**
+   * The difference in frequency between two "adjacent" channels, in MHz. That
+   * is, any two radio channels' frequencies differ by at least channelWidth
+   * MHz. Usually, the value is one of:
+   *  - 0.05 MHz
+   *  - 0.1  MHz
+   *  - 0.2  MHz
+   */
+  readonly attribute double channelWidth;
+
+  /* Fired when the FM radio is enabled. */
+  [SetterThrows]
+  attribute EventHandler onenabled;
+
+  /* Fired when the FM radio is disabled. */
+  [SetterThrows]
+  attribute EventHandler ondisabled;
+
+  /**
+   * Fired when the antenna becomes available or unavailable, i.e., fired when
+   * the antennaAvailable attribute changes.
+   */
+  [SetterThrows]
+  attribute EventHandler onantennaavailablechange;
+
+  /* Fired when the FM radio's frequency is changed. */
+  [SetterThrows]
+  attribute EventHandler onfrequencychange;
+
+  /**
+   * Power the FM radio off. The disabled event will be fired if this request
+   * completes successfully.
+   */
+  DOMRequest disable();
+
+  /**
+   * Power the FM radio on, and tune the radio to the given frequency in MHz.
+   * This will fail if the given frequency is out of range. The enabled event
+   * and frequencychange event will be fired if this request completes
+   * successfully.
+   */
+  DOMRequest enable(double frequency);
+
+  /**
+   * Tune the FM radio to the given frequency. This will fail if the given
+   * frequency is out of range.
+   *
+   * Note that the FM radio may not tuned to the exact frequency given. To get
+   * the frequency the radio is actually tuned to, wait for the request to fire
+   * sucess (or wait for the frequencychange event to fire), and then read the
+   * frequency attribute.
+   */
+  DOMRequest setFrequency(double frequency);
+
+  /**
+   * Tell the FM radio to seek up to the next channel. If the frequency is
+   * successfully changed, the frequencychange event will be triggered.
+   *
+   * Only one seek is allowed at once: If the radio is seeking when the seekUp
+   * is called, error will be fired.
+   */
+  DOMRequest seekUp();
+
+  /**
+   * Tell the FM radio to seek down to the next channel. If the frequency is
+   * successfully changed, the frequencychange event will be triggered.
+   *
+   * Only one seek is allowed at once: If the radio is seeking when the
+   * seekDown is called, error will be fired.
+   */
+  DOMRequest seekDown();
+
+  /**
+   * Cancel the seek action. If the radio is not currently seeking up or down,
+   * error will be fired.
+   */
+  DOMRequest cancelSeek();
+};
+
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -294,16 +294,23 @@ partial interface Navigator {
 
 #ifdef MOZ_B2G_BT
 partial interface Navigator {
   [Throws, Func="Navigator::HasBluetoothSupport"]
   readonly attribute BluetoothManager mozBluetooth;
 };
 #endif // MOZ_B2G_BT
 
+#ifdef MOZ_B2G_FM
+partial interface Navigator {
+  [Throws, Func="Navigator::HasFMRadioSupport"]
+  readonly attribute FMRadio mozFMRadio;
+};
+#endif // MOZ_B2G_FM
+
 #ifdef MOZ_TIME_MANAGER
 // nsIDOMMozNavigatorTime
 partial interface Navigator {
   [Throws, Func="Navigator::HasTimeSupport"]
   readonly attribute MozTimeManager mozTime;
 };
 #endif // MOZ_TIME_MANAGER
 
--- a/dom/webidl/WebIDL.mk
+++ b/dom/webidl/WebIDL.mk
@@ -518,16 +518,20 @@ endif
 
 ifdef MOZ_WEBSPEECH
 webidl_files += \
   SpeechRecognitionError.webidl \
   SpeechRecognitionEvent.webidl \
   $(NULL)
 endif
 
+ifdef MOZ_B2G_FM
+webidl_files += FMRadio.webidl
+endif
+
 ifdef ENABLE_TESTS
 test_webidl_files := \
   TestCodeGen.webidl \
   TestDictionary.webidl \
   TestExampleGen.webidl \
   TestJSImplGen.webidl \
   TestJSImplInheritanceGen.webidl \
   TestTypedef.webidl \
--- a/layout/build/Makefile.in
+++ b/layout/build/Makefile.in
@@ -118,17 +118,18 @@ ifdef MOZ_OMX_DECODER #{
 SHARED_LIBRARY_LIBS 	+= \
 	$(DEPTH)/content/media/omx/$(LIB_PREFIX)gkconomx_s.$(LIB_SUFFIX) \
 	$(DEPTH)/content/media/omx/mediaresourcemanager/$(LIB_PREFIX)mediaresourcemanager.$(LIB_SUFFIX) \
 	$(NULL)
 endif #}
 
 ifdef MOZ_B2G_FM #{
 SHARED_LIBRARY_LIBS	+= \
-  $(DEPTH)/dom/fm/$(LIB_PREFIX)domfm_s.$(LIB_SUFFIX) \
+  $(DEPTH)/dom/fmradio/$(LIB_PREFIX)domfmradio_s.$(LIB_SUFFIX) \
+  $(DEPTH)/dom/fmradio/ipc/$(LIB_PREFIX)domfmradio_s.$(LIB_SUFFIX) \
   $(NULL)
 endif #}
 
 ifdef MOZ_B2G_BT #{
 SHARED_LIBRARY_LIBS += $(DEPTH)/dom/bluetooth/$(LIB_PREFIX)dombluetooth_s.$(LIB_SUFFIX)
 endif #}
 
 SHARED_LIBRARY_LIBS	+= $(DEPTH)/dom/camera/$(LIB_PREFIX)domcamera_s.$(LIB_SUFFIX)
@@ -322,17 +323,17 @@ ifdef MOZ_GSTREAMER
 LOCAL_INCLUDES	+= $(GSTREAMER_CFLAGS)
 endif
 
 ifdef MOZ_B2G_RIL #{
 LOCAL_INCLUDES	+= -I$(topsrcdir)/dom/system/gonk
 endif #}
 
 ifdef MOZ_B2G_FM #{
-LOCAL_INCLUDES	+= -I$(topsrcdir)/dom/fm
+LOCAL_INCLUDES	+= -I$(topsrcdir)/dom/fmradio
 endif #}
 
 ifdef MOZ_B2G_BT #{
 LOCAL_INCLUDES	+= -I$(topsrcdir)/dom/bluetooth
 endif #}
 
 ifdef MOZ_WEBSPEECH #{
 LOCAL_INCLUDES	+= -I$(topsrcdir)/content/media/webspeech/synth
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -120,21 +120,16 @@ using mozilla::dom::bluetooth::Bluetooth
 
 #ifdef MOZ_WIDGET_GONK
 #include "AudioManager.h"
 using mozilla::dom::gonk::AudioManager;
 #include "nsVolumeService.h"
 using mozilla::system::nsVolumeService;
 #endif
 
-#ifdef MOZ_B2G_FM
-#include "FMRadio.h"
-using mozilla::dom::fm::FMRadio;
-#endif
-
 #include "AudioChannelAgent.h"
 using mozilla::dom::AudioChannelAgent;
 
 // Editor stuff
 #include "nsEditorCID.h"
 #include "nsEditor.h"
 #include "nsPlaintextEditor.h"
 #include "nsEditorController.h" //CID
@@ -298,20 +293,16 @@ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsSynthVoiceRegistry,
                                          nsSynthVoiceRegistry::GetInstanceForService)
 #endif
 
 #ifdef MOZ_WIDGET_GONK
 NS_GENERIC_FACTORY_CONSTRUCTOR(AudioManager)
 #endif
 
-#ifdef MOZ_B2G_FM
-NS_GENERIC_FACTORY_CONSTRUCTOR(FMRadio)
-#endif
-
 NS_GENERIC_FACTORY_CONSTRUCTOR(AudioChannelAgent)
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDeviceSensors)
 
 #ifndef MOZ_WIDGET_GONK
 #if defined(ANDROID) || defined(MOZ_PLATFORM_MAEMO)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHapticFeedback)
 #endif
@@ -756,20 +747,16 @@ NS_DEFINE_NAMED_CID(SYSTEMWORKERMANAGER_
 #ifdef MOZ_B2G_BT
 NS_DEFINE_NAMED_CID(BLUETOOTHSERVICE_CID);
 #endif
 #ifdef MOZ_WIDGET_GONK
 NS_DEFINE_NAMED_CID(NS_AUDIOMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_VOLUMESERVICE_CID);
 #endif
 
-#ifdef MOZ_B2G_FM
-NS_DEFINE_NAMED_CID(NS_FMRADIO_CID);
-#endif
-
 NS_DEFINE_NAMED_CID(NS_AUDIOCHANNELAGENT_CID);
 
 NS_DEFINE_NAMED_CID(NS_HTMLEDITOR_CID);
 NS_DEFINE_NAMED_CID(NS_EDITORCONTROLLER_CID);
 NS_DEFINE_NAMED_CID(NS_EDITINGCONTROLLER_CID);
 NS_DEFINE_NAMED_CID(NS_EDITORCOMMANDTABLE_CID);
 NS_DEFINE_NAMED_CID(NS_EDITINGCOMMANDTABLE_CID);
 NS_DEFINE_NAMED_CID(NS_TEXTSERVICESDOCUMENT_CID);
@@ -1045,19 +1032,16 @@ static const mozilla::Module::CIDEntry k
 #endif
 #ifdef MOZ_B2G_BT
   { &kBLUETOOTHSERVICE_CID, true, NULL, BluetoothServiceConstructor },
 #endif
 #ifdef MOZ_WIDGET_GONK
   { &kNS_AUDIOMANAGER_CID, true, NULL, AudioManagerConstructor },
   { &kNS_VOLUMESERVICE_CID, true, NULL, nsVolumeServiceConstructor },
 #endif
-#ifdef MOZ_B2G_FM
-  { &kNS_FMRADIO_CID, true, NULL, FMRadioConstructor },
-#endif
   { &kNS_AUDIOCHANNELAGENT_CID, true, NULL, AudioChannelAgentConstructor },
   { &kNS_HTMLEDITOR_CID, false, NULL, nsHTMLEditorConstructor },
   { &kNS_EDITORCONTROLLER_CID, false, NULL, nsEditorControllerConstructor },
   { &kNS_EDITINGCONTROLLER_CID, false, NULL, nsEditingControllerConstructor },
   { &kNS_EDITORCOMMANDTABLE_CID, false, NULL, nsEditorCommandTableConstructor },
   { &kNS_EDITINGCOMMANDTABLE_CID, false, NULL, nsEditingCommandTableConstructor },
   { &kNS_TEXTSERVICESDOCUMENT_CID, false, NULL, nsTextServicesDocumentConstructor },
   { &kNS_GEOLOCATION_SERVICE_CID, false, NULL, nsGeolocationServiceConstructor },
@@ -1203,19 +1187,16 @@ static const mozilla::Module::ContractID
 #endif
 #ifdef MOZ_B2G_BT
   { BLUETOOTHSERVICE_CONTRACTID, &kBLUETOOTHSERVICE_CID },
 #endif
 #ifdef MOZ_WIDGET_GONK
   { NS_AUDIOMANAGER_CONTRACTID, &kNS_AUDIOMANAGER_CID },
   { NS_VOLUMESERVICE_CONTRACTID, &kNS_VOLUMESERVICE_CID },
 #endif
-#ifdef MOZ_B2G_FM
-  { NS_FMRADIO_CONTRACTID, &kNS_FMRADIO_CID },
-#endif
   { NS_AUDIOCHANNELAGENT_CONTRACTID, &kNS_AUDIOCHANNELAGENT_CID },
   { "@mozilla.org/editor/htmleditor;1", &kNS_HTMLEDITOR_CID },
   { "@mozilla.org/editor/editorcontroller;1", &kNS_EDITORCONTROLLER_CID },
   { "@mozilla.org/editor/editingcontroller;1", &kNS_EDITINGCONTROLLER_CID },
   { "@mozilla.org/textservices/textservicesdocument;1", &kNS_TEXTSERVICESDOCUMENT_CID },
   { "@mozilla.org/geolocation/service;1", &kNS_GEOLOCATION_SERVICE_CID },
   { "@mozilla.org/geolocation;1", &kNS_GEOLOCATION_CID },
   { "@mozilla.org/audiochannel/service;1", &kNS_AUDIOCHANNEL_SERVICE_CID },