Merge the last PGO-green inbound changeset to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 21 Sep 2012 22:38:53 -0400
changeset 107866 8f359a9d1f19ca897fc4d1ca93e56b27e5523ecf
parent 107803 b061c2316d0ce367d4a1aa05159146431c4d9edc (current diff)
parent 107865 d1e20611089c6cd09e12ac1a6b8f2a0e48a2d29a (diff)
child 107875 9cfb80a82883725b5fbdba4adb271d9f2d8f2e41
child 107897 7c8b8d36679ff71e4b01e49a65674dfdb3b58253
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
milestone18.0a1
Merge the last PGO-green inbound changeset to m-c.
dom/workers/FileReaderSyncPrivate.cpp
dom/workers/FileReaderSyncPrivate.h
widget/tests/test_bug760802.html
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -76,16 +76,55 @@ SettingsListener.observe('language.curre
     SettingsListener.observe(key, null, function(value) {
       if (value != null) {
         Services.prefs.setIntPref(key, value);
       }
     });
   });
 })();
 
+//=================== DeviceInfo ====================
+Components.utils.import('resource://gre/modules/XPCOMUtils.jsm');
+Components.utils.import('resource://gre/modules/ctypes.jsm');
+(function DeviceInfoToSettings() {
+  XPCOMUtils.defineLazyServiceGetter(this, 'gSettingsService',
+                                     '@mozilla.org/settingsService;1',
+                                     'nsISettingsService');
+  let lock = gSettingsService.createLock();
+  //MOZ_B2G_VERSION is set in b2g/confvars.sh, and is outputed as a #define value
+  //from configure.in, defaults to 1.0.0 if this value is not exist
+#filter attemptSubstitution
+  let os_version = '@MOZ_B2G_VERSION@';
+#unfilter attemptSubstitution
+  lock.set('deviceinfo.os', os_version, null, null);
+
+  //Get the hardware info from android properties
+  var hardware_version = null;
+  try {
+    let cutils = ctypes.open('libcutils.so');
+    let cbuf = ctypes.char.array(128)();
+    let c_property_get = cutils.declare('property_get', ctypes.default_abi,
+                                        ctypes.int,       // return value: length
+                                        ctypes.char.ptr,  // key
+                                        ctypes.char.ptr,  // value
+                                        ctypes.char.ptr); // default
+    let property_get = function (key, defaultValue) {
+      if (defaultValue === undefined) {
+        defaultValue = null;
+      }
+      c_property_get(key, cbuf, defaultValue);
+      return cbuf.readString();
+    }
+    hardware_version = property_get('ro.hardware');
+    cutils.close();
+  } catch(e) {
+    //Error
+  }
+  lock.set('deviceinfo.hardware', hardware_version, null, null);
+})();
 
 // =================== Debugger ====================
 SettingsListener.observe('devtools.debugger.remote-enabled', false, function(enabled) {
   Services.prefs.setBoolPref('devtools.debugger.remote-enabled', value);
 });
 
 SettingsListener.observe('devtools.debugger.log', false, function(value) {
   Services.prefs.setBoolPref('devtools.debugger.log', value);
@@ -105,9 +144,9 @@ SettingsListener.observe('debug.log-anim
 
 SettingsListener.observe('debug.dev-mode', false, function(value) {
   Services.prefs.setBoolPref('dom.mozApps.dev_mode', value);
 });
 
 // =================== Privacy ====================
 SettingsListener.observe('privacy.donottrackheader.enabled', false, function(value) {
   Services.prefs.setBoolPref('privacy.donottrackheader.enabled', value);
-});
\ No newline at end of file
+});
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -8,16 +8,19 @@ const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import('resource://gre/modules/Services.jsm');
 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/PermissionSettings.jsm');
 Cu.import('resource://gre/modules/ObjectWrapper.jsm');
 Cu.import('resource://gre/modules/accessibility/AccessFu.jsm');
 Cu.import('resource://gre/modules/Payment.jsm');
 
--- a/b2g/chrome/jar.mn
+++ b/b2g/chrome/jar.mn
@@ -6,17 +6,17 @@
 
 chrome.jar:
 % content branding %content/branding/
 % content browser %content/
 
   content/arrow.svg                     (content/arrow.svg)
   content/dbg-browser-actors.js         (content/dbg-browser-actors.js)
   content/forms.js                      (content/forms.js)
-  content/settings.js                   (content/settings.js)
+* content/settings.js                   (content/settings.js)
 * content/shell.xul                     (content/shell.xul)
 * content/shell.js                      (content/shell.js)
 #ifndef ANDROID
   content/screen.js                     (content/screen.js)
   content/runapp.js                     (content/runapp.js)
 #endif
   content/content.css                   (content/content.css)
   content/touchcontrols.css             (content/touchcontrols.css)
--- a/b2g/confvars.sh
+++ b/b2g/confvars.sh
@@ -3,16 +3,18 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MOZ_APP_BASENAME=B2G
 MOZ_APP_VENDOR=Mozilla
 
 MOZ_APP_VERSION=18.0a1
 MOZ_APP_UA_NAME=Firefox
 
+MOZ_B2G_VERSION=1.0.0
+
 MOZ_BRANDING_DIRECTORY=b2g/branding/unofficial
 MOZ_OFFICIAL_BRANDING_DIRECTORY=b2g/branding/official
 # MOZ_APP_DISPLAYNAME is set by branding/configure.sh
 
 MOZ_SAFE_BROWSING=
 MOZ_SERVICES_SYNC=1
 
 MOZ_WEBSMS_BACKEND=1
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -157,16 +157,19 @@
 @BINPATH@/components/dom_apps.xpt
 @BINPATH@/components/dom_base.xpt
 #ifdef MOZ_B2G_RIL
 @BINPATH@/components/dom_telephony.xpt
 @BINPATH@/components/dom_wifi.xpt
 @BINPATH@/components/dom_system_gonk.xpt
 @BINPATH@/components/dom_icc.xpt
 #endif
+#ifdef MOZ_B2G_FM
+@BINPATH@/components/dom_fm.xpt
+#endif
 @BINPATH@/components/dom_battery.xpt
 #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
@@ -450,16 +453,20 @@
 @BINPATH@/components/RILContentHelper.js
 @BINPATH@/components/SmsDatabaseService.manifest
 @BINPATH@/components/SmsDatabaseService.js
 @BINPATH@/components/WifiWorker.js
 @BINPATH@/components/WifiWorker.manifest
 @BINPATH@/components/DOMWifiManager.js
 @BINPATH@/components/DOMWifiManager.manifest
 #endif
+#ifdef MOZ_B2G_FM
+@BINPATH@/components/DOMFMRadioChild.js
+@BINPATH@/components/DOMFMRadio.manifest
+#endif
 #ifdef XP_MACOSX
 @BINPATH@/components/libalerts.dylib
 #endif
 #ifdef MOZ_ENABLE_DBUS
 @BINPATH@/components/@DLL_PREFIX@dbusservice@DLL_SUFFIX@
 #endif
 @BINPATH@/components/nsINIProcessor.manifest
 @BINPATH@/components/nsINIProcessor.js
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -284,17 +284,28 @@ let SocialFlyout = {
         aCallback(iframe.contentWindow);
       } catch(e) {
         Cu.reportError(e);
       }
     }
 
     sizeSocialPanelToContent(iframe);
     let anchor = document.getElementById("social-sidebar-browser");
-    panel.openPopup(anchor, "start_before", 0, yOffset, false, false);
+    if (panel.state == "open") {
+      // this is painful - there is no way to say "move to a new anchor offset",
+      // only "move to new screen pos".  So we remember the last yOffset,
+      // calculate the adjustment needed to the new yOffset, then calc the
+      // screen Y position.
+      let yAdjust = yOffset - this.yOffset;
+      let box = panel.boxObject;
+      panel.moveTo(box.screenX, box.screenY + yAdjust);
+    } else {
+      panel.openPopup(anchor, "start_before", 0, yOffset, false, false);
+    }
+    this.yOffset = yOffset;
   }
 }
 
 let SocialShareButton = {
   // promptImages and promptMessages being null means we are yet to get the
   // message back from the provider with the images and icons (or that we got
   // the response but determined it was invalid.)
   promptImages: null,
--- a/build/virtualenv/packages.txt
+++ b/build/virtualenv/packages.txt
@@ -5,11 +5,12 @@ mozinstall.pth:testing/mozbase/mozinstal
 mozlog.pth:testing/mozbase/mozlog
 mozprocess.pth:testing/mozbase/mozprocess
 mozprofile.pth:testing/mozbase/mozprofile
 mozrunner.pth:testing/mozbase/mozrunner
 blessings.pth:python/blessings
 mozbuild.pth:python/mozbuild
 optional:setup.py:python/psutil:build_ext:--inplace
 optional:psutil.pth:python/psutil
+which.pth:python/which
 mozilla.pth:build
 mozilla.pth:config
 copy:build/buildconfig.py
--- a/configure.in
+++ b/configure.in
@@ -2077,16 +2077,17 @@ ia64*-hpux*)
 
 *-android*|*-linuxandroid*)
     AC_DEFINE(NO_PW_GECOS)
     no_x=yes
     if test -n "$gonkdir"; then
         _PLATFORM_DEFAULT_TOOLKIT=cairo-gonk
         MOZ_B2G_RIL=1
         MOZ_B2G_BT=1
+        MOZ_B2G_FM=1
     else
         _PLATFORM_DEFAULT_TOOLKIT=cairo-android
         MOZ_LINKER=1
     fi
     TARGET_NSPR_MDCPUCFG='\"md/_linux.cfg\"'
 
     MOZ_GFX_OPTIMIZE_MOBILE=1
     MOZ_OPTIMIZE_FLAGS="-Os -freorder-blocks -fno-reorder-functions"
@@ -2976,16 +2977,18 @@ dnl Quota support
 MOZ_CHECK_HEADERS(sys/quota.h sys/sysmacros.h)
 MOZ_CHECK_HEADERS(linux/quota.h)
 
 dnl Try for MMX support
 dnl NB - later gcc versions require -mmmx for this header to be successfully
 dnl included (or another option which implies it, such as -march=pentium-mmx)
 MOZ_CHECK_HEADERS(mmintrin.h)
 
+MOZ_CHECK_HEADERS(sys/types.h netinet/in.h byteswap.h)
+
 dnl Check whether the compiler supports the new-style C++ standard
 dnl library headers (i.e. <new>) or needs the old "new.h"
 AC_LANG_CPLUSPLUS
 NEW_H=new.h
 MOZ_CHECK_HEADER(new, [NEW_H=new])
 AC_DEFINE_UNQUOTED(NEW_H, <$NEW_H>)
 AC_LANG_C
 
@@ -4184,16 +4187,17 @@ MOZ_VORBIS=
 MOZ_TREMOR=
 MOZ_WAVE=1
 MOZ_SAMPLE_TYPE_FLOAT32=
 MOZ_SAMPLE_TYPE_S16=
 MOZ_MEDIA=
 MOZ_OPUS=1
 MOZ_WEBM=1
 MOZ_WEBRTC=1
+MOZ_SRTP=
 MOZ_WEBRTC_SIGNALING=
 MOZ_MEDIA_PLUGINS=
 MOZ_MEDIA_NAVIGATOR=
 MOZ_OMX_PLUGIN=
 MOZ_VP8=
 MOZ_VP8_ERROR_CONCEALMENT=
 MOZ_VP8_ENCODER=
 VPX_AS=
@@ -5186,19 +5190,22 @@ MOZ_ARG_DISABLE_BOOL(webrtc,
 
 if test -n "$MOZ_WEBRTC"; then
     AC_DEFINE(MOZ_WEBRTC)
     MOZ_MEDIA=1
     MOZ_RAW=1
     MOZ_VP8=1
     MOZ_VP8_ENCODER=1
     MOZ_VP8_ERROR_CONCEALMENT=1
+    MOZ_SRTP=1
+    AC_DEFINE(MOZ_SRTP)
 fi
 
 AC_SUBST(MOZ_WEBRTC)
+AC_SUBST(MOZ_SRTP)
 
 case "$target_cpu" in
 arm*)
     MOZ_SAMPLE_TYPE_S16=1
     AC_DEFINE(MOZ_SAMPLE_TYPE_S16)
     AC_SUBST(MOZ_SAMPLE_TYPE_S16)
 ;;
 *)
@@ -7310,16 +7317,24 @@ MOZ_ARG_ENABLE_BOOL(b2g-ril,
     MOZ_B2G_RIL=1,
     MOZ_B2G_RIL= )
 if test -n "$MOZ_B2G_RIL"; then
     AC_DEFINE(MOZ_B2G_RIL)
 fi
 AC_SUBST(MOZ_B2G_RIL)
 
 dnl ========================================================
+dnl = Enable Radio FM for B2G (Gonk usually)
+dnl ========================================================
+if test -n "$MOZ_B2G_FM"; then
+    AC_DEFINE(MOZ_B2G_FM)
+fi
+AC_SUBST(MOZ_B2G_FM)
+
+dnl ========================================================
 dnl = Enable Bluetooth Interface for B2G (Gonk usually)
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(b2g-bt,
 [  --enable-b2g-bt      Set compile flags necessary for compiling Bluetooth API for B2G ],
     MOZ_B2G_BT=1,
     MOZ_B2G_BT= )
 if test -n "$MOZ_B2G_BT"; then
     AC_DEFINE(MOZ_B2G_BT)
@@ -8348,16 +8363,19 @@ IS_ALPHA=`echo $MOZ_APP_VERSION | grep a
 if test -z "$IS_ALPHA"; then
   changequote(,)
   MOZ_APP_MAXVERSION=`echo $MOZ_APP_VERSION | sed "s|\(^[0-9]*.[0-9]*\).*|\1|"`.*
   changequote([,])
 else
   MOZ_APP_MAXVERSION=$MOZ_APP_VERSION
 fi
 
+MOZ_B2G_VERSION=${MOZ_B2G_VERSION:-"1.0.0"}
+AC_DEFINE_UNQUOTED(MOZ_B2G_VERSION,"$MOZ_B2G_VERSION")
+
 AC_SUBST(MOZ_APP_NAME)
 AC_SUBST(MOZ_APP_DISPLAYNAME)
 AC_SUBST(MOZ_APP_BASENAME)
 AC_SUBST(MOZ_APP_VENDOR)
 AC_SUBST(MOZ_APP_PROFILE)
 AC_SUBST(MOZ_APP_ID)
 AC_SUBST(MAR_CHANNEL_ID)
 AC_SUBST(ACCEPTED_MAR_CHANNEL_IDS)
--- a/content/base/src/FragmentOrElement.cpp
+++ b/content/base/src/FragmentOrElement.cpp
@@ -742,29 +742,17 @@ FragmentOrElement::HasAttributes(bool* a
   *aReturn = GetAttrCount() > 0;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 FragmentOrElement::GetAttributes(nsIDOMNamedNodeMap** aAttributes)
 {
-  if (!IsElement()) {
-    *aAttributes = nullptr;
-    return NS_OK;
-  }
-
-  nsDOMSlots *slots = DOMSlots();
-
-  if (!slots->mAttributeMap) {
-    slots->mAttributeMap = new nsDOMAttributeMap(this->AsElement());
-  }
-
-  NS_ADDREF(*aAttributes = slots->mAttributeMap);
-
+  *aAttributes = nullptr;
   return NS_OK;
 }
 
 nsresult
 FragmentOrElement::HasChildNodes(bool* aReturn)
 {
   *aReturn = mAttrsAndChildren.ChildCount() > 0;
 
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -1682,16 +1682,30 @@ nsGenericElement::GetExistingAttrNameFro
   }
   else {
     NS_ADDREF(nodeInfo = name->NodeInfo());
   }
 
   return nodeInfo;
 }
 
+NS_IMETHODIMP
+nsGenericElement::GetAttributes(nsIDOMNamedNodeMap** aAttributes)
+{
+  nsDOMSlots *slots = DOMSlots();
+
+  if (!slots->mAttributeMap) {
+    slots->mAttributeMap = new nsDOMAttributeMap(this);
+  }
+
+  NS_ADDREF(*aAttributes = slots->mAttributeMap);
+
+  return NS_OK;
+}
+
 // static
 bool
 nsGenericElement::ShouldBlur(nsIContent *aContent)
 {
   // Determine if the current element is focused, if it is not focused
   // then we should not try to blur
   nsIDocument *document = aContent->GetDocument();
   if (!document)
--- a/content/base/src/nsGenericElement.h
+++ b/content/base/src/nsGenericElement.h
@@ -76,16 +76,19 @@ public:
                               bool aNullParent = true);
   virtual nsIAtom *GetClassAttributeName() const;
   virtual already_AddRefed<nsINodeInfo> GetExistingAttrNameFromQName(const nsAString& aStr) const;
   nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
                    const nsAString& aValue, bool aNotify)
   {
     return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify);
   }
+
+  NS_IMETHOD GetAttributes(nsIDOMNamedNodeMap** aAttributes);
+
   /**
    * Helper for SetAttr/SetParsedAttr. This method will return true if aNotify
    * is true or there are mutation listeners that must be triggered, the
    * attribute is currently set, and the new value that is about to be set is
    * different to the current value. As a perf optimization the new and old
    * values will not actually be compared if we aren't notifying and we don't
    * have mutation listeners (in which case it's cheap to just return false
    * and let the caller go ahead and set the value).
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -755,16 +755,18 @@ GK_ATOM(onresize, "onresize")
 GK_ATOM(onscroll, "onscroll")
 GK_ATOM(onselect, "onselect")
 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/content/canvas/Makefile.in
+++ b/content/canvas/Makefile.in
@@ -7,12 +7,12 @@ DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 PARALLEL_DIRS	= public src
 
-TEST_DIRS += test
+TOOLS_DIRS += test
 
 include $(topsrcdir)/config/rules.mk
 
--- a/content/canvas/src/Makefile.in
+++ b/content/canvas/src/Makefile.in
@@ -15,16 +15,17 @@ include $(DEPTH)/config/autoconf.mk
 MODULE		= content
 LIBRARY_NAME	= gkconcvs_s
 LIBXUL_LIBRARY  = 1
 
 EXPORTS = \
 	CustomQS_Canvas.h \
 	CustomQS_Canvas2D.h \
 	WebGLContext.h \
+	WebGLElementArrayCache.h \
 	$(NULL)
 
 EXPORTS_NAMESPACES = mozilla/dom
 
 EXPORTS_mozilla/dom = \
   ImageData.h \
   $(NULL)
 
@@ -47,16 +48,17 @@ CPPSRCS += \
 	WebGLContextReporter.cpp \
 	WebGLContextValidate.cpp \
 	WebGLExtensionStandardDerivatives.cpp \
 	WebGLExtensionTextureFilterAnisotropic.cpp \
 	WebGLExtensionLoseContext.cpp \
 	WebGLTexelConversions.cpp \
 	WebGLExtensionCompressedTextureS3TC.cpp \
 	WebGLExtensionDepthTexture.cpp \
+	WebGLElementArrayCache.cpp \
 	$(NULL)
 
 DEFINES += -DUSE_ANGLE
 USE_ANGLE=1
 
 else
 
 CPPSRCS += WebGLContextNotSupported.cpp
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -607,18 +607,18 @@ WebGLContext::Render(gfxContext *ctx, gf
 
 void WebGLContext::LoseOldestWebGLContextIfLimitExceeded()
 {
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
     // some mobile devices can't have more than 8 GL contexts overall
     const size_t kMaxWebGLContextsPerPrincipal = 2;
     const size_t kMaxWebGLContexts             = 4;
 #else
-    const size_t kMaxWebGLContextsPerPrincipal = 8;
-    const size_t kMaxWebGLContexts             = 16;
+    const size_t kMaxWebGLContextsPerPrincipal = 16;
+    const size_t kMaxWebGLContexts             = 32;
 #endif
     MOZ_ASSERT(kMaxWebGLContextsPerPrincipal < kMaxWebGLContexts);
 
     // it's important to update the index on a new context before losing old contexts,
     // otherwise new unused contexts would all have index 0 and we couldn't distinguish older ones
     // when choosing which one to lose first.
     UpdateLastUseIndex();
 
@@ -1007,24 +1007,16 @@ WebGLContext::GetExtension(const nsAStri
             ext = OES_standard_derivatives;
     }
     else if (aName.Equals(NS_LITERAL_STRING("EXT_texture_filter_anisotropic"),
              nsCaseInsensitiveStringComparator()))
     {
         if (IsExtensionSupported(EXT_texture_filter_anisotropic))
             ext = EXT_texture_filter_anisotropic;
     }
-    else if (aName.Equals(NS_LITERAL_STRING("MOZ_EXT_texture_filter_anisotropic"),
-             nsCaseInsensitiveStringComparator()))
-    {
-        GenerateWarning("MOZ_EXT_texture_filter_anisotropic has been renamed to EXT_texture_filter_anisotropic. "
-                        "Support for the MOZ_-prefixed string will be removed very soon.");
-        if (IsExtensionSupported(EXT_texture_filter_anisotropic))
-            ext = EXT_texture_filter_anisotropic;
-    }
     else if (aName.Equals(NS_LITERAL_STRING("MOZ_WEBGL_lose_context"),
              nsCaseInsensitiveStringComparator()))
     {
         if (IsExtensionSupported(WEBGL_lose_context))
             ext = WEBGL_lose_context;
     }
     else if (aName.Equals(NS_LITERAL_STRING("MOZ_WEBGL_compressed_texture_s3tc"),
              nsCaseInsensitiveStringComparator()))
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -36,16 +36,18 @@
 
 #include "angle/ShaderLang.h"
 
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/dom/Nullable.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/BindingUtils.h"
 
+#include "WebGLElementArrayCache.h"
+
 /* 
  * Minimum value constants defined in 6.2 State Tables of OpenGL ES - 2.0.25
  *   https://bugzilla.mozilla.org/show_bug.cgi?id=686732
  * 
  * Exceptions: some of the following values are set to higher values than in the spec because
  * the values in the spec are ridiculously low. They are explicitly marked below
 */
 #define MINVALUE_GL_MAX_TEXTURE_SIZE                  1024  // Different from the spec, which sets it to 64 on page 162
@@ -1491,140 +1493,79 @@ class WebGLBuffer MOZ_FINAL
     , public WebGLContextBoundObject
 {
 public:
     WebGLBuffer(WebGLContext *context)
         : WebGLContextBoundObject(context)
         , mHasEverBeenBound(false)
         , mByteLength(0)
         , mTarget(LOCAL_GL_NONE)
-        , mData(nullptr)
     {
         mContext->MakeContextCurrent();
         mContext->gl->fGenBuffers(1, &mGLName);
         mContext->mBuffers.insertBack(this);
     }
 
     ~WebGLBuffer() {
         DeleteOnce();
     }
 
     void Delete() {
         mContext->MakeContextCurrent();
         mContext->gl->fDeleteBuffers(1, &mGLName);
-        free(mData);
-        mData = nullptr;
         mByteLength = 0;
+        mCache = nullptr;
         LinkedListElement<WebGLBuffer>::remove(); // remove from mContext->mBuffers
     }
 
     size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const {
-        return aMallocSizeOf(this) + aMallocSizeOf(mData);
+        size_t sizeOfCache = mCache ? mCache->SizeOfIncludingThis(aMallocSizeOf) : 0;
+        return aMallocSizeOf(this) + sizeOfCache;
     }
 
     bool HasEverBeenBound() { return mHasEverBeenBound; }
     void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; }
     GLuint GLName() const { return mGLName; }
     GLuint ByteLength() const { return mByteLength; }
     GLenum Target() const { return mTarget; }
-    const void *Data() const { return mData; }
 
     void SetByteLength(GLuint byteLength) { mByteLength = byteLength; }
-    void SetTarget(GLenum target) { mTarget = target; }
-
-    // element array buffers are the only buffers for which we need to keep a copy of the data.
-    // this method assumes that the byte length has previously been set by calling SetByteLength.
-    bool CopyDataIfElementArray(const void* data) {
-        if (mTarget == LOCAL_GL_ELEMENT_ARRAY_BUFFER) {
-            mData = realloc(mData, mByteLength);
-            if (!mData) {
-                mByteLength = 0;
-                return false;
-            }
-            memcpy(mData, data, mByteLength);
-        }
-        return true;
-    }
-
-    // same comments as for CopyElementArrayData
-    bool ZeroDataIfElementArray() {
-        if (mTarget == LOCAL_GL_ELEMENT_ARRAY_BUFFER) {
-            mData = realloc(mData, mByteLength);
-            if (!mData) {
-                mByteLength = 0;
-                return false;
-            }
-            memset(mData, 0, mByteLength);
-        }
+
+    void SetTarget(GLenum target) {
+        mTarget = target;
+        if (!mCache && mTarget == LOCAL_GL_ELEMENT_ARRAY_BUFFER)
+            mCache = new WebGLElementArrayCache;
+     }
+
+    bool ElementArrayCacheBufferData(const void* ptr, size_t buffer_size_in_bytes) {
+        if (mTarget == LOCAL_GL_ELEMENT_ARRAY_BUFFER)
+            return mCache->BufferData(ptr, buffer_size_in_bytes);
         return true;
     }
 
-    // same comments as for CopyElementArrayData
-    void CopySubDataIfElementArray(GLuint byteOffset, GLuint byteLength, const void* data) {
-        if (mTarget == LOCAL_GL_ELEMENT_ARRAY_BUFFER && mByteLength) {
-            memcpy((void*) (size_t(mData)+byteOffset), data, byteLength);
-        }
-    }
-
-    // this method too is only for element array buffers. It returns the maximum value in the part of
-    // the buffer starting at given offset, consisting of given count of elements. The type T is the type
-    // to interpret the array elements as, must be GLushort or GLubyte.
-    template<typename T>
-    T FindMaxElementInSubArray(GLuint count, GLuint byteOffset)
-    {
-        const T* start = reinterpret_cast<T*>(reinterpret_cast<size_t>(mData) + byteOffset);
-        const T* stop = start + count;
-        T result = 0;
-        for(const T* ptr = start; ptr != stop; ++ptr) {
-            if (*ptr > result) result = *ptr;
-        }
-        return result;
+    void ElementArrayCacheBufferSubData(size_t pos, const void* ptr, size_t update_size_in_bytes) {
+        if (mTarget == LOCAL_GL_ELEMENT_ARRAY_BUFFER)
+            mCache->BufferSubData(pos, ptr, update_size_in_bytes);
     }
 
-    void InvalidateCachedMaxElements() {
-      mHasCachedMaxUbyteElement = false;
-      mHasCachedMaxUshortElement = false;
-    }
-
-    int32_t FindMaxUbyteElement() {
-      if (mHasCachedMaxUbyteElement) {
-        return mCachedMaxUbyteElement;
-      } else {
-        mHasCachedMaxUbyteElement = true;
-        mCachedMaxUbyteElement = FindMaxElementInSubArray<GLubyte>(mByteLength, 0);
-        return mCachedMaxUbyteElement;
-      }
-    }
-
-    int32_t FindMaxUshortElement() {
-      if (mHasCachedMaxUshortElement) {
-        return mCachedMaxUshortElement;
-      } else {
-        mHasCachedMaxUshortElement = true;
-        mCachedMaxUshortElement = FindMaxElementInSubArray<GLushort>(mByteLength>>1, 0);
-        return mCachedMaxUshortElement;
-      }
+    bool Validate(WebGLenum type, uint32_t max_allowed, size_t first, size_t count) {
+        return mCache->Validate(type, max_allowed, first, count);
     }
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIWEBGLBUFFER
 
 protected:
 
     WebGLuint mGLName;
     bool mHasEverBeenBound;
     GLuint mByteLength;
     GLenum mTarget;
 
-    uint8_t mCachedMaxUbyteElement;
-    bool mHasCachedMaxUbyteElement;
-    uint16_t mCachedMaxUshortElement;
-    bool mHasCachedMaxUshortElement;
-
-    void* mData; // in the case of an Element Array Buffer, we keep a copy.
+    nsAutoPtr<WebGLElementArrayCache> mCache;
 };
 
 // NOTE: When this class is switched to new DOM bindings, update the (then-slow)
 // WrapObject calls in GetParameter and GetFramebufferAttachmentParameter.
 class WebGLTexture MOZ_FINAL
     : public nsIWebGLTexture
     , public WebGLRefCountedObject<WebGLTexture>
     , public LinkedListElement<WebGLTexture>
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -323,17 +323,16 @@ GLenum WebGLContext::CheckedBufferData(G
         return LOCAL_GL_NO_ERROR;
     }
 }
 
 void
 WebGLContext::BufferData(WebGLenum target, WebGLsizeiptr size,
                          WebGLenum usage)
 {
-    InvalidateCachedMinInUseAttribArrayLength();
     if (!IsContextStable())
         return;
 
     WebGLBuffer *boundBuffer = NULL;
 
     if (target == LOCAL_GL_ARRAY_BUFFER) {
         boundBuffer = mBoundArrayBuffer;
     } else if (target == LOCAL_GL_ELEMENT_ARRAY_BUFFER) {
@@ -347,33 +346,33 @@ WebGLContext::BufferData(WebGLenum targe
 
     if (!ValidateBufferUsageEnum(usage, "bufferData: usage"))
         return;
 
     if (!boundBuffer)
         return ErrorInvalidOperation("bufferData: no buffer bound!");
 
     MakeContextCurrent();
-    
+    InvalidateCachedMinInUseAttribArrayLength();
+
     GLenum error = CheckedBufferData(target, size, 0, usage);
     if (error) {
         GenerateWarning("bufferData generated error %s", ErrorName(error));
         return;
     }
 
     boundBuffer->SetByteLength(size);
-    boundBuffer->InvalidateCachedMaxElements();
-    if (!boundBuffer->ZeroDataIfElementArray())
+    if (!boundBuffer->ElementArrayCacheBufferData(nullptr, size)) {
         return ErrorOutOfMemory("bufferData: out of memory");
+    }
 }
 
 void
 WebGLContext::BufferData(WebGLenum target, ArrayBuffer *data, WebGLenum usage)
 {
-    InvalidateCachedMinInUseAttribArrayLength();
     if (!IsContextStable())
         return;
 
     if (!data) {
         // see http://www.khronos.org/bugzilla/show_bug.cgi?id=386
         return ErrorInvalidValue("bufferData: null object passed");
     }
 
@@ -389,34 +388,34 @@ WebGLContext::BufferData(WebGLenum targe
 
     if (!ValidateBufferUsageEnum(usage, "bufferData: usage"))
         return;
 
     if (!boundBuffer)
         return ErrorInvalidOperation("bufferData: no buffer bound!");
 
     MakeContextCurrent();
+    InvalidateCachedMinInUseAttribArrayLength();
 
     GLenum error = CheckedBufferData(target, data->Length(), data->Data(), usage);
 
     if (error) {
         GenerateWarning("bufferData generated error %s", ErrorName(error));
         return;
     }
 
     boundBuffer->SetByteLength(data->Length());
-    boundBuffer->InvalidateCachedMaxElements();
-    if (!boundBuffer->CopyDataIfElementArray(data->Data()))
+    if (!boundBuffer->ElementArrayCacheBufferData(data->Data(), data->Length())) {
         return ErrorOutOfMemory("bufferData: out of memory");
+    }
 }
 
 void
 WebGLContext::BufferData(WebGLenum target, ArrayBufferView& data, WebGLenum usage)
 {
-    InvalidateCachedMinInUseAttribArrayLength();
     if (!IsContextStable())
         return;
 
     WebGLBuffer *boundBuffer = NULL;
 
     if (target == LOCAL_GL_ARRAY_BUFFER) {
         boundBuffer = mBoundArrayBuffer;
     } else if (target == LOCAL_GL_ELEMENT_ARRAY_BUFFER) {
@@ -426,28 +425,29 @@ WebGLContext::BufferData(WebGLenum targe
     }
 
     if (!ValidateBufferUsageEnum(usage, "bufferData: usage"))
         return;
 
     if (!boundBuffer)
         return ErrorInvalidOperation("bufferData: no buffer bound!");
 
+    InvalidateCachedMinInUseAttribArrayLength();
     MakeContextCurrent();
 
     GLenum error = CheckedBufferData(target, data.Length(), data.Data(), usage);
     if (error) {
         GenerateWarning("bufferData generated error %s", ErrorName(error));
         return;
     }
 
     boundBuffer->SetByteLength(data.Length());
-    boundBuffer->InvalidateCachedMaxElements();
-    if (!boundBuffer->CopyDataIfElementArray(data.Data()))
+    if (!boundBuffer->ElementArrayCacheBufferData(data.Data(), data.Length())) {
         return ErrorOutOfMemory("bufferData: out of memory");
+    }
 }
 
 void
 WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr byteOffset,
                             ArrayBuffer *data)
 {
     if (!IsContextStable())
         return;
@@ -478,18 +478,17 @@ WebGLContext::BufferSubData(GLenum targe
         return ErrorInvalidOperation("bufferSubData: integer overflow computing the needed byte length");
 
     if (checked_neededByteLength.value() > boundBuffer->ByteLength())
         return ErrorInvalidOperation("bufferSubData: not enough data - operation requires %d bytes, but buffer only has %d bytes",
                                      checked_neededByteLength.value(), boundBuffer->ByteLength());
 
     MakeContextCurrent();
 
-    boundBuffer->CopySubDataIfElementArray(byteOffset, data->Length(), data->Data());
-    boundBuffer->InvalidateCachedMaxElements();
+    boundBuffer->ElementArrayCacheBufferSubData(byteOffset, data->Data(), data->Length());
 
     gl->fBufferSubData(target, byteOffset, data->Length(), data->Data());
 }
 
 void
 WebGLContext::BufferSubData(WebGLenum target, WebGLsizeiptr byteOffset,
                             ArrayBufferView& data)
 {
@@ -515,21 +514,19 @@ WebGLContext::BufferSubData(WebGLenum ta
     CheckedUint32 checked_neededByteLength = CheckedUint32(byteOffset) + data.Length();
     if (!checked_neededByteLength.isValid())
         return ErrorInvalidOperation("bufferSubData: integer overflow computing the needed byte length");
 
     if (checked_neededByteLength.value() > boundBuffer->ByteLength())
         return ErrorInvalidOperation("bufferSubData: not enough data -- operation requires %d bytes, but buffer only has %d bytes",
                                      checked_neededByteLength.value(), boundBuffer->ByteLength());
 
+    boundBuffer->ElementArrayCacheBufferSubData(byteOffset, data.Data(), data.Length());
+
     MakeContextCurrent();
-
-    boundBuffer->CopySubDataIfElementArray(byteOffset, data.Length(), data.Data());
-    boundBuffer->InvalidateCachedMaxElements();
-
     gl->fBufferSubData(target, byteOffset, data.Length(), data.Data());
 }
 
 WebGLenum
 WebGLContext::CheckFramebufferStatus(WebGLenum target)
 {
     if (!IsContextStable())
     {
@@ -1139,24 +1136,24 @@ WebGLContext::DepthRange(WebGLfloat zNea
 
     MakeContextCurrent();
     gl->fDepthRange(zNear, zFar);
 }
 
 void
 WebGLContext::DisableVertexAttribArray(WebGLuint index)
 {
-    InvalidateCachedMinInUseAttribArrayLength();
     if (!IsContextStable())
         return;
 
     if (!ValidateAttribIndex(index, "disableVertexAttribArray"))
         return;
 
     MakeContextCurrent();
+    InvalidateCachedMinInUseAttribArrayLength();
 
     if (index || gl->IsGLES2())
         gl->fDisableVertexAttribArray(index);
 
     mAttribBuffers[index].enabled = false;
 }
 
 int
@@ -1454,92 +1451,75 @@ WebGLContext::DrawElements(WebGLenum mod
         return;
 
     // If count is 0, there's nothing to do.
     if (count == 0)
         return;
 
     CheckedUint32 checked_byteCount;
 
+    WebGLsizei first = 0;
+
     if (type == LOCAL_GL_UNSIGNED_SHORT) {
         checked_byteCount = 2 * CheckedUint32(count);
         if (byteOffset % 2 != 0)
             return ErrorInvalidOperation("drawElements: invalid byteOffset for UNSIGNED_SHORT (must be a multiple of 2)");
+        first = byteOffset / 2;
     } else if (type == LOCAL_GL_UNSIGNED_BYTE) {
         checked_byteCount = count;
+        first = byteOffset;
     } else {
         return ErrorInvalidEnum("drawElements: type must be UNSIGNED_SHORT or UNSIGNED_BYTE");
     }
 
     if (!checked_byteCount.isValid())
         return ErrorInvalidValue("drawElements: overflow in byteCount");
 
     // If there is no current program, this is silently ignored.
     // Any checks below this depend on a program being available.
     if (!mCurrentProgram)
         return;
 
     if (!mBoundElementArrayBuffer)
         return ErrorInvalidOperation("drawElements: must have element array buffer binding");
 
-    if (!mBoundElementArrayBuffer->Data())
+    if (!mBoundElementArrayBuffer->ByteLength())
         return ErrorInvalidOperation("drawElements: bound element array buffer doesn't have any data");
 
     CheckedUint32 checked_neededByteCount = checked_byteCount + byteOffset;
 
     if (!checked_neededByteCount.isValid())
         return ErrorInvalidOperation("drawElements: overflow in byteOffset+byteCount");
 
     if (checked_neededByteCount.value() > mBoundElementArrayBuffer->ByteLength())
         return ErrorInvalidOperation("drawElements: bound element array buffer is too small for given count and offset");
 
     int32_t maxAllowedCount = 0;
     if (!ValidateBuffers(&maxAllowedCount, "drawElements"))
       return;
 
-    int32_t maxIndex
-      = type == LOCAL_GL_UNSIGNED_SHORT
-        ? mBoundElementArrayBuffer->FindMaxUshortElement()
-        : mBoundElementArrayBuffer->FindMaxUbyteElement();
-
-    CheckedInt32 checked_maxIndexPlusOne = CheckedInt32(maxIndex) + 1;
-
-    if (!checked_maxIndexPlusOne.isValid() ||
-        checked_maxIndexPlusOne.value() > maxAllowedCount)
-    {
-        // the index array contains invalid indices for the current drawing state, but they
-        // might not be used by the present drawElements call, depending on first and count.
-
-        int32_t maxIndexInSubArray
-          = type == LOCAL_GL_UNSIGNED_SHORT
-            ? mBoundElementArrayBuffer->FindMaxElementInSubArray<GLushort>(count, byteOffset)
-            : mBoundElementArrayBuffer->FindMaxElementInSubArray<GLubyte>(count, byteOffset);
-
-        CheckedInt32 checked_maxIndexInSubArrayPlusOne = CheckedInt32(maxIndexInSubArray) + 1;
-
-        if (!checked_maxIndexInSubArrayPlusOne.isValid() ||
-            checked_maxIndexInSubArrayPlusOne.value() > maxAllowedCount)
-        {
-            return ErrorInvalidOperation(
-                "DrawElements: bound vertex attribute buffers do not have sufficient "
-                "size for given indices from the bound element array");
-        }
+    int32_t maxAllowedIndex = NS_MAX(maxAllowedCount - 1, 0);
+
+    if (!mBoundElementArrayBuffer->Validate(type, maxAllowedIndex, first, count)) {
+        return ErrorInvalidOperation(
+            "DrawElements: bound vertex attribute buffers do not have sufficient "
+            "size for given indices from the bound element array");
     }
 
     MakeContextCurrent();
 
     if (mBoundFramebuffer) {
         if (!mBoundFramebuffer->CheckAndInitializeRenderbuffers())
             return ErrorInvalidFramebufferOperation("drawElements: incomplete framebuffer");
     } else {
         EnsureBackbufferClearedAsNeeded();
     }
 
     BindFakeBlackTextures();
-    if (!DoFakeVertexAttrib0(checked_maxIndexPlusOne.value()))
+    if (!DoFakeVertexAttrib0(maxAllowedCount))
         return;
 
     SetupContextLossTimer();
     gl->fDrawElements(mode, count, type, reinterpret_cast<GLvoid*>(byteOffset));
 
     UndoFakeVertexAttrib0();
     UnbindFakeBlackTextures();
 
@@ -1589,24 +1569,24 @@ WebGLContext::Disable(WebGLenum cap)
 
     MakeContextCurrent();
     gl->fDisable(cap);
 }
 
 void
 WebGLContext::EnableVertexAttribArray(WebGLuint index)
 {
-    InvalidateCachedMinInUseAttribArrayLength();
     if (!IsContextStable())
         return;
 
     if (!ValidateAttribIndex(index, "enableVertexAttribArray"))
         return;
 
     MakeContextCurrent();
+    InvalidateCachedMinInUseAttribArrayLength();
 
     gl->fEnableVertexAttribArray(index);
     mAttribBuffers[index].enabled = true;
 }
 
 void
 WebGLContext::FramebufferRenderbuffer(WebGLenum target, WebGLenum attachment, WebGLenum rbtarget, WebGLRenderbuffer *wrb)
 {
@@ -3019,23 +2999,25 @@ WebGLContext::IsEnabled(WebGLenum cap)
 
     MakeContextCurrent();
     return gl->fIsEnabled(cap);
 }
 
 void
 WebGLContext::LinkProgram(WebGLProgram *program)
 {
-    InvalidateCachedMinInUseAttribArrayLength();
     if (!IsContextStable())
         return;
 
     if (!ValidateObject("linkProgram", program))
         return;
 
+    InvalidateCachedMinInUseAttribArrayLength(); // we do it early in this function
+    // as some of the validation below changes program state
+
     GLuint progname = program->GLName();
 
     if (!program->NextGeneration()) {
         // XXX throw?
         return;
     }
 
     if (!program->HasBothShaderTypesAttached()) {
@@ -3951,25 +3933,26 @@ WebGLContext::name##_base(WebGLuint idx,
 SIMPLE_ARRAY_METHOD_NO_COUNT(VertexAttrib1fv, 1, WebGLfloat)
 SIMPLE_ARRAY_METHOD_NO_COUNT(VertexAttrib2fv, 2, WebGLfloat)
 SIMPLE_ARRAY_METHOD_NO_COUNT(VertexAttrib3fv, 3, WebGLfloat)
 SIMPLE_ARRAY_METHOD_NO_COUNT(VertexAttrib4fv, 4, WebGLfloat)
 
 void
 WebGLContext::UseProgram(WebGLProgram *prog)
 {
-    InvalidateCachedMinInUseAttribArrayLength();
     if (!IsContextStable())
         return;
 
     if (!ValidateObjectAllowNull("useProgram", prog))
         return;
 
-    WebGLuint progname = prog ? prog->GLName() : 0;;
     MakeContextCurrent();
+    InvalidateCachedMinInUseAttribArrayLength();
+
+    WebGLuint progname = prog ? prog->GLName() : 0;
 
     if (prog && !prog->LinkStatus())
         return ErrorInvalidOperation("useProgram: program was not linked successfully");
 
     gl->fUseProgram(progname);
 
     mCurrentProgram = prog;
 }
@@ -4509,17 +4492,16 @@ WebGLContext::ShaderSource(WebGLShader *
     shader->SetNeedsTranslation();
 }
 
 void
 WebGLContext::VertexAttribPointer(WebGLuint index, WebGLint size, WebGLenum type,
                                   WebGLboolean normalized, WebGLsizei stride,
                                   WebGLintptr byteOffset)
 {
-    InvalidateCachedMinInUseAttribArrayLength();
     if (!IsContextStable())
         return;
 
     if (mBoundArrayBuffer == nullptr)
         return ErrorInvalidOperation("vertexAttribPointer: must have valid GL_ARRAY_BUFFER binding");
 
     WebGLsizei requiredAlignment = 1;
     switch (type) {
@@ -4559,17 +4541,19 @@ WebGLContext::VertexAttribPointer(WebGLu
                                      "requirement of given type");
     }
 
     if (byteOffset & requiredAlignmentMask) {
         return ErrorInvalidOperation("vertexAttribPointer: byteOffset doesn't satisfy the alignment "
                                      "requirement of given type");
 
     }
-    
+
+    InvalidateCachedMinInUseAttribArrayLength();
+
     /* XXX make work with bufferSubData & heterogeneous types 
     if (type != mBoundArrayBuffer->GLType())
         return ErrorInvalidOperation("vertexAttribPointer: type must match bound VBO type: %d != %d", type, mBoundArrayBuffer->GLType());
     */
 
     WebGLVertexAttribData &vd = mAttribBuffers[index];
 
     vd.buf = mBoundArrayBuffer;
new file mode 100644
--- /dev/null
+++ b/content/canvas/src/WebGLElementArrayCache.cpp
@@ -0,0 +1,539 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "WebGLElementArrayCache.h"
+
+#include "nsTArray.h"
+#include "mozilla/Assertions.h"
+
+#include <cstdlib>
+#include <cstring>
+
+namespace mozilla {
+
+/*
+ * WebGLElementArrayCacheTree contains most of the implementation of WebGLElementArrayCache,
+ * which performs WebGL element array buffer validation for drawElements.
+ *
+ * Attention: Here lie nontrivial data structures, bug-prone algorithms, and non-canonical tweaks!
+ * Whence the explanatory comments, and compiled unit test.
+ *
+ * *** What problem are we solving here? ***
+ *
+ * WebGL::DrawElements has to validate that the elements are in range wrt the current vertex attribs.
+ * This boils down to the problem, given an array of integers, of computing the maximum in an arbitrary
+ * sub-array. The naive algorithm has linear complexity; this has been a major performance problem,
+ * see bug 569431. In that bug, we took the approach of caching the max for the whole array, which
+ * does cover most cases (DrawElements typically consumes the whole element array buffer) but doesn't
+ * help in other use cases:
+ *  - when doing "partial DrawElements" i.e. consuming only part of the element array buffer
+ *  - when doing frequent "partial buffer updates" i.e. bufferSubData calls updating parts of the
+ *    element array buffer
+ *
+ * *** The solution: a binary tree ***
+ *
+ * The solution implemented here is to use a binary tree as the cache data structure. Each tree node
+ * contains the max of its two children nodes. In this way, finding the maximum in any contiguous sub-array
+ * has log complexity instead of linear complexity.
+ *
+ * Simplistically, if the element array is
+ *
+ *     1   4   3   2
+ *
+ * then the corresponding tree is
+ *
+ *           4
+ *         _/ \_
+ *       4       3
+ *      / \     / \
+ *     1   4   3   2
+ *
+ * In practice, the bottom-most levels of the tree are both the largest to store (because they
+ * have more nodes), and the least useful performance-wise (because each node in the bottom
+ * levels concerns only few entries in the elements array buffer, it is cheap to compute).
+ *
+ * For this reason, we stop the tree a few levels above, so that each tree leaf actually corresponds
+ * to more than one element array entry.
+ *
+ * The number of levels that we "drop" is |sSkippedBottomTreeLevels| and the number of element array entries
+ * that each leaf corresponds to, is |sElementsPerLeaf|. This being a binary tree, we have
+ *
+ *   sElementsPerLeaf = 2 ^ sSkippedBottomTreeLevels.
+ *
+ * *** Storage layout of the binary tree ***
+ *
+ * We take advantage of the specifics of the situation to avoid generalist tree storage and instead
+ * store the tree entries in a vector, mTreeData.
+ *
+ * The number of leaves is given by mNumLeaves, and mTreeData is always a vector of length
+ *
+ *    2 * mNumLeaves.
+ *
+ * Its data layout is as follows: mTreeData[0] is unused, mTreeData[1] is the root node,
+ * then at offsets 2..3 is the tree level immediately below the root node, then at offsets 4..7
+ * is the tree level below that, etc.
+ *
+ * The figure below illustrates this by writing at each tree node the offset into mTreeData at
+ * which it is stored:
+ *
+ *           1
+ *         _/ \_
+ *       2       3
+ *      / \     / \
+ *     4   5   6   7
+ *    ...
+ *
+ * Thus, under the convention that the root level is level 0, we see that level N is stored at offsets
+ *
+ *    [ 2^n .. 2^(n+1) - 1 ]
+ *
+ * in mTreeData. Likewise, all the usual tree operations have simple mathematical expressions in
+ * terms of mTreeData offsets, see all the methods such as ParentNode, LeftChildNode, etc.
+ *
+ * *** Design constraint: element types aren't known at buffer-update time ***
+ *
+ * Note that a key constraint that we're operating under, is that we don't know the types of the elements
+ * by the time WebGL bufferData/bufferSubData methods are called. The type of elements is only
+ * specified in the drawElements call. This means that we may potentially have to store caches for
+ * multiple element types, for the same element array buffer. Since we don't know yet how many
+ * element types we'll eventually support (extensions add more), the concern about memory usage is serious.
+ * This is addressed by sSkippedBottomTreeLevels as explained above. Of course, in the typical
+ * case where each element array buffer is only ever used with one type, this is also addressed
+ * by having WebGLElementArrayCache lazily create trees for each type only upon first use.
+ *
+ * Another consequence of this constraint is that when invalidating the trees, we have to invalidate
+ * all existing trees. So if trees for types uint8_t and uint16_t have ever been constructed for this buffer,
+ * every subsequent invalidation will have to invalidate both trees even if one of the two types is never
+ * used again. This implies that it is important to minimize the cost of invalidation i.e.
+ * do lazy updates upon use as opposed to immediately updating invalidated trees. This poses a problem:
+ * it is nontrivial to keep track of the part of the tree that's invalidated. The current solution
+ * can only keep track of an invalidated interval, from |mFirstInvalidatedLeaf| to |mLastInvalidatedLeaf|.
+ * The problem is that if one does two small, far-apart partial buffer updates, the resulting invalidated
+ * area is very large even though only a small part of the array really needed to be invalidated.
+ * The real solution to this problem would be to use a smarter data structure to keep track of the
+ * invalidated area, probably an interval tree. Meanwhile, we can probably live with the current situation
+ * as the unfavorable case seems to be a small corner case: in order to run into performance issues,
+ * the number of bufferSubData in between two consecutive draws must be small but greater than 1, and
+ * the partial buffer updates must be small and far apart. Anything else than this corner case
+ * should run fast in the current setting.
+ */
+template<typename T>
+struct WebGLElementArrayCacheTree
+{
+  // A too-high sSkippedBottomTreeLevels would harm the performance of small drawElements calls
+  // A too-low sSkippedBottomTreeLevels would cause undue memory usage.
+  // The current value has been validated by some benchmarking. See bug 732660.
+  static const size_t sSkippedBottomTreeLevels = 3;
+  static const size_t sElementsPerLeaf = 1 << sSkippedBottomTreeLevels;
+  static const size_t sElementsPerLeafMask = sElementsPerLeaf - 1; // sElementsPerLeaf is POT
+
+private:
+  WebGLElementArrayCache& mParent;
+  nsTArray<T> mTreeData;
+  size_t mNumLeaves;
+  bool mInvalidated;
+  size_t mFirstInvalidatedLeaf;
+  size_t mLastInvalidatedLeaf;
+
+public:
+  WebGLElementArrayCacheTree(WebGLElementArrayCache& p)
+    : mParent(p)
+    , mNumLeaves(0)
+    , mInvalidated(false)
+    , mFirstInvalidatedLeaf(0)
+    , mLastInvalidatedLeaf(0)
+  {
+    ResizeToParentSize();
+    Invalidate(0, mParent.ByteSize() - 1);
+  }
+
+  T GlobalMaximum() const {
+    MOZ_ASSERT(!mInvalidated);
+    return mTreeData[1];
+  }
+
+  // returns the index of the parent node; if treeIndex=1 (the root node),
+  // the return value is 0.
+  static size_t ParentNode(size_t treeIndex) {
+    MOZ_ASSERT(treeIndex);
+    return treeIndex >> 1;
+  }
+
+  static bool IsRightNode(size_t treeIndex) {
+    MOZ_ASSERT(treeIndex);
+    return treeIndex & 1;
+  }
+
+  static bool IsLeftNode(size_t treeIndex) {
+    MOZ_ASSERT(treeIndex);
+    return !IsRightNode(treeIndex);
+  }
+
+  static size_t SiblingNode(size_t treeIndex) {
+    MOZ_ASSERT(treeIndex);
+    return treeIndex ^ 1;
+  }
+
+  static size_t LeftChildNode(size_t treeIndex) {
+    MOZ_ASSERT(treeIndex);
+    return treeIndex << 1;
+  }
+
+  static size_t RightChildNode(size_t treeIndex) {
+    MOZ_ASSERT(treeIndex);
+    return SiblingNode(LeftChildNode(treeIndex));
+  }
+
+  static size_t LeftNeighborNode(size_t treeIndex, size_t distance = 1) {
+    MOZ_ASSERT(treeIndex);
+    return treeIndex - distance;
+  }
+
+  static size_t RightNeighborNode(size_t treeIndex, size_t distance = 1) {
+    MOZ_ASSERT(treeIndex);
+    return treeIndex + distance;
+  }
+
+  size_t LeafForElement(size_t element) {
+    size_t leaf = element / sElementsPerLeaf;
+    MOZ_ASSERT(leaf < mNumLeaves);
+    return leaf;
+  }
+
+  size_t LeafForByte(size_t byte) {
+    return LeafForElement(byte / sizeof(T));
+  }
+
+  // Returns the index, into the tree storage, where a given leaf is stored
+  size_t TreeIndexForLeaf(size_t leaf) {
+    // See above class comment. The tree storage is an array of length 2*mNumLeaves.
+    // The leaves are stored in its second half.
+    return leaf + mNumLeaves;
+  }
+
+  static size_t LastElementUnderSameLeaf(size_t element) {
+    return element | sElementsPerLeafMask;
+  }
+
+  static size_t FirstElementUnderSameLeaf(size_t element) {
+    return element & ~sElementsPerLeafMask;
+  }
+
+  static size_t NextMultipleOfElementsPerLeaf(size_t numElements) {
+    return ((numElements - 1) | sElementsPerLeafMask) + 1;
+  }
+
+  bool Validate(T maxAllowed, size_t firstLeaf, size_t lastLeaf) {
+    MOZ_ASSERT(!mInvalidated);
+
+    size_t firstTreeIndex = TreeIndexForLeaf(firstLeaf);
+    size_t lastTreeIndex  = TreeIndexForLeaf(lastLeaf);
+
+    while (true) {
+      // given that we tweak these values in nontrivial ways, it doesn't hurt to do
+      // this sanity check
+      MOZ_ASSERT(firstTreeIndex <= lastTreeIndex);
+
+      // final case where there is only 1 node to validate at the current tree level
+      if (lastTreeIndex == firstTreeIndex) {
+        return mTreeData[firstTreeIndex] <= maxAllowed;
+      }
+
+      // if the first node at current tree level is a right node, handle it individually
+      // and replace it with its right neighbor, which is a left node
+      if (IsRightNode(firstTreeIndex)) {
+        if (mTreeData[firstTreeIndex] > maxAllowed)
+          return false;
+        firstTreeIndex = RightNeighborNode(firstTreeIndex);
+      }
+
+      // if the last node at current tree level is a left node, handle it individually
+      // and replace it with its left neighbor, which is a right node
+      if (IsLeftNode(lastTreeIndex)) {
+        if (mTreeData[lastTreeIndex] > maxAllowed)
+          return false;
+        lastTreeIndex = LeftNeighborNode(lastTreeIndex);
+      }
+
+      // at this point it can happen that firstTreeIndex and lastTreeIndex "crossed" each
+      // other. That happens if firstTreeIndex was a right node and lastTreeIndex was its
+      // right neighor: in that case, both above tweaks happened and as a result, they ended
+      // up being swapped: lastTreeIndex is now the _left_ neighbor of firstTreeIndex.
+      // When that happens, there is nothing left to validate.
+      if (lastTreeIndex == LeftNeighborNode(firstTreeIndex)) {
+        return true;
+      }
+
+      // walk up 1 level
+      firstTreeIndex = ParentNode(firstTreeIndex);
+      lastTreeIndex = ParentNode(lastTreeIndex);
+    }
+  }
+
+  template<typename U>
+  static U NextPowerOfTwo(U x) {
+    U result = 1;
+    while (result < x)
+      result <<= 1;
+    MOZ_ASSERT(result >= x);
+    MOZ_ASSERT((result & (result - 1)) == 0);
+    return result;
+  }
+
+  bool ResizeToParentSize()
+  {
+    size_t numberOfElements = mParent.ByteSize() / sizeof(T);
+    size_t requiredNumLeaves = (numberOfElements + sElementsPerLeaf - 1) / sElementsPerLeaf;
+
+    mNumLeaves = NextPowerOfTwo(requiredNumLeaves);
+
+    // see class comment for why we the tree storage size is 2 * mNumLeaves
+    return mTreeData.SetLength(2 * mNumLeaves);
+  }
+
+  void Invalidate(size_t firstByte, size_t lastByte);
+
+  void Update();
+
+  size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const
+  {
+    return aMallocSizeOf(this) + aMallocSizeOf(mTreeData.Elements());
+  }
+};
+
+// TreeForType: just a template helper to select the right tree object for a given
+// element type.
+template<typename T>
+struct TreeForType {};
+
+template<>
+struct TreeForType<uint8_t>
+{
+  static WebGLElementArrayCacheTree<uint8_t>*& Run(WebGLElementArrayCache *b) { return b->mUint8Tree; }
+};
+
+template<>
+struct TreeForType<uint16_t>
+{
+  static WebGLElementArrayCacheTree<uint16_t>*& Run(WebGLElementArrayCache *b) { return b->mUint16Tree; }
+};
+
+// When the buffer gets updated from firstByte to lastByte,
+// calling this method will notify the tree accordingly
+template<typename T>
+void WebGLElementArrayCacheTree<T>::Invalidate(size_t firstByte, size_t lastByte)
+{
+  lastByte = NS_MIN(lastByte, mNumLeaves * sElementsPerLeaf * sizeof(T) - 1);
+
+  size_t firstLeaf = LeafForByte(firstByte);
+  size_t lastLeaf = LeafForByte(lastByte);
+
+  if (mInvalidated) {
+    mFirstInvalidatedLeaf = NS_MIN(firstLeaf, mFirstInvalidatedLeaf);
+    mLastInvalidatedLeaf = NS_MAX(lastLeaf, mLastInvalidatedLeaf);
+  } else {
+    mInvalidated = true;
+    mFirstInvalidatedLeaf = firstLeaf;
+    mLastInvalidatedLeaf = lastLeaf;
+  }
+}
+
+
+// When tree has been partially invalidated, from mFirstInvalidatedLeaf to
+// mLastInvalidatedLeaf, calling this method will 1) update the leaves in this interval
+// from the raw buffer data, and 2) propagate this update up the tree
+template<typename T>
+void WebGLElementArrayCacheTree<T>::Update()
+{
+  if (!mInvalidated) {
+    return;
+  }
+
+  MOZ_ASSERT(mLastInvalidatedLeaf < mNumLeaves);
+
+  size_t firstTreeIndex = TreeIndexForLeaf(mFirstInvalidatedLeaf);
+  size_t lastTreeIndex = TreeIndexForLeaf(mLastInvalidatedLeaf);
+
+  // Step #1: initialize the tree leaves from plain buffer data.
+  // That is, each tree leaf must be set to the max of the |sElementsPerLeaf| corresponding
+  // buffer entries.
+  // condition-less scope to prevent leaking this scope's variables into the code below
+  {
+    // treeIndex is the index of the tree leaf we're writing, i.e. the destination index
+    size_t treeIndex = firstTreeIndex;
+    // srcIndex is the index in the source buffer
+    size_t srcIndex = mFirstInvalidatedLeaf * sElementsPerLeaf;
+    size_t numberOfElements = mParent.ByteSize() / sizeof(T);
+    while (treeIndex <= lastTreeIndex) {
+      T m = 0;
+      size_t a = srcIndex;
+      size_t srcIndexNextLeaf = NS_MIN(a + sElementsPerLeaf, numberOfElements);
+      for (; srcIndex < srcIndexNextLeaf; srcIndex++) {
+        m = NS_MAX(m, mParent.Element<T>(srcIndex));
+      }
+      mTreeData[treeIndex] = m;
+      treeIndex++;
+    }
+  }
+
+  // Step #2: propagate the values up the tree. This is simply a matter of walking up
+  // the tree and setting each node to the max of its two children.
+  while (true) {
+
+    // fast-exit case where only one node is invalidated at the current level
+    if (firstTreeIndex == lastTreeIndex) {
+      size_t firstTreeIndexParent = ParentNode(firstTreeIndex);
+      while (firstTreeIndexParent) {
+        mTreeData[firstTreeIndexParent] = NS_MAX(mTreeData[firstTreeIndex], mTreeData[SiblingNode(firstTreeIndex)]);
+        firstTreeIndex = firstTreeIndexParent;
+        firstTreeIndexParent = ParentNode(firstTreeIndex);
+      }
+      break;
+    }
+
+    // move up 1 level
+    firstTreeIndex = ParentNode(firstTreeIndex);
+    lastTreeIndex = ParentNode(lastTreeIndex);
+
+    // initialize local iteration variables: child and parent.
+    size_t child = LeftChildNode(firstTreeIndex);
+    size_t parent = firstTreeIndex;
+
+    // the unrolling makes this look more complicated than it is; the plain non-unrolled
+    // version is in the second while loop below
+    const int unrollSize = 8;
+    while (RightNeighborNode(parent, unrollSize - 1) <= lastTreeIndex)
+    {
+      for (int unroll = 0; unroll < unrollSize; unroll++)
+      {
+        T a = mTreeData[child];
+        child = RightNeighborNode(child);
+        T b = mTreeData[child];
+        child = RightNeighborNode(child);
+        mTreeData[parent] = NS_MAX(a, b);
+        parent = RightNeighborNode(parent);
+      }
+    }
+    // plain non-unrolled version, used to terminate the job after the last unrolled iteration
+    while (parent <= lastTreeIndex)
+    {
+      T a = mTreeData[child];
+      child = RightNeighborNode(child);
+      T b = mTreeData[child];
+      child = RightNeighborNode(child);
+      mTreeData[parent] = NS_MAX(a, b);
+      parent = RightNeighborNode(parent);
+    }
+  }
+
+  mInvalidated = false;
+}
+
+WebGLElementArrayCache::~WebGLElementArrayCache() {
+  delete mUint8Tree;
+  delete mUint16Tree;
+  free(mUntypedData);
+}
+
+bool WebGLElementArrayCache::BufferData(const void* ptr, size_t byteSize) {
+  mByteSize = byteSize;
+  if (mUint8Tree)
+    if (!mUint8Tree->ResizeToParentSize())
+      return false;
+  if (mUint16Tree)
+    if (!mUint16Tree->ResizeToParentSize())
+      return false;
+  mUntypedData = realloc(mUntypedData, byteSize);
+  if (!mUntypedData)
+    return false;
+  BufferSubData(0, ptr, byteSize);
+  return true;
+}
+
+void WebGLElementArrayCache::BufferSubData(size_t pos, const void* ptr, size_t updateByteSize) {
+  if (!updateByteSize) return;
+  if (ptr)
+      memcpy(static_cast<uint8_t*>(mUntypedData) + pos, ptr, updateByteSize);
+  else
+      memset(static_cast<uint8_t*>(mUntypedData) + pos, 0, updateByteSize);
+  InvalidateTrees(pos, pos + updateByteSize - 1);
+}
+
+void WebGLElementArrayCache::InvalidateTrees(size_t firstByte, size_t lastByte)
+{
+  if (mUint8Tree)
+    mUint8Tree->Invalidate(firstByte, lastByte);
+  if (mUint16Tree)
+    mUint16Tree->Invalidate(firstByte, lastByte);
+}
+
+template<typename T>
+bool WebGLElementArrayCache::Validate(T maxAllowed, size_t firstElement, size_t countElements) {
+  if (!mByteSize || !countElements)
+    return true;
+
+  WebGLElementArrayCacheTree<T>*& tree = TreeForType<T>::Run(this);
+  if (!tree) {
+    tree = new WebGLElementArrayCacheTree<T>(*this);
+  }
+
+  size_t lastElement = firstElement + countElements - 1;
+
+  tree->Update();
+
+  // fast exit path when the global maximum for the whole element array buffer
+  // falls in the allowed range
+  if (tree->GlobalMaximum() <= maxAllowed)
+  {
+    return true;
+  }
+
+  const T* elements = Elements<T>();
+
+  // before calling tree->Validate, we have to validate ourselves the boundaries of the elements span,
+  // to round them to the nearest multiple of sElementsPerLeaf.
+  size_t firstElementAdjustmentEnd = NS_MIN(lastElement,
+                                            tree->LastElementUnderSameLeaf(firstElement));
+  while (firstElement <= firstElementAdjustmentEnd) {
+    if (elements[firstElement] > maxAllowed)
+      return false;
+    firstElement++;
+  }
+  size_t lastElementAdjustmentEnd = NS_MAX(firstElement,
+                                           tree->FirstElementUnderSameLeaf(lastElement));
+  while (lastElement >= lastElementAdjustmentEnd) {
+    if (elements[lastElement] > maxAllowed)
+      return false;
+    lastElement--;
+  }
+
+  // at this point, for many tiny validations, we're already done.
+  if (firstElement > lastElement)
+    return true;
+
+  // general case
+  return tree->Validate(maxAllowed,
+                        tree->LeafForElement(firstElement),
+                        tree->LeafForElement(lastElement));
+}
+
+bool WebGLElementArrayCache::Validate(GLenum type, uint32_t maxAllowed, size_t firstElement, size_t countElements) {
+  if (type == LOCAL_GL_UNSIGNED_BYTE)
+    return Validate<uint8_t>(uint8_t(maxAllowed), firstElement, countElements);
+  if (type == LOCAL_GL_UNSIGNED_SHORT)
+    return Validate<uint16_t>(uint16_t(maxAllowed), firstElement, countElements);
+  return false;
+}
+
+size_t WebGLElementArrayCache::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const {
+  size_t uint8TreeSize  = mUint8Tree  ? mUint8Tree->SizeOfIncludingThis(aMallocSizeOf) : 0;
+  size_t uint16TreeSize = mUint16Tree ? mUint16Tree->SizeOfIncludingThis(aMallocSizeOf) : 0;
+  return aMallocSizeOf(this) +
+          mByteSize +
+          uint8TreeSize +
+          uint16TreeSize;
+}
+
+} // end namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/canvas/src/WebGLElementArrayCache.h
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 WEBGLELEMENTARRAYCACHE_H
+#define WEBGLELEMENTARRAYCACHE_H
+
+#include "mozilla/StandardInteger.h"
+#include "nscore.h"
+#include "GLDefs.h"
+
+namespace mozilla {
+
+template<typename T>
+struct WebGLElementArrayCacheTree;
+
+/*
+ * WebGLElementArrayCache implements WebGL element array buffer validation for drawElements.
+ *
+ * Its exposes methods meant to be called by WebGL method implementations:
+ *  - Validate, to be called by WebGLContext::DrawElements, is where we use the cache
+ *  - BufferData and BufferSubData, to be called by eponymous WebGL methods, are how
+ *    data is fed into the cache
+ *
+ * Most of the implementation is hidden in the auxilary class template, WebGLElementArrayCacheTree.
+ * Refer to its code for design comments.
+ */
+class WebGLElementArrayCache {
+
+public:
+  bool BufferData(const void* ptr, size_t byteSize);
+  void BufferSubData(size_t pos, const void* ptr, size_t updateByteSize);
+
+  bool Validate(GLenum type, uint32_t maxAllowed, size_t first, size_t count);
+
+  template<typename T>
+  T Element(size_t i) const { return Elements<T>()[i]; }
+
+  WebGLElementArrayCache()
+    : mUntypedData(nullptr)
+    , mByteSize(0)
+    , mUint8Tree(nullptr)
+    , mUint16Tree(nullptr)
+  {}
+
+  ~WebGLElementArrayCache();
+
+  size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const;
+
+private:
+
+  template<typename T>
+  bool Validate(T maxAllowed, size_t first, size_t count);
+
+  size_t ByteSize() const {
+    return mByteSize;
+  }
+
+  template<typename T>
+  const T* Elements() const { return static_cast<const T*>(mUntypedData); }
+  template<typename T>
+  T* Elements() { return static_cast<T*>(mUntypedData); }
+
+  void InvalidateTrees(size_t firstByte, size_t lastByte);
+
+  template<typename T>
+  friend class WebGLElementArrayCacheTree;
+  template<typename T>
+  friend struct TreeForType;
+
+  void* mUntypedData;
+  size_t mByteSize;
+  WebGLElementArrayCacheTree<uint8_t>* mUint8Tree;
+  WebGLElementArrayCacheTree<uint16_t>* mUint16Tree;
+};
+
+
+} // end namespace mozilla
+
+#endif // WEBGLELEMENTARRAYCACHE_H
--- a/content/canvas/test/Makefile.in
+++ b/content/canvas/test/Makefile.in
@@ -5,16 +5,18 @@
 
 DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = @relativesrcdir@
 DIRS		+= webgl crossorigin
 
+TOOLS_DIRS += compiled
+
 include $(DEPTH)/config/autoconf.mk
 MOCHITEST_FILES = \
 	test_canvas.html \
 	image_transparent50.png \
 	image_redtransparent.png \
 	image_yellow.png \
 	image_anim-poster-gr.png \
 	image_green-16x16.png \
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/compiled/Makefile.in
@@ -0,0 +1,22 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEPTH := @DEPTH@
+topsrcdir := @top_srcdir@
+srcdir := @srcdir@
+VPATH := @srcdir@
+
+FAIL_ON_WARNINGS = 1
+
+include $(DEPTH)/config/autoconf.mk
+
+LOCAL_INCLUDES := \
+    -I$(srcdir)/../../src \
+    $(NULL)
+
+CPP_UNIT_TESTS := \
+  TestWebGLElementArrayCache.cpp \
+  $(NULL)
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/compiled/TestWebGLElementArrayCache.cpp
@@ -0,0 +1,167 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Assertions.h"
+
+#include "WebGLElementArrayCache.cpp"
+
+#include <cstdlib>
+#include <iostream>
+#include "nscore.h"
+#include "nsTArray.h"
+
+using namespace mozilla;
+
+int gTestsPassed = 0;
+
+void VerifyImplFunction(bool condition, const char* file, int line)
+{
+  if (condition) {
+    gTestsPassed++;
+  } else {
+    std::cerr << "Test failed at " << file << ":" << line << std::endl;
+    abort();
+  }
+}
+
+#define VERIFY(condition) \
+    VerifyImplFunction((condition), __FILE__, __LINE__)
+
+void MakeRandomVector(nsTArray<uint8_t>& a, size_t size) {
+  a.SetLength(size);
+  // only the most-significant bits of rand() are reasonably random
+  // 16 here is arbitrary, may fail on platforms where RAND_MAX is low,
+  // but guarded by an assertion.
+  enum { bitsToIgnore = 16 };
+  MOZ_STATIC_ASSERT((unsigned int)(RAND_MAX) >> (8 + bitsToIgnore),
+                    "Didn't expect RAND_MAX to be so low");
+  for (size_t i = 0; i < size; i++)
+    a[i] = static_cast<uint8_t>((unsigned int)(rand()) >> bitsToIgnore);
+}
+
+template<typename T>
+T RandomInteger(T a, T b)
+{
+  T result(a + rand() % (b - a + 1));
+  return result;
+}
+
+template<typename T>
+GLenum GLType()
+{
+  return sizeof(T) == 1
+             ? LOCAL_GL_UNSIGNED_BYTE
+             : LOCAL_GL_UNSIGNED_SHORT;
+}
+
+template<typename T>
+void CheckValidateOneType(WebGLElementArrayCache& c, size_t firstByte, size_t countBytes)
+{
+  size_t first = firstByte / sizeof(T);
+  size_t count = countBytes / sizeof(T);
+
+  GLenum type = GLType<T>();
+
+  T max = 0;
+  for (size_t i = 0; i < count; i++)
+    if (c.Element<T>(first + i) > max)
+      max = c.Element<T>(first + i);
+
+  VERIFY(c.Validate(type, max, first, count));
+  VERIFY(c.Validate(type, T(-1), first, count));
+  if (T(max + 1)) VERIFY(c.Validate(type, T(max + 1), first, count));
+  if (max > 0) {
+    VERIFY(!c.Validate(type, max - 1, first, count));
+    VERIFY(!c.Validate(type, 0, first, count));
+  }
+}
+
+void CheckValidate(WebGLElementArrayCache& c, size_t firstByte, size_t countBytes)
+{
+  CheckValidateOneType<uint8_t>(c, firstByte, countBytes);
+  CheckValidateOneType<uint16_t>(c, firstByte, countBytes);
+}
+
+template<typename T>
+void CheckSanity()
+{
+  const size_t numElems = 64; // should be significantly larger than tree leaf size to
+                        // ensure we exercise some nontrivial tree-walking
+  T data[numElems] = {1,0,3,1,2,6,5,4}; // intentionally specify only 8 elements for now
+  size_t numBytes = numElems * sizeof(T);
+  MOZ_ASSERT(numBytes == sizeof(data));
+
+  GLenum type = GLType<T>();
+
+  WebGLElementArrayCache c;
+  c.BufferData(data, numBytes);
+  VERIFY( c.Validate(type, 6, 0, 8));
+  VERIFY(!c.Validate(type, 5, 0, 8));
+  VERIFY( c.Validate(type, 3, 0, 3));
+  VERIFY(!c.Validate(type, 2, 0, 3));
+  VERIFY( c.Validate(type, 6, 2, 4));
+  VERIFY(!c.Validate(type, 5, 2, 4));
+
+  c.BufferSubData(5*sizeof(T), data, sizeof(T));
+  VERIFY( c.Validate(type, 5, 0, 8));
+  VERIFY(!c.Validate(type, 4, 0, 8));
+
+  // now test a somewhat larger size to ensure we exceed the size of a tree leaf
+  for(size_t i = 0; i < numElems; i++)
+    data[i] = numElems - i;
+  c.BufferData(data, numBytes);
+  VERIFY( c.Validate(type, numElems,     0, numElems));
+  VERIFY(!c.Validate(type, numElems - 1, 0, numElems));
+
+  MOZ_ASSERT(numElems > 10);
+  VERIFY( c.Validate(type, numElems - 10, 10, numElems - 10));
+  VERIFY(!c.Validate(type, numElems - 11, 10, numElems - 10));
+}
+
+int main(int argc, char *argv[])
+{
+  srand(0); // do not want a random seed here.
+
+  CheckSanity<uint8_t>();
+  CheckSanity<uint16_t>();
+
+  nsTArray<uint8_t> v, vsub;
+  WebGLElementArrayCache b;
+
+  for (int maxBufferSize = 1; maxBufferSize <= 4096; maxBufferSize *= 2) {
+    int repeat = std::min(maxBufferSize, 20);
+    for (int i = 0; i < repeat; i++) {
+      size_t size = RandomInteger<size_t>(1, maxBufferSize);
+      MakeRandomVector(v, size);
+      b.BufferData(v.Elements(), size);
+      CheckValidate(b, 0, size);
+
+      for (int j = 0; j < 16; j++) {
+        for (int bufferSubDataCalls = 1; bufferSubDataCalls <= 8; bufferSubDataCalls *= 2) {
+          for (int validateCalls = 1; validateCalls <= 8; validateCalls *= 2) {
+
+            size_t offset = 0, subsize = 0;
+
+            for (int k = 0; k < bufferSubDataCalls; k++) {
+              offset = RandomInteger<size_t>(0, size);
+              subsize = RandomInteger<size_t>(0, size - offset);
+              MakeRandomVector(vsub, subsize);
+              b.BufferSubData(offset, vsub.Elements(), subsize);
+            }
+
+            for (int k = 0; k < validateCalls; k++) {
+              offset = RandomInteger<size_t>(0, size);
+              subsize = RandomInteger<size_t>(0, size - offset);
+              CheckValidate(b, offset, subsize);
+            }
+          } // validateCalls
+        } // bufferSubDataCalls
+      } // j
+    } // i
+  } // maxBufferSize
+
+  std::cerr << argv[0] << ": all " << gTestsPassed << " tests passed" << std::endl;
+  return 0;
+}
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/AudioBufferSourceNode.cpp
@@ -0,0 +1,29 @@
+/* -*- 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 "AudioBufferSourceNode.h"
+#include "mozilla/dom/AudioBufferSourceNodeBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_ISUPPORTS_INHERITED0(AudioBufferSourceNode, AudioSourceNode)
+
+AudioBufferSourceNode::AudioBufferSourceNode(AudioContext* aContext)
+  : AudioSourceNode(aContext)
+{
+}
+
+JSObject*
+AudioBufferSourceNode::WrapObject(JSContext* aCx, JSObject* aScope,
+                                  bool* aTriedToWrap)
+{
+  return AudioBufferSourceNodeBinding::Wrap(aCx, aScope, this, aTriedToWrap);
+}
+
+}
+}
+
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/AudioBufferSourceNode.h
@@ -0,0 +1,31 @@
+/* -*- 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/. */
+
+#pragma once
+
+#include "AudioSourceNode.h"
+
+namespace mozilla {
+namespace dom {
+
+class AudioBufferSourceNode : public AudioSourceNode
+{
+public:
+  explicit AudioBufferSourceNode(AudioContext* aContext);
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+  void NoteOn(double) { /* no-op for now */ }
+  void NoteOff(double) { /* no-op for now */ }
+
+  virtual JSObject* WrapObject(JSContext* aCx, JSObject* aScope,
+                               bool* aTriedToWrap);
+
+};
+
+}
+}
+
--- a/content/media/webaudio/AudioContext.cpp
+++ b/content/media/webaudio/AudioContext.cpp
@@ -4,30 +4,33 @@
  * 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 "AudioContext.h"
 #include "nsContentUtils.h"
 #include "nsIDOMWindow.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/AudioContextBinding.h"
+#include "AudioDestinationNode.h"
+#include "AudioBufferSourceNode.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(AudioContext, mWindow)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_2(AudioContext, mWindow, mDestination)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(AudioContext)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(AudioContext)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AudioContext)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 AudioContext::AudioContext(nsIDOMWindow* aWindow)
   : mWindow(aWindow)
+  , mDestination(new AudioDestinationNode(this))
 {
   SetIsDOMBinding();
 }
 
 AudioContext::~AudioContext()
 {
 }
 
@@ -47,11 +50,19 @@ AudioContext::Constructor(nsISupports* a
     return nullptr;
   }
 
   AudioContext* object = new AudioContext(window);
   NS_ADDREF(object);
   return object;
 }
 
+already_AddRefed<AudioBufferSourceNode>
+AudioContext::CreateBufferSource()
+{
+  nsRefPtr<AudioBufferSourceNode> bufferNode =
+    new AudioBufferSourceNode(this);
+  return bufferNode.forget();
+}
+
 }
 }
 
--- a/content/media/webaudio/AudioContext.h
+++ b/content/media/webaudio/AudioContext.h
@@ -1,29 +1,35 @@
 /* -*- 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/. */
 
+#pragma once
+
 #include "nsWrapperCache.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/Attributes.h"
 #include "nsCOMPtr.h"
 #include "EnableWebAudioCheck.h"
+#include "nsAutoPtr.h"
 
 class JSContext;
 class nsIDOMWindow;
 
 namespace mozilla {
 
 class ErrorResult;
 
 namespace dom {
 
+class AudioDestinationNode;
+class AudioBufferSourceNode;
+
 class AudioContext MOZ_FINAL : public nsISupports,
                                public nsWrapperCache,
                                public EnableWebAudioCheck
 {
   explicit AudioContext(nsIDOMWindow* aParentWindow);
 
 public:
   virtual ~AudioContext();
@@ -37,15 +43,23 @@ public:
   }
 
   virtual JSObject* WrapObject(JSContext* aCx, JSObject* aScope,
                                bool* aTriedToWrap);
 
   static already_AddRefed<AudioContext>
   Constructor(nsISupports* aGlobal, ErrorResult& aRv);
 
+  AudioDestinationNode* Destination() const
+  {
+    return mDestination;
+  }
+
+  already_AddRefed<AudioBufferSourceNode> CreateBufferSource();
+
 private:
   nsCOMPtr<nsIDOMWindow> mWindow;
+  nsRefPtr<AudioDestinationNode> mDestination;
 };
 
 }
 }
 
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/AudioDestinationNode.cpp
@@ -0,0 +1,29 @@
+/* -*- 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 "AudioDestinationNode.h"
+#include "mozilla/dom/AudioDestinationNodeBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_ISUPPORTS_INHERITED0(AudioDestinationNode, AudioNode)
+
+AudioDestinationNode::AudioDestinationNode(AudioContext* aContext)
+  : AudioNode(aContext)
+{
+}
+
+JSObject*
+AudioDestinationNode::WrapObject(JSContext* aCx, JSObject* aScope,
+                                 bool* aTriedToWrap)
+{
+  return AudioDestinationNodeBinding::Wrap(aCx, aScope, this, aTriedToWrap);
+}
+
+}
+}
+
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/AudioDestinationNode.h
@@ -0,0 +1,30 @@
+/* -*- 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/. */
+
+#pragma once
+
+#include "AudioNode.h"
+
+namespace mozilla {
+namespace dom {
+
+class AudioContext;
+
+class AudioDestinationNode : public AudioNode
+{
+public:
+  explicit AudioDestinationNode(AudioContext* aContext);
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+  virtual JSObject* WrapObject(JSContext* aCx, JSObject* aScope,
+                               bool* aTriedToWrap);
+
+};
+
+}
+}
+
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/AudioNode.cpp
@@ -0,0 +1,31 @@
+/* -*- 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 "AudioNode.h"
+#include "AudioContext.h"
+#include "nsContentUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(AudioNode, mContext)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(AudioNode)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(AudioNode)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AudioNode)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+AudioNode::AudioNode(AudioContext* aContext)
+  : mContext(aContext)
+{
+  MOZ_ASSERT(aContext);
+  SetIsDOMBinding();
+}
+
+}
+}
+
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/AudioNode.h
@@ -0,0 +1,55 @@
+/* -*- 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/. */
+
+#pragma once
+
+#include "nsWrapperCache.h"
+#include "nsCycleCollectionParticipant.h"
+#include "mozilla/Attributes.h"
+#include "EnableWebAudioCheck.h"
+#include "nsAutoPtr.h"
+#include "AudioContext.h"
+
+struct JSContext;
+
+namespace mozilla {
+namespace dom {
+
+class AudioNode : public nsISupports,
+                  public nsWrapperCache,
+                  public EnableWebAudioCheck
+{
+public:
+  explicit AudioNode(AudioContext* aContext);
+  virtual ~AudioNode() {}
+
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AudioNode)
+
+  AudioContext* GetParentObject() const
+  {
+    return mContext;
+  }
+
+  AudioContext* Context() const
+  {
+    return mContext;
+  }
+
+  void Connect(AudioNode& aDestination, uint32_t aOutput,
+               uint32_t aInput)
+  { /* no-op for now */ }
+
+  void Disconnect(uint32_t aOutput)
+  { /* no-op for now */ }
+
+private:
+  nsRefPtr<AudioContext> mContext;
+};
+
+}
+}
+
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/AudioSourceNode.cpp
@@ -0,0 +1,21 @@
+/* -*- 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 "AudioSourceNode.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_ISUPPORTS_INHERITED0(AudioSourceNode, AudioNode)
+
+AudioSourceNode::AudioSourceNode(AudioContext* aContext)
+  : AudioNode(aContext)
+{
+}
+
+}
+}
+
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/AudioSourceNode.h
@@ -0,0 +1,25 @@
+/* -*- 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/. */
+
+#pragma once
+
+#include "AudioNode.h"
+
+namespace mozilla {
+namespace dom {
+
+class AudioSourceNode : public AudioNode
+{
+public:
+  explicit AudioSourceNode(AudioContext* aContext);
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+};
+
+}
+}
+
--- a/content/media/webaudio/EnableWebAudioCheck.h
+++ b/content/media/webaudio/EnableWebAudioCheck.h
@@ -1,14 +1,16 @@
 /* -*- 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/. */
 
+#pragma once
+
 namespace mozilla {
 namespace dom {
 
 // This is a helper class which enables Web Audio to be enabled or disabled
 // as whole.  Individual Web Audio object classes should inherit from this.
 class EnableWebAudioCheck {
 public:
   static bool PrefEnabled();
--- a/content/media/webaudio/Makefile.in
+++ b/content/media/webaudio/Makefile.in
@@ -10,17 +10,29 @@ FAIL_ON_WARNINGS := 1
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE         := content
 LIBRARY_NAME   := gkconwebaudio_s
 LIBXUL_LIBRARY := 1
 
 CPPSRCS := \
+  AudioBufferSourceNode.cpp \
   AudioContext.cpp \
+  AudioDestinationNode.cpp \
+  AudioNode.cpp \
+  AudioSourceNode.cpp \
   EnableWebAudioCheck.cpp \
   $(NULL)
 
+EXPORTS_NAMESPACES := mozilla/dom
+EXPORTS_mozilla/dom := \
+  AudioBufferSourceNode.h \
+  AudioDestinationNode.h \
+  AudioNode.h \
+  AudioSourceNode.h \
+  $(NULL)
+
 PARALLEL_DIRS := test
 
 FORCE_STATIC_LIB := 1
 
 include $(topsrcdir)/config/rules.mk
--- a/docshell/base/nsDSURIContentListener.cpp
+++ b/docshell/base/nsDSURIContentListener.cpp
@@ -13,16 +13,18 @@
 #include "nsIDOMWindow.h"
 #include "nsNetUtil.h"
 #include "nsAutoPtr.h"
 #include "nsIHttpChannel.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsError.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "mozilla/Preferences.h"
+#include "nsIConsoleService.h"
+#include "nsIScriptError.h"
 
 using namespace mozilla;
 
 //*****************************************************************************
 //***    nsDSURIContentListener: Object Management
 //*****************************************************************************
 
 nsDSURIContentListener::nsDSURIContentListener(nsDocShell* aDocShell)
@@ -270,115 +272,123 @@ bool nsDSURIContentListener::CheckOneFra
         !isAllowFrom)
         return true;
 
     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
     if (!httpChannel) {
         return true;
     }
 
-    if (mDocShell) {
-        // We need to check the location of this window and the location of the top
-        // window, if we're not the top.  X-F-O: SAMEORIGIN requires that the
-        // document must be same-origin with top window.  X-F-O: DENY requires that
-        // the document must never be framed.
-        nsCOMPtr<nsIDOMWindow> thisWindow = do_GetInterface(static_cast<nsIDocShell*>(mDocShell));
-        // If we don't have DOMWindow there is no risk of clickjacking
-        if (!thisWindow)
-            return true;
+    nsCOMPtr<nsIURI> uri;
+    httpChannel->GetURI(getter_AddRefs(uri));
+
+    // XXXkhuey when does this happen?  Is returning true safe here?
+    if (!mDocShell) {
+        return true;
+    }
 
-        // GetScriptableTop, not GetTop, because we want this to respect
-        // <iframe mozbrowser> boundaries.
-        nsCOMPtr<nsIDOMWindow> topWindow;
-        thisWindow->GetScriptableTop(getter_AddRefs(topWindow));
+    // We need to check the location of this window and the location of the top
+    // window, if we're not the top.  X-F-O: SAMEORIGIN requires that the
+    // document must be same-origin with top window.  X-F-O: DENY requires that
+    // the document must never be framed.
+    nsCOMPtr<nsIDOMWindow> thisWindow = do_GetInterface(static_cast<nsIDocShell*>(mDocShell));
+    // If we don't have DOMWindow there is no risk of clickjacking
+    if (!thisWindow)
+        return true;
+
+    // GetScriptableTop, not GetTop, because we want this to respect
+    // <iframe mozbrowser> boundaries.
+    nsCOMPtr<nsIDOMWindow> topWindow;
+    thisWindow->GetScriptableTop(getter_AddRefs(topWindow));
+
+    // if the document is in the top window, it's not in a frame.
+    if (thisWindow == topWindow)
+        return true;
 
-        // if the document is in the top window, it's not in a frame.
-        if (thisWindow == topWindow)
-            return true;
+    // Find the top docshell in our parent chain that doesn't have the system
+    // principal and use it for the principal comparison.  Finding the top
+    // content-type docshell doesn't work because some chrome documents are
+    // loaded in content docshells (see bug 593387).
+    nsCOMPtr<nsIDocShellTreeItem> thisDocShellItem(do_QueryInterface(
+                                                   static_cast<nsIDocShell*> (mDocShell)));
+    nsCOMPtr<nsIDocShellTreeItem> parentDocShellItem,
+                                  curDocShellItem = thisDocShellItem;
+    nsCOMPtr<nsIDocument> topDoc;
+    nsresult rv;
+    nsCOMPtr<nsIScriptSecurityManager> ssm =
+        do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
+    if (!ssm) {
+        MOZ_CRASH();
+    }
 
-        // Find the top docshell in our parent chain that doesn't have the system
-        // principal and use it for the principal comparison.  Finding the top
-        // content-type docshell doesn't work because some chrome documents are
-        // loaded in content docshells (see bug 593387).
-        nsCOMPtr<nsIDocShellTreeItem> thisDocShellItem(do_QueryInterface(
-                                                       static_cast<nsIDocShell*> (mDocShell)));
-        nsCOMPtr<nsIDocShellTreeItem> parentDocShellItem,
-                                      curDocShellItem = thisDocShellItem;
-        nsCOMPtr<nsIDocument> topDoc;
-        nsresult rv;
-        nsCOMPtr<nsIScriptSecurityManager> ssm =
-            do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
-        if (!ssm) {
-            NS_ASSERTION(ssm, "Failed to get the ScriptSecurityManager.");
-            return false;
+    // Traverse up the parent chain and stop when we see a docshell whose
+    // parent has a system principal, or a docshell corresponding to
+    // <iframe mozbrowser>.
+    while (NS_SUCCEEDED(curDocShellItem->GetParent(getter_AddRefs(parentDocShellItem))) &&
+           parentDocShellItem) {
+
+        nsCOMPtr<nsIDocShell> curDocShell = do_QueryInterface(curDocShellItem);
+        if (curDocShell && curDocShell->GetIsContentBoundary()) {
+          break;
         }
 
-        // Traverse up the parent chain and stop when we see a docshell whose
-        // parent has a system principal, or a docshell corresponding to
-        // <iframe mozbrowser>.
-        while (NS_SUCCEEDED(curDocShellItem->GetParent(getter_AddRefs(parentDocShellItem))) &&
-               parentDocShellItem) {
-
-            nsCOMPtr<nsIDocShell> curDocShell = do_QueryInterface(curDocShellItem);
-            if (curDocShell && curDocShell->GetIsContentBoundary()) {
-              break;
+        bool system = false;
+        topDoc = do_GetInterface(parentDocShellItem);
+        if (topDoc) {
+            if (NS_SUCCEEDED(ssm->IsSystemPrincipal(topDoc->NodePrincipal(),
+                                                    &system)) && system) {
+                // Found a system-principled doc: last docshell was top.
+                break;
             }
-
-            bool system = false;
-            topDoc = do_GetInterface(parentDocShellItem);
-            if (topDoc) {
-                if (NS_SUCCEEDED(ssm->IsSystemPrincipal(topDoc->NodePrincipal(),
-                                                        &system)) && system) {
-                    // Found a system-principled doc: last docshell was top.
-                    break;
-                }
-            }
-            else {
-                return false;
-            }
-            curDocShellItem = parentDocShellItem;
         }
-
-        // If this document has the top non-SystemPrincipal docshell it is not being
-        // framed or it is being framed by a chrome document, which we allow.
-        if (curDocShellItem == thisDocShellItem)
-            return true;
-
-        // If the value of the header is DENY, and the previous condition is
-        // not met (current docshell is not the top docshell), prohibit the
-        // load.
-        if (policy.LowerCaseEqualsLiteral("deny")) {
+        else {
             return false;
         }
+        curDocShellItem = parentDocShellItem;
+    }
 
-        topDoc = do_GetInterface(curDocShellItem);
-        nsCOMPtr<nsIURI> topUri;
-        topDoc->NodePrincipal()->GetURI(getter_AddRefs(topUri));
-        nsCOMPtr<nsIURI> uri;
+    // If this document has the top non-SystemPrincipal docshell it is not being
+    // framed or it is being framed by a chrome document, which we allow.
+    if (curDocShellItem == thisDocShellItem)
+        return true;
+
+    // If the value of the header is DENY, and the previous condition is
+    // not met (current docshell is not the top docshell), prohibit the
+    // load.
+    if (policy.LowerCaseEqualsLiteral("deny")) {
+        ReportXFOViolation(curDocShellItem, uri, eDENY);
+        return false;
+    }
+
+    topDoc = do_GetInterface(curDocShellItem);
+    nsCOMPtr<nsIURI> topUri;
+    topDoc->NodePrincipal()->GetURI(getter_AddRefs(topUri));
 
-        // If the X-Frame-Options value is SAMEORIGIN, then the top frame in the
-        // parent chain must be from the same origin as this document.
-        if (policy.LowerCaseEqualsLiteral("sameorigin")) {
-            httpChannel->GetURI(getter_AddRefs(uri));
-            rv = ssm->CheckSameOriginURI(uri, topUri, true);
-            if (NS_FAILED(rv))
-                return false; /* wasn't same-origin */
+    // If the X-Frame-Options value is SAMEORIGIN, then the top frame in the
+    // parent chain must be from the same origin as this document.
+    if (policy.LowerCaseEqualsLiteral("sameorigin")) {
+        rv = ssm->CheckSameOriginURI(uri, topUri, true);
+        if (NS_FAILED(rv)) {
+            ReportXFOViolation(curDocShellItem, uri, eSAMEORIGIN);
+            return false; /* wasn't same-origin */
         }
+    }
 
-        // If the X-Frame-Options value is "allow-from [uri]", then the top
-        // frame in the parent chain must be from that origin
-        if (isAllowFrom) {
-            rv = NS_NewURI(getter_AddRefs(uri),
-                           Substring(policy, allowFromLen));
-            if (NS_FAILED(rv))
-              return false;
+    // If the X-Frame-Options value is "allow-from [uri]", then the top
+    // frame in the parent chain must be from that origin
+    if (isAllowFrom) {
+        rv = NS_NewURI(getter_AddRefs(uri),
+                       Substring(policy, allowFromLen));
+        if (NS_FAILED(rv))
+          return false;
 
-            rv = ssm->CheckSameOriginURI(uri, topUri, true);
-            if (NS_FAILED(rv))
-                return false;
+        rv = ssm->CheckSameOriginURI(uri, topUri, true);
+        if (NS_FAILED(rv)) {
+            ReportXFOViolation(curDocShellItem, uri, eALLOWFROM);
+            return false;
         }
     }
 
     return true;
 }
 
 // Check if X-Frame-Options permits this document to be loaded as a subdocument.
 // This will iterate through and check any number of X-Frame-Options policies
@@ -415,8 +425,82 @@ bool nsDSURIContentListener::CheckFrameO
                 }
             }
             return false;
         }
     }
 
     return true;
 }
+
+void
+nsDSURIContentListener::ReportXFOViolation(nsIDocShellTreeItem* aTopDocShellItem,
+                                           nsIURI* aThisURI,
+                                           XFOHeader aHeader)
+{
+    nsresult rv = NS_OK;
+
+    nsCOMPtr<nsPIDOMWindow> topOuterWindow = do_GetInterface(aTopDocShellItem);
+    if (!topOuterWindow)
+        return;
+
+    NS_ASSERTION(topOuterWindow->IsOuterWindow(), "Huh?");
+    nsPIDOMWindow* topInnerWindow = topOuterWindow->GetCurrentInnerWindow();
+    if (!topInnerWindow)
+        return;
+
+    nsCOMPtr<nsIURI> topURI;
+
+    nsCOMPtr<nsIDocument> document;
+
+    document = do_GetInterface(aTopDocShellItem);
+    rv = document->NodePrincipal()->GetURI(getter_AddRefs(topURI));
+    if (NS_FAILED(rv))
+        return;
+
+    if (!topURI)
+        return;
+
+    nsCString topURIString;
+    nsCString thisURIString;
+
+    rv = topURI->GetSpec(topURIString);
+    if (NS_FAILED(rv))
+        return;
+
+    rv = aThisURI->GetSpec(thisURIString);
+    if (NS_FAILED(rv))
+        return;
+
+    nsCOMPtr<nsIConsoleService> consoleService =
+      do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+    nsCOMPtr<nsIScriptError> errorObject =
+      do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
+
+    if (!consoleService || !errorObject)
+        return;
+
+    nsString msg = NS_LITERAL_STRING("Load denied by X-Frame-Options: ");
+    msg.Append(NS_ConvertUTF8toUTF16(thisURIString));
+
+    switch (aHeader) {
+        case eDENY:
+            msg.AppendLiteral(" does not permit framing.");
+            break;
+        case eSAMEORIGIN:
+            msg.AppendLiteral(" does not permit cross-origin framing.");
+            break;
+        case eALLOWFROM:
+            msg.AppendLiteral(" does not permit framing by ");
+            msg.Append(NS_ConvertUTF8toUTF16(topURIString));
+            msg.AppendLiteral(".");
+            break;
+    }
+
+    rv = errorObject->InitWithWindowID(msg, EmptyString(), EmptyString(), 0, 0,
+                                       nsIScriptError::errorFlag,
+                                       "X-Frame-Options",
+                                       topInnerWindow->WindowID());
+    if (NS_FAILED(rv))
+        return;
+
+    consoleService->LogMessage(errorObject);
+}
--- a/docshell/base/nsDSURIContentListener.h
+++ b/docshell/base/nsDSURIContentListener.h
@@ -36,16 +36,25 @@ protected:
     }
 
     // Determine if X-Frame-Options allows content to be framed
     // as a subdocument
     bool CheckFrameOptions(nsIRequest* request);
     bool CheckOneFrameOptionsPolicy(nsIRequest* request,
                                     const nsAString& policy);
 
+    enum XFOHeader {
+      eDENY,
+      eSAMEORIGIN,
+      eALLOWFROM
+    };
+
+    void ReportXFOViolation(nsIDocShellTreeItem* aTopDocShellItem,
+                            nsIURI* aThisURI,
+                            XFOHeader aHeader);
 protected:
     nsDocShell*                      mDocShell;
 
     // Store the parent listener in either of these depending on
     // if supports weak references or not. Proper weak refs are
     // preferred and encouraged!
     nsWeakPtr                        mWeakParentContentListener;
     nsIURIContentListener*           mParentContentListener;
--- a/dom/Makefile.in
+++ b/dom/Makefile.in
@@ -77,16 +77,20 @@ PARALLEL_DIRS += \
 ifdef MOZ_B2G_RIL
 PARALLEL_DIRS += \
   telephony \
   wifi \
   icc \
   $(NULL)
 endif
 
+ifdef MOZ_B2G_FM
+PARALLEL_DIRS += fm
+endif
+
 ifdef MOZ_PAY
 PARALLEL_DIRS += \
   payment \
   $(NULL)
 endif
 
 # bindings/test is here, because it needs to build after bindings/, and
 # we build subdirectories before ourselves.
--- a/dom/base/Makefile.in
+++ b/dom/base/Makefile.in
@@ -114,16 +114,20 @@ include $(topsrcdir)/dom/dom-config.mk
 ifdef MOZ_JSDEBUGGER
 DEFINES += -DMOZ_JSDEBUGGER
 endif
 
 ifdef MOZ_B2G_RIL
 DEFINES += -DMOZ_B2G_RIL
 endif
 
+ifdef MOZ_B2G_FM
+DEFINES += -DMOZ_B2G_FM
+endif
+
 include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 
 include $(topsrcdir)/config/rules.mk
 
 LOCAL_INCLUDES += \
 		-I$(topsrcdir)/js/xpconnect/src \
 		-I$(topsrcdir)/js/xpconnect/wrappers \
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -108,16 +108,17 @@ NS_INTERFACE_MAP_BEGIN(Navigator)
   NS_INTERFACE_MAP_ENTRY(nsIDOMNavigator)
   NS_INTERFACE_MAP_ENTRY(nsIDOMClientInformation)
   NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorDeviceStorage)
   NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorGeolocation)
   NS_INTERFACE_MAP_ENTRY(nsINavigatorBattery)
   NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorDesktopNotification)
   NS_INTERFACE_MAP_ENTRY(nsIDOMMozNavigatorSms)
 #ifdef MOZ_MEDIA_NAVIGATOR
+  NS_INTERFACE_MAP_ENTRY(nsINavigatorUserMedia)
   NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorUserMedia)
 #endif
 #ifdef MOZ_B2G_RIL
   NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorTelephony)
 #endif
   NS_INTERFACE_MAP_ENTRY(nsIDOMMozNavigatorNetwork)
 #ifdef MOZ_B2G_RIL
   NS_INTERFACE_MAP_ENTRY(nsIMozNavigatorMobileConnection)
@@ -934,17 +935,17 @@ NS_IMETHODIMP Navigator::GetDeviceStorag
   nsRefPtr<nsDOMDeviceStorage> storage;
   nsDOMDeviceStorage::CreateDeviceStoragesFor(win, aType, getter_AddRefs(storage));
 
   if (!storage) {
     return NS_OK;
   }
 
   NS_ADDREF(*_retval = storage.get());
-  mDeviceStorageStores.AppendElement(storage);                                                                                                                                                                                              
+  mDeviceStorageStores.AppendElement(storage);
   return NS_OK;
 }
 
 //*****************************************************************************
 //    Navigator::nsIDOMNavigatorGeolocation
 //*****************************************************************************
 
 NS_IMETHODIMP Navigator::GetGeolocation(nsIDOMGeoGeolocation** _retval)
@@ -982,32 +983,55 @@ NS_IMETHODIMP Navigator::GetGeolocation(
 }
 
 //*****************************************************************************
 //    Navigator::nsIDOMNavigatorUserMedia (mozGetUserMedia)
 //*****************************************************************************
 #ifdef MOZ_MEDIA_NAVIGATOR
 NS_IMETHODIMP
 Navigator::MozGetUserMedia(nsIMediaStreamOptions* aParams,
-                           nsIDOMGetUserMediaSuccessCallback* onSuccess,
-                           nsIDOMGetUserMediaErrorCallback* onError)
+                           nsIDOMGetUserMediaSuccessCallback* aOnSuccess,
+                           nsIDOMGetUserMediaErrorCallback* aOnError)
 {
   if (!Preferences::GetBool("media.navigator.enabled", false)) {
     return NS_OK;
   }
 
-  MediaManager *manager = MediaManager::Get();
   nsCOMPtr<nsPIDOMWindow> win = do_QueryReferent(mWindow);
-
   if (!win || !win->GetOuterWindow() ||
       win->GetOuterWindow()->GetCurrentInnerWindow() != win) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  return manager->GetUserMedia(win, aParams, onSuccess, onError);
+  bool privileged = nsContentUtils::IsChromeDoc(win->GetExtantDoc());
+
+  MediaManager* manager = MediaManager::Get();
+  return manager->GetUserMedia(privileged, win, aParams, aOnSuccess, aOnError);
+}
+
+//*****************************************************************************
+//    Navigator::nsINavigatorUserMedia (mozGetUserMediaDevices)
+//*****************************************************************************
+NS_IMETHODIMP
+Navigator::MozGetUserMediaDevices(nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
+                                  nsIDOMGetUserMediaErrorCallback* aOnError)
+{
+  nsCOMPtr<nsPIDOMWindow> win = do_QueryReferent(mWindow);
+  if (!win || !win->GetOuterWindow() ||
+      win->GetOuterWindow()->GetCurrentInnerWindow() != win) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  // Check if the caller is chrome privileged, bail if not
+  if (!nsContentUtils::IsChromeDoc(win->GetExtantDoc())) {
+    return NS_ERROR_FAILURE;
+  }
+
+  MediaManager* manager = MediaManager::Get();
+  return manager->GetUserMediaDevices(win, aOnSuccess, aOnError);
 }
 #endif
 
 //*****************************************************************************
 //    Navigator::nsIDOMNavigatorDesktopNotification
 //*****************************************************************************
 
 NS_IMETHODIMP Navigator::GetMozNotification(nsIDOMDesktopNotificationCenter** aRetVal)
@@ -1157,25 +1181,22 @@ NS_IMETHODIMP
 Navigator::GetMozVoicemail(nsIDOMMozVoicemail** aVoicemail)
 {
   *aVoicemail = nullptr;
 
   if (!mVoicemail) {
     nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
     NS_ENSURE_TRUE(window, NS_OK);
 
-    nsCOMPtr<nsIDocument> document = do_QueryInterface(window->GetExtantDocument());
-    NS_ENSURE_TRUE(document, NS_OK);
-    nsCOMPtr<nsIPrincipal> principal = document->NodePrincipal();
     nsCOMPtr<nsIPermissionManager> permMgr =
       do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
     NS_ENSURE_TRUE(permMgr, NS_OK);
 
     uint32_t permission = nsIPermissionManager::DENY_ACTION;
-    permMgr->TestPermissionFromPrincipal(principal, "voicemail", &permission);
+    permMgr->TestPermissionFromWindow(window, "voicemail", &permission);
 
     if (permission != nsIPermissionManager::ALLOW_ACTION) {
       return NS_OK;
     }
 
     nsresult rv = NS_NewVoicemail(window, getter_AddRefs(mVoicemail));
     NS_ENSURE_SUCCESS(rv, rv);
   }
@@ -1215,25 +1236,22 @@ NS_IMETHODIMP
 Navigator::GetMozMobileConnection(nsIDOMMozMobileConnection** aMobileConnection)
 {
   *aMobileConnection = nullptr;
 
   if (!mMobileConnection) {
     nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
     NS_ENSURE_TRUE(window, NS_OK);
 
-    nsCOMPtr<nsIDocument> document = do_QueryInterface(window->GetExtantDocument());
-    NS_ENSURE_TRUE(document, NS_OK);
-    nsCOMPtr<nsIPrincipal> principal = document->NodePrincipal();
     nsCOMPtr<nsIPermissionManager> permMgr =
       do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
     NS_ENSURE_TRUE(permMgr, NS_OK);
 
     uint32_t permission = nsIPermissionManager::DENY_ACTION;
-    permMgr->TestPermissionFromPrincipal(principal, "mobileconnection", &permission);
+    permMgr->TestPermissionFromWindow(window, "mobileconnection", &permission);
 
     if (permission != nsIPermissionManager::ALLOW_ACTION) {
       return NS_OK;
     }
 
     mMobileConnection = new network::MobileConnection();
     mMobileConnection->Init(window);
   }
@@ -1371,17 +1389,18 @@ Navigator::GetMozCameras(nsIDOMCameraMan
   if (!mCameraManager) {
     nsCOMPtr<nsPIDOMWindow> win = do_QueryReferent(mWindow);
     NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
 
     if (!win->GetOuterWindow() || win->GetOuterWindow()->GetCurrentInnerWindow() != win) {
       return NS_ERROR_NOT_AVAILABLE;
     }
 
-    mCameraManager = nsDOMCameraManager::Create(win->WindowID());
+    mCameraManager = nsDOMCameraManager::CheckPermissionAndCreateInstance(win);
+    NS_ENSURE_TRUE(mCameraManager, NS_OK);
   }
 
   nsRefPtr<nsDOMCameraManager> cameraManager = mCameraManager;
   cameraManager.forget(aCameraManager);
 
   return NS_OK;
 }
 
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -82,16 +82,17 @@ class TimeManager;
 class Navigator : public nsIDOMNavigator
                 , public nsIDOMClientInformation
                 , public nsIDOMNavigatorDeviceStorage
                 , public nsIDOMNavigatorGeolocation
                 , public nsIDOMNavigatorDesktopNotification
                 , public nsINavigatorBattery
                 , public nsIDOMMozNavigatorSms
 #ifdef MOZ_MEDIA_NAVIGATOR
+                , public nsINavigatorUserMedia
                 , public nsIDOMNavigatorUserMedia
 #endif
 #ifdef MOZ_B2G_RIL
                 , public nsIDOMNavigatorTelephony
 #endif
                 , public nsIDOMMozNavigatorNetwork
 #ifdef MOZ_B2G_RIL
                 , public nsIMozNavigatorMobileConnection
@@ -111,16 +112,17 @@ public:
   NS_DECL_NSIDOMNAVIGATOR
   NS_DECL_NSIDOMCLIENTINFORMATION
   NS_DECL_NSIDOMNAVIGATORDEVICESTORAGE
   NS_DECL_NSIDOMNAVIGATORGEOLOCATION
   NS_DECL_NSIDOMNAVIGATORDESKTOPNOTIFICATION
   NS_DECL_NSINAVIGATORBATTERY
   NS_DECL_NSIDOMMOZNAVIGATORSMS
 #ifdef MOZ_MEDIA_NAVIGATOR
+  NS_DECL_NSINAVIGATORUSERMEDIA
   NS_DECL_NSIDOMNAVIGATORUSERMEDIA
 #endif
 #ifdef MOZ_B2G_RIL
   NS_DECL_NSIDOMNAVIGATORTELEPHONY
 #endif
   NS_DECL_NSIDOMMOZNAVIGATORNETWORK
 #ifdef MOZ_B2G_RIL
   NS_DECL_NSIMOZNAVIGATORMOBILECONNECTION
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -513,16 +513,20 @@ using mozilla::dom::indexedDB::IDBWrappe
 #include "TelephonyCall.h"
 #include "CallEvent.h"
 #include "nsIDOMVoicemail.h"
 #include "nsIDOMVoicemailEvent.h"
 #include "nsIDOMIccManager.h"
 #include "StkCommandEvent.h"
 #endif
 
+#ifdef MOZ_B2G_FM
+#include "FMRadio.h"
+#endif
+
 #ifdef MOZ_B2G_BT
 #include "BluetoothManager.h"
 #include "BluetoothAdapter.h"
 #include "BluetoothDevice.h"
 #include "BluetoothPropertyEvent.h"
 #endif
 
 #include "nsIDOMNavigatorSystemMessages.h"
@@ -1677,16 +1681,21 @@ static nsDOMClassInfoData sClassInfoData
   NS_DEFINE_CLASSINFO_DATA(MozVoicemailEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(MozIccManager, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(MozStkCommandEvent, 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(BluetoothManager, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(BluetoothAdapter, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)  
   NS_DEFINE_CLASSINFO_DATA(BluetoothDevice, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(BluetoothPropertyEvent, nsDOMGenericSH,
@@ -2475,16 +2484,17 @@ nsDOMClassInfo::Init()
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorGeolocation)
     DOM_CLASSINFO_MAP_CONDITIONAL_ENTRY(nsIDOMNavigatorDesktopNotification,
                                         Navigator::HasDesktopNotificationSupport())
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMClientInformation)
     DOM_CLASSINFO_MAP_CONDITIONAL_ENTRY(nsINavigatorBattery,
                                         battery::BatteryManager::HasSupport())
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozNavigatorSms)
 #ifdef MOZ_MEDIA_NAVIGATOR
+    DOM_CLASSINFO_MAP_ENTRY(nsINavigatorUserMedia)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorUserMedia)
 #endif
 #ifdef MOZ_B2G_RIL
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorTelephony)
 #endif
     DOM_CLASSINFO_MAP_CONDITIONAL_ENTRY(nsIDOMMozNavigatorNetwork,
                                         network::IsAPIEnabled())
 #ifdef MOZ_B2G_RIL
@@ -4478,16 +4488,23 @@ nsDOMClassInfo::Init()
 
   DOM_CLASSINFO_MAP_BEGIN(MozStkCommandEvent, nsIDOMMozStkCommandEvent)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozStkCommandEvent)
     DOM_CLASSINFO_EVENT_MAP_ENTRIES
   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(BluetoothManager, nsIDOMBluetoothManager)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMBluetoothManager)
   DOM_CLASSINFO_MAP_END  
 
   DOM_CLASSINFO_MAP_BEGIN(BluetoothAdapter, nsIDOMBluetoothAdapter)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMBluetoothAdapter)
   DOM_CLASSINFO_MAP_END
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -514,16 +514,20 @@ DOMCI_CLASS(Telephony)
 DOMCI_CLASS(TelephonyCall)
 DOMCI_CLASS(CallEvent)
 DOMCI_CLASS(MozVoicemail)
 DOMCI_CLASS(MozVoicemailEvent)
 DOMCI_CLASS(MozIccManager)
 DOMCI_CLASS(MozStkCommandEvent)
 #endif
 
+#ifdef MOZ_B2G_FM
+DOMCI_CLASS(FMRadio)
+#endif
+
 #ifdef MOZ_B2G_BT
 DOMCI_CLASS(BluetoothManager)
 DOMCI_CLASS(BluetoothAdapter)
 DOMCI_CLASS(BluetoothDevice)
 DOMCI_CLASS(BluetoothPropertyEvent)
 #endif
 
 DOMCI_CLASS(CameraManager)
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -22,18 +22,23 @@
 #                  to an educated guess).
 #   * castable - Indicates whether the value in the wrapper can be cast to
 #                nativeType, or whether it needs to be QI-ed (defaults to True
 #                for everything but callback interfaces and external interfaces,
 #                for which it defaults to false and is not allowed to be set
 #                at all).
 #   * concrete - Indicates whether there exist objects with this interface as
 #                their primary interface (defaults to True).
-#   * prefable - Indicates whether this binding is subject to the about:config
-#                pref, or whether it's always enabled (defaults to False).
+#   * prefable - Indicates whether this bindings should be disabled if the
+#                global pref for Web IDL bindings is set to false.  This is a
+#                risk mitigation strategy and it will cause all of the Web IDL
+#                bindings marked as prefable to fall back to the xpconnect
+#                bindings in case something goes wrong.  This defaults to False.
+#                Setting this on objects which only have Web IDL bindings does
+#                not make any sense.
 #                Cannot be set on external interfaces.
 #   * workers - Indicates whether the descriptor is intended to be used for
 #               worker threads (defaults to false).
 #   * customTrace - The native class will use a custom trace hook (defaults to
 #                   true for workers, false otherwise).
 #   * customFinalize - The native class will use a custom finalize hook
 #                      (defaults to true for workers, false otherwise).
 #   * notflattened - The native type does not have nsIClassInfo, so when
@@ -58,17 +63,30 @@
 #                         that require a JSContext as the first argument
 #   * resultNotAddRefed - attributes and methods specified in the .webidl file
 #                         that do not AddRef the return value
 
 DOMInterfaces = {
 
 'mozAudioContext': {
     'nativeType': 'AudioContext',
-    'prefable': True,
+},
+
+'AudioNode' : {
+    'concrete': False,
+},
+
+'AudioSourceNode': {
+    'concrete': False,
+},
+
+'AudioBufferSourceNode': {
+},
+
+'AudioDestinationNode': {
 },
 
 'Blob': [
 {
     'headerFile': 'nsIDOMFile.h',
 },
 {
     'workers': True,
@@ -165,16 +183,22 @@ DOMInterfaces = {
 'FileList': [
 {
     'nativeType': 'nsDOMFileList',
     'headerFile': 'nsDOMFile.h',
     'prefable': True,
     'resultNotAddRefed': [ 'item' ]
 }],
 
+'FileReaderSync': [
+{
+    'workers': True,
+    'headerFile': 'mozilla/dom/workers/bindings/FileReaderSync.h'
+}],
+
 'FormData': [
 {
 },
 {
     'workers': True,
 }],
 
 'HTMLCollection': [
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -2844,23 +2844,27 @@ def infallibleForMember(member, type, de
 
     CURRENT ASSUMPTIONS:
         We assume that successCode for wrapping up return values cannot contain
         failure conditions.
     """
     return getWrapTemplateForType(type, descriptorProvider, 'result', None,\
                                   memberIsCreator(member))[1]
 
-def typeNeedsCx(type):
+def typeNeedsCx(type, retVal=False):
     if type is None:
         return False
+    if type.nullable():
+        type = type.inner
     if type.isSequence() or type.isArray():
         type = type.inner
     if type.isUnion():
         return any(typeNeedsCx(t) for t in type.unroll().flatMemberTypes)
+    if retVal and type.isSpiderMonkeyInterface():
+        return True
     return type.isCallback() or type.isAny() or type.isObject()
 
 # Returns a tuple consisting of a CGThing containing the type of the return
 # value, or None if there is no need for a return value, and a boolean signaling
 # whether the return value is passed in an out parameter.
 def getRetvalDeclarationForType(returnType, descriptorProvider,
                                 resultAlreadyAddRefed):
     if returnType is None or returnType.isVoid():
@@ -2944,17 +2948,17 @@ class CGCallGenerator(CGThing):
             args.append(CGGeneric(name))
 
         # Return values that go in outparams go here
         if resultOutParam:
             args.append(CGGeneric("result"))
         if isFallible:
             args.append(CGGeneric("rv"))
 
-        needsCx = (typeNeedsCx(returnType) or
+        needsCx = (typeNeedsCx(returnType, True) or
                    any(typeNeedsCx(a.type) for (a, _) in arguments) or
                    'implicitJSContext' in extendedAttributes)
 
         if not "cx" in argsPre and needsCx:
             args.prepend(CGGeneric("cx"))
 
         # Build up our actual call
         self.cgRoot = CGList([], "\n")
@@ -3611,17 +3615,17 @@ class CGMemberJITInfo(CGThing):
     def __init__(self, descriptor, member):
         self.member = member
         self.descriptor = descriptor
 
     def declare(self):
         return ""
 
     def defineJitInfo(self, infoName, opName, infallible):
-        protoID = "prototypes::id::%s" % self.descriptor.interface.identifier.name
+        protoID = "prototypes::id::%s" % self.descriptor.name
         depth = "PrototypeTraits<%s>::Depth" % protoID
         failstr = "true" if infallible else "false"
         return ("\n"
                 "const JSJitInfo %s = {\n"
                 "  %s,\n"
                 "  %s,\n"
                 "  %s,\n"
                 "  %s,  /* isInfallible. False in setters. */\n"
--- a/dom/bindings/stubgenerator/Skeleton.h
+++ b/dom/bindings/stubgenerator/Skeleton.h
@@ -1,14 +1,16 @@
 /* -*- 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/. */
 
+#pragma once
+
 #include "nsWrapperCache.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/Attributes.h"
 
 struct JSContext;
 
 namespace mozilla {
 namespace dom {
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -291,16 +291,17 @@ public:
   void PassOptionalNullableSequence(const Optional<Nullable<Sequence<int32_t> > >&);
   void PassOptionalNullableSequenceWithDefaultValue(const Nullable< Sequence<int32_t> >&);
   void PassOptionalObjectSequence(const Optional<Sequence<OwningNonNull<TestInterface> > >&);
 
   void ReceiveStringSequence(nsTArray<nsString>&);
   void PassStringSequence(const Sequence<nsString>&);
 
   void ReceiveAnySequence(JSContext*, nsTArray<JS::Value>&);
+  void ReceiveNullableAnySequence(JSContext*, Nullable<nsTArray<JS::Value> >);
 
   // Typed array types
   void PassArrayBuffer(ArrayBuffer&);
   void PassNullableArrayBuffer(ArrayBuffer*);
   void PassOptionalArrayBuffer(const Optional<ArrayBuffer>&);
   void PassOptionalNullableArrayBuffer(const Optional<ArrayBuffer*>&);
   void PassOptionalNullableArrayBufferWithDefaultValue(ArrayBuffer*);
   void PassArrayBufferView(ArrayBufferView&);
@@ -308,17 +309,17 @@ public:
   void PassInt16Array(Int16Array&);
   void PassInt32Array(Int32Array&);
   void PassUint8Array(Uint8Array&);
   void PassUint16Array(Uint16Array&);
   void PassUint32Array(Uint32Array&);
   void PassUint8ClampedArray(Uint8ClampedArray&);
   void PassFloat32Array(Float32Array&);
   void PassFloat64Array(Float64Array&);
-  JSObject* ReceiveUint8Array();
+  JSObject* ReceiveUint8Array(JSContext*);
 
   // String types
   void PassString(const nsAString&);
   void PassNullableString(const nsAString&);
   void PassOptionalString(const Optional<nsAString>&);
   void PassOptionalStringWithDefaultValue(const nsAString&);
   void PassOptionalNullableString(const Optional<nsAString>&);
   void PassOptionalNullableStringWithDefaultValue(const nsAString&);
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -212,16 +212,17 @@ interface TestInterface {
   void passOptionalNullableSequence(optional sequence<long>? arg);
   void passOptionalNullableSequenceWithDefaultValue(optional sequence<long>? arg = null);
   void passOptionalObjectSequence(optional sequence<TestInterface> arg);
 
   sequence<DOMString> receiveStringSequence();
   void passStringSequence(sequence<DOMString> arg);
 
   sequence<any> receiveAnySequence();
+  sequence<any>? receiveNullableAnySequence();
 
   // Typed array types
   void passArrayBuffer(ArrayBuffer arg);
   void passNullableArrayBuffer(ArrayBuffer? arg);
   void passOptionalArrayBuffer(optional ArrayBuffer arg);
   void passOptionalNullableArrayBuffer(optional ArrayBuffer? arg);
   void passOptionalNullableArrayBufferWithDefaultValue(optional ArrayBuffer? arg= null);
   void passArrayBufferView(ArrayBufferView arg);
--- a/dom/bluetooth/BluetoothManager.cpp
+++ b/dom/bluetooth/BluetoothManager.cpp
@@ -244,35 +244,24 @@ BluetoothManager::Create(nsPIDOMWindow* 
 }
 
 nsresult
 NS_NewBluetoothManager(nsPIDOMWindow* aWindow,
                        nsIDOMBluetoothManager** aBluetoothManager)
 {
   NS_ASSERTION(aWindow, "Null pointer!");
 
-  nsPIDOMWindow* innerWindow = aWindow->IsInnerWindow() ?
-    aWindow :
-    aWindow->GetCurrentInnerWindow();
-
-  // Need the document for security check.
-  nsCOMPtr<nsIDocument> document = innerWindow->GetExtantDoc();
-  NS_ENSURE_TRUE(document, NS_NOINTERFACE);
-
-  nsCOMPtr<nsIPrincipal> principal = document->NodePrincipal();
-  NS_ENSURE_TRUE(principal, NS_ERROR_UNEXPECTED);
-
   nsCOMPtr<nsIPermissionManager> permMgr =
     do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
   NS_ENSURE_TRUE(permMgr, NS_ERROR_UNEXPECTED);
 
   uint32_t permission;
   nsresult rv =
-    permMgr->TestPermissionFromPrincipal(principal, "mozBluetooth",
-                                         &permission);
+    permMgr->TestPermissionFromWindow(aWindow, "mozBluetooth",
+                                      &permission);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsRefPtr<BluetoothManager> bluetoothManager;
 
   if (permission == nsIPermissionManager::ALLOW_ACTION) {
     bluetoothManager = BluetoothManager::Create(aWindow);
   }
 
--- a/dom/bluetooth/BluetoothService.h
+++ b/dom/bluetooth/BluetoothService.h
@@ -170,24 +170,22 @@ public:
   virtual nsresult
   GetProperties(BluetoothObjectType aType,
                 const nsAString& aPath,
                 BluetoothReplyRunnable* aRunnable) = 0;
 
   /** 
    * Fetches the propertes for the specified device
    *
-   * @param aDevicePath Path of the object
-   * @param aSignalPath Path to distrubute signal after receiving properties
+   * @param aSignal BluetoothSignal to be distrubuted after retrieving device properties
    *
    * @return NS_OK on function run, NS_ERROR_FAILURE otherwise
    */
   virtual nsresult
-  GetDevicePropertiesInternal(const nsAString& aDevicePath,
-                              const nsAString& aSignalPath) = 0;
+  GetDevicePropertiesInternal(const BluetoothSignal& aSignal) = 0;
 
   /**
    * Set a property for the specified object
    *
    * @param aPath Path to the object
    * @param aPropName Name of the property
    * @param aValue Boolean value
    * @param aRunnable Runnable to run on async reply
--- a/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp
+++ b/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp
@@ -99,18 +99,17 @@ BluetoothServiceChildProcess::GetDefault
                                               BluetoothReplyRunnable* aRunnable)
 {
   SendRequest(aRunnable, DefaultAdapterPathRequest());
   return NS_OK;
 }
 
 nsresult
 BluetoothServiceChildProcess::GetDevicePropertiesInternal(
-                                                   const nsAString& aDevicePath,
-                                                   const nsAString& aSignalPath)
+                                                 const BluetoothSignal& aSignal)
 {
   MOZ_NOT_REACHED("Should never be called from child");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 nsresult
 BluetoothServiceChildProcess::GetPairedDevicePropertiesInternal(
                                      const nsTArray<nsString>& aDeviceAddresses,
@@ -226,60 +225,60 @@ BluetoothServiceChildProcess::CloseSocke
 bool
 BluetoothServiceChildProcess::SetPinCodeInternal(
                                                 const nsAString& aDeviceAddress,
                                                 const nsAString& aPinCode,
                                                 BluetoothReplyRunnable* aRunnable)
 {
   SendRequest(aRunnable,
               SetPinCodeRequest(nsString(aDeviceAddress), nsString(aPinCode)));
-  return NS_OK;
+  return true;
 }
 
 bool
 BluetoothServiceChildProcess::SetPasskeyInternal(
                                                 const nsAString& aDeviceAddress,
                                                 uint32_t aPasskey,
                                                 BluetoothReplyRunnable* aRunnable)
 {
   SendRequest(aRunnable,
               SetPasskeyRequest(nsString(aDeviceAddress), aPasskey));
-  return NS_OK;
+  return true;
 }
 
 bool
 BluetoothServiceChildProcess::SetPairingConfirmationInternal(
                                                 const nsAString& aDeviceAddress,
                                                 bool aConfirm,
                                                 BluetoothReplyRunnable* aRunnable)
 {
   if(aConfirm) {
     SendRequest(aRunnable,
                 ConfirmPairingConfirmationRequest(nsString(aDeviceAddress)));
   } else {
     SendRequest(aRunnable,
                 DenyPairingConfirmationRequest(nsString(aDeviceAddress)));
   }
-  return NS_OK;
+  return true;
 }
 
 bool
 BluetoothServiceChildProcess::SetAuthorizationInternal(
                                                 const nsAString& aDeviceAddress,
                                                 bool aAllow,
                                                 BluetoothReplyRunnable* aRunnable)
 {
   if(aAllow) {
     SendRequest(aRunnable,
                 ConfirmAuthorizationRequest(nsString(aDeviceAddress)));
   } else {
     SendRequest(aRunnable,
                 DenyAuthorizationRequest(nsString(aDeviceAddress)));
   }
-  return NS_OK;
+  return true;
 }
 
 nsresult
 BluetoothServiceChildProcess::PrepareAdapterInternal(const nsAString& aPath)
 {
   MOZ_NOT_REACHED("Should never be called from child");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
--- a/dom/bluetooth/ipc/BluetoothServiceChildProcess.h
+++ b/dom/bluetooth/ipc/BluetoothServiceChildProcess.h
@@ -137,18 +137,17 @@ private:
   StartInternal() MOZ_OVERRIDE;
 
   // This method should never be called.
   virtual nsresult
   StopInternal() MOZ_OVERRIDE;
 
   // Should never be called from the child
   virtual nsresult
-  GetDevicePropertiesInternal(const nsAString& aDevicePath,
-                              const nsAString& aSignalPath) MOZ_OVERRIDE;
+  GetDevicePropertiesInternal(const BluetoothSignal& aSignal) MOZ_OVERRIDE;
 
   // This method should never be called from the child.
   virtual nsresult
   PrepareAdapterInternal(const nsAString& aPath) MOZ_OVERRIDE;
 };
 
 END_BLUETOOTH_NAMESPACE
 
--- a/dom/bluetooth/linux/BluetoothDBusService.cpp
+++ b/dom/bluetooth/linux/BluetoothDBusService.cpp
@@ -231,16 +231,50 @@ public:
     }
     return NS_OK;
   }
 
 private:
   nsString mPath;
 };
 
+class DevicePropertiesSignalHandler : public nsRunnable
+{
+public:
+  DevicePropertiesSignalHandler(const BluetoothSignal& aSignal) :
+    mSignal(aSignal)
+  {
+  }
+
+  NS_IMETHODIMP
+  Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    // Get device properties and then send to BluetoothAdapter
+    BluetoothService* bs = BluetoothService::Get();
+    if (!bs) {
+      NS_WARNING("BluetoothService not available!");
+      return NS_ERROR_FAILURE;
+    }
+
+    // Due to the fact that we need to queue the dbus call to the command thread
+    // inside the bluetoothservice, we have to route the call down to the main
+    // thread and then back out to the command thread. There has to be a better
+    // way to do this.
+    if (NS_FAILED(bs->GetDevicePropertiesInternal(mSignal))) {
+      NS_WARNING("get properties failed");
+      return NS_ERROR_FAILURE;
+    }
+    return NS_OK;
+  }
+private:
+  BluetoothSignal mSignal;
+};
+
 static bool
 IsDBusMessageError(DBusMessage* aMsg, DBusError* aErr, nsAString& aErrorStr)
 {
   if (aErr && dbus_error_is_set(aErr)) {
     aErrorStr = NS_ConvertUTF8toUTF16(aErr->message);
     LOG_AND_FREE_DBUS_ERROR(aErr);
     return true;
   }
@@ -413,58 +447,85 @@ AgentEventFilter(DBusConnection *conn, D
     uint32_t passkey;
     if (!dbus_message_get_args(msg, NULL,
                                DBUS_TYPE_OBJECT_PATH, &objectPath,
                                DBUS_TYPE_UINT32, &passkey,
                                DBUS_TYPE_INVALID)) {
       LOG("%s: Invalid arguments for RequestConfirmation() method", __FUNCTION__);
       errorStr.AssignLiteral("Invalid arguments for RequestConfirmation() method");
     } else {
-      nsString deviceAddress = GetAddressFromObjectPath(NS_ConvertUTF8toUTF16(objectPath));
-      parameters.AppendElement(BluetoothNamedValue(NS_LITERAL_STRING("deviceAddress"), deviceAddress));
+      nsString address = NS_LITERAL_STRING("address");
+      parameters.AppendElement(BluetoothNamedValue(address, NS_ConvertUTF8toUTF16(objectPath)));
       parameters.AppendElement(BluetoothNamedValue(NS_LITERAL_STRING("passkey"), passkey));
 
-      KeepDBusPairingMessage(deviceAddress, msg);
+      KeepDBusPairingMessage(address, msg);
+
+      BluetoothSignal signal(signalName, signalPath, parameters);
 
-      v = parameters;
+      // Fire a Device properties fetcher at the main thread
+      nsRefPtr<DevicePropertiesSignalHandler> b =
+        new DevicePropertiesSignalHandler(signal);
+      if (NS_FAILED(NS_DispatchToMainThread(b))) {
+        NS_WARNING("Failed to dispatch to main thread!");
+      }
+      // Since we're handling this in other threads, just fall out here
+      return DBUS_HANDLER_RESULT_HANDLED;
     }
   } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "RequestPinCode")) {
     // This method gets called when the service daemon needs to get the passkey for an
     // authentication. The return value should be a string of 1-16 characters length.
     // The string can be alphanumeric.
     char *objectPath;
     if (!dbus_message_get_args(msg, NULL,
                                DBUS_TYPE_OBJECT_PATH, &objectPath,
                                DBUS_TYPE_INVALID)) {
       LOG("%s: Invalid arguments for RequestPinCode() method", __FUNCTION__);
       errorStr.AssignLiteral("Invalid arguments for RequestPinCode() method");
     } else {
-      nsString deviceAddress = GetAddressFromObjectPath(NS_ConvertUTF8toUTF16(objectPath));
-      parameters.AppendElement(BluetoothNamedValue(NS_LITERAL_STRING("deviceAddress"), deviceAddress));
+      nsString address = NS_LITERAL_STRING("address");
+      parameters.AppendElement(BluetoothNamedValue(address, NS_ConvertUTF8toUTF16(objectPath)));
+
+      KeepDBusPairingMessage(address, msg);
+
+      BluetoothSignal signal(signalName, signalPath, parameters);
 
-      KeepDBusPairingMessage(deviceAddress, msg);
-
-      v = parameters;
+      // Fire a Device properties fetcher at the main thread
+      nsRefPtr<DevicePropertiesSignalHandler> b =
+        new DevicePropertiesSignalHandler(signal);
+      if (NS_FAILED(NS_DispatchToMainThread(b))) {
+        NS_WARNING("Failed to dispatch to main thread!");
+      }
+      // Since we're handling this in other threads, just fall out here
+      return DBUS_HANDLER_RESULT_HANDLED;
     }
   } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "RequestPasskey")) {
     // This method gets called when the service daemon needs to get the passkey for an
     // authentication. The return value should be a numeric value between 0-999999.
     char *objectPath;
     if (!dbus_message_get_args(msg, NULL,
                                DBUS_TYPE_OBJECT_PATH, &objectPath,
                                DBUS_TYPE_INVALID)) {
       LOG("%s: Invalid arguments for RequestPasskey() method", __FUNCTION__);
       errorStr.AssignLiteral("Invalid arguments for RequestPasskey() method");
     } else {
-      nsString deviceAddress = GetAddressFromObjectPath(NS_ConvertUTF8toUTF16(objectPath));
-      parameters.AppendElement(BluetoothNamedValue(NS_LITERAL_STRING("deviceAddress"), deviceAddress));
+      nsString address = NS_LITERAL_STRING("address");
+      parameters.AppendElement(BluetoothNamedValue(address, NS_ConvertUTF8toUTF16(objectPath)));
+
+      KeepDBusPairingMessage(address, msg);
+
+      BluetoothSignal signal(signalName, signalPath, parameters);
 
-      KeepDBusPairingMessage(deviceAddress, msg);
-
-      v = parameters;
+      // Fire a Device properties fetcher at the main thread
+      nsRefPtr<DevicePropertiesSignalHandler> b =
+        new DevicePropertiesSignalHandler(signal);
+      if (NS_FAILED(NS_DispatchToMainThread(b))) {
+        NS_WARNING("Failed to dispatch to main thread!");
+      }
+      // Since we're handling this in other threads, just fall out here
+      return DBUS_HANDLER_RESULT_HANDLED;
     }
   } else if (dbus_message_is_method_call(msg, DBUS_AGENT_IFACE, "Release")) {
     // This method gets called when the service daemon unregisters the agent. An agent
     // can use it to do cleanup tasks. There is no need to unregister the agent, because
     // when this method gets called it has already been unregistered.
     DBusMessage *reply = dbus_message_new_method_return(msg);
 
     if (!reply) {
@@ -1025,53 +1086,16 @@ GetPropertiesInternal(const nsAString& a
     return false;
   }
   if (msg) {
     dbus_message_unref(msg);
   }
   return true;
 }
 
-class DevicePropertiesSignalHandler : public nsRunnable
-{
-public:
-  DevicePropertiesSignalHandler(const BluetoothValue& aValue,
-                                const nsAString& aPath) :
-    mValue(aValue),
-    mPath(aPath)
-  {
-  }
-
-  NS_IMETHODIMP
-  Run()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    // Get device properties and then send to BluetoothAdapter
-    BluetoothService* bs = BluetoothService::Get();
-    if (!bs) {
-      NS_WARNING("BluetoothService not available!");
-      return NS_ERROR_FAILURE;
-    }
-
-    // Due to the fact that we need to queue the dbus call to the command thread
-    // inside the bluetoothservice, we have to route the call down to the main
-    // thread and then back out to the command thread. There has to be a better
-    // way to do this.
-    if (NS_FAILED(bs->GetDevicePropertiesInternal(mValue, mPath))) {
-      NS_WARNING("get properties failed");
-      return NS_ERROR_FAILURE;
-    }
-    return NS_OK;
-  }
-private:
-  BluetoothValue mValue;
-  nsString mPath;
-};
-
 // Called by dbus during WaitForAndDispatchEventNative()
 // This function is called on the IOThread
 static
 DBusHandlerResult
 EventFilter(DBusConnection* aConn, DBusMessage* aMsg, void* aData)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Shouldn't be called from Main Thread!");
 
@@ -1142,19 +1166,22 @@ EventFilter(DBusConnection* aConn, DBusM
     if (!dbus_message_get_args(aMsg, &err,
                                DBUS_TYPE_OBJECT_PATH, &str,
                                DBUS_TYPE_INVALID)) {
       LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, aMsg);
       errorStr.AssignLiteral("Cannot parse device path!");
     } else {
       v = NS_ConvertUTF8toUTF16(str);
     }
+
+    BluetoothSignal signal(signalName, signalPath, v);
+
     // Fire a Device properties fetcher at the main thread
     nsRefPtr<DevicePropertiesSignalHandler> b =
-      new DevicePropertiesSignalHandler(v, signalPath);
+      new DevicePropertiesSignalHandler(signal);
     if (NS_FAILED(NS_DispatchToMainThread(b))) {
       NS_WARNING("Failed to dispatch to main thread!");
     }
     // Since we're handling this in other threads, just fall out here
     return DBUS_HANDLER_RESULT_HANDLED;
   } else if (dbus_message_is_signal(aMsg, DBUS_ADAPTER_IFACE, "DeviceRemoved")) {
     const char* str;
     if (!dbus_message_get_args(aMsg, &err,
@@ -1485,53 +1512,91 @@ BluetoothDBusService::StartDiscoveryInte
     return NS_ERROR_FAILURE;
   }
   return SendDiscoveryMessage(aAdapterPath, "StartDiscovery", aRunnable);
 }
 
 class BluetoothDevicePropertiesRunnable : public nsRunnable
 {
 public:
-  BluetoothDevicePropertiesRunnable(const nsAString& aDevicePath,
-                                    const nsAString& aSignalPath) :
-    mDevicePath(aDevicePath),
-    mSignalPath(aSignalPath)
+  BluetoothDevicePropertiesRunnable(const BluetoothSignal& aSignal) :
+    mSignal(aSignal)
   {
     MOZ_ASSERT(NS_IsMainThread());
   }
 
   ~BluetoothDevicePropertiesRunnable()
   {
   }
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(!NS_IsMainThread());
-    BluetoothValue v;
-    if (!GetPropertiesInternal(mDevicePath, DBUS_DEVICE_IFACE, v)) {
+
+    nsString devicePath;
+    BluetoothValue v = mSignal.value();
+    if (v.type() == BluetoothValue::TArrayOfBluetoothNamedValue &&
+        v.get_ArrayOfBluetoothNamedValue().Length() ) {
+      InfallibleTArray<BluetoothNamedValue> arr = v.get_ArrayOfBluetoothNamedValue();
+      NS_ASSERTION(arr[0].value().type() == BluetoothValue::TnsString, "failed to get_nsString");
+      devicePath = arr[0].value().get_nsString();
+    }
+    else if (v.type() == BluetoothValue::TnsString) {
+      devicePath = v.get_nsString();
+    }
+    else {
+      NS_WARNING("Invalid value type for GetDeviceProperties() method");
+      return NS_ERROR_FAILURE;
+    }
+
+    BluetoothValue prop;
+    if (!GetPropertiesInternal(devicePath, DBUS_DEVICE_IFACE, prop)) {
       NS_WARNING("Getting properties failed!");
       return NS_ERROR_FAILURE;
     }
-    BluetoothSignal signal(NS_LITERAL_STRING("DeviceCreated"),
-                           mSignalPath, v);
+
+    InfallibleTArray<BluetoothNamedValue> properties = prop.get_ArrayOfBluetoothNamedValue();
+    if (v.type() == BluetoothValue::TArrayOfBluetoothNamedValue) {
+      // Return original dbus message parameters and also device name
+      // for agent events "RequestConfirmation", "RequestPinCode", and "RequestPasskey"
+      InfallibleTArray<BluetoothNamedValue> parameters = v.get_ArrayOfBluetoothNamedValue();
+
+      // Replace object path with device address
+      nsString address = GetAddressFromObjectPath(parameters[0].value());
+      parameters[0].value() = address;
+
+      uint8_t i;
+      for (i = 0; i < properties.Length(); i++) {
+        // Append device name
+        if (properties[i].name().EqualsLiteral("Name")) {
+          properties[i].name().AssignLiteral("name");
+          parameters.AppendElement(properties[i]);
+          mSignal.value() = parameters;
+          break;
+        }
+      }
+      NS_ASSERTION(i != properties.Length(), "failed to get device name");
+    } else {
+      // Return all device properties for event "DeviceCreated"
+      mSignal.value() = properties;
+    }
 
     nsRefPtr<DistributeBluetoothSignalTask> t =
-      new DistributeBluetoothSignalTask(signal);
+      new DistributeBluetoothSignalTask(mSignal);
 
     if (NS_FAILED(NS_DispatchToMainThread(t))) {
        NS_WARNING("Failed to dispatch to main thread!");
        return NS_ERROR_FAILURE;
     }
 
     return NS_OK;
   }
 
 private:
-  nsString mDevicePath;
-  nsString mSignalPath;
+  BluetoothSignal mSignal;
 };
 
 class BluetoothPairedDevicePropertiesRunnable : public nsRunnable
 {
 public:
   BluetoothPairedDevicePropertiesRunnable(BluetoothReplyRunnable* aRunnable,
                                           const nsTArray<nsString>& aDeviceAddresses)
     : mRunnable(dont_AddRef(aRunnable)),
@@ -1620,27 +1685,26 @@ BluetoothDBusService::GetProperties(Blue
     NS_WARNING("Could not start async function!");
     return NS_ERROR_FAILURE;
   }
   runnable.forget();
   return NS_OK;
 }
 
 nsresult
-BluetoothDBusService::GetDevicePropertiesInternal(const nsAString& aDevicePath,
-                                                  const nsAString& aSignalPath)
+BluetoothDBusService::GetDevicePropertiesInternal(const BluetoothSignal& aSignal)
 {
   NS_ASSERTION(NS_IsMainThread(), "Must be called from main thread!");
 
   if (!mConnection || !gThreadConnection) {
     NS_ERROR("Bluetooth service not started yet!");
     return NS_ERROR_FAILURE;
   }
 
-  nsRefPtr<nsRunnable> func(new BluetoothDevicePropertiesRunnable(aDevicePath, aSignalPath));
+  nsRefPtr<nsRunnable> func(new BluetoothDevicePropertiesRunnable(aSignal));
   if (NS_FAILED(mBluetoothCommandThread->Dispatch(func, NS_DISPATCH_NORMAL))) {
     NS_WARNING("Cannot dispatch task!");
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
--- a/dom/bluetooth/linux/BluetoothDBusService.h
+++ b/dom/bluetooth/linux/BluetoothDBusService.h
@@ -20,37 +20,44 @@ BEGIN_BLUETOOTH_NAMESPACE
  * linux/android/B2G. Function comments are in BluetoothService.h
  */
 
 class BluetoothDBusService : public BluetoothService
                            , private mozilla::ipc::RawDBusConnection
 {
 public:
   virtual nsresult StartInternal();
+
   virtual nsresult StopInternal();
+
   virtual nsresult GetDefaultAdapterPathInternal(BluetoothReplyRunnable* aRunnable);
+
   virtual nsresult GetPairedDevicePropertiesInternal(const nsTArray<nsString>& aDeviceAddresses,
                                                      BluetoothReplyRunnable* aRunnable);
+
   virtual nsresult StartDiscoveryInternal(const nsAString& aAdapterPath,
                                           BluetoothReplyRunnable* aRunnable);
+
   virtual nsresult StopDiscoveryInternal(const nsAString& aAdapterPath,
                                          BluetoothReplyRunnable* aRunnable);
+
   virtual nsresult
   GetProperties(BluetoothObjectType aType,
                 const nsAString& aPath,
                 BluetoothReplyRunnable* aRunnable);
+
   virtual nsresult
-  GetDevicePropertiesInternal(const nsAString& aDevicePath,
-                              const nsAString& aSignalPath);
+  GetDevicePropertiesInternal(const BluetoothSignal& aSignal);
 
   virtual nsresult
   SetProperty(BluetoothObjectType aType,
               const nsAString& aPath,
               const BluetoothNamedValue& aValue,
               BluetoothReplyRunnable* aRunnable);
+
   virtual bool
   GetDevicePath(const nsAString& aAdapterPath,
                 const nsAString& aDeviceAddress,
                 nsAString& aDevicePath);
 
   static bool
   AddReservedServicesInternal(const nsAString& aAdapterPath,
                               const nsTArray<uint32_t>& aServices,
--- a/dom/camera/DOMCameraControl.h
+++ b/dom/camera/DOMCameraControl.h
@@ -1,17 +1,15 @@
 /* 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 DOM_CAMERA_DOMCAMERACONTROL_H
 #define DOM_CAMERA_DOMCAMERACONTROL_H
 
-#include "base/basictypes.h"
-#include "prtypes.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "DictionaryHelpers.h"
 #include "ICameraControl.h"
 #include "DOMCameraPreview.h"
 #include "nsIDOMCameraManager.h"
 #include "CameraCommon.h"
--- a/dom/camera/DOMCameraManager.cpp
+++ b/dom/camera/DOMCameraManager.cpp
@@ -1,12 +1,15 @@
 /* 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 "nsDebug.h"
+#include "nsIDocument.h"
+#include "nsIPermissionManager.h"
 #include "DOMCameraControl.h"
 #include "DOMCameraManager.h"
 #include "nsDOMClassInfo.h"
 #include "DictionaryHelpers.h"
 #include "CameraCommon.h"
 
 using namespace mozilla;
 
@@ -22,19 +25,17 @@ NS_IMPL_ADDREF(nsDOMCameraManager)
 NS_IMPL_RELEASE(nsDOMCameraManager)
 
 /**
  * Global camera logging object
  *
  * Set the NSPR_LOG_MODULES environment variable to enable logging
  * in a debug build, e.g. NSPR_LOG_MODULES=Camera:5
  */
-#ifdef PR_LOGGING
-PRLogModuleInfo* gCameraLog;
-#endif
+PRLogModuleInfo* gCameraLog = PR_LOG_DEFINE("Camera");
 
 /**
  * nsDOMCameraManager::GetListOfCameras
  * is implementation-specific, and can be found in (e.g.)
  * GonkCameraManager.cpp and FallbackCameraManager.cpp.
  */
 
 nsDOMCameraManager::nsDOMCameraManager(uint64_t aWindowId)
@@ -53,26 +54,31 @@ nsDOMCameraManager::~nsDOMCameraManager(
 void
 nsDOMCameraManager::OnNavigation(uint64_t aWindowId)
 {
   // TODO: see bug 779145.
 }
 
 // static creator
 already_AddRefed<nsDOMCameraManager>
-nsDOMCameraManager::Create(uint64_t aWindowId)
+nsDOMCameraManager::CheckPermissionAndCreateInstance(nsPIDOMWindow* aWindow)
 {
-  // TODO: see bug 776934.
+  nsCOMPtr<nsIPermissionManager> permMgr =
+    do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
+  NS_ENSURE_TRUE(permMgr, nullptr);
 
-#ifdef PR_LOGGING
-  if (!gCameraLog) {
-    gCameraLog = PR_LOG_DEFINE("Camera");
+  uint32_t permission = nsIPermissionManager::DENY_ACTION;
+  permMgr->TestPermissionFromWindow(aWindow, "camera", &permission);
+  if (permission != nsIPermissionManager::ALLOW_ACTION) {
+    NS_WARNING("No permission to access camera");
+    return nullptr;
   }
-#endif
-  nsRefPtr<nsDOMCameraManager> cameraManager = new nsDOMCameraManager(aWindowId);
+
+  nsRefPtr<nsDOMCameraManager> cameraManager =
+    new nsDOMCameraManager(aWindow->WindowID());
   return cameraManager.forget();
 }
 
 /* [implicit_jscontext] void getCamera ([optional] in jsval aOptions, in nsICameraGetCameraCallback onSuccess, [optional] in nsICameraErrorCallback onError); */
 NS_IMETHODIMP
 nsDOMCameraManager::GetCamera(const JS::Value& aOptions, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, JSContext* cx)
 {
   NS_ENSURE_TRUE(onSuccess, NS_ERROR_INVALID_ARG);
--- a/dom/camera/DOMCameraManager.h
+++ b/dom/camera/DOMCameraManager.h
@@ -9,23 +9,26 @@
 
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsIThread.h"
 #include "nsThreadUtils.h"
 #include "nsIDOMCameraManager.h"
 #include "mozilla/Attributes.h"
 
+class nsPIDOMWindow;
+
 class nsDOMCameraManager MOZ_FINAL : public nsIDOMCameraManager
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMCAMERAMANAGER
 
-  static already_AddRefed<nsDOMCameraManager> Create(uint64_t aWindowId);
+  static already_AddRefed<nsDOMCameraManager>
+    CheckPermissionAndCreateInstance(nsPIDOMWindow* aWindow);
 
   void OnNavigation(uint64_t aWindowId);
 
 private:
   nsDOMCameraManager();
   nsDOMCameraManager(uint64_t aWindowId);
   nsDOMCameraManager(const nsDOMCameraManager&) MOZ_DELETE;
   nsDOMCameraManager& operator=(const nsDOMCameraManager&) MOZ_DELETE;
--- a/dom/camera/ICameraControl.h
+++ b/dom/camera/ICameraControl.h
@@ -1,17 +1,15 @@
 /* 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 DOM_CAMERA_ICAMERACONTROL_H
 #define DOM_CAMERA_ICAMERACONTROL_H
 
-#include "base/basictypes.h"
-#include "prtypes.h"
 #include "jsapi.h"
 #include "nsIDOMCameraManager.h"
 #include "DictionaryHelpers.h"
 #include "CameraCommon.h"
 
 namespace mozilla {
 
 using namespace dom;
--- a/dom/dom-config.mk
+++ b/dom/dom-config.mk
@@ -40,14 +40,20 @@ ifdef MOZ_B2G_RIL
 DOM_SRCDIRS += \
   dom/system/gonk \
   dom/telephony \
   dom/wifi \
   dom/icc/src \
   $(NULL)
 endif
 
+ifdef MOZ_B2G_FM
+DOM_SRCDIRS += \
+  dom/fm \
+  $(NULL)
+endif
+
 ifdef MOZ_B2G_BT
 DOM_SRCDIRS += dom/bluetooth
 endif
 
 LOCAL_INCLUDES += $(DOM_SRCDIRS:%=-I$(topsrcdir)/%)
 DEFINES += -D_IMPL_NS_LAYOUT
new file mode 100644
--- /dev/null
+++ b/dom/fm/DOMFMRadio.manifest
@@ -0,0 +1,4 @@
+# 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
new file mode 100644
--- /dev/null
+++ b/dom/fm/DOMFMRadioChild.js
@@ -0,0 +1,390 @@
+/* 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]),
+
+  // nsIDOMGlobalPropertyInitializer implementation
+  init: function(aWindow) {
+    let principal = aWindow.document.nodePrincipal;
+    let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"]
+                   .getService(Ci.nsIScriptSecurityManager);
+
+    let perm = (principal == secMan.getSystemPrincipal()) ?
+                 Ci.nsIPermissionManager.ALLOW_ACTION :
+                 Services.perms.testExactPermission(principal.URI, "fmradio");
+    this._hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION;
+
+    if (!this._hasPrivileges) {
+      throw new Components.Exception("Denied", Cr.NS_ERROR_FAILURE);
+    }
+
+    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.initHelper(aWindow, messages);
+  },
+
+  // 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);
+  },
+
+  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":
+        request = this.takeRequest(msg.rid);
+        if (!request) {
+          return;
+        }
+        Services.DOMRequest.fireSuccess(request, null);
+        break;
+      case "DOMFMRadio:disable:Return:NO":
+        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) {
+    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);
+        }
+      }
+    }
+  }
+};
+
+const NSGetFactory = XPCOMUtils.generateNSGetFactory([DOMFMRadioChild]);
+
new file mode 100644
--- /dev/null
+++ b/dom/fm/DOMFMRadioParent.jsm
@@ -0,0 +1,456 @@
+/* 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");
+
+let EXPORTED_SYMBOLS = ["DOMFMRadioParent"];
+
+let DOMFMRadioParent = {
+  _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"
+                     ];
+    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() {
+      debug("FM Radio is enabled!");
+      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", { });
+    });
+
+    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;
+    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;
+    }
+  }
+};
+
+DOMFMRadioParent.init();
+
new file mode 100644
--- /dev/null
+++ b/dom/fm/FMRadio.cpp
@@ -0,0 +1,241 @@
+/* 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_DIABLED_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)
+{
+  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_IMPL_CYCLE_COLLECTION_CLASS(FMRadio)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FMRadio,
+                                                  nsDOMEventTargetHelper)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(FMRadio,
+                                               nsDOMEventTargetHelper)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FMRadio,
+                                                nsDOMEventTargetHelper)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(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_ON;
+  }
+  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;
+
+  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);
+
+  return NS_OK;
+}
+
+/* void disableRadio (); */
+NS_IMETHODIMP FMRadio::Disable()
+{
+  nsCOMPtr<nsIAudioManager> audioManager =
+    do_GetService(NS_AUDIOMANAGER_CONTRACTID);
+  NS_ENSURE_TRUE(audioManager, NS_OK);
+
+  audioManager->SetFmRadioAudioEnabled(false);
+
+  DisableFMRadio();
+
+  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;
+}
+
+void FMRadio::Notify(const SwitchEvent& aEvent)
+{
+  if (mHeadphoneState != aEvent.status()) {
+    LOG("Antenna state is changed!");
+    mHeadphoneState = aEvent.status();
+    DispatchTrustedEventToSelf(ANTENNA_STATE_CHANGED_EVENT_NAME);
+  }
+}
+
+void FMRadio::Notify(const FMRadioOperationInformation& info)
+{
+  switch (info.operation())
+  {
+    case FM_RADIO_OPERATION_ENABLE:
+      DispatchTrustedEventToSelf(RADIO_ENABLED_EVENT_NAME);
+      break;
+    case FM_RADIO_OPERATION_DISABLE:
+      DispatchTrustedEventToSelf(RADIO_DIABLED_EVENT_NAME);
+      break;
+    case FM_RADIO_OPERATION_SEEK:
+      DispatchTrustedEventToSelf(RADIO_SEEK_COMPLETE_EVENT_NAME);
+      break;
+  }
+}
+
+nsresult
+FMRadio::DispatchTrustedEventToSelf(const nsAString& aEventName)
+{
+  nsRefPtr<nsDOMEvent> event = new nsDOMEvent(nullptr, nullptr);
+  nsresult rv = event->InitEvent(aEventName,
+                                 /* bubbles = */ false,
+                                 /* cancelable = */ false);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = event->SetTrusted(true);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool dummy;
+  rv = DispatchEvent(event, &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
new file mode 100644
--- /dev/null
+++ b/dom/fm/FMRadio.h
@@ -0,0 +1,56 @@
+/* -*- 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"
+
+#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:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIFMRADIO
+
+  NS_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper::)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(
+                                                   FMRadio,
+                                                   nsDOMEventTargetHelper)
+  FMRadio();
+  virtual void Notify(const hal::FMRadioOperationInformation& info);
+  virtual void Notify(const hal::SwitchEvent& aEvent);
+
+private:
+  ~FMRadio();
+  bool mHasInternalAntenna;
+  hal::SwitchState mHeadphoneState;
+  /**
+   * Dispatch a trusted non-cancellable and no-bubbling event to itself
+   */
+  nsresult DispatchTrustedEventToSelf(const nsAString& aEventName);
+};
+
+} // namespace fm
+} // namespace dom
+} // namespace mozilla
+#endif
+
new file mode 100644
--- /dev/null
+++ b/dom/fm/Makefile.in
@@ -0,0 +1,45 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEPTH            = ../..
+topsrcdir        = @top_srcdir@
+srcdir           = @srcdir@
+VPATH            = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE           = dom
+LIBRARY_NAME     = domfm_s
+XPIDL_MODULE     = dom_fm
+LIBXUL_LIBRARY   = 1
+FORCE_STATIC_LIB = 1
+
+include $(topsrcdir)/dom/dom-config.mk
+
+CPPSRCS += \
+  FMRadio.cpp \
+  nsFMRadioSettings.cpp \
+  $(NULL)
+
+XPIDLSRCS = \
+  nsIDOMFMRadio.idl \
+  nsIFMRadio.idl \
+  $(NULL)
+
+EXTRA_COMPONENTS =      \
+  DOMFMRadioChild.js       \
+  DOMFMRadio.manifest \
+  $(NULL)
+
+EXTRA_JS_MODULES =   \
+  DOMFMRadioParent.jsm \
+  $(NULL)
+
+include $(topsrcdir)/config/rules.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
+
+XPIDL_FLAGS += \
+  -I$(topsrcdir)/dom/interfaces/events \
+  $(NULL)
+
new file mode 100644
--- /dev/null
+++ b/dom/fm/nsFMRadioSettings.cpp
@@ -0,0 +1,61 @@
+/* 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;
+}
+
new file mode 100644
--- /dev/null
+++ b/dom/fm/nsFMRadioSettings.h
@@ -0,0 +1,26 @@
+/* -*- 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
+
new file mode 100644
--- /dev/null
+++ b/dom/fm/nsIDOMFMRadio.idl
@@ -0,0 +1,122 @@
+/* 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);
+};
+
new file mode 100644
--- /dev/null
+++ b/dom/fm/nsIFMRadio.idl
@@ -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/. */
+
+#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(26288adc-d2c1-4fbc-86b5-ecd8173fbf90)]
+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);
+
+    /**
+     * 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;
+};
+
--- a/dom/interfaces/events/nsIDOMEventTarget.idl
+++ b/dom/interfaces/events/nsIDOMEventTarget.idl
@@ -98,32 +98,32 @@ interface nsIDOMEventTarget : nsISupport
                                    [optional] in boolean aWantsUntrusted);
 
 %{C++
   // non-virtual so it won't affect the vtable
   nsresult AddEventListener(const nsAString& aType,
                             nsIDOMEventListener* aListener,
                             bool aUseCapture)
   {
-    return AddEventListener(aType, aListener, aUseCapture, PR_FALSE, 1);
+    return AddEventListener(aType, aListener, aUseCapture, false, 1);
   }
   // non-virtual so it won't affect the vtable
   nsresult AddEventListener(const nsAString& aType,
                             nsIDOMEventListener* aListener,
                             bool aUseCapture,
                             bool aWantsUntrusted)
   {
     return AddEventListener(aType, aListener, aUseCapture, aWantsUntrusted, 2);
   }
   // non-virtual so it won't affect the vtable
   nsresult AddSystemEventListener(const nsAString& aType,
                                   nsIDOMEventListener* aListener,
                                   bool aUseCapture)
   {
-    return AddSystemEventListener(aType, aListener, aUseCapture, PR_FALSE, 1);
+    return AddSystemEventListener(aType, aListener, aUseCapture, false, 1);
   }
   // non-virtual so it won't affect the vtable
   nsresult AddSystemEventListener(const nsAString& aType,
                                   nsIDOMEventListener* aListener,
                                   bool aUseCapture,
                                   bool aWantsUntrusted)
   {
     return AddSystemEventListener(aType, aListener, aUseCapture,
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -281,24 +281,35 @@ ContentParent::GetNewOrUsed(bool aForBro
     p->Init();
     gNonAppContentParents->AppendElement(p);
     return p;
 }
 
 static bool
 AppNeedsInheritedOSPrivileges(mozIApplication* aApp)
 {
-    bool needsInherit = false;
-    // FIXME/bug 785592: implement a CameraBridge so we don't have to
-    // hack around with OS permissions
-    if (NS_FAILED(aApp->HasPermission("camera", &needsInherit))) {
-        NS_WARNING("Unable to check permissions.  Breakage may follow.");
-        return false;
+    const char* const needInheritPermissions[] = {
+        // FIXME/bug 785592: implement a CameraBridge so we don't have
+        // to hack around with OS permissions
+        "camera",
+        // FIXME/bug 793034: change our video architecture so that we
+        // can stream video from remote processes
+        "deprecated-hwvideo",
+    };
+    for (size_t i = 0; i < ArrayLength(needInheritPermissions); ++i) {
+        const char* const permission = needInheritPermissions[i];
+        bool needsInherit = false;
+        if (NS_FAILED(aApp->HasPermission(permission, &needsInherit))) {
+            NS_WARNING("Unable to check permissions.  Breakage may follow.");
+            return false;
+        } else if (needsInherit) {
+            return true;
+        }
     }
-    return needsInherit;
+    return false;
 }
 
 /*static*/ TabParent*
 ContentParent::CreateBrowser(mozIApplication* aApp, bool aIsBrowserElement)
 {
     // We currently don't set the <app> ancestor for <browser> content
     // correctly.  This assertion is to notify the person who fixes
     // this code that they need to reevaluate places here where we may
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -20,100 +20,206 @@
 #else
 #include "MediaEngineDefault.h"
 #endif
 
 namespace mozilla {
 
 /**
  * Send an error back to content. The error is the form a string.
- * Do this only on the main thread.
+ * Do this only on the main thread. The success callback is also passed here
+ * so it can be released correctly.
  */
 class ErrorCallbackRunnable : public nsRunnable
 {
 public:
-  ErrorCallbackRunnable(nsIDOMGetUserMediaErrorCallback* aError,
+  ErrorCallbackRunnable(
+    already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
+    already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
     const nsString& aErrorMsg, uint64_t aWindowID)
-    : mError(aError)
+    : mSuccess(aSuccess)
+    , mError(aError)
     , mErrorMsg(aErrorMsg)
     , mWindowID(aWindowID) {}
 
   NS_IMETHOD
   Run()
   {
     // Only run if the window is still active.
+    NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+
+    nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> success(mSuccess);
+    nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error(mError);
+
     WindowTable* activeWindows = MediaManager::Get()->GetActiveWindows();
     if (activeWindows->Get(mWindowID)) {
-      mError->OnError(mErrorMsg);
+      error->OnError(mErrorMsg);
     }
     return NS_OK;
   }
 
 private:
-  nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mError;
+  already_AddRefed<nsIDOMGetUserMediaSuccessCallback> mSuccess;
+  already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
   const nsString mErrorMsg;
   uint64_t mWindowID;
 };
 
 /**
  * Invoke the "onSuccess" callback in content. The callback will take a
  * DOMBlob in the case of {picture:true}, and a MediaStream in the case of
  * {audio:true} or {video:true}. There is a constructor available for each
  * form. Do this only on the main thread.
  */
 class SuccessCallbackRunnable : public nsRunnable
 {
 public:
-  SuccessCallbackRunnable(nsIDOMGetUserMediaSuccessCallback* aSuccess,
+  SuccessCallbackRunnable(
+    already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
+    already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
     nsIDOMFile* aFile, uint64_t aWindowID)
     : mSuccess(aSuccess)
+    , mError(aError)
     , mFile(aFile)
     , mWindowID(aWindowID) {}
 
   NS_IMETHOD
   Run()
   {
     // Only run if the window is still active.
+    NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+
+    nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> success(mSuccess);
+    nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error(mError);
+
     WindowTable* activeWindows = MediaManager::Get()->GetActiveWindows();
     if (activeWindows->Get(mWindowID)) {
       // XPConnect is a magical unicorn.
-      mSuccess->OnSuccess(mFile);
+      success->OnSuccess(mFile);
     }
     return NS_OK;
   }
 
 private:
-  nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mSuccess;
+  already_AddRefed<nsIDOMGetUserMediaSuccessCallback> mSuccess;
+  already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
   nsCOMPtr<nsIDOMFile> mFile;
   uint64_t mWindowID;
 };
 
 /**
+ * Invoke the GetUserMediaDevices success callback. Wrapped in a runnable
+ * so that it may be called on the main thread. The error callback is also
+ * passed so it can be released correctly.
+ */
+class DeviceSuccessCallbackRunnable: public nsRunnable
+{
+public:
+  DeviceSuccessCallbackRunnable(
+    already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> aSuccess,
+    already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
+    const nsTArray<nsCOMPtr<nsIMediaDevice> >& aDevices)
+    : mSuccess(aSuccess)
+    , mError(aError)
+    , mDevices(aDevices) {}
+
+  NS_IMETHOD
+  Run()
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+
+    nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> success(mSuccess);
+    nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error(mError);
+
+    nsCOMPtr<nsIWritableVariant> devices =
+      do_CreateInstance("@mozilla.org/variant;1");
+
+    int32_t len = mDevices.Length();
+    if (len == 0) {
+      devices->SetAsEmptyArray();
+      success->OnSuccess(devices);
+      return NS_OK;
+    }
+
+    nsTArray<nsIMediaDevice*> tmp(len);
+    for (int32_t i = 0; i < len; i++) {
+      tmp.AppendElement(mDevices.ElementAt(i));
+    }
+
+    devices->SetAsArray(nsIDataType::VTYPE_INTERFACE,
+                        &NS_GET_IID(nsIMediaDevice),
+                        mDevices.Length(),
+                        const_cast<void*>(
+                          static_cast<const void*>(tmp.Elements())
+                        ));
+
+    success->OnSuccess(devices);
+    return NS_OK;
+  }
+
+private:
+  already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> mSuccess;
+  already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
+  nsTArray<nsCOMPtr<nsIMediaDevice> > mDevices;
+};
+
+/**
+ * nsIMediaDevice implementation.
+ */
+NS_IMPL_THREADSAFE_ISUPPORTS1(MediaDevice, nsIMediaDevice)
+
+NS_IMETHODIMP
+MediaDevice::GetName(nsAString& aName)
+{
+  aName.Assign(mName);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MediaDevice::GetType(nsAString& aType)
+{
+  aType.Assign(mType);
+  return NS_OK;
+}
+
+MediaEngineSource*
+MediaDevice::GetSource()
+{
+  return mSource;
+}
+
+/**
  * Creates a MediaStream, attaches a listener and fires off a success callback
- * to the DOM with the stream.
+ * to the DOM with the stream. We also pass in the error callback so it can
+ * be released correctly.
  *
  * All of this must be done on the main thread!
  */
 class GetUserMediaStreamRunnable : public nsRunnable
 {
 public:
-  GetUserMediaStreamRunnable(nsIDOMGetUserMediaSuccessCallback* aSuccess,
+  GetUserMediaStreamRunnable(
+    already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
+    already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
     MediaEngineSource* aSource, StreamListeners* aListeners,
     uint64_t aWindowID, TrackID aTrackID)
     : mSuccess(aSuccess)
+    , mError(aError)
     , mSource(aSource)
     , mListeners(aListeners)
     , mWindowID(aWindowID)
     , mTrackID(aTrackID) {}
 
   ~GetUserMediaStreamRunnable() {}
 
   NS_IMETHOD
   Run()
   {
+    NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+
     // Create a media stream.
     nsCOMPtr<nsDOMMediaStream> stream = nsDOMMediaStream::CreateInputStream();
 
     nsPIDOMWindow *window = static_cast<nsPIDOMWindow*>
       (nsGlobalWindow::GetInnerWindowWithId(mWindowID));
 
     if (window && window->GetExtantDoc()) {
       stream->CombineWithPrincipal(window->GetExtantDoc()->NodePrincipal());
@@ -125,26 +231,30 @@ public:
     GetUserMediaCallbackMediaStreamListener* listener =
       new GetUserMediaCallbackMediaStreamListener(mSource, stream, mTrackID);
     stream->GetStream()->AddListener(listener);
 
     // No need for locking because we always do this in the main thread.
     mListeners->AppendElement(listener);
 
     // We're in the main thread, so no worries here either.
+    nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> success(mSuccess);
+    nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error(mError);
+
     WindowTable* activeWindows = MediaManager::Get()->GetActiveWindows();
     if (activeWindows->Get(mWindowID)) {
-      mSuccess->OnSuccess(stream);
+      success->OnSuccess(stream);
     }
 
     return NS_OK;
   }
 
 private:
-  nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mSuccess;
+  already_AddRefed<nsIDOMGetUserMediaSuccessCallback> mSuccess;
+  already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
   nsRefPtr<MediaEngineSource> mSource;
   StreamListeners* mListeners;
   uint64_t mWindowID;
   TrackID mTrackID;
 };
 
 /**
  * Runs on a seperate thread and is responsible for enumerating devices.
@@ -153,294 +263,420 @@ private:
  * are sent back to the DOM.
  *
  * Do not run this on the main thread. The success and error callbacks *MUST*
  * be dispatched on the main thread!
  */
 class GetUserMediaRunnable : public nsRunnable
 {
 public:
+  /**
+   * The caller can choose to provide a MediaDevice as the last argument,
+   * if one is not provided, a default device is automatically chosen.
+   */
   GetUserMediaRunnable(bool aAudio, bool aVideo, bool aPicture,
-    nsIDOMGetUserMediaSuccessCallback* aSuccess,
-    nsIDOMGetUserMediaErrorCallback* aError,
+    already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
+    already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
+    StreamListeners* aListeners, uint64_t aWindowID, MediaDevice* aDevice)
+    : mAudio(aAudio)
+    , mVideo(aVideo)
+    , mPicture(aPicture)
+    , mSuccess(aSuccess)
+    , mError(aError)
+    , mListeners(aListeners)
+    , mWindowID(aWindowID)
+    , mDevice(aDevice)
+    , mInited(true) {}
+
+  GetUserMediaRunnable(bool aAudio, bool aVideo, bool aPicture,
+    already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
+    already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
     StreamListeners* aListeners, uint64_t aWindowID)
     : mAudio(aAudio)
     , mVideo(aVideo)
     , mPicture(aPicture)
     , mSuccess(aSuccess)
     , mError(aError)
     , mListeners(aListeners)
-    , mWindowID(aWindowID) {}
+    , mWindowID(aWindowID)
+    , mInited(false) {}
 
   ~GetUserMediaRunnable() {}
 
   // We only support 1 audio and 1 video track for now.
   enum {
     kVideoTrack = 1,
     kAudioTrack = 2
   };
 
   NS_IMETHOD
   Run()
   {
+    NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
+
     mManager = MediaManager::Get();
 
+    // Was a device provided?
+    if (!mInited) {
+      nsresult rv = SelectDevice();
+      if (rv != NS_OK) {
+        return rv;
+      }
+      mInited = true;
+    }
+
     // It is an error if audio or video are requested along with picture.
     if (mPicture && (mAudio || mVideo)) {
       NS_DispatchToMainThread(new ErrorCallbackRunnable(
-        mError, NS_LITERAL_STRING("NOT_SUPPORTED_ERR"), mWindowID
+        mSuccess, mError, NS_LITERAL_STRING("NOT_SUPPORTED_ERR"), mWindowID
       ));
       return NS_OK;
     }
 
-    if (mPicture) {
-      SendPicture();
-      return NS_OK;
-    }
-
     // XXX: Implement merging two streams (See bug 758391).
     if (mAudio && mVideo) {
       NS_DispatchToMainThread(new ErrorCallbackRunnable(
-        mError, NS_LITERAL_STRING("NOT_IMPLEMENTED"), mWindowID
+        mSuccess, mError, NS_LITERAL_STRING("NOT_IMPLEMENTED"), mWindowID
       ));
       return NS_OK;
     }
 
+    if (mPicture) {
+      ProcessGetUserMediaSnapshot(mDevice->GetSource(), 0);
+      return NS_OK;
+    }
+
     if (mVideo) {
-      SendVideo();
+      ProcessGetUserMedia(mDevice->GetSource(), kVideoTrack);
       return NS_OK;
     }
 
     if (mAudio) {
-      SendAudio();
+      ProcessGetUserMedia(mDevice->GetSource(), kAudioTrack);
       return NS_OK;
     }
 
     return NS_OK;
   }
 
+  nsresult
+  SelectDevice()
+  {
+    uint32_t count;
+    if (mPicture || mVideo) {
+      nsTArray<nsRefPtr<MediaEngineVideoSource> > videoSources;
+      mManager->GetBackend()->EnumerateVideoDevices(&videoSources);
+
+      count = videoSources.Length();
+      if (count <= 0) {
+        NS_DispatchToMainThread(new ErrorCallbackRunnable(
+          mSuccess, mError, NS_LITERAL_STRING("NO_DEVICES_FOUND"), mWindowID
+        ));
+        return NS_ERROR_FAILURE;
+      }
+      mDevice = new MediaDevice(videoSources[0]);
+    } else {
+      nsTArray<nsRefPtr<MediaEngineAudioSource> > audioSources;
+      mManager->GetBackend()->EnumerateAudioDevices(&audioSources);
+
+      count = audioSources.Length();
+      if (count <= 0) {
+        NS_DispatchToMainThread(new ErrorCallbackRunnable(
+          mSuccess, mError, NS_LITERAL_STRING("NO_DEVICES_FOUND"), mWindowID
+        ));
+        return NS_ERROR_FAILURE;
+      }
+      mDevice = new MediaDevice(audioSources[0]);
+    }
+
+    return NS_OK;
+  }
+
   /**
    * Allocates a video or audio device and returns a MediaStream via
    * a GetUserMediaStreamRunnable. Runs off the main thread.
    */
   void
   ProcessGetUserMedia(MediaEngineSource* aSource, TrackID aTrackID)
   {
-    /**
-     * Normally we would now get the name & UUID for the device and ask the
-     * user permission. We will do that when we have some UI. Currently,
-     * only the Android {picture:true} backend is functional, which does not
-     * need a permission prompt, as permission is implicit by user action.
-     *
-     * See bug 748835 for progress on the desktop UI.
-     */
     nsresult rv = aSource->Allocate();
     if (NS_FAILED(rv)) {
       NS_DispatchToMainThread(new ErrorCallbackRunnable(
-        mError, NS_LITERAL_STRING("HARDWARE_UNAVAILABLE"), mWindowID
+        mSuccess, mError, NS_LITERAL_STRING("HARDWARE_UNAVAILABLE"), mWindowID
       ));
       return;
     }
 
     NS_DispatchToMainThread(new GetUserMediaStreamRunnable(
-      mSuccess.get(), aSource, mListeners, mWindowID, aTrackID
+      mSuccess, mError, aSource, mListeners, mWindowID, aTrackID
     ));
     return;
   }
 
   /**
    * Allocates a video device, takes a snapshot and returns a DOMFile via
    * a SuccessRunnable or an error via the ErrorRunnable. Off the main thread.
    */
   void
   ProcessGetUserMediaSnapshot(MediaEngineSource* aSource, int aDuration)
   {
     nsresult rv = aSource->Allocate();
     if (NS_FAILED(rv)) {
       NS_DispatchToMainThread(new ErrorCallbackRunnable(
-        mError, NS_LITERAL_STRING("HARDWARE_UNAVAILABLE"), mWindowID
+        mSuccess, mError, NS_LITERAL_STRING("HARDWARE_UNAVAILABLE"), mWindowID
       ));
       return;
     }
 
+    /**
+     * Display picture capture UI here before calling Snapshot() - Bug 748835.
+     */
     nsCOMPtr<nsIDOMFile> file;
     aSource->Snapshot(aDuration, getter_AddRefs(file));
     aSource->Deallocate();
 
     NS_DispatchToMainThread(new SuccessCallbackRunnable(
-      mSuccess, file, mWindowID
+      mSuccess, mError, file, mWindowID
     ));
     return;
   }
 
-  // {picture:true}
-  void
-  SendPicture()
-  {
-    nsTArray<nsRefPtr<MediaEngineVideoSource> > videoSources;
-    mManager->GetBackend()->EnumerateVideoDevices(&videoSources);
-
-    uint32_t count = videoSources.Length();
-    if (!count) {
-      NS_DispatchToMainThread(new ErrorCallbackRunnable(
-        mError, NS_LITERAL_STRING("NO_DEVICES_FOUND"), mWindowID
-      ));
-      return;
-    }
-
-    // We pick the first source as the "default". Work is needed here in the
-    // form of UI to let the user pick a source. (Also true for audio).
-    MediaEngineVideoSource* videoSource = videoSources[0];
-    ProcessGetUserMediaSnapshot(videoSource, 0 /* duration */);
-  }
-
-  // {video:true}
-  void
-  SendVideo()
-  {
-    nsTArray<nsRefPtr<MediaEngineVideoSource> > videoSources;
-    mManager->GetBackend()->EnumerateVideoDevices(&videoSources);
-
-    uint32_t count = videoSources.Length();
-    if (!count) {
-      NS_DispatchToMainThread(new ErrorCallbackRunnable(
-        mError, NS_LITERAL_STRING("NO_DEVICES_FOUND"), mWindowID
-      ));
-      return;
-    }
-
-    MediaEngineVideoSource* videoSource = videoSources[0];
-    ProcessGetUserMedia(videoSource, kVideoTrack);
-  }
-
-  // {audio:true}
-  void
-  SendAudio()
-  {
-    nsTArray<nsRefPtr<MediaEngineAudioSource> > audioSources;
-    mManager->GetBackend()->EnumerateAudioDevices(&audioSources);
-
-    uint32_t count = audioSources.Length();
-    if (!count) {
-      NS_DispatchToMainThread(new ErrorCallbackRunnable(
-        mError, NS_LITERAL_STRING("NO_DEVICES_FOUND"), mWindowID
-      ));
-      return;
-    }
-
-    MediaEngineAudioSource* audioSource = audioSources[0];
-    ProcessGetUserMedia(audioSource, kAudioTrack);
-  }
-
 private:
   bool mAudio;
   bool mVideo;
   bool mPicture;
 
-  nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mSuccess;
-  nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mError;
+  already_AddRefed<nsIDOMGetUserMediaSuccessCallback> mSuccess;
+  already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
   StreamListeners* mListeners;
   uint64_t mWindowID;
+  nsRefPtr<MediaDevice> mDevice;
 
+  bool mInited;
   MediaManager* mManager;
 };
 
+/**
+ * Similar to GetUserMediaRunnable, but used for the chrome-only
+ * GetUserMediaDevices function. Enumerates a list of audio & video devices,
+ * wraps them up in nsIMediaDevice objects and returns it to the success
+ * callback.
+ */
+class GetUserMediaDevicesRunnable : public nsRunnable
+{
+public:
+  GetUserMediaDevicesRunnable(
+    already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> aSuccess,
+    already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError)
+    : mSuccess(aSuccess)
+    , mError(aError) {}
+  ~GetUserMediaDevicesRunnable() {}
+
+  NS_IMETHOD
+  Run()
+  {
+    NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
+
+    uint32_t audioCount, videoCount, total, i;
+    MediaManager* manager = MediaManager::Get();
+
+    nsTArray<nsRefPtr<MediaEngineVideoSource> > videoSources;
+    manager->GetBackend()->EnumerateVideoDevices(&videoSources);
+    videoCount = videoSources.Length();
+
+    nsTArray<nsRefPtr<MediaEngineAudioSource> > audioSources;
+    manager->GetBackend()->EnumerateAudioDevices(&audioSources);
+    audioCount = videoSources.Length();
+
+    total = videoCount + audioCount;
+
+    nsTArray<nsCOMPtr<nsIMediaDevice> > *devices =
+      new nsTArray<nsCOMPtr<nsIMediaDevice> >;
+
+    for (i = 0; i < videoCount; i++) {
+      devices->AppendElement(new MediaDevice(videoSources[i]));
+    }
+    for (i = 0; i < audioCount; i++) {
+      devices->AppendElement(new MediaDevice(audioSources[i]));
+    }
+
+    NS_DispatchToMainThread(new DeviceSuccessCallbackRunnable(
+      mSuccess, mError, *devices
+    ));
+    return NS_OK;
+  }
+
+private:
+  already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> mSuccess;
+  already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
+};
 
 nsRefPtr<MediaManager> MediaManager::sSingleton;
 
-NS_IMPL_ISUPPORTS1(MediaManager, nsIObserver)
+NS_IMPL_THREADSAFE_ISUPPORTS1(MediaManager, nsIObserver)
 
 /**
  * The entry point for this file. A call from Navigator::mozGetUserMedia
  * will end up here. MediaManager is a singleton that is responsible
  * for handling all incoming getUserMedia calls from every window.
  */
 nsresult
-MediaManager::GetUserMedia(nsPIDOMWindow* aWindow, nsIMediaStreamOptions* aParams,
-  nsIDOMGetUserMediaSuccessCallback* onSuccess,
-  nsIDOMGetUserMediaErrorCallback* onError)
+MediaManager::GetUserMedia(bool aPrivileged, nsPIDOMWindow* aWindow,
+  nsIMediaStreamOptions* aParams,
+  nsIDOMGetUserMediaSuccessCallback* aOnSuccess,
+  nsIDOMGetUserMediaErrorCallback* aOnError)
 {
+  NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+
   NS_ENSURE_TRUE(aParams, NS_ERROR_NULL_POINTER);
   NS_ENSURE_TRUE(aWindow, NS_ERROR_NULL_POINTER);
 
+  nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onSuccess(aOnSuccess);
+  nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onError(aOnError);
+
+  /* Get options */
+  nsresult rv;
   bool audio, video, picture;
 
-  nsresult rv = aParams->GetPicture(&picture);
+  rv = aParams->GetPicture(&picture);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = aParams->GetAudio(&audio);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = aParams->GetVideo(&video);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIMediaDevice> device;
+  rv = aParams->GetDevice(getter_AddRefs(device));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // If a device was provided, make sure it support the type of stream requested.
+  if (device) {
+    nsString type;
+    device->GetType(type);
+    if ((picture || video) && !type.EqualsLiteral("video")) {
+      return NS_ERROR_FAILURE;
+    }
+    if (audio && !type.EqualsLiteral("audio")) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  // We only support "front" or "back". TBD: Send to GetUserMediaRunnable.
+  nsString cameraType;
+  rv = aParams->GetCamera(cameraType);
   NS_ENSURE_SUCCESS(rv, rv);
 
   /**
    * If we were asked to get a picture, before getting a snapshot, we check if
    * the calling page is allowed to open a popup. We do this because
    * {picture:true} will open a new "window" to let the user preview or select
    * an image, on Android. The desktop UI for {picture:true} is TBD, at which
    * may point we can decide whether to extend this test there as well.
    */
 #if !defined(MOZ_WEBRTC)
-  if (picture) {
+  if (picture && !aPrivileged) {
     if (aWindow->GetPopupControlState() > openControlled) {
       nsCOMPtr<nsIPopupWindowManager> pm =
         do_GetService(NS_POPUPWINDOWMANAGER_CONTRACTID);
-      if (!pm)
+      if (!pm) {
         return NS_OK;
-
+      }
       uint32_t permission;
       nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
       pm->TestPermission(doc->NodePrincipal(), &permission);
       if ((permission == nsIPopupWindowManager::DENY_POPUP)) {
         nsCOMPtr<nsIDOMDocument> domDoc = aWindow->GetExtantDocument();
         nsGlobalWindow::FirePopupBlockedEvent(
           domDoc, aWindow, nullptr, EmptyString(), EmptyString()
-                                              );
+        );
         return NS_OK;
       }
     }
   }
 #endif
 
-  rv = aParams->GetAudio(&audio);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = aParams->GetVideo(&video);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // We only support "front" or "back". TBD: Send to GetUserMediaRunnable.
-  nsString cameraType;
-  rv = aParams->GetCamera(cameraType);
-  NS_ENSURE_SUCCESS(rv, rv);
+  /**
+   * UI integration point. Check for permission with the user!
+   * No UI for picture:true here, since user permission is implied by the
+   * preview dialog that will be shown by GetUserMediaRunnable in SendPicture.
+   */
+  if (!aPrivileged && !picture) {
+    // To be filled in by code from bug 729522. If permission is denied, call
+    // onError, and do not continue.
+  }
 
   // Store the WindowID in a hash table and mark as active. The entry is removed
   // when this window is closed or navigated away from.
   uint64_t windowID = aWindow->WindowID();
   StreamListeners* listeners = mActiveWindows.Get(windowID);
   if (!listeners) {
     listeners = new StreamListeners;
     mActiveWindows.Put(windowID, listeners);
   }
 
-  // Pass runnables along to GetUserMediaRunnable so it can add the
-  // MediaStreamListener to the runnable list.
-  nsCOMPtr<nsIRunnable> gUMRunnable = new GetUserMediaRunnable(
-    audio, video, picture, onSuccess, onError, listeners, windowID
-  );
+  /**
+   * Pass runnables along to GetUserMediaRunnable so it can add the
+   * MediaStreamListener to the runnable list. The last argument can
+   * optionally be a MediaDevice object, which should provided if one was
+   * selected by the user via the UI, or was provided by privileged code
+   * via the device: attribute via nsIMediaStreamOptions.
+   */
+  nsCOMPtr<nsIRunnable> gUMRunnable;
+  if (device) {
+    gUMRunnable = new GetUserMediaRunnable(
+      audio, video, picture, onSuccess.forget(), onError.forget(), listeners,
+      windowID, static_cast<MediaDevice*>(device.get())
+    );
+  } else {
+    gUMRunnable = new GetUserMediaRunnable(
+      audio, video, picture, onSuccess.forget(), onError.forget(), listeners,
+      windowID
+    );
+  }
 
   if (picture) {
     // ShowFilePickerForMimeType() must run on the Main Thread! (on Android)
     NS_DispatchToMainThread(gUMRunnable);
   } else {
     // Reuse the same thread to save memory.
     if (!mMediaThread) {
       rv = NS_NewThread(getter_AddRefs(mMediaThread));
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     mMediaThread->Dispatch(gUMRunnable, NS_DISPATCH_NORMAL);
   }
   return NS_OK;
 }
 
+nsresult
+MediaManager::GetUserMediaDevices(nsPIDOMWindow* aWindow,
+  nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
+  nsIDOMGetUserMediaErrorCallback* aOnError)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+
+  nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> onSuccess(aOnSuccess);
+  nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onError(aOnError);
+
+  nsCOMPtr<nsIRunnable> gUMDRunnable = new GetUserMediaDevicesRunnable(
+    onSuccess.forget(), onError.forget()
+  );
+
+  nsCOMPtr<nsIThread> deviceThread;
+  nsresult rv = NS_NewThread(getter_AddRefs(deviceThread));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+
+  deviceThread->Dispatch(gUMDRunnable, NS_DISPATCH_NORMAL);
+  return NS_OK;
+}
+
 MediaEngine*
 MediaManager::GetBackend()
 {
   // Plugin backends as appropriate. The default engine also currently
   // includes picture support for Android.
   if (!mBackend) {
 #if defined(MOZ_WEBRTC)
     mBackend = new MediaEngineWebRTC();
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -112,17 +112,43 @@ private:
   nsCOMPtr<nsDOMMediaStream> mStream;
   TrackID mID;
   bool mValid;
 };
 
 typedef nsTArray<nsRefPtr<GetUserMediaCallbackMediaStreamListener> > StreamListeners;
 typedef nsClassHashtable<nsUint64HashKey, StreamListeners> WindowTable;
 
-class MediaManager MOZ_FINAL : public nsIObserver {
+class MediaDevice : public nsIMediaDevice
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIMEDIADEVICE
+
+  MediaDevice(MediaEngineVideoSource* aSource) {
+    mSource = aSource;
+    mType.Assign(NS_LITERAL_STRING("video"));
+    mSource->GetName(mName);
+  };
+  MediaDevice(MediaEngineAudioSource* aSource) {
+    mSource = aSource;
+    mType.Assign(NS_LITERAL_STRING("audio"));
+    mSource->GetName(mName);
+  };
+  virtual ~MediaDevice() {};
+
+  MediaEngineSource* GetSource();
+private:
+  nsString mName;
+  nsString mType;
+  nsRefPtr<MediaEngineSource> mSource;
+};
+
+class MediaManager MOZ_FINAL : public nsIObserver
+{
 public:
   static MediaManager* Get() {
     if (!sSingleton) {
       sSingleton = new MediaManager();
 
       nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
       obs->AddObserver(sSingleton, "xpcom-shutdown", false);
     }
@@ -130,19 +156,23 @@ public:
   }
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
   MediaEngine* GetBackend();
   WindowTable* GetActiveWindows();
 
-  nsresult GetUserMedia(nsPIDOMWindow* aWindow, nsIMediaStreamOptions* aParams,
+  nsresult GetUserMedia(bool aPrivileged, nsPIDOMWindow* aWindow,
+    nsIMediaStreamOptions* aParams,
     nsIDOMGetUserMediaSuccessCallback* onSuccess,
     nsIDOMGetUserMediaErrorCallback* onError);
+  nsresult GetUserMediaDevices(nsPIDOMWindow* aWindow,
+    nsIGetUserMediaDevicesSuccessCallback* onSuccess,
+    nsIDOMGetUserMediaErrorCallback* onError);
   void OnNavigation(uint64_t aWindowID);
 
 private:
   // Make private because we want only one instance of this class
   MediaManager()
   : mBackend(nullptr)
   , mMediaThread(nullptr) {
     mActiveWindows.Init();
--- a/dom/media/nsIDOMNavigatorUserMedia.idl
+++ b/dom/media/nsIDOMNavigatorUserMedia.idl
@@ -1,39 +1,62 @@
 /* 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 "nsIVariant.idl"
 #include "nsIDOMMediaStream.idl"
 
+[scriptable, builtinclass, uuid(6de854f9-acf8-4383-b464-4803631ef309)]
+interface nsIMediaDevice : nsISupports
+{
+  readonly attribute DOMString type;
+  readonly attribute DOMString name;
+};
+
+[scriptable, function, uuid(24544878-d35e-4962-8c5f-fb84e97bdfee)]
+interface nsIGetUserMediaDevicesSuccessCallback : nsISupports
+{
+  void onSuccess(in nsIVariant devices);
+};
+
 [scriptable, function, uuid(f2a144fc-3534-4761-8c5d-989ae720f89a)]
 interface nsIDOMGetUserMediaSuccessCallback : nsISupports
 {
   /*
    * value must be a nsIDOMBlob if picture is true and a
    * nsIDOMMediaStream if either audio or video are true.
    */
   void onSuccess(in nsISupports value);
 };
 
 [scriptable, function, uuid(2614bbcf-85cc-43e5-8740-964f52bdc7ca)]
 interface nsIDOMGetUserMediaErrorCallback : nsISupports
 {
   void onError(in DOMString error);
 };
 
-[scriptable, uuid(8a26205e-e8f7-4372-bd15-97ff982b4c1c)]
+[scriptable, uuid(92a19f9e-9fed-40d1-aeeb-b07fa7f191e8)]
 interface nsIMediaStreamOptions : nsISupports
 {
   readonly attribute boolean audio;
   readonly attribute boolean video;
   readonly attribute boolean picture;
   readonly attribute DOMString camera;
+  readonly attribute nsIMediaDevice device;
 };
 
 [scriptable, uuid(381e0071-0be5-4f6b-ae21-8e3407a37faa)]
 interface nsIDOMNavigatorUserMedia : nsISupports
 {
   void mozGetUserMedia(in nsIMediaStreamOptions params,
     in nsIDOMGetUserMediaSuccessCallback onsuccess,
     in nsIDOMGetUserMediaErrorCallback onerror);
 };
+
+[scriptable, uuid(20e9c794-fdfe-43f4-a81b-ebd9069e0af1)]
+interface nsINavigatorUserMedia : nsISupports
+{
+  void mozGetUserMediaDevices(
+    in nsIGetUserMediaDevicesSuccessCallback onsuccess,
+    in nsIDOMGetUserMediaErrorCallback onerror);
+};
--- a/dom/payment/Payment.js
+++ b/dom/payment/Payment.js
@@ -41,29 +41,48 @@ PaymentContentHelper.prototype = {
     interfaces: [Ci.nsIDOMNavigatorPayment]
   }),
 
   // nsIDOMNavigatorPayment
 
   pay: function pay(aJwts) {
     let request = this.createRequest();
     let requestId = this.getRequestId(request);
+
+    let docShell = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
+                   .getInterface(Ci.nsIWebNavigation)
+                   .QueryInterface(Ci.nsIDocShell);
+    if (!docShell.isActive) {
+      debug("The caller application is a background app. No request " +
+            "will be sent");
+      let runnable = {
+        run: function run() {
+          Services.DOMRequest.fireError(request, "BACKGROUND_APP");
+        }
+      }
+      Services.tm.currentThread.dispatch(runnable,
+                                         Ci.nsIThread.DISPATCH_NORMAL);
+      return request;
+    }
+
     if (!Array.isArray(aJwts)) {
       aJwts = [aJwts];
     }
+
     cpmm.sendAsyncMessage("Payment:Pay", {
       jwts: aJwts,
       requestId: requestId
     });
     return request;
   },
 
   // nsIDOMGlobalPropertyInitializer
 
   init: function(aWindow) {
+    this._window = aWindow;
     this.initHelper(aWindow, PAYMENT_IPC_MSG_NAMES);
     return this.pay.bind(this);
   },
 
   // nsIFrameMessageListener
 
   receiveMessage: function receiveMessage(aMessage) {
     let name = aMessage.name;
--- a/dom/permission/PermissionSettings.js
+++ b/dom/permission/PermissionSettings.js
@@ -11,34 +11,33 @@ else
   debug = function (s) {}
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/PermissionSettings.jsm");
 
-var cpm = Components.classes["@mozilla.org/childprocessmessagemanager;1"].getService(Components.interfaces.nsISyncMessageSender);
+var cpm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsISyncMessageSender);
 
 // PermissionSettings
 
 const PERMISSIONSETTINGS_CONTRACTID = "@mozilla.org/permissionSettings;1";
 const PERMISSIONSETTINGS_CID        = Components.ID("{18390770-02ab-11e2-a21f-0800200c9a66}");
-const nsIDOMPermissionSettings      = Components.interfaces.nsIDOMPermissionSettings;
+const nsIDOMPermissionSettings      = Ci.nsIDOMPermissionSettings;
 
 function PermissionSettings()
 {
   debug("Constructor");
 }
 
-var permissionManager = Components.classes["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
-var secMan = Components.classes["@mozilla.org/scriptsecuritymanager;1"].getService(Components.interfaces.nsIScriptSecurityManager);
-var appsService = Components.classes["@mozilla.org/AppsService;1"].getService(Components.interfaces.nsIAppsService);
+var permissionManager = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
+var secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager);
+var appsService = Cc["@mozilla.org/AppsService;1"].getService(Ci.nsIAppsService);
 
 PermissionSettings.prototype = {
   get: function get(aPermission, aManifestURL, aOrigin, aBrowserFlag) {
     debug("Get called with: " + aPermission + ", " + aManifestURL + ", " + aOrigin + ", " + aBrowserFlag);
     let uri = Services.io.newURI(aOrigin, null, null);
     let appID = appsService.getAppLocalIdByManifestURL(aManifestURL);
     let principal = secMan.getAppCodebasePrincipal(uri, appID, aBrowserFlag);
     let result = permissionManager.testExactPermissionFromPrincipal(principal, aPermission);
--- a/dom/power/PowerManager.cpp
+++ b/dom/power/PowerManager.cpp
@@ -170,32 +170,22 @@ PowerManager::SetCpuSleepAllowed(bool aA
 {
   hal::SetCpuSleepAllowed(aAllowed);
   return NS_OK;
 }
 
 already_AddRefed<PowerManager>
 PowerManager::CheckPermissionAndCreateInstance(nsPIDOMWindow* aWindow)
 {
-  nsPIDOMWindow* innerWindow = aWindow->IsInnerWindow() ?
-    aWindow :
-    aWindow->GetCurrentInnerWindow();
-
-  // Need the document for security check.
-  nsCOMPtr<nsIDocument> document = innerWindow->GetExtantDoc();
-  NS_ENSURE_TRUE(document, nullptr);
-
-  nsCOMPtr<nsIPrincipal> principal = document->NodePrincipal();
-
   nsCOMPtr<nsIPermissionManager> permMgr =
     do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
   NS_ENSURE_TRUE(permMgr, nullptr);
 
   uint32_t permission = nsIPermissionManager::DENY_ACTION;
-  permMgr->TestPermissionFromPrincipal(principal, "power", &permission);
+  permMgr->TestPermissionFromWindow(aWindow, "power", &permission);
 
   if (permission != nsIPermissionManager::ALLOW_ACTION) {
     return nullptr;
   }
 
   nsRefPtr<PowerManager> powerManager = new PowerManager();
   powerManager->Init(aWindow);
 
--- a/dom/sms/src/SmsManager.cpp
+++ b/dom/sms/src/SmsManager.cpp
@@ -64,34 +64,22 @@ SmsManager::CheckPermissionAndCreateInst
 {
   NS_ASSERTION(aWindow, "Null pointer!");
 
   // First of all, the general pref has to be turned on.
   bool enabled = false;
   Preferences::GetBool("dom.sms.enabled", &enabled);
   NS_ENSURE_TRUE(enabled, nullptr);
 
-  nsPIDOMWindow* innerWindow = aWindow->IsInnerWindow() ?
-    aWindow :
-    aWindow->GetCurrentInnerWindow();
-
-  // Need the document for security check.
-  nsCOMPtr<nsIDocument> document =
-    do_QueryInterface(innerWindow->GetExtantDocument());
-  NS_ENSURE_TRUE(document, nullptr);
-
-  nsCOMPtr<nsIPrincipal> principal = document->NodePrincipal();
-  NS_ENSURE_TRUE(principal, nullptr);
-
   nsCOMPtr<nsIPermissionManager> permMgr =
     do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
   NS_ENSURE_TRUE(permMgr, nullptr);
 
   uint32_t permission = nsIPermissionManager::DENY_ACTION;
-  permMgr->TestPermissionFromPrincipal(principal, "sms", &permission);
+  permMgr->TestPermissionFromWindow(aWindow, "sms", &permission);
 
   if (permission != nsIPermissionManager::ALLOW_ACTION) {
     return nullptr;
   }
 
   nsRefPtr<SmsManager> smsMgr = new SmsManager();
   smsMgr->Init(aWindow);
 
--- a/dom/system/gonk/AudioManager.cpp
+++ b/dom/system/gonk/AudioManager.cpp
@@ -28,16 +28,29 @@ using namespace mozilla;
 
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "AudioManager" , ## args) 
 
 #define HEADPHONES_STATUS_CHANGED "headphones-status-changed"
 #define HEADPHONES_STATUS_ON      NS_LITERAL_STRING("on").get()
 #define HEADPHONES_STATUS_OFF     NS_LITERAL_STRING("off").get()
 #define HEADPHONES_STATUS_UNKNOWN NS_LITERAL_STRING("unknown").get()
 
+static bool
+IsFmRadioAudioOn()
+{
+  if (static_cast<
+      audio_policy_dev_state_t (*) (audio_devices_t, const char *)
+      >(AudioSystem::getDeviceConnectionState)) {
+    return AudioSystem::getDeviceConnectionState(AUDIO_DEVICE_OUT_FM, "") == 
+           AUDIO_POLICY_DEVICE_STATE_AVAILABLE ? true : false;
+  } else {
+    return false;
+  }
+}
+
 NS_IMPL_ISUPPORTS1(AudioManager, nsIAudioManager)
 
 static AudioSystem::audio_devices
 GetRoutingMode(int aType) {
   if (aType == nsIAudioManager::FORCE_SPEAKER) {
     return AudioSystem::DEVICE_OUT_SPEAKER;
   } else if (aType == nsIAudioManager::FORCE_HEADPHONES) {
     return AudioSystem::DEVICE_OUT_WIRED_HEADSET;
@@ -128,16 +141,21 @@ AudioManager::SetMasterVolume(float aMas
 {
   if (AudioSystem::setMasterVolume(aMasterVolume)) {
     return NS_ERROR_FAILURE;
   }
   // For now, just set the voice volume at the same level
   if (AudioSystem::setVoiceVolume(aMasterVolume)) {
     return NS_ERROR_FAILURE;
   }
+
+  if (IsFmRadioAudioOn() && AudioSystem::setFmVolume(aMasterVolume)) {
+    return NS_ERROR_FAILURE;
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 AudioManager::GetMasterMuted(bool* aMasterMuted)
 {
   if (AudioSystem::getMasterMute(aMasterMuted)) {
     return NS_ERROR_FAILURE;
@@ -229,10 +247,41 @@ AudioManager::SetAudioRoute(int aRoutes)
     AudioSystem::setParameters(handle, cmd);
   } else if (static_cast<
              status_t (*)(audio_devices_t, audio_policy_dev_state_t, const char*)
              >(AudioSystem::setDeviceConnectionState)) {
     AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_WIRED_HEADSET, 
         GetRoutingMode(aRoutes) == AudioSystem::DEVICE_OUT_WIRED_HEADSET ? 
         AUDIO_POLICY_DEVICE_STATE_AVAILABLE : AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
         "");
+
+    // The audio volume is not consistent when we plug and unplug the headset.
+    // Set the fm volume again here.
+    if (IsFmRadioAudioOn()) {
+      float masterVolume;
+      AudioSystem::getMasterVolume(&masterVolume);
+      AudioSystem::setFmVolume(masterVolume);
+    }
   }
 }
+
+NS_IMETHODIMP
+AudioManager::GetFmRadioAudioEnabled(bool *aFmRadioAudioEnabled)
+{
+  *aFmRadioAudioEnabled = IsFmRadioAudioOn();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AudioManager::SetFmRadioAudioEnabled(bool aFmRadioAudioEnabled)
+{
+  if (static_cast<
+      status_t (*) (AudioSystem::audio_devices, AudioSystem::device_connection_state, const char *)
+      >(AudioSystem::setDeviceConnectionState)) {
+    AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_FM,
+      aFmRadioAudioEnabled ? AUDIO_POLICY_DEVICE_STATE_AVAILABLE : 
+      AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, "");
+    InternalSetAudioRoutes(GetCurrentSwitchState(SWITCH_HEADPHONES));
+    return NS_OK;
+  } else {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+}
--- a/dom/system/gonk/AudioManager.h
+++ b/dom/system/gonk/AudioManager.h
@@ -40,16 +40,17 @@ class AudioManager : public nsIAudioMana
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIAUDIOMANAGER
 
   AudioManager();
   ~AudioManager();
 
   static void SetAudioRoute(int aRoutes);
+
 protected:
   int32_t mPhoneState;
 
 private:
   nsAutoPtr<mozilla::hal::SwitchObserver> mObserver;
 };
 
 } /* namespace gonk */
--- a/dom/system/gonk/nsIAudioManager.idl
+++ b/dom/system/gonk/nsIAudioManager.idl
@@ -18,16 +18,21 @@ interface nsIAudioManager : nsISupports
   attribute float masterVolume;
 
   /**
    * Master volume muted?
    */
   attribute boolean masterMuted;
 
   /**
+   * Are we playing audio from the FM radio?
+   */
+  attribute boolean fmRadioAudioEnabled;
+ 
+  /**
    * Set the phone's audio mode.
    */
   const long PHONE_STATE_INVALID          = -2;
   const long PHONE_STATE_CURRENT          = -1;
   const long PHONE_STATE_NORMAL           = 0;
   const long PHONE_STATE_RINGTONE         = 1;
   const long PHONE_STATE_IN_CALL          = 2;
   const long PHONE_STATE_IN_COMMUNICATION = 3;
--- a/dom/telephony/Telephony.cpp
+++ b/dom/telephony/Telephony.cpp
@@ -531,31 +531,23 @@ nsresult
 NS_NewTelephony(nsPIDOMWindow* aWindow, nsIDOMTelephony** aTelephony)
 {
   NS_ASSERTION(aWindow, "Null pointer!");
 
   nsPIDOMWindow* innerWindow = aWindow->IsInnerWindow() ?
     aWindow :
     aWindow->GetCurrentInnerWindow();
 
-  // Need the document for security check.
-  nsCOMPtr<nsIDocument> document =
-    do_QueryInterface(innerWindow->GetExtantDocument());
-  NS_ENSURE_TRUE(document, NS_NOINTERFACE);
-
-  nsCOMPtr<nsIPrincipal> principal = document->NodePrincipal();
-  NS_ENSURE_TRUE(principal, NS_ERROR_UNEXPECTED);
-
   nsCOMPtr<nsIPermissionManager> permMgr =
     do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
   NS_ENSURE_TRUE(permMgr, NS_ERROR_UNEXPECTED);
 
   uint32_t permission;
   nsresult rv =
-    permMgr->TestPermissionFromPrincipal(principal, "telephony", &permission);
+    permMgr->TestPermissionFromWindow(aWindow, "telephony", &permission);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (permission != nsIPermissionManager::ALLOW_ACTION) {
     *aTelephony = nullptr;
     return NS_OK;
   }
 
   nsCOMPtr<nsIRILContentHelper> ril =
new file mode 100644
--- /dev/null
+++ b/dom/webidl/AudioBufferSourceNode.webidl
@@ -0,0 +1,35 @@
+/* -*- Mode: IDL; 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/.
+ *
+ * The origin of this IDL file is
+ * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html
+ *
+ * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+[PrefControlled]
+interface AudioBufferSourceNode : AudioSourceNode {
+
+    const unsigned short UNSCHEDULED_STATE = 0;
+    const unsigned short SCHEDULED_STATE = 1;
+    const unsigned short PLAYING_STATE = 2;
+    const unsigned short FINISHED_STATE = 3;
+
+    //readonly attribute unsigned short playbackState;
+
+    // Playback this in-memory audio asset  
+    // Many sources can share the same buffer  
+    //attribute AudioBuffer buffer;
+
+    //attribute AudioParam playbackRate;
+    //attribute boolean loop;
+
+    void noteOn(double when);
+    //void noteGrainOn(double when, double grainOffset, double grainDuration);
+    void noteOff(double when);
+
+};
+
--- a/dom/webidl/AudioContext.webidl
+++ b/dom/webidl/AudioContext.webidl
@@ -7,10 +7,18 @@
  * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 [Constructor, PrefControlled]
 interface mozAudioContext {
+
+    readonly attribute AudioDestinationNode destination;
+
+    // AudioNode creation 
+    AudioBufferSourceNode createBufferSource();
+
 };
 
+typedef mozAudioContext AudioContext;
+
new file mode 100644
--- /dev/null
+++ b/dom/webidl/AudioDestinationNode.webidl
@@ -0,0 +1,20 @@
+/* -*- Mode: IDL; 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/.
+ *
+ * The origin of this IDL file is
+ * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html
+ *
+ * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+[PrefControlled]
+interface AudioDestinationNode : AudioNode {
+
+    //readonly attribute unsigned long maxNumberOfChannels;
+    //attribute unsigned long numberOfChannels;
+
+};
+
new file mode 100644
--- /dev/null
+++ b/dom/webidl/AudioNode.webidl
@@ -0,0 +1,27 @@
+/* -*- Mode: IDL; 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/.
+ *
+ * The origin of this IDL file is
+ * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html
+ *
+ * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+[PrefControlled]
+interface AudioNode {
+
+    void connect(AudioNode destination, optional unsigned long output = 0, optional unsigned long input = 0);
+
+    //void connect(AudioParam destination, optional unsigned long output = 0);
+
+    void disconnect(optional unsigned long output = 0);
+
+    readonly attribute AudioContext context;
+    //readonly attribute unsigned long numberOfInputs;
+    //readonly attribute unsigned long numberOfOutputs;
+
+};
+
new file mode 100644
--- /dev/null
+++ b/dom/webidl/AudioSourceNode.webidl
@@ -0,0 +1,17 @@
+/* -*- Mode: IDL; 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/.
+ *
+ * The origin of this IDL file is
+ * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html
+ *
+ * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+[PrefControlled]
+interface AudioSourceNode : AudioNode {
+
+};
+
new file mode 100644
--- /dev/null
+++ b/dom/webidl/FileReaderSync.webidl
@@ -0,0 +1,28 @@
+/* -*- Mode: IDL; 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/.
+ *
+ * The origin of this IDL file is
+ * http://dev.w3.org/2006/webapi/FileAPI/
+ *
+ * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+interface Blob;
+
+[Constructor]
+interface FileReaderSync {
+
+  // Synchronously return strings
+
+  [Throws]
+  ArrayBuffer readAsArrayBuffer(Blob blob);
+  [Throws]
+  DOMString readAsBinaryString(Blob blob);
+  [Throws]
+  DOMString readAsText(Blob blob, optional DOMString encoding);
+  [Throws]
+  DOMString readAsDataURL(Blob blob);
+};
--- a/dom/webidl/WebIDL.mk
+++ b/dom/webidl/WebIDL.mk
@@ -4,27 +4,32 @@
 
 webidl_base = $(topsrcdir)/dom/webidl
 
 generated_webidl_files = \
   CSS2Properties.webidl \
   $(NULL)
 
 webidl_files = \
+  AudioBufferSourceNode.webidl \
   AudioContext.webidl \
+  AudioDestinationNode.webidl \
+  AudioNode.webidl \
+  AudioSourceNode.webidl \
   Blob.webidl \
   CanvasRenderingContext2D.webidl \
   ClientRectList.webidl \
   CSSStyleDeclaration.webidl \
   DOMTokenList.webidl \
   DOMSettableTokenList.webidl \
   Function.webidl \
   EventListener.webidl \
   EventTarget.webidl \
   FileList.webidl \
+  FileReaderSync.webidl \
   HTMLCollection.webidl \
   HTMLOptionsCollection.webidl \
   HTMLPropertiesCollection.webidl \
   NodeList.webidl \
   PaintRequestList.webidl \
   Performance.webidl \
   PerformanceNavigation.webidl \
   PerformanceTiming.webidl \
--- a/dom/workers/DOMBindingBase.cpp
+++ b/dom/workers/DOMBindingBase.cpp
@@ -24,16 +24,23 @@ DOMBindingBase::DOMBindingBase(JSContext
 
 DOMBindingBase::~DOMBindingBase()
 {
   if (!mJSContext) {
     AssertIsOnMainThread();
   }
 }
 
+NS_IMPL_ADDREF(DOMBindingBase)
+NS_IMPL_RELEASE(DOMBindingBase)
+NS_INTERFACE_MAP_BEGIN(DOMBindingBase)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+NS_INTERFACE_MAP_END
+
 void
 DOMBindingBase::_trace(JSTracer* aTrc)
 {
   JSObject* obj = GetJSObject();
   if (obj) {
     JS_CALL_OBJECT_TRACER(aTrc, obj, "cached wrapper");
   }
 }
--- a/dom/workers/DOMBindingBase.h
+++ b/dom/workers/DOMBindingBase.h
@@ -22,17 +22,18 @@ BEGIN_WORKERS_NAMESPACE
       aRv = _result; \
       return _retval; \
     } \
   PR_END_MACRO
 
 #define BINDING_ENSURE_SUCCESS(_cond, _result, _retval) \
   BINDING_ENSURE_TRUE(NS_SUCCEEDED(_cond), _result, _retval)
 
-class DOMBindingBase : public nsWrapperCache
+class DOMBindingBase : public nsWrapperCache,
+                       public nsISupports
 {
   JSContext* mJSContext;
   mutable nsCOMPtr<nsIThreadJSContextStack> mContextStack;
 
 protected:
   DOMBindingBase(JSContext* aCx);
   virtual ~DOMBindingBase();
 
@@ -41,17 +42,17 @@ protected:
 
   virtual void
   _finalize(JSFreeOp* aFop);
 
   JSContext*
   GetJSContextFromContextStack() const;
 
 public:
-  NS_INLINE_DECL_REFCOUNTING(DOMBindingBase)
+  NS_DECL_ISUPPORTS
 
   JSContext*
   GetJSContext() const
   {
     return mJSContext ? mJSContext : GetJSContextFromContextStack();
   }
 
 #ifdef DEBUG
--- a/dom/workers/DOMBindingInlines.h
+++ b/dom/workers/DOMBindingInlines.h
@@ -1,21 +1,23 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 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_workers_dombindinginlines_h__
 #define mozilla_dom_workers_dombindinginlines_h__
 
+#include "mozilla/dom/FileReaderSyncBinding.h"
 #include "mozilla/dom/XMLHttpRequestBinding.h"
 #include "mozilla/dom/XMLHttpRequestUploadBinding.h"
 
 BEGIN_WORKERS_NAMESPACE
 
+class FileReaderSync;
 class XMLHttpRequest;
 class XMLHttpRequestUpload;
 
 namespace {
 
 template <class T>
 struct WrapPrototypeTraits
 { };
@@ -36,16 +38,17 @@ struct WrapPrototypeTraits
     static inline JSObject*                                                    \
     GetProtoObject(JSContext* aCx, JSObject* aGlobal)                          \
     {                                                                          \
       using namespace mozilla::dom;                                            \
       return _class##Binding_workers::GetProtoObject(aCx, aGlobal, aGlobal);   \
     }                                                                          \
   };
 
+SPECIALIZE_PROTO_TRAITS(FileReaderSync)
 SPECIALIZE_PROTO_TRAITS(XMLHttpRequest)
 SPECIALIZE_PROTO_TRAITS(XMLHttpRequestUpload)
 
 #undef SPECIALIZE_PROTO_TRAITS
 
 } // anonymous namespace
 
 template <class T>
--- a/dom/workers/FileReaderSync.cpp
+++ b/dom/workers/FileReaderSync.cpp
@@ -1,336 +1,397 @@
-/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "FileReaderSync.h"
 
-#include "nsIDOMFile.h"
+#include "nsCExternalHandlerService.h"
+#include "nsComponentManagerUtils.h"
+#include "nsCOMPtr.h"
+#include "nsDOMClassInfoID.h"
 #include "nsError.h"
-
-#include "jsapi.h"
-#include "jsfriendapi.h"
-#include "nsJSUtils.h"
+#include "nsIDOMFile.h"
+#include "nsCharsetAlias.h"
+#include "nsICharsetDetector.h"
+#include "nsIConverterInputStream.h"
+#include "nsIInputStream.h"
+#include "nsIPlatformCharset.h"
+#include "nsISeekableStream.h"
+#include "nsISupportsImpl.h"
+#include "nsISupportsImpl.h"
+#include "nsNetUtil.h"
+#include "nsServiceManagerUtils.h"
+#include "File.h"
+#include "RuntimeService.h"
+#include "DOMBindingInlines.h"
 
-#include "Exceptions.h"
-#include "File.h"
-#include "FileReaderSyncPrivate.h"
-#include "WorkerInlines.h"
-
-#define FUNCTION_FLAGS \
-  JSPROP_ENUMERATE
+#include "mozilla/Base64.h"
 
 USING_WORKERS_NAMESPACE
+using mozilla::ErrorResult;
+using mozilla::dom::Optional;
 
-using mozilla::dom::workers::exceptions::ThrowDOMExceptionForNSResult;
+NS_IMPL_ADDREF_INHERITED(FileReaderSync, DOMBindingBase)
+NS_IMPL_RELEASE_INHERITED(FileReaderSync, DOMBindingBase)
+NS_INTERFACE_MAP_BEGIN(FileReaderSync)
+  NS_INTERFACE_MAP_ENTRY(nsICharsetDetectionObserver)
+NS_INTERFACE_MAP_END_INHERITING(DOMBindingBase)
+
+FileReaderSync::FileReaderSync(JSContext* aCx)
+  : DOMBindingBase(aCx)
+{
+}
+
+void
+FileReaderSync::_trace(JSTracer* aTrc)
+{
+  DOMBindingBase::_trace(aTrc);
+}
+
+void
+FileReaderSync::_finalize(JSFreeOp* aFop)
+{
+  DOMBindingBase::_finalize(aFop);
+}
 
-namespace {
+// static
+FileReaderSync*
+FileReaderSync::Constructor(JSContext* aCx, JSObject* aGlobal,
+                            ErrorResult& aRv)
+{
+  nsRefPtr<FileReaderSync> frs = new FileReaderSync(aCx);
+
+  if (!Wrap(aCx, aGlobal, frs)) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  return frs;
+}
+
+JSObject*
+FileReaderSync::ReadAsArrayBuffer(JSContext* aCx, JSObject* aBlob,
+                                  ErrorResult& aRv)
+{
+  nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aBlob);
+  if (!blob) {
+    aRv.Throw(NS_ERROR_INVALID_ARG);
+    return nullptr;
+  }
 
-inline bool
-EnsureSucceededOrThrow(JSContext* aCx, nsresult rv)
+  uint64_t blobSize;
+  nsresult rv = blob->GetSize(&blobSize);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return nullptr;
+  }
+
+  JSObject* jsArrayBuffer = JS_NewArrayBuffer(aCx, blobSize);
+  if (!jsArrayBuffer) {
+    // XXXkhuey we need a way to indicate to the bindings that the call failed
+    // but there's already a pending exception that we should not clobber.
+    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+    return nullptr;
+  }
+
+  uint32_t bufferLength = JS_GetArrayBufferByteLength(jsArrayBuffer, aCx);
+  uint8_t* arrayBuffer = JS_GetArrayBufferData(jsArrayBuffer, aCx);
+
+  nsCOMPtr<nsIInputStream> stream;
+  rv = blob->GetInternalStream(getter_AddRefs(stream));
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return nullptr;
+  }
+
+  uint32_t numRead;
+  rv = stream->Read((char*)arrayBuffer, bufferLength, &numRead);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return nullptr;
+  }
+  NS_ASSERTION(numRead == bufferLength, "failed to read data");
+
+  return jsArrayBuffer;
+}
+
+void
+FileReaderSync::ReadAsBinaryString(JSObject* aBlob, nsAString& aResult,
+                                   ErrorResult& aRv)
 {
-  if (NS_SUCCEEDED(rv)) {
-    return true;
+  nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aBlob);
+  if (!blob) {
+    aRv.Throw(NS_ERROR_INVALID_ARG);
+    return;
+  }
+
+  nsCOMPtr<nsIInputStream> stream;
+  nsresult rv = blob->GetInternalStream(getter_AddRefs(stream));
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return;
   }
 
-  rv = rv == NS_ERROR_FILE_NOT_FOUND ?
-              NS_ERROR_DOM_FILE_NOT_FOUND_ERR :
-              NS_ERROR_DOM_FILE_NOT_READABLE_ERR;
-  ThrowDOMExceptionForNSResult(aCx, rv);
-  return false;
+  uint32_t numRead;
+  do {
+    char readBuf[4096];
+    rv = stream->Read(readBuf, sizeof(readBuf), &numRead);
+    if (NS_FAILED(rv)) {
+      aRv.Throw(rv);
+      return;
+    }
+
+    uint32_t oldLength = aResult.Length();
+    AppendASCIItoUTF16(Substring(readBuf, readBuf + numRead), aResult);
+    if (aResult.Length() - oldLength != numRead) {
+      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+      return;
+    }
+  } while (numRead > 0);
+}
+
+void
+FileReaderSync::ReadAsText(JSObject* aBlob,
+                           const Optional<nsAString>& aEncoding,
+                           nsAString& aResult,
+                           ErrorResult& aRv)
+{
+  nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aBlob);
+  if (!blob) {
+    aRv.Throw(NS_ERROR_INVALID_ARG);
+    return;
+  }
+
+  nsCOMPtr<nsIInputStream> stream;
+  nsresult rv = blob->GetInternalStream(getter_AddRefs(stream));
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return;
+  }
+
+  nsCString charsetGuess;
+  if (!aEncoding.WasPassed() || aEncoding.Value().IsEmpty()) {
+    rv = GuessCharset(stream, charsetGuess);
+    if (NS_FAILED(rv)) {
+      aRv.Throw(rv);
+      return;
+    }
+
+    nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(stream);
+    if (!seekable) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return;
+    }
+
+    // Seek to 0 because guessing the charset advances the stream.
+    rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
+    if (NS_FAILED(rv)) {
+      aRv.Throw(rv);
+      return;
+    }
+  } else {
+    CopyUTF16toUTF8(aEncoding.Value(), charsetGuess);
+  }
+
+  nsCString charset;
+  rv = nsCharsetAlias::GetPreferred(charsetGuess, charset);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return;
+  }
+
+  rv = ConvertStream(stream, charset.get(), aResult);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return;
+  }
 }
 
-inline nsIDOMBlob*
-GetDOMBlobFromJSObject(JSContext* aCx, JSObject* aObj) {
-  // aObj can be null as JS_ConvertArguments("o") successfully converts JS
-  // null to a null pointer to JSObject 
-  if (aObj) {
-    nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aObj);
-    if (blob) {
-      return blob;
+void
+FileReaderSync::ReadAsDataURL(JSObject* aBlob, nsAString& aResult,
+                              ErrorResult& aRv)
+{
+  nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aBlob);
+  if (!blob) {
+    aRv.Throw(NS_ERROR_INVALID_ARG);
+    return;
+  }
+
+  nsAutoString scratchResult;
+  scratchResult.AssignLiteral("data:");
+
+  nsString contentType;
+  blob->GetType(contentType);
+
+  if (contentType.IsEmpty()) {
+    scratchResult.AppendLiteral("application/octet-stream");
+  } else {
+    scratchResult.Append(contentType);
+  }
+  scratchResult.AppendLiteral(";base64,");
+
+  nsCOMPtr<nsIInputStream> stream;
+  nsresult rv = blob->GetInternalStream(getter_AddRefs(stream));
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return;
+  }
+
+  uint64_t size;
+  rv = blob->GetSize(&size);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return;
+  }
+
+  nsCOMPtr<nsIInputStream> bufferedStream;
+  rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream, size);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return;
+  }
+
+  nsAutoString encodedData;
+  rv = Base64EncodeInputStream(bufferedStream, encodedData, size);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return;
+  }
+
+  scratchResult.Append(encodedData);
+
+  aResult = scratchResult;
+}
+
+nsresult
+FileReaderSync::ConvertStream(nsIInputStream *aStream,
+                              const char *aCharset,
+                              nsAString &aResult)
+{
+  nsCOMPtr<nsIConverterInputStream> converterStream =
+    do_CreateInstance("@mozilla.org/intl/converter-input-stream;1");
+  NS_ENSURE_TRUE(converterStream, NS_ERROR_FAILURE);
+
+  nsresult rv = converterStream->Init(aStream, aCharset, 8192,
+                  nsIConverterInputStream::DEFAULT_REPLACEMENT_CHARACTER);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIUnicharInputStream> unicharStream =
+    do_QueryInterface(converterStream);
+  NS_ENSURE_TRUE(unicharStream, NS_ERROR_FAILURE);
+
+  uint32_t numChars;
+  nsString result;
+  while (NS_SUCCEEDED(unicharStream->ReadString(8192, result, &numChars)) &&
+         numChars > 0) {
+    uint32_t oldLength = aResult.Length();
+    aResult.Append(result);
+    if (aResult.Length() - oldLength != result.Length()) {
+      return NS_ERROR_OUT_OF_MEMORY;
     }
   }
 
-  JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
-                       aObj ? JS_GetClass(aObj)->name : "Object", "not a Blob.");
-  return NULL;
+  return rv;
 }
 
-class FileReaderSync
+nsresult
+FileReaderSync::GuessCharset(nsIInputStream *aStream, nsACString &aCharset)
 {
-  // FileReaderSync should not be instantiated.
-  FileReaderSync();
-  ~FileReaderSync();
-
-  static JSClass sClass;
-  static JSFunctionSpec sFunctions[];
-
-public:
-  static JSObject*
-  InitClass(JSContext* aCx, JSObject* aObj)
-  {
-    return JS_InitClass(aCx, aObj, NULL, &sClass, Construct, 0,
-                        NULL, sFunctions, NULL, NULL);
-  }
-
-  static FileReaderSyncPrivate*
-  GetPrivate(JSObject* aObj)
-  {
-    if (aObj) {
-      JSClass* classPtr = JS_GetClass(aObj);
-      if (classPtr == &sClass) {
-        FileReaderSyncPrivate* fileReader =
-          GetJSPrivateSafeish<FileReaderSyncPrivate>(aObj);
-        return fileReader;
-      }
-    }
-    return NULL;
-  }
-
-private:
-  static FileReaderSyncPrivate*
-  GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName)
-  {
-    FileReaderSyncPrivate* fileReader = GetPrivate(aObj);
-    if (fileReader) {
-      return fileReader;
-    }
-
-    JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL,
-                         JSMSG_INCOMPATIBLE_PROTO, sClass.name, aFunctionName,
-                         JS_GetClass(aObj)->name);
-    return NULL;
-  }
-
-  static JSBool
-  Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
-  {
-    JSObject* obj = JS_NewObject(aCx, &sClass, NULL, NULL);
-    if (!obj) {
-      return false;
-    }
-
-    FileReaderSyncPrivate* fileReader = new FileReaderSyncPrivate();
-    NS_ADDREF(fileReader);
-
-    SetJSPrivateSafeish(obj, fileReader);
+  // First try the universal charset detector
+  nsCOMPtr<nsICharsetDetector> detector
+    = do_CreateInstance(NS_CHARSET_DETECTOR_CONTRACTID_BASE
+                        "universal_charset_detector");
+  if (!detector) {
+    RuntimeService* runtime = RuntimeService::GetService();
+    NS_ASSERTION(runtime, "This should never be null!");
 
-    JS_SET_RVAL(aCx, aVp, OBJECT_TO_JSVAL(obj));
-    return true;
-  }
-
-  static void
-  Finalize(JSFreeOp* aFop, JSObject* aObj)
-  {
-    JS_ASSERT(JS_GetClass(aObj) == &sClass);
-    FileReaderSyncPrivate* fileReader =
-      GetJSPrivateSafeish<FileReaderSyncPrivate>(aObj);
-    NS_IF_RELEASE(fileReader);
-  }
-
-  static JSBool
-  ReadAsArrayBuffer(JSContext* aCx, unsigned aArgc, jsval* aVp)
-  {
-    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
-    if (!obj) {
-      return false;
-    }
-
-    FileReaderSyncPrivate* fileReader =
-      GetInstancePrivate(aCx, obj, "readAsArrayBuffer");
-    if (!fileReader) {
-      return false;
-    }
+    // No universal charset detector, try the default charset detector
+    const nsACString& detectorName = runtime->GetDetectorName();
 
-    JSObject* jsBlob;
-    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "o", &jsBlob)) {
-      return false;
-    }
-
-    nsIDOMBlob* blob = GetDOMBlobFromJSObject(aCx, jsBlob);
-    if (!blob) {
-      return false;
-    }
-
-    uint64_t blobSize;
-    nsresult rv = blob->GetSize(&blobSize);
-    if (!EnsureSucceededOrThrow(aCx, rv)) {
-      return false;
+    if (!detectorName.IsEmpty()) {
+      nsAutoCString detectorContractID;
+      detectorContractID.AssignLiteral(NS_CHARSET_DETECTOR_CONTRACTID_BASE);
+      detectorContractID += detectorName;
+      detector = do_CreateInstance(detectorContractID.get());
     }
-
-    JSObject* jsArrayBuffer = JS_NewArrayBuffer(aCx, blobSize);
-    if (!jsArrayBuffer) {
-      return false;
-    }
-
-    uint32_t bufferLength = JS_GetArrayBufferByteLength(jsArrayBuffer, aCx);
-    uint8_t* arrayBuffer = JS_GetArrayBufferData(jsArrayBuffer, aCx);
-
-    rv = fileReader->ReadAsArrayBuffer(blob, bufferLength, arrayBuffer);
-    if (!EnsureSucceededOrThrow(aCx, rv)) {
-      return false;
-    }
-
-    JS_SET_RVAL(aCx, aVp, OBJECT_TO_JSVAL(jsArrayBuffer));
-    return true;
   }
 
-  static JSBool
-  ReadAsDataURL(JSContext* aCx, unsigned aArgc, jsval* aVp)
-  {
-    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
-    if (!obj) {
-      return false;
-    }
+  nsresult rv;
+  if (detector) {
+    detector->Init(this);
 
-    FileReaderSyncPrivate* fileReader =
-      GetInstancePrivate(aCx, obj, "readAsDataURL");
-    if (!fileReader) {
-      return false;
-    }
+    bool done;
+    uint32_t numRead;
+    do {
+      char readBuf[4096];
+      rv = aStream->Read(readBuf, sizeof(readBuf), &numRead);
+      NS_ENSURE_SUCCESS(rv, rv);
+      if (numRead <= 0) {
+        break;
+      }
+      rv = detector->DoIt(readBuf, numRead, &done);
+      NS_ENSURE_SUCCESS(rv, rv);
+    } while (!done);
 
-    JSObject* jsBlob;
-    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "o", &jsBlob)) {
-      return false;
-    }
+    rv = detector->Done();
+    NS_ENSURE_SUCCESS(rv, rv);
+  } else {
+    // no charset detector available, check the BOM
+    unsigned char sniffBuf[4];
+    uint32_t numRead;
+    rv = aStream->Read(reinterpret_cast<char*>(sniffBuf),
+                       sizeof(sniffBuf), &numRead);
+    NS_ENSURE_SUCCESS(rv, rv);
 
-    nsIDOMBlob* blob = GetDOMBlobFromJSObject(aCx, jsBlob);
-    if (!blob) {
-      return false;
-    }
-
-    nsString blobText;
-    nsresult rv = fileReader->ReadAsDataURL(blob, blobText);
-    if (!EnsureSucceededOrThrow(aCx, rv)) {
-      return false;
+    if (numRead >= 4 &&
+        sniffBuf[0] == 0x00 &&
+        sniffBuf[1] == 0x00 &&
+        sniffBuf[2] == 0xfe &&
+        sniffBuf[3] == 0xff) {
+      mCharset = "UTF-32BE";
+    } else if (numRead >= 4 &&
+               sniffBuf[0] == 0xff &&
+               sniffBuf[1] == 0xfe &&
+               sniffBuf[2] == 0x00 &&
+               sniffBuf[3] == 0x00) {
+      mCharset = "UTF-32LE";
+    } else if (numRead >= 2 &&
+               sniffBuf[0] == 0xfe &&
+               sniffBuf[1] == 0xff) {
+      mCharset = "UTF-16BE";
+    } else if (numRead >= 2 &&
+               sniffBuf[0] == 0xff &&
+               sniffBuf[1] == 0xfe) {
+      mCharset = "UTF-16LE";
+    } else if (numRead >= 3 &&
+               sniffBuf[0] == 0xef &&
+               sniffBuf[1] == 0xbb &&
+               sniffBuf[2] == 0xbf) {
+      mCharset = "UTF-8";
     }
-
-    JSString* jsBlobText = JS_NewUCStringCopyN(aCx, blobText.get(),
-                                               blobText.Length());
-    if (!jsBlobText) {
-      return false;
-    }
-
-    JS_SET_RVAL(aCx, aVp, STRING_TO_JSVAL(jsBlobText));
-    return true;
   }
 
-  static JSBool
-  ReadAsBinaryString(JSContext* aCx, unsigned aArgc, jsval* aVp)
-  {
-    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
-    if (!obj) {
-      return false;
-    }
-
-    FileReaderSyncPrivate* fileReader =
-      GetInstancePrivate(aCx, obj, "readAsBinaryString");
-    if (!fileReader) {
-      return false;
-    }
-
-    JSObject* jsBlob;
-    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "o", &jsBlob)) {
-      return false;
-    }
+  if (mCharset.IsEmpty()) {
+    RuntimeService* runtime = RuntimeService::GetService();
+    mCharset = runtime->GetSystemCharset();
+  }
 
-    nsIDOMBlob* blob = GetDOMBlobFromJSObject(aCx, jsBlob);
-    if (!blob) {
-      return false;
-    }
-
-    nsString blobText;
-    nsresult rv = fileReader->ReadAsBinaryString(blob, blobText);
-    if (!EnsureSucceededOrThrow(aCx, rv)) {
-      return false;
-    }
-
-    JSString* jsBlobText = JS_NewUCStringCopyN(aCx, blobText.get(),
-                                               blobText.Length());
-    if (!jsBlobText) {
-      return false;
-    }
-
-    JS_SET_RVAL(aCx, aVp, STRING_TO_JSVAL(jsBlobText));
-    return true;
+  if (mCharset.IsEmpty()) {
+    // no sniffed or default charset, try UTF-8
+    mCharset.AssignLiteral("UTF-8");
   }
 
-  static JSBool
-  ReadAsText(JSContext* aCx, unsigned aArgc, jsval* aVp)
-  {
-    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
-    if (!obj) {
-      return false;
-    }
-
-    FileReaderSyncPrivate* fileReader =
-      GetInstancePrivate(aCx, obj, "readAsText");
-    if (!fileReader) {
-      return false;
-    }
-
-    JSObject* jsBlob;
-    JSString* jsEncoding = JS_GetEmptyString(JS_GetRuntime(aCx));
-    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "o/S", &jsBlob,
-                             &jsEncoding)) {
-      return false;
-    }
-
-    nsDependentJSString encoding;
-    if (!encoding.init(aCx, jsEncoding)) {
-      return false;
-    }
-
-    nsIDOMBlob* blob = GetDOMBlobFromJSObject(aCx, jsBlob);
-    if (!blob) {
-      return false;
-    }
-
-    nsString blobText;
-    nsresult rv = fileReader->ReadAsText(blob, encoding, blobText);
-    if (!EnsureSucceededOrThrow(aCx, rv)) {
-      return false;
-    }
+  aCharset = mCharset;
+  mCharset.Truncate();
 
-    JSString* jsBlobText = JS_NewUCStringCopyN(aCx, blobText.get(),
-                                               blobText.Length());
-    if (!jsBlobText) {
-      return false;
-    }
-
-    JS_SET_RVAL(aCx, aVp, STRING_TO_JSVAL(jsBlobText));
-    return true;
-  }
-};
-
-JSClass FileReaderSync::sClass = {
-  "FileReaderSync",
-  JSCLASS_HAS_PRIVATE,
-  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
-  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize
-};
-
-JSFunctionSpec FileReaderSync::sFunctions[] = {
-  JS_FN("readAsArrayBuffer", ReadAsArrayBuffer, 1, FUNCTION_FLAGS),
-  JS_FN("readAsBinaryString", ReadAsBinaryString, 1, FUNCTION_FLAGS),
-  JS_FN("readAsText", ReadAsText, 1, FUNCTION_FLAGS),
-  JS_FN("readAsDataURL", ReadAsDataURL, 1, FUNCTION_FLAGS),
-  JS_FS_END
-};
-
-} // anonymous namespace
-
-BEGIN_WORKERS_NAMESPACE
-
-namespace filereadersync {
-
-bool
-InitClass(JSContext* aCx, JSObject* aGlobal)
-{
-  return !!FileReaderSync::InitClass(aCx, aGlobal);
+  return NS_OK;
 }
 
-} // namespace filereadersync
+NS_IMETHODIMP
+FileReaderSync::Notify(const char* aCharset, nsDetectionConfident aConf)
+{
+  mCharset.Assign(aCharset);
 
-END_WORKERS_NAMESPACE
+  return NS_OK;
+}
--- a/dom/workers/FileReaderSync.h
+++ b/dom/workers/FileReaderSync.h
@@ -1,25 +1,59 @@
-/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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_workers_filereadersync_h__
 #define mozilla_dom_workers_filereadersync_h__
 
 #include "Workers.h"
+#include "mozilla/dom/workers/bindings/DOMBindingBase.h"
 
-#include "jspubtd.h"
+#include "nsICharsetDetectionObserver.h"
+#include "nsStringGlue.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/BindingUtils.h"
+
+class nsIInputStream;
+class nsIDOMBlob;
 
 BEGIN_WORKERS_NAMESPACE
 
-namespace filereadersync {
+class FileReaderSync MOZ_FINAL : public DOMBindingBase,
+                                 public nsICharsetDetectionObserver
+{
+  nsCString mCharset;
+  nsresult ConvertStream(nsIInputStream *aStream, const char *aCharset,
+                         nsAString &aResult);
+  nsresult GuessCharset(nsIInputStream *aStream, nsACString &aCharset);
+
+public:
+  virtual void
+  _trace(JSTracer* aTrc) MOZ_OVERRIDE;
+
+  virtual void
+  _finalize(JSFreeOp* aFop) MOZ_OVERRIDE;
 
-bool
-InitClass(JSContext* aCx, JSObject* aGlobal);
+  static FileReaderSync*
+  Constructor(JSContext* aCx, JSObject* aGlobal, ErrorResult& aRv);
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+  FileReaderSync(JSContext* aCx);
 
-} // namespace filereadersync
+  JSObject* ReadAsArrayBuffer(JSContext* aCx, JSObject* aBlob,
+                              ErrorResult& aRv);
+  void ReadAsBinaryString(JSObject* aBlob, nsAString& aResult,
+                          ErrorResult& aRv);
+  void ReadAsText(JSObject* aBlob, const Optional<nsAString>& aEncoding,
+                  nsAString& aResult, ErrorResult& aRv);
+  void ReadAsDataURL(JSObject* aBlob, nsAString& aResult, ErrorResult& aRv);
+
+  // From nsICharsetDetectionObserver
+  NS_IMETHOD Notify(const char *aCharset, nsDetectionConfident aConf);
+};
 
 END_WORKERS_NAMESPACE
 
 #endif // mozilla_dom_workers_filereadersync_h__
deleted file mode 100644
--- a/dom/workers/FileReaderSyncPrivate.cpp
+++ /dev/null
@@ -1,281 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 "FileReaderSyncPrivate.h"
-
-#include "nsCExternalHandlerService.h"
-#include "nsComponentManagerUtils.h"
-#include "nsCOMPtr.h"
-#include "nsDOMClassInfoID.h"
-#include "nsError.h"
-#include "nsIDOMFile.h"
-#include "nsCharsetAlias.h"
-#include "nsICharsetDetector.h"
-#include "nsIConverterInputStream.h"
-#include "nsIInputStream.h"
-#include "nsIPlatformCharset.h"
-#include "nsISeekableStream.h"
-#include "nsISupportsImpl.h"
-#include "nsISupportsImpl.h"
-#include "nsNetUtil.h"
-#include "nsServiceManagerUtils.h"
-#include "RuntimeService.h"
-
-#include "mozilla/Base64.h"
-
-USING_WORKERS_NAMESPACE
-
-NS_IMPL_ISUPPORTS1(FileReaderSyncPrivate, nsICharsetDetectionObserver)
-
-FileReaderSyncPrivate::FileReaderSyncPrivate()
-{
-  MOZ_COUNT_CTOR(mozilla::dom::workers::FileReaderSyncPrivate);
-}
-
-FileReaderSyncPrivate::~FileReaderSyncPrivate()
-{
-  MOZ_COUNT_DTOR(mozilla::dom::workers::FileReaderSyncPrivate);
-}
-
-nsresult
-FileReaderSyncPrivate::ReadAsArrayBuffer(nsIDOMBlob* aBlob, uint32_t aLength,
-                                         uint8* aBuffer)
-{
-  nsCOMPtr<nsIInputStream> stream;
-  nsresult rv = aBlob->GetInternalStream(getter_AddRefs(stream));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  uint32_t numRead;
-  rv = stream->Read((char*)aBuffer, aLength, &numRead);
-  NS_ENSURE_SUCCESS(rv, rv);
-  NS_ASSERTION(numRead == aLength, "failed to read data");
-
-  return NS_OK;
-}
-
-nsresult
-FileReaderSyncPrivate::ReadAsBinaryString(nsIDOMBlob* aBlob, nsAString& aResult)
-{
-  nsCOMPtr<nsIInputStream> stream;
-  nsresult rv = aBlob->GetInternalStream(getter_AddRefs(stream));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  uint32_t numRead;
-  do {
-    char readBuf[4096];
-    rv = stream->Read(readBuf, sizeof(readBuf), &numRead);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    uint32_t oldLength = aResult.Length();
-    AppendASCIItoUTF16(Substring(readBuf, readBuf + numRead), aResult);
-    if (aResult.Length() - oldLength != numRead) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-  } while (numRead > 0);
-
-  return NS_OK;
-}
-
-nsresult
-FileReaderSyncPrivate::ReadAsText(nsIDOMBlob* aBlob,
-                                  const nsAString& aEncoding, nsAString& aResult)
-{
-  nsCOMPtr<nsIInputStream> stream;
-  nsresult rv = aBlob->GetInternalStream(getter_AddRefs(stream));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCString charsetGuess;
-  if (aEncoding.IsEmpty()) {
-    rv = GuessCharset(stream, charsetGuess);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(stream);
-    NS_ENSURE_TRUE(seekable, NS_ERROR_FAILURE);
-
-    // Seek to 0 because guessing the charset advances the stream.
-    rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
-    NS_ENSURE_SUCCESS(rv, rv);
-  } else {
-    CopyUTF16toUTF8(aEncoding, charsetGuess);
-  }
-
-  nsCString charset;
-  rv = nsCharsetAlias::GetPreferred(charsetGuess, charset);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return ConvertStream(stream, charset.get(), aResult);
-}
-
-nsresult
-FileReaderSyncPrivate::ReadAsDataURL(nsIDOMBlob* aBlob, nsAString& aResult)
-{
-  nsAutoString scratchResult;
-  scratchResult.AssignLiteral("data:");
-
-  nsString contentType;
-  aBlob->GetType(contentType);
-
-  if (contentType.IsEmpty()) {
-    scratchResult.AppendLiteral("application/octet-stream");
-  } else {
-    scratchResult.Append(contentType);
-  }
-  scratchResult.AppendLiteral(";base64,");
-
-  nsCOMPtr<nsIInputStream> stream;
-  nsresult rv = aBlob->GetInternalStream(getter_AddRefs(stream));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  uint64_t size;
-  rv = aBlob->GetSize(&size);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIInputStream> bufferedStream;
-  rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream, size);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsAutoString encodedData;
-  rv = Base64EncodeInputStream(bufferedStream, encodedData, size);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  scratchResult.Append(encodedData);
-
-  aResult = scratchResult;
-  return NS_OK;
-}
-
-nsresult
-FileReaderSyncPrivate::ConvertStream(nsIInputStream *aStream,
-                                     const char *aCharset,
-                                     nsAString &aResult)
-{
-  nsCOMPtr<nsIConverterInputStream> converterStream =
-    do_CreateInstance("@mozilla.org/intl/converter-input-stream;1");
-  NS_ENSURE_TRUE(converterStream, NS_ERROR_FAILURE);
-
-  nsresult rv = converterStream->Init(aStream, aCharset, 8192,
-                  nsIConverterInputStream::DEFAULT_REPLACEMENT_CHARACTER);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIUnicharInputStream> unicharStream =
-    do_QueryInterface(converterStream);
-  NS_ENSURE_TRUE(unicharStream, NS_ERROR_FAILURE);
-
-  uint32_t numChars;
-  nsString result;
-  while (NS_SUCCEEDED(unicharStream->ReadString(8192, result, &numChars)) &&
-         numChars > 0) {
-    uint32_t oldLength = aResult.Length();
-    aResult.Append(result);
-    if (aResult.Length() - oldLength != result.Length()) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-  }
-
-  return rv;
-}
-
-nsresult
-FileReaderSyncPrivate::GuessCharset(nsIInputStream *aStream,
-                                    nsACString &aCharset)
-{
-  // First try the universal charset detector
-  nsCOMPtr<nsICharsetDetector> detector
-    = do_CreateInstance(NS_CHARSET_DETECTOR_CONTRACTID_BASE
-                        "universal_charset_detector");
-  if (!detector) {
-    RuntimeService* runtime = RuntimeService::GetService();
-    NS_ASSERTION(runtime, "This should never be null!");
-
-    // No universal charset detector, try the default charset detector
-    const nsACString& detectorName = runtime->GetDetectorName();
-
-    if (!detectorName.IsEmpty()) {
-      nsAutoCString detectorContractID;
-      detectorContractID.AssignLiteral(NS_CHARSET_DETECTOR_CONTRACTID_BASE);
-      detectorContractID += detectorName;
-      detector = do_CreateInstance(detectorContractID.get());
-    }
-  }
-
-  nsresult rv;
-  if (detector) {
-    detector->Init(this);
-
-    bool done;
-    uint32_t numRead;
-    do {
-      char readBuf[4096];
-      rv = aStream->Read(readBuf, sizeof(readBuf), &numRead);
-      NS_ENSURE_SUCCESS(rv, rv);
-      if (numRead <= 0) {
-        break;
-      }
-      rv = detector->DoIt(readBuf, numRead, &done);
-      NS_ENSURE_SUCCESS(rv, rv);
-    } while (!done);
-
-    rv = detector->Done();
-    NS_ENSURE_SUCCESS(rv, rv);
-  } else {
-    // no charset detector available, check the BOM
-    unsigned char sniffBuf[4];
-    uint32_t numRead;
-    rv = aStream->Read(reinterpret_cast<char*>(sniffBuf),
-                       sizeof(sniffBuf), &numRead);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (numRead >= 4 &&
-        sniffBuf[0] == 0x00 &&
-        sniffBuf[1] == 0x00 &&
-        sniffBuf[2] == 0xfe &&
-        sniffBuf[3] == 0xff) {
-      mCharset = "UTF-32BE";
-    } else if (numRead >= 4 &&
-               sniffBuf[0] == 0xff &&
-               sniffBuf[1] == 0xfe &&
-               sniffBuf[2] == 0x00 &&
-               sniffBuf[3] == 0x00) {
-      mCharset = "UTF-32LE";
-    } else if (numRead >= 2 &&
-               sniffBuf[0] == 0xfe &&
-               sniffBuf[1] == 0xff) {
-      mCharset = "UTF-16BE";
-    } else if (numRead >= 2 &&
-               sniffBuf[0] == 0xff &&
-               sniffBuf[1] == 0xfe) {
-      mCharset = "UTF-16LE";
-    } else if (numRead >= 3 &&
-               sniffBuf[0] == 0xef &&
-               sniffBuf[1] == 0xbb &&
-               sniffBuf[2] == 0xbf) {
-      mCharset = "UTF-8";
-    }
-  }
-
-  if (mCharset.IsEmpty()) {
-    RuntimeService* runtime = RuntimeService::GetService();
-    mCharset = runtime->GetSystemCharset();
-  }
-
-  if (mCharset.IsEmpty()) {
-    // no sniffed or default charset, try UTF-8
-    mCharset.AssignLiteral("UTF-8");
-  }
-
-  aCharset = mCharset;
-  mCharset.Truncate();
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-FileReaderSyncPrivate::Notify(const char* aCharset, nsDetectionConfident aConf)
-{
-  mCharset.Assign(aCharset);
-
-  return NS_OK;
-}
deleted file mode 100644
--- a/dom/workers/FileReaderSyncPrivate.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 nsDOMFileReaderSyncPrivate_h
-#define nsDOMFileReaderSyncPrivate_h
-
-#include "Workers.h"
-
-#include "nsICharsetDetectionObserver.h"
-#include "nsStringGlue.h"
-#include "mozilla/Attributes.h"
-
-class nsIInputStream;
-class nsIDOMBlob;
-
-BEGIN_WORKERS_NAMESPACE
-
-class FileReaderSyncPrivate MOZ_FINAL : public PrivatizableBase,
-                                        public nsICharsetDetectionObserver
-{
-  nsCString mCharset;
-  nsresult ConvertStream(nsIInputStream *aStream, const char *aCharset,
-                         nsAString &aResult);
-  nsresult GuessCharset(nsIInputStream *aStream, nsACString &aCharset);
-
-public:
-  NS_DECL_ISUPPORTS
-
-  FileReaderSyncPrivate();
-  ~FileReaderSyncPrivate();
-
-  nsresult ReadAsArrayBuffer(nsIDOMBlob* aBlob, uint32_t aLength,
-                             uint8* aBuffer);
-  nsresult ReadAsBinaryString(nsIDOMBlob* aBlob, nsAString& aResult);
-  nsresult ReadAsText(nsIDOMBlob* aBlob, const nsAString& aEncoding,
-                      nsAString& aResult);
-  nsresult ReadAsDataURL(nsIDOMBlob* aBlob, nsAString& aResult);
-
-  // From nsICharsetDetectionObserver
-  NS_IMETHOD Notify(const char *aCharset, nsDetectionConfident aConf);
-};
-
-END_WORKERS_NAMESPACE
-
-#endif
--- a/dom/workers/Makefile.in
+++ b/dom/workers/Makefile.in
@@ -19,17 +19,16 @@ CPPSRCS = \
   ChromeWorkerScope.cpp \
   DOMBindingBase.cpp \
   Events.cpp \
   EventListenerManager.cpp \
   EventTarget.cpp \
   Exceptions.cpp \
   File.cpp \
   FileReaderSync.cpp \
-  FileReaderSyncPrivate.cpp \
   ImageData.cpp \
   Location.cpp \
   Navigator.cpp \
   Principal.cpp \
   RuntimeService.cpp \
   ScriptLoader.cpp \
   Worker.cpp \
   WorkerPrivate.cpp \
@@ -47,16 +46,17 @@ EXPORTS_NAMESPACES = \
 # Public stuff.
 EXPORTS_mozilla/dom/workers = Workers.h
 
 # Stuff needed for the bindings, not really public though.
 EXPORTS_mozilla/dom/workers/bindings = \
   DOMBindingBase.h \
   EventListenerManager.h \
   EventTarget.h \
+  FileReaderSync.h \
   WorkerFeature.h \
   XMLHttpRequestEventTarget.h \
   XMLHttpRequestUpload.h \
   XMLHttpRequest.h \
   $(NULL)
 
 LOCAL_INCLUDES = \
   -I$(topsrcdir)/content/base/src \
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -7,16 +7,17 @@
 #include "WorkerScope.h"
 
 #include "jsapi.h"
 #include "jsdbgapi.h"
 #include "mozilla/Util.h"
 #include "mozilla/dom/DOMJSClass.h"
 #include "mozilla/dom/EventTargetBinding.h"
 #include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/FileReaderSyncBinding.h"
 #include "mozilla/dom/XMLHttpRequestBinding.h"
 #include "mozilla/dom/XMLHttpRequestUploadBinding.h"
 #include "mozilla/OSFileConstants.h"
 #include "nsTraceRefcnt.h"
 #include "xpcpublic.h"
 
 #ifdef ANDROID
 #include <android/log.h>
@@ -970,30 +971,30 @@ CreateDedicatedWorkerGlobalScope(JSConte
 
   if (!DefineOSFileConstants(aCx, global)) {
     return NULL;
   }
 
   // Init other classes we care about.
   if (!events::InitClasses(aCx, global, false) ||
       !file::InitClasses(aCx, global) ||
-      !filereadersync::InitClass(aCx, global) ||
       !exceptions::InitClasses(aCx, global) ||
       !location::InitClass(aCx, global) ||
       !imagedata::InitClass(aCx, global) ||
       !navigator::InitClass(aCx, global)) {
     return NULL;
   }
 
   // Init other paris-bindings.  Use GetProtoObject so the proto will
   // be correctly cached in the proto cache.  Otherwise we'll end up
   // double-calling CreateInterfaceObjects when we actually create an
   // object which has these protos, which breaks things like
   // instanceof.
-  if (!XMLHttpRequestBinding_workers::GetProtoObject(aCx, global, global) ||
+  if (!FileReaderSyncBinding_workers::GetProtoObject(aCx, global, global) ||
+      !XMLHttpRequestBinding_workers::GetProtoObject(aCx, global, global) ||
       !XMLHttpRequestUploadBinding_workers::GetProtoObject(aCx, global, global)) {
     return NULL;
   }
 
   if (!JS_DefineProfilingFunctions(aCx, global)) {
     return NULL;
   }
 
--- a/embedding/android/GeckoAppShell.java
+++ b/embedding/android/GeckoAppShell.java
@@ -1657,35 +1657,16 @@ public class GeckoAppShell
         if (sCamera != null) {
             sCamera.stopPreview();
             sCamera.release();
             sCamera = null;
             sCameraBuffer = null;
         }
     }
 
-
-    static SynchronousQueue<Date> sTracerQueue = new SynchronousQueue<Date>();
-    public static void fireAndWaitForTracerEvent() {
-        getMainHandler().post(new Runnable() { 
-                public void run() {
-                    try {
-                        sTracerQueue.put(new Date());
-                    } catch(InterruptedException ie) {
-                        Log.w("GeckoAppShell", "exception firing tracer", ie);
-                    }
-                }
-        });
-        try {
-            sTracerQueue.take();
-        } catch(InterruptedException ie) {
-            Log.w("GeckoAppShell", "exception firing tracer", ie);
-        }
-    }
-
     // unused
     static void checkUriVisited(String uri) {}
     // unused
     static void markUriVisited(final String uri) {}
 
     /*
      * Battery API related methods.
      */
--- a/embedding/android/GeckoEvent.java
+++ b/embedding/android/GeckoEvent.java
@@ -46,16 +46,27 @@ public class GeckoEvent {
     public static final int SAVE_STATE = 18;
     public static final int BROADCAST = 19;
     public static final int VIEWPORT = 20;
     public static final int VISITED = 21;
     public static final int NETWORK_CHANGED = 22;
     public static final int PROXIMITY_EVENT = 23;
     public static final int SCREENORIENTATION_CHANGED = 26;
 
+    /**
+     * These DOM_KEY_LOCATION constants mirror the DOM KeyboardEvent's constants.
+     * @see https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent#Key_location_constants
+     */
+    private static final int DOM_KEY_LOCATION_STANDARD = 0;
+    private static final int DOM_KEY_LOCATION_LEFT = 1;
+    private static final int DOM_KEY_LOCATION_RIGHT = 2;
+    private static final int DOM_KEY_LOCATION_NUMPAD = 3;
+    private static final int DOM_KEY_LOCATION_MOBILE = 4;
+    private static final int DOM_KEY_LOCATION_JOYSTICK = 5;
+
     public static final int IME_COMPOSITION_END = 0;
     public static final int IME_COMPOSITION_BEGIN = 1;
     public static final int IME_SET_TEXT = 2;
     public static final int IME_GET_TEXT = 3;
     public static final int IME_DELETE_TEXT = 4;
     public static final int IME_SET_SELECTION = 5;
     public static final int IME_GET_SELECTION = 6;
     public static final int IME_ADD_RANGE = 7;
@@ -88,16 +99,17 @@ public class GeckoEvent {
     public int mKeyCode, mUnicodeChar;
     public int mRepeatCount;
     public int mOffset, mCount;
     public String mCharacters, mCharactersExtra;
     public int mRangeType, mRangeStyles;
     public int mRangeForeColor, mRangeBackColor;
     public Location mLocation;
     public Address  mAddress;
+    public int mDomKeyLocation;
 
     public double mBandwidth;
     public boolean mCanBeMetered;
 
     public short mScreenOrientation;
 
     public int mNativeWindow;
 
@@ -116,18 +128,86 @@ public class GeckoEvent {
         mAction = k.getAction();
         mTime = k.getEventTime();
         mMetaState = k.getMetaState();
         mFlags = k.getFlags();
         mKeyCode = k.getKeyCode();
         mUnicodeChar = k.getUnicodeChar();
         mRepeatCount = k.getRepeatCount();
         mCharacters = k.getCharacters();
+        mDomKeyLocation = isJoystickButton(mKeyCode) ? DOM_KEY_LOCATION_JOYSTICK : DOM_KEY_LOCATION_MOBILE;
+    }
+
+    /**
+     * This method tests if a key is one of the described in:
+     * https://bugzilla.mozilla.org/show_bug.cgi?id=756504#c0
+     * @param keyCode int with the key code (Android key constant from KeyEvent)
+     * @return true if the key is one of the listed above, false otherwise.
+     */
+    private static boolean isJoystickButton(int keyCode) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DPAD_CENTER:
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+            case KeyEvent.KEYCODE_DPAD_UP:
+                return true;
+            default:
+                if (Build.VERSION.SDK_INT >= 12) {
+                    return KeyEvent.isGamepadButton(keyCode);
+                }
+                return GeckoEvent.isGamepadButton(keyCode);
+        }
     }
 
+    /**
+     * This method is a replacement for the the KeyEvent.isGamepadButton method to be
+     * compatible with Build.VERSION.SDK_INT < 12. This is an implementantion of the
+     * same method isGamepadButton available after SDK 12.
+     * @param keyCode int with the key code (Android key constant from KeyEvent).
+     * @return True if the keycode is a gamepad button, such as {@link #KEYCODE_BUTTON_A}.
+     */
+    private static boolean isGamepadButton(int keyCode) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_BUTTON_A:
+            case KeyEvent.KEYCODE_BUTTON_B:
+            case KeyEvent.KEYCODE_BUTTON_C:
+            case KeyEvent.KEYCODE_BUTTON_X:
+            case KeyEvent.KEYCODE_BUTTON_Y:
+            case KeyEvent.KEYCODE_BUTTON_Z:
+            case KeyEvent.KEYCODE_BUTTON_L1:
+            case KeyEvent.KEYCODE_BUTTON_R1:
+            case KeyEvent.KEYCODE_BUTTON_L2:
+            case KeyEvent.KEYCODE_BUTTON_R2:
+            case KeyEvent.KEYCODE_BUTTON_THUMBL:
+            case KeyEvent.KEYCODE_BUTTON_THUMBR:
+            case KeyEvent.KEYCODE_BUTTON_START:
+            case KeyEvent.KEYCODE_BUTTON_SELECT:
+            case KeyEvent.KEYCODE_BUTTON_MODE:
+            case KeyEvent.KEYCODE_BUTTON_1:
+            case KeyEvent.KEYCODE_BUTTON_2:
+            case KeyEvent.KEYCODE_BUTTON_3:
+            case KeyEvent.KEYCODE_BUTTON_4:
+            case KeyEvent.KEYCODE_BUTTON_5:
+            case KeyEvent.KEYCODE_BUTTON_6:
+            case KeyEvent.KEYCODE_BUTTON_7:
+            case KeyEvent.KEYCODE_BUTTON_8:
+            case KeyEvent.KEYCODE_BUTTON_9:
+            case KeyEvent.KEYCODE_BUTTON_10:
+            case KeyEvent.KEYCODE_BUTTON_11:
+            case KeyEvent.KEYCODE_BUTTON_12:
+            case KeyEvent.KEYCODE_BUTTON_13:
+            case KeyEvent.KEYCODE_BUTTON_14:
+            case KeyEvent.KEYCODE_BUTTON_15:
+            case KeyEvent.KEYCODE_BUTTON_16:
+                return true;
+            default:
+                return false;
+        }
+    }
     public GeckoEvent(MotionEvent m) {
         mType = MOTION_EVENT;
         mAction = m.getAction();
         mTime = m.getEventTime();
         mMetaState = m.getMetaState();
 
         switch (mAction & MotionEvent.ACTION_MASK) {
             case MotionEvent.ACTION_CANCEL:
--- a/extensions/cookie/nsPermissionManager.cpp
+++ b/extensions/cookie/nsPermissionManager.cpp
@@ -905,16 +905,36 @@ nsPermissionManager::TestPermission(nsIU
   nsCOMPtr<nsIPrincipal> principal;
   nsresult rv = GetPrincipal(aURI, getter_AddRefs(principal));
   NS_ENSURE_SUCCESS(rv, rv);
 
   return TestPermissionFromPrincipal(principal, aType, aPermission);
 }
 
 NS_IMETHODIMP
+nsPermissionManager::TestPermissionFromWindow(nsIDOMWindow* aWindow,
+                                              const char* aType,
+                                              uint32_t* aPermission)
+{
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
+  NS_ENSURE_TRUE(window, NS_NOINTERFACE);
+
+  nsPIDOMWindow* innerWindow = window->IsInnerWindow() ?
+    window.get() :
+    window->GetCurrentInnerWindow();
+
+  // Get the document for security check
+  nsCOMPtr<nsIDocument> document = innerWindow->GetExtantDoc();
+  NS_ENSURE_TRUE(document, NS_NOINTERFACE);
+
+  nsCOMPtr<nsIPrincipal> principal = document->NodePrincipal();
+  return TestPermissionFromPrincipal(principal, aType, aPermission);
+}
+
+NS_IMETHODIMP
 nsPermissionManager::TestPermissionFromPrincipal(nsIPrincipal* aPrincipal,
                                                  const char* aType,
                                                  uint32_t* aPermission)
 {
   NS_ENSURE_ARG_POINTER(aPrincipal);
 
   // System principals do not have URI so we can't try to get
   // retro-compatibility here.
--- a/hal/Hal.cpp
+++ b/hal/Hal.cpp
@@ -776,10 +776,213 @@ SetProcessPriority(int aPid, ProcessPrio
   if (InSandbox()) {
     hal_sandbox::SetProcessPriority(aPid, aPriority);
   }
   else {
     hal_impl::SetProcessPriority(aPid, aPriority);
   }
 }
 
+static StaticAutoPtr<ObserverList<FMRadioOperationInformation> > sFMRadioObservers;
+
+static void
+InitializeFMRadioObserver()
+{
+  if (!sFMRadioObservers) {
+    sFMRadioObservers = new ObserverList<FMRadioOperationInformation>;
+    ClearOnShutdown(&sFMRadioObservers);
+  }
+}
+
+void
+RegisterFMRadioObserver(FMRadioObserver* aFMRadioObserver) {
+  AssertMainThread();
+  InitializeFMRadioObserver();
+  sFMRadioObservers->AddObserver(aFMRadioObserver);
+}
+
+void
+UnregisterFMRadioObserver(FMRadioObserver* aFMRadioObserver) {
+  AssertMainThread();
+  InitializeFMRadioObserver();
+  sFMRadioObservers->RemoveObserver(aFMRadioObserver);
+}
+
+void
+NotifyFMRadioStatus(const FMRadioOperationInformation& aFMRadioState) {
+  InitializeFMRadioObserver();
+  sFMRadioObservers->Broadcast(aFMRadioState);
+}
+
+void
+EnableFMRadio(const FMRadioSettings& aInfo) {
+  AssertMainThread();
+  PROXY_IF_SANDBOXED(EnableFMRadio(aInfo));
+}
+
+void
+DisableFMRadio() {
+  AssertMainThread();
+  PROXY_IF_SANDBOXED(DisableFMRadio());
+}
+
+void
+FMRadioSeek(const FMRadioSeekDirection& aDirection) {
+  AssertMainThread();
+  PROXY_IF_SANDBOXED(FMRadioSeek(aDirection));
+}
+
+void
+GetFMRadioSettings(FMRadioSettings* aInfo) {
+  AssertMainThread();
+  PROXY_IF_SANDBOXED(GetFMRadioSettings(aInfo));
+}
+
+void
+SetFMRadioFrequency(const uint32_t aFrequency) {
+  AssertMainThread();
+  PROXY_IF_SANDBOXED(SetFMRadioFrequency(aFrequency));
+}
+
+uint32_t
+GetFMRadioFrequency() {
+  AssertMainThread();
+  RETURN_PROXY_IF_SANDBOXED(GetFMRadioFrequency());
+}
+
+bool
+IsFMRadioOn() {
+  AssertMainThread();
+  RETURN_PROXY_IF_SANDBOXED(IsFMRadioOn());
+}
+
+uint32_t
+GetFMRadioSignalStrength() {
+  AssertMainThread();
+  RETURN_PROXY_IF_SANDBOXED(GetFMRadioSignalStrength());
+}
+
+void
+CancelFMRadioSeek() {
+  AssertMainThread();
+  PROXY_IF_SANDBOXED(CancelFMRadioSeek());
+}
+
+FMRadioSettings
+GetFMBandSettings(FMRadioCountry aCountry) {
+  FMRadioSettings settings;
+
+  switch (aCountry) {
+    case FM_RADIO_COUNTRY_US:
+    case FM_RADIO_COUNTRY_EU:
+      settings.upperLimit() = 108000;
+      settings.lowerLimit() = 87800;
+      settings.spaceType() = 200;
+      settings.preEmphasis() = 75;
+      break;
+    case FM_RADIO_COUNTRY_JP_STANDARD:
+      settings.upperLimit() = 76000;
+      settings.lowerLimit() = 90000;
+      settings.spaceType() = 100;
+      settings.preEmphasis() = 50;
+      break;
+    case FM_RADIO_COUNTRY_CY:
+    case FM_RADIO_COUNTRY_DE:
+    case FM_RADIO_COUNTRY_DK:
+    case FM_RADIO_COUNTRY_ES:
+    case FM_RADIO_COUNTRY_FI:
+    case FM_RADIO_COUNTRY_FR:
+    case FM_RADIO_COUNTRY_HU:
+    case FM_RADIO_COUNTRY_IR:
+    case FM_RADIO_COUNTRY_IT:
+    case FM_RADIO_COUNTRY_KW:
+    case FM_RADIO_COUNTRY_LT:
+    case FM_RADIO_COUNTRY_ML:
+    case FM_RADIO_COUNTRY_NO:
+    case FM_RADIO_COUNTRY_OM:
+    case FM_RADIO_COUNTRY_PG:
+    case FM_RADIO_COUNTRY_NL:
+    case FM_RADIO_COUNTRY_CZ:
+    case FM_RADIO_COUNTRY_UK:
+    case FM_RADIO_COUNTRY_RW:
+    case FM_RADIO_COUNTRY_SN:
+    case FM_RADIO_COUNTRY_SI:
+    case FM_RADIO_COUNTRY_ZA:
+    case FM_RADIO_COUNTRY_SE:
+    case FM_RADIO_COUNTRY_CH:
+    case FM_RADIO_COUNTRY_TW:
+    case FM_RADIO_COUNTRY_UA:
+      settings.upperLimit() = 108000;
+      settings.lowerLimit() = 87500;
+      settings.spaceType() = 100;
+      settings.preEmphasis() = 50;
+      break;
+    case FM_RADIO_COUNTRY_VA:
+    case FM_RADIO_COUNTRY_MA:
+    case FM_RADIO_COUNTRY_TR:
+      settings.upperLimit() = 10800;
+      settings.lowerLimit() = 87500;
+      settings.spaceType() = 100;
+      settings.preEmphasis() = 75;
+      break;
+    case FM_RADIO_COUNTRY_AU:
+    case FM_RADIO_COUNTRY_BD:
+      settings.upperLimit() = 108000;
+      settings.lowerLimit() = 87500;
+      settings.spaceType() = 200;
+      settings.preEmphasis() = 75;
+      break;
+    case FM_RADIO_COUNTRY_AW:
+    case FM_RADIO_COUNTRY_BS:
+    case FM_RADIO_COUNTRY_CO:
+    case FM_RADIO_COUNTRY_KR:
+      settings.upperLimit() = 108000;
+      settings.lowerLimit() = 88000;
+      settings.spaceType() = 200;
+      settings.preEmphasis() = 75;
+      break;
+    case FM_RADIO_COUNTRY_EC:
+      settings.upperLimit() = 108000;
+      settings.lowerLimit() = 88000;
+      settings.spaceType() = 200;
+      settings.preEmphasis() = 0;
+      break;
+    case FM_RADIO_COUNTRY_GM:
+      settings.upperLimit() = 108000;
+      settings.lowerLimit() = 88000;
+      settings.spaceType() = 0;
+      settings.preEmphasis() = 75;
+      break;
+    case FM_RADIO_COUNTRY_QA:
+      settings.upperLimit() = 108000;
+      settings.lowerLimit() = 88000;
+      settings.spaceType() = 200;
+      settings.preEmphasis() = 50;
+      break;
+    case FM_RADIO_COUNTRY_SG:
+      settings.upperLimit() = 108000;
+      settings.lowerLimit() = 88000;
+      settings.spaceType() = 200;
+      settings.preEmphasis() = 50;
+      break;
+    case FM_RADIO_COUNTRY_IN:
+      settings.upperLimit() = 100000;
+      settings.lowerLimit() = 108000;
+      settings.spaceType() = 100;
+      settings.preEmphasis() = 50;
+      break;
+    case FM_RADIO_COUNTRY_NZ:
+      settings.upperLimit() = 100000;
+      settings.lowerLimit() = 88000;
+      settings.spaceType() = 50;
+      settings.preEmphasis() = 50;
+      break;
+    case FM_RADIO_COUNTRY_USER_DEFINED:
+      break;
+    default:
+      MOZ_ASSERT(0);
+      break;
+    };
+    return settings;
+}
+
 } // namespace hal
 } // namespace mozilla
--- a/hal/Hal.h
+++ b/hal/Hal.h
@@ -432,16 +432,83 @@ bool SetAlarm(int32_t aSeconds, int32_t 
  * Set the priority of the given process.
  *
  * Exactly what this does will vary between platforms.  On *nix we might give
  * background processes higher nice values.  On other platforms, we might
  * ignore this call entirely.
  */
 void SetProcessPriority(int aPid, hal::ProcessPriority aPriority);
 
+/**
+ * Register an observer for the FM radio.
+ */
+void RegisterFMRadioObserver(hal::FMRadioObserver* aRadioObserver);
+
+/**
+ * Unregister the observer for the FM radio.
+ */
+void UnregisterFMRadioObserver(hal::FMRadioObserver* aRadioObserver);
+
+/**
+ * Notify observers that a call to EnableFMRadio, DisableFMRadio, or FMRadioSeek
+ * has completed, and indicate what the call returned.
+ */
+void NotifyFMRadioStatus(const hal::FMRadioOperationInformation& aRadioState);
+
+/**
+ * Enable the FM radio and configure it according to the settings in aInfo.
+ */
+void EnableFMRadio(const hal::FMRadioSettings& aInfo);
+
+/**
+ * Disable the FM radio.
+ */
+void DisableFMRadio();
+
+/**
+ * Seek to an available FM radio station.
+ *
+ */
+void FMRadioSeek(const hal::FMRadioSeekDirection& aDirection);
+
+/**
+ * Get the current FM radio settings.
+ */
+void GetFMRadioSettings(hal::FMRadioSettings* aInfo);
+
+/**
+ * Set the FM radio's frequency.
+ */
+void SetFMRadioFrequency(const uint32_t frequency);
+
+/**
+ * Get the FM radio's frequency.
+ */
+uint32_t GetFMRadioFrequency();
+
+/**
+ * Get FM radio power state
+ */
+bool IsFMRadioOn();
+
+/**
+ * Get FM radio signal strength
+ */
+uint32_t GetFMRadioSignalStrength();
+
+/**
+ * Cancel FM radio seeking
+ */
+void CancelFMRadioSeek();
+
+/**
+ * Get FM radio band settings by country.
+ */
+hal::FMRadioSettings GetFMBandSettings(hal::FMRadioCountry aCountry);
+
 } // namespace MOZ_HAL_NAMESPACE
 } // namespace mozilla
 
 #ifdef MOZ_DEFINED_HAL_NAMESPACE
 # undef MOZ_DEFINED_HAL_NAMESPACE
 # undef MOZ_HAL_NAMESPACE
 #endif
 
--- a/hal/HalTypes.h
+++ b/hal/HalTypes.h
@@ -76,16 +76,92 @@ enum WakeLockControl {
 
 enum SystemTimeChange {
   SYS_TIME_CHANGE_UNKNOWN = -1,
   SYS_TIME_CHANGE_CLOCK,
   SYS_TIME_CHANGE_TZ,
   SYS_TIME_CHANGE_GUARD
 };
 
+class FMRadioOperationInformation;
+
+enum FMRadioOperation {
+  FM_RADIO_OPERATION_UNKNOWN = -1,
+  FM_RADIO_OPERATION_ENABLE,
+  FM_RADIO_OPERATION_DISABLE,
+  FM_RADIO_OPERATION_SEEK,
+  NUM_FM_RADIO_OPERATION
+};
+
+enum FMRadioOperationStatus {
+  FM_RADIO_OPERATION_STATUS_UNKNOWN = -1,
+  FM_RADIO_OPERATION_STATUS_SUCCESS,
+  FM_RADIO_OPERATION_STATUS_FAIL,
+  NUM_FM_RADIO_OPERATION_STATUS
+};
+
+enum FMRadioSeekDirection {
+  FM_RADIO_SEEK_DIRECTION_UNKNOWN = -1,
+  FM_RADIO_SEEK_DIRECTION_UP,
+  FM_RADIO_SEEK_DIRECTION_DOWN,
+  NUM_FM_RADIO_SEEK_DIRECTION
+};
+
+enum FMRadioCountry {
+  FM_RADIO_COUNTRY_UNKNOWN = -1,
+  FM_RADIO_COUNTRY_US,  //USA
+  FM_RADIO_COUNTRY_EU,
+  FM_RADIO_COUNTRY_JP_STANDARD,
+  FM_RADIO_COUNTRY_JP_WIDE,
+  FM_RADIO_COUNTRY_DE,  //Germany
+  FM_RADIO_COUNTRY_AW,  //Aruba
+  FM_RADIO_COUNTRY_AU,  //Australlia
+  FM_RADIO_COUNTRY_BS,  //Bahamas
+  FM_RADIO_COUNTRY_BD,  //Bangladesh
+  FM_RADIO_COUNTRY_CY,  //Cyprus
+  FM_RADIO_COUNTRY_VA,  //Vatican
+  FM_RADIO_COUNTRY_CO,  //Colombia
+  FM_RADIO_COUNTRY_KR,  //Korea
+  FM_RADIO_COUNTRY_DK,  //Denmark
+  FM_RADIO_COUNTRY_EC,  //Ecuador
+  FM_RADIO_COUNTRY_ES,  //Spain
+  FM_RADIO_COUNTRY_FI,  //Finland
+  FM_RADIO_COUNTRY_FR,  //France
+  FM_RADIO_COUNTRY_GM,  //Gambia
+  FM_RADIO_COUNTRY_HU,  //Hungary
+  FM_RADIO_COUNTRY_IN,  //India
+  FM_RADIO_COUNTRY_IR,  //Iran
+  FM_RADIO_COUNTRY_IT,  //Italy
+  FM_RADIO_COUNTRY_KW,  //Kuwait
+  FM_RADIO_COUNTRY_LT,  //Lithuania
+  FM_RADIO_COUNTRY_ML,  //Mali
+  FM_RADIO_COUNTRY_MA,  //Morocco
+  FM_RADIO_COUNTRY_NO,  //Norway
+  FM_RADIO_COUNTRY_NZ,  //New Zealand
+  FM_RADIO_COUNTRY_OM,  //Oman
+  FM_RADIO_COUNTRY_PG,  //Papua New Guinea
+  FM_RADIO_COUNTRY_NL,  //Netherlands
+  FM_RADIO_COUNTRY_QA,  //Qatar
+  FM_RADIO_COUNTRY_CZ,  //Czech Republic
+  FM_RADIO_COUNTRY_UK,  //United Kingdom of Great Britain and Northern Ireland
+  FM_RADIO_COUNTRY_RW,  //Rwandese Republic
+  FM_RADIO_COUNTRY_SN,  //Senegal
+  FM_RADIO_COUNTRY_SG,  //Singapore
+  FM_RADIO_COUNTRY_SI,  //Slovenia
+  FM_RADIO_COUNTRY_ZA,  //South Africa
+  FM_RADIO_COUNTRY_SE,  //Sweden
+  FM_RADIO_COUNTRY_CH,  //Switzerland
+  FM_RADIO_COUNTRY_TW,  //Taiwan
+  FM_RADIO_COUNTRY_TR,  //Turkey
+  FM_RADIO_COUNTRY_UA,  //Ukraine
+  FM_RADIO_COUNTRY_USER_DEFINED,
+  NUM_FM_RADIO_COUNTRY
+};
+
+typedef Observer<FMRadioOperationInformation> FMRadioObserver;
 } // namespace hal
 } // namespace mozilla
 
 namespace IPC {
 
 /**
  * Light type serializer.
  */
@@ -158,11 +234,50 @@ struct ParamTraits<mozilla::hal::Process
  */
 template <>
 struct ParamTraits<mozilla::hal::SystemTimeChange>
   : public EnumSerializer<mozilla::hal::SystemTimeChange,
                           mozilla::hal::SYS_TIME_CHANGE_UNKNOWN,
                           mozilla::hal::SYS_TIME_CHANGE_GUARD>
 {};
  
+/**
+ * Serializer for FMRadioOperation
+ */
+template <>
+struct ParamTraits<mozilla::hal::FMRadioOperation>:
+  public EnumSerializer<mozilla::hal::FMRadioOperation,
+                        mozilla::hal::FM_RADIO_OPERATION_UNKNOWN,
+                        mozilla::hal::NUM_FM_RADIO_OPERATION>
+{};
+
+/**
+ * Serializer for FMRadioOperationStatus
+ */
+template <>
+struct ParamTraits<mozilla::hal::FMRadioOperationStatus>:
+  public EnumSerializer<mozilla::hal::FMRadioOperationStatus,
+                        mozilla::hal::FM_RADIO_OPERATION_STATUS_UNKNOWN,
+                        mozilla::hal::NUM_FM_RADIO_OPERATION_STATUS>
+{};
+
+/**
+ * Serializer for FMRadioSeekDirection
+ */
+template <>
+struct ParamTraits<mozilla::hal::FMRadioSeekDirection>:
+  public EnumSerializer<mozilla::hal::FMRadioSeekDirection,
+                        mozilla::hal::FM_RADIO_SEEK_DIRECTION_UNKNOWN,
+                        mozilla::hal::NUM_FM_RADIO_SEEK_DIRECTION>
+{};
+
+/**
+ * Serializer for FMRadioCountry
+ **/
+template <>
+struct ParamTraits<mozilla::hal::FMRadioCountry>:
+  public EnumSerializer<mozilla::hal::FMRadioCountry,
+                        mozilla::hal::FM_RADIO_COUNTRY_UNKNOWN,
+                        mozilla::hal::NUM_FM_RADIO_COUNTRY>
+{};
 } // namespace IPC
 
 #endif // mozilla_hal_Types_h
--- a/hal/Makefile.in
+++ b/hal/Makefile.in
@@ -52,16 +52,17 @@ CPPSRCS += \
   $(NULL)
 else ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
 CPPSRCS += \
   GonkHal.cpp \
   LinuxPower.cpp \
   GonkSensor.cpp \
   UeventPoller.cpp \
   GonkSwitch.cpp \
+  GonkFMRadio.cpp \
   $(NULL)
 else ifeq (Linux,$(OS_TARGET))
 CPPSRCS += \
   LinuxPower.cpp \
   FallbackScreenConfiguration.cpp \
   FallbackSensor.cpp \
   FallbackVibration.cpp \
   FallbackAlarm.cpp \
@@ -120,16 +121,17 @@ endif
 ifneq (gonk,$(MOZ_WIDGET_TOOLKIT)) #{
 CPPSRCS += \
   FallbackLights.cpp  \
   FallbackTime.cpp \
   FallbackWakeLocks.cpp \
   FallbackSwitch.cpp \
   FallbackScreenPower.cpp \
   FallbackProcessPriority.cpp \
+  FallbackFMRadio.cpp \
   $(NULL)
 endif #}
 
 # Fallbacks for backends implemented on Android only.
 ifneq (android,$(MOZ_WIDGET_TOOLKIT))
 CPPSRCS += FallbackNetwork.cpp
 endif
 
new file mode 100644
--- /dev/null
+++ b/hal/fallback/FallbackFMRadio.cpp
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et ft=cpp : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Hal.h"
+
+namespace mozilla {
+namespace hal_impl {
+
+void
+EnableFMRadio(const hal::FMRadioSettings& aInfo)
+{}
+
+void
+DisableFMRadio()
+{}
+
+void
+FMRadioSeek(const hal::FMRadioSeekDirection& aDirection)
+{}
+
+void
+GetFMRadioSettings(hal::FMRadioSettings* aInfo)
+{
+  aInfo->country() = hal::FM_RADIO_COUNTRY_UNKNOWN;
+  aInfo->upperLimit() = 0;
+  aInfo->lowerLimit() = 0;
+  aInfo->spaceType() = 0;
+  aInfo->preEmphasis() = 0;
+}
+
+void
+SetFMRadioFrequency(const uint32_t frequency)
+{}
+
+uint32_t
+GetFMRadioFrequency()
+{
+  return 0;
+}
+
+bool
+IsFMRadioOn()
+{
+  return false;
+}
+
+uint32_t
+GetFMRadioSignalStrength()
+{
+  return 0;
+}
+
+void
+CancelFMRadioSeek()
+{}
+
+} // hal_impl
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/hal/gonk/GonkFMRadio.cpp
@@ -0,0 +1,324 @@
+/* Copyright 2012 Mozilla Foundation and Mozilla contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Hal.h"
+#include "tavarua.h"
+#include "nsThreadUtils.h"
+#include "mozilla/FileUtils.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/videodev2.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+namespace mozilla {
+namespace hal_impl {
+
+uint32_t GetFMRadioFrequency();
+
+static int sRadioFD;
+static bool sRadioEnabled;
+static pthread_t sMonitorThread;
+
+static int
+setControl(uint32_t id, int32_t value)
+{
+  struct v4l2_control control;
+  control.id = id;
+  control.value = value;
+  return ioctl(sRadioFD, VIDIOC_S_CTRL, &control);
+}
+
+class RadioUpdate : public nsRunnable {
+  hal::FMRadioOperation mOp;
+public:
+  RadioUpdate(hal::FMRadioOperation op)
+    : mOp(op)
+  {}
+
+  NS_IMETHOD Run() {
+    hal::FMRadioOperationInformation info;
+    info.operation() = mOp;
+    info.status() = hal::FM_RADIO_OPERATION_STATUS_SUCCESS;
+    info.frequency() = GetFMRadioFrequency();
+    hal::NotifyFMRadioStatus(info);
+    return NS_OK;
+  }
+};
+
+static void *
+pollTavaruaRadio(void *)
+{
+  uint8_t buf[128];
+  struct v4l2_buffer buffer = {0};
+  buffer.index = 1;
+  buffer.type = V4L2_BUF_TYPE_PRIVATE;
+  buffer.length = sizeof(buf);
+  buffer.m.userptr = (long unsigned int)buf;
+
+  while (sRadioEnabled) {
+    int rc = ioctl(sRadioFD, VIDIOC_DQBUF, &buffer);
+    if (rc)
+      return NULL;
+
+    for (unsigned int i = 0; i < buffer.bytesused; i++) {
+      switch (buf[i]) {
+      case TAVARUA_EVT_RADIO_READY:
+        NS_DispatchToMainThread(new RadioUpdate(hal::FM_RADIO_OPERATION_ENABLE));
+        break;
+      case TAVARUA_EVT_SEEK_COMPLETE:
+        NS_DispatchToMainThread(new RadioUpdate(hal::FM_RADIO_OPERATION_SEEK));
+        break;
+      default:
+        break;
+      }
+    }
+  }
+
+  return NULL;
+}
+
+void
+EnableFMRadio(const hal::FMRadioSettings& aInfo)
+{
+  if (sRadioEnabled) {
+    HAL_LOG(("Radio already enabled!"));
+    return;
+  }
+
+  mozilla::ScopedClose fd(open("/dev/radio0", O_RDWR));
+  if (fd < 0) {
+    HAL_LOG(("Unable to open radio device"));
+    return;
+  }
+
+  struct v4l2_capability cap;
+  int rc = ioctl(fd, VIDIOC_QUERYCAP, &cap);
+  if (rc < 0) {
+    HAL_LOG(("Unable to query radio device"));
+    return;
+  }
+
+  HAL_LOG(("Radio: %s (%s)\n", cap.driver, cap.card));
+
+  if (!(cap.capabilities & V4L2_CAP_RADIO)) {
+    HAL_LOG(("/dev/radio0 isn't a radio"));
+    return;
+  }
+
+  if (!(cap.capabilities & V4L2_CAP_TUNER)) {
+    HAL_LOG(("/dev/radio0 doesn't support the tuner interface"));
+    return;
+  }
+  sRadioFD = fd;
+
+  // Tavarua specific start
+  char command[64];
+  snprintf(command, sizeof(command), "/system/bin/fm_qsoc_patches %d 0", cap.version);
+  rc = system(command);
+  if (rc) {
+    HAL_LOG(("Unable to initialize radio"));
+    return;
+  }
+
+  rc = setControl(V4L2_CID_PRIVATE_TAVARUA_STATE, FM_RECV);
+  if (rc < 0) {
+    HAL_LOG(("Unable to turn on radio |%s|", strerror(errno)));
+    return;
+  }
+
+  int preEmphasis = aInfo.preEmphasis() <= 50;
+  rc = setControl(V4L2_CID_PRIVATE_TAVARUA_EMPHASIS, preEmphasis);
+  if (rc) {
+    HAL_LOG(("Unable to configure preemphasis"));
+    return;
+  }
+
+  rc = setControl(V4L2_CID_PRIVATE_TAVARUA_RDS_STD, 0);
+  if (rc) {
+    HAL_LOG(("Unable to configure RDS"));
+    return;
+  }
+
+  int spacing;
+  switch (aInfo.spaceType()) {
+  case 50:
+    spacing = FM_CH_SPACE_50KHZ;
+    break;
+  case 100:
+    spacing = FM_CH_SPACE_100KHZ;
+    break;
+  case 200:
+    spacing = FM_CH_SPACE_200KHZ;
+    break;
+  default:
+    HAL_LOG(("Unsupported space value - %d", aInfo.spaceType()));
+    return;
+  }
+
+  rc = setControl(V4L2_CID_PRIVATE_TAVARUA_SPACING, spacing);
+  if (rc) {
+    HAL_LOG(("Unable to configure spacing"));
+    return;
+  }
+
+  /*
+   * Frequency conversions
+   *
+   * HAL uses units of 1k for frequencies
+   * V4L2 uses units of 62.5kHz
+   * Multiplying by (10000 / 625) converts from HAL units to V4L2.
+   */
+
+  struct v4l2_tuner tuner = {0};
+  tuner.rangelow = (aInfo.lowerLimit() * 10000) / 625;
+  tuner.rangehigh = (aInfo.upperLimit() * 10000) / 625;
+  rc = ioctl(fd, VIDIOC_S_TUNER, &tuner);
+  if (rc < 0) {
+    HAL_LOG(("Unable to adjust band limits"));
+    return;
+  }
+
+  rc = setControl(V4L2_CID_PRIVATE_TAVARUA_REGION, TAVARUA_REGION_OTHER);
+  if (rc < 0) {
+    HAL_LOG(("Unable to configure region"));
+    return;
+  }
+
+  rc = setControl(V4L2_CID_PRIVATE_TAVARUA_SET_AUDIO_PATH, FM_DIGITAL_PATH);
+  if (rc < 0) {
+    HAL_LOG(("Unable to set audio path"));
+    return;
+  }
+
+  pthread_create(&sMonitorThread, NULL, pollTavaruaRadio, NULL);
+  // Tavarua specific end
+
+  fd.forget();
+  sRadioEnabled = true;
+}
+
+void
+DisableFMRadio()
+{
+  if (!sRadioEnabled)
+    return;
+
+  sRadioEnabled = false;
+
+  // Tavarua specific start
+  int rc = setControl(V4L2_CID_PRIVATE_TAVARUA_STATE, FM_OFF);
+  if (rc < 0) {
+    HAL_LOG(("Unable to turn off radio"));
+  }
+  // Tavarua specific end
+
+  pthread_join(sMonitorThread, NULL);
+
+  close(sRadioFD);
+
+  hal::FMRadioOperationInformation info;
+  info.operation() = hal::FM_RADIO_OPERATION_DISABLE;
+  info.status() = hal::FM_RADIO_OPERATION_STATUS_SUCCESS;
+  hal::NotifyFMRadioStatus(info);
+}
+
+void
+FMRadioSeek(const hal::FMRadioSeekDirection& aDirection)
+{
+  struct v4l2_hw_freq_seek seek = {0};
+  seek.type = V4L2_TUNER_RADIO;
+  seek.seek_upward = aDirection == hal::FMRadioSeekDirection::FM_RADIO_SEEK_DIRECTION_UP;
+  int rc = ioctl(sRadioFD, VIDIOC_S_HW_FREQ_SEEK, &seek);
+  if (rc < 0) {
+    HAL_LOG(("Could not initiate hardware seek"));
+    return;
+  }
+}
+
+void
+GetFMRadioSettings(hal::FMRadioSettings* aInfo)
+{
+  if (!sRadioEnabled) {
+    return;
+  }
+
+  struct v4l2_tuner tuner = {0};
+  int rc = ioctl(sRadioFD, VIDIOC_G_TUNER, &tuner);
+  if (rc < 0) {
+    HAL_LOG(("Could not query fm radio for settings"));
+    return;
+  }
+
+  aInfo->upperLimit() = (tuner.rangehigh * 625) / 10000;
+  aInfo->lowerLimit() = (tuner.rangelow * 625) / 10000;
+}
+
+void
+SetFMRadioFrequency(const uint32_t frequency)
+{
+  struct v4l2_frequency freq = {0};
+  freq.type = V4L2_TUNER_RADIO;
+  freq.frequency = (frequency * 10000) / 625;
+
+  int rc = ioctl(sRadioFD, VIDIOC_S_FREQUENCY, &freq);
+  if (rc < 0)
+    HAL_LOG(("Could not set radio frequency"));
+}
+
+uint32_t
+GetFMRadioFrequency()
+{
+  if (!sRadioEnabled)
+    return 0;
+
+  struct v4l2_frequency freq;
+  int rc = ioctl(sRadioFD, VIDIOC_G_FREQUENCY, &freq);
+  if (rc < 0) {
+    HAL_LOG(("Could not get radio frequency"));
+    return 0;
+  }
+
+  return (freq.frequency * 625) / 10000;
+}
+
+bool
+IsFMRadioOn()
+{
+  return sRadioEnabled;
+}
+
+uint32_t
+GetFMRadioSignalStrength()
+{
+  struct v4l2_tuner tuner = {0};
+  int rc = ioctl(sRadioFD, VIDIOC_G_TUNER, &tuner);
+  if (rc < 0) {
+    HAL_LOG(("Could not query fm radio for signal strength"));
+    return 0;
+  }
+
+  return tuner.signal;
+}
+
+void
+CancelFMRadioSeek()
+{}
+
+} // hal_impl
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/hal/gonk/tavarua.h
@@ -0,0 +1,484 @@
+#ifndef __LINUX_TAVARUA_H
+#define __LINUX_TAVARUA_H
+
+/* This is a Linux header generated by "make headers_install" */
+
+#include <stdint.h>
+#include <linux/ioctl.h>
+#include <linux/videodev2.h>
+
+
+#undef FM_DEBUG
+
+/* constants */
+#define  RDS_BLOCKS_NUM             (4)
+#define BYTES_PER_BLOCK             (3)
+#define MAX_PS_LENGTH              (96)
+#define MAX_RT_LENGTH              (64)
+
+#define XFRDAT0                    (0x20)
+#define XFRDAT1                    (0x21)
+#define XFRDAT2                    (0x22)
+
+#define INTDET_PEEK_MSB            (0x88)
+#define INTDET_PEEK_LSB            (0x26)
+
+#define RMSSI_PEEK_MSB             (0x88)
+#define RMSSI_PEEK_LSB             (0xA8)
+
+#define MPX_DCC_BYPASS_POKE_MSB    (0x88)
+#define MPX_DCC_BYPASS_POKE_LSB    (0xC0)
+
+#define MPX_DCC_PEEK_MSB_REG1      (0x88)
+#define MPX_DCC_PEEK_LSB_REG1      (0xC2)
+
+#define MPX_DCC_PEEK_MSB_REG2      (0x88)
+#define MPX_DCC_PEEK_LSB_REG2      (0xC3)
+
+#define MPX_DCC_PEEK_MSB_REG3      (0x88)
+#define MPX_DCC_PEEK_LSB_REG3      (0xC4)
+
+#define ON_CHANNEL_TH_MSB          (0x0B)
+#define ON_CHANNEL_TH_LSB          (0xA8)
+
+#define OFF_CHANNEL_TH_MSB         (0x0B)
+#define OFF_CHANNEL_TH_LSB         (0xAC)
+
+#define ENF_200Khz                    (1)
+#define SRCH200KHZ_OFFSET             (7)
+#define SRCH_MASK                  (1 << SRCH200KHZ_OFFSET)
+
+/* Standard buffer size */
+#define STD_BUF_SIZE               (128)
+/* Search direction */
+#define SRCH_DIR_UP                 (0)
+#define SRCH_DIR_DOWN               (1)
+
+/* control options */
+#define CTRL_ON                     (1)
+#define CTRL_OFF                    (0)
+
+#define US_LOW_BAND                (87.5)
+#define US_HIGH_BAND               (108)
+
+/* constant for Tx */
+
+#define MASK_PI                    (0x0000FFFF)
+#define MASK_PI_MSB                (0x0000FF00)
+#define MASK_PI_LSB                (0x000000FF)
+#define MASK_PTY                   (0x0000001F)
+#define MASK_TXREPCOUNT            (0x0000000F)
+
+#undef FMDBG
+#ifdef FM_DEBUG
+  #define FMDBG(fmt, args...) printk(KERN_INFO "tavarua_radio: " fmt, ##args)
+#else
+  #define FMDBG(fmt, args...)
+#endif
+
+#undef FMDERR
+#define FMDERR(fmt, args...) printk(KERN_INFO "tavarua_radio: " fmt, ##args)
+
+#undef FMDBG_I2C
+#ifdef FM_DEBUG_I2C
+  #define FMDBG_I2C(fmt, args...) printk(KERN_INFO "fm_i2c: " fmt, ##args)
+#else
+  #define FMDBG_I2C(fmt, args...)
+#endif
+
+/* function declarations */
+/* FM Core audio paths. */
+#define TAVARUA_AUDIO_OUT_ANALOG_OFF	(0)
+#define TAVARUA_AUDIO_OUT_ANALOG_ON	(1)
+#define TAVARUA_AUDIO_OUT_DIGITAL_OFF	(0)
+#define TAVARUA_AUDIO_OUT_DIGITAL_ON	(1)
+
+int tavarua_set_audio_path(int digital_on, int analog_on);
+
+/* defines and enums*/
+
+#define MARIMBA_A0	0x01010013
+#define MARIMBA_2_1	0x02010204
+#define BAHAMA_1_0	0x0302010A
+#define BAHAMA_2_0	0x04020205
+#define WAIT_TIMEOUT 2000
+#define RADIO_INIT_TIME 15
+#define TAVARUA_DELAY 10
+/*
+ * The frequency is set in units of 62.5 Hz when using V4L2_TUNER_CAP_LOW,
+ * 62.5 kHz otherwise.
+ * The tuner is able to have a channel spacing of 50, 100 or 200 kHz.
+ * tuner->capability is therefore set to V4L2_TUNER_CAP_LOW
+ * The FREQ_MUL is then: 1 MHz / 62.5 Hz = 16000
+ */
+#define FREQ_MUL (1000000 / 62.5)
+
+enum v4l2_cid_private_tavarua_t {
+	V4L2_CID_PRIVATE_TAVARUA_SRCHMODE = (V4L2_CID_PRIVATE_BASE + 1),
+	V4L2_CID_PRIVATE_TAVARUA_SCANDWELL,
+	V4L2_CID_PRIVATE_TAVARUA_SRCHON,
+	V4L2_CID_PRIVATE_TAVARUA_STATE,
+	V4L2_CID_PRIVATE_TAVARUA_TRANSMIT_MODE,
+	V4L2_CID_PRIVATE_TAVARUA_RDSGROUP_MASK,
+	V4L2_CID_PRIVATE_TAVARUA_REGION,
+	V4L2_CID_PRIVATE_TAVARUA_SIGNAL_TH,
+	V4L2_CID_PRIVATE_TAVARUA_SRCH_PTY,
+	V4L2_CID_PRIVATE_TAVARUA_SRCH_PI,
+	V4L2_CID_PRIVATE_TAVARUA_SRCH_CNT,
+	V4L2_CID_PRIVATE_TAVARUA_EMPHASIS,
+	V4L2_CID_PRIVATE_TAVARUA_RDS_STD,
+	V4L2_CID_PRIVATE_TAVARUA_SPACING,
+	V4L2_CID_PRIVATE_TAVARUA_RDSON,
+	V4L2_CID_PRIVATE_TAVARUA_RDSGROUP_PROC,
+	V4L2_CID_PRIVATE_TAVARUA_LP_MODE,
+	V4L2_CID_PRIVATE_TAVARUA_ANTENNA,
+	V4L2_CID_PRIVATE_TAVARUA_RDSD_BUF,
+	V4L2_CID_PRIVATE_TAVARUA_PSALL,
+	/*v4l2 Tx controls*/
+	V4L2_CID_PRIVATE_TAVARUA_TX_SETPSREPEATCOUNT,
+	V4L2_CID_PRIVATE_TAVARUA_STOP_RDS_TX_PS_NAME,
+	V4L2_CID_PRIVATE_TAVARUA_STOP_RDS_TX_RT,
+	V4L2_CID_PRIVATE_TAVARUA_IOVERC,
+	V4L2_CID_PRIVATE_TAVARUA_INTDET,
+	V4L2_CID_PRIVATE_TAVARUA_MPX_DCC,
+	V4L2_CID_PRIVATE_TAVARUA_AF_JUMP,
+	V4L2_CID_PRIVATE_TAVARUA_RSSI_DELTA,
+	V4L2_CID_PRIVATE_TAVARUA_HLSI,
+
+	/*
+	* Here we have IOCTl's that are specific to IRIS
+	* (V4L2_CID_PRIVATE_BASE + 0x1E to V4L2_CID_PRIVATE_BASE + 0x28)
+	*/
+	V4L2_CID_PRIVATE_SOFT_MUTE,/* 0x800001E*/
+	V4L2_CID_PRIVATE_RIVA_ACCS_ADDR,
+	V4L2_CID_PRIVATE_RIVA_ACCS_LEN,
+	V4L2_CID_PRIVATE_RIVA_PEEK,
+	V4L2_CID_PRIVATE_RIVA_POKE,
+	V4L2_CID_PRIVATE_SSBI_ACCS_ADDR,
+	V4L2_CID_PRIVATE_SSBI_PEEK,
+	V4L2_CID_PRIVATE_SSBI_POKE,
+	V4L2_CID_PRIVATE_TX_TONE,
+	V4L2_CID_PRIVATE_RDS_GRP_COUNTERS,
+	V4L2_CID_PRIVATE_SET_NOTCH_FILTER,/* 0x8000028 */
+
+	V4L2_CID_PRIVATE_TAVARUA_SET_AUDIO_PATH,/* 0x8000029 */
+	V4L2_CID_PRIVATE_TAVARUA_DO_CALIBRATION,/* 0x800002A : IRIS */
+	V4L2_CID_PRIVATE_TAVARUA_SRCH_ALGORITHM,/* 0x800002B */
+	V4L2_CID_PRIVATE_IRIS_GET_SINR, /* 0x800002C : IRIS */
+	V4L2_CID_PRIVATE_INTF_LOW_THRESHOLD, /* 0x800002D */
+	V4L2_CID_PRIVATE_INTF_HIGH_THRESHOLD, /* 0x800002E */
+	V4L2_CID_PRIVATE_SINR_THRESHOLD,  /* 0x800002F : IRIS */
+	V4L2_CID_PRIVATE_SINR_SAMPLES,  /* 0x8000030 : IRIS */
+
+};
+
+enum tavarua_buf_t {
+	TAVARUA_BUF_SRCH_LIST,
+	TAVARUA_BUF_EVENTS,
+	TAVARUA_BUF_RT_RDS,
+	TAVARUA_BUF_PS_RDS,
+	TAVARUA_BUF_RAW_RDS,
+	TAVARUA_BUF_AF_LIST,
+	TAVARUA_BUF_MAX
+};
+
+enum tavarua_xfr_t {
+	TAVARUA_XFR_SYNC,
+	TAVARUA_XFR_ERROR,
+	TAVARUA_XFR_SRCH_LIST,
+	TAVARUA_XFR_RT_RDS,
+	TAVARUA_XFR_PS_RDS,
+	TAVARUA_XFR_AF_LIST,
+	TAVARUA_XFR_MAX
+};
+
+enum channel_spacing {
+	FM_CH_SPACE_200KHZ,
+	FM_CH_SPACE_100KHZ,
+	FM_CH_SPACE_50KHZ
+};
+
+enum step_size {
+	NO_SRCH200khz,
+	ENF_SRCH200khz
+};
+
+enum emphasis {
+	EMP_75,
+	EMP_50
+};
+
+enum rds_std {
+	RBDS_STD,
+	RDS_STD
+};
+
+/* offsets */
+#define RAW_RDS		0x0F
+#define RDS_BLOCK 	3
+
+/* registers*/
+#define MARIMBA_XO_BUFF_CNTRL 0x07
+#define RADIO_REGISTERS 0x30
+#define XFR_REG_NUM     16
+#define STATUS_REG_NUM 	3
+
+/* TX constants */
+#define HEADER_SIZE	4
+#define TX_ON		0x80
+#define TAVARUA_TX_RT	RDS_RT_0
+#define TAVARUA_TX_PS	RDS_PS_0
+
+enum register_t {
+	STATUS_REG1 = 0,
+	STATUS_REG2,
+	STATUS_REG3,
+	RDCTRL,
+	FREQ,
+	TUNECTRL,
+	SRCHRDS1,
+	SRCHRDS2,
+	SRCHCTRL,
+	IOCTRL,
+	RDSCTRL,
+	ADVCTRL,
+	AUDIOCTRL,
+	RMSSI,
+	IOVERC,
+	AUDIOIND = 0x1E,
+	XFRCTRL,
+	FM_CTL0 = 0xFF,
+	LEAKAGE_CNTRL = 0xFE,
+};
+#define BAHAMA_RBIAS_CTL1       0x07
+#define	BAHAMA_FM_MODE_REG      0xFD
+#define	BAHAMA_FM_CTL1_REG      0xFE
+#define	BAHAMA_FM_CTL0_REG      0xFF
+#define BAHAMA_FM_MODE_NORMAL   0x00
+#define BAHAMA_LDO_DREG_CTL0    0xF0
+#define BAHAMA_LDO_AREG_CTL0    0xF4
+
+/* Radio Control */
+#define RDCTRL_STATE_OFFSET	0
+#define RDCTRL_STATE_MASK	(3 << RDCTRL_STATE_OFFSET)
+#define RDCTRL_BAND_OFFSET	2
+#define RDCTRL_BAND_MASK	(1 << RDCTRL_BAND_OFFSET)
+#define RDCTRL_CHSPACE_OFFSET	3
+#define RDCTRL_CHSPACE_MASK	(3 << RDCTRL_CHSPACE_OFFSET)
+#define RDCTRL_DEEMPHASIS_OFFSET 5
+#define RDCTRL_DEEMPHASIS_MASK	(1 << RDCTRL_DEEMPHASIS_OFFSET)
+#define RDCTRL_HLSI_OFFSET	6
+#define RDCTRL_HLSI_MASK	(3 << RDCTRL_HLSI_OFFSET)
+#define RDSAF_OFFSET		6
+#define RDSAF_MASK		(1 << RDSAF_OFFSET)
+
+/* Tune Control */
+#define TUNE_STATION	0x01
+#define ADD_OFFSET	(1 << 1)
+#define SIGSTATE	(1 << 5)
+#define MOSTSTATE	(1 << 6)
+#define RDSSYNC		(1 << 7)
+/* Search Control */
+#define SRCH_MODE_OFFSET	0
+#define SRCH_MODE_MASK		(7 << SRCH_MODE_OFFSET)
+#define SRCH_DIR_OFFSET		3
+#define SRCH_DIR_MASK		(1 << SRCH_DIR_OFFSET)
+#define SRCH_DWELL_OFFSET	4
+#define SRCH_DWELL_MASK		(7 << SRCH_DWELL_OFFSET)
+#define SRCH_STATE_OFFSET	7
+#define SRCH_STATE_MASK		(1 << SRCH_STATE_OFFSET)
+
+/* I/O Control */
+#define IOC_HRD_MUTE	0x03
+#define IOC_SFT_MUTE    (1 << 2)
+#define IOC_MON_STR     (1 << 3)
+#define IOC_SIG_BLND    (1 << 4)
+#define IOC_INTF_BLND   (1 << 5)
+#define IOC_ANTENNA     (1 << 6)
+#define IOC_ANTENNA_OFFSET	6
+#define IOC_ANTENNA_MASK     	(1 << IOC_ANTENNA_OFFSET)
+
+/* RDS Control */
+#define RDS_ON		0x01
+#define RDSCTRL_STANDARD_OFFSET 1
+#define RDSCTRL_STANDARD_MASK	(1 << RDSCTRL_STANDARD_OFFSET)
+
+/* Advanced features controls */
+#define RDSRTEN		(1 << 3)
+#define RDSPSEN		(1 << 4)
+
+/* Audio path control */
+#define AUDIORX_ANALOG_OFFSET 	0
+#define AUDIORX_ANALOG_MASK 	(1 << AUDIORX_ANALOG_OFFSET)
+#define AUDIORX_DIGITAL_OFFSET 	1
+#define AUDIORX_DIGITAL_MASK 	(1 << AUDIORX_DIGITAL_OFFSET)
+#define AUDIOTX_OFFSET		2
+#define AUDIOTX_MASK		(1 << AUDIOTX_OFFSET)
+#define I2SCTRL_OFFSET		3
+#define I2SCTRL_MASK		(1 << I2SCTRL_OFFSET)
+
+/* Search options */
+enum search_t {
+	SEEK,
+	SCAN,
+	SCAN_FOR_STRONG,
+	SCAN_FOR_WEAK,
+	RDS_SEEK_PTY,
+	RDS_SCAN_PTY,
+	RDS_SEEK_PI,
+	RDS_AF_JUMP,
+};
+
+enum audio_path {
+	FM_DIGITAL_PATH,
+	FM_ANALOG_PATH
+};
+#define SRCH_MODE	0x07
+#define SRCH_DIR	0x08 /* 0-up 1-down */
+#define SCAN_DWELL	0x70
+#define SRCH_ON		0x80
+
+/* RDS CONFIG */
+#define RDS_CONFIG_PSALL 0x01
+
+#define FM_ENABLE	0x22
+#define SET_REG_FIELD(reg, val, offset, mask) \
+	(reg = (reg & ~mask) | (((val) << offset) & mask))
+#define GET_REG_FIELD(reg, offset, mask) ((reg & mask) >> offset)
+#define RSH_DATA(val, offset)    ((val) >> (offset))
+#define LSH_DATA(val, offset)    ((val) << (offset))
+#define GET_ABS_VAL(val)        ((val) & (0xFF))
+
+enum radio_state_t {
+	FM_OFF,
+	FM_RECV,
+	FM_TRANS,
+	FM_RESET,
+};
+
+#define XFRCTRL_WRITE   (1 << 7)
+
+/* Interrupt status */
+
+/* interrupt register 1 */
+#define	READY		(1 << 0) /* Radio ready after powerup or reset */
+#define	TUNE		(1 << 1) /* Tune completed */
+#define	SEARCH		(1 << 2) /* Search completed (read FREQ) */
+#define	SCANNEXT	(1 << 3) /* Scanning for next station */
+#define	SIGNAL		(1 << 4) /* Signal indicator change (read SIGSTATE) */
+#define	INTF		(1 << 5) /* Interference cnt has fallen outside range */
+#define	SYNC		(1 << 6) /* RDS sync state change (read RDSSYNC) */
+#define	AUDIO		(1 << 7) /* Audio Control indicator (read AUDIOIND) */
+
+/* interrupt register 2 */
+#define	RDSDAT		(1 << 0) /* New unread RDS data group available */
+#define	BLOCKB		(1 << 1) /* Block-B match condition exists */
+#define	PROGID		(1 << 2) /* Block-A or Block-C matched stored PI value*/
+#define	RDSPS		(1 << 3) /* New RDS Program Service Table available */
+#define	RDSRT		(1 << 4) /* New RDS Radio Text available */
+#define	RDSAF		(1 << 5) /* New RDS AF List available */
+#define	TXRDSDAT	(1 << 6) /* Transmitted an RDS group */
+#define	TXRDSDONE	(1 << 7) /* RDS raw group one-shot transmit completed */
+
+/* interrupt register 3 */
+#define	TRANSFER	(1 << 0) /* Data transfer (XFR) completed */
+#define	RDSPROC		(1 << 1) /* Dynamic RDS Processing complete */
+#define	ERROR		(1 << 7) /* Err occurred.Read code to determine cause */
+
+
+#define	FM_TX_PWR_LVL_0		0 /* Lowest power lvl that can be set for Tx */
+#define	FM_TX_PWR_LVL_MAX	7 /* Max power lvl for Tx */
+/* Transfer */
+enum tavarua_xfr_ctrl_t {
+	RDS_PS_0 = 0x01,
+	RDS_PS_1,
+	RDS_PS_2,
+	RDS_PS_3,
+	RDS_PS_4,
+	RDS_PS_5,
+	RDS_PS_6,
+	RDS_RT_0,
+	RDS_RT_1,
+	RDS_RT_2,
+	RDS_RT_3,
+	RDS_RT_4,
+	RDS_AF_0,
+	RDS_AF_1,
+	RDS_CONFIG,
+	RDS_TX_GROUPS,
+	RDS_COUNT_0,
+	RDS_COUNT_1,
+	RDS_COUNT_2,
+	RADIO_CONFIG,
+	RX_CONFIG,
+	RX_TIMERS,
+	RX_STATIONS_0,
+	RX_STATIONS_1,
+	INT_CTRL,
+	ERROR_CODE,
+	CHIPID,
+	CAL_DAT_0 = 0x20,
+	CAL_DAT_1,
+	CAL_DAT_2,
+	CAL_DAT_3,
+	CAL_CFG_0,
+	CAL_CFG_1,
+	DIG_INTF_0,
+	DIG_INTF_1,
+	DIG_AGC_0,
+	DIG_AGC_1,
+	DIG_AGC_2,
+	DIG_AUDIO_0,
+	DIG_AUDIO_1,
+	DIG_AUDIO_2,
+	DIG_AUDIO_3,
+	DIG_AUDIO_4,
+	DIG_RXRDS,
+	DIG_DCC,
+	DIG_SPUR,
+	DIG_MPXDCC,
+	DIG_PILOT,
+	DIG_DEMOD,
+	DIG_MOST,
+	DIG_TX_0,
+	DIG_TX_1,
+	PHY_TXGAIN = 0x3B,
+	PHY_CONFIG,
+	PHY_TXBLOCK,
+	PHY_TCB,
+	XFR_PEEK_MODE = 0x40,
+	XFR_POKE_MODE = 0xC0,
+	TAVARUA_XFR_CTRL_MAX
+};
+
+enum tavarua_evt_t {
+	TAVARUA_EVT_RADIO_READY,
+	TAVARUA_EVT_TUNE_SUCC,
+	TAVARUA_EVT_SEEK_COMPLETE,
+	TAVARUA_EVT_SCAN_NEXT,
+	TAVARUA_EVT_NEW_RAW_RDS,
+	TAVARUA_EVT_NEW_RT_RDS,
+	TAVARUA_EVT_NEW_PS_RDS,
+	TAVARUA_EVT_ERROR,
+	TAVARUA_EVT_BELOW_TH,
+	TAVARUA_EVT_ABOVE_TH,
+	TAVARUA_EVT_STEREO,
+	TAVARUA_EVT_MONO,
+	TAVARUA_EVT_RDS_AVAIL,
+	TAVARUA_EVT_RDS_NOT_AVAIL,
+	TAVARUA_EVT_NEW_SRCH_LIST,
+	TAVARUA_EVT_NEW_AF_LIST,
+	TAVARUA_EVT_TXRDSDAT,
+	TAVARUA_EVT_TXRDSDONE,
+	TAVARUA_EVT_RADIO_DISABLED
+};
+
+enum tavarua_region_t {
+	TAVARUA_REGION_US,
+	TAVARUA_REGION_EU,
+	TAVARUA_REGION_JAPAN,
+	TAVARUA_REGION_JAPAN_WIDE,
+	TAVARUA_REGION_OTHER
+};
+
+#endif /* __LINUX_TAVARUA_H */
--- a/hal/sandbox/PHal.ipdl
+++ b/hal/sandbox/PHal.ipdl
@@ -20,16 +20,20 @@ using mozilla::hal::SensorType;
 using mozilla::hal::SensorAccuracyType;
 using mozilla::hal::WakeLockControl;
 using mozilla::hal::SwitchState;
 using mozilla::hal::SwitchDevice;
 using mozilla::hal::ProcessPriority;
 using nsIntRect;
 using PRTime;
 using mozilla::hal::SystemTimeChange;
+using mozilla::hal::FMRadioCountry;
+using mozilla::hal::FMRadioOperation;
+using mozilla::hal::FMRadioOperationStatus;
+using mozilla::hal::FMRadioSeekDirection;
 
 namespace mozilla {
 
 namespace hal {
 struct BatteryInformation {
   double level;
   bool   charging;
   double remainingTime;
@@ -69,30 +73,45 @@ struct WakeLockInformation {
 
 struct ScreenConfiguration {
   nsIntRect rect;
   ScreenOrientation orientation;
   uint32_t colorDepth;
   uint32_t pixelDepth;
 };
 
+struct FMRadioOperationInformation {
+  FMRadioOperation operation;
+  FMRadioOperationStatus status;
+  uint32_t frequency;
+};
+
+struct FMRadioSettings {
+  FMRadioCountry country;
+  uint32_t upperLimit;
+  uint32_t lowerLimit;
+  uint32_t spaceType;
+  uint32_t preEmphasis;
+};
+
 } // namespace hal
 
 namespace hal_sandbox {
 
 sync protocol PHal {
     manager PContent;
 
 child:
     NotifyBatteryChange(BatteryInformation aBatteryInfo);
     NotifyNetworkChange(NetworkInformation aNetworkInfo);
     NotifyWakeLockChange(WakeLockInformation aWakeLockInfo);
     NotifyScreenConfigurationChange(ScreenConfiguration aScreenOrientation);
     NotifySwitchChange(SwitchEvent aEvent);
     NotifySystemTimeChange(SystemTimeChange aReason); 
+    NotifyFMRadioStatus(FMRadioOperationInformation aInfo);
 
 parent:
     Vibrate(uint32_t[] pattern, uint64_t[] id, PBrowser browser);
     CancelVibrate(uint64_t[] id, PBrowser browser);
 
     EnableBatteryNotifications();
     DisableBatteryNotifications();
     sync GetCurrentBatteryInformation()
@@ -141,20 +160,34 @@ parent:
  
     EnableSwitchNotifications(SwitchDevice aDevice);
     DisableSwitchNotifications(SwitchDevice aDevice);
     sync GetCurrentSwitchState(SwitchDevice aDevice)
       returns (SwitchState aState);
 
     SetProcessPriority(int aPid, ProcessPriority aPriority);
 
+    EnableFMRadio(FMRadioSettings aSettings);
+    DisableFMRadio();
+    FMRadioSeek(FMRadioSeekDirection aDirection);
+    sync GetFMRadioSettings()
+      returns (FMRadioSettings settings);
+    SetFMRadioFrequency(uint32_t frequency);
+    sync GetFMRadioFrequency()
+      returns (uint32_t frequency);
+    sync IsFMRadioOn()
+      returns (bool radioOn);
+    sync GetFMRadioSignalStrength()
+      returns (uint32_t strength);
+    CancelFMRadioSeek();
+
 child:
     NotifySensorChange(SensorData aSensorData);
 
-parent:    
+parent:
     EnableSensorNotifications(SensorType aSensor);
     DisableSensorNotifications(SensorType aSensor);
 
     __delete__();
 };
 
 } // namespace hal
 } // namespace mozilla
--- a/hal/sandbox/SandboxHal.cpp
+++ b/hal/sandbox/SandboxHal.cpp
@@ -290,16 +290,76 @@ SetAlarm(int32_t aSeconds, int32_t aNano
 }
 
 void
 SetProcessPriority(int aPid, ProcessPriority aPriority)
 {
   Hal()->SendSetProcessPriority(aPid, aPriority);
 }
 
+void
+EnableFMRadio(const hal::FMRadioSettings& aSettings)
+{
+  Hal()->SendEnableFMRadio(aSettings);
+}
+
+void
+DisableFMRadio()
+{
+  Hal()->SendDisableFMRadio();
+}
+
+void
+FMRadioSeek(const hal::FMRadioSeekDirection& aDirection)
+{
+  Hal()->SendFMRadioSeek(aDirection);
+}
+
+void
+GetFMRadioSettings(FMRadioSettings* aSettings)
+{
+  Hal()->SendGetFMRadioSettings(aSettings);
+}
+
+void
+SetFMRadioFrequency(const uint32_t aFrequency)
+{
+  Hal()->SendSetFMRadioFrequency(aFrequency);
+}
+
+uint32_t
+GetFMRadioFrequency()
+{
+  uint32_t frequency;
+  Hal()->SendGetFMRadioFrequency(&frequency);
+  return frequency;
+}
+
+bool
+IsFMRadioOn()
+{
+  bool FMRadioOn;
+  Hal()->SendIsFMRadioOn(&FMRadioOn);
+  return FMRadioOn;
+}
+
+uint32_t
+GetFMRadioSignalStrength()
+{
+  uint32_t strength;
+  Hal()->SendGetFMRadioSignalStrength(&strength);
+  return strength;
+}
+
+void
+CancelFMRadioSeek()
+{
+  Hal()->SendCancelFMRadioSeek();
+}
+
 class HalParent : public PHalParent
                 , public BatteryObserver
                 , public NetworkObserver
                 , public ISensorObserver
                 , public WakeLockObserver
                 , public ScreenConfigurationObserver
                 , public SwitchObserver
 {
@@ -660,16 +720,84 @@ public:
     hal::SetProcessPriority(aPid, aPriority);
     return true;
   }
 
   void Notify(const SystemTimeChange& aReason)
   {
     unused << SendNotifySystemTimeChange(aReason);
   }
+
+  virtual bool
+  RecvEnableFMRadio(const hal::FMRadioSettings& aSettings)
+  {
+    hal::EnableFMRadio(aSettings);
+    return true;
+  }
+
+  virtual bool
+  RecvDisableFMRadio()
+  {
+    hal::DisableFMRadio();
+    return true;
+  }
+
+  virtual bool
+  RecvFMRadioSeek(const hal::FMRadioSeekDirection& aDirection)
+  {
+    hal::FMRadioSeek(aDirection);
+    return true;
+  }
+
+  virtual bool
+  RecvGetFMRadioSettings(hal::FMRadioSettings* aSettings)
+  {
+    hal::GetFMRadioSettings(aSettings);
+    return true;
+  }
+
+  virtual bool
+  RecvSetFMRadioFrequency(const uint32_t& aFrequency)
+  {
+    hal::SetFMRadioFrequency(aFrequency);
+    return true;
+  }
+
+  virtual bool
+  RecvGetFMRadioFrequency(uint32_t* aFrequency)
+  {
+    *aFrequency = hal::GetFMRadioFrequency();
+    return true;
+  }
+
+  void Notify(const hal::FMRadioOperationInformation& aRadioStatus)
+  {
+    unused << SendNotifyFMRadioStatus(aRadioStatus);
+  }
+
+  virtual bool
+  RecvIsFMRadioOn(bool* radioOn)
+  {
+    *radioOn = hal::IsFMRadioOn();
+    return true;
+  }
+
+  virtual bool
+  RecvGetFMRadioSignalStrength(uint32_t* strength)
+  {
+    *strength = hal::GetFMRadioSignalStrength();
+    return true;
+  }
+
+  virtual bool
+  RecvCancelFMRadioSeek()
+  {
+    hal::CancelFMRadioSeek();
+    return true;
+  }
 };
 
 class HalChild : public PHalChild {
 public:
   virtual bool
   RecvNotifyBatteryChange(const BatteryInformation& aBatteryInfo) MOZ_OVERRIDE {
     hal::NotifyBatteryChange(aBatteryInfo);
     return true;
@@ -702,16 +830,22 @@ public:
     return true;
   }
 
   virtual bool
   RecvNotifySystemTimeChange(const SystemTimeChange& aReason) {
     hal::NotifySystemTimeChange(aReason);
     return true;
   }
+
+  virtual bool
+  RecvNotifyFMRadioStatus(const FMRadioOperationInformation& aRadioStatus) {
+    hal::NotifyFMRadioStatus(aRadioStatus);
+    return true;
+  }
 };
 
 bool
 HalChild::RecvNotifySensorChange(const hal::SensorData &aSensorData) {
   hal::NotifySensorChange(aSensorData);
   
   return true;
 }
--- a/ipc/chromium/src/base/file_util.cc
+++ b/ipc/chromium/src/base/file_util.cc
@@ -189,17 +189,17 @@ bool ReadFileToString(const FilePath& pa
   CloseFile(file);
 
   return true;
 }
 
 FILE* CreateAndOpenTemporaryFile(FilePath* path) {
   FilePath directory;
   if (!GetTempDir(&directory))
-    return false;
+    return NULL;
 
   return CreateAndOpenTemporaryFileInDir(directory, path);
 }
 
 bool GetFileSize(const FilePath& file_path, int64_t* file_size) {
   FileInfo info;
   if (!GetFileInfo(file_path, &info))
     return false;
--- a/ipc/chromium/src/base/file_util_posix.cc
+++ b/ipc/chromium/src/base/file_util_posix.cc
@@ -387,17 +387,17 @@ bool CreateTemporaryFileName(FilePath* p
     return false;
   close(fd);
   return true;
 }
 
 FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) {
   FilePath directory;
   if (!GetShmemTempDir(&directory))
-    return false;
+    return NULL;
 
   return CreateAndOpenTemporaryFileInDir(directory, path);
 }
 
 FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
   int fd = CreateAndOpenFdForTemporaryFile(dir, path);
   if (fd < 0)
     return NULL;
--- a/ipc/chromium/src/base/pickle.cc
+++ b/ipc/chromium/src/base/pickle.cc
@@ -487,17 +487,17 @@ bool Pickle::WriteData(const char* data,
   return WriteInt(length) && WriteBytes(data, length);
 }
 
 char* Pickle::BeginWriteData(int length) {
   DCHECK_EQ(variable_buffer_offset_, 0U) <<
     "There can only be one variable buffer in a Pickle";
 
   if (!WriteInt(length))
-    return false;
+    return NULL;
 
   char *data_ptr = BeginWrite(length, sizeof(uint32_t));
   if (!data_ptr)
     return NULL;
 
   variable_buffer_offset_ =
       data_ptr - reinterpret_cast<char*>(header_) - sizeof(int);
 
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -1265,21 +1265,23 @@ if test "$GNU_CXX"; then
     # Turn on GNU-specific warnings:
     # -Wall - turn on a lot of warnings
     # -pedantic - this is turned on below
     # -Wpointer-arith - enabled with -pedantic, but good to have even if not
     # -Woverloaded-virtual - ???
     # -Werror=return-type - catches missing returns, zero false positives
     # -Wtype-limits - catches overflow bugs, few false positives
     # -Wempty-body - catches bugs, e.g. "if (c); foo();", few false positives
+    # -Werror=conversion-null - catches conversions between NULL and non-pointer types
     #
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wall -Wpointer-arith -Woverloaded-virtual"
     MOZ_CXX_SUPPORTS_WARNING(-W, error=return-type, ac_cxx_has_werror_return_type)
     MOZ_CXX_SUPPORTS_WARNING(-W, type-limits, ac_cxx_has_wtype_limits)
     MOZ_CXX_SUPPORTS_WARNING(-W, empty-body, ac_cxx_has_wempty_body)
+    MOZ_CXX_SUPPORTS_WARNING(-W, error=conversion-null, ac_cxx_has_werror_conversion_null)
 
     # Turn off the following warnings that -Wall/-pedantic turn on:
     # -Wno-ctor-dtor-privacy - ???
     # -Wno-overlength-strings - we exceed the minimum maximum length frequently
     # -Wno-invalid-offsetof - we use offsetof on non-POD types frequently
     # -Wno-variadic-macros - ???
     #
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-ctor-dtor-privacy"
--- a/js/src/ds/LifoAlloc.cpp
+++ b/js/src/ds/LifoAlloc.cpp
@@ -2,18 +2,16 @@
  * vim: set ts=8 sw=4 et tw=99 ft=cpp:
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "LifoAlloc.h"
 
-#include <new>
-
 using namespace js;
 
 namespace js {
 namespace detail {
 
 BumpChunk *
 BumpChunk::new_(size_t chunkSize)
 {
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -189,18 +189,16 @@ frontend::CompileScript(JSContext *cx, H
         if (!pn)
             return NULL;
 
         if (!FoldConstants(cx, pn, &parser))
             return NULL;
         if (!NameFunctions(cx, pn))
             return NULL;
 
-        pc.functionList = NULL;
-
         if (!EmitTree(cx, &bce, pn))
             return NULL;
 
 #if JS_HAS_XML_SUPPORT
         if (!pn->isKind(PNK_SEMI) || !pn->pn_kid || !pn->pn_kid->isXMLItem())
             onlyXML = false;
 #endif
         parser.freeTree(pn);
@@ -305,17 +303,17 @@ frontend::CompileFunctionBody(JSContext 
     }
 
     /*
      * After we're done parsing, we must fold constants, analyze any nested
      * functions, and generate code for this function, including a stop opcode
      * at the end.
      */
     ParseNode *pn = parser.functionBody(Parser::StatementListBody);
-    if (!pn) 
+    if (!pn)
         return false;
 
     if (!parser.tokenStream.matchToken(TOK_EOF)) {
         parser.reportError(NULL, JSMSG_SYNTAX_ERROR);
         return false;
     }
 
     if (!FoldConstants(cx, pn, &parser))
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -9,17 +9,16 @@
  * JS bytecode generation.
  */
 
 #include "mozilla/FloatingPoint.h"
 
 #ifdef HAVE_MEMORY_H
 #include <memory.h>
 #endif
-#include <new>
 #include <string.h>
 
 #include "jstypes.h"
 #include "jsutil.h"
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jsatom.h"
 #include "jscntxt.h"
@@ -4846,16 +4845,23 @@ EmitFunc(JSContext *cx, BytecodeEmitter 
         JS_ASSERT(pn->functionIsHoisted());
         JS_ASSERT(bce->sc->isFunction);
         return EmitFunctionDefNop(cx, bce, pn->pn_index);
     }
 
     {
         SharedContext *outersc = bce->sc;
         FunctionBox *funbox = pn->pn_funbox;
+
+        // If funbox's strictModeState is still unknown (as can happen for
+        // functions defined in defaults), inherit it from the parent.
+        if (funbox->strictModeState == StrictMode::UNKNOWN) {
+            JS_ASSERT(outersc->strictModeState != StrictMode::UNKNOWN);
+            funbox->strictModeState = outersc->strictModeState;
+        }
         if (outersc->isFunction && outersc->asFunbox()->mightAliasLocals())
             funbox->setMightAliasLocals();      // inherit mightAliasLocals from parent
         JS_ASSERT_IF(outersc->inStrictMode(), funbox->inStrictMode());
 
         // Inherit most things (principals, version, etc) from the parent.
         Rooted<JSScript*> parent(cx, bce->script);
         Rooted<JSObject*> enclosingScope(cx, EnclosingStaticScope(bce));
         CompileOptions options(cx);
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -258,22 +258,16 @@ ParseNodeAllocator::prepareNodeForMutati
                 freeNode(pn);
         }
     }
 }
 
 /*
  * Return the nodes in the subtree |pn| to the parser's free node list, for
  * reallocation.
- *
- * Note that all functions in |pn| that are not enclosed by other functions
- * in |pn| must be direct children of |pc|, because we only clean up |pc|'s
- * function and method lists. You must not reach into a function and
- * recycle some part of it (unless you've updated |pc|->functionList, the
- * way js_FoldConstants does).
  */
 ParseNode *
 ParseNodeAllocator::freeTree(ParseNode *pn)
 {
     if (!pn)
         return NULL;
 
     ParseNode *savedNext = pn->pn_next;
--- a/js/src/frontend/Parser-inl.h
+++ b/js/src/frontend/Parser-inl.h
@@ -36,17 +36,16 @@ ParseContext::ParseContext(Parser *prs, 
     staticLevel(staticLevel),
     parenDepth(0),
     yieldCount(0),
     blockNode(NULL),
     decls_(prs->context),
     args_(prs->context),
     vars_(prs->context),
     yieldNode(NULL),
-    functionList(NULL),
     queuedStrictModeError(NULL),
     parserPC(&prs->pc),
     lexdeps(prs->context),
     parent(prs->pc),
     funcStmts(NULL),
     funHasReturnExpr(false),
     funHasReturnVoid(false),
     parsingForInit(false),
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -398,18 +398,16 @@ Parser::newObjectBox(JSObject *obj)
 
     return objbox;
 }
 
 FunctionBox::FunctionBox(JSContext *cx, ObjectBox* traceListHead, JSFunction *fun,
                          ParseContext *outerpc, StrictMode sms)
   : SharedContext(cx, /* isFunction = */ true, sms),
     objbox(traceListHead, fun, this),
-    siblings(outerpc ? outerpc->functionList : NULL),
-    kids(NULL),
     bindings(),
     bufStart(0),
     bufEnd(0),
     ndefaults(0),
     inWith(false),                  // initialized below
     inGenexpLambda(false),
     funCxFlags()
 {
@@ -470,18 +468,16 @@ Parser::newFunctionBox(JSFunction *fun, 
      */
     FunctionBox *funbox =
         context->tempLifoAlloc().new_<FunctionBox>(context, traceListHead, fun, outerpc, sms);
     if (!funbox) {
         js_ReportOutOfMemory(context);
         return NULL;
     }
 
-    if (outerpc)
-        outerpc->functionList = funbox;
     traceListHead = &funbox->objbox;
 
     return funbox;
 }
 
 void
 Parser::trace(JSTracer *trc)
 {
@@ -1146,17 +1142,16 @@ LeaveFunction(ParseNode *fn, Parser *par
 {
     JSContext *cx = parser->context;
     ParseContext *funpc = parser->pc;
     ParseContext *pc = funpc->parent;
     pc->blockidGen = funpc->blockidGen;
 
     FunctionBox *funbox = fn->pn_funbox;
     JS_ASSERT(funbox == funpc->sc->asFunbox());
-    funbox->kids = funpc->functionList;
 
     if (!pc->topStmt || pc->topStmt->type == STMT_BLOCK)
         fn->pn_dflags |= PND_BLOCKCHILD;
 
     /* Propagate unresolved lexical names up to pc->lexdeps. */
     if (funpc->lexdeps->count()) {
         for (AtomDefnRange r = funpc->lexdeps->all(); !r.empty(); r.popFront()) {
             JSAtom *atom = r.front().key();
@@ -1790,71 +1785,63 @@ Parser::functionExpr()
     JS_ASSERT(tokenStream.currentToken().type == TOK_FUNCTION);
     if (tokenStream.getToken(TSF_KEYWORD_IS_NAME) == TOK_NAME)
         name = tokenStream.currentToken().name();
     else
         tokenStream.ungetToken();
     return functionDef(name, Normal, Expression);
 }
 
-void
-FunctionBox::recursivelySetStrictMode(StrictMode strictness)
-{
-    if (strictModeState == StrictMode::UNKNOWN) {
-        strictModeState = strictness;
-        for (FunctionBox *kid = kids; kid; kid = kid->siblings)
-            kid->recursivelySetStrictMode(strictness);
-    }
-}
-
 /*
  * Indicate that the current scope can't switch to strict mode with a body-level
  * "use strict" directive anymore. Return false on error.
  */
 bool
 Parser::setStrictMode(bool strictMode)
 {
     if (pc->sc->strictModeState != StrictMode::UNKNOWN) {
         // Strict mode was inherited.
         JS_ASSERT(pc->sc->strictModeState == StrictMode::STRICT);
         if (pc->sc->isFunction) {
             JS_ASSERT(pc->parent->sc->strictModeState == StrictMode::STRICT);
         } else {
-            JS_ASSERT(StrictModeFromContext(context) == StrictMode::STRICT || pc->staticLevel);
+            JS_ASSERT_IF(pc->staticLevel == 0,
+                         StrictModeFromContext(context) == StrictMode::STRICT);
         }
         return true;
     }
+
     if (strictMode) {
         if (pc->queuedStrictModeError) {
             // There was a strict mode error in this scope before we knew it was
             // strict. Throw it.
             JS_ASSERT(!(pc->queuedStrictModeError->report.flags & JSREPORT_WARNING));
             pc->queuedStrictModeError->throwError();
             return false;
         }
         pc->sc->strictModeState = StrictMode::STRICT;
-    } else if (!pc->parent || pc->parent->sc->strictModeState == StrictMode::NOTSTRICT) {
-        // This scope will not be strict.
-        pc->sc->strictModeState = StrictMode::NOTSTRICT;
-        if (pc->queuedStrictModeError && context->hasStrictOption() &&
-            pc->queuedStrictModeError->report.errorNumber != JSMSG_STRICT_CODE_WITH) {
-            // Convert queued strict mode error to a warning.
-            pc->queuedStrictModeError->report.flags |= JSREPORT_WARNING;
-            pc->queuedStrictModeError->throwError();
+    } else {
+        if (!pc->parent || pc->parent->sc->strictModeState == StrictMode::NOTSTRICT) {
+            // This scope lacks a strict directive, and its parent (if it has
+            // one) definitely isn't strict, so it definitely won't be strict.
+            pc->sc->strictModeState = StrictMode::NOTSTRICT;
+            if (pc->queuedStrictModeError && context->hasStrictOption() &&
+                pc->queuedStrictModeError->report.errorNumber != JSMSG_STRICT_CODE_WITH) {
+                // Convert queued strict mode error to a warning.
+                pc->queuedStrictModeError->report.flags |= JSREPORT_WARNING;
+                pc->queuedStrictModeError->throwError();
+            }
+        } else {
+            // This scope (which has a parent and so must be a function) lacks
+            // a strict directive, but it's not yet clear if its parent is
+            // strict.  (This can only happen for functions in default
+            // arguments.)  Leave it in the UNKNOWN state for now.
+            JS_ASSERT(pc->sc->isFunction);
         }
     }
-    JS_ASSERT_IF(!pc->sc->isFunction, !pc->functionList);
-    if (pc->sc->strictModeState != StrictMode::UNKNOWN && pc->sc->isFunction) {
-        // We changed the strict mode state. Retroactively recursively set
-        // strict mode status on all the function children we've seen so far
-        // children (That is, functions in default expressions).
-        pc->sc->asFunbox()->strictModeState = pc->sc->strictModeState;
-        for (FunctionBox *kid = pc->functionList; kid; kid = kid->siblings)
-            kid->recursivelySetStrictMode(pc->sc->strictModeState);
-    }
     return true;
 }
 
 /*
  * Return true if this token, known to be an unparenthesized string literal,
  * could be the string of a directive in a Directive Prologue. Directive
  * strings never contain escape sequences or line continuations.
  */
@@ -4772,22 +4759,21 @@ Parser::unaryExpr()
  * NB: This is not a general tree transplanter -- it knows in particular that
  * the one or more bindings induced by V have not yet been created.
  */
 class CompExprTransplanter {
     ParseNode       *root;
     Parser          *parser;
     bool            genexp;
     unsigned        adjust;
-    unsigned        funcLevel;
     HashSet<Definition *> visitedImplicitArguments;
 
   public:
     CompExprTransplanter(ParseNode *pn, Parser *parser, bool ge, unsigned adj)
-      : root(pn), parser(parser), genexp(ge), adjust(adj), funcLevel(0),
+      : root(pn), parser(parser), genexp(ge), adjust(adj),