Merge inbound to m-c
authorWes Kocher <wkocher@mozilla.com>
Thu, 12 Dec 2013 19:12:51 -0800
changeset 176304 1bc33fa19b24d8076b9a09511fcced1f20747757
parent 176228 f551c8244289a784446d4cf8516032a3a61ead7e (current diff)
parent 176303 ad1ad5c344050397f26517290bb71e5660c97249 (diff)
child 176305 8b5875dc7e316c36f8ff1fb4a0d89bd75681e8b0
child 176307 2d269eb27f2444c93eb5ca1df9943ae808a60802
child 176370 f9614eb176adce6995f4c26afc125e804ba065c2
push id3343
push userffxbld
push dateMon, 17 Mar 2014 21:55:32 +0000
treeherdermozilla-beta@2f7d3415f79f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c
CLOBBER
browser/base/content/browser.js
browser/components/sessionstore/src/SessionStore.jsm
browser/metro/base/content/browser.js
build/docs/requirements.txt
dom/tests/mochitest/general/test_interfaces.html
gfx/layers/Layers.cpp
media/webrtc/trunk/tools/gyp/pylib/gyp/generator/mozmake.py
media/webrtc/webrtc_config.gypi
python/mozbuild/mozbuild/test/backend/data/external_make_dirs/Makefile.in
python/mozbuild/mozbuild/test/backend/data/external_make_dirs/dir/Makefile.in
python/mozbuild/mozbuild/test/backend/data/external_make_dirs/dir/moz.build
python/mozbuild/mozbuild/test/backend/data/external_make_dirs/external/Makefile.in
python/mozbuild/mozbuild/test/backend/data/external_make_dirs/external/moz.build
python/mozbuild/mozbuild/test/backend/data/external_make_dirs/moz.build
python/mozbuild/mozbuild/test/backend/data/external_make_dirs/p_dir/Makefile.in
python/mozbuild/mozbuild/test/backend/data/external_make_dirs/p_dir/moz.build
python/mozbuild/mozbuild/test/backend/data/external_make_dirs/p_external/Makefile.in
python/mozbuild/mozbuild/test/backend/data/external_make_dirs/p_external/moz.build
--- a/CLOBBER
+++ b/CLOBBER
@@ -13,14 +13,9 @@
 #          |               |
 #          O <-- Clobber   O  <-- Clobber
 #
 # Note: The description below will be part of the error message shown to users.
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
-Bug 942231 needs a clobber -- JNI wrappers need to be re-generated.
-and
-Bug 934646 needs a clobber -- the icon resources previously copied
-into $OBJDIR/mobile/android/base/res will conflict with those in
-$BRANDING_DIRECTORY/res.
-
+Bug 887836 - webidl changes require a Windows clobber.
--- a/accessible/src/atk/AccessibleWrap.cpp
+++ b/accessible/src/atk/AccessibleWrap.cpp
@@ -687,17 +687,17 @@ getRoleCB(AtkObject *aAtkObj)
       MOZ_CRASH("Unknown role.");
   };
 
 #undef ROLE
 
   return aAtkObj->role;
 }
 
-AtkAttributeSet*
+static AtkAttributeSet*
 ConvertToAtkAttributeSet(nsIPersistentProperties* aAttributes)
 {
     if (!aAttributes)
         return nullptr;
 
     AtkAttributeSet *objAttributeSet = nullptr;
     nsCOMPtr<nsISimpleEnumerator> propEnum;
     nsresult rv = aAttributes->Enumerate(getter_AddRefs(propEnum));
--- a/accessible/src/atk/nsMaiInterfaceText.cpp
+++ b/accessible/src/atk/nsMaiInterfaceText.cpp
@@ -7,22 +7,97 @@
 #include "InterfaceInitFuncs.h"
 
 #include "Accessible-inl.h"
 #include "HyperTextAccessible-inl.h"
 #include "nsMai.h"
 
 #include "nsIAccessibleTypes.h"
 #include "nsIPersistentProperties2.h"
+#include "nsISimpleEnumerator.h"
 
 #include "mozilla/Likely.h"
 
+using namespace mozilla;
 using namespace mozilla::a11y;
 
-AtkAttributeSet* ConvertToAtkAttributeSet(nsIPersistentProperties* aAttributes);
+static const char* sAtkTextAttrNames[ATK_TEXT_ATTR_LAST_DEFINED];
+
+static AtkAttributeSet*
+ConvertToAtkTextAttributeSet(nsIPersistentProperties* aAttributes)
+{
+  if (!aAttributes)
+    return nullptr;
+
+  AtkAttributeSet* objAttributeSet = nullptr;
+  nsCOMPtr<nsISimpleEnumerator> propEnum;
+  nsresult rv = aAttributes->Enumerate(getter_AddRefs(propEnum));
+  NS_ENSURE_SUCCESS(rv, nullptr);
+
+  bool hasMore = false;
+  while (NS_SUCCEEDED(propEnum->HasMoreElements(&hasMore)) && hasMore) {
+    nsCOMPtr<nsISupports> sup;
+    rv = propEnum->GetNext(getter_AddRefs(sup));
+    NS_ENSURE_SUCCESS(rv, objAttributeSet);
+
+    nsCOMPtr<nsIPropertyElement> propElem(do_QueryInterface(sup));
+    NS_ENSURE_TRUE(propElem, objAttributeSet);
+
+    nsAutoCString name;
+    rv = propElem->GetKey(name);
+    NS_ENSURE_SUCCESS(rv, objAttributeSet);
+
+    nsAutoString value;
+    rv = propElem->GetValue(value);
+    NS_ENSURE_SUCCESS(rv, objAttributeSet);
+
+    AtkAttribute* objAttr = (AtkAttribute*)g_malloc(sizeof(AtkAttribute));
+    objAttr->name = g_strdup(name.get());
+    objAttr->value = g_strdup(NS_ConvertUTF16toUTF8(value).get());
+    objAttributeSet = g_slist_prepend(objAttributeSet, objAttr);
+
+    // Handle attributes where atk has its own name.
+    const char* atkName = nullptr;
+    nsAutoString atkValue;
+    if (name.EqualsLiteral("color")) {
+      // The format of the atk attribute is r,g,b and the gecko one is
+      // rgb(r,g,b).
+      atkValue = Substring(value, 5, value.Length() - 1);
+      atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_FG_COLOR];
+    } else if (name.EqualsLiteral("background-color")) {
+      // The format of the atk attribute is r,g,b and the gecko one is
+      // rgb(r,g,b).
+      atkValue = Substring(value, 5, value.Length() - 1);
+      atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_BG_COLOR];
+    } else if (name.EqualsLiteral("font-family")) {
+      atkValue = value;
+      atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_FAMILY_NAME];
+    } else if (name.Equals("font-size")) {
+      // ATK wants the number of pixels without px at the end.
+      atkValue = StringHead(value, value.Length() - 2);
+      atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_SIZE];
+    } else if (name.EqualsLiteral("font-weight")) {
+      atkValue = value;
+      atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_WEIGHT];
+    } else if (name.EqualsLiteral("invalid")) {
+      atkValue = value;
+      atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_INVALID];
+    }
+
+    if (atkName) {
+      objAttr = static_cast<AtkAttribute*>(g_malloc(sizeof(AtkAttribute)));
+      objAttr->name = g_strdup(atkName);
+      objAttr->value = g_strdup(NS_ConvertUTF16toUTF8(atkValue).get());
+      objAttributeSet = g_slist_prepend(objAttributeSet, objAttr);
+    }
+  }
+
+  // libatk-adaptor will free it
+  return objAttributeSet;
+}
 
 static void
 ConvertTexttoAsterisks(AccessibleWrap* accWrap, nsAString& aString)
 {
   // convert each char to "*" when it's "password text" 
   if (accWrap->NativeRole() == roles::PASSWORD_TEXT) {
     for (uint32_t i = 0; i < aString.Length(); i++)
       aString.Replace(i, 1, NS_LITERAL_STRING("*"));
@@ -183,32 +258,32 @@ getRunAttributesCB(AtkText *aText, gint 
 
   int32_t startOffset = 0, endOffset = 0;
   nsCOMPtr<nsIPersistentProperties> attributes =
     text->TextAttributes(false, aOffset, &startOffset, &endOffset);
 
   *aStartOffset = startOffset;
   *aEndOffset = endOffset;
 
-  return ConvertToAtkAttributeSet(attributes);
+  return ConvertToAtkTextAttributeSet(attributes);
 }
 
 static AtkAttributeSet*
 getDefaultAttributesCB(AtkText *aText)
 {
   AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
   if (!accWrap)
     return nullptr;
 
   HyperTextAccessible* text = accWrap->AsHyperText();
   if (!text || !text->IsTextRole())
     return nullptr;
 
   nsCOMPtr<nsIPersistentProperties> attributes = text->DefaultTextAttributes();
-  return ConvertToAtkAttributeSet(attributes);
+  return ConvertToAtkTextAttributeSet(attributes);
 }
 
 static void
 getCharacterExtentsCB(AtkText *aText, gint aOffset,
                       gint *aX, gint *aY,
                       gint *aWidth, gint *aHeight,
                       AtkCoordType aCoords)
 {
@@ -410,9 +485,14 @@ textInterfaceInitCB(AtkTextIface* aIface
   aIface->get_n_selections = getTextSelectionCountCB;
   aIface->get_selection = getTextSelectionCB;
 
     // set methods
   aIface->add_selection = addTextSelectionCB;
   aIface->remove_selection = removeTextSelectionCB;
   aIface->set_selection = setTextSelectionCB;
   aIface->set_caret_offset = setCaretOffsetCB;
+
+  // Cache the string values of the atk text attribute names.
+  for (uint32_t i = 0; i < ArrayLength(sAtkTextAttrNames); i++)
+    sAtkTextAttrNames[i] =
+      atk_text_attribute_get_name(static_cast<AtkTextAttribute>(i));
 }
--- a/accessible/src/windows/msaa/nsWinUtils.cpp
+++ b/accessible/src/windows/msaa/nsWinUtils.cpp
@@ -11,16 +11,17 @@
 #include "DocAccessible.h"
 #include "nsCoreUtils.h"
 
 #include "mozilla/Preferences.h"
 #include "nsArrayUtils.h"
 #include "nsIArray.h"
 #include "nsIDocument.h"
 #include "nsIDocShellTreeItem.h"
+#include "nsIXULRuntime.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 // Window property used by ipc related code in identifying accessible
 // tab windows.
 const wchar_t* kPropNameTabContent = L"AccessibleTabWindow";
 
@@ -53,17 +54,17 @@ nsWinUtils::GetComputedStyleDeclaration(
 
 bool
 nsWinUtils::MaybeStartWindowEmulation()
 {
   // Register window class that'll be used for document accessibles associated
   // with tabs.
   if (Compatibility::IsJAWS() || Compatibility::IsWE() ||
       Compatibility::IsDolphin() ||
-      Preferences::GetBool("browser.tabs.remote")) {
+      BrowserTabsRemote()) {
     RegisterNativeWindow(kClassNameTabContent);
     sHWNDCache = new nsRefPtrHashtable<nsPtrHashKey<void>, DocAccessible>(4);
     return true;
   }
 
   return false;
 }
 
--- a/b2g/components/FilePicker.js
+++ b/b2g/components/FilePicker.js
@@ -27,16 +27,17 @@ const VIDEO_FILTERS = ['video/mpeg', 'vi
                        'video/quicktime', 'video/webm', 'video/x-matroska',
                        'video/x-ms-wmv', 'video/x-flv'];
 const AUDIO_FILTERS = ['audio/basic', 'audio/L24', 'audio/mp4',
                        'audio/mpeg', 'audio/ogg', 'audio/vorbis',
                        'audio/vnd.rn-realaudio', 'audio/vnd.wave',
                        'audio/webm'];
 
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
+Cu.import("resource://gre/modules/FileUtils.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, 'cpmm',
                                    '@mozilla.org/childprocessmessagemanager;1',
                                    'nsIMessageSender');
 
 function FilePicker() {
 }
 
@@ -174,28 +175,49 @@ FilePicker.prototype = {
     cpmm.removeMessageListener('file-picked', this);
 
     let data = message.data;
     if (!data.success || !data.result.blob) {
       this.fireError();
       return;
     }
 
-    var name = 'blob';
-    if (data.result.blob.type) {
-      let mimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
-      let mimeInfo = mimeSvc.getFromTypeAndExtension(data.result.blob.type, '');
-      if (mimeInfo) {
-        name += '.' + mimeInfo.primaryExtension;
+    // The name to be shown can be part of the message, or can be taken from
+    // the DOMFile (if the blob is a DOMFile).
+    let name = data.result.name;
+    if (!name &&
+        (data.result.blob instanceof this.mParent.File) &&
+        data.result.blob.name) {
+      name = data.result.blob.name;
+    }
+
+    // Let's try to remove the full path and take just the filename.
+    if (name) {
+      let file = new FileUtils.File(data.result.blob.name);
+      if (file && file.leafName) {
+        name = file.leafName;
+      }
+    }
+
+    // the fallback is a filename composed by 'blob' + extension.
+    if (!name) {
+      name = 'blob';
+      if (data.result.blob.type) {
+        let mimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
+        let mimeInfo = mimeSvc.getFromTypeAndExtension(data.result.blob.type, '');
+        if (mimeInfo) {
+          name += '.' + mimeInfo.primaryExtension;
+        }
       }
     }
 
     let file = new this.mParent.File(data.result.blob,
                                      { name: name,
                                        type: data.result.blob.type });
+
     if (file) {
       this.fireSuccess(file);
     } else {
       this.fireError();
     }
   }
 };
 
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -737,17 +737,17 @@ const gFormSubmitObserver = {
     this.panel.openPopup(element, position, offset, 0);
   }
 };
 
 var gBrowserInit = {
   delayedStartupFinished: false,
 
   onLoad: function() {
-    gMultiProcessBrowser = gPrefService.getBoolPref("browser.tabs.remote");
+    gMultiProcessBrowser = Services.appinfo.browserTabsRemote;
 
     var mustLoadSidebar = false;
 
     if (!gMultiProcessBrowser) {
       // There is a Content:Click message manually sent from content.
       Cc["@mozilla.org/eventlistenerservice;1"]
         .getService(Ci.nsIEventListenerService)
         .addSystemEventListener(gBrowser, "click", contentAreaClick, true);
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -3032,17 +3032,17 @@
             this._waitForInitialContentDocument();
           }
 
           this.style.backgroundColor =
             Services.prefs.getBoolPref("browser.display.use_system_colors") ?
               "-moz-default-background-color" :
               Services.prefs.getCharPref("browser.display.background_color");
 
-          if (Services.prefs.getBoolPref("browser.tabs.remote")) {
+          if (Services.appinfo.browserTabsRemote) {
             messageManager.addMessageListener("DOMTitleChanged", this);
             messageManager.addMessageListener("contextmenu", this);
           }
         ]]>
       </constructor>
 
       <method name="_generateUniquePanelID">
         <body><![CDATA[
@@ -3098,17 +3098,17 @@
             this.mTabFilters[i].removeProgressListener(this.mTabListeners[i]);
             this.mTabFilters[i] = null;
             this.mTabListeners[i].destroy();
             this.mTabListeners[i] = null;
           }
           document.removeEventListener("keypress", this, false);
           window.removeEventListener("sizemodechange", this, false);
 
-          if (Services.prefs.getBoolPref("browser.tabs.remote")) {
+          if (Services.appinfo.browserTabsRemote) {
             messageManager.removeMessageListener("DOMTitleChanged", this);
             messageManager.removeMessageListener("contextmenu", this);
           }
         ]]>
       </destructor>
 
       <!-- Deprecated stuff, implemented for backwards compatibility. -->
       <method name="enterTabbedMode">
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -476,17 +476,17 @@ BrowserGlue.prototype = {
 #ifdef NIGHTLY_BUILD
     ShumwayUtils.init();
 #endif
     webrtcUI.init();
     AboutHome.init();
     SessionStore.init();
     BrowserUITelemetry.init();
 
-    if (Services.prefs.getBoolPref("browser.tabs.remote"))
+    if (Services.appinfo.browserTabsRemote)
       ContentClick.init();
 
     Services.obs.notifyObservers(null, "browser-ui-startup-complete", "");
   },
 
   _checkForOldBuildUpdates: function () {
     // check for update if our build is old
     if (Services.prefs.getBoolPref("app.update.enabled") &&
--- a/browser/components/sessionstore/src/SessionStore.jsm
+++ b/browser/components/sessionstore/src/SessionStore.jsm
@@ -382,17 +382,17 @@ let SessionStoreInternal = {
   /**
    * Initialize the sessionstore service.
    */
   init: function () {
     if (this._initialized) {
       throw new Error("SessionStore.init() must only be called once!");
     }
 
-    this._disabledForMultiProcess = Services.prefs.getBoolPref("browser.tabs.remote");
+    this._disabledForMultiProcess = Services.appinfo.browserTabsRemote;
     if (this._disabledForMultiProcess) {
       this._deferredInitialized.resolve();
       return;
     }
 
     TelemetryTimestamps.add("sessionRestoreInitialized");
     OBSERVING.forEach(function(aTopic) {
       Services.obs.addObserver(this, aTopic, true);
--- a/browser/metro/base/content/browser.js
+++ b/browser/metro/base/content/browser.js
@@ -1424,17 +1424,17 @@ Tab.prototype = {
     let notification = this._notification = document.createElement("notificationbox");
 
     let browser = this._browser = document.createElement("browser");
     browser.id = "browser-" + this._id;
     this._chromeTab.linkedBrowser = browser;
 
     browser.setAttribute("type", "content");
 
-    let useRemote = Services.prefs.getBoolPref("browser.tabs.remote");
+    let useRemote = Services.appinfo.browserTabsRemote;
     let useLocal = Util.isLocalScheme(aURI);
     browser.setAttribute("remote", (!useLocal && useRemote) ? "true" : "false");
 
     // Append the browser to the document, which should start the page load
     let stack = document.createElementNS(XUL_NS, "stack");
     stack.className = "browserStack";
     stack.appendChild(browser);
     stack.setAttribute("flex", "1");
--- a/build/autoconf/arch.m4
+++ b/build/autoconf/arch.m4
@@ -242,16 +242,17 @@ if test "$CPU_ARCH" = "arm"; then
       fi
   fi
 
 fi # CPU_ARCH = arm
 
 AC_SUBST(HAVE_ARM_SIMD)
 AC_SUBST(HAVE_ARM_NEON)
 AC_SUBST(BUILD_ARM_NEON)
+AC_SUBST(ARM_ARCH)
 
 if test -n "$MOZ_ARCH"; then
   NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-arch=$MOZ_ARCH"
   NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-thumb=$MOZ_THUMB"
   NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-thumb-interwork=$MOZ_THUMB_INTERWORK"
   NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-fpu=$MOZ_FPU"
   NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-float-abi=$MOZ_FLOAT_ABI"
   NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-soft-float=$MOZ_SOFT_FLOAT"
new file mode 100644
--- /dev/null
+++ b/build/gyp.mozbuild
@@ -0,0 +1,109 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+gyp_vars = {
+    'build_with_mozilla': 1,
+    'build_with_chromium': 0,
+    'have_clock_monotonic': 1 if CONFIG['HAVE_CLOCK_MONOTONIC'] else 0,
+    'have_ethtool_cmd_speed_hi': 1 if CONFIG['MOZ_WEBRTC_HAVE_ETHTOOL_SPEED_HI'] else 0,
+    'include_alsa_audio': 1 if CONFIG['MOZ_ALSA'] else 0,
+    'include_pulse_audio': 1 if CONFIG['MOZ_PULSEAUDIO'] else 0,
+    # basic stuff for everything
+    'include_internal_video_render': 0,
+    'clang_use_chrome_plugins': 0,
+    'enable_protobuf': 0,
+    'include_tests': 0,
+    'enable_android_opensl': 1,
+    # use_system_lib* still seems to be in use in trunk/build
+    'use_system_libjpeg': 0,
+    'use_system_libvpx': 0,
+    'build_libjpeg': 0,
+    'build_libvpx': 0,
+    # saves 4MB when webrtc_trace is off
+    'enable_lazy_trace_alloc': 1,
+
+     # turn off mandatory use of NEON and instead use NEON detection
+    'arm_neon': 0,
+
+    'moz_widget_toolkit_gonk': 0,
+
+    # (for vp8) chromium sets to 0 also
+    'use_temporal_layers': 0,
+    # Creates AEC internal sample dump files in current directory
+    # 'aec_debug_dump': 1,
+
+    # codec enable/disables:
+    # Note: if you change one here, you must modify layout/media/webrtc/Makefile.in!
+    'include_g711': 1,
+    'include_opus': 1,
+    'include_g722': 0,
+    'include_ilbc': 0,
+    'include_isac': 0,
+    'include_pcm16b': 1,
+}
+
+os = CONFIG['OS_TARGET']
+
+if os == 'WINNT':
+    gyp_vars.update(
+        MSVS_VERSION=CONFIG['_MSVS_VERSION'],
+        MSVS_OS_BITS=64 if CONFIG['HAVE_64BIT_OS'] else 32,
+    )
+elif os == 'Android':
+    if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
+        gyp_vars['build_with_gonk'] = 1
+        gyp_vars['moz_widget_toolkit_gonk'] = 1
+    else:
+        gyp_vars.update(
+            gtest_target_type='executable',
+            android_toolchain=CONFIG['ANDROID_TOOLCHAIN'],
+        )
+
+flavors = {
+    'WINNT': 'win',
+    'Android': 'linux' if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' else 'android',
+    'Linux': 'linux',
+    'Darwin': 'mac' if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa' else 'ios',
+    'SunOS': 'solaris',
+    'GNU_kFreeBSD': 'freebsd',
+    'DragonFly': 'dragonfly',
+    'FreeBSD': 'freebsd',
+    'NetBSD': 'netbsd',
+    'OpenBSD': 'openbsd',
+}
+gyp_vars['OS'] = flavors[os]
+
+arches = {
+    'x86_64': 'x64',
+    'arm': 'arm',
+    'x86': 'ia32',
+    'ppc': 'ppc',
+    'ppc64': 'ppc',
+    'ia64': 'ia64',
+}
+
+gyp_vars['target_arch'] = arches[CONFIG['CPU_ARCH']]
+
+if CONFIG['ARM_ARCH']:
+    # We currently don't have a way to convert a string to an int in moz.build.
+    # As of writing, ARM_ARCH is not going to be over 8, so a string comparison
+    # works.
+    if CONFIG['ARM_ARCH'] < '7':
+        gyp_vars['armv7'] = 0
+    elif os == 'Android':
+        gyp_vars['armv7'] = 1
+    else:
+        # CPU detection for ARM works on Android only.  armv7 always uses CPU
+        # detection, so we have to set armv7=0 for non-Android target
+        gyp_vars['armv7'] = 0
+
+# Don't try to compile ssse3/sse4.1 code if toolchain doesn't support
+if CONFIG['INTEL_ARCHITECTURE']:
+    if not CONFIG['HAVE_TOOLCHAIN_SUPPORT_MSSSE3'] or not CONFIG['HAVE_TOOLCHAIN_SUPPORT_MSSE4_1']:
+        gyp_vars['yuv_disable_asm'] = 1
+
+if CONFIG['MACOS_SDK_DIR']:
+    gyp_vars['mac_sdk_path'] = CONFIG['MACOS_SDK_DIR']
--- a/build/virtualenv_packages.txt
+++ b/build/virtualenv_packages.txt
@@ -12,8 +12,9 @@ codegen.pth:python/codegen/
 mock.pth:python/mock-1.0.0
 mozilla.pth:build
 mozilla.pth:config
 mozilla.pth:xpcom/typelib/xpt/tools
 moztreedocs.pth:tools/docs
 copy:build/buildconfig.py
 packages.txt:testing/mozbase/packages.txt
 objdir:build
+gyp.pth:media/webrtc/trunk/tools/gyp/pylib
--- a/client.mk
+++ b/client.mk
@@ -170,17 +170,17 @@ MOZ_MAKE = $(MAKE) $(MOZ_MAKE_FLAGS) -C 
 
 endif # MOZ_BUILD_PROJECTS
 
 # 'configure' scripts generated by autoconf.
 CONFIGURES := $(TOPSRCDIR)/configure
 CONFIGURES += $(TOPSRCDIR)/js/src/configure
 
 # Make targets that are going to be passed to the real build system
-OBJDIR_TARGETS = install export libs clean realclean distclean maybe_clobber_profiledbuild upload sdk installer package fast-package package-compare stage-package source-package l10n-check
+OBJDIR_TARGETS = install export libs clean realclean distclean maybe_clobber_profiledbuild upload sdk installer package package-compare stage-package source-package l10n-check
 
 #######################################################################
 # Rules
 
 # The default rule is build
 build::
 	$(MAKE) -f $(TOPSRCDIR)/client.mk $(if $(MOZ_PGO),profiledbuild,realbuild)
 
@@ -219,17 +219,17 @@ ifneq (,$(strip $(MOZCONFIG_OUT_FILTERED
 	$(foreach line,$(MOZCONFIG_OUT_FILTERED),$(info $(NULL) $(NULL) $(NULL) $(NULL) $(subst ||, ,$(line))))
 endif
 
 # Windows equivalents
 build_all: build
 clobber clobber_all: clean
 
 # helper target for mobile
-build_and_deploy: build fast-package install
+build_and_deploy: build package install
 
 # Do everything from scratch
 everything: clean build
 
 ####################################
 # Profile-Guided Optimization
 #  This is up here, outside of the MOZ_CURRENT_PROJECT logic so that this
 #  is usable in multi-pass builds, where you might not have a runnable
--- a/config/config.mk
+++ b/config/config.mk
@@ -552,50 +552,51 @@ endif # FAIL_ON_WARNINGS
 
 ifeq ($(OS_ARCH)_$(GNU_CC),WINNT_)
 #// Currently, unless USE_STATIC_LIBS is defined, the multithreaded
 #// DLL version of the RTL is used...
 #//
 #//------------------------------------------------------------------------
 ifdef USE_STATIC_LIBS
 RTL_FLAGS=-MT          # Statically linked multithreaded RTL
-ifneq (,$(MOZ_DEBUG)$(NS_TRACE_MALLOC)$(MOZ_DMD))
+ifneq (,$(MOZ_DEBUG)$(NS_TRACE_MALLOC))
 ifndef MOZ_NO_DEBUG_RTL
 RTL_FLAGS=-MTd         # Statically linked multithreaded MSVC4.0 debug RTL
 endif
-endif # MOZ_DEBUG || NS_TRACE_MALLOC || MOZ_DMD
+endif # MOZ_DEBUG || NS_TRACE_MALLOC
 
 else # !USE_STATIC_LIBS
 
 RTL_FLAGS=-MD          # Dynamically linked, multithreaded RTL
-ifneq (,$(MOZ_DEBUG)$(NS_TRACE_MALLOC)$(MOZ_DMD))
+ifneq (,$(MOZ_DEBUG)$(NS_TRACE_MALLOC))
 ifndef MOZ_NO_DEBUG_RTL
 RTL_FLAGS=-MDd         # Dynamically linked, multithreaded MSVC4.0 debug RTL
 endif
-endif # MOZ_DEBUG || NS_TRACE_MALLOC || MOZ_DMD
+endif # MOZ_DEBUG || NS_TRACE_MALLOC
 endif # USE_STATIC_LIBS
 endif # WINNT && !GNU_CC
 
 ifeq ($(OS_ARCH),Darwin)
 # Compiling ObjC requires an Apple compiler anyway, so it's ok to set
 # host CMFLAGS here.
 HOST_CMFLAGS += -fobjc-exceptions
 HOST_CMMFLAGS += -fobjc-exceptions
 OS_COMPILE_CMFLAGS += -fobjc-exceptions
 OS_COMPILE_CMMFLAGS += -fobjc-exceptions
 ifeq ($(MOZ_WIDGET_TOOLKIT),uikit)
 OS_COMPILE_CMFLAGS += -fobjc-abi-version=2 -fobjc-legacy-dispatch
 OS_COMPILE_CMMFLAGS += -fobjc-abi-version=2 -fobjc-legacy-dispatch
 endif
 endif
 
-COMPILE_CFLAGS	= $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_CPPFLAGS) $(OS_COMPILE_CFLAGS) $(CFLAGS)
-COMPILE_CXXFLAGS = $(STL_FLAGS) $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_CPPFLAGS) $(OS_COMPILE_CXXFLAGS) $(CXXFLAGS)
-COMPILE_CMFLAGS = $(OS_COMPILE_CMFLAGS)
-COMPILE_CMMFLAGS = $(OS_COMPILE_CMMFLAGS)
+COMPILE_CFLAGS	= $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_CPPFLAGS) $(OS_COMPILE_CFLAGS) $(CFLAGS) $(EXTRA_COMPILE_FLAGS)
+COMPILE_CXXFLAGS = $(STL_FLAGS) $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_CPPFLAGS) $(OS_COMPILE_CXXFLAGS) $(CXXFLAGS) $(EXTRA_COMPILE_FLAGS)
+COMPILE_CMFLAGS = $(OS_COMPILE_CMFLAGS) $(EXTRA_COMPILE_FLAGS)
+COMPILE_CMMFLAGS = $(OS_COMPILE_CMMFLAGS) $(EXTRA_COMPILE_FLAGS)
+ASFLAGS += $(EXTRA_ASSEMBLER_FLAGS)
 
 ifndef CROSS_COMPILE
 HOST_CFLAGS += $(RTL_FLAGS)
 endif
 
 #
 # Name of the binary code directories
 #
@@ -879,8 +880,26 @@ PLY_INCLUDE = -I$(topsrcdir)/other-licen
 
 export CL_INCLUDES_PREFIX
 
 ifdef MOZ_GTK2_CFLAGS
 MOZ_GTK2_CFLAGS := -I$(topsrcdir)/widget/gtk/compat $(MOZ_GTK2_CFLAGS)
 endif
 
 DEFINES += -DNO_NSPR_10_SUPPORT
+
+ifdef IS_GYP_DIR
+LOCAL_INCLUDES += \
+  -I$(topsrcdir)/ipc/chromium/src \
+  -I$(topsrcdir)/ipc/glue \
+  -I$(DEPTH)/ipc/ipdl/_ipdlheaders \
+  $(NULL)
+
+ifeq (WINNT,$(OS_TARGET))
+# These get set via VC project file settings for normal GYP builds.
+DEFINES += -DUNICODE -D_UNICODE
+LOCAL_INCLUDES += -I'$(MOZ_DIRECTX_SDK_PATH)/include'
+endif
+
+STL_FLAGS=
+# Skip most Mozilla-specific include locations.
+INCLUDES = -I. $(LOCAL_INCLUDES) -I$(DEPTH)/dist/include
+endif
--- a/configure.in
+++ b/configure.in
@@ -451,26 +451,27 @@ case "$target" in
             AC_MSG_ERROR([The major versions of \$CC and \$CXX do not match.])
         fi
 
         AC_DEFINE(_CRT_SECURE_NO_WARNINGS)
         AC_DEFINE(_CRT_NONSTDC_NO_WARNINGS)
 
         if test "$_CC_MAJOR_VERSION" = "16"; then
             _CC_SUITE=10
-            _MSVS_VERSION=2010
+            MSVS_VERSION=2010
         elif test "$_CC_MAJOR_VERSION" = "17"; then
             _CC_SUITE=11
-            _MSVS_VERSION=2012
+            MSVS_VERSION=2012
         elif test "$_CC_MAJOR_VERSION" = "18"; then
             _CC_SUITE=12
-            _MSVS_VERSION=2013
+            MSVS_VERSION=2013
         else
             AC_MSG_ERROR([This version ($CC_VERSION) of the MSVC compiler is unsupported. See https://developer.mozilla.org/en/Windows_Build_Prerequisites.])
         fi
+	AC_SUBST(MSVS_VERSION)
 
         AC_DEFINE(HAVE_SEH_EXCEPTIONS)
 
         if test -n "$WIN32_REDIST_DIR"; then
           if test ! -d "$WIN32_REDIST_DIR"; then
             AC_MSG_ERROR([Invalid Win32 Redist directory: ${WIN32_REDIST_DIR}])
           fi
           WIN32_REDIST_DIR=`cd "$WIN32_REDIST_DIR" && pwd`
@@ -5063,39 +5064,25 @@ if test -n "$MOZ_WEBRTC"; then
         ;;
     esac
 fi
 
 AC_TRY_COMPILE([#include <linux/ethtool.h>],
                [ struct ethtool_cmd cmd; cmd.speed_hi = 0; ],
                MOZ_WEBRTC_HAVE_ETHTOOL_SPEED_HI=1)
 
+AC_SUBST(MOZ_WEBRTC_HAVE_ETHTOOL_SPEED_HI)
+
 # target_arch is from {ia32|x64|arm|ppc}
 case "$CPU_ARCH" in
-x86_64)
-    WEBRTC_TARGET_ARCH=x64
-    ;;
-
-arm*)
-    WEBRTC_TARGET_ARCH=arm
-    ;;
-
-x86)
-    WEBRTC_TARGET_ARCH=ia32
-    ;;
-
-ppc*)
-    WEBRTC_TARGET_ARCH=ppc
-    ;;
-ia64)
-    WEBRTC_TARGET_ARCH=ia64
+x86_64 | arm | x86 | ppc* | ia64)
+    :
     ;;
 *)
 # unsupported arch for webrtc
-    WEBRTC_TARGET_ARCH=unknown
     MOZ_WEBRTC=
     ;;
 
 esac
 
 dnl ========================================================
 dnl = Disable WebRTC code
 dnl ========================================================
@@ -8833,171 +8820,16 @@ case "$host" in
 *-apple-darwin11*)
     FIXED_EGREP="env ARCHPREFERENCE=i386,x86_64 arch egrep"
     ;;
 *)
     FIXED_EGREP="egrep"
     ;;
 esac
 
-# Generate Makefiles for WebRTC directly from .gyp files
-if test "${OS_TARGET}" = "WINNT"; then
-   if test "$HAVE_64BIT_OS"; then
-      OS_BITS=64
-   else
-      OS_BITS=32
-   fi
-   EXTRA_GYP_DEFINES="-D MSVS_VERSION=${_MSVS_VERSION} -D MSVS_OS_BITS=${OS_BITS}"
-
-elif test "${OS_TARGET}" = "Android"; then
-   if test "${MOZ_WIDGET_TOOLKIT}" = "gonk"; then
-      EXTRA_GYP_DEFINES="-G os=linux "
-   else
-      EXTRA_GYP_DEFINES="-D gtest_target_type=executable -D android_toolchain=${android_toolchain} -G os=android "
-   fi
-fi
-
-if test -n "$ARM_ARCH"; then
-    if test "$ARM_ARCH" -lt 7; then
-        EXTRA_GYP_DEFINES="${EXTRA_GYP_DEFINES} -D armv7=0 "
-    else
-        if test "${OS_TARGET}" = "Android"; then
-            EXTRA_GYP_DEFINES="${EXTRA_GYP_DEFINES} -D armv7=1 "
-        else
-            dnl CPU detection for ARM works on Android only.  armv7 always uses CPU detection, so
-            dnl we have to set armv7=0 for non-Android target
-            EXTRA_GYP_DEFINES="${EXTRA_GYP_DEFINES} -D armv7=0 "
-        fi
-    fi
-fi
-
-# Keep libcubeb and audio_device backends in sync
-if test -n "$MOZ_ALSA"; then
-   EXTRA_GYP_DEFINES="$EXTRA_GYP_DEFINES -D include_alsa_audio=1"
-else
-   EXTRA_GYP_DEFINES="$EXTRA_GYP_DEFINES -D include_alsa_audio=0"
-fi
-if test -n "$MOZ_PULSEAUDIO"; then
-   EXTRA_GYP_DEFINES="$EXTRA_GYP_DEFINES -D include_pulse_audio=1"
-else
-   EXTRA_GYP_DEFINES="$EXTRA_GYP_DEFINES -D include_pulse_audio=0"
-fi
-
-# Don't try to compile ssse3/sse4.1 code if toolchain doesn't support
-if test -n "$INTEL_ARCHITECTURE"; then
-  if test -z "$HAVE_TOOLCHAIN_SUPPORT_MSSSE3" -o -z "$HAVE_TOOLCHAIN_SUPPORT_MSSE4_1"; then
-    EXTRA_GYP_DEFINES="$EXTRA_GYP_DEFINES -D yuv_disable_asm=1"
-  fi
-fi
-
-if test -n "$MOZ_WEBRTC"; then
-   AC_MSG_RESULT("generating WebRTC Makefiles...")
-
-   if test "${MOZ_WIDGET_TOOLKIT}" = "gonk"; then
-      EXTRA_GYP_DEFINES="${EXTRA_GYP_DEFINES} -D build_with_gonk=1"
-   fi
-
-dnl Any --include files must also appear in -D FORCED_INCLUDE_FILE= entries
-dnl so that regeneration via dependencies works correctly
-   WEBRTC_CONFIG="-D build_with_mozilla=1 -D build_with_chromium=0 --include ${srcdir}/media/webrtc/webrtc_config.gypi -D FORCED_INCLUDE_FILE=${srcdir}/media/webrtc/webrtc_config.gypi"
-
-   if test -n HAVE_CLOCK_MONOTONIC; then
-      WEBRTC_CONFIG="${WEBRTC_CONFIG} -D have_clock_monotonic=1"
-   else
-      WEBRTC_CONFIG="${WEBRTC_CONFIG} -D have_clock_monotonic=0"
-   fi
-
-   if test -n "$MOZ_WEBRTC_HAVE_ETHTOOL_SPEED_HI"; then
-      WEBRTC_CONFIG="${WEBRTC_CONFIG} -D have_ethtool_cmd_speed_hi=1"
-   else
-      WEBRTC_CONFIG="${WEBRTC_CONFIG} -D have_ethtool_cmd_speed_hi=0"
-   fi
-
-   if test -n "$CROSS_COMPILE"; then
-      case "$target" in
-      *-mingw*)
-      	GYP_MOZMAKE_OPTIONS="-G os=win"
-        ;;
-      *-darwin*)
-        GYP_MOZMAKE_OPTIONS="-G os=mac"
-        if test "$MACOS_SDK_DIR"; then
-           GYP_MOZMAKE_OPTIONS="${GYP_MOZMAKE_OPTIONS} -D mac_sdk_path=$MACOS_SDK_DIR"
-        fi
-        ;;
-      *-*linux*)
-        GYP_MOZMAKE_OPTIONS="-G os=linux"
-        ;;
-      *)
-        AC_MSG_ERROR([Don't know what options to give to WebRTC for cross-compilation])
-    	;;
-      esac
-   fi
-
-   GYP_WEBRTC_OPTIONS="--format=mozmake ${GYP_MOZMAKE_OPTIONS} ${WEBRTC_CONFIG} -D target_arch=${WEBRTC_TARGET_ARCH} ${EXTRA_GYP_DEFINES} --depth=${srcdir}/media/webrtc/trunk --toplevel-dir=${srcdir} -G OBJDIR=${_objdir}"
-
-   $PYTHON ${srcdir}/media/webrtc/trunk/build/gyp_chromium \
-     $GYP_WEBRTC_OPTIONS \
-     --generator-output=${_objdir}/media/webrtc/trunk \
-     ${srcdir}/media/webrtc/trunk/peerconnection.gyp
-   if test "$?" != 0; then
-      AC_MSG_ERROR([failed to generate WebRTC Makefiles])
-   fi
-
-   # XXX disable until we land the tranche with signaling
-   if test -n "$MOZ_WEBRTC_SIGNALING"; then
-     AC_MSG_RESULT("generating WebRTC/Signaling Makefiles...")
-     $PYTHON ${srcdir}/media/webrtc/trunk/build/gyp_chromium \
-       $GYP_WEBRTC_OPTIONS \
-       -D build_for_test=0 \
-       --generator-output=${_objdir}/media/webrtc/signaling \
-       ${srcdir}/media/webrtc/signaling/signaling.gyp
-     if test "$?" != 0; then
-        AC_MSG_ERROR([failed to generate WebRTC/Signaling Makefiles])
-     fi
-
-     AC_MSG_RESULT("generating WebRTC/SignalingTest Makefiles...")
-     $PYTHON ${srcdir}/media/webrtc/trunk/build/gyp_chromium --format=mozmake \
-       $GYP_WEBRTC_OPTIONS \
-       -D build_for_test=1 \
-       --generator-output=${_objdir}/media/webrtc/signalingtest \
-       ${srcdir}/media/webrtc/signaling/signaling.gyp
-     if test "$?" != 0; then
-       AC_MSG_ERROR([failed to generate WebRTC/SignalingTest Makefiles])
-     fi
-   fi
-
-   AC_MSG_RESULT("generating gtest Makefiles...")
-   # Ok to pass some extra -D's that are ignored here
-   $PYTHON ${srcdir}/media/webrtc/trunk/build/gyp_chromium \
-     $GYP_WEBRTC_OPTIONS \
-     --generator-output=${_objdir}/media/webrtc/trunk/testing/ \
-     ${srcdir}/media/webrtc/trunk/testing/gtest.gyp
-   if test "$?" != 0; then
-      AC_MSG_ERROR([failed to generate gtest Makefiles])
-   fi
-
-   AC_MSG_RESULT("generating nrappkit Makefiles...")
-   $PYTHON ${srcdir}/media/webrtc/trunk/build/gyp_chromium --format=mozmake \
-     $GYP_WEBRTC_OPTIONS \
-     --generator-output=${_objdir}/media/mtransport/third_party/nrappkit \
-     ${srcdir}/media/mtransport/third_party/nrappkit/nrappkit.gyp
-   if test "$?" != 0; then
-      AC_MSG_ERROR([failed to generate nrappkit Makefiles])
-   fi
-
-   AC_MSG_RESULT("generating nICEr Makefiles...")
-   $PYTHON ${srcdir}/media/webrtc/trunk/build/gyp_chromium --format=mozmake \
-     $GYP_WEBRTC_OPTIONS \
-     --generator-output=${_objdir}/media/mtransport/third_party/nICEr \
-     ${srcdir}/media/mtransport/third_party/nICEr/nicer.gyp
-   if test "$?" != 0; then
-      AC_MSG_ERROR([failed to generate nICEr Makefiles])
-   fi
-fi
-
 # Run jemalloc configure script
 
 if test -z "$MOZ_NATIVE_JEMALLOC" -a "$MOZ_MEMORY" && test -n "$MOZ_JEMALLOC3" -o -n "$MOZ_REPLACE_MALLOC"; then
   ac_configure_args="--build=$build --host=$target --enable-stats --with-jemalloc-prefix=je_"
   if test -n "$MOZ_REPLACE_MALLOC"; then
     # When using replace_malloc, we always want memalign and valloc exported from jemalloc.
     ac_configure_args="$ac_configure_args ac_cv_func_memalign=yes"
     ac_configure_args="$ac_configure_args ac_cv_func_valloc=yes"
--- a/content/base/public/nsDeprecatedOperationList.h
+++ b/content/base/public/nsDeprecatedOperationList.h
@@ -35,8 +35,9 @@ DEPRECATED_OPERATION(GetPreventDefault)
 DEPRECATED_OPERATION(GetSetUserData)
 DEPRECATED_OPERATION(MozGetAsFile)
 DEPRECATED_OPERATION(UseOfCaptureEvents)
 DEPRECATED_OPERATION(UseOfReleaseEvents)
 DEPRECATED_OPERATION(UseOfDOM3LoadMethod)
 DEPRECATED_OPERATION(ShowModalDialog)
 DEPRECATED_OPERATION(UnsafeCloneNode)
 DEPRECATED_OPERATION(UnsafeImportNode)
+DEPRECATED_OPERATION(Window_Content)
--- a/content/base/src/Link.cpp
+++ b/content/base/src/Link.cpp
@@ -209,17 +209,26 @@ Link::SetPathname(const nsAString &aPath
     return;
   }
 
   (void)url->SetFilePath(NS_ConvertUTF16toUTF8(aPathname));
   SetHrefAttribute(uri);
 }
 
 void
-Link::SetSearch(const nsAString &aSearch)
+Link::SetSearch(const nsAString& aSearch)
+{
+  SetSearchInternal(aSearch);
+  if (mSearchParams) {
+    mSearchParams->Invalidate();
+  }
+}
+
+void
+Link::SetSearchInternal(const nsAString& aSearch)
 {
   nsCOMPtr<nsIURI> uri(GetURIToMutate());
   nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
   if (!url) {
     // Ignore failures to be compatible with NS4.
     return;
   }
 
@@ -473,16 +482,19 @@ Link::ResetLinkState(bool aNotify, bool 
     UnregisterFromHistory();
   }
 
   // If we have an href, we should register with the history.
   mNeedsRegistration = aHasHref;
 
   // If we've cached the URI, reset always invalidates it.
   mCachedURI = nullptr;
+  if (mSearchParams) {
+    mSearchParams->Invalidate();
+  }
 
   // Update our state back to the default.
   mLinkState = defaultState;
 
   // We have to be very careful here: if aNotify is false we do NOT
   // want to call UpdateState, because that will call into LinkState()
   // and try to start off loads, etc.  But ResetLinkState is called
   // with aNotify false when things are in inconsistent states, so
@@ -558,10 +570,90 @@ Link::SizeOfExcludingThis(mozilla::Mallo
 
   // The following members don't need to be measured:
   // - mElement, because it is a pointer-to-self used to avoid QIs
   // - mHistory, because it is non-owning
 
   return n;
 }
 
+URLSearchParams*
+Link::GetSearchParams()
+{
+  CreateSearchParamsIfNeeded();
+  return mSearchParams;
+}
+
+void
+Link::SetSearchParams(URLSearchParams* aSearchParams)
+{
+  if (!aSearchParams) {
+    return;
+  }
+
+  if (!aSearchParams->HasURLAssociated()) {
+    MOZ_ASSERT(aSearchParams->IsValid());
+
+    mSearchParams = aSearchParams;
+    mSearchParams->SetObserver(this);
+  } else {
+    CreateSearchParamsIfNeeded();
+    mSearchParams->CopyFromURLSearchParams(*aSearchParams);
+  }
+
+  nsAutoString search;
+  mSearchParams->Serialize(search);
+  SetSearchInternal(search);
+}
+
+void
+Link::URLSearchParamsUpdated()
+{
+  MOZ_ASSERT(mSearchParams && mSearchParams->IsValid());
+
+  nsString search;
+  mSearchParams->Serialize(search);
+  SetSearchInternal(search);
+}
+
+void
+Link::URLSearchParamsNeedsUpdates()
+{
+  MOZ_ASSERT(mSearchParams);
+
+  nsAutoCString search;
+  nsCOMPtr<nsIURI> uri(GetURI());
+  nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
+  if (url) {
+    nsresult rv = url->GetQuery(search);
+    if (NS_FAILED(rv)) {
+      NS_WARNING("Failed to get the query from a nsIURL.");
+    }
+  }
+
+  mSearchParams->ParseInput(search);
+}
+
+void
+Link::CreateSearchParamsIfNeeded()
+{
+  if (!mSearchParams) {
+    mSearchParams = new URLSearchParams();
+    mSearchParams->SetObserver(this);
+    mSearchParams->Invalidate();
+  }
+}
+
+void
+Link::Unlink()
+{
+  mSearchParams = nullptr;
+}
+
+void
+Link::Traverse(nsCycleCollectionTraversalCallback &cb)
+{
+  Link* tmp = this;
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSearchParams);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/content/base/src/Link.h
+++ b/content/base/src/Link.h
@@ -8,29 +8,30 @@
  * This is the base class for all link classes.
  */
 
 #ifndef mozilla_dom_Link_h__
 #define mozilla_dom_Link_h__
 
 #include "mozilla/IHistory.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/dom/URLSearchParams.h"
 #include "nsEventStates.h"
 #include "nsIContent.h"
 
 namespace mozilla {
 namespace dom {
 
 class Element;
 
 #define MOZILLA_DOM_LINK_IMPLEMENTATION_IID               \
 { 0xb25edee6, 0xdd35, 0x4f8b,                             \
   { 0xab, 0x90, 0x66, 0xd0, 0xbd, 0x3c, 0x22, 0xd5 } }
 
-class Link : public nsISupports
+class Link : public URLSearchParamsObserver
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOM_LINK_IMPLEMENTATION_IID)
 
   /**
    * aElement is the element pointer corresponding to this link.
    */
   Link(Element* aElement);
@@ -56,26 +57,28 @@ public:
    */
   void SetProtocol(const nsAString &aProtocol);
   void SetUsername(const nsAString &aUsername);
   void SetPassword(const nsAString &aPassword);
   void SetHost(const nsAString &aHost);
   void SetHostname(const nsAString &aHostname);
   void SetPathname(const nsAString &aPathname);
   void SetSearch(const nsAString &aSearch);
+  void SetSearchParams(mozilla::dom::URLSearchParams* aSearchParams);
   void SetPort(const nsAString &aPort);
   void SetHash(const nsAString &aHash);
   void GetOrigin(nsAString &aOrigin);
   void GetProtocol(nsAString &_protocol);
   void GetUsername(nsAString &aUsername);
   void GetPassword(nsAString &aPassword);
   void GetHost(nsAString &_host);
   void GetHostname(nsAString &_hostname);
   void GetPathname(nsAString &_pathname);
   void GetSearch(nsAString &_search);
+  URLSearchParams* GetSearchParams();
   void GetPort(nsAString &_port);
   void GetHash(nsAString &_hash);
 
   /**
    * Invalidates any link caching, and resets the state to the default.
    *
    * @param aNotify
    *        true if ResetLinkState should notify the owning document about style
@@ -104,16 +107,20 @@ public:
    */
   virtual bool HasDeferredDNSPrefetchRequest() { return true; }
 
   virtual size_t
     SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
   bool ElementHasHref() const;
 
+  // URLSearchParamsObserver
+  void URLSearchParamsUpdated() MOZ_OVERRIDE;
+  void URLSearchParamsNeedsUpdates() MOZ_OVERRIDE;
+
 protected:
   virtual ~Link();
 
   /**
    * Return true if the link has associated URI.
    */
   bool HasURI() const
   {
@@ -122,39 +129,50 @@ protected:
     }
 
     return !!GetURI();
   }
 
   nsIURI* GetCachedURI() const { return mCachedURI; }
   bool HasCachedURI() const { return !!mCachedURI; }
 
+  // CC methods
+  void Unlink();
+  void Traverse(nsCycleCollectionTraversalCallback &cb);
+
 private:
   /**
    * Unregisters from History so this node no longer gets notifications about
    * changes to visitedness.
    */
   void UnregisterFromHistory();
 
   already_AddRefed<nsIURI> GetURIToMutate();
   void SetHrefAttribute(nsIURI *aURI);
 
+  void CreateSearchParamsIfNeeded();
+
+  void SetSearchInternal(const nsAString& aSearch);
+
   mutable nsCOMPtr<nsIURI> mCachedURI;
 
   Element * const mElement;
 
   // Strong reference to History.  The link has to unregister before History
   // can disappear.
   nsCOMPtr<IHistory> mHistory;
 
   uint16_t mLinkState;
 
   bool mNeedsRegistration;
 
   bool mRegistered;
+
+protected:
+  nsRefPtr<URLSearchParams> mSearchParams;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(Link, MOZILLA_DOM_LINK_IMPLEMENTATION_IID)
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Link_h__
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -6576,18 +6576,17 @@ nsContentUtils::GetSelectionInTextContro
   // Make sure aOutStartOffset <= aOutEndOffset.
   aOutStartOffset = std::min(anchorOffset, focusOffset);
   aOutEndOffset = std::max(anchorOffset, focusOffset);
 }
 
 nsIEditor*
 nsContentUtils::GetHTMLEditor(nsPresContext* aPresContext)
 {
-  nsCOMPtr<nsISupports> container = aPresContext->GetContainer();
-  nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container));
+  nsCOMPtr<nsIDocShell> docShell(aPresContext->GetDocShell());
   bool isEditable;
   if (!docShell ||
       NS_FAILED(docShell->GetEditable(&isEditable)) || !isEditable)
     return nullptr;
 
   nsCOMPtr<nsIEditor> editor;
   docShell->GetEditor(getter_AddRefs(editor));
   return editor;
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -6178,17 +6178,18 @@ nsDocument::DoNotifyPossibleTitleChange(
   mPendingTitleChangeEvent.Forget();
   mHaveFiredTitleChange = true;
 
   nsAutoString title;
   GetTitle(title);
 
   nsCOMPtr<nsIPresShell> shell = GetShell();
   if (shell) {
-    nsCOMPtr<nsISupports> container = shell->GetPresContext()->GetContainer();
+    nsCOMPtr<nsISupports> container =
+      shell->GetPresContext()->GetContainerWeak();
     if (container) {
       nsCOMPtr<nsIBaseWindow> docShellWin = do_QueryInterface(container);
       if (docShellWin) {
         docShellWin->SetTitle(title.get());
       }
     }
   }
 
--- a/content/events/src/nsDOMEvent.cpp
+++ b/content/events/src/nsDOMEvent.cpp
@@ -482,16 +482,37 @@ nsDOMEvent::PreventDefaultInternal(bool 
 
   // Note that even if preventDefault() has already been called by chrome,
   // a call of preventDefault() by content needs to overwrite
   // mDefaultPreventedByContent to true because in such case, defaultPrevented
   // must be true when web apps check it after they call preventDefault().
   if (!aCalledByDefaultHandler) {
     mEvent->mFlags.mDefaultPreventedByContent = true;
   }
+
+  if (!IsTrusted()) {
+    return;
+  }
+
+  WidgetDragEvent* dragEvent = mEvent->AsDragEvent();
+  if (!dragEvent) {
+    return;
+  }
+
+  nsCOMPtr<nsINode> node = do_QueryInterface(mEvent->currentTarget);
+  if (!node) {
+    nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(mEvent->currentTarget);
+    if (!win) {
+      return;
+    }
+    node = win->GetExtantDoc();
+  }
+  if (!nsContentUtils::IsChromeDoc(node->OwnerDoc())) {
+    dragEvent->mDefaultPreventedOnContent = true;
+  }
 }
 
 void
 nsDOMEvent::SetEventType(const nsAString& aEventTypeArg)
 {
   if (mIsMainThreadEvent) {
     mEvent->userType =
       nsContentUtils::GetEventIdAndAtom(aEventTypeArg, mEvent->eventStructType,
--- a/content/events/src/nsDOMUIEvent.cpp
+++ b/content/events/src/nsDOMUIEvent.cpp
@@ -58,17 +58,17 @@ nsDOMUIEvent::nsDOMUIEvent(mozilla::dom:
     default:
       mDetail = 0;
       break;
   }
 
   mView = nullptr;
   if (mPresContext)
   {
-    nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
+    nsISupports* container = mPresContext->GetContainerWeak();
     if (container)
     {
        nsCOMPtr<nsIDOMWindow> window = do_GetInterface(container);
        if (window)
           mView = do_QueryInterface(window);
     }
   }
 }
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -1450,17 +1450,17 @@ nsEventStateManager::ExecuteAccessKey(ns
 
 bool
 nsEventStateManager::GetAccessKeyLabelPrefix(nsAString& aPrefix)
 {
   aPrefix.Truncate();
   nsAutoString separator, modifierText;
   nsContentUtils::GetModifierSeparatorText(separator);
 
-  nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
+  nsCOMPtr<nsISupports> container = mPresContext->GetContainerWeak();
   int32_t modifierMask = GetAccessModifierMaskFor(container);
 
   if (modifierMask & NS_MODIFIER_CONTROL) {
     nsContentUtils::GetControlText(modifierText);
     aPrefix.Append(modifierText + separator);
   }
   if (modifierMask & NS_MODIFIER_META) {
     nsContentUtils::GetMetaText(modifierText);
@@ -1484,17 +1484,17 @@ nsEventStateManager::GetAccessKeyLabelPr
 void
 nsEventStateManager::HandleAccessKey(nsPresContext* aPresContext,
                                      WidgetKeyboardEvent* aEvent,
                                      nsEventStatus* aStatus,
                                      nsIDocShellTreeItem* aBubbledFrom,
                                      ProcessingAccessKeyState aAccessKeyState,
                                      int32_t aModifierMask)
 {
-  nsCOMPtr<nsISupports> pcContainer = aPresContext->GetContainer();
+  nsCOMPtr<nsISupports> pcContainer = aPresContext->GetContainerWeak();
 
   // Alt or other accesskey modifier is down, we may need to do an accesskey
   if (mAccessKeys.Count() > 0 &&
       aModifierMask == GetAccessModifierMaskFor(pcContainer)) {
     // Someone registered an accesskey.  Find and activate it.
     nsAutoTArray<uint32_t, 10> accessCharCodes;
     nsContentUtils::GetAccessKeyCandidates(aEvent, accessCharCodes);
     if (ExecuteAccessKey(accessCharCodes, aEvent->mFlags.mIsTrusted)) {
@@ -2199,17 +2199,17 @@ void
 nsEventStateManager::DetermineDragTarget(nsPresContext* aPresContext,
                                          nsIContent* aSelectionTarget,
                                          nsDOMDataTransfer* aDataTransfer,
                                          nsISelection** aSelection,
                                          nsIContent** aTargetNode)
 {
   *aTargetNode = nullptr;
 
-  nsCOMPtr<nsISupports> container = aPresContext->GetContainer();
+  nsCOMPtr<nsISupports> container = aPresContext->GetContainerWeak();
   nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(container);
   if (!window)
     return;
 
   // GetDragData determines if a selection, link or image in the content
   // should be dragged, and places the data associated with the drag in the
   // data transfer.
   // mGestureDownContent is the node where the mousedown event for the drag
@@ -2418,20 +2418,17 @@ nsEventStateManager::GetMarkupDocumentVi
   nsIDocument *doc = GetDocumentFromWindow(contentWindow);
   if(!doc) return NS_ERROR_FAILURE;
 
   nsIPresShell *presShell = doc->GetShell();
   if(!presShell) return NS_ERROR_FAILURE;
   nsPresContext *presContext = presShell->GetPresContext();
   if(!presContext) return NS_ERROR_FAILURE;
 
-  nsCOMPtr<nsISupports> pcContainer = presContext->GetContainer();
-  if(!pcContainer) return NS_ERROR_FAILURE;
-
-  nsCOMPtr<nsIDocShell> docshell(do_QueryInterface(pcContainer));
+  nsCOMPtr<nsIDocShell> docshell(presContext->GetDocShell());
   if(!docshell) return NS_ERROR_FAILURE;
 
   nsCOMPtr<nsIContentViewer> cv;
   docshell->GetContentViewer(getter_AddRefs(cv));
   if(!cv) return NS_ERROR_FAILURE;
 
   nsCOMPtr<nsIMarkupDocumentViewer> mv(do_QueryInterface(cv));
   if(!mv) return NS_ERROR_FAILURE;
@@ -2482,17 +2479,17 @@ nsEventStateManager::ChangeFullZoom(int3
   mv->SetFullZoom(fullzoom);
 
   return NS_OK;
 }
 
 void
 nsEventStateManager::DoScrollHistory(int32_t direction)
 {
-  nsCOMPtr<nsISupports> pcContainer(mPresContext->GetContainer());
+  nsCOMPtr<nsISupports> pcContainer(mPresContext->GetContainerWeak());
   if (pcContainer) {
     nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(pcContainer));
     if (webNav) {
       // positive direction to go back one step, nonpositive to go forward
       if (direction > 0)
         webNav->GoBack();
       else
         webNav->GoForward();
@@ -3566,19 +3563,20 @@ nsEventStateManager::PostHandleEvent(nsP
 
         // inform the drag session that a drop is allowed on this node.
         dragSession->SetDragAction(action);
         dragSession->SetCanDrop(action != nsIDragService::DRAGDROP_ACTION_NONE);
 
         // For now, do this only for dragover.
         //XXXsmaug dragenter needs some more work.
         if (aEvent->message == NS_DRAGDROP_OVER && !isChromeDoc) {
-          // Someone has called preventDefault(), check whether is was content.
+          // Someone has called preventDefault(), check whether is was on
+          // content or chrome.
           dragSession->SetOnlyChromeDrop(
-            !aEvent->mFlags.mDefaultPreventedByContent);
+            !dragEvent->mDefaultPreventedOnContent);
         }
       } else if (aEvent->message == NS_DRAGDROP_OVER && !isChromeDoc) {
         // No one called preventDefault(), so handle drop only in chrome.
         dragSession->SetOnlyChromeDrop(true);
       }
 
       // now set the drop effect in the initial dataTransfer. This ensures
       // that we can get the desired drop effect in the drop event.
@@ -3781,18 +3779,17 @@ nsEventStateManager::UpdateCursor(nsPres
       container = framecursor.mContainer;
       haveHotspot = framecursor.mHaveHotspot;
       hotspotX = framecursor.mHotspotX;
       hotspotY = framecursor.mHotspotY;
   }
 
   if (Preferences::GetBool("ui.use_activity_cursor", false)) {
     // Check whether or not to show the busy cursor
-    nsCOMPtr<nsISupports> pcContainer = aPresContext->GetContainer();
-    nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(pcContainer));
+    nsCOMPtr<nsIDocShell> docShell(aPresContext->GetDocShell());
     if (!docShell) return;
     uint32_t busyFlags = nsIDocShell::BUSY_FLAGS_NONE;
     docShell->GetBusyFlags(&busyFlags);
 
     // Show busy cursor everywhere before page loads
     // and just replace the arrow cursor after page starts loading
     if (busyFlags & nsIDocShell::BUSY_FLAGS_BUSY &&
           (cursor == NS_STYLE_CURSOR_AUTO || cursor == NS_STYLE_CURSOR_DEFAULT))
--- a/content/html/content/src/HTMLAnchorElement.cpp
+++ b/content/html/content/src/HTMLAnchorElement.cpp
@@ -36,18 +36,36 @@ enum {
 ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + 2);
 
 #undef ANCHOR_ELEMENT_FLAG_BIT
 
 HTMLAnchorElement::~HTMLAnchorElement()
 {
 }
 
-NS_IMPL_ISUPPORTS_INHERITED2(HTMLAnchorElement, nsGenericHTMLElement,
-                             nsIDOMHTMLAnchorElement, Link)
+NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLAnchorElement)
+  NS_INTERFACE_TABLE_INHERITED2(HTMLAnchorElement,
+                                nsIDOMHTMLAnchorElement,
+                                Link)
+NS_INTERFACE_TABLE_TAIL_INHERITING(nsGenericHTMLElement)
+
+NS_IMPL_ADDREF_INHERITED(HTMLAnchorElement, Element)
+NS_IMPL_RELEASE_INHERITED(HTMLAnchorElement, Element)
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLAnchorElement)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLAnchorElement,
+                                                  nsGenericHTMLElement)
+  tmp->Link::Traverse(cb);
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLAnchorElement,
+                                                nsGenericHTMLElement)
+  tmp->Link::Unlink();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_ELEMENT_CLONE(HTMLAnchorElement)
 
 JSObject*
 HTMLAnchorElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aScope)
 {
   return HTMLAnchorElementBinding::Wrap(aCx, aScope, this);
 }
--- a/content/html/content/src/HTMLAnchorElement.h
+++ b/content/html/content/src/HTMLAnchorElement.h
@@ -28,16 +28,20 @@ public:
     , Link(MOZ_THIS_IN_INITIALIZER_LIST())
   {
   }
   virtual ~HTMLAnchorElement();
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
+  // CC
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLAnchorElement,
+                                           nsGenericHTMLElement)
+
   virtual int32_t TabIndexDefault() MOZ_OVERRIDE;
   virtual bool Draggable() const MOZ_OVERRIDE;
 
   // nsIDOMHTMLAnchorElement
   NS_DECL_NSIDOMHTMLANCHORELEMENT
 
   // DOM memory reporter participant
   NS_DECL_SIZEOF_EXCLUDING_THIS
@@ -130,40 +134,24 @@ public:
     SetHTMLAttr(nsGkAtoms::type, aValue, rv);
   }
   // The XPCOM GetText is OK for us
   void SetText(const nsAString& aValue, mozilla::ErrorResult& rv)
   {
     rv = SetText(aValue);
   }
 
-  void GetOrigin(nsAString& aOrigin)
-  {
-    Link::GetOrigin(aOrigin);
-  }
-
-  void GetUsername(nsAString& aUsername)
-  {
-    Link::GetUsername(aUsername);
-  }
+  // Link::GetOrigin is OK for us
 
-  void SetUsername(const nsAString& aUsername)
-  {
-    Link::SetUsername(aUsername);
-  }
+  // Link::GetUsername is OK for us
+  // Link::SetUsername is OK for us
 
-  void GetPassword(nsAString& aPassword)
-  {
-    Link::GetPassword(aPassword);
-  }
+  // Link::Getpassword is OK for us
+  // Link::Setpassword is OK for us
 
-  void SetPassword(const nsAString& aPassword)
-  {
-    Link::SetPassword(aPassword);
-  }
   // The XPCOM URI decomposition attributes are fine for us
   void GetCoords(nsString& aValue)
   {
     GetHTMLAttr(nsGkAtoms::coords, aValue);
   }
   void SetCoords(const nsAString& aValue, mozilla::ErrorResult& rv)
   {
     SetHTMLAttr(nsGkAtoms::coords, aValue, rv);
--- a/content/html/content/src/HTMLAreaElement.cpp
+++ b/content/html/content/src/HTMLAreaElement.cpp
@@ -20,18 +20,36 @@ HTMLAreaElement::HTMLAreaElement(already
   , Link(MOZ_THIS_IN_INITIALIZER_LIST())
 {
 }
 
 HTMLAreaElement::~HTMLAreaElement()
 {
 }
 
-NS_IMPL_ISUPPORTS_INHERITED2(HTMLAreaElement, nsGenericHTMLElement,
-                             nsIDOMHTMLAreaElement, Link)
+NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLAreaElement)
+  NS_INTERFACE_TABLE_INHERITED2(HTMLAreaElement,
+                                nsIDOMHTMLAreaElement,
+                                Link)
+NS_INTERFACE_TABLE_TAIL_INHERITING(nsGenericHTMLElement)
+
+NS_IMPL_ADDREF_INHERITED(HTMLAreaElement, Element)
+NS_IMPL_RELEASE_INHERITED(HTMLAreaElement, Element)
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLAreaElement)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLAreaElement,
+                                                  nsGenericHTMLElement)
+  tmp->Link::Traverse(cb);
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLAreaElement,
+                                                nsGenericHTMLElement)
+  tmp->Link::Unlink();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_ELEMENT_CLONE(HTMLAreaElement)
 
 
 NS_IMPL_STRING_ATTR(HTMLAreaElement, Alt, alt)
 NS_IMPL_STRING_ATTR(HTMLAreaElement, Coords, coords)
 NS_IMPL_URI_ATTR(HTMLAreaElement, Href, href)
 NS_IMPL_BOOL_ATTR(HTMLAreaElement, NoHref, nohref)
--- a/content/html/content/src/HTMLAreaElement.h
+++ b/content/html/content/src/HTMLAreaElement.h
@@ -25,16 +25,20 @@ class HTMLAreaElement MOZ_FINAL : public
 {
 public:
   HTMLAreaElement(already_AddRefed<nsINodeInfo> aNodeInfo);
   virtual ~HTMLAreaElement();
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
+  // CC
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLAreaElement,
+                                           nsGenericHTMLElement)
+
   // DOM memory reporter participant
   NS_DECL_SIZEOF_EXCLUDING_THIS
 
   virtual int32_t TabIndexDefault() MOZ_OVERRIDE;
 
   // nsIDOMHTMLAreaElement
   NS_DECL_NSIDOMHTMLAREAELEMENT
 
@@ -103,43 +107,26 @@ public:
   }
 
   // The XPCOM GetPing is OK for us
   void SetPing(const nsAString& aPing, ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::ping, aPing, aError);
   }
 
-  void GetOrigin(nsAString &aOrigin)
-  {
-    Link::GetOrigin(aOrigin);
-  }
+  // The Link::GetOrigin is OK for us
 
   // The XPCOM GetProtocol is OK for us
   // The XPCOM SetProtocol is OK for us
 
-  void GetUsername(nsAString& aUsername)
-  {
-    Link::GetUsername(aUsername);
-  }
-
-  void SetUsername(const nsAString& aUsername)
-  {
-    Link::SetUsername(aUsername);
-  }
+  // The Link::GetUsername is OK for us
+  // The Link::SetUsername is OK for us
 
-  void GetPassword(nsAString& aPassword)
-  {
-    Link::GetPassword(aPassword);
-  }
-
-  void SetPassword(const nsAString& aPassword)
-  {
-    Link::SetPassword(aPassword);
-  }
+  // The Link::GetPassword is OK for us
+  // The Link::SetPassword is OK for us
 
   // The XPCOM GetHost is OK for us
   // The XPCOM SetHost is OK for us
 
   // The XPCOM GetHostname is OK for us
   // The XPCOM SetHostname is OK for us
 
   // The XPCOM GetPort is OK for us
@@ -149,16 +136,19 @@ public:
   // The XPCOM SetPathname is OK for us
 
   // The XPCOM GetSearch is OK for us
   // The XPCOM SetSearch is OK for us
 
   // The XPCOM GetHash is OK for us
   // The XPCOM SetHash is OK for us
 
+  // The Link::GetSearchParams is OK for us
+  // The Link::SetSearchParams is OK for us
+
   bool NoHref() const
   {
     return GetBoolAttr(nsGkAtoms::nohref);
   }
 
   void SetNoHref(bool aValue, ErrorResult& aError)
   {
     SetHTMLBoolAttr(nsGkAtoms::nohref, aValue, aError);
--- a/content/html/content/src/HTMLBodyElement.cpp
+++ b/content/html/content/src/HTMLBodyElement.cpp
@@ -125,54 +125,51 @@ BodyRule::MapRuleInfoInto(nsRuleData* aD
       }
     }
 
   }
 
   // if marginwidth or marginheight is set in the <frame> and not set in the <body>
   // reflect them as margin in the <body>
   if (bodyMarginWidth == -1 || bodyMarginHeight == -1) {
-    nsCOMPtr<nsISupports> container = aData->mPresContext->GetContainer();
-    if (container) {
-      nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container));
-      if (docShell) {
-        nscoord frameMarginWidth=-1;  // default value
-        nscoord frameMarginHeight=-1; // default value
-        docShell->GetMarginWidth(&frameMarginWidth); // -1 indicates not set   
-        docShell->GetMarginHeight(&frameMarginHeight); 
-        if ((frameMarginWidth >= 0) && (bodyMarginWidth == -1)) { // set in <frame> & not in <body> 
-          if (eCompatibility_NavQuirks == mode) {
-            if ((bodyMarginHeight == -1) && (0 > frameMarginHeight)) // nav quirk 
-              frameMarginHeight = 0;
-          }
+    nsCOMPtr<nsIDocShell> docShell(aData->mPresContext->GetDocShell());
+    if (docShell) {
+      nscoord frameMarginWidth=-1;  // default value
+      nscoord frameMarginHeight=-1; // default value
+      docShell->GetMarginWidth(&frameMarginWidth); // -1 indicates not set
+      docShell->GetMarginHeight(&frameMarginHeight);
+      if ((frameMarginWidth >= 0) && (bodyMarginWidth == -1)) { // set in <frame> & not in <body>
+        if (eCompatibility_NavQuirks == mode) {
+          if ((bodyMarginHeight == -1) && (0 > frameMarginHeight)) // nav quirk
+            frameMarginHeight = 0;
+        }
+      }
+      if ((frameMarginHeight >= 0) && (bodyMarginHeight == -1)) { // set in <frame> & not in <body>
+        if (eCompatibility_NavQuirks == mode) {
+          if ((bodyMarginWidth == -1) && (0 > frameMarginWidth)) // nav quirk
+            frameMarginWidth = 0;
         }
-        if ((frameMarginHeight >= 0) && (bodyMarginHeight == -1)) { // set in <frame> & not in <body> 
-          if (eCompatibility_NavQuirks == mode) {
-            if ((bodyMarginWidth == -1) && (0 > frameMarginWidth)) // nav quirk
-              frameMarginWidth = 0;
-          }
-        }
+      }
 
-        if ((bodyMarginWidth == -1) && (frameMarginWidth >= 0)) {
-          nsCSSValue* marginLeft = aData->ValueForMarginLeftValue();
-          if (marginLeft->GetUnit() == eCSSUnit_Null)
-            marginLeft->SetFloatValue((float)frameMarginWidth, eCSSUnit_Pixel);
-          nsCSSValue* marginRight = aData->ValueForMarginRightValue();
-          if (marginRight->GetUnit() == eCSSUnit_Null)
-            marginRight->SetFloatValue((float)frameMarginWidth, eCSSUnit_Pixel);
-        }
+      if ((bodyMarginWidth == -1) && (frameMarginWidth >= 0)) {
+        nsCSSValue* marginLeft = aData->ValueForMarginLeftValue();
+        if (marginLeft->GetUnit() == eCSSUnit_Null)
+          marginLeft->SetFloatValue((float)frameMarginWidth, eCSSUnit_Pixel);
+        nsCSSValue* marginRight = aData->ValueForMarginRightValue();
+        if (marginRight->GetUnit() == eCSSUnit_Null)
+          marginRight->SetFloatValue((float)frameMarginWidth, eCSSUnit_Pixel);
+      }
 
-        if ((bodyMarginHeight == -1) && (frameMarginHeight >= 0)) {
-          nsCSSValue* marginTop = aData->ValueForMarginTop();
-          if (marginTop->GetUnit() == eCSSUnit_Null)
-            marginTop->SetFloatValue((float)frameMarginHeight, eCSSUnit_Pixel);
-          nsCSSValue* marginBottom = aData->ValueForMarginBottom();
-          if (marginBottom->GetUnit() == eCSSUnit_Null)
-            marginBottom->SetFloatValue((float)frameMarginHeight, eCSSUnit_Pixel);
-        }
+      if ((bodyMarginHeight == -1) && (frameMarginHeight >= 0)) {
+        nsCSSValue* marginTop = aData->ValueForMarginTop();
+        if (marginTop->GetUnit() == eCSSUnit_Null)
+          marginTop->SetFloatValue((float)frameMarginHeight, eCSSUnit_Pixel);
+        nsCSSValue* marginBottom = aData->ValueForMarginBottom();
+        if (marginBottom->GetUnit() == eCSSUnit_Null)
+          marginBottom->SetFloatValue((float)frameMarginHeight, eCSSUnit_Pixel);
       }
     }
   }
 }
 
 #ifdef DEBUG
 /* virtual */ void
 BodyRule::List(FILE* out, int32_t aIndent) const
@@ -457,18 +454,17 @@ HTMLBodyElement::GetAssociatedEditor()
   }
 
   // For designmode, try to get document's editor
   nsPresContext* presContext = GetPresContext();
   if (!presContext) {
     return nullptr;
   }
 
-  nsCOMPtr<nsISupports> container = presContext->GetContainer();
-  nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(container);
+  nsCOMPtr<nsIDocShell> docShell = presContext->GetDocShell();
   if (!docShell) {
     return nullptr;
   }
 
   docShell->GetEditor(getter_AddRefs(editor));
   return editor.forget();
 }
 
--- a/content/html/content/src/HTMLLinkElement.cpp
+++ b/content/html/content/src/HTMLLinkElement.cpp
@@ -39,21 +39,23 @@ HTMLLinkElement::~HTMLLinkElement()
 {
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLLinkElement)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLLinkElement,
                                                   nsGenericHTMLElement)
   tmp->nsStyleLinkElement::Traverse(cb);
+  tmp->Link::Traverse(cb);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLLinkElement,
                                                 nsGenericHTMLElement)
   tmp->nsStyleLinkElement::Unlink();
+  tmp->Link::Unlink();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_ADDREF_INHERITED(HTMLLinkElement, Element)
 NS_IMPL_RELEASE_INHERITED(HTMLLinkElement, Element)
 
 
 // QueryInterface implementation for HTMLLinkElement
 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLLinkElement)
--- a/content/media/AudioStream.cpp
+++ b/content/media/AudioStream.cpp
@@ -71,16 +71,25 @@ bool AudioStream::sCubebLatencyPrefSet;
 }
 
 /*static*/ cubeb* AudioStream::GetCubebContext()
 {
   StaticMutexAutoLock lock(sMutex);
   return GetCubebContextUnlocked();
 }
 
+/*static*/ void AudioStream::InitPreferredSampleRate()
+{
+  StaticMutexAutoLock lock(sMutex);
+  if (sPreferredSampleRate != 0 ||
+      cubeb_get_preferred_sample_rate(GetCubebContextUnlocked(), &sPreferredSampleRate) != CUBEB_OK) {
+    sPreferredSampleRate = 44100;
+  }
+}
+
 /*static*/ cubeb* AudioStream::GetCubebContextUnlocked()
 {
   sMutex.AssertCurrentThreadOwns();
   if (sCubebContext ||
       cubeb_init(&sCubebContext, "AudioStream") == CUBEB_OK) {
     return sCubebContext;
   }
   NS_WARNING("cubeb_init failed");
@@ -261,35 +270,18 @@ int64_t AudioStream::GetWritten()
     return static_cast<int>(maxNumberOfChannels);
   }
 
   return 0;
 }
 
 /*static*/ int AudioStream::PreferredSampleRate()
 {
-  const int fallbackSampleRate = 44100;
-  StaticMutexAutoLock lock(sMutex);
-  if (sPreferredSampleRate != 0) {
-    return sPreferredSampleRate;
-  }
-
-  cubeb* cubebContext = GetCubebContextUnlocked();
-  if (!cubebContext) {
-    sPreferredSampleRate = fallbackSampleRate;
-  }
-  // Get the preferred samplerate for this platform, or fallback to something
-  // sensible if we fail. We cache the value, because this might be accessed
-  // often, and the complexity of the function call below depends on the
-  // backend used.
-  if (cubeb_get_preferred_sample_rate(cubebContext,
-                                      &sPreferredSampleRate) != CUBEB_OK) {
-    sPreferredSampleRate = fallbackSampleRate;
-  }
-
+  MOZ_ASSERT(sPreferredSampleRate,
+             "sPreferredSampleRate has not been initialized!");
   return sPreferredSampleRate;
 }
 
 static void SetUint16LE(uint8_t* aDest, uint16_t aValue)
 {
   aDest[0] = aValue & 0xFF;
   aDest[1] = aValue >> 8;
 }
--- a/content/media/AudioStream.h
+++ b/content/media/AudioStream.h
@@ -174,18 +174,21 @@ public:
 
   // Shutdown Audio Library. Some Audio backends require shutting down the
   // library after using it.
   static void ShutdownLibrary();
 
   // Returns the maximum number of channels supported by the audio hardware.
   static int MaxNumberOfChannels();
 
-  // Returns the samplerate the systems prefer, because it is the
-  // samplerate the hardware/mixer supports.
+  // Queries the samplerate the hardware/mixer runs at, and stores it.
+  // Can be called on any thread. When this returns, it is safe to call
+  // PreferredSampleRate without locking.
+  static void InitPreferredSampleRate();
+  // Get the aformentionned sample rate. Does not lock.
   static int PreferredSampleRate();
 
   AudioStream();
   ~AudioStream();
 
   enum LatencyRequest {
     HighLatency,
     LowLatency
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -1323,28 +1323,35 @@ MediaStreamGraphImpl::ForceShutDown()
   STREAM_LOG(PR_LOG_DEBUG, ("MediaStreamGraph %p ForceShutdown", this));
   {
     MonitorAutoLock lock(mMonitor);
     mForceShutDown = true;
     EnsureImmediateWakeUpLocked(lock);
   }
 }
 
+void
+MediaStreamGraphImpl::Init()
+{
+  AudioStream::InitPreferredSampleRate();
+}
+
 namespace {
 
 class MediaStreamGraphInitThreadRunnable : public nsRunnable {
 public:
   explicit MediaStreamGraphInitThreadRunnable(MediaStreamGraphImpl* aGraph)
     : mGraph(aGraph)
   {
   }
   NS_IMETHOD Run()
   {
     char aLocal;
     profiler_register_thread("MediaStreamGraph", &aLocal);
+    mGraph->Init();
     mGraph->RunThread();
     return NS_OK;
   }
 private:
   MediaStreamGraphImpl* mGraph;
 };
 
 class MediaStreamGraphThreadRunnable : public nsRunnable {
--- a/content/media/MediaStreamGraph.h
+++ b/content/media/MediaStreamGraph.h
@@ -440,16 +440,20 @@ public:
   void AddConsumer(MediaInputPort* aPort)
   {
     mConsumers.AppendElement(aPort);
   }
   void RemoveConsumer(MediaInputPort* aPort)
   {
     mConsumers.RemoveElement(aPort);
   }
+  uint32_t ConsumerCount()
+  {
+    return mConsumers.Length();
+  }
   const StreamBuffer& GetStreamBuffer() { return mBuffer; }
   GraphTime GetStreamBufferStartTime() { return mBufferStartTime; }
   /**
    * Convert graph time to stream time. aTime must be <= mStateComputedTime
    * to ensure we know exactly how much time this stream will be blocked during
    * the interval.
    */
   StreamTime GraphTimeToStreamTime(GraphTime aTime);
@@ -939,16 +943,20 @@ public:
   virtual void RemoveInput(MediaInputPort* aPort)
   {
     mInputs.RemoveElement(aPort);
   }
   bool HasInputPort(MediaInputPort* aPort)
   {
     return mInputs.Contains(aPort);
   }
+  uint32_t InputPortCount()
+  {
+    return mInputs.Length();
+  }
   virtual void DestroyImpl();
   /**
    * This gets called after we've computed the blocking states for all
    * streams (mBlocked is up to date up to mStateComputedTime).
    * Also, we've produced output for all streams up to this one. If this stream
    * is not in a cycle, then all its source streams have produced data.
    * Generate output up to mStateComputedTime.
    * This is called only on streams that have not finished.
--- a/content/media/MediaStreamGraphImpl.h
+++ b/content/media/MediaStreamGraphImpl.h
@@ -145,16 +145,20 @@ public:
    * This is called during application shutdown.
    */
   void ForceShutDown();
   /**
    * Shutdown() this MediaStreamGraph's threads and return when they've shut down.
    */
   void ShutdownThreads();
 
+  /**
+   * Called before the thread runs.
+   */
+  void Init();
   // The following methods run on the graph thread (or possibly the main thread if
   // mLifecycleState > LIFECYCLE_RUNNING)
   /**
    * Runs main control loop on the graph thread. Normally a single invocation
    * of this runs for the entire lifetime of the graph thread.
    */
   void RunThread();
   /**
--- a/content/media/webaudio/AudioContext.cpp
+++ b/content/media/webaudio/AudioContext.cpp
@@ -29,16 +29,17 @@
 #include "ChannelMergerNode.h"
 #include "ChannelSplitterNode.h"
 #include "MediaStreamAudioDestinationNode.h"
 #include "WaveShaperNode.h"
 #include "PeriodicWave.h"
 #include "ConvolverNode.h"
 #include "OscillatorNode.h"
 #include "nsNetUtil.h"
+#include "AudioStream.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(AudioContext)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioContext)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDestination)
@@ -60,22 +61,32 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_ADDREF_INHERITED(AudioContext, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(AudioContext, nsDOMEventTargetHelper)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AudioContext)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 static uint8_t gWebAudioOutputKey;
 
+float GetSampleRateForAudioContext(bool aIsOffline, float aSampleRate)
+{
+  if (aIsOffline) {
+    return aSampleRate;
+  } else {
+    AudioStream::InitPreferredSampleRate();
+    return static_cast<float>(AudioStream::PreferredSampleRate());
+  }
+}
+
 AudioContext::AudioContext(nsPIDOMWindow* aWindow,
                            bool aIsOffline,
                            uint32_t aNumberOfChannels,
                            uint32_t aLength,
                            float aSampleRate)
-  : mSampleRate(aIsOffline ? aSampleRate : IdealAudioRate())
+  : mSampleRate(GetSampleRateForAudioContext(aIsOffline, aSampleRate))
   , mNumberOfChannels(aNumberOfChannels)
   , mIsOffline(aIsOffline)
   , mIsStarted(!aIsOffline)
   , mIsShutDown(false)
 {
   nsDOMEventTargetHelper::BindToOwner(aWindow);
   aWindow->AddAudioContext(this);
   SetIsDOMBinding();
--- a/content/media/webaudio/ScriptProcessorNode.cpp
+++ b/content/media/webaudio/ScriptProcessorNode.cpp
@@ -62,16 +62,23 @@ private:
       mMutex.AssertCurrentThreadOwns();
       MOZ_ASSERT(!NS_IsMainThread());
       MOZ_ASSERT(ReadyToConsume() > 0);
       AudioChunk front = mBufferList.front();
       mBufferList.pop_front();
       return front;
     }
 
+    // Empties the buffer queue.
+    void Clear()
+    {
+      mMutex.AssertCurrentThreadOwns();
+      mBufferList.clear();
+    }
+
   private:
     typedef std::deque<AudioChunk> BufferList;
 
     // Synchronizes access to mBufferList.  Note that it's the responsibility
     // of the callers to perform the required locking, and we assert that every
     // time we access mBufferList.
     Mutex mMutex;
     // The list representing the queue.
@@ -162,16 +169,28 @@ public:
   }
 
   TrackTicks DelaySoFar() const
   {
     MOZ_ASSERT(!NS_IsMainThread());
     return mDelaySoFar == TRACK_TICKS_MAX ? 0 : mDelaySoFar;
   }
 
+  void Reset()
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+    mDelaySoFar = TRACK_TICKS_MAX;
+    mLatency = 0.0f;
+    {
+      MutexAutoLock lock(mOutputQueue.Lock());
+      mOutputQueue.Clear();
+    }
+    mLastEventTime = TimeStamp();
+  }
+
 private:
   OutputQueue mOutputQueue;
   // How much delay we've seen so far.  This measures the amount of delay
   // caused by the main thread lagging behind in producing output buffers.
   // TRACK_TICKS_MAX means that we have not received our first buffer yet.
   TrackTicks mDelaySoFar;
   // The samplerate of the context.
   float mSampleRate;
@@ -219,16 +238,28 @@ public:
     MutexAutoLock lock(NodeMutex());
 
     // If our node is dead, just output silence.
     if (!Node()) {
       aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
       return;
     }
 
+    // This node is not connected to anything. Per spec, we don't fire the
+    // onaudioprocess event. We also want to clear out the input and output
+    // buffer queue, and output a null buffer.
+    if (!(aStream->ConsumerCount() ||
+          aStream->AsProcessedStream()->InputPortCount())) {
+      aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
+      mSharedBuffers->Reset();
+      mSeenNonSilenceInput = false;
+      mInputWriteIndex = 0;
+      return;
+    }
+
     // First, record our input buffer
     for (uint32_t i = 0; i < mInputChannels.Length(); ++i) {
       if (aInput.IsNull()) {
         PodZero(mInputChannels[i] + mInputWriteIndex,
                 aInput.GetDuration());
       } else {
         mSeenNonSilenceInput = true;
         MOZ_ASSERT(aInput.GetDuration() == WEBAUDIO_BLOCK_SIZE, "sanity check");
--- a/content/media/webaudio/test/mochitest.ini
+++ b/content/media/webaudio/test/mochitest.ini
@@ -103,15 +103,16 @@ support-files =
 [test_pannerNodeAbove.html]
 [test_pannerNodeChannelCount.html]
 [test_pannerNodeTail.html]
 [test_pannerNode_equalPower.html]
 [test_periodicWave.html]
 [test_scriptProcessorNode.html]
 [test_scriptProcessorNodeChannelCount.html]
 [test_scriptProcessorNodeZeroInputOutput.html]
+[test_scriptProcessorNodeNotConnected.html]
 [test_singleSourceDest.html]
 [test_waveShaper.html]
 [test_waveShaperNoCurve.html]
 [test_waveShaperZeroLengthCurve.html]
 [test_audioDestinationNode.html]
 [test_mozaudiochannel.html]
 [test_waveDecoder.html]
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/test/test_scriptProcessorNodeNotConnected.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test AudioBufferSourceNode: should not fire audioprocess if not connected.</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="webaudio.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+  var context = new AudioContext();
+
+  var sp = context.createScriptProcessor(2048, 2, 2);
+  sp.onaudioprocess = function(e) {
+    ok(false, "Should not call onaudioprocess if the node is not connected.");
+    sp.onaudioprocess = null;
+    SimpleTest.finish();
+  };
+  setTimeout(function() {
+    console.log(sp.onaudioprocess);
+    if (sp.onaudioprocess) {
+      ok(true, "onaudioprocess not fired.");
+      SimpleTest.finish();
+    }
+  }, 4000);
+});
+</script>
+</pre>
+</body>
+</html>
--- a/content/media/webaudio/test/test_scriptProcessorNodeZeroInputOutput.html
+++ b/content/media/webaudio/test/test_scriptProcessorNodeZeroInputOutput.html
@@ -23,15 +23,17 @@ addLoadEvent(function() {
     sp = context.createScriptProcessor(2048, 2, 0);
     sp.onaudioprocess = function(e) {
       is(e.inputBuffer.numberOfChannels, 2, "Should have 2 input channels");
       is(e.outputBuffer.numberOfChannels, 0, "Should have 0 output channels");
       sp.onaudioprocess = null;
 
       SimpleTest.finish();
     };
+    sp.connect(context.destination);
   };
+  sp.connect(context.destination);
 });
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/smil/nsDOMTimeEvent.cpp
+++ b/content/smil/nsDOMTimeEvent.cpp
@@ -28,17 +28,17 @@ nsDOMTimeEvent::nsDOMTimeEvent(mozilla::
   if (mEvent->eventStructType == NS_SMIL_TIME_EVENT) {
     mDetail = mEvent->AsUIEvent()->detail;
   }
 
   mEvent->mFlags.mBubbles = false;
   mEvent->mFlags.mCancelable = false;
 
   if (mPresContext) {
-    nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
+    nsISupports* container = mPresContext->GetContainerWeak();
     if (container) {
       nsCOMPtr<nsIDOMWindow> window = do_GetInterface(container);
       if (window) {
         mView = do_QueryInterface(window);
       }
     }
   }
 }
--- a/content/svg/content/src/SVGAElement.cpp
+++ b/content/svg/content/src/SVGAElement.cpp
@@ -29,22 +29,38 @@ nsSVGElement::StringInfo SVGAElement::sS
   { &nsGkAtoms::href, kNameSpaceID_XLink, true },
   { &nsGkAtoms::target, kNameSpaceID_None, true }
 };
 
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
-NS_IMPL_ISUPPORTS_INHERITED4(SVGAElement, SVGAElementBase,
-                             nsIDOMNode,
-                             nsIDOMElement,
-                             nsIDOMSVGElement,
-                             Link)
+NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(SVGAElement)
+  NS_INTERFACE_TABLE_INHERITED4(SVGAElement,
+                                nsIDOMNode,
+                                nsIDOMElement,
+                                nsIDOMSVGElement,
+                                Link)
+NS_INTERFACE_TABLE_TAIL_INHERITING(SVGAElementBase)
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(SVGAElement)
 
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SVGAElement,
+                                                  SVGAElementBase)
+  tmp->Link::Traverse(cb);
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SVGAElement,
+                                                SVGAElementBase)
+  tmp->Link::Unlink();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_ADDREF_INHERITED(SVGAElement, SVGAElementBase)
+NS_IMPL_RELEASE_INHERITED(SVGAElement, SVGAElementBase)
 
 //----------------------------------------------------------------------
 // Implementation
 
 SVGAElement::SVGAElement(already_AddRefed<nsINodeInfo> aNodeInfo)
   : SVGAElementBase(aNodeInfo)
   , Link(MOZ_THIS_IN_INITIALIZER_LIST())
 {
--- a/content/svg/content/src/SVGAElement.h
+++ b/content/svg/content/src/SVGAElement.h
@@ -25,16 +25,17 @@ protected:
   SVGAElement(already_AddRefed<nsINodeInfo> aNodeInfo);
   friend nsresult (::NS_NewSVGAElement(nsIContent **aResult,
                                        already_AddRefed<nsINodeInfo> aNodeInfo));
   virtual JSObject* WrapNode(JSContext *cx,
                              JS::Handle<JSObject*> scope) MOZ_OVERRIDE;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SVGAElement, SVGAElementBase)
 
   // nsINode interface methods
   virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor) MOZ_OVERRIDE;
   virtual nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor) MOZ_OVERRIDE;
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
 
   // nsIContent
   virtual nsresult BindToTree(nsIDocument *aDocument, nsIContent *aParent,
--- a/content/xul/document/src/XULDocument.cpp
+++ b/content/xul/document/src/XULDocument.cpp
@@ -2037,22 +2037,17 @@ XULDocument::StartLayout(void)
     nsCOMPtr<nsIPresShell> shell = GetShell();
     if (shell) {
         // Resize-reflow this time
         nsPresContext *cx = shell->GetPresContext();
         NS_ASSERTION(cx != nullptr, "no pres context");
         if (! cx)
             return NS_ERROR_UNEXPECTED;
 
-        nsCOMPtr<nsISupports> container = cx->GetContainer();
-        NS_ASSERTION(container != nullptr, "pres context has no container");
-        if (! container)
-            return NS_ERROR_UNEXPECTED;
-
-        nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container));
+        nsCOMPtr<nsIDocShell> docShell = cx->GetDocShell();
         NS_ASSERTION(docShell != nullptr, "container is not a docshell");
         if (! docShell)
             return NS_ERROR_UNEXPECTED;
 
         nsresult rv = NS_OK;
         nsRect r = cx->GetVisibleArea();
         rv = shell->Initialize(r.width, r.height);
         NS_ENSURE_SUCCESS(rv, rv);
--- a/dom/base/URL.cpp
+++ b/dom/base/URL.cpp
@@ -15,16 +15,25 @@
 #include "nsIIOService.h"
 #include "nsEscape.h"
 #include "nsNetCID.h"
 #include "nsIURL.h"
 
 namespace mozilla {
 namespace dom {
 
+NS_IMPL_CYCLE_COLLECTION_1(URL, mSearchParams)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(URL)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(URL)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URL)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
 URL::URL(nsIURI* aURI)
   : mURI(aURI)
 {
 }
 
 JSObject*
 URL::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
@@ -207,16 +216,20 @@ URL::SetHref(const nsAString& aHref, Err
   rv = ioService->NewURI(href, nullptr, nullptr, getter_AddRefs(uri));
   if (NS_FAILED(rv)) {
     nsAutoString label(aHref);
     aRv.ThrowTypeError(MSG_INVALID_URL, &label);
     return;
   }
 
   aRv = mURI->SetSpec(href);
+
+  if (mSearchParams) {
+    mSearchParams->Invalidate();
+  }
 }
 
 void
 URL::GetOrigin(nsString& aOrigin) const
 {
   nsContentUtils::GetUTFNonNullOrigin(mURI, aOrigin);
 }
 
@@ -284,16 +297,43 @@ URL::GetHost(nsString& aHost) const
 
 void
 URL::SetHost(const nsAString& aHost)
 {
   mURI->SetHostPort(NS_ConvertUTF16toUTF8(aHost));
 }
 
 void
+URL::URLSearchParamsUpdated()
+{
+  MOZ_ASSERT(mSearchParams && mSearchParams->IsValid());
+
+  nsAutoString search;
+  mSearchParams->Serialize(search);
+  SetSearchInternal(search);
+}
+
+void
+URL::URLSearchParamsNeedsUpdates()
+{
+  MOZ_ASSERT(mSearchParams);
+
+  nsAutoCString search;
+  nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
+  if (url) {
+    nsresult rv = url->GetQuery(search);
+    if (NS_FAILED(rv)) {
+      NS_WARNING("Failed to get the query from a nsIURL.");
+    }
+  }
+
+  mSearchParams->ParseInput(search);
+}
+
+void
 URL::GetHostname(nsString& aHostname) const
 {
   URL_GETTER(aHostname, GetHost);
 }
 
 void
 URL::SetHostname(const nsAString& aHostname)
 {
@@ -380,25 +420,64 @@ URL::GetSearch(nsString& aSearch) const
   if (NS_SUCCEEDED(rv) && !search.IsEmpty()) {
     CopyUTF8toUTF16(NS_LITERAL_CSTRING("?") + search, aSearch);
   }
 }
 
 void
 URL::SetSearch(const nsAString& aSearch)
 {
+  SetSearchInternal(aSearch);
+
+  if (mSearchParams) {
+    mSearchParams->Invalidate();
+  }
+}
+
+void
+URL::SetSearchInternal(const nsAString& aSearch)
+{
   nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
   if (!url) {
     // Ignore failures to be compatible with NS4.
     return;
   }
 
   url->SetQuery(NS_ConvertUTF16toUTF8(aSearch));
 }
 
+URLSearchParams*
+URL::GetSearchParams()
+{
+  CreateSearchParamsIfNeeded();
+  return mSearchParams;
+}
+
+void
+URL::SetSearchParams(URLSearchParams* aSearchParams)
+{
+  if (!aSearchParams) {
+    return;
+  }
+
+  if (!aSearchParams->HasURLAssociated()) {
+    MOZ_ASSERT(aSearchParams->IsValid());
+
+    mSearchParams = aSearchParams;
+    mSearchParams->SetObserver(this);
+  } else {
+    CreateSearchParamsIfNeeded();
+    mSearchParams->CopyFromURLSearchParams(*aSearchParams);
+  }
+
+  nsAutoString search;
+  mSearchParams->Serialize(search);
+  SetSearchInternal(search);
+}
+
 void
 URL::GetHash(nsString& aHash) const
 {
   aHash.Truncate();
 
   nsAutoCString ref;
   nsresult rv = mURI->GetRef(ref);
   if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) {
@@ -417,10 +496,20 @@ URL::SetHash(const nsAString& aHash)
 bool IsChromeURI(nsIURI* aURI)
 {
   bool isChrome = false;
   if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)))
       return isChrome;
   return false;
 }
 
+void
+URL::CreateSearchParamsIfNeeded()
+{
+  if (!mSearchParams) {
+    mSearchParams = new URLSearchParams();
+    mSearchParams->SetObserver(this);
+    mSearchParams->Invalidate();
+  }
+}
+
 }
 }
--- a/dom/base/URL.h
+++ b/dom/base/URL.h
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef URL_h___
 #define URL_h___
 
 #include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/URLSearchParams.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsAutoPtr.h"
 #include "nsString.h"
 
 class nsIDOMBlob;
 class nsISupports;
 class nsIURI;
 
@@ -24,20 +25,21 @@ namespace dom {
 class MediaSource;
 class GlobalObject;
 struct objectURLOptions;
 
 namespace workers {
 class URLProxy;
 }
 
-class URL MOZ_FINAL
+class URL MOZ_FINAL : public URLSearchParamsObserver
 {
 public:
-  NS_INLINE_DECL_REFCOUNTING(URL)
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(URL)
 
   URL(nsIURI* aURI);
 
   // WebIDL methods
   nsISupports* GetParentObject() const
   {
     return nullptr;
   }
@@ -103,34 +105,47 @@ public:
   void GetPathname(nsString& aPathname) const;
 
   void SetPathname(const nsAString& aPathname);
 
   void GetSearch(nsString& aRetval) const;
 
   void SetSearch(const nsAString& aArg);
 
+  URLSearchParams* GetSearchParams();
+
+  void SetSearchParams(URLSearchParams* aSearchParams);
+
   void GetHash(nsString& aRetval) const;
 
   void SetHash(const nsAString& aArg);
 
+  // URLSearchParamsObserver
+  void URLSearchParamsUpdated() MOZ_OVERRIDE;
+  void URLSearchParamsNeedsUpdates() MOZ_OVERRIDE;
+
 private:
   nsIURI* GetURI() const
   {
     return mURI;
   }
 
+  void CreateSearchParamsIfNeeded();
+
+  void SetSearchInternal(const nsAString& aSearch);
+
   static void CreateObjectURLInternal(const GlobalObject& aGlobal,
                                       nsISupports* aObject,
                                       const nsACString& aScheme,
                                       const objectURLOptions& aOptions,
                                       nsString& aResult,
                                       ErrorResult& aError);
 
   nsCOMPtr<nsIURI> mURI;
+  nsRefPtr<URLSearchParams> mSearchParams;
 
   friend class mozilla::dom::workers::URLProxy;
 };
 
 bool IsChromeURI(nsIURI* aURI);
 
 }
 }
new file mode 100644
--- /dev/null
+++ b/dom/base/URLSearchParams.cpp
@@ -0,0 +1,397 @@
+/* -*- 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 "URLSearchParams.h"
+#include "mozilla/dom/URLSearchParamsBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(URLSearchParams, mObserver)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(URLSearchParams)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(URLSearchParams)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URLSearchParams)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+URLSearchParams::URLSearchParams()
+  : mValid(false)
+{
+  SetIsDOMBinding();
+}
+
+URLSearchParams::~URLSearchParams()
+{
+  DeleteAll();
+}
+
+JSObject*
+URLSearchParams::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
+{
+  return URLSearchParamsBinding::Wrap(aCx, aScope, this);
+}
+
+/* static */ already_AddRefed<URLSearchParams>
+URLSearchParams::Constructor(const GlobalObject& aGlobal,
+                             const nsAString& aInit,
+                             ErrorResult& aRv)
+{
+  nsRefPtr<URLSearchParams> sp = new URLSearchParams();
+  sp->ParseInput(NS_ConvertUTF16toUTF8(aInit));
+  return sp.forget();
+}
+
+/* static */ already_AddRefed<URLSearchParams>
+URLSearchParams::Constructor(const GlobalObject& aGlobal,
+                             URLSearchParams& aInit,
+                             ErrorResult& aRv)
+{
+  nsRefPtr<URLSearchParams> sp = new URLSearchParams();
+  aInit.mSearchParams.EnumerateRead(CopyEnumerator, sp);
+  sp->mValid = true;
+  return sp.forget();
+}
+
+void
+URLSearchParams::ParseInput(const nsACString& aInput)
+{
+  // Remove all the existing data before parsing a new input.
+  DeleteAll();
+
+  nsACString::const_iterator start, end;
+  aInput.BeginReading(start);
+  aInput.EndReading(end);
+  nsACString::const_iterator iter(start);
+
+  while (start != end) {
+    nsAutoCString string;
+
+    if (FindCharInReadable('&', iter, end)) {
+      string.Assign(Substring(start, iter));
+      start = ++iter;
+    } else {
+      string.Assign(Substring(start, end));
+      start = end;
+    }
+
+    if (string.IsEmpty()) {
+      continue;
+    }
+
+    nsACString::const_iterator eqStart, eqEnd;
+    string.BeginReading(eqStart);
+    string.EndReading(eqEnd);
+    nsACString::const_iterator eqIter(eqStart);
+
+    nsAutoCString name;
+    nsAutoCString value;
+
+    if (FindCharInReadable('=', eqIter, eqEnd)) {
+      name.Assign(Substring(eqStart, eqIter));
+
+      ++eqIter;
+      value.Assign(Substring(eqIter, eqEnd));
+    } else {
+      name.Assign(string);
+    }
+
+    nsAutoCString decodedName;
+    DecodeString(name, decodedName);
+
+    nsAutoCString decodedValue;
+    DecodeString(value, decodedValue);
+
+    AppendInternal(NS_ConvertUTF8toUTF16(decodedName),
+                   NS_ConvertUTF8toUTF16(decodedValue));
+  }
+
+  mValid = true;
+}
+
+void
+URLSearchParams::DecodeString(const nsACString& aInput, nsACString& aOutput)
+{
+  nsACString::const_iterator start, end;
+  aInput.BeginReading(start);
+  aInput.EndReading(end);
+
+  while (start != end) {
+    // replace '+' with U+0020
+    if (*start == '+') {
+      aOutput.Append(' ');
+      ++start;
+      continue;
+    }
+
+    // Percent decode algorithm
+    if (*start == '%') {
+      nsACString::const_iterator first(start);
+      ++first;
+
+      nsACString::const_iterator second(first);
+      ++second;
+
+#define ASCII_HEX_DIGIT( x )    \
+  ((x >= 0x41 && x <= 0x46) ||  \
+   (x >= 0x61 && x <= 0x66) ||  \
+   (x >= 0x30 && x <= 0x39))
+
+#define HEX_DIGIT( x )              \
+   (*x >= 0x30 && *x <= 0x39        \
+     ? *x - 0x30                    \
+     : (*x >= 0x41 && *x <= 0x46    \
+        ? *x - 0x37                 \
+        : *x - 0x57))
+
+      if (first != end && second != end &&
+          ASCII_HEX_DIGIT(*first) && ASCII_HEX_DIGIT(*second)) {
+        aOutput.Append(HEX_DIGIT(first) * 16 + HEX_DIGIT(second));
+        start = ++second;
+        continue;
+
+      } else {
+        aOutput.Append('%');
+        ++start;
+        continue;
+      }
+    }
+
+    aOutput.Append(*start);
+    ++start;
+  }
+}
+
+void
+URLSearchParams::CopyFromURLSearchParams(URLSearchParams& aSearchParams)
+{
+  // The other SearchParams must be valid before copying its data.
+  aSearchParams.Validate();
+
+  // Remove all the existing data before parsing a new input.
+  DeleteAll();
+  aSearchParams.mSearchParams.EnumerateRead(CopyEnumerator, this);
+  mValid = true;
+}
+
+/* static */ PLDHashOperator
+URLSearchParams::CopyEnumerator(const nsAString& aName,
+                                nsTArray<nsString>* aArray,
+                                void *userData)
+{
+  URLSearchParams* aSearchParams = static_cast<URLSearchParams*>(userData);
+
+  nsTArray<nsString>* newArray = new nsTArray<nsString>();
+  newArray->AppendElements(*aArray);
+
+  aSearchParams->mSearchParams.Put(aName, newArray);
+  return PL_DHASH_NEXT;
+}
+
+void
+URLSearchParams::SetObserver(URLSearchParamsObserver* aObserver)
+{
+  MOZ_ASSERT(!mObserver);
+  mObserver = aObserver;
+}
+
+void
+URLSearchParams::Validate()
+{
+  MOZ_ASSERT(mValid || mObserver);
+  if (!mValid) {
+    mObserver->URLSearchParamsNeedsUpdates();
+    MOZ_ASSERT(mValid);
+  }
+}
+
+void
+URLSearchParams::Get(const nsAString& aName, nsString& aRetval)
+{
+  Validate();
+
+  nsTArray<nsString>* array;
+  if (!mSearchParams.Get(aName, &array)) {
+    aRetval.Truncate();
+    return;
+  }
+
+  aRetval.Assign(array->ElementAt(0));
+}
+
+void
+URLSearchParams::GetAll(const nsAString& aName, nsTArray<nsString>& aRetval)
+{
+  Validate();
+
+  nsTArray<nsString>* array;
+  if (!mSearchParams.Get(aName, &array)) {
+    return;
+  }
+
+  aRetval.AppendElements(*array);
+}
+
+void
+URLSearchParams::Set(const nsAString& aName, const nsAString& aValue)
+{
+  // Before setting any new value we have to be sure to have all the previous
+  // values in place.
+  Validate();
+
+  nsTArray<nsString>* array;
+  if (!mSearchParams.Get(aName, &array)) {
+    array = new nsTArray<nsString>();
+    array->AppendElement(aValue);
+    mSearchParams.Put(aName, array);
+  } else {
+    array->ElementAt(0) = aValue;
+  }
+
+  NotifyObserver();
+}
+
+void
+URLSearchParams::Append(const nsAString& aName, const nsAString& aValue)
+{
+  // Before setting any new value we have to be sure to have all the previous
+  // values in place.
+  Validate();
+
+  AppendInternal(aName, aValue);
+  NotifyObserver();
+}
+
+void
+URLSearchParams::AppendInternal(const nsAString& aName, const nsAString& aValue)
+{
+  nsTArray<nsString>* array;
+  if (!mSearchParams.Get(aName, &array)) {
+    array = new nsTArray<nsString>();
+    mSearchParams.Put(aName, array);
+  }
+
+  array->AppendElement(aValue);
+}
+
+bool
+URLSearchParams::Has(const nsAString& aName)
+{
+  Validate();
+  return mSearchParams.Get(aName, nullptr);
+}
+
+void
+URLSearchParams::Delete(const nsAString& aName)
+{
+  // Before deleting any value we have to be sure to have all the previous
+  // values in place.
+  Validate();
+
+  nsTArray<nsString>* array;
+  if (!mSearchParams.Get(aName, &array)) {
+    return;
+  }
+
+  mSearchParams.Remove(aName);
+
+  NotifyObserver();
+}
+
+uint32_t
+URLSearchParams::Size()
+{
+  Validate();
+  return mSearchParams.Count();
+}
+
+void
+URLSearchParams::DeleteAll()
+{
+  mSearchParams.Clear();
+}
+
+class MOZ_STACK_CLASS SerializeData
+{
+public:
+  SerializeData()
+    : mFirst(true)
+  {}
+
+  nsAutoString mValue;
+  bool mFirst;
+
+  void Serialize(const nsCString& aInput)
+  {
+    const unsigned char* p = (const unsigned char*) aInput.get();
+
+    while (p && *p) {
+      // ' ' to '+'
+      if (*p == 0x20) {
+        mValue.Append(0x2B);
+      // Percent Encode algorithm
+      } else if (*p == 0x2A || *p == 0x2D || *p == 0x2E ||
+                 (*p >= 0x30 && *p <= 0x39) ||
+                 (*p >= 0x41 && *p <= 0x5A) || *p == 0x5F ||
+                 (*p >= 0x61 && *p <= 0x7A)) {
+        mValue.Append(*p);
+      } else {
+        mValue.AppendPrintf("%%%X", *p);
+      }
+
+      ++p;
+    }
+  }
+};
+
+void
+URLSearchParams::Serialize(nsAString& aValue)
+{
+  MOZ_ASSERT(mValid);
+
+  SerializeData data;
+  mSearchParams.EnumerateRead(SerializeEnumerator, &data);
+  aValue.Assign(data.mValue);
+}
+
+/* static */ PLDHashOperator
+URLSearchParams::SerializeEnumerator(const nsAString& aName,
+                                     nsTArray<nsString>* aArray,
+                                     void *userData)
+{
+  SerializeData* data = static_cast<SerializeData*>(userData);
+
+  for (uint32_t i = 0, len = aArray->Length(); i < len; ++i) {
+    if (data->mFirst) {
+      data->mFirst = false;
+    } else {
+      data->mValue.Append(NS_LITERAL_STRING("&"));
+    }
+
+    data->Serialize(NS_ConvertUTF16toUTF8(aName));
+    data->mValue.Append(NS_LITERAL_STRING("="));
+    data->Serialize(NS_ConvertUTF16toUTF8(aArray->ElementAt(i)));
+  }
+
+  return PL_DHASH_NEXT;
+}
+
+void
+URLSearchParams::NotifyObserver()
+{
+  if (mObserver) {
+    mObserver->URLSearchParamsUpdated();
+  }
+}
+
+void
+URLSearchParams::Invalidate()
+{
+  mValid = false;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/base/URLSearchParams.h
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_URLSearchParams_h
+#define mozilla_dom_URLSearchParams_h
+
+#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/ErrorResult.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsWrapperCache.h"
+#include "nsClassHashtable.h"
+#include "nsHashKeys.h"
+#include "nsISupports.h"
+
+namespace mozilla {
+namespace dom {
+
+class URLSearchParamsObserver : public nsISupports
+{
+public:
+  virtual ~URLSearchParamsObserver() {}
+
+  virtual void URLSearchParamsUpdated() = 0;
+  virtual void URLSearchParamsNeedsUpdates() = 0;
+};
+
+class URLSearchParams MOZ_FINAL : public nsISupports,
+                                  public nsWrapperCache
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(URLSearchParams)
+
+  URLSearchParams();
+  ~URLSearchParams();
+
+  bool HasURLAssociated() const
+  {
+    return !!mObserver;
+  }
+
+  // WebIDL methods
+  nsISupports* GetParentObject() const
+  {
+    return nullptr;
+  }
+
+  virtual JSObject*
+  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+
+  static already_AddRefed<URLSearchParams>
+  Constructor(const GlobalObject& aGlobal, const nsAString& aInit,
+              ErrorResult& aRv);
+
+  static already_AddRefed<URLSearchParams>
+  Constructor(const GlobalObject& aGlobal, URLSearchParams& aInit,
+              ErrorResult& aRv);
+
+  void ParseInput(const nsACString& aInput);
+
+  void CopyFromURLSearchParams(URLSearchParams& aSearchParams);
+
+  void SetObserver(URLSearchParamsObserver* aObserver);
+
+  void Invalidate();
+
+  bool IsValid() const
+  {
+    return mValid;
+  }
+
+  void Serialize(nsAString& aValue);
+
+  void Get(const nsAString& aName, nsString& aRetval);
+
+  void GetAll(const nsAString& aName, nsTArray<nsString >& aRetval);
+
+  void Set(const nsAString& aName, const nsAString& aValue);
+
+  void Append(const nsAString& aName, const nsAString& aValue);
+
+  bool Has(const nsAString& aName);
+
+  void Delete(const nsAString& aName);
+
+  uint32_t Size();
+
+private:
+  void AppendInternal(const nsAString& aName, const nsAString& aValue);
+
+  void DeleteAll();
+
+  void DecodeString(const nsACString& aInput, nsACString& aOutput);
+
+  void NotifyObserver();
+
+  static PLDHashOperator
+  CopyEnumerator(const nsAString& aName, nsTArray<nsString>* aArray,
+                 void *userData);
+
+  static PLDHashOperator
+  SerializeEnumerator(const nsAString& aName, nsTArray<nsString>* aArray,
+                      void *userData);
+
+  void
+  Validate();
+
+  nsClassHashtable<nsStringHashKey, nsTArray<nsString>> mSearchParams;
+
+  nsRefPtr<URLSearchParamsObserver> mObserver;
+
+  bool mValid;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_URLSearchParams_h */
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -57,16 +57,17 @@ EXPORTS.mozilla.dom += [
     'MessageChannel.h',
     'MessagePort.h',
     'MessagePortList.h',
     'Navigator.h',
     'ScreenOrientation.h',
     'ScriptSettings.h',
     'StructuredCloneTags.h',
     'URL.h',
+    'URLSearchParams.h',
 ]
 
 UNIFIED_SOURCES += [
     'BarProps.cpp',
     'CompositionStringSynthesizer.cpp',
     'Crypto.cpp',
     'DOMCursor.cpp',
     'DOMError.cpp',
@@ -92,16 +93,17 @@ UNIFIED_SOURCES += [
     'nsScreen.cpp',
     'nsScriptNameSpaceManager.cpp',
     'nsStructuredCloneContainer.cpp',
     'nsWindowMemoryReporter.cpp',
     'nsWindowRoot.cpp',
     'nsWrapperCache.cpp',
     'ScriptSettings.cpp',
     'URL.cpp',
+    'URLSearchParams.cpp',
     'WindowNamedPropertiesHandler.cpp',
 ]
 
 # these files couldn't be in UNIFIED_SOURCES for now for reasons given below:
 SOURCES += [
     # this file doesn't like windows.h
     'MessagePort.cpp',
     # this file doesn't like windows.h
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -320,18 +320,17 @@ MaybeReflowForInflationScreenWidthChange
     if (presShell && presShell->FontSizeInflationEnabled() &&
         presShell->FontSizeInflationMinTwips() != 0) {
       aPresContext->ScreenWidthInchesForFontInflation(&changed);
     }
 
     changed = changed ||
       (fontInflationWasEnabled != presShell->FontSizeInflationEnabled());
     if (changed) {
-      nsCOMPtr<nsISupports> container = aPresContext->GetContainer();
-      nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(container);
+      nsCOMPtr<nsIDocShell> docShell = aPresContext->GetDocShell();
       if (docShell) {
         nsCOMPtr<nsIContentViewer> cv;
         docShell->GetContentViewer(getter_AddRefs(cv));
         nsCOMPtr<nsIMarkupDocumentViewer> mudv = do_QueryInterface(cv);
         if (mudv) {
           nsTArray<nsCOMPtr<nsIMarkupDocumentViewer> > array;
           mudv->AppendSubtree(array);
           for (uint32_t i = 0, iEnd = array.Length(); i < iEnd; ++i) {
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -941,16 +941,19 @@ public:
                                             const nsAString& aUrl,
                                             const nsAString& aName,
                                             const nsAString& aOptions,
                                             const mozilla::dom::Sequence<JS::Value>& aExtraArgument,
                                             mozilla::ErrorResult& aError);
   JSObject* GetContent(JSContext* aCx, mozilla::ErrorResult& aError);
   JSObject* Get_content(JSContext* aCx, mozilla::ErrorResult& aError)
   {
+    if (mDoc) {
+      mDoc->WarnOnceAbout(nsIDocument::eWindow_Content);
+    }
     return GetContent(aCx, aError);
   }
 
 protected:
   // Array of idle observers that are notified of idle events.
   nsTObserverArray<IdleObserverHolder> mIdleObservers;
 
   // Idle timer used for function callbacks to notify idle observers.
--- a/dom/base/nsHistory.cpp
+++ b/dom/base/nsHistory.cpp
@@ -134,17 +134,17 @@ nsHistory::GetState(JSContext* aCx, Erro
     if (!JS_WrapValue(aCx, &jsData)) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
       return JS::UndefinedValue();
     }
 
     return jsData;
   }
 
-  return JS::UndefinedValue();
+  return JS::NullValue();
 }
 
 void
 nsHistory::Go(int32_t aDelta, ErrorResult& aRv)
 {
   nsCOMPtr<nsPIDOMWindow> win(do_QueryReferent(mInnerWindow));
   if (!win || !win->HasActiveDocument()) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -14,16 +14,17 @@ support-files =
 [test_domcursor.html]
 [test_domrequest.html]
 [test_e4x_for_each.html]
 [test_error.html]
 [test_gsp-qualified.html]
 [test_gsp-quirks.html]
 [test_gsp-standards.html]
 [test_history_document_open.html]
+[test_history_state_null.html]
 [test_innersize_scrollport.html]
 [test_messageChannel.html]
 [test_messageChannel_cloning.html]
 [test_messageChannel_pingpong.html]
 [test_messageChannel_post.html]
 [test_messageChannel_pref.html]
 [test_messageChannel_start.html]
 [test_messageChannel_transferable.html]
@@ -39,8 +40,9 @@ support-files =
 [test_window_enumeration.html]
 [test_window_extensible.html]
 [test_window_indexing.html]
 [test_writable-replaceable.html]
 [test_urlExceptions.html]
 [test_openDialogChromeOnly.html]
 [test_messagemanager_targetchain.html]
 [test_url_empty_port.html]
+[test_urlSearchParams.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_history_state_null.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=949471
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 949471</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+  /** Test for Bug 949471 **/
+    ise(history.state, null, "history.state should be null by default");
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=949471">Mozilla Bug 949471</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_urlSearchParams.html
@@ -0,0 +1,237 @@
+
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=887836
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 887836</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=887836">Mozilla Bug 887836</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <iframe name="x" id="x"></iframe>
+  <iframe name="y" id="y"></iframe>
+</div>
+<pre id="test">
+</pre>
+<a href="http://www.example.net?a=b&c=d" id="anchor">foobar</a>
+<area href="http://www.example.net?a=b&c=d" id="area">foobar</area>
+<script type="application/javascript">
+
+  /** Test for Bug 887836 **/
+  ok("URLSearchParams" in window, "window.URLSearchParams exists");
+
+  function testSimpleURLSearchParams() {
+    var u = new URLSearchParams();
+    ok(u, "URLSearchParams created");
+    is(u.has('foo'), false, 'URLSearchParams.has(foo)');
+    is(u.get('foo'), '', 'URLSearchParams.get(foo)');
+    is(u.getAll('foo').length, 0, 'URLSearchParams.getAll(foo)');
+    is(u.size, 0, 'URLSearchParams.size()');
+
+    u.append('foo', 'bar');
+    is(u.has('foo'), true, 'URLSearchParams.has(foo)');
+    is(u.get('foo'), 'bar', 'URLSearchParams.get(foo)');
+    is(u.getAll('foo').length, 1, 'URLSearchParams.getAll(foo)');
+    is(u.size, 1, 'URLSearchParams.size()');
+
+    u.set('foo', 'bar2');
+    is(u.get('foo'), 'bar2', 'URLSearchParams.get(foo)');
+    is(u.getAll('foo').length, 1, 'URLSearchParams.getAll(foo)');
+    is(u.size, 1, 'URLSearchParams.size()');
+
+    u.delete('foo');
+    is(u.size, 0, 'URLSearchParams.size()');
+
+    runTest();
+  }
+
+  function testCopyURLSearchParams() {
+    var u = new URLSearchParams();
+    ok(u, "URLSearchParams created");
+    u.append('foo', 'bar');
+    is(u.size, 1, "u.size()");
+
+    var uu = new URLSearchParams(u);
+    is(uu.size, 1, "uu.size()");
+    is(uu.get('foo'), 'bar', 'uu.get()');
+
+    u.append('foo', 'bar2');
+    is(u.getAll('foo').length, 2, "u.getAll()");
+    is(uu.getAll('foo').length, 1, "uu.getAll()");
+
+    runTest();
+  }
+
+  function testParserURLSearchParams() {
+    var checks = [
+      { input: '', data: {} },
+      { input: 'a', data: { 'a' : [''] } },
+      { input: 'a=b', data: { 'a' : ['b'] } },
+      { input: 'a=', data: { 'a' : [''] } },
+      { input: '=b', data: { '' : ['b'] } },
+      { input: '&', data: {} },
+      { input: '&a', data: { 'a' : [''] } },
+      { input: 'a&', data: { 'a' : [''] } },
+      { input: 'a&a', data: { 'a' : ['', ''] } },
+      { input: 'a&b&c', data: { 'a' : [''], 'b' : [''], 'c' : [''] } },
+      { input: 'a=b&c=d', data: { 'a' : ['b'], 'c' : ['d'] } },
+      { input: 'a=b&c=d&', data: { 'a' : ['b'], 'c' : ['d'] } },
+      { input: '&&&a=b&&&&c=d&', data: { 'a' : ['b'], 'c' : ['d'] } },
+      { input: 'a=a&a=b&a=c', data: { 'a' : ['a', 'b', 'c'] } },
+      { input: 'a==a', data: { 'a' : ['=a'] } },
+      { input: 'a=a+b+c+d', data: { 'a' : ['a b c d'] } },
+      { input: '%=a', data: { '%' : ['a'] } },
+      { input: '%a=a', data: { '%a' : ['a'] } },
+      { input: '%a_=a', data: { '%a_' : ['a'] } },
+      { input: '%61=a', data: { 'a' : ['a'] } },
+      { input: '%=a', data: { '%' : ['a'] } },
+      { input: '%a=a', data: { '%a' : ['a'] } },
+      { input: '%a_=a', data: { '%a_' : ['a'] } },
+      { input: '%61=a', data: { 'a' : ['a'] } },
+      { input: '%61+%4d%4D=', data: { 'a MM' : [''] } },
+    ];
+
+    for (var i = 0; i < checks.length; ++i) {
+      var u = new URLSearchParams(checks[i].input);
+
+      var count = 0;
+      for (var key in checks[i].data) {
+        ++count;
+        ok(u.has(key), "key " + key + " found");
+
+        var all = u.getAll(key);
+        is(all.length, checks[i].data[key].length, "same number of elements");
+
+        for (var k = 0; k < all.length; ++k) {
+          is(all[k], checks[i].data[key][k], "value matches");
+        }
+      }
+
+      is(u.size, count, "size matches");
+    }
+
+    runTest();
+  }
+
+  function testURL() {
+    var url = new URL('http://www.example.net?a=b&c=d');
+    ok(url.searchParams, "URL searchParams exists!");
+    ok(url.searchParams.has('a'), "URL.searchParams.has('a')");
+    is(url.searchParams.get('a'), 'b', "URL.searchParams.get('a')");
+    ok(url.searchParams.has('c'), "URL.searchParams.has('c')");
+    is(url.searchParams.get('c'), 'd', "URL.searchParams.get('c')");
+
+    url.searchParams.set('e', 'f');
+    ok(url.href.indexOf('e=f') != 1, 'URL right');
+
+    var u = new URLSearchParams();
+    u.append('foo', 'bar');
+    url.searchParams = u;
+    is(url.searchParams, u, "URL.searchParams is the same object");
+    is(url.searchParams.get('foo'), 'bar', "URL.searchParams.get('foo')");
+    is(url.href, 'http://www.example.net/?foo=bar', 'URL right');
+
+    url.searchParams = null;
+    is(url.searchParams.get('foo'), 'bar', "URL.searchParams.get('foo')");
+    is(url.href, 'http://www.example.net/?foo=bar', 'URL right');
+
+    var url2 = new URL('http://www.example.net?e=f');
+    url.searchParams = url2.searchParams;
+    isnot(url.searchParams, url2.searchParams, "URL.searchParams is not the same object");
+    is(url.searchParams.get('e'), 'f', "URL.searchParams.get('e')");
+
+    url.href = "http://www.example.net?bar=foo";
+    is(url.searchParams.get('bar'), 'foo', "URL.searchParams.get('bar')");
+
+    runTest();
+  }
+
+  function testElement(e) {
+    ok(e, 'element exists');
+    ok(e.searchParams, "e.searchParams exists!");
+    ok(e.searchParams.has('a'), "e.searchParams.has('a')");
+    is(e.searchParams.get('a'), 'b', "e.searchParams.get('a')");
+    ok(e.searchParams.has('c'), "e.searchParams.has('c')");
+    is(e.searchParams.get('c'), 'd', "e.searchParams.get('c')");
+
+    e.searchParams.set('e', 'f');
+    ok(e.href.indexOf('e=f') != 1, 'e is right');
+
+    var u = new URLSearchParams();
+    u.append('foo', 'bar');
+    e.searchParams = u;
+    is(e.searchParams, u, "e.searchParams is the same object");
+    is(e.searchParams.get('foo'), 'bar', "e.searchParams.get('foo')");
+    is(e.href, 'http://www.example.net/?foo=bar', 'e is right');
+
+    e.searchParams = null;
+    is(e.searchParams.get('foo'), 'bar', "e.searchParams.get('foo')");
+    is(e.href, 'http://www.example.net/?foo=bar', 'e is right');
+
+    var url2 = new URL('http://www.example.net?e=f');
+    e.searchParams = url2.searchParams;
+    isnot(e.searchParams, url2.searchParams, "e.searchParams is not the same object");
+    is(e.searchParams.get('e'), 'f', "e.searchParams.get('e')");
+
+    e.href = "http://www.example.net?bar=foo";
+    is(e.searchParams.get('bar'), 'foo', "e.searchParams.get('bar')");
+
+    e.setAttribute('href', "http://www.example.net?bar2=foo2");
+    is(e.searchParams.get('bar2'), 'foo2', "e.searchParams.get('bar2')");
+
+    runTest();
+  }
+
+  function testEncoding() {
+    var encoding = [ [ '1', '1' ],
+                     [ 'a b', 'a+b' ],
+                     [ '<>', '%3C%3E' ],
+                     [ '\u0541', '%D5%81'] ];
+
+    for (var i = 0; i < encoding.length; ++i) {
+      var a = new URLSearchParams();
+      a.set('a', encoding[i][0]);
+
+      var url = new URL('http://www.example.net');
+      url.searchParams = a;
+      is(url.href, 'http://www.example.net/?a=' + encoding[i][1]);
+
+      var url2 = new URL(url.href);
+      is(url2.searchParams.get('a'), encoding[i][0], 'a is still there');
+    }
+
+    runTest();
+  }
+
+  var tests = [
+    testSimpleURLSearchParams,
+    testCopyURLSearchParams,
+    testParserURLSearchParams,
+    testURL,
+    function() { testElement(document.getElementById('anchor')) },
+    function() { testElement(document.getElementById('area')) },
+    testEncoding
+  ];
+
+  function runTest() {
+    if (!tests.length) {
+      SimpleTest.finish();
+      return;
+    }
+
+    var test = tests.shift();
+    test();
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  runTest();
+
+</script>
+</body>
+</html>
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1282,17 +1282,16 @@ DOMInterfaces = {
 },
 
 'URL' : [{
     'wrapperCache': False,
 },
 {
     'workers': True,
     'wrapperCache': False,
-    'nativeOwnership': 'owned',
 }],
 
 'VTTCue': {
     'nativeType': 'mozilla::dom::TextTrackCue'
 },
 
 'VTTRegion': {
   'nativeType': 'mozilla::dom::TextTrackRegion',
@@ -1890,16 +1889,17 @@ addExternalIface('MozWakeLockListener', 
 addExternalIface('MozXULTemplateBuilder', nativeType='nsIXULTemplateBuilder')
 addExternalIface('nsIControllers', nativeType='nsIControllers')
 addExternalIface('nsIDOMCrypto', nativeType='nsIDOMCrypto',
                  headerFile='Crypto.h')
 addExternalIface('nsIInputStreamCallback', nativeType='nsIInputStreamCallback',
                  headerFile='nsIAsyncInputStream.h')
 addExternalIface('nsIStreamListener', nativeType='nsIStreamListener', notflattened=True)
 addExternalIface('nsISupports', nativeType='nsISupports')
+addExternalIface('nsIDocShell', nativeType='nsIDocShell', notflattened=True)
 addExternalIface('nsIEditor', nativeType='nsIEditor', notflattened=True)
 addExternalIface('nsIVariant', nativeType='nsIVariant', notflattened=True)
 addExternalIface('OutputStream', nativeType='nsIOutputStream',
                  notflattened=True)
 addExternalIface('Pkcs11')
 addExternalIface('Principal', nativeType='nsIPrincipal',
                  headerFile='nsIPrincipal.h', notflattened=True)
 addExternalIface('Selection', nativeType='nsISelection')
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -3272,17 +3272,17 @@ for (uint32_t i = 0; i < length; ++i) {
                           '  ThrowErrorMessage(cx, MSG_NOT_IN_UNION, "%s", "%s");\n'
                           "%s\n"
                           "}" % (exceptionCodeIndented.define(),
                                  firstCap(sourceDescription),
                                  ", ".join(names),
                                  exceptionCodeIndented.define()))
         templateBody = CGWrapper(CGIndenter(CGList([templateBody, throw], "\n")), pre="{\n", post="\n}")
 
-        typeName = ("Owning" if isMember else "") + type.name
+        typeName = CGUnionStruct.unionTypeDecl(type, isMember)
         argumentTypeName = typeName + "Argument"
         if nullable:
             typeName = "Nullable<" + typeName + " >"
 
         def handleNull(templateBody, setToNullVar, extraConditionForNull=""):
             nullTest = "%s${val}.isNullOrUndefined()" % extraConditionForNull
             return CGIfElseWrapper(nullTest,
                                    CGGeneric("%s.SetNull();" % setToNullVar),
@@ -3840,17 +3840,17 @@ for (uint32_t i = 0; i < length; ++i) {
                      "}" % (val, firstCap(sourceDescription),
                             exceptionCodeIndented.define()))
 
         # Dictionary arguments that might contain traceable things need to get
         # traced
         if not isMember and isCallbackReturnValue:
             # Go ahead and just convert directly into our actual return value
             declType = CGWrapper(declType, post="&")
-            declArgs = "retval"
+            declArgs = "aRetVal"
         elif not isMember and typeNeedsRooting(type):
             declType = CGTemplatedType("RootedDictionary", declType);
             declArgs = "cx"
         else:
             declArgs = None
 
         return JSToNativeConversionInfo(template, declType=declType,
                                         declArgs=declArgs)
@@ -4697,17 +4697,17 @@ def getRetvalDeclarationForType(returnTy
                 result = CGTemplatedType("RootedDictionary", result)
             resultArgs = "cx"
         else:
             if nullable:
                 result = CGTemplatedType("Nullable", result)
             resultArgs = None
         return result, True, None, resultArgs
     if returnType.isUnion():
-        result = CGGeneric("Owning" + returnType.unroll().name)
+        result = CGGeneric(CGUnionStruct.unionTypeName(returnType.unroll(), True))
         if not isMember and typeNeedsRooting(returnType):
             if returnType.nullable():
                 result = CGTemplatedType("NullableRootedUnion", result)
             else:
                 result = CGTemplatedType("RootedUnion", result)
             resultArgs = "cx"
         else:
             if returnType.nullable():
@@ -6886,17 +6886,17 @@ class CGUnionStruct(CGThing):
         methods.append(ClassMethod("ToJSVal", "bool", [
                                    Argument("JSContext*", "cx"),
                                    Argument("JS::Handle<JSObject*>", "scopeObj"),
                                    Argument("JS::MutableHandle<JS::Value>", "rval")
         ], body=CGSwitch("mType", toJSValCases,
                          default=CGGeneric("return false;")).define(), const=True))
 
         constructors = [ctor]
-        selfName = ("Owning" if self.ownsMembers else "") + str(self.type)
+        selfName = CGUnionStruct.unionTypeName(self.type, self.ownsMembers)
         if self.ownsMembers:
             if len(traceCases):
                 traceBody = CGSwitch("mType", traceCases,
                                      default=CGGeneric("")).define()
             else:
                 traceBody = ""
             methods.append(ClassMethod("TraceUnion", "void",
                                        [Argument("JSTracer*", "trc")],
@@ -6945,16 +6945,38 @@ class CGUnionStruct(CGThing):
                 "typedArraysAreStructs": True
                 })
         return CGGeneric(wrapCode)
 
     @staticmethod
     def isUnionCopyConstructible(type):
         return all(isTypeCopyConstructible(t) for t in type.flatMemberTypes)
 
+    @staticmethod
+    def unionTypeName(type, ownsMembers):
+        """
+        Returns a string name for this known union type.
+        """
+        assert type.isUnion() and not type.nullable()
+        return ("Owning" if ownsMembers else "") + type.name
+
+    @staticmethod
+    def unionTypeDecl(type, ownsMembers):
+        """
+        Returns a string for declaring this possibly-nullable union type.
+        """
+        assert type.isUnion()
+        nullable = type.nullable()
+        if nullable:
+            type = type.inner
+        decl = CGGeneric(CGUnionStruct.unionTypeName(type, ownsMembers))
+        if nullable:
+            decl = CGTemplatedType("Nullable", decl)
+        return decl.define()
+
 class CGUnionConversionStruct(CGThing):
     def __init__(self, type, descriptorProvider):
         CGThing.__init__(self)
         self.type = type.unroll()
         self.descriptorProvider = descriptorProvider
 
     def declare(self):
 
@@ -9479,17 +9501,20 @@ class CGForwardDeclarations(CGWrapper):
                         pass
             elif t.isCallback():
                 builder.addInMozillaDom(str(t))
             elif t.isDictionary():
                 builder.addInMozillaDom(t.inner.identifier.name, isStruct=True)
             elif t.isCallbackInterface():
                 builder.addInMozillaDom(t.inner.identifier.name)
             elif t.isUnion():
-                builder.addInMozillaDom(str(t))
+                # Forward declare both the owning and non-owning version,
+                # since we don't know which one we might want
+                builder.addInMozillaDom(CGUnionStruct.unionTypeName(t, False))
+                builder.addInMozillaDom(CGUnionStruct.unionTypeName(t, True))
             # Don't need to do anything for void, primitive, string, any or object.
             # There may be some other cases we are missing.
 
         # Needed for at least Wrap.
         for d in descriptors:
             builder.add(d.nativeType)
 
         # We just about always need NativePropertyHooks
@@ -9762,23 +9787,23 @@ class CGNativeMember(ClassMethod):
             return (result.define(),
                     "%s(%s)" % (result.define(), defaultReturnArg),
                     "return ${declName};")
         if type.isDOMString():
             if isMember:
                 # No need for a third element in the isMember case
                 return "nsString", None, None
             # Outparam
-            return "void", "", "retval = ${declName};"
+            return "void", "", "aRetVal = ${declName};"
         if type.isByteString():
             if isMember:
                 # No need for a third element in the isMember case
                 return "nsCString", None, None
             # Outparam
-            return "void", "", "retval = ${declName};"
+            return "void", "", "aRetVal = ${declName};"
         if type.isEnum():
             enumName = type.unroll().inner.identifier.name
             if type.nullable():
                 enumName = CGTemplatedType("Nullable",
                                            CGGeneric(enumName)).define()
                 defaultValue = "%s()" % enumName
             else:
                 defaultValue = "%s(0)" % enumName
@@ -9828,66 +9853,78 @@ class CGNativeMember(ClassMethod):
         if type.isSequence():
             # If we want to handle sequence-of-sequences return values, we're
             # going to need to fix example codegen to not produce nsTArray<void>
             # for the relevant argument...
             assert not isMember
             # Outparam.
             if type.nullable():
                 returnCode = ("if (${declName}.IsNull()) {\n"
-                              "  retval.SetNull();\n"
+                              "  aRetVal.SetNull();\n"
                               "} else {\n"
-                              "  retval.SetValue().SwapElements(${declName}.Value());\n"
+                              "  aRetVal.SetValue().SwapElements(${declName}.Value());\n"
                               "}")
             else:
-                returnCode = "retval.SwapElements(${declName});"
+                returnCode = "aRetVal.SwapElements(${declName});"
             return "void", "", returnCode
         if type.isDate():
             result = CGGeneric("Date")
             if type.nullable():
                 result = CGTemplatedType("Nullable", result)
             return (result.define(), "%s()" % result.define(),
                     "return ${declName};")
         if type.isDictionary():
             if isMember:
                 # Only the first member of the tuple matters here, but return
                 # bogus values for the others in case someone decides to use
                 # them.
                 return CGDictionary.makeDictionaryName(type.inner), None, None
             # In this case we convert directly into our outparam to start with
             return "void", "", ""
+        if type.isUnion():
+            if isMember:
+                # Only the first member of the tuple matters here, but return
+                # bogus values for the others in case someone decides to use
+                # them.
+                return CGUnionStruct.unionTypeDecl(type, True), None, None
+            # In this case we convert directly into our outparam to start with
+            return "void", "", ""
 
         raise TypeError("Don't know how to declare return value for %s" %
                         type)
 
     def getArgs(self, returnType, argList):
         args = [self.getArg(arg) for arg in argList]
         # Now the outparams
         if returnType.isDOMString():
-            args.append(Argument("nsString&", "retval"))
+            args.append(Argument("nsString&", "aRetVal"))
         elif returnType.isByteString():
-            args.append(Argument("nsCString&", "retval"))
+            args.append(Argument("nsCString&", "aRetVal"))
         elif returnType.isSequence():
             nullable = returnType.nullable()
             if nullable:
                 returnType = returnType.inner
             # And now the actual underlying type
             elementDecl = self.getReturnType(returnType.inner, True)
             type = CGTemplatedType("nsTArray", CGGeneric(elementDecl))
             if nullable:
                 type = CGTemplatedType("Nullable", type)
-            args.append(Argument("%s&" % type.define(), "retval"))
+            args.append(Argument("%s&" % type.define(), "aRetVal"))
         elif returnType.isDictionary():
             nullable = returnType.nullable()
             if nullable:
                 returnType = returnType.inner
             dictType = CGGeneric(CGDictionary.makeDictionaryName(returnType.inner))
             if nullable:
                 dictType = CGTemplatedType("Nullable", dictType)
-            args.append(Argument("%s&" % dictType.define(), "retval"))
+            args.append(Argument("%s&" % dictType.define(), "aRetVal"))
+        elif returnType.isUnion():
+            args.append(Argument("%s&" %
+                                 CGUnionStruct.unionTypeDecl(returnType, True),
+                                 "aRetVal"))
         # And the ErrorResult
         if not 'infallible' in self.extendedAttrs:
             # Use aRv so it won't conflict with local vars named "rv"
             args.append(Argument("ErrorResult&", "aRv"))
         # The legacycaller thisval
         if self.member.isMethod() and self.member.isLegacycaller():
             # If it has an identifier, we can't deal with it yet
             assert self.member.isIdentifierLess()
@@ -9922,19 +9959,19 @@ class CGNativeMember(ClassMethod):
             if nullable:
                 type = type.inner
             elementType = type.inner
             argType = self.getArgType(elementType, False, "Sequence")[0]
             decl = CGTemplatedType("Sequence", argType)
             return decl.define(), True, True
 
         if type.isUnion():
-            if type.nullable():
-                type = type.inner
-            return str(type), True, True
+            # unionTypeDecl will handle nullable types, so return False for
+            # auto-wrapping in Nullable
+            return CGUnionStruct.unionTypeDecl(type, isMember), True, False
 
         if type.isGeckoInterface() and not type.isCallbackInterface():
             iface = type.unroll().inner
             argIsPointer = type.nullable() or iface.isExternal()
             forceOwningType = iface.isCallback() or isMember
             if argIsPointer:
                 if (optional or isMember) and forceOwningType:
                     typeDecl = "nsRefPtr<%s>"
@@ -11430,99 +11467,59 @@ class CGEventGetter(CGNativeMember):
     def __init__(self, descriptor, attr):
         ea = descriptor.getExtendedAttributes(attr, getter=True)
         ea.append('resultNotAddRefed')
         CGNativeMember.__init__(self, descriptor, attr,
                                 CGSpecializedGetter.makeNativeName(descriptor,
                                                                    attr),
                                 (attr.type, []),
                                 ea)
-
-    def retval(self, type):
-        if type.isPrimitive() and type.tag() in builtinNames:
-            result = CGGeneric(builtinNames[type.tag()])
-            if type.nullable():
-                result = CGTemplatedType("Nullable", result)
-            return result.define()
-        if type.isDOMString():
-            return "void"
-        if type.isByteString():
-            return "void"
-        if type.isEnum():
-            enumName = type.unroll().inner.identifier.name
-            if type.nullable():
-                enumName = CGTemplatedType("Nullable",
-                                           CGGeneric(enumName)).define()
-            return enumName
-        if type.isGeckoInterface():
-            iface = type.unroll().inner;
-            nativeType = self.descriptorProvider.getDescriptor(
-                iface.identifier.name).nativeType
-            # Now trim off unnecessary namespaces
-            nativeType = nativeType.split("::")
-            if nativeType[0] == "mozilla":
-                nativeType.pop(0)
-                if nativeType[0] == "dom":
-                    nativeType.pop(0)
-            return CGWrapper(CGGeneric("::".join(nativeType)), post="*").define()
-        if type.isAny():
-            return "JS::Value"
-        if type.isObject():
-            return "JSObject*"
-        if type.isSpiderMonkeyInterface():
-            return "JSObject*"
-        raise TypeError("Don't know how to declare return value for %s" %
-                        type)
+        self.body = self.getMethodBody()
 
     def getArgs(self, returnType, argList):
-        args = [self.getArg(arg) for arg in argList]
-        if returnType.isDOMString():
-            args.append(Argument("nsString&", "aRetVal"))
-        elif returnType.isByteString():
-            args.append(Argument("nsCString&", "aRetVal"))
-        if needCx(returnType, argList, self.extendedAttrs, True):
-            args.insert(0, Argument("JSContext*", "aCx"))
         if not 'infallible' in self.extendedAttrs:
             raise TypeError("Event code generator does not support [Throws]!")
-        return args
+        if not self.member.isAttr():
+            raise TypeError("Event code generator does not support methods")
+        if self.member.isStatic():
+            raise TypeError("Event code generators does not support static attributes")
+        return CGNativeMember.getArgs(self, returnType, argList)
 
     def getMethodBody(self):
         type = self.member.type
         memberName = CGDictionary.makeMemberName(self.member.identifier.name)
         if (type.isPrimitive() and type.tag() in builtinNames) or type.isEnum() or type.isGeckoInterface():
-            return "  return " + memberName + ";"
+            return "return " + memberName + ";"
         if type.isDOMString() or type.isByteString():
-            return "  aRetVal = " + memberName + ";";
+            return "aRetVal = " + memberName + ";";
         if type.isSpiderMonkeyInterface() or type.isObject():
-            ret =  "  if (%s) {\n" % memberName
-            ret += "    JS::ExposeObjectToActiveJS(%s);\n" % memberName
-            ret += "  }\n"
-            ret += "  return " + memberName + ";"
+            ret =  "if (%s) {\n" % memberName
+            ret += "  JS::ExposeObjectToActiveJS(%s);\n" % memberName
+            ret += "}\n"
+            ret += "return " + memberName + ";"
             return ret;
         if type.isAny():
-            ret =  "  JS::ExposeValueToActiveJS("+ memberName + ");\n"
-            ret += "  return " + memberName + ";"
+            ret =  "JS::ExposeValueToActiveJS("+ memberName + ");\n"
+            ret += "return " + memberName + ";"
             return ret;
+        if type.isUnion():
+            return "aRetVal = " + memberName + ";"
         raise TypeError("Event code generator does not support this type!")
 
     def declare(self, cgClass):
         if getattr(self.member, "originatingInterface",
                    cgClass.descriptor.interface) != cgClass.descriptor.interface:
             return ""
         return CGNativeMember.declare(self, cgClass)
 
     def define(self, cgClass):
         if getattr(self.member, "originatingInterface",
                    cgClass.descriptor.interface) != cgClass.descriptor.interface:
             return ""
-        ret = self.retval(self.member.type);
-        methodName = self.descriptorProvider.name + '::' + self.name
-        args = (', '.join([a.declare() for a in self.args]))
-        body = self.getMethodBody()
-        return ret + "\n" + methodName + '(' + args + ') const\n{\n' + body + "\n}\n"
+        return CGNativeMember.define(self, cgClass)
 
 class CGEventSetter(CGNativeMember):
     def __init__(self):
         raise TypeError("Event code generator does not support setters!")
 
 class CGEventMethod(CGNativeMember):
     def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
         if not isConstructor:
@@ -11649,16 +11646,21 @@ class CGEventClass(CGBindingImplClass):
                         nativeType.pop(0)
                         if nativeType[0] == "dom":
                             nativeType.pop(0)
                     nativeType = CGWrapper(CGGeneric("::".join(nativeType)), pre="nsRefPtr<", post=">").define()
                 elif m.type.isAny():
                     nativeType = "JS::Heap<JS::Value>"
                 elif m.type.isObject() or m.type.isSpiderMonkeyInterface():
                     nativeType = "JS::Heap<JSObject*>"
+                elif m.type.isUnion():
+                    nativeType = CGUnionStruct.unionTypeDecl(m.type, True)
+                else:
+                    raise TypeError("Don't know how to declare member of type %s" %
+                                    m.type)
                 members.append(ClassMember(CGDictionary.makeMemberName(m.identifier.name),
                                nativeType,
                                visibility="private",
                                body="body"))
 
         parent = self.descriptor.interface.parent
         self.parentType = self.descriptor.getDescriptor(parent.identifier.name).nativeType.split('::')[-1]
         baseDeclarations=(
@@ -11710,16 +11712,19 @@ class CGEventClass(CGBindingImplClass):
         retVal = ""
         for m in self.descriptor.interface.members:
             if m.isAttr():
                 name = CGDictionary.makeMemberName(m.identifier.name)
                 if m.type.isAny():
                     retVal += "  NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(" + name + ")\n"
                 elif m.type.isObject() or m.type.isSpiderMonkeyInterface():
                     retVal += "  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(" + name + ")\n"
+                elif typeNeedsRooting(m.type):
+                    raise TypeError("Need to implement tracing for event "
+                                    "member of type %s" % m.type)
         return retVal
 
     def define(self):
         dropJS = ""
         for m in self.descriptor.interface.members:
             if m.isAttr():
                 member = CGDictionary.makeMemberName(m.identifier.name);
                 if m.type.isAny():
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -554,16 +554,19 @@ public:
   void PassNullableUnionWithDefaultValue6(const Nullable<FloatOrString>& arg);
   void PassNullableUnionWithDefaultValue7(const Nullable<UnrestrictedDoubleOrString>& arg);
   void PassNullableUnionWithDefaultValue8(const Nullable<UnrestrictedDoubleOrString>& arg);
   void PassNullableUnionWithDefaultValue9(const Nullable<UnrestrictedDoubleOrString>& arg);
   void PassNullableUnionWithDefaultValue10(const Nullable<UnrestrictedFloatOrString>& arg);
   void PassNullableUnionWithDefaultValue11(const Nullable<UnrestrictedFloatOrString>& arg);
   void PassNullableUnionWithDefaultValue12(const Nullable<UnrestrictedFloatOrString>& arg);
 
+  void PassSequenceOfUnions(const Sequence<OwningCanvasPatternOrCanvasGradient>&);
+  void PassVariadicUnion(const Sequence<OwningCanvasPatternOrCanvasGradient>&);
+
   void ReceiveUnion(OwningCanvasPatternOrCanvasGradient&);
   void ReceiveUnion2(JSContext*, OwningObjectOrLong&);
   void ReceiveUnionContainingNull(OwningCanvasPatternOrNullOrCanvasGradient&);
   void ReceiveNullableUnion(Nullable<OwningCanvasPatternOrCanvasGradient>&);
   void ReceiveNullableUnion2(JSContext*, Nullable<OwningObjectOrLong>&);
   void GetWritableUnion(OwningCanvasPatternOrCanvasGradient&);
   void SetWritableUnion(const CanvasPatternOrCanvasGradient&);
   void GetWritableUnionContainingNull(OwningCanvasPatternOrNullOrCanvasGradient&);
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -500,16 +500,19 @@ interface TestInterface {
   void passNullableUnionWithDefaultValue6(optional (float or DOMString)? arg = null);
   void passNullableUnionWithDefaultValue7(optional (unrestricted double or DOMString)? arg = "");
   void passNullableUnionWithDefaultValue8(optional (unrestricted double or DOMString)? arg = 1);
   void passNullableUnionWithDefaultValue9(optional (unrestricted double or DOMString)? arg = null);
   void passNullableUnionWithDefaultValue10(optional (unrestricted float or DOMString)? arg = "");
   void passNullableUnionWithDefaultValue11(optional (unrestricted float or DOMString)? arg = 1);
   void passNullableUnionWithDefaultValue12(optional (unrestricted float or DOMString)? arg = null);
 
+  void passSequenceOfUnions(sequence<(CanvasPattern or CanvasGradient)> arg);
+  void passVariadicUnion((CanvasPattern or CanvasGradient)... arg);
+
   (CanvasPattern or CanvasGradient) receiveUnion();
   (object or long) receiveUnion2();
   (CanvasPattern? or CanvasGradient) receiveUnionContainingNull();
   (CanvasPattern or CanvasGradient)? receiveNullableUnion();
   (object or long)? receiveNullableUnion2();
 
   attribute (CanvasPattern or CanvasGradient) writableUnion;
   attribute (CanvasPattern? or CanvasGradient) writableUnionContainingNull;
--- a/dom/bindings/test/TestExampleGen.webidl
+++ b/dom/bindings/test/TestExampleGen.webidl
@@ -396,16 +396,19 @@ interface TestExampleInterface {
   void passNullableUnionWithDefaultValue6(optional (float or DOMString)? arg = null);
   void passNullableUnionWithDefaultValue7(optional (unrestricted double or DOMString)? arg = "");
   void passNullableUnionWithDefaultValue8(optional (unrestricted double or DOMString)? arg = 1);
   void passNullableUnionWithDefaultValue9(optional (unrestricted double or DOMString)? arg = null);
   void passNullableUnionWithDefaultValue10(optional (unrestricted float or DOMString)? arg = "");
   void passNullableUnionWithDefaultValue11(optional (unrestricted float or DOMString)? arg = 1);
   void passNullableUnionWithDefaultValue12(optional (unrestricted float or DOMString)? arg = null);
 
+  void passSequenceOfUnions(sequence<(CanvasPattern or CanvasGradient)> arg);
+  void passVariadicUnion((CanvasPattern or CanvasGradient)... arg);
+
   //(CanvasPattern or CanvasGradient) receiveUnion();
   //(object or long) receiveUnion2();
   //(CanvasPattern? or CanvasGradient) receiveUnionContainingNull();
   //(CanvasPattern or CanvasGradient)? receiveNullableUnion();
   //(object or long)? receiveNullableUnion2();
 
   //attribute (CanvasPattern or CanvasGradient) writableUnion;
   //attribute (CanvasPattern? or CanvasGradient) writableUnionContainingNull;
--- a/dom/bindings/test/TestJSImplGen.webidl
+++ b/dom/bindings/test/TestJSImplGen.webidl
@@ -375,22 +375,20 @@ interface TestJSImplInterface {
   void passUnion9((object or DOMString or long or boolean) arg);
   void passUnion10(optional (EventInit or long) arg);
   void passUnion11(optional (CustomEventInit or long) arg);
   void passUnion12(optional (EventInit or long) arg = 5);
   void passUnion13(optional (object or long?) arg = null);
   void passUnion14(optional (object or long?) arg = 5);
 #endif
   void passUnionWithNullable((object? or long) arg);
-  // FIXME: Bug 863948 Nullable unions not supported yet
-  //   void passNullableUnion((object or long)? arg);
+  void passNullableUnion((object or long)? arg);
   void passOptionalUnion(optional (object or long) arg);
-  // FIXME: Bug 863948 Nullable unions not supported yet
-  //  void passOptionalNullableUnion(optional (object or long)? arg);
-  //  void passOptionalNullableUnionWithDefaultValue(optional (object or long)? arg = null);
+  void passOptionalNullableUnion(optional (object or long)? arg);
+  void passOptionalNullableUnionWithDefaultValue(optional (object or long)? arg = null);
   //void passUnionWithInterfaces((TestJSImplInterface or TestExternalInterface) arg);
   //void passUnionWithInterfacesAndNullable((TestJSImplInterface? or TestExternalInterface) arg);
   //void passUnionWithSequence((sequence<object> or long) arg);
   void passUnionWithArrayBuffer((ArrayBuffer or long) arg);
   void passUnionWithString((DOMString or object) arg);
   //void passUnionWithEnum((MyTestEnum or object) arg);
   // Trying to use a callback in a union won't include the test
   // headers, unfortunately, so won't compile.
@@ -420,16 +418,19 @@ interface TestJSImplInterface {
   void passNullableUnionWithDefaultValue6(optional (float or DOMString)? arg = null);
   void passNullableUnionWithDefaultValue7(optional (unrestricted double or DOMString)? arg = "");
   void passNullableUnionWithDefaultValue8(optional (unrestricted double or DOMString)? arg = 1);
   void passNullableUnionWithDefaultValue9(optional (unrestricted double or DOMString)? arg = null);
   void passNullableUnionWithDefaultValue10(optional (unrestricted float or DOMString)? arg = "");
   void passNullableUnionWithDefaultValue11(optional (unrestricted float or DOMString)? arg = 1);
   void passNullableUnionWithDefaultValue12(optional (unrestricted float or DOMString)? arg = null);
 
+  void passSequenceOfUnions(sequence<(CanvasPattern or CanvasGradient)> arg);
+  void passVariadicUnion((CanvasPattern or CanvasGradient)... arg);
+
   //(CanvasPattern or CanvasGradient) receiveUnion();
   //(object or long) receiveUnion2();
   //(CanvasPattern? or CanvasGradient) receiveUnionContainingNull();
   //(CanvasPattern or CanvasGradient)? receiveNullableUnion();
   //(object or long)? receiveNullableUnion2();
 
   //attribute (CanvasPattern or CanvasGradient) writableUnion;
   //attribute (CanvasPattern? or CanvasGradient) writableUnionContainingNull;
--- a/dom/datastore/DataStore.jsm
+++ b/dom/datastore/DataStore.jsm
@@ -4,17 +4,17 @@
  * 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'
 
 this.EXPORTED_SYMBOLS = ["DataStore"];
 
 function debug(s) {
-  // dump('DEBUG DataStore: ' + s + '\n');
+  //dump('DEBUG DataStore: ' + s + '\n');
 }
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 const REVISION_ADDED = "added";
 const REVISION_UPDATED = "updated";
 const REVISION_REMOVED = "removed";
 const REVISION_VOID = "void";
--- a/dom/datastore/DataStoreCursor.jsm
+++ b/dom/datastore/DataStoreCursor.jsm
@@ -4,17 +4,17 @@
  * 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'
 
 this.EXPORTED_SYMBOLS = ['DataStoreCursor'];
 
 function debug(s) {
-  // dump('DEBUG DataStoreCursor: ' + s + '\n');
+  //dump('DEBUG DataStoreCursor: ' + s + '\n');
 }
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 const STATE_INIT = 0;
 const STATE_REVISION_INIT = 1;
 const STATE_REVISION_CHECK = 2;
 const STATE_SEND_ALL = 3;
--- a/dom/datastore/DataStoreDB.jsm
+++ b/dom/datastore/DataStoreDB.jsm
@@ -4,17 +4,17 @@
 
 'use strict';
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 this.EXPORTED_SYMBOLS = ['DataStoreDB'];
 
 function debug(s) {
-  // dump('DEBUG DataStoreDB: ' + s + '\n');
+  //dump('DEBUG DataStoreDB: ' + s + '\n');
 }
 
 const DATASTOREDB_VERSION = 1;
 const DATASTOREDB_OBJECTSTORE_NAME = 'DataStoreDB';
 const DATASTOREDB_REVISION = 'revision';
 const DATASTOREDB_REVISION_INDEX = 'revisionIndex';
 
 Cu.import('resource://gre/modules/IndexedDBHelper.jsm');
--- a/dom/datastore/DataStoreService.js
+++ b/dom/datastore/DataStoreService.js
@@ -4,17 +4,17 @@
  * 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'
 
 /* static functions */
 
 function debug(s) {
-  // dump('DEBUG DataStoreService: ' + s + '\n');
+  //dump('DEBUG DataStoreService: ' + s + '\n');
 }
 
 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/DataStore.jsm');
 Cu.import("resource://gre/modules/DataStoreDB.jsm");
@@ -132,34 +132,38 @@ DataStoreService.prototype = {
       'readwrite',
       function(aTxn, aRevisionStore) {
         debug("createFirstRevisionId - transaction success");
 
         let request = aRevisionStore.openCursor(null, 'prev');
         request.onsuccess = function(aEvent) {
           let cursor = aEvent.target.result;
           if (cursor) {
-            ppmm.broadcastAsyncMessage('datastore-first-revision-created',
-                                       { name: aName, owner: aOwner });
+            debug("First revision already created.");
+            self.enableDataStore(aAppId, aName, aOwner);
           } else {
             // If the revision doesn't exist, let's create the first one.
             db.addRevision(aRevisionStore, 0, REVISION_VOID, function() {
               debug("First revision created.");
-              if (aName in self.stores && aAppId in self.stores[aName]) {
-                self.stores[aName][aAppId].enabled = true;
-                ppmm.broadcastAsyncMessage('datastore-first-revision-created',
-                                           { name: aName, owner: aOwner });
-              }
+              self.enableDataStore(aAppId, aName, aOwner);
             });
           }
         };
       }
     );
   },
 
+  enableDataStore: function(aAppId, aName, aOwner) {
+    if (aName in this.stores && aAppId in this.stores[aName]) {
+      this.stores[aName][aAppId].enabled = true;
+      ppmm.broadcastAsyncMessage('datastore-first-revision-created',
+                                 { name: aName, owner: aOwner });
+    }
+  },
+
   addPermissions: function(aAppId, aName, aOrigin, aOwner, aReadOnly) {
     // When a new DataStore is installed, the permissions must be set for the
     // owner app.
     let permission = "indexedDB-chrome-" + aName + '|' + aOwner;
     this.resetPermissions(aAppId, aOrigin, aOwner, permission, aReadOnly);
 
     // For any app that wants to have access to this DataStore we add the
     // permissions.
--- a/dom/datastore/DataStoreServiceInternal.jsm
+++ b/dom/datastore/DataStoreServiceInternal.jsm
@@ -4,17 +4,17 @@
 
 "use strict"
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 this.EXPORTED_SYMBOLS = ["DataStoreServiceInternal"];
 
 function debug(s) {
-  // dump('DEBUG DataStoreServiceInternal: ' + s + '\n');
+  //dump('DEBUG DataStoreServiceInternal: ' + s + '\n');
 }
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
                                    "@mozilla.org/parentprocessmessagemanager;1",
                                    "nsIMessageBroadcaster");
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -74,16 +74,17 @@
 #include "nsIObserverService.h"
 #include "nsIPresShell.h"
 #include "nsIRemoteBlob.h"
 #include "nsIScriptError.h"
 #include "nsIStyleSheet.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIURIFixup.h"
 #include "nsIWindowWatcher.h"
+#include "nsIXULRuntime.h"
 #include "nsMemoryReporterManager.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStyleSheetService.h"
 #include "nsThreadUtils.h"
 #include "nsToolkitCompsCID.h"
 #include "nsWidgetsCID.h"
 #include "PreallocatedProcessManager.h"
 #include "ProcessPriorityManager.h"
@@ -3108,17 +3109,17 @@ ContentParent::RecvKeywordToURI(const ns
   return true;
 }
 
 bool
 ContentParent::ShouldContinueFromReplyTimeout()
 {
   // The only time ContentParent sends blocking messages is for CPOWs, so
   // timeouts should only ever occur in electrolysis-enabled sessions.
-  MOZ_ASSERT(Preferences::GetBool("browser.tabs.remote", false));
+  MOZ_ASSERT(BrowserTabsRemote());
   return false;
 }
 
 bool
 ContentParent::ShouldSandboxContentProcesses()
 {
 #ifdef MOZ_CONTENT_SANDBOX
   return !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX");
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -50,16 +50,17 @@
 #include "nsIDocShell.h"
 #include "nsIURI.h"
 #include "nsIURIFixup.h"
 #include "nsCDefaultURIFixup.h"
 #include "nsIWebBrowser.h"
 #include "nsIWebBrowserFocus.h"
 #include "nsIWebBrowserSetup.h"
 #include "nsIWebProgress.h"
+#include "nsIXULRuntime.h"
 #include "nsInterfaceHashtable.h"
 #include "nsPIDOMWindow.h"
 #include "nsPIWindowRoot.h"
 #include "nsLayoutUtils.h"
 #include "nsPrintfCString.h"
 #include "nsThreadUtils.h"
 #include "nsWeakReference.h"
 #include "PermissionMessageUtils.h"
@@ -2309,18 +2310,18 @@ TabChild::InitRenderingState()
         observerService->AddObserver(this,
                                      BROWSER_ZOOM_TO_RECT,
                                      false);
         observerService->AddObserver(this,
                                      BEFORE_FIRST_PAINT,
                                      false);
     }
 
-    // This state can't really change during the lifetime of the child.
-    sCpowsEnabled = Preferences::GetBool("browser.tabs.remote", false);
+    // This state can't change during the lifetime of the child.
+    sCpowsEnabled = BrowserTabsRemote();
     if (Preferences::GetBool("dom.ipc.cpows.force-enabled", false))
       sCpowsEnabled = true;
 
     return true;
 }
 
 void
 TabChild::SetBackgroundColor(const nscolor& aColor)
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -141,8 +141,10 @@ UseOfReleaseEventsWarning=Use of release
 # LOCALIZATION NOTE: Do not translate "document.load()" or "XMLHttpRequest"
 UseOfDOM3LoadMethodWarning=Use of document.load() is deprecated. To upgrade your code, use the DOM XMLHttpRequest object. For more help https://developer.mozilla.org/en/XMLHttpRequest
 # LOCALIZATION NOTE: Do not translate "window.showModalDialog()" or "window.open()" 
 ShowModalDialogWarning=Use of window.showModalDialog() is deprecated. Use window.open() instead. For more help https://developer.mozilla.org/en-US/docs/Web/API/Window.open
 # LOCALIZATION NOTE: Do not translate "cloneNode()"
 UnsafeCloneNodeWarning=The behavior of cloneNode() with no boolean argument is about to change from doing a deep clone to doing a shallow clone.  Make sure to pass an explicit boolean argument to keep your current behavior.
 # LOCALIZATION NOTE: Do not translate "importNode()"
 UnsafeImportNodeWarning=The behavior of importNode() with no boolean argument is about to change from doing a deep clone to doing a shallow clone.  Make sure to pass an explicit boolean argument to keep your current behavior.
+# LOCALIZATION NOTE: Do not translate "window._content" or "window.content"
+Window_ContentWarning=window._content is deprecated.  Please use window.content instead.
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -657,35 +657,46 @@ static bool SatisfyConstraint(const Medi
 typedef nsTArray<nsCOMPtr<nsIMediaDevice> > SourceSet;
 
 // Source getter that constrains list returned
 
 template<class SourceType>
 static SourceSet *
   GetSources(MediaEngine *engine,
              const MediaTrackConstraintsInternal &aConstraints,
-             void (MediaEngine::* aEnumerate)(nsTArray<nsRefPtr<SourceType> >*))
+             void (MediaEngine::* aEnumerate)(nsTArray<nsRefPtr<SourceType> >*),
+             char* media_device_name = nullptr)
 {
   const SourceType * const type = nullptr;
-
+  nsString deviceName;
   // First collect sources
   SourceSet candidateSet;
   {
     nsTArray<nsRefPtr<SourceType> > sources;
     (engine->*aEnumerate)(&sources);
-
     /**
       * We're allowing multiple tabs to access the same camera for parity
       * with Chrome.  See bug 811757 for some of the issues surrounding
       * this decision.  To disallow, we'd filter by IsAvailable() as we used
       * to.
       */
-
     for (uint32_t len = sources.Length(), i = 0; i < len; i++) {
-      candidateSet.AppendElement(new MediaDevice(sources[i]));
+#ifdef DEBUG
+      sources[i]->GetName(deviceName);
+      if (media_device_name && strlen(media_device_name) > 0)  {
+        if (deviceName.EqualsASCII(media_device_name)) {
+          candidateSet.AppendElement(new MediaDevice(sources[i]));
+          break;
+        }
+      } else {
+#endif
+        candidateSet.AppendElement(new MediaDevice(sources[i]));
+#ifdef DEBUG
+      }
+#endif
     }
   }
 
   // Then apply mandatory constraints
 
   // Note: Iterator must be signed as it can dip below zero
   for (int i = 0; i < int(candidateSet.Length()); i++) {
     // Overloading instead of template specialization keeps things local
@@ -998,47 +1009,57 @@ private:
  */
 class GetUserMediaDevicesRunnable : public nsRunnable
 {
 public:
   GetUserMediaDevicesRunnable(
     const MediaStreamConstraintsInternal& aConstraints,
     already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> aSuccess,
     already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
-    uint64_t aWindowId)
+    uint64_t aWindowId, char* aAudioLoopbackDev, char* aVideoLoopbackDev)
     : mConstraints(aConstraints)
     , mSuccess(aSuccess)
     , mError(aError)
     , mManager(MediaManager::GetInstance())
-    , mWindowId(aWindowId) {}
+    , mWindowId(aWindowId)
+    , mLoopbackAudioDevice(aAudioLoopbackDev)
+    , mLoopbackVideoDevice(aVideoLoopbackDev) {}
 
   NS_IMETHOD
   Run()
   {
     NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
+
     MediaEngine *backend = mManager->GetBackend(mWindowId);
 
     ScopedDeletePtr<SourceSet> final (GetSources(backend, mConstraints.mVideom,
-                                          &MediaEngine::EnumerateVideoDevices));
+                                          &MediaEngine::EnumerateVideoDevices,
+                                          mLoopbackVideoDevice));
     {
       ScopedDeletePtr<SourceSet> s (GetSources(backend, mConstraints.mAudiom,
-                                        &MediaEngine::EnumerateAudioDevices));
+                                        &MediaEngine::EnumerateAudioDevices,
+                                        mLoopbackAudioDevice));
       final->MoveElementsFrom(*s);
     }
     NS_DispatchToMainThread(new DeviceSuccessCallbackRunnable(mSuccess, mError,
                                                               final.forget()));
     return NS_OK;
   }
 
 private:
   MediaStreamConstraintsInternal mConstraints;
   already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> mSuccess;
   already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
   nsRefPtr<MediaManager> mManager;
   uint64_t mWindowId;
+  // Audio & Video loopback devices to be used based on
+  // the preference settings. This is currently used for
+  // automated media tests only.
+  char* mLoopbackAudioDevice;
+  char* mLoopbackVideoDevice;
 };
 
 MediaManager::MediaManager()
   : mMediaThread(nullptr)
   , mMutex("mozilla::MediaManager")
   , mBackend(nullptr) {
   mPrefs.mWidth  = MediaEngine::DEFAULT_VIDEO_WIDTH;
   mPrefs.mHeight = MediaEngine::DEFAULT_VIDEO_HEIGHT;
@@ -1387,23 +1408,38 @@ MediaManager::GetUserMediaDevices(nsPIDO
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   NS_ENSURE_TRUE(aOnError, NS_ERROR_NULL_POINTER);
   NS_ENSURE_TRUE(aOnSuccess, NS_ERROR_NULL_POINTER);
 
   nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> onSuccess(aOnSuccess);
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onError(aOnError);
+  char* loopbackAudioDevice = nullptr;
+  char* loopbackVideoDevice = nullptr;
+  nsresult rv;
+#ifdef DEBUG
+  // Check if the preference for using loopback devices is enabled.
+  nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv);
+  if (NS_SUCCEEDED(rv)) {
+    nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs);
+    if (branch) {
+      branch->GetCharPref("media.audio_loopback_dev", &loopbackAudioDevice);
+      branch->GetCharPref("media.video_loopback_dev", &loopbackVideoDevice);
+    }
+  }
+#endif
 
   nsCOMPtr<nsIRunnable> gUMDRunnable = new GetUserMediaDevicesRunnable(
-    aConstraints, onSuccess.forget(), onError.forget(), aWindow->WindowID()
+    aConstraints, onSuccess.forget(), onError.forget(), aWindow->WindowID(),
+    loopbackAudioDevice, loopbackVideoDevice
   );
 
   nsCOMPtr<nsIThread> deviceThread;
-  nsresult rv = NS_NewThread(getter_AddRefs(deviceThread));
+  rv = NS_NewThread(getter_AddRefs(deviceThread));
   NS_ENSURE_SUCCESS(rv, rv);
 
 
   deviceThread->Dispatch(gUMDRunnable, NS_DISPATCH_NORMAL);
   return NS_OK;
 }
 
 MediaEngine*
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -493,17 +493,17 @@ NS_IMETHODIMP nsPluginInstanceOwner::Get
   }
 
   nsPresContext *presContext = presShell->GetPresContext();
   if (!presContext) {
     return NS_ERROR_FAILURE;
   }
 
   // the container of the pres context will give us the link handler
-  nsCOMPtr<nsISupports> container = presContext->GetContainer();
+  nsCOMPtr<nsISupports> container = presContext->GetContainerWeak();
   NS_ENSURE_TRUE(container,NS_ERROR_FAILURE);
   nsCOMPtr<nsILinkHandler> lh = do_QueryInterface(container);
   NS_ENSURE_TRUE(lh, NS_ERROR_FAILURE);
 
   nsAutoString  unitarget;
   unitarget.AssignASCII(aTarget); // XXX could this be nonascii?
 
   nsCOMPtr<nsIURI> baseURI = GetBaseURI();
@@ -549,22 +549,17 @@ NS_IMETHODIMP nsPluginInstanceOwner::Sho
 
 NS_IMETHODIMP nsPluginInstanceOwner::ShowStatus(const PRUnichar *aStatusMsg)
 {
   nsresult  rv = NS_ERROR_FAILURE;
 
   if (!mObjectFrame) {
     return rv;
   }
-  nsCOMPtr<nsISupports> cont = mObjectFrame->PresContext()->GetContainer();
-  if (!cont) {
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsIDocShellTreeItem> docShellItem(do_QueryInterface(cont, &rv));
+  nsCOMPtr<nsIDocShellTreeItem> docShellItem = mObjectFrame->PresContext()->GetDocShell();
   if (NS_FAILED(rv) || !docShellItem) {
     return rv;
   }
 
   nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
   rv = docShellItem->GetTreeOwner(getter_AddRefs(treeOwner));
   if (NS_FAILED(rv) || !treeOwner) {
     return rv;
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -608,16 +608,17 @@ var interfaceNamesInGlobalScope =
     {name: "TreeColumn", xbl: true},
     {name: "TreeColumns", xbl: true},
     {name: "TreeContentView", xbl: true},
     {name: "TreeSelection", xbl: true},
     "TreeWalker",
     "UIEvent",
     "UndoManager",
     "URL",
+    "URLSearchParams",
     {name: "UserDataHandler", xbl: true},
     "UserProximityEvent",
     {name: "USSDReceivedEvent", b2g: true, pref: "dom.mobileconnection.enabled"},
     "ValidityState",
     "VideoStreamTrack",
     "VTTCue",
     "VTTRegion",
     "VTTRegionList",
--- a/dom/tests/mochitest/whatwg/test_bug500328.html
+++ b/dom/tests/mochitest/whatwg/test_bug500328.html
@@ -273,39 +273,39 @@ function runTest() {
      "Wrong state object popped after going back to page 1.");
   ok(gLastPopStateEvent.state === iframeCw.history.state,
      "Wrong state object in document after going back to page 1.");
   ok(iframeCw.location.toString().match(/file_bug500328_1.html$/),
       "Going back to page 1 hould take us to original page.");
 
   iframeCw.history.back();
   popstateExpected("Going back to page 0 should trigger a popstate.");
-  is(gLastPopStateEvent.state, null,
+  ise(gLastPopStateEvent.state, null,
       "Going back to page 0 should pop a null state.");
-  is(iframeCw.history.state, null,
+  ise(iframeCw.history.state, null,
       "Going back to page 0 should pop a null state.");
-  is(iframeCw.location.search, "",
+  ise(iframeCw.location.search, "",
       "Going back to page 0 should clear the querystring.");
 
   iframeCw.history.forward();
   popstateExpected("Going forward to page 1 should trigger a popstate.");
   is(JSON.stringify(gLastPopStateEvent.state), JSON.stringify(testObj1),
       "Wrong state object popped after going forward to page 1.");
-  ok(gLastPopStateEvent.state === iframeCw.history.state,
+  ise(gLastPopStateEvent.state, iframeCw.history.state,
       "Wrong state object in document after going forward to page 1.");
   ok(iframeCw.location.toString().match(/file_bug500328_1.html$/),
       "Going forward to page 1 should leave us at original page.");
 
   statusMsg("About to go forward to page 2.");
   iframeCw.history.forward();
   statusMsg("Awake after going forward to page 2.");
   popstateExpected("Going forward to page 2 should trigger a popstate.");
   is(JSON.stringify(gLastPopStateEvent.state), JSON.stringify(testObj2),
      "Wrong state object popped after going forward to page 2.");
-  ok(iframeCw.history.state === gLastPopStateEvent.state,
+  ise(iframeCw.history.state, gLastPopStateEvent.state,
      "Wrong state object in document after going forward to page 2.");
   ok(iframeCw.location.toString().match(/file_bug500328_1.html\?test1#foo$/),
      "Going forward to page 2 took us to " + iframeCw.location.toString());
 
   statusMsg("About to reload page 2.");
   iframeCw.location.reload();
   enableChildLoadCallback();
   yield undefined;
@@ -324,17 +324,17 @@ function runTest() {
   enableChildPopStateCallback();
   sendMouseEvent({type:'click'}, 'link-anchor1', iframeCw);
   yield undefined;
   popstateExpected("Clicking on link-anchor1 should trigger a popstate.");
   is(iframeCw.location.search, "?test1",
       "search should be ?test1 after clicking link.");
   is(iframeCw.location.hash, "#1",
       "hash should be #1 after clicking link.");
-  ok(iframeCw.history.state === null,
+  ise(iframeCw.history.state, null,
      "Wrong state object in document after clicking link to hash '#1'.");
 
   /*
    * Reload file_bug500328_1.html; we're now going to test that link hrefs
    * and colors are updated correctly on push/popstates.
    */
 
   iframe.onload = onChildLoad;
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -14,16 +14,17 @@
  *
  * http://mxr.mozilla.org/mozilla-central/source/dom/interfaces/core/nsIDOMDocument.idl
  */
 
 interface StyleSheetList;
 interface WindowProxy;
 interface nsISupports;
 interface URI;
+interface nsIDocShell;
 
 enum VisibilityState { "hidden", "visible" };
 
 /* http://dom.spec.whatwg.org/#interface-document */
 [Constructor]
 interface Document : Node {
   [Throws]
   readonly attribute DOMImplementation implementation;
@@ -335,16 +336,18 @@ partial interface Document {
 
   [ChromeOnly]
   attribute boolean styleSheetChangeEventsEnabled;
 
   [ChromeOnly, Throws]
   void obsoleteSheet(URI sheetURI);
   [ChromeOnly, Throws]
   void obsoleteSheet(DOMString sheetURI);
+
+  [ChromeOnly] readonly attribute nsIDocShell? docShell;
 };
 
 // Extension to give chrome JS the ability to determine when a document was
 // created to satisfy an iframe with srcdoc attribute.
 partial interface Document {
   [ChromeOnly] readonly attribute boolean isSrcdocDocument;
 };
 
new file mode 100644
--- /dev/null
+++ b/dom/webidl/URLSearchParams.webidl
@@ -0,0 +1,26 @@
+/* -*- 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://url.spec.whatwg.org/#urlsearchparams
+ *
+ * To the extent possible under law, the editors have waived all copyright
+ * and related or neighboring rights to this work. In addition, as of 17
+ * February 2013, the editors have made this specification available under
+ * the Open Web Foundation Agreement Version 1.0, which is available at
+ * http://www.openwebfoundation.org/legal/the-owf-1-0-agreements/owfa-1-0.
+ */
+
+[Constructor(optional DOMString init = ""),
+ Constructor(URLSearchParams init)]
+interface URLSearchParams {
+  DOMString? get(DOMString name);
+  sequence<DOMString> getAll(DOMString name);
+  void set(DOMString name, DOMString value);
+  void append(DOMString name, DOMString value);
+  boolean has(DOMString name);
+  void delete(DOMString name);
+  readonly attribute unsigned long size;
+};
--- a/dom/webidl/URLUtils.webidl
+++ b/dom/webidl/URLUtils.webidl
@@ -22,12 +22,12 @@ interface URLUtils {
            attribute DOMString protocol;
            attribute DOMString username;
            attribute DOMString password;
            attribute DOMString host;
            attribute DOMString hostname;
            attribute DOMString port;
            attribute DOMString pathname;
            attribute DOMString search;
-           // attribute URLQuery? query;
+           attribute URLSearchParams? searchParams;
            attribute DOMString hash;
 };
 
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -402,16 +402,17 @@ WEBIDL_FILES = [
     'TouchEvent.webidl',
     'TouchList.webidl',
     'TransitionEvent.webidl',
     'TreeColumns.webidl',
     'TreeWalker.webidl',
     'UIEvent.webidl',
     'UndoManager.webidl',
     'URL.webidl',
+    'URLSearchParams.webidl',
     'URLUtils.webidl',
     'URLUtilsReadOnly.webidl',
     'ValidityState.webidl',
     'VideoPlaybackQuality.webidl',
     'VideoStreamTrack.webidl',
     'VTTCue.webidl',
     'VTTRegion.webidl',
     'VTTRegionList.webidl',
--- a/dom/workers/RegisterBindings.cpp
+++ b/dom/workers/RegisterBindings.cpp
@@ -21,16 +21,17 @@
 #include "mozilla/dom/MessageEventBinding.h"
 #include "mozilla/dom/MessagePortBinding.h"
 #include "mozilla/dom/PromiseBinding.h"
 #include "mozilla/dom/TextDecoderBinding.h"
 #include "mozilla/dom/TextEncoderBinding.h"
 #include "mozilla/dom/XMLHttpRequestBinding.h"
 #include "mozilla/dom/XMLHttpRequestUploadBinding.h"
 #include "mozilla/dom/URLBinding.h"
+#include "mozilla/dom/URLSearchParamsBinding.h"
 #include "mozilla/dom/WorkerBinding.h"
 #include "mozilla/dom/WorkerLocationBinding.h"
 #include "mozilla/dom/WorkerNavigatorBinding.h"
 #include "mozilla/OSFileConstants.h"
 
 USING_WORKERS_NAMESPACE
 using namespace mozilla::dom;
 
@@ -65,16 +66,17 @@ WorkerPrivate::RegisterBindings(JSContex
       !MessagePortBinding::GetConstructorObject(aCx, aGlobal) ||
       (PromiseEnabled() &&
         !PromiseBinding::GetConstructorObject(aCx, aGlobal)) ||
       !TextDecoderBinding::GetConstructorObject(aCx, aGlobal) ||
       !TextEncoderBinding::GetConstructorObject(aCx, aGlobal) ||
       !XMLHttpRequestBinding_workers::GetConstructorObject(aCx, aGlobal) ||
       !XMLHttpRequestUploadBinding_workers::GetConstructorObject(aCx, aGlobal) ||
       !URLBinding_workers::GetConstructorObject(aCx, aGlobal) ||
+      !URLSearchParamsBinding::GetConstructorObject(aCx, aGlobal) ||
       !WorkerBinding::GetConstructorObject(aCx, aGlobal) ||
       !WorkerLocationBinding_workers::GetConstructorObject(aCx, aGlobal) ||
       !WorkerNavigatorBinding_workers::GetConstructorObject(aCx, aGlobal)) {
     return false;
   }
 
   if (!JS_DefineProfilingFunctions(aCx, aGlobal)) {
     return false;
--- a/dom/workers/URL.cpp
+++ b/dom/workers/URL.cpp
@@ -14,16 +14,17 @@
 #include "nsHostObjectProtocolHandler.h"
 #include "nsServiceManagerUtils.h"
 
 #include "nsIDocument.h"
 #include "nsIDOMFile.h"
 
 #include "mozilla/dom/URL.h"
 #include "mozilla/dom/URLBinding.h"
+#include "mozilla/dom/URLSearchParams.h"
 #include "nsIIOService.h"
 #include "nsNetCID.h"
 
 BEGIN_WORKERS_NAMESPACE
 using mozilla::dom::GlobalObject;
 
 class URLProxy MOZ_FINAL
 {
@@ -531,16 +532,27 @@ public:
 
 private:
   const nsString mValue;
   SetterType mType;
   nsRefPtr<URLProxy> mURLProxy;
   mozilla::ErrorResult& mRv;
 };
 
+NS_IMPL_CYCLE_COLLECTION_1(URL, mSearchParams)
+
+// The reason for using worker::URL is to have different refcnt logging than
+// for main thread URL.
+NS_IMPL_CYCLE_COLLECTING_ADDREF(workers::URL)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(workers::URL)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URL)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
 // static
 URL*
 URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
                  URL& aBase, ErrorResult& aRv)
 {
   JSContext* cx = aGlobal.GetContext();
   WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
 
@@ -601,20 +613,19 @@ URL::~URL()
 
     if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
       NS_ERROR("Failed to dispatch teardown runnable!");
     }
   }
 }
 
 JSObject*
-URL::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope,
-                bool* aTookOwnership)
+URL::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
-  return URLBinding_workers::Wrap(aCx, aScope, this, aTookOwnership);
+  return URLBinding_workers::Wrap(aCx, aScope, this);
 }
 
 void
 URL::GetHref(nsString& aHref) const
 {
   nsRefPtr<GetterRunnable> runnable =
     new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterHref, aHref,
                        mURLProxy);
@@ -629,16 +640,20 @@ URL::SetHref(const nsAString& aHref, Err
 {
   nsRefPtr<SetterRunnable> runnable =
     new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterHref, aHref,
                        mURLProxy, aRv);
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
+
+  if (mSearchParams) {
+    mSearchParams->Invalidate();
+  }
 }
 
 void
 URL::GetOrigin(nsString& aOrigin) const
 {
   nsRefPtr<GetterRunnable> runnable =
     new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterOrigin, aOrigin,
                        mURLProxy);
@@ -833,26 +848,66 @@ URL::GetSearch(nsString& aSearch) const
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
 }
 
 void
 URL::SetSearch(const nsAString& aSearch)
 {
+  SetSearchInternal(aSearch);
+
+  if (mSearchParams) {
+    mSearchParams->Invalidate();
+  }
+}
+
+void
+URL::SetSearchInternal(const nsAString& aSearch)
+{
   ErrorResult rv;
   nsRefPtr<SetterRunnable> runnable =
     new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterSearch,
                        aSearch, mURLProxy, rv);
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
 }
 
+mozilla::dom::URLSearchParams*
+URL::GetSearchParams()
+{
+  CreateSearchParamsIfNeeded();
+  return mSearchParams;
+}
+
+void
+URL::SetSearchParams(URLSearchParams* aSearchParams)
+{
+  if (!aSearchParams) {
+    return;
+  }
+
+  if (!aSearchParams->HasURLAssociated()) {
+    MOZ_ASSERT(aSearchParams->IsValid());
+
+    mSearchParams = aSearchParams;
+    mSearchParams->SetObserver(this);
+  } else {
+    CreateSearchParamsIfNeeded();
+    mSearchParams->CopyFromURLSearchParams(*aSearchParams);
+  }
+
+
+  nsString search;
+  mSearchParams->Serialize(search);
+  SetSearchInternal(search);
+}
+
 void
 URL::GetHash(nsString& aHash) const
 {
   nsRefPtr<GetterRunnable> runnable =
     new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterHash, aHash,
                        mURLProxy);
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
@@ -919,9 +974,39 @@ URL::RevokeObjectURL(const GlobalObject&
   nsRefPtr<RevokeURLRunnable> runnable =
     new RevokeURLRunnable(workerPrivate, aUrl);
 
   if (!runnable->Dispatch(cx)) {
     JS_ReportPendingException(cx);
   }
 }
 
+void
+URL::URLSearchParamsUpdated()
+{
+  MOZ_ASSERT(mSearchParams && mSearchParams->IsValid());
+
+  nsString search;
+  mSearchParams->Serialize(search);
+  SetSearchInternal(search);
+}
+
+void
+URL::URLSearchParamsNeedsUpdates()
+{
+  MOZ_ASSERT(mSearchParams);
+
+  nsString search;
+  GetSearch(search);
+  mSearchParams->ParseInput(NS_ConvertUTF16toUTF8(Substring(search, 1)));
+}
+
+void
+URL::CreateSearchParamsIfNeeded()
+{
+  if (!mSearchParams) {
+    mSearchParams = new URLSearchParams();
+    mSearchParams->SetObserver(this);
+    mSearchParams->Invalidate();
+  }
+}
+
 END_WORKERS_NAMESPACE
--- a/dom/workers/URL.h
+++ b/dom/workers/URL.h
@@ -6,45 +6,48 @@
 
 #ifndef mozilla_dom_workers_url_h__
 #define mozilla_dom_workers_url_h__
 
 #include "Workers.h"
 
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/BindingDeclarations.h"
-#include "mozilla/dom/NonRefcountedDOMObject.h"
+#include "mozilla/dom/URLSearchParams.h"
 
 namespace mozilla {
 namespace dom {
 struct objectURLOptions;
 }
 }
 
 BEGIN_WORKERS_NAMESPACE
 
 class URLProxy;
 
-class URL MOZ_FINAL : public NonRefcountedDOMObject
+class URL MOZ_FINAL : public mozilla::dom::URLSearchParamsObserver
 {
+  typedef mozilla::dom::URLSearchParams URLSearchParams;
+
 public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(URL)
 
   URL(WorkerPrivate* aWorkerPrivate, URLProxy* aURLProxy);
   ~URL();
 
   nsISupports*
   GetParentObject() const
   {
     // There's only one global on a worker, so we don't need to specify.
     return nullptr;
   }
 
   JSObject*
-  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope,
-             bool* aTookOwnership);
+  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope);
 
   // Methods for WebIDL
 
   static URL*
   Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
               URL& aBase, ErrorResult& aRv);
   static URL*
   Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
@@ -96,25 +99,38 @@ public:
   void GetPathname(nsString& aPathname) const;
 
   void SetPathname(const nsAString& aPathname);
 
   void GetSearch(nsString& aSearch) const;
 
   void SetSearch(const nsAString& aSearch);
 
+  URLSearchParams* GetSearchParams();
+
+  void SetSearchParams(URLSearchParams* aSearchParams);
+
   void GetHash(nsString& aHost) const;
 
   void SetHash(const nsAString& aHash);
 
+  // IURLSearchParamsObserver
+  void URLSearchParamsUpdated() MOZ_OVERRIDE;
+  void URLSearchParamsNeedsUpdates() MOZ_OVERRIDE;
+
 private:
   URLProxy* GetURLProxy() const
   {
     return mURLProxy;
   }
 
+  void CreateSearchParamsIfNeeded();
+
+  void SetSearchInternal(const nsAString& aSearch);
+
   WorkerPrivate* mWorkerPrivate;
   nsRefPtr<URLProxy> mURLProxy;
+  nsRefPtr<URLSearchParams> mSearchParams;
 };
 
 END_WORKERS_NAMESPACE
 
 #endif /* mozilla_dom_workers_url_h__ */
--- a/dom/workers/test/mochitest.ini
+++ b/dom/workers/test/mochitest.ini
@@ -52,16 +52,17 @@ support-files =
   url_worker.js
   workersDisabled_worker.js
   xhr2_worker.js
   xhrAbort_worker.js
   xhr_implicit_cancel_worker.js
   xhr_worker.js
   url_exceptions_worker.js
   jsversion_worker.js
+  urlSearchParams_worker.js
 
 [test_404.html]
 [test_atob.html]
 [test_blobConstructor.html]
 [test_blobWorkers.html]
 [test_chromeWorker.html]
 [test_clearTimeouts.html]
 [test_close.html]
@@ -109,9 +110,10 @@ support-files =
 [test_xhr2.html]
 [test_xhrAbort.html]
 [test_xhr_implicit_cancel.html]
 [test_xhr_parameters.html]
 [test_xhr_parameters.js]
 [test_xhr_system.html]
 [test_xhr_system.js]
 [test_url_exceptions.html]
+[test_urlSearchParams.html]
 [test_jsversion.html]
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_urlSearchParams.html
@@ -0,0 +1,43 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for URLSearchParams object in workers</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+  var worker = new Worker("urlSearchParams_worker.js");
+
+  worker.onmessage = function(event) {
+    is(event.target, worker);
+
+    if (event.data.type == 'finish') {
+      SimpleTest.finish();
+    } else if (event.data.type == 'status') {
+      ok(event.data.status, event.data.msg);
+    }
+  };
+
+  worker.onerror = function(event) {
+    is(event.target, worker);
+    ok(false, "Worker had an error: " + event.data);
+    SimpleTest.finish();
+  };
+
+  worker.postMessage(true);
+
+  SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/urlSearchParams_worker.js
@@ -0,0 +1,192 @@
+function ok(a, msg) {
+  dump("OK: " + !!a + "  =>  " + a + " " + msg + "\n");
+  postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
+}
+
+function is(a, b, msg) {
+  dump("IS: " + (a===b) + "  =>  " + a + " | " + b + " " + msg + "\n");
+  postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
+}
+
+function isnot(a, b, msg) {
+  dump("ISNOT: " + (a!==b) + "  =>  " + a + " | " + b + " " + msg + "\n");
+  postMessage({type: 'status', status: a !== b, msg: a + " !== " + b + ": " + msg });
+}
+
+onmessage = function() {
+  status = false;
+  try {
+    if ((URLSearchParams instanceof Object)) {
+      status = true;
+    }
+  } catch(e) {
+  }
+  ok(status, "URLSearchParams in workers \\o/");
+
+  function testSimpleURLSearchParams() {
+    var u = new URLSearchParams();
+    ok(u, "URLSearchParams created");
+    is(u.has('foo'), false, 'URLSearchParams.has(foo)');
+    is(u.get('foo'), '', 'URLSearchParams.get(foo)');
+    is(u.getAll('foo').length, 0, 'URLSearchParams.getAll(foo)');
+    is(u.size, 0, 'URLSearchParams.size()');
+
+    u.append('foo', 'bar');
+    is(u.has('foo'), true, 'URLSearchParams.has(foo)');
+    is(u.get('foo'), 'bar', 'URLSearchParams.get(foo)');
+    is(u.getAll('foo').length, 1, 'URLSearchParams.getAll(foo)');
+    is(u.size, 1, 'URLSearchParams.size()');
+
+    u.set('foo', 'bar2');
+    is(u.get('foo'), 'bar2', 'URLSearchParams.get(foo)');
+    is(u.getAll('foo').length, 1, 'URLSearchParams.getAll(foo)');
+    is(u.size, 1, 'URLSearchParams.size()');
+
+    u.delete('foo');
+    is(u.size, 0, 'URLSearchParams.size()');
+
+    runTest();
+  }
+
+  function testCopyURLSearchParams() {
+    var u = new URLSearchParams();
+    ok(u, "URLSearchParams created");
+    u.append('foo', 'bar');
+    is(u.size, 1, "u.size()");
+
+    var uu = new URLSearchParams(u);
+    is(uu.size, 1, "uu.size()");
+    is(uu.get('foo'), 'bar', 'uu.get()');
+
+    u.append('foo', 'bar2');
+    is(u.getAll('foo').length, 2, "u.getAll()");
+    is(uu.getAll('foo').length, 1, "uu.getAll()");
+
+    runTest();
+  }
+
+  function testParserURLSearchParams() {
+    var checks = [
+      { input: '', data: {} },
+      { input: 'a', data: { 'a' : [''] } },
+      { input: 'a=b', data: { 'a' : ['b'] } },
+      { input: 'a=', data: { 'a' : [''] } },
+      { input: '=b', data: { '' : ['b'] } },
+      { input: '&', data: {} },
+      { input: '&a', data: { 'a' : [''] } },
+      { input: 'a&', data: { 'a' : [''] } },
+      { input: 'a&a', data: { 'a' : ['', ''] } },
+      { input: 'a&b&c', data: { 'a' : [''], 'b' : [''], 'c' : [''] } },
+      { input: 'a=b&c=d', data: { 'a' : ['b'], 'c' : ['d'] } },
+      { input: 'a=b&c=d&', data: { 'a' : ['b'], 'c' : ['d'] } },
+      { input: '&&&a=b&&&&c=d&', data: { 'a' : ['b'], 'c' : ['d'] } },
+      { input: 'a=a&a=b&a=c', data: { 'a' : ['a', 'b', 'c'] } },
+      { input: 'a==a', data: { 'a' : ['=a'] } },
+      { input: 'a=a+b+c+d', data: { 'a' : ['a b c d'] } },
+      { input: '%=a', data: { '%' : ['a'] } },
+      { input: '%a=a', data: { '%a' : ['a'] } },
+      { input: '%a_=a', data: { '%a_' : ['a'] } },
+      { input: '%61=a', data: { 'a' : ['a'] } },
+      { input: '%=a', data: { '%' : ['a'] } },
+      { input: '%a=a', data: { '%a' : ['a'] } },
+      { input: '%a_=a', data: { '%a_' : ['a'] } },
+      { input: '%61=a', data: { 'a' : ['a'] } },
+      { input: '%61+%4d%4D=', data: { 'a MM' : [''] } },
+    ];
+
+    for (var i = 0; i < checks.length; ++i) {
+      var u = new URLSearchParams(checks[i].input);
+
+      var count = 0;
+      for (var key in checks[i].data) {
+        ++count;
+        ok(u.has(key), "key " + key + " found");
+
+        var all = u.getAll(key);
+        is(all.length, checks[i].data[key].length, "same number of elements");
+
+        for (var k = 0; k < all.length; ++k) {
+          is(all[k], checks[i].data[key][k], "value matches");
+        }
+      }
+
+      is(u.size, count, "size matches");
+    }
+
+    runTest();
+  }
+
+  function testURL() {
+    var url = new URL('http://www.example.net?a=b&c=d');
+    ok(url.searchParams, "URL searchParams exists!");
+    ok(url.searchParams.has('a'), "URL.searchParams.has('a')");
+    is(url.searchParams.get('a'), 'b', "URL.searchParams.get('a')");
+    ok(url.searchParams.has('c'), "URL.searchParams.has('c')");
+    is(url.searchParams.get('c'), 'd', "URL.searchParams.get('c')");
+
+    url.searchParams.set('e', 'f');
+    ok(url.href.indexOf('e=f') != 1, 'URL right');
+
+    var u = new URLSearchParams();
+    u.append('foo', 'bar');
+    url.searchParams = u;
+    is(url.searchParams, u, "URL.searchParams is the same object");
+    is(url.searchParams.get('foo'), 'bar', "URL.searchParams.get('foo')");
+    is(url.href, 'http://www.example.net/?foo=bar', 'URL right');
+
+    url.searchParams = null;
+    is(url.searchParams.get('foo'), 'bar', "URL.searchParams.get('foo')");
+    is(url.href, 'http://www.example.net/?foo=bar', 'URL right');
+
+    var url2 = new URL('http://www.example.net?e=f');
+    url.searchParams = url2.searchParams;
+    isnot(url.searchParams, url2.searchParams, "URL.searchParams is not the same object");
+    is(url.searchParams.get('e'), 'f', "URL.searchParams.get('e')");
+
+    url.href = "http://www.example.net?bar=foo";
+    is(url.searchParams.get('bar'), 'foo', "URL.searchParams.get('bar')");
+
+    runTest();
+  }
+
+  function testEncoding() {
+    var encoding = [ [ '1', '1' ],
+                     [ 'a b', 'a+b' ],
+                     [ '<>', '%3C%3E' ],
+                     [ '\u0541', '%D5%81'] ];
+
+    for (var i = 0; i < encoding.length; ++i) {
+      var a = new URLSearchParams();
+      a.set('a', encoding[i][0]);
+
+      var url = new URL('http://www.example.net');
+      url.searchParams = a;
+      is(url.href, 'http://www.example.net/?a=' + encoding[i][1]);
+
+      var url2 = new URL(url.href);
+      is(url2.searchParams.get('a'), encoding[i][0], 'a is still there');
+    }
+
+    runTest();
+  }
+
+  var tests = [
+    testSimpleURLSearchParams,
+    testCopyURLSearchParams,
+    testParserURLSearchParams,
+    testURL,
+    testEncoding
+  ];
+
+  function runTest() {
+    if (!tests.length) {
+      postMessage({type: 'finish' });
+      return;
+    }
+
+    var test = tests.shift();
+    test();
+  }
+
+  runTest();
+}
--- a/editor/composer/src/nsComposerDocumentCommands.cpp
+++ b/editor/composer/src/nsComposerDocumentCommands.cpp
@@ -100,21 +100,17 @@ nsSetDocumentOptionsCommand::DoCommandPa
     // http://lxr.mozilla.org/seamonkey/source/image/public/imgIContainer.idl
     presContext->SetImageAnimationMode(animationMode);
   }
 
   bool allowPlugins; 
   rv = aParams->GetBooleanValue("plugins", &allowPlugins);
   if (NS_SUCCEEDED(rv))
   {
-    nsCOMPtr<nsISupports> container = presContext->GetContainer();
-    NS_ENSURE_TRUE(container, NS_ERROR_FAILURE);
-
-    nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container, &rv));
-    NS_ENSURE_SUCCESS(rv, rv);
+    nsCOMPtr<nsIDocShell> docShell(presContext->GetDocShell());
     NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
 
     rv = docShell->SetAllowPlugins(allowPlugins);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
@@ -153,21 +149,17 @@ nsSetDocumentOptionsCommand::GetCommandS
                                presContext->ImageAnimationMode());
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   bool allowPlugins = false; 
   rv = aParams->GetBooleanValue("plugins", &allowPlugins);
   if (NS_SUCCEEDED(rv))
   {
-    nsCOMPtr<nsISupports> container = presContext->GetContainer();
-    NS_ENSURE_TRUE(container, NS_ERROR_FAILURE);
-
-    nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container, &rv));
-    NS_ENSURE_SUCCESS(rv, rv);
+    nsCOMPtr<nsIDocShell> docShell(presContext->GetDocShell());
     NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
 
     allowPlugins = docShell->PluginsAllowedInCurrentDoc();
 
     rv = aParams->SetBooleanValue("plugins", allowPlugins);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -518,17 +518,17 @@ public:
    */
   virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget) = 0;
 
   /* This copies the path describing the glyphs into a PathBuilder. We use this
    * API rather than a generic API to append paths because it allows easier
    * implementation in some backends, and more efficient implementation in
    * others.
    */
-  virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint = nullptr) = 0;
+  virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint = nullptr) = 0;
 
   virtual bool GetFontFileData(FontFileDataOutput, void *) { return false; }
 
   void AddUserData(UserDataKey *key, void *userData, void (*destroy)(void*)) {
     mUserData.Add(key, userData, destroy);
   }
   void *GetUserData(UserDataKey *key) {
     return mUserData.Get(key);
--- a/gfx/2d/PathSkia.cpp
+++ b/gfx/2d/PathSkia.cpp
@@ -104,16 +104,22 @@ PathBuilderSkia::CurrentPoint() const
 
 TemporaryRef<Path>
 PathBuilderSkia::Finish()
 {
   RefPtr<PathSkia> path = new PathSkia(mPath, mFillRule);
   return path;
 }
 
+void
+PathBuilderSkia::AppendPath(const SkPath &aPath)
+{
+  mPath.addPath(aPath);
+}
+
 TemporaryRef<PathBuilder>
 PathSkia::CopyToBuilder(FillRule aFillRule) const
 {
   return TransformedCopyToBuilder(Matrix(), aFillRule);
 }
 
 TemporaryRef<PathBuilder>
 PathSkia::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
--- a/gfx/2d/PathSkia.h
+++ b/gfx/2d/PathSkia.h
@@ -28,17 +28,20 @@ public:
   virtual void QuadraticBezierTo(const Point &aCP1,
                                  const Point &aCP2);
   virtual void Close();
   virtual void Arc(const Point &aOrigin, float aRadius, float aStartAngle,
                    float aEndAngle, bool aAntiClockwise = false);
   virtual Point CurrentPoint() const;
   virtual TemporaryRef<Path> Finish();
 
+  void AppendPath(const SkPath &aPath);
+
 private:
+
   void SetFillRule(FillRule aFillRule);
 
   SkPath mPath;
   FillRule mFillRule;
 };
 
 class PathSkia : public Path
 {
--- a/gfx/2d/ScaledFontBase.cpp
+++ b/gfx/2d/ScaledFontBase.cpp
@@ -2,18 +2,18 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ScaledFontBase.h"
 
 #ifdef USE_SKIA
 #include "PathSkia.h"
+#include "skia/SkEmptyShader.h"
 #include "skia/SkPaint.h"
-#include "skia/SkPath.h"
 #endif
 
 #ifdef USE_CAIRO
 #include "PathCairo.h"
 #include "DrawTargetCairo.h"
 #include "HelpersCairo.h"
 #endif
 
@@ -41,39 +41,51 @@ ScaledFontBase::ScaledFontBase(Float aSi
 #ifdef USE_SKIA
   mTypeface = nullptr;
 #endif
 #ifdef USE_CAIRO_SCALED_FONT
   mScaledFont = nullptr;
 #endif
 }
 
+#ifdef USE_SKIA
+SkPath
+ScaledFontBase::GetSkiaPathForGlyphs(const GlyphBuffer &aBuffer)
+{
+  SkTypeface *typeFace = GetSkTypeface();
+  MOZ_ASSERT(typeFace);
+
+  SkPaint paint;
+  paint.setTypeface(typeFace);
+  paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+  paint.setTextSize(SkFloatToScalar(mSize));
+
+  std::vector<uint16_t> indices;
+  std::vector<SkPoint> offsets;
+  indices.resize(aBuffer.mNumGlyphs);
+  offsets.resize(aBuffer.mNumGlyphs);
+
+  for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
+    indices[i] = aBuffer.mGlyphs[i].mIndex;
+    offsets[i].fX = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.x);
+    offsets[i].fY = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.y);
+  }
+
+  SkPath path;
+  paint.getPosTextPath(&indices.front(), aBuffer.mNumGlyphs*2, &offsets.front(), &path);
+  return path;
+}
+#endif
+
 TemporaryRef<Path>
 ScaledFontBase::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget)
 {
 #ifdef USE_SKIA
   if (aTarget->GetType() == BACKEND_SKIA) {
-    SkPaint paint;
-    paint.setTypeface(GetSkTypeface());
-    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-    paint.setTextSize(SkFloatToScalar(mSize));
-
-    std::vector<uint16_t> indices;
-    std::vector<SkPoint> offsets;
-    indices.resize(aBuffer.mNumGlyphs);
-    offsets.resize(aBuffer.mNumGlyphs);
-
-    for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
-      indices[i] = aBuffer.mGlyphs[i].mIndex;
-      offsets[i].fX = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.x);
-      offsets[i].fY = SkFloatToScalar(aBuffer.mGlyphs[i].mPosition.y);
-    }
-
-    SkPath path;
-    paint.getPosTextPath(&indices.front(), aBuffer.mNumGlyphs*2, &offsets.front(), &path);
+    SkPath path = GetSkiaPathForGlyphs(aBuffer);
     return new PathSkia(path, FILL_WINDING);
   }
 #endif
 #ifdef USE_CAIRO
   if (aTarget->GetType() == BACKEND_CAIRO) {
     MOZ_ASSERT(mScaledFont);
 
     DrawTarget *dt = const_cast<DrawTarget*>(aTarget);
@@ -108,46 +120,58 @@ ScaledFontBase::GetPathForGlyphs(const G
 
     return newPath;
   }
 #endif
   return nullptr;
 }
 
 void
-ScaledFontBase::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint)
+ScaledFontBase::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint)
 {
+#ifdef USE_SKIA
+  if (aBackendType == BACKEND_SKIA) {
+    PathBuilderSkia *builder = static_cast<PathBuilderSkia*>(aBuilder);
+    builder->AppendPath(GetSkiaPathForGlyphs(aBuffer));
+    return;
+  }
+#endif
 #ifdef USE_CAIRO
-  PathBuilderCairo* builder = static_cast<PathBuilderCairo*>(aBuilder);
+  if (aBackendType == BACKEND_CAIRO) {
+    MOZ_ASSERT(mScaledFont);
 
-  
-  cairo_t *ctx = cairo_create(DrawTargetCairo::GetDummySurface());
-
-  if (aTransformHint) {
-    cairo_matrix_t mat;
-    GfxMatrixToCairoMatrix(*aTransformHint, mat);
-    cairo_set_matrix(ctx, &mat);
-  }
+    PathBuilderCairo* builder = static_cast<PathBuilderCairo*>(aBuilder);
+    cairo_t *ctx = cairo_create(DrawTargetCairo::GetDummySurface());
 
-  // Convert our GlyphBuffer into an array of Cairo glyphs.
-  std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs);
-  for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) {
-    glyphs[i].index = aBuffer.mGlyphs[i].mIndex;
-    glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x;
-    glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y;
-  }
+    if (aTransformHint) {
+      cairo_matrix_t mat;
+      GfxMatrixToCairoMatrix(*aTransformHint, mat);
+      cairo_set_matrix(ctx, &mat);
+    }
 
-  cairo_set_scaled_font(ctx, mScaledFont);
-  cairo_glyph_path(ctx, &glyphs[0], aBuffer.mNumGlyphs);
+    // Convert our GlyphBuffer into an array of Cairo glyphs.
+    std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs);
+    for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) {
+      glyphs[i].index = aBuffer.mGlyphs[i].mIndex;
+      glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x;
+      glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y;
+    }
 
-  RefPtr<PathCairo> cairoPath = new PathCairo(ctx);
-  cairo_destroy(ctx);
+    cairo_set_scaled_font(ctx, mScaledFont);
+    cairo_glyph_path(ctx, &glyphs[0], aBuffer.mNumGlyphs);
+
+    RefPtr<PathCairo> cairoPath = new PathCairo(ctx);
+    cairo_destroy(ctx);
 
-  cairoPath->AppendPathToBuilder(builder);
+    cairoPath->AppendPathToBuilder(builder);
+    return;
+  }
 #endif
+
+  MOZ_CRASH("The specified backend type is not supported by CopyGlyphsToBuilder");
 }
 
 #ifdef USE_CAIRO_SCALED_FONT
 void
 ScaledFontBase::SetCairoScaledFont(cairo_scaled_font_t* font)
 {
   MOZ_ASSERT(!mScaledFont);
 
--- a/gfx/2d/ScaledFontBase.h
+++ b/gfx/2d/ScaledFontBase.h
@@ -9,16 +9,17 @@
 #include "2D.h"
 
 // Skia uses cairo_scaled_font_t as the internal font type in ScaledFont
 #if defined(USE_SKIA) || defined(USE_CAIRO)
 #define USE_CAIRO_SCALED_FONT
 #endif
 
 #ifdef USE_SKIA
+#include "skia/SkPath.h"
 #include "skia/SkTypeface.h"
 #endif
 #ifdef USE_CAIRO_SCALED_FONT
 #include "cairo.h"
 #endif
 
 class gfxFont;
 
@@ -28,17 +29,17 @@ namespace gfx {
 class ScaledFontBase : public ScaledFont
 {
 public:
   ScaledFontBase(Float aSize);
   virtual ~ScaledFontBase();
 
   virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
 
-  virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint);
+  virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint);
 
   float GetSize() { return mSize; }
 
 #ifdef USE_SKIA
   virtual SkTypeface* GetSkTypeface() { return mTypeface; }
 #endif
 
   // Not true, but required to instantiate a ScaledFontBase.
@@ -48,16 +49,17 @@ public:
   cairo_scaled_font_t* GetCairoScaledFont() { return mScaledFont; }
   void SetCairoScaledFont(cairo_scaled_font_t* font);
 #endif
 
 protected:
   friend class DrawTargetSkia;
 #ifdef USE_SKIA
   SkTypeface* mTypeface;
+  SkPath GetSkiaPathForGlyphs(const GlyphBuffer &aBuffer);
 #endif
 #ifdef USE_CAIRO_SCALED_FONT
   cairo_scaled_font_t* mScaledFont;
 #endif
   Float mSize;
 };
 
 }
--- a/gfx/2d/ScaledFontDWrite.cpp
+++ b/gfx/2d/ScaledFontDWrite.cpp
@@ -319,19 +319,23 @@ ScaledFontDWrite::GetPathForGlyphs(const
     static_cast<PathBuilderD2D*>(pathBuilder.get());
 
   CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink());
 
   return pathBuilder->Finish();
 }
 
 void
-ScaledFontDWrite::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *)
+ScaledFontDWrite::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint)
 {
-  // XXX - Check path builder type!
+  if (aBackendType != BACKEND_DIRECT2D) {
+    ScaledFontBase::CopyGlyphsToBuilder(aBuffer, aBuilder, aBackendType, aTransformHint);
+    return;
+  }
+
   PathBuilderD2D *pathBuilderD2D =
     static_cast<PathBuilderD2D*>(aBuilder);
 
   CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink());
 }
 
 void
 ScaledFontDWrite::CopyGlyphsToSink(const GlyphBuffer &aBuffer, ID2D1GeometrySink *aSink)
--- a/gfx/2d/ScaledFontDWrite.h
+++ b/gfx/2d/ScaledFontDWrite.h
@@ -21,17 +21,17 @@ public:
     : mFontFace(aFont)
     , ScaledFontBase(aSize)
   {}
   ScaledFontDWrite(uint8_t *aData, uint32_t aSize, uint32_t aIndex, Float aGlyphSize);
 
   virtual FontType GetType() const { return FONT_DWRITE; }
 
   virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
-  virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint);
+  virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint);
 
   void CopyGlyphsToSink(const GlyphBuffer &aBuffer, ID2D1GeometrySink *aSink);
 
   virtual bool GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton);
 
   virtual AntialiasMode GetDefaultAAMode();
 
 #ifdef USE_SKIA
--- a/gfx/2d/ScaledFontMac.cpp
+++ b/gfx/2d/ScaledFontMac.cpp
@@ -97,18 +97,23 @@ ScaledFontMac::GetPathForGlyphs(const Gl
       CGPathRelease(path);
       return ret;
   } else {
       return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget);
   }
 }
 
 void
-ScaledFontMac::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *)
+ScaledFontMac::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint)
 {
+  if (!(aBackendType == BACKEND_COREGRAPHICS || aBackendType == BACKEND_COREGRAPHICS_ACCELERATED)) {
+    ScaledFontBase::CopyGlyphsToBuilder(aBuffer, aBuilder, aBackendType, aTransformHint);
+    return;
+  }
+
   PathBuilderCG *pathBuilderCG =
     static_cast<PathBuilderCG*>(aBuilder);
   // XXX: check builder type
   for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
     // XXX: we could probably fold both of these transforms together to avoid extra work
     CGAffineTransform flip = CGAffineTransformMakeScale(1, -1);
     CGPathRef glyphPath = ::CGFontGetGlyphPath(mFont, &flip, 0, aBuffer.mGlyphs[i].mIndex);
 
--- a/gfx/2d/ScaledFontMac.h
+++ b/gfx/2d/ScaledFontMac.h
@@ -20,17 +20,17 @@ public:
   ScaledFontMac(CGFontRef aFont, Float aSize);
   virtual ~ScaledFontMac();
 
   virtual FontType GetType() const { return FONT_MAC; }
 #ifdef USE_SKIA
   virtual SkTypeface* GetSkTypeface();
 #endif
   virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
-  virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint);
+  virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint);
   virtual bool GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton);
 
 private:
   friend class DrawTargetCG;
   CGFontRef mFont;
   CTFontRef mCTFont; // only created if CTFontDrawGlyphs is available, otherwise null
 
   typedef void (CTFontDrawGlyphsFuncT)(CTFontRef,
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -485,24 +485,27 @@ CopyPlane(uint8_t *aDst, const uint8_t *
 }
 
 void
 PlanarYCbCrImage::CopyData(const Data& aData)
 {
   mData = aData;
 
   // update buffer size
-  mBufferSize = mData.mCbCrStride * mData.mCbCrSize.height * 2 +
+  size_t size = mData.mCbCrStride * mData.mCbCrSize.height * 2 +
                 mData.mYStride * mData.mYSize.height;
 
   // get new buffer
-  mBuffer = AllocateBuffer(mBufferSize);
+  mBuffer = AllocateBuffer(size);
   if (!mBuffer)
     return;
 
+  // update buffer size
+  mBufferSize = size;
+
   mData.mYChannel = mBuffer;
   mData.mCbChannel = mData.mYChannel + mData.mYStride * mData.mYSize.height;
   mData.mCrChannel = mData.mCbChannel + mData.mCbCrStride * mData.mCbCrSize.height;
 
   CopyPlane(mData.mYChannel, aData.mYChannel,
             mData.mYSize, mData.mYStride, mData.mYSkip);
   CopyPlane(mData.mCbChannel, aData.mCbChannel,
             mData.mCbCrSize, mData.mCbCrStride, mData.mCbSkip);
@@ -531,21 +534,22 @@ PlanarYCbCrImage::SetDataNoCopy(const Da
 {
   mData = aData;
   mSize = aData.mPicSize;
 }
 
 uint8_t*
 PlanarYCbCrImage::AllocateAndGetNewBuffer(uint32_t aSize)
 {
-  // update buffer size
-  mBufferSize = aSize;
-
   // get new buffer
-  mBuffer = AllocateBuffer(mBufferSize); 
+  mBuffer = AllocateBuffer(aSize);
+  if (mBuffer) {
+    // update buffer size
+    mBufferSize = aSize;
+  }
   return mBuffer;
 }
 
 already_AddRefed<gfxASurface>
 PlanarYCbCrImage::GetAsSurface()
 {
   if (mSurface) {
     nsRefPtr<gfxASurface> result = mSurface.get();
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -1271,17 +1271,18 @@ Layer::PrintInfo(nsACString& aTo, const 
   }
   if (GetScrollbarDirection() == VERTICAL) {
     aTo.AppendPrintf(" [vscrollbar=%lld]", GetScrollbarTargetContainerId());
   }
   if (GetScrollbarDirection() == HORIZONTAL) {
     aTo.AppendPrintf(" [hscrollbar=%lld]", GetScrollbarTargetContainerId());
   }
   if (GetIsFixedPosition()) {
-    aTo.AppendPrintf(" [isFixedPosition anchor=%f,%f]", mAnchor.x, mAnchor.y);
+    aTo.AppendPrintf(" [isFixedPosition anchor=%f,%f margin=%f,%f,%f,%f]", mAnchor.x, mAnchor.y,
+                     mMargins.top, mMargins.right, mMargins.bottom, mMargins.left);
   }
   if (GetIsStickyPosition()) {
     aTo.AppendPrintf(" [isStickyPosition scrollId=%d outer=%f,%f %fx%f "
                      "inner=%f,%f %fx%f]", mStickyPositionData->mScrollId,
                      mStickyPositionData->mOuter.x, mStickyPositionData->mOuter.y,
                      mStickyPositionData->mOuter.width, mStickyPositionData->mOuter.height,
                      mStickyPositionData->mInner.x, mStickyPositionData->mInner.y,
                      mStickyPositionData->mInner.width, mStickyPositionData->mInner.height);
--- a/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
+++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
@@ -121,24 +121,26 @@ SharedPlanarYCbCrImage::SetData(const Pl
 
 // needs to be overriden because the parent class sets mBuffer which we
 // do not want to happen.
 uint8_t*
 SharedPlanarYCbCrImage::AllocateAndGetNewBuffer(uint32_t aSize)
 {
   NS_ABORT_IF_FALSE(!mTextureClient->IsAllocated(), "This image already has allocated data");
   size_t size = YCbCrImageDataSerializer::ComputeMinBufferSize(aSize);
+
+  // get new buffer _without_ setting mBuffer.
+  if (!mTextureClient->Allocate(size)) {
+    return nullptr;
+  }
+
   // update buffer size
   mBufferSize = size;
 
-  // get new buffer _without_ setting mBuffer.
-  bool status = mTextureClient->Allocate(mBufferSize);
-  MOZ_ASSERT(status);
   YCbCrImageDataSerializer serializer(mTextureClient->GetBuffer());
-
   return serializer.GetData();
 }
 
 void
 SharedPlanarYCbCrImage::SetDataNoCopy(const Data &aData)
 {
   mData = aData;
   mSize = aData.mPicSize;
@@ -243,27 +245,29 @@ DeprecatedSharedPlanarYCbCrImage::SetDat
 
 // needs to be overriden because the parent class sets mBuffer which we
 // do not want to happen.
 uint8_t*
 DeprecatedSharedPlanarYCbCrImage::AllocateAndGetNewBuffer(uint32_t aSize)
 {
   NS_ABORT_IF_FALSE(!mAllocated, "This image already has allocated data");
   size_t size = YCbCrImageDataSerializer::ComputeMinBufferSize(aSize);
+
+  // get new buffer _without_ setting mBuffer.
+  if (!AllocateBuffer(size)) {
+    return nullptr;
+  }
+
   // update buffer size
   mBufferSize = size;
 
-  // get new buffer _without_ setting mBuffer.
-  AllocateBuffer(mBufferSize);
   YCbCrImageDataSerializer serializer(mShmem.get<uint8_t>());
-
   return serializer.GetData();
 }
 
-
 void
 DeprecatedSharedPlanarYCbCrImage::SetDataNoCopy(const Data &aData)
 {
   mData = aData;
   mSize = aData.mPicSize;
   YCbCrImageDataSerializer serializer(mShmem.get<uint8_t>());
   serializer.InitializeBufferInfo(aData.mYSize,
                                   aData.mCbCrSize,
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -2320,17 +2320,17 @@ struct GlyphBufferAzure {
             } else {
                 aDT->FillGlyphs(aFont, buf, ColorPattern(state.color),
                                 aDrawOptions, aOptions);
             }
         }
         if (int(aDrawMode) & int(DrawMode::GLYPH_PATH)) {
             aThebesContext->EnsurePathBuilder();
 			Matrix mat = aDT->GetTransform();
-            aFont->CopyGlyphsToBuilder(buf, aThebesContext->mPathBuilder, &mat);
+            aFont->CopyGlyphsToBuilder(buf, aThebesContext->mPathBuilder, aDT->GetType(), &mat);
         }
         if ((int(aDrawMode) & (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) ==
                               int(DrawMode::GLYPH_STROKE)) {
             FlushStroke(aDT, aContextPaint, aFont, aThebesContext, buf, state);
         }
 
         mNumGlyphs = 0;
     }
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -80,16 +80,17 @@
 #endif
 
 #include "mozilla/Preferences.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Mutex.h"
 
 #include "nsIGfxInfo.h"
+#include "nsIXULRuntime.h"
 
 using namespace mozilla;
 using namespace mozilla::layers;
 
 gfxPlatform *gPlatform = nullptr;
 static bool gEverInitialized = false;
 
 static Mutex* gGfxPlatformPrefsLock = nullptr;
@@ -2070,17 +2071,17 @@ InitLayersAccelerationPrefs()
     sPrefLayersAccelerationDisabled = Preferences::GetBool("layers.acceleration.disabled", false);
     sPrefLayersPreferOpenGL = Preferences::GetBool("layers.prefer-opengl", false);
     sPrefLayersPreferD3D9 = Preferences::GetBool("layers.prefer-d3d9", false);
     sPrefLayersDump = Preferences::GetBool("layers.dump", false);
     sPrefLayersScrollGraph = Preferences::GetBool("layers.scroll-graph", false);
     sPrefLayoutFrameRate = Preferences::GetInt("layout.frame_rate", -1);
     sBufferRotationEnabled = Preferences::GetBool("layers.bufferrotation.enabled", true);
     sComponentAlphaEnabled = Preferences::GetBool("layers.componentalpha.enabled", true);
-    sPrefBrowserTabsRemote = Preferences::GetBool("browser.tabs.remote", false);
+    sPrefBrowserTabsRemote = BrowserTabsRemote();
 
 #ifdef XP_WIN
     if (sPrefLayersAccelerationForceEnabled) {
       sLayersSupportsD3D9 = true;
     } else {
       nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
       if (gfxInfo) {
         int32_t status;
--- a/image/src/FrameAnimator.cpp
+++ b/image/src/FrameAnimator.cpp
@@ -83,55 +83,55 @@ FrameAnimator::AdvanceFrame(TimeStamp aT
   uint32_t nextFrameIndex = currentFrameIndex + 1;
   int32_t timeout = 0;
 
   RefreshResult ret;
 
   // If we're done decoding, we know we've got everything we're going to get.
   // If we aren't, we only display fully-downloaded frames; everything else
   // gets delayed.
-  bool needToWait = !mDoneDecoding &&
-                    mFrameBlender.RawGetFrame(nextFrameIndex) &&
-                    !mFrameBlender.RawGetFrame(nextFrameIndex)->ImageComplete();
+  bool canDisplay = mDoneDecoding ||
+                    (mFrameBlender.RawGetFrame(nextFrameIndex) &&
+                     mFrameBlender.RawGetFrame(nextFrameIndex)->ImageComplete());
 
-  if (needToWait) {
+  if (!canDisplay) {
     // Uh oh, the frame we want to show is currently being decoded (partial)
     // Wait until the next refresh driver tick and try again
     return ret;
-  } else {
-    // If we're done decoding the next frame, go ahead and display it now and
-    // reinit with the next frame's delay time.
-    if (mFrameBlender.GetNumFrames() == nextFrameIndex) {
-      // End of an animation loop...
-
-      // If we are not looping forever, initialize the loop counter
-      if (mLoopCounter < 0 && mFrameBlender.GetLoopCount() >= 0) {
-        mLoopCounter = mFrameBlender.GetLoopCount();
-      }
+  }
 
-      // If animation mode is "loop once", or we're at end of loop counter, it's time to stop animating
-      if (mAnimationMode == imgIContainer::kLoopOnceAnimMode || mLoopCounter == 0) {
-        ret.animationFinished = true;
-      }
-
-      nextFrameIndex = 0;
+  // If we're done decoding the next frame, go ahead and display it now and
+  // reinit with the next frame's delay time.
+  if (mFrameBlender.GetNumFrames() == nextFrameIndex) {
+    // End of an animation loop...
 
-      if (mLoopCounter > 0) {
-        mLoopCounter--;
-      }
-
-      // If we're done, exit early.
-      if (ret.animationFinished) {
-        return ret;
-      }
+    // If we are not looping forever, initialize the loop counter
+    if (mLoopCounter < 0 && mFrameBlender.GetLoopCount() >= 0) {
+      mLoopCounter = mFrameBlender.GetLoopCount();
     }
 
-    timeout = mFrameBlender.GetTimeoutForFrame(nextFrameIndex);
+    // If animation mode is "loop once", or we're at end of loop counter, it's time to stop animating
+    if (mAnimationMode == imgIContainer::kLoopOnceAnimMode || mLoopCounter == 0) {
+      ret.animationFinished = true;
+    }
+
+    nextFrameIndex = 0;
+
+    if (mLoopCounter > 0) {
+      mLoopCounter--;
+    }
+
+    // If we're done, exit early.
+    if (ret.animationFinished) {
+      return ret;
+    }
   }
 
+  timeout = mFrameBlender.GetTimeoutForFrame(nextFrameIndex);
+
   // Bad data
   if (timeout < 0) {
     ret.animationFinished = true;
     ret.error = true;
   }
 
   if (nextFrameIndex == 0) {
     ret.dirtyRect = mFirstFrameRefreshArea;
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -643,17 +643,17 @@ struct GCMethods<T *>
         JS::HeapCellPostBarrier(reinterpret_cast<js::gc::Cell **>(vp));
     }
     static void relocate(T **vp) {
         JS::HeapCellRelocate(reinterpret_cast<js::gc::Cell **>(vp));
     }
 #endif
 };
 
-#if defined(JS_DEBUG)
+#ifdef JS_DEBUG
 /* This helper allows us to assert that Rooted<T> is scoped within a request. */
 extern JS_PUBLIC_API(bool)
 IsInRequest(JSContext *cx);
 #endif
 
 } /* namespace js */
 
 namespace JS {
@@ -683,26 +683,30 @@ class MOZ_STACK_CLASS Rooted : public js
     }
 
   public:
     Rooted(JSContext *cx
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : ptr(js::GCMethods<T>::initial())
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+#ifdef JS_DEBUG
         MOZ_ASSERT(js::IsInRequest(cx));
+#endif
         init(js::ContextFriendFields::get(cx));
     }
 
     Rooted(JSContext *cx, T initial
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : ptr(initial)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+#ifdef JS_DEBUG
         MOZ_ASSERT(js::IsInRequest(cx));
+#endif
         init(js::ContextFriendFields::get(cx));
     }
 
     Rooted(js::ContextFriendFields *cx
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : ptr(js::GCMethods<T>::initial())
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
--- a/js/src/assembler/assembler/X86Assembler.h
+++ b/js/src/assembler/assembler/X86Assembler.h
@@ -265,17 +265,17 @@ private:
         OP_RET_Iz                       = 0xC2,
         OP_RET                          = 0xC3,
         OP_GROUP11_EvIb                 = 0xC6,
         OP_GROUP11_EvIz                 = 0xC7,
         OP_INT3                         = 0xCC,
         OP_GROUP2_Ev1                   = 0xD1,
         OP_GROUP2_EvCL                  = 0xD3,
         OP_FPU6                         = 0xDD,
-        OP_FLD32                        = 0xD9,
+        OP_FPU6_F32                     = 0xD9,
         OP_CALL_rel32                   = 0xE8,
         OP_JMP_rel32                    = 0xE9,
         PRE_SSE_F2                      = 0xF2,
         PRE_SSE_F3                      = 0xF3,
         OP_HLT                          = 0xF4,
         OP_GROUP3_EbIb                  = 0xF6,
         OP_GROUP3_Ev                    = 0xF7,
         OP_GROUP3_EvIz                  = 0xF7, // OP_GROUP3_Ev has an immediate, when instruction is a test.
@@ -727,32 +727,32 @@ public:
     void fld_m(int offset, RegisterID base)
     {
         spew("fld        %s0x%x(%s)", PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp(OP_FPU6, FPU6_OP_FLD, base, offset);
     }
     void fld32_m(int offset, RegisterID base)
     {
         spew("fld        %s0x%x(%s)", PRETTY_PRINT_OFFSET(offset), nameIReg(base));
-        m_formatter.oneByteOp(OP_FLD32, FPU6_OP_FLD, base, offset);
+        m_formatter.oneByteOp(OP_FPU6_F32, FPU6_OP_FLD, base, offset);
     }
     void fisttp_m(int offset, RegisterID base)
     {
         spew("fisttp     %s0x%x(%s)", PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp(OP_FPU6, FPU6_OP_FISTTP, base, offset);
     }
     void fstp_m(int offset, RegisterID base)
     {
         spew("fstp       %s0x%x(%s)", PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp(OP_FPU6, FPU6_OP_FSTP, base, offset);
     }
     void fstp32_m(int offset, RegisterID base)
     {
         spew("fstp32       %s0x%x(%s)", PRETTY_PRINT_OFFSET(offset), nameIReg(base));
-        m_formatter.oneByteOp(OP_FLD32, FPU6_OP_FSTP, base, offset);
+        m_formatter.oneByteOp(OP_FPU6_F32, FPU6_OP_FSTP, base, offset);
     }
 
     void negl_r(RegisterID dst)
     {
         spew("negl       %s", nameIReg(4,dst));
         m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_NEG, dst);
     }
 
--- a/js/src/build/autoconf/arch.m4
+++ b/js/src/build/autoconf/arch.m4
@@ -242,16 +242,17 @@ if test "$CPU_ARCH" = "arm"; then
       fi
   fi
 
 fi # CPU_ARCH = arm
 
 AC_SUBST(HAVE_ARM_SIMD)
 AC_SUBST(HAVE_ARM_NEON)
 AC_SUBST(BUILD_ARM_NEON)
+AC_SUBST(ARM_ARCH)
 
 if test -n "$MOZ_ARCH"; then
   NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-arch=$MOZ_ARCH"
   NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-thumb=$MOZ_THUMB"
   NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-thumb-interwork=$MOZ_THUMB_INTERWORK"
   NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-fpu=$MOZ_FPU"
   NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-float-abi=$MOZ_FLOAT_ABI"
   NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-soft-float=$MOZ_SOFT_FLOAT"
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -13,23 +13,16 @@
 
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::types;
 
 using mozilla::ArrayLength;
 
-static inline bool
-DefinePropertyHelper(JSContext *cx, HandleObject obj, Handle<PropertyName*> name, HandleValue v)
-{
-    return !!baseops::DefineProperty(cx, obj, name, v,
-                                     JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE);
-}
-
 bool
 js::CreateRegExpMatchResult(JSContext *cx, HandleString input_, const jschar *chars, size_t length,
                             MatchPairs &matches, MutableHandleValue rval)
 {
     RootedString input(cx, input_);
     RootedValue undefinedValue(cx, UndefinedValue());
 
     /*
@@ -64,32 +57,46 @@ js::CreateRegExpMatchResult(JSContext *c
         } else {
             JSLinearString *str = js_NewDependentString(cx, input, pair.start, pair.length());
             if (!str)
                 return false;
             elements.infallibleAppend(StringValue(str));
         }
     }
 
-    /* Copy the rooted vector into the array object. */
-    RootedObject array(cx, NewDenseCopiedArray(cx, elements.length(), elements.begin()));
-    if (!array)
+    /* Get the templateObject that defines the shape and type of the output object */
+    JSObject *templateObject = cx->compartment()->regExps.getOrCreateMatchResultTemplateObject(cx);
+    if (!templateObject)
         return false;
 
-    /* Set the |index| property. */
+    /* Copy the rooted vector into the array object. */
+    RootedObject arr(cx, NewDenseCopiedArrayWithTemplate(cx, elements.length(), elements.begin(),
+                                                         templateObject));
+
+    /* Set the |index| property. (TemplateObject positions it in slot 0) */
     RootedValue index(cx, Int32Value(matches[0].start));
-    if (!DefinePropertyHelper(cx, array, cx->names().index, index))
-        return false;
+    arr->nativeSetSlot(0, index);
+
+    /* Set the |input| property. (TemplateObject positions it in slot 1) */
+    RootedValue inputVal(cx, StringValue(input));
+    arr->nativeSetSlot(1, inputVal);
 
-    /* Set the |input| property. */
-    RootedValue inputVal(cx, StringValue(input));
-    if (!DefinePropertyHelper(cx, array, cx->names().input, inputVal))
+#ifdef DEBUG
+    RootedValue test(cx);
+    RootedId id(cx, NameToId(cx->names().index));
+    if (!baseops::GetProperty(cx, arr, id, &test))
         return false;
+    JS_ASSERT(test == index);
+    id = NameToId(cx->names().input);
+    if (!baseops::GetProperty(cx, arr, id, &test))
+        return false;
+    JS_ASSERT(test == inputVal);
+#endif
 
-    rval.setObject(*array);
+    rval.setObject(*arr);
     return true;
 }
 
 bool
 js::CreateRegExpMatchResult(JSContext *cx, HandleString string, MatchPairs &matches,
                             MutableHandleValue rval)
 {
     Rooted<JSLinearString*> input(cx, string->ensureLinear(cx));
--- a/js/src/builtin/TypeRepresentation.cpp
+++ b/js/src/builtin/TypeRepresentation.cpp
@@ -378,17 +378,17 @@ TypeRepresentation::addToTableOrFree(JSC
     {
         return nullptr;
     }
     RootedTypeObject typeObject(cx, comp->types.newTypeObject(cx, clasp, proto));
     if (!typeObject)
         return nullptr;
 
     // Next, attempt to add the type representation to the table.
-    if (!comp->typeReprs.add(p, this)) {
+    if (!comp->typeReprs.relookupOrAdd(p, this, this)) {
         js_ReportOutOfMemory(cx);
         js_free(this); // do not finalize, not present in the table
         return nullptr;
     }
 
     // Now that the object is in the table, try to make the owner
     // object. If this succeeds, then the owner will remove from the
     // table once it is finalized. Otherwise, if this fails, we must
--- a/js/src/config/config.mk
+++ b/js/src/config/config.mk
@@ -552,50 +552,51 @@ endif # FAIL_ON_WARNINGS
 
 ifeq ($(OS_ARCH)_$(GNU_CC),WINNT_)
 #// Currently, unless USE_STATIC_LIBS is defined, the multithreaded
 #// DLL version of the RTL is used...
 #//
 #//------------------------------------------------------------------------
 ifdef USE_STATIC_LIBS
 RTL_FLAGS=-MT          # Statically linked multithreaded RTL
-ifneq (,$(MOZ_DEBUG)$(NS_TRACE_MALLOC)$(MOZ_DMD))
+ifneq (,$(MOZ_DEBUG)$(NS_TRACE_MALLOC))
 ifndef MOZ_NO_DEBUG_RTL
 RTL_FLAGS=-MTd         # Statically linked multithreaded MSVC4.0 debug RTL
 endif
-endif # MOZ_DEBUG || NS_TRACE_MALLOC || MOZ_DMD
+endif # MOZ_DEBUG || NS_TRACE_MALLOC
 
 else # !USE_STATIC_LIBS
 
 RTL_FLAGS=-MD          # Dynamically linked, multithreaded RTL
-ifneq (,$(MOZ_DEBUG)$(NS_TRACE_MALLOC)$(MOZ_DMD))
+ifneq (,$(MOZ_DEBUG)$(NS_TRACE_MALLOC))
 ifndef MOZ_NO_DEBUG_RTL
 RTL_FLAGS=-MDd         # Dynamically linked, multithreaded MSVC4.0 debug RTL
 endif
-endif # MOZ_DEBUG || NS_TRACE_MALLOC || MOZ_DMD
+endif # MOZ_DEBUG || NS_TRACE_MALLOC
 endif # USE_STATIC_LIBS
 endif # WINNT && !GNU_CC
 
 ifeq ($(OS_ARCH),Darwin)
 # Compiling ObjC requires an Apple compiler anyway, so it's ok to set
 # host CMFLAGS here.
 HOST_CMFLAGS += -fobjc-exceptions
 HOST_CMMFLAGS += -fobjc-exceptions
 OS_COMPILE_CMFLAGS += -fobjc-exceptions
 OS_COMPILE_CMMFLAGS += -fobjc-exceptions
 ifeq ($(MOZ_WIDGET_TOOLKIT),uikit)
 OS_COMPILE_CMFLAGS += -fobjc-abi-version=2 -fobjc-legacy-dispatch
 OS_COMPILE_CMMFLAGS += -fobjc-abi-version=2 -fobjc-legacy-dispatch
 endif
 endif
 
-COMPILE_CFLAGS	= $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_CPPFLAGS) $(OS_COMPILE_CFLAGS) $(CFLAGS)
-COMPILE_CXXFLAGS = $(STL_FLAGS) $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_CPPFLAGS) $(OS_COMPILE_CXXFLAGS) $(CXXFLAGS)
-COMPILE_CMFLAGS = $(OS_COMPILE_CMFLAGS)
-COMPILE_CMMFLAGS = $(OS_COMPILE_CMMFLAGS)
+COMPILE_CFLAGS	= $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_CPPFLAGS) $(OS_COMPILE_CFLAGS) $(CFLAGS) $(EXTRA_COMPILE_FLAGS)
+COMPILE_CXXFLAGS = $(STL_FLAGS) $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_CPPFLAGS) $(OS_COMPILE_CXXFLAGS) $(CXXFLAGS) $(EXTRA_COMPILE_FLAGS)
+COMPILE_CMFLAGS = $(OS_COMPILE_CMFLAGS) $(EXTRA_COMPILE_FLAGS)
+COMPILE_CMMFLAGS = $(OS_COMPILE_CMMFLAGS) $(EXTRA_COMPILE_FLAGS)
+ASFLAGS += $(EXTRA_ASSEMBLER_FLAGS)
 
 ifndef CROSS_COMPILE
 HOST_CFLAGS += $(RTL_FLAGS)
 endif
 
 #
 # Name of the binary code directories
 #
@@ -879,8 +880,26 @@ PLY_INCLUDE = -I$(topsrcdir)/other-licen
 
 export CL_INCLUDES_PREFIX
 
 ifdef MOZ_GTK2_CFLAGS
 MOZ_GTK2_CFLAGS := -I$(topsrcdir)/widget/gtk/compat $(MOZ_GTK2_CFLAGS)
 endif
 
 DEFINES += -DNO_NSPR_10_SUPPORT
+
+ifdef IS_GYP_DIR
+LOCAL_INCLUDES += \
+  -I$(topsrcdir)/ipc/chromium/src \
+  -I$(topsrcdir)/ipc/glue \
+  -I$(DEPTH)/ipc/ipdl/_ipdlheaders \
+  $(NULL)
+
+ifeq (WINNT,$(OS_TARGET))
+# These get set via VC project file settings for normal GYP builds.
+DEFINES += -DUNICODE -D_UNICODE
+LOCAL_INCLUDES += -I'$(MOZ_DIRECTX_SDK_PATH)/include'
+endif
+
+STL_FLAGS=
+# Skip most Mozilla-specific include locations.
+INCLUDES = -I. $(LOCAL_INCLUDES) -I$(DEPTH)/dist/include
+endif
--- a/js/src/jit-test/lib/asm.js
+++ b/js/src/jit-test/lib/asm.js
@@ -52,17 +52,17 @@ function assertAsmDirectiveFail(str)
     assertEq(oldOpts.indexOf("werror"), -1);
 
     // Verify an error is thrown
     var caught = false;
     try {
         eval(str);
     } catch (e) {
         if ((''+e).indexOf(ASM_DIRECTIVE_FAIL_STRING) == -1)
-            throw new Error("Didn't catch the expected directive failure error; instead caught: " + e);
+            throw new Error("Didn't catch the expected directive failure error; instead caught: " + e + "\nStack: " + new Error().stack);
         caught = true;
     }
     if (!caught)
         throw new Error("Didn't catch the directive failure error");
 
     // Turn warnings-as-errors back off
     options("werror");
 }
@@ -80,17 +80,17 @@ function assertAsmTypeFail()
     assertEq(oldOpts.indexOf("werror"), -1);
 
     // Verify an error is thrown
     var caught = false;
     try {
         Function.apply(null, arguments);
     } catch (e) {
         if ((''+e).indexOf(ASM_TYPE_FAIL_STRING) == -1)
-            throw new Error("Didn't catch the expected type failure error; instead caught: " + e);
+            throw new Error("Didn't catch the expected type failure error; instead caught: " + e + "\nStack: " + new Error().stack);
         caught = true;
     }
     if (!caught)
         throw new Error("Didn't catch the type failure error");
 
     // Turn warnings-as-errors back off
     options("werror");
 }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/asm.js/testFloat32.js
@@ -0,0 +1,301 @@
+load(libdir + "asm.js");
+const TO_FLOAT32 = "var toF = glob.Math.fround;";
+const HEAP32 = "var f32 = new glob.Float32Array(heap);";
+var heap = new ArrayBuffer(4096);
+
+// Module linking
+assertAsmLinkAlwaysFail(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() {} return f"), null);
+assertAsmLinkAlwaysFail(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() {} return f"), {fround: Math.fround});
+assertAsmLinkFail(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() {} return f"), {Math: {fround: Math.imul}});
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() {} return f"), {Math:{fround: Math.fround}})(), undefined);
+
+// Argument coercions
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = unknown(x); } return f");
+assertAsmTypeFail('glob', USE_ASM + "function f(i) { i = toF(i); } return f");
+assertAsmTypeFail('glob', USE_ASM + "var cos = glob.Math.cos; function f(x) { x = cos(x); } return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = toF(); } return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = toF(x, x); } return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = toF('hi'); } return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = toF(loat); } return f");
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f(i) { i = toF(i); } return f"), this)(), undefined);
+
+// Local variables declarations
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { var i = unknown(); } return f");
+assertAsmTypeFail('glob', USE_ASM + "var cos = glob.Math.cos; function f() { var i = cos(); } return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { var i = toF(); } return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { var i = toF(x, x); } return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { var i = toF('hi'); } return f");
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { var i = toF(5); } return f"), this)(), undefined);
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { var i = toF(5.); } return f"), this)(), undefined);
+
+// Return values
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(4, 4); } return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(); } return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF({}); } return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(x); } return f");
+
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(42); } return f"), this)(), 42);
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(0.); } return f"), this)(), 0);
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(-0.); } return f"), this)(), -0);
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var inf = glob.Infinity; function f() { return toF(inf); } return f"), this)(), Infinity);
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(13.37); } return f"), this)(), Math.fround(13.37));
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return +toF(4.); } return f"), this)(), 4);
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return +~~toF(4.5); } return f"), this)(), 4);
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(4.5) | 0; } return f"), this)(), 4);
+
+// Assign values
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { var i = toF(5.); i = 5; return toF(i); } return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { var i = toF(5.); i = 6.; return toF(i); } return f");
+
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { var i = toF(5.); return toF(i); } return f"), this)(), 5);
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { var i = toF(5.); i = toF(42); return toF(i); } return f"), this)(), 42);
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { var i = toF(5.); i = toF(6.); return toF(i); } return f"), this)(), 6);
+
+assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { var i = toF(5.); f32[0] = toF(6.); i = f32[0]; return toF(i); } return f");
+assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { var i = toF(5.); f32[0] = toF(6.); i = toF(f32[0]); return toF(i); } return f"), this, null, heap)(), 6);
+
+// Special array assignments (the other ones are tested in testHeapAccess)
+assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { var i = 5.; f32[0] = i; return toF(f32[0]); } return f"), this, null, heap)(), 5);
+assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "var f64 = new glob.Float64Array(heap); function f() { var i = toF(5.); f64[0] = i; return +f64[0]; } return f"), this, null, heap)(), 5);
+
+var HEAP64 = "var f64 = new glob.Float64Array(heap);"
+assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 1.5; return +f32[0]; } return f"), this, null, heap)(), 1.5);
+assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + HEAP64 + "function f() { f64[0] = 1.5; return toF(f64[0]); } return f"), this, null, heap)(), 1.5);
+
+// Coercions
+// -> from Float32
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { var i = toF(5.); var n = 0; n = i | 0; return n | 0; } return f");
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = toF(x); var n = 0.; n = +x; return +n; } return f"), this)(16.64), Math.fround(16.64));
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = toF(x); var n = 0; n = ~~x; return n | 0; } return f"), this)(16.64), 16);
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = toF(x); var n = 0; n = ~~x >>> 0; return n | 0; } return f"), this)(16.64), 16);
+
+// -> from float?
+function makeCoercion(coercionFunc) {
+    return USE_ASM + HEAP32 + TO_FLOAT32 + "function f(x) { x = toF(x); f32[0] = x; return " + coercionFunc('f32[0]') + " } return f";
+}
+assertAsmTypeFail('glob', 'ffi', 'heap', makeCoercion(x => x + '|0'));
+assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', makeCoercion(x => '+' + x)), this, null, heap)(16.64), Math.fround(16.64));
+assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', makeCoercion(x => 'toF(' + x + ')')), this, null, heap)(16.64), Math.fround(16.64));
+assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', makeCoercion(x => '~~+' + x + '|0')), this, null, heap)(16.64), 16);
+assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', makeCoercion(x => '~~toF(' + x + ')|0')), this, null, heap)(16.64), 16);
+
+// -> to Float32
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = x|0; return toF(~~x); } return f"), this)(23), 23);
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = x|0; return toF(x >> 0); } return f"), this)(23), 23);
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = +x; return toF(x); } return f"), this)(13.37), Math.fround(13.37));
+
+UINT32_MAX = Math.pow(2, 32)-1;
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = x|0; return toF(x >>> 0); } return f"), this)(-1), Math.fround(UINT32_MAX));
+
+// Global variables imports
+assertAsmTypeFail('glob', USE_ASM + "var x = toF(); function f() {} return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var x = some(3); function f() {} return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var x = toF(); function f() {} return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var x = toF(3, 4); function f() {} return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var x = toF({x: 3}); function f() {} return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var x = toF(true); function f() {} return f");
+assertAsmTypeFail('glob', USE_ASM + "var x = toF(3);" + TO_FLOAT32 + "function f() {} return f");
+
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var x = toF(3.5); function f() {} return f"), this)(), undefined);
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var x = toF(3); function f() {} return f"), this)(), undefined);
+assertEq(asmLink(asmCompile('glob', 'ffi', USE_ASM + TO_FLOAT32 + "var x = toF(ffi.x); function f() {} return f"), this, {x:3})(), undefined);
+assertEq(asmLink(asmCompile('glob', 'ffi', USE_ASM + TO_FLOAT32 + "var x = toF(ffi.x); function f() {} return f"), this, {x:3.5})(), undefined);
+
+// Global variables uses
+values = [2.01, 13.37, -3.141592653]
+specials = [NaN, Infinity]
+
+for (v of values) {
+    assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var x = toF(" + v + "); function f() {return toF(x);} return f"), this)(), Math.fround(v));
+    assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var x = toF(0.); function f() {x = toF(" + v + "); return toF(x);} return f"), this)(), Math.fround(v));
+    assertEq(asmLink(asmCompile('glob', 'ffi', USE_ASM + TO_FLOAT32 + "var x = toF(ffi.x); function f() {return toF(x);} return f"), this, {x:v})(), Math.fround(v));
+}
+
+for (v of specials) {
+    assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var special = glob." + v + "; var g=toF(0.); function f() {g=toF(special); return toF(g);} return f"), this)(), Math.fround(v));
+}
+
+// Math builtins
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var imul = glob.Math.imul; function f() {var x = toF(1.5), y = toF(2.4); return imul(x, y) | 0;} return f");
+
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var abs = glob.Math.abs; function f() {var x = toF(1.5); return +abs(x);} return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var abs = glob.Math.abs; function f() {var x = toF(1.5); return abs(x) | 0;} return f");
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var abs = glob.Math.abs; function f() {var x = toF(1.5); return toF(abs(x))} return f"), this)(), Math.fround(1.5));
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var abs = glob.Math.abs; function f() {var x = toF(-1.5); return toF(abs(x))} return f"), this)(), Math.fround(1.5));
+
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var sqrt = glob.Math.sqrt; function f() {var x = toF(1.5); return +sqrt(x);} return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var sqrt = glob.Math.sqrt; function f() {var x = toF(1.5); return sqrt(x) | 0;} return f");
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var sqrt = glob.Math.sqrt; function f() {var x = toF(2.25); return toF(sqrt(x))} return f"), this)(), Math.fround(1.5));
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var sqrt = glob.Math.sqrt; function f() {var x = toF(-1.); return toF(sqrt(x))} return f"), this)(), NaN);
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var sqrt = glob.Math.sqrt; var inf = glob.Infinity; function f() {var x = toF(0.); x = toF(inf); return toF(sqrt(x))} return f"), this)(), Infinity);
+
+// float?s
+assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + HEAP32 + TO_FLOAT32 + "var sqrt = glob.Math.sqrt; function f(x) { x = toF(x); f32[0] = x; return toF(sqrt(f32[0])) } return f"), this, null, heap)(64), Math.fround(8));
+assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + HEAP32 + TO_FLOAT32 + "var cos = glob.Math.cos; function f(x) { x = toF(x); f32[0] = x; return toF(cos(f32[0])) } return f"), this, null, heap)(3.141592653), -1);
+
+// All Math functions with arity 1 behave like cos and sin
+var cosModule = asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var g = glob.Math.cos; function f(x) {x = toF(x); return toF(g(x))} return f"), this);
+for (v of [0, 3.141592653, Math.Infinity, NaN])
+    assertEq(cosModule(v), Math.fround(Math.cos(Math.fround(v))));
+
+var sinModule = asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var g = glob.Math.sin; function f(x) {x = toF(x); return toF(g(x))} return f"), this);
+for (v of [0, 3.141592653, Math.Infinity, NaN])
+    assertEq(sinModule(v), Math.fround(Math.sin(Math.fround(v))));
+
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var g = glob.Math.cos; function f(x) {x = toF(x); return +(g(x))} return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var g = glob.Math.cos; function f(x) {x = +x; return toF(g(x))} return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var g = glob.Math.cos; function f(x) {x = x|0; return toF(g(x))} return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var g = glob.Math.cos; function f(x) {x = toF(x); return g(x) | 0} return f");
+
+// Math functions with arity of two are not specialized for floats, so we shouldn't feed them with floats arguments or
+// return type
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var g = glob.Math.pow; function f(x) {x = toF(x); return toF(g(x, 2.))} return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var g = glob.Math.pow; function f(x) {x = toF(x); return +g(x, 2.)} return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var g = glob.Math.pow; function f(x) {x = toF(x); return toF(g(+x, 2.))} return f");
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var g = glob.Math.pow; function f(x) {x = toF(x); return +g(+x, 2.)} return f"), this)(3), 9);
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var g = glob.Math.pow; function f(x) {x = toF(x); return toF(+g(+x, 2.))} return f"), this)(3), 9);
+
+// Other function calls
+// -> Signature comparisons
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function g(x){x=toF(x); return toF(x);} function f() {var x=toF(4.); var y=toF(0.); var z = 0.; y=toF(g(x)); z = +g(x); return toF(z); } return f");
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var sqrt=glob.Math.sqrt; function g(x){x=toF(x); return toF(sqrt(x));} function f() {var x=toF(4.); var y=toF(0.); var z = 0.; y = toF(g(x)); z = +toF(g(x)); return toF(z); } return f"), this)(), 2);
+
+// -> FFI
+var FFI = "var ffi = env.ffi;";
+assertAsmTypeFail('glob', 'env', USE_ASM + TO_FLOAT32 + FFI + "function f() {var x = toF(3.14); return +ffi(x);} return f");
+assertAsmTypeFail('glob', 'env', USE_ASM + TO_FLOAT32 + FFI + "function f() {var x = toF(3.14); return toF(ffi(+x));} return f");
+
+var env = {ffi: function(x) { return x+1; }}; // use registers
+assertEq(asmLink(asmCompile('glob', 'env', USE_ASM + TO_FLOAT32 + FFI + "function f(x) {x = toF(x); return toF(+ffi(+x));} return f"), this, env)(5), Math.fround(6));
+env = {ffi: function(a,b,c,d,e,f,g,h,i) { return a+b+c+d+e+f+g+h+i; }}; // use stack arguments (> 8 arguments on linux x64)
+assertEq(asmLink(asmCompile('glob', 'env', USE_ASM + TO_FLOAT32 + FFI + "function f(x) {x = toF(x); return toF(+ffi(+x, 1., 2., 3., 4., 5., -5., -4., 1.));} return f"), this, env)(5), Math.fround(12));
+
+// -> Internal calls
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function g(x){x=toF(x);return toF(+x + 1.);} function f(x) {x = +x; return toF(g(x));} return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function g(x){x=toF(x);return toF(+x + 1.);} function f(x) {x = x|0; return toF(g(x));} return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function g(x){x=toF(x);return toF(+x + 1.);} function f(x) {x = x|0; return toF(g(x));} return f");
+
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function g(x){x=toF(x);return toF(+x + 1.);} function f(x) {x = toF(x); return toF(g(x));} return f"), this, env)(5), Math.fround(6));
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function g(x,y){x=toF(x);y=toF(y);return toF(+x + +y);} function f(x) {x = toF(x); return toF(g(x, toF(1.)));} return f"), this, env)(5), Math.fround(6));
+
+// --> internal calls with unused return values
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "var g = 4; function s(x) { x = +x; g = (g + ~~x)|0; return g|0;} function f(x) { x = +x; toF(s(x)); return g|0} return f");
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var g = 4; function s(x) { x = +x; g = (g + ~~x)|0; return toF(g|0);} function f(x) { x = +x; toF(s(x)); return g|0} return f"), this)(3), 7);
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var g = 4; function s(x) { x = toF(x); g = (g + ~~x)|0; return toF(g|0);} function f(x) { x = toF(x); return (toF(s(x)), g)|0} return f"), this)(3), 7);
+
+// --> coerced calls
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var g = 4; function s(x) { x = toF(x); g = (g + ~~x)|0; return +(g|0);} function f(x) { x = toF(x); return toF(+s(x))} return f"), this)(3), 7);
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var g = 4; function s(x) { x = toF(x); g = (g + ~~x)|0; return g|0;} function f(x) { x = toF(x); return toF(s(x)|0)} return f"), this)(3), 7);
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "var g = 4; function s(x) { x = toF(x); g = (g + ~~x)|0; return toF(g|0);} function f(x) { x = toF(x); return toF(toF(s(x)))} return f"), this)(3), 7);
+
+// --> test pressure on registers in internal calls when there are |numArgs| arguments
+for (numArgs of [5, 9, 17, 33, 65, 129]) {
+    let code = (function(n) {
+        let args = "", coercions = "", sum = "", call="x";
+        for (let i = 0; i < n; i++) {
+            let name = 'a' + i;
+            args += name + ((i == n-1)?'':',');
+            coercions += name + '=toF(' + name + ');';
+            sum += ((i>0)?'+':'') + ' +' + name;
+            call += (i==0)?'':',toF(' + i + '.)'
+        }
+        return "function g(" + args + "){" + coercions + "return toF(" + sum + ");}"
+               +"function f(x) { x = toF(x); return toF(g(" + call + "))}";
+    })(numArgs);
+    assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + code + "return f"), this, env)(5), Math.fround(5 + numArgs * (numArgs-1) / 2));
+}
+
+// -> Pointer calls
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function a(x){x=toF(x);return toF(+x + .5);} function b(x){x=toF(x);return toF(+x - .5);} function f(x, n) {x=+x;n=n|0;return toF(t[n&1](x));} var t=[a,b]; return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function a(x){x=toF(x);return toF(+x + .5);} function b(x){x=toF(x);return toF(+x - .5);} function f(x, n) {x=x|0;n=n|0;return toF(t[n&1](x));} var t=[a,b]; return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function a(x){x=toF(x);return toF(+x + .5);} function b(x){x=toF(x);return toF(+x - .5);} function f(x, n) {x=toF(x);n=n|0;return t[n&1](x)|0;} var t=[a,b]; return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function a(x){x=toF(x);return toF(+x + .5);} function b(x){x=toF(x);return toF(+x - .5);} function f(x, n) {x=toF(x);n=n|0;return +t[n&1](x);} var t=[a,b]; return f");
+
+code = asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function a(x){x=toF(x);return toF(+x + .5);} function b(x){x=toF(x);return toF(+x - .5);}"
+               + "function f(x, n) {x=toF(x);n=n|0;return toF(t[n&1](x));} var t=[a,b]; return f"), this);
+assertEq(code(0, 0), .5);
+assertEq(code(0, 1), -.5);
+assertEq(code(13.37, 0), Math.fround(13.37 + .5));
+assertEq(code(13.37, 1), Math.fround(13.37 - .5));
+
+// Arithmetic operations
+// -> mul
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(3 * toF(4.));} return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(3. * toF(4.)); } return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return +(toF(3.) * toF(4.));} return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(3) * toF(4) * toF(5));} return f");
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(3.) * toF(4.)); } return f"), this)(), 12);
+
+assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return toF(3 * f32[0]);} return f");
+assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return toF(3. * f32[0]);} return f");
+assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return +(toF(3.) * f32[0]);} return f");
+assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return toF(toF(3.) * f32[0]);} return f"), this, null, heap)(), 12);
+
+var mul = asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x=toF(x); return toF(x * toF(4.));} return f"), this);
+assertEq(mul(Infinity), Infinity);
+assertEq(mul(NaN), NaN);
+assertEq(mul(0), 0);
+assertEq(mul(1), 4);
+assertEq(mul(0.33), Math.fround(0.33 * 4));
+
+// -> add
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(3 + toF(4.));} return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(3.5 + toF(4.));} return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return +(toF(3.5) + toF(4.));} return f");
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(3.5) + toF(4.)); } return f"), this)(), 7.5);
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(3.5) + toF(4.) + toF(4.5));} return f");
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(toF(3.5) + toF(4.)) + toF(4.5)); } return f"), this)(), 12);
+assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return toF(toF(3) + f32[0]);} return f"), this, null, heap)(), 7);
+
+// --> no additions with float? or floatish
+assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return toF(3 + f32[0]);} return f");
+assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return toF(3. + f32[0]);} return f");
+
+// -> sub
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(3 - toF(4.));} return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(3.5 - toF(4.));} return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return +(toF(3.5) - toF(4.));} return f");
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(3.5) - toF(4.)); } return f"), this)(), -.5);
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(3.5) - toF(4.) - toF(4.5));} return f");
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(toF(3.5) - toF(4.)) - toF(4.5)); } return f"), this)(), -5);
+
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(3.5) + toF(4.) - toF(4.5));} return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(3.5) - toF(4.) + toF(4.5));} return f");
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(toF(3.5) + toF(4.)) - toF(4.5)); } return f"), this)(), 3);
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(toF(3.5) - toF(4.)) + toF(4.5)); } return f"), this)(), 4);
+
+assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return toF(3 - f32[0]);} return f");
+assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return toF(3. - f32[0]);} return f");
+assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return +(toF(3) - f32[0]);} return f");
+assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return toF(toF(3) - f32[0]);} return f"), this, null, heap)(), -1);
+
+// -> div
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(3 / toF(4.));} return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(3.5 / toF(4.));} return f");
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return +(toF(3.5) / toF(4.));} return f");
+assertEq(asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(12.) / toF(4.)); } return f"), this)(), 3);
+
+assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return toF(3 / f32[0]);} return f");
+assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return toF(3. / f32[0]);} return f");
+assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 4.; return +(toF(3) / f32[0]);} return f");
+assertEq(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { f32[0] = 2.; return toF(toF(4) / f32[0]);} return f"), this, null, heap)(), 2);
+
+// -> mod
+assertAsmTypeFail('glob', USE_ASM + TO_FLOAT32 + "function f() { return toF(toF(3.5) % toF(4.));} return f");
+assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { return toF(f32[0] % toF(4.));} return f");
+assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { return toF(toF(3.5) % f32[0]);} return f");
+assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { return toF(f32[1] % f32[0]);} return f");
+
+// Comparisons
+for (op of ['==', '!=', '<', '>', '<=', '>=']) {
+    let code = asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = toF(x); if( x " + op + " toF(3.) ) return 1; else return 0; return -1; } return f"), this);
+    let ternary = asmLink(asmCompile('glob', USE_ASM + TO_FLOAT32 + "function f(x) { x = toF(x); return ((x " + op + " toF(3.)) ? 1 : 0)|0 } return f"), this);
+    for (v of [-5, 0, 2.5, 3, 13.37, NaN, Infinity]) {
+        let expected = eval("("+ v + " " + op + " 3)|0");
+        assertEq(code(v) | 0, expected);
+        assertEq(ternary(v) | 0, expected);
+    }
+
+    assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { if( f32[0] " + op + " toF(3.) ) return 1; else return 0; return -1; } return f");
+    assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + TO_FLOAT32 + HEAP32 + "function f() { if( (toF(1.) + toF(2.)) " + op + " toF(3.) ) return 1; else return 0; return -1; } return f");
+}
--- a/js/src/jit-test/tests/asm.js/testHeapAccess.js
+++ b/js/src/jit-test/tests/asm.js/testHeapAccess.js
@@ -149,16 +149,17 @@ asmLink(asmCompile('glob', 'imp', 'b', U
 assertEq(new Int32Array(BUF_64KB)[1], -0xabcde);
 asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f32[(4&0xffff)>>2] = 1.0 } return f'), this, null, BUF_64KB)();
 assertEq(new Float32Array(BUF_64KB)[1], 1.0);
 asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f64[(8&0xffff)>>3] = 1.3 } return f'), this, null, BUF_64KB)();
 assertEq(new Float64Array(BUF_64KB)[1], 1.3);
 
 new Float32Array(BUF_64KB)[1] = 1.0;
 assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f32[(4&0xffff)>>2] } return f'), this, null, BUF_64KB)(), 1.0);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'var toFloat32 = glob.Math.fround; function f() { return toFloat32(f32[(4&0xffff)>>2]) } return f'), this, null, BUF_64KB)(), 1.0);
 new Float64Array(BUF_64KB)[1] = 1.3;
 assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[(8&0xffff)>>3] } return f'), this, null, BUF_64KB)(), 1.3);
 
 asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) { i=i|0; u8[255]; u8[i] } return f');
 asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) { i=i|0; u8[i&0xff]; u8[255] } return f');
 asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) { i=i|0; u32[63]; u32[i>>2] } return f');
 asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) { i=i|0; u32[i>>2]; u32[63] } return f');
 
--- a/js/src/jit-test/tests/asm.js/testZOOB.js
+++ b/js/src/jit-test/tests/asm.js/testZOOB.js
@@ -75,31 +75,31 @@ function testInt(ctor, shift, scale, dis
             var v = arr[index]|0;
             arr[index] = 0;
             f(i+j, v);
             assertEq(arr[index]|0, v);
         }
     }
 }
 
-function testFloat(ctor, shift, scale, disp) {
+function testFloat(ctor, shift, scale, disp, coercion) {
     var ab = new ArrayBuffer(4096);
     var arr = new ctor(ab);
     for (var i = 0; i < arr.length; i++)
         arr[i] = i;
-    var f = asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.' + ctor.name + '(b); function f(i) {i=i|0; return +arr[((i<<' + scale + ')+' + disp + ')>>' + shift + '] } return f'), this, null, ab);
+    var f = asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.' + ctor.name + '(b); var toF = glob.Math.fround; function f(i) {i=i|0; return ' + coercion + '(arr[((i<<' + scale + ')+' + disp + ')>>' + shift + ']) } return f'), this, null, ab);
     for (var i of [0,1,2,3,4,1023,1024,1025,4095,4096,4097])
         assertEq(f(i), +arr[((i<<scale)+disp)>>shift]);
 
     for (var i of [-Math.pow(2,31), Math.pow(2,31)-1, Math.pow(2,32)]) {
         for (var j of [-8,-4,-1,0,1,4,8])
             assertEq(f(i+j), +arr[(((i+j)<<scale)+disp)>>shift]);
     }
 
-    var f = asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.' + ctor.name + '(b); function f(i,j) {i=i|0;j=+j; arr[((i<<' + scale + ')+' + disp + ')>>' + shift + '] = j } return f'), this, null, ab);
+    var f = asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.' + ctor.name + '(b); var toF = glob.Math.fround; function f(i,j) {i=i|0;j=+j; arr[((i<<' + scale + ')+' + disp + ')>>' + shift + '] = j } return f'), this, null, ab);
     for (var i of [0,1,2,3,4,1023,1024,1025,4095,4096,4097]) {
         var index = ((i<<scale)+disp)>>shift;
         var v = +arr[index];
         arr[index] = 0;
         f(i, v);
         assertEq(+arr[index], v);
     }
 
@@ -109,23 +109,30 @@ function testFloat(ctor, shift, scale, d
             var v = +arr[index];
             arr[index] = 0;
             f(i+j, v);
             assertEq(+arr[index], v);
         }
     }
 }
 
+function testFloat32(ctor, shift, scale, disp) {
+    testFloat(ctor, shift, scale, disp, "toF");
+}
+function testFloat64(ctor, shift, scale, disp) {
+    testFloat(ctor, shift, scale, disp, "+");
+}
+
 function test(tester, ctor, shift) {
     for (scale of [0,1,2,3]) {
         for (disp of [0,1,8,Math.pow(2,31)-1,Math.pow(2,31),Math.pow(2,32)-1])
             tester(ctor, shift, scale, disp);
     }
 }
 
 test(testInt, Int8Array, 0);
 test(testInt, Uint8Array, 0);
 test(testInt, Int16Array, 1);
 test(testInt, Uint16Array, 1);
 test(testInt, Int32Array, 2);
 test(testInt, Uint32Array, 2);
-test(testFloat, Float32Array, 2);
-test(testFloat, Float64Array, 3);
+test(testFloat32, Float32Array, 2);
+test(testFloat64, Float64Array, 3);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-948423.js
@@ -0,0 +1,20 @@
+var ArrayType = TypedObject.ArrayType;
+var StructType = TypedObject.StructType;
+var uint8 = TypedObject.uint8;
+var uint32 = TypedObject.uint32;
+var ObjectType = TypedObject.Object;
+function runTests() {
+  (function DimensionLinkedToUndimension() {
+    var UintsA = uint32.array();
+    var FiveUintsA = UintsA.dimension(5);
+    var FiveUintsB = uint32.array(5);
+    assertEq(true, 
+	FiveUintsA.equivalent(FiveUintsB)
+	);
+  })();
+  (function PrototypeHierarchy() {
+    schedulegc(3);
+    var Uint8s = uint8.array();
+  })();
+}
+runTests();
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -363,17 +363,20 @@ ParseVarOrConstStatement(AsmJSParser &pa
 namespace {
 
 // Respresents the type of a general asm.js expression.
 class Type
 {
   public:
     enum Which {
         Double,
-        Doublish,
+        MaybeDouble,
+        Float,
+        MaybeFloat,
+        Floatish,
         Fixnum,
         Int,
         Signed,
         Unsigned,
         Intish,
         Void
     };
 
@@ -402,108 +405,131 @@ class Type
     bool isIntish() const {
         return isInt() || which_ == Intish;
     }
 
     bool isDouble() const {
         return which_ == Double;
     }
 
-    bool isDoublish() const {
-        return isDouble() || which_ == Doublish;
+    bool isMaybeDouble() const {
+        return isDouble() || which_ == MaybeDouble;
+    }
+
+    bool isFloat() const {
+        return which_ == Float;
+    }
+
+    bool isMaybeFloat() const {
+        return isFloat() || which_ == MaybeFloat;
+    }
+
+    bool isFloatish() const {
+        return isMaybeFloat() || which_ == Floatish;
     }
 
     bool isVoid() const {
         return which_ == Void;
     }
 
     bool isExtern() const {
         return isDouble() || isSigned();
     }
 
     bool isVarType() const {
-        return isInt() || isDouble();
+        return isInt() || isDouble() || isFloat();
     }
 
     MIRType toMIRType() const {
         switch (which_) {
           case Double:
-          case Doublish:
+          case MaybeDouble:
             return MIRType_Double;
+          case Float:
+          case Floatish:
+          case MaybeFloat:
+            return MIRType_Float32;
           case Fixnum:
           case Int:
           case Signed:
           case Unsigned:
           case Intish:
             return MIRType_Int32;
           case Void:
             return MIRType_None;
         }
         MOZ_ASSUME_UNREACHABLE("Invalid Type");
     }
 
     const char *toChars() const {
         switch (which_) {
-          case Double:    return "double";
-          case Doublish:  return "doublish";
-          case Fixnum:    return "fixnum";
-          case Int:       return "int";
-          case Signed:    return "signed";
-          case Unsigned:  return "unsigned";
-          case Intish:    return "intish";
-          case Void:      return "void";
+          case Double:      return "double";
+          case MaybeDouble: return "double?";
+          case Float:       return "float";
+          case Floatish:    return "floatish";
+          case MaybeFloat:  return "float?";
+          case Fixnum:      return "fixnum";
+          case Int:         return "int";
+          case Signed:      return "signed";
+          case Unsigned:    return "unsigned";
+          case Intish:      return "intish";
+          case Void:        return "void";
         }
         MOZ_ASSUME_UNREACHABLE("Invalid Type");
     }
 };
 
 } /* anonymous namespace */
 
 // Represents the subset of Type that can be used as the return type of a
 // function.
 class RetType
 {
   public:
     enum Which {
         Void = Type::Void,
         Signed = Type::Signed,
-        Double = Type::Double
+        Double = Type::Double,
+        Float = Type::Float
     };
 
   private:
     Which which_;
 
   public:
     RetType() {}
     RetType(Which w) : which_(w) {}
     RetType(AsmJSCoercion coercion) {
         switch (coercion) {
           case AsmJS_ToInt32: which_ = Signed; break;
           case AsmJS_ToNumber: which_ = Double; break;
+          case AsmJS_FRound: which_ = Float; break;
         }
     }
     Which which() const {
         return which_;
     }
     Type toType() const {
         return Type::Which(which_);
     }
     AsmJSModule::ReturnType toModuleReturnType() const {
         switch (which_) {
           case Void: return AsmJSModule::Return_Void;
           case Signed: return AsmJSModule::Return_Int32;
+          case Float: // will be converted to a Double
           case Double: return AsmJSModule::Return_Double;
         }
         MOZ_ASSUME_UNREACHABLE("Unexpected return type");
     }
     MIRType toMIRType() const {
         switch (which_) {
           case Void: return MIRType_None;
           case Signed: return MIRType_Int32;
           case Double: return MIRType_Double;
+          case Float: return MIRType_Float32;
         }
         MOZ_ASSUME_UNREACHABLE("Unexpected return type");
     }
     bool operator==(RetType rhs) const { return which_ == rhs.which_; }
     bool operator!=(RetType rhs) const { return which_ != rhs.which_; }
 };
 
 namespace {
@@ -529,66 +555,91 @@ namespace {
 // it. For (2), the AsmJSCoercion is also Signed but, when translated to an
 // RetType, the result is Signed since callers (asm.js and non-asm.js) can
 // rely on the return value being Signed.
 class VarType
 {
   public:
     enum Which {
         Int = Type::Int,
-        Double = Type::Double
+        Double = Type::Double,
+        Float = Type::Float
     };
 
   private:
     Which which_;
 
   public:
     VarType()
       : which_(Which(-1)) {}
     VarType(Which w)
       : which_(w) {}
     VarType(AsmJSCoercion coercion) {
         switch (coercion) {
           case AsmJS_ToInt32: which_ = Int; break;
           case AsmJS_ToNumber: which_ = Double; break;
+          case AsmJS_FRound: which_ = Float; break;
         }
     }
     Which which() const {
         return which_;
     }
     Type toType() const {
         return Type::Which(which_);
     }
     MIRType toMIRType() const {
-        return which_ == Int ? MIRType_Int32 : MIRType_Double;
+        switch(which_) {
+          case Int:     return MIRType_Int32;
+          case Double:  return MIRType_Double;
+          case Float:   return MIRType_Float32;
+        }
+        MOZ_ASSUME_UNREACHABLE("VarType can only be Int, Double or Float");
+        return MIRType_None;
     }
     AsmJSCoercion toCoercion() const {
-        return which_ == Int ? AsmJS_ToInt32 : AsmJS_ToNumber;
+        switch(which_) {
+          case Int:     return AsmJS_ToInt32;
+          case Double:  return AsmJS_ToNumber;
+          case Float:   return AsmJS_FRound;
+        }
+        MOZ_ASSUME_UNREACHABLE("VarType can only be Int, Double or Float");
+        return AsmJS_ToInt32;
     }
     static VarType FromMIRType(MIRType type) {
-        JS_ASSERT(type == MIRType_Int32 || type == MIRType_Double);
-        return type == MIRType_Int32 ? Int : Double;
+        JS_ASSERT(type == MIRType_Int32 || type == MIRType_Double || type == MIRType_Float32);
+        switch(type) {
+          case MIRType_Int32:   return Int;
+          case MIRType_Float32: return Float;
+          case MIRType_Double:  return Double;
+          default: MOZ_ASSUME_UNREACHABLE("FromMIRType MIR type not handled"); return Int;
+        }
     }
     static VarType FromCheckedType(Type type) {
-        JS_ASSERT(type.isInt() || type.isDoublish());
-        return type.isDoublish() ? Double : Int;
+        JS_ASSERT(type.isInt() || type.isMaybeDouble() || type.isFloatish());
+        if (type.isMaybeDouble())
+            return Double;
+        else if (type.isFloatish())
+            return Float;
+        else
+            return Int;
     }
     bool operator==(VarType rhs) const { return which_ == rhs.which_; }
     bool operator!=(VarType rhs) const { return which_ != rhs.which_; }
 };
 
 } /* anonymous namespace */
 
 // Implements <: (subtype) operator when the rhs is an VarType
 static inline bool
 operator<=(Type lhs, VarType rhs)
 {
     switch (rhs.which()) {
       case VarType::Int:    return lhs.isInt();
       case VarType::Double: return lhs.isDouble();
+      case VarType::Float:  return lhs.isFloat();
     }
     MOZ_ASSUME_UNREACHABLE("Unexpected rhs type");
 }
 
 /*****************************************************************************/
 
 static inline MIRType ToMIRType(MIRType t) { return t; }
 static inline MIRType ToMIRType(VarType t) { return t.toMIRType(); }
@@ -701,16 +752,18 @@ class NumLit
         OutOfRangeInt = -1
     };
 
   private:
     Which which_;
     Value v_;
 
   public:
+    NumLit() {}
+
     NumLit(Which w, Value v)
       : which_(w), v_(v)
     {}
 
     Which which() const {
         return which_;
     }
 
@@ -787,16 +840,39 @@ ExtractNumericLiteral(ParseNode *pn)
             return NumLit(NumLit::Fixnum, Int32Value(i64));
         JS_ASSERT(i64 <= UINT32_MAX);
         return NumLit(NumLit::BigUnsigned, Int32Value(uint32_t(i64)));
     }
     JS_ASSERT(i64 >= INT32_MIN);
     return NumLit(NumLit::NegativeInt, Int32Value(i64));
 }
 
+static bool
+ExtractFRoundableLiteral(ParseNode *pn, double *value)
+{
+    if (!IsNumericLiteral(pn))
+        return false;
+
+    NumLit literal = ExtractNumericLiteral(pn);
+    switch (literal.which()) {
+      case NumLit::Double:
+        *value = literal.toDouble();
+        return true;
+      case NumLit::Fixnum:
+      case NumLit::NegativeInt:
+      case NumLit::BigUnsigned:
+        literal = NumLit(NumLit::Double, DoubleValue(literal.toInt32()));
+        *value = literal.toDouble();
+        return true;
+      case NumLit::OutOfRangeInt:
+        break;
+    }
+    return false;
+}
+
 static inline bool
 IsLiteralInt(ParseNode *pn, uint32_t *u32)
 {
     if (!IsNumericLiteral(pn))
         return false;
 
     NumLit literal = ExtractNumericLiteral(pn);
     switch (literal.which()) {
@@ -843,42 +919,19 @@ TypedArrayLoadType(ArrayBufferView::View
       case ArrayBufferView::TYPE_INT8:
       case ArrayBufferView::TYPE_INT16:
       case ArrayBufferView::TYPE_INT32:
       case ArrayBufferView::TYPE_UINT8:
       case ArrayBufferView::TYPE_UINT16:
       case ArrayBufferView::TYPE_UINT32:
         return Type::Intish;
       case ArrayBufferView::TYPE_FLOAT32:
+        return Type::MaybeFloat;
       case ArrayBufferView::TYPE_FLOAT64:
-        return Type::Doublish;
-      default:;
-    }
-    MOZ_ASSUME_UNREACHABLE("Unexpected array type");
-}
-
-enum ArrayStoreEnum {
-    ArrayStore_Intish,
-    ArrayStore_Doublish
-};
-
-static ArrayStoreEnum
-TypedArrayStoreType(ArrayBufferView::ViewType viewType)
-{
-    switch (viewType) {
-      case ArrayBufferView::TYPE_INT8:
-      case ArrayBufferView::TYPE_INT16:
-      case ArrayBufferView::TYPE_INT32:
-      case ArrayBufferView::TYPE_UINT8:
-      case ArrayBufferView::TYPE_UINT16:
-      case ArrayBufferView::TYPE_UINT32:
-        return ArrayStore_Intish;
-      case ArrayBufferView::TYPE_FLOAT32:
-      case ArrayBufferView::TYPE_FLOAT64:
-        return ArrayStore_Doublish;
+        return Type::MaybeDouble;
       default:;
     }
     MOZ_ASSUME_UNREACHABLE("Unexpected array type");
 }
 
 enum NeedsBoundsCheck {
     NO_BOUNDS_CHECK,
     NEEDS_BOUNDS_CHECK
@@ -1300,17 +1353,18 @@ class MOZ_STACK_CLASS ModuleCompiler
             !addStandardLibraryMathName("ceil", AsmJSMathBuiltin_ceil) ||
             !addStandardLibraryMathName("floor", AsmJSMathBuiltin_floor) ||
             !addStandardLibraryMathName("exp", AsmJSMathBuiltin_exp) ||
             !addStandardLibraryMathName("log", AsmJSMathBuiltin_log) ||
             !addStandardLibraryMathName("pow", AsmJSMathBuiltin_pow) ||
             !addStandardLibraryMathName("sqrt", AsmJSMathBuiltin_sqrt) ||
             !addStandardLibraryMathName("abs", AsmJSMathBuiltin_abs) ||
             !addStandardLibraryMathName("atan2", AsmJSMathBuiltin_atan2) ||
-            !addStandardLibraryMathName("imul", AsmJSMathBuiltin_imul))
+            !addStandardLibraryMathName("imul", AsmJSMathBuiltin_imul) ||
+            !addStandardLibraryMathName("fround", AsmJSMathBuiltin_fround))
         {
             return false;
         }
 
         module_ = cx_->new_<AsmJSModule>(parser_.ss, parser_.offsetOfCurrentAsmJSModule());
         if (!module_)
             return false;
 
@@ -1436,17 +1490,17 @@ class MOZ_STACK_CLASS ModuleCompiler
 
     void initGlobalArgumentName(PropertyName *n) { module_->initGlobalArgumentName(n); }
     void initImportArgumentName(PropertyName *n) { module_->initImportArgumentName(n); }
     void initBufferArgumentName(PropertyName *n) { module_->initBufferArgumentName(n); }
 
     bool addGlobalVarInitConstant(PropertyName *varName, VarType type, const Value &v,
                                   bool isConst) {
         uint32_t index;
-        if (!module_->addGlobalVarInitConstant(v, &index))
+        if (!module_->addGlobalVarInitConstant(v, type.toCoercion(), &index))
             return false;
         Global *global = moduleLifo_.new_<Global>(Global::Variable);
         if (!global)
             return false;
         global->u.var.index_ = index;
         global->u.var.type_ = type.which();
         global->u.var.isConst_ = isConst;
         global->u.var.isLitConst_ = isConst;
@@ -1803,19 +1857,26 @@ class FunctionCompiler
   public:
     struct Local
     {
         VarType type;
         unsigned slot;
         Local(VarType t, unsigned slot) : type(t), slot(slot) {}
     };
 
+    struct TypedValue
+    {
+        VarType type;
+        Value value;
+        TypedValue(VarType t, const Value &v) : type(t), value(v) {}
+    };
+
   private:
     typedef HashMap<PropertyName*, Local> LocalMap;
-    typedef js::Vector<Value> VarInitializerVector;
+    typedef js::Vector<TypedValue> VarInitializerVector;
     typedef HashMap<PropertyName*, BlockVector> LabeledBlockMap;
     typedef HashMap<ParseNode*, BlockVector> UnlabeledBlockMap;
     typedef js::Vector<ParseNode*, 4> NodeStack;
 
     ModuleCompiler &       m_;
     LifoAlloc &            lifo_;
     ParseNode *            fn_;
 
@@ -1919,17 +1980,17 @@ class FunctionCompiler
 
     bool addVariable(ParseNode *pn, PropertyName *name, VarType type, const Value &init)
     {
         LocalMap::AddPtr p = locals_.lookupForAdd(name);
         if (p)
             return failName(pn, "duplicate local name '%s' not allowed", name);
         if (!locals_.add(p, name, Local(type, locals_.count())))
             return false;
-        return varInitializers_.append(init);
+        return varInitializers_.append(TypedValue(type, init));
     }
 
     bool prepareToEmitMIR(const VarTypeVector &argTypes)
     {
         JS_ASSERT(locals_.count() == argTypes.length() + varInitializers_.length());
 
         alloc_  = lifo_.new_<TempAllocator>(&lifo_);
         ionContext_.construct(m_.cx(), alloc_);
@@ -1945,17 +2006,18 @@ class FunctionCompiler
 
         for (ABIArgTypeIter i = argTypes; !i.done(); i++) {
             MAsmJSParameter *ins = MAsmJSParameter::New(alloc(), *i, i.mirType());
             curBlock_->add(ins);
             curBlock_->initSlot(info().localSlot(i.index()), ins);
         }
         unsigned firstLocalSlot = argTypes.length();
         for (unsigned i = 0; i < varInitializers_.length(); i++) {
-            MConstant *ins = MConstant::New(alloc(), varInitializers_[i]);
+            MConstant *ins = MConstant::NewAsmJS(alloc(), varInitializers_[i].value,
+                                                 varInitializers_[i].type.toMIRType());
             curBlock_->add(ins);
             curBlock_->initSlot(info().localSlot(firstLocalSlot + i), ins);
         }
         return true;
     }
 
     /******************************* For consistency of returns in a function */
 
@@ -2005,16 +2067,26 @@ class FunctionCompiler
         if (!curBlock_)
             return nullptr;
         JS_ASSERT(v.isNumber());
         MConstant *constant = MConstant::New(alloc(), v);
         curBlock_->add(constant);
         return constant;
     }
 
+    MDefinition *constantFloat(float f)
+    {
+        if (!curBlock_)
+            return NULL;
+
+        MConstant *constant = MConstant::NewAsmJS(alloc(), DoubleValue(double(f)), MIRType_Float32);
+        curBlock_->add(constant);
+        return constant;
+    }
+
     template <class T>
     MDefinition *unary(MDefinition *op)
     {
         if (!curBlock_)
             return nullptr;
         T *ins = T::NewAsmJS(alloc(), op);
         curBlock_->add(ins);
         return ins;
@@ -2865,19 +2937,48 @@ CheckGlobalVariableInitConstant(ModuleCo
         break;
       case NumLit::OutOfRangeInt:
         return m.fail(initNode, "global initializer is out of representable integer range");
     }
     return m.addGlobalVarInitConstant(varName, type, literal.value(), isConst);
 }
 
 static bool
+CheckFloat32Coercion(ModuleCompiler &m, ParseNode *callNode, ParseNode **coercedExpr,
+                     const char* errorMessage)
+{
+    JS_ASSERT(callNode->isKind(PNK_CALL));
+
+    ParseNode *callee = CallCallee(callNode);
+    if (!callee->isKind(PNK_NAME))
+        return m.fail(callee, errorMessage);
+
+    PropertyName *calleeName = callee->name();
+
+    const ModuleCompiler::Global *global = m.lookupGlobal(calleeName);
+    if (!global || global->which() != ModuleCompiler::Global::MathBuiltin ||
+        global->mathBuiltin() != AsmJSMathBuiltin_fround)
+    {
+        return m.fail(callee, errorMessage);
+    }
+
+    unsigned numArgs = CallArgListLength(callNode);
+    if (numArgs != 1)
+        return m.failf(callee, "fround passed %u arguments, expected one", numArgs);
+
+    if (coercedExpr)
+        *coercedExpr = CallArgList(callNode);
+    return true;
+}
+
+static bool
 CheckTypeAnnotation(ModuleCompiler &m, ParseNode *coercionNode, AsmJSCoercion *coercion,
                     ParseNode **coercedExpr = nullptr)
 {
+    static const char *errorMessage = "in coercion expression, the expression must be of the form +x, fround(x) or x|0";
     switch (coercionNode->getKind()) {
       case PNK_BITOR: {
         ParseNode *rhs = BinaryRight(coercionNode);
 
         if (!IsNumericLiteral(rhs))
             return m.fail(rhs, "must use |0 for argument/return coercion");
 
         NumLit rhsLiteral = ExtractNumericLiteral(rhs);
@@ -2890,47 +2991,75 @@ CheckTypeAnnotation(ModuleCompiler &m, P
         return true;
       }
       case PNK_POS: {
         *coercion = AsmJS_ToNumber;
         if (coercedExpr)
             *coercedExpr = UnaryKid(coercionNode);
         return true;
       }
+      case PNK_CALL: {
+        *coercion = AsmJS_FRound;
+        return CheckFloat32Coercion(m, coercionNode, coercedExpr, errorMessage);
+      }
       default:;
     }
 
-    return m.fail(coercionNode, "in coercion expression, the expression must be of the form +x or x|0");
+    return m.fail(coercionNode, errorMessage);
 }
 
 static bool
-CheckGlobalVariableInitImport(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode,
-                              bool isConst)
-{
-    AsmJSCoercion coercion;
-    ParseNode *coercedExpr;
-    if (!CheckTypeAnnotation(m, initNode, &coercion, &coercedExpr))
-        return false;
-
+CheckGlobalVariableImportExpr(ModuleCompiler &m, PropertyName *varName, AsmJSCoercion coercion,
+                              ParseNode *coercedExpr, bool isConst)
+{
     if (!coercedExpr->isKind(PNK_DOT))
         return m.failName(coercedExpr, "invalid import expression for global '%s'", varName);
 
     ParseNode *base = DotBase(coercedExpr);
     PropertyName *field = DotMember(coercedExpr);
 
     PropertyName *importName = m.module().importArgumentName();
     if (!importName)
         return m.fail(coercedExpr, "cannot import without an asm.js foreign parameter");
     if (!IsUseOfName(base, importName))
         return m.failName(coercedExpr, "base of import expression must be '%s'", importName);
 
     return m.addGlobalVarImport(varName, field, coercion, isConst);
 }
 
 static bool
+CheckGlobalVariableInitImport(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode,
+                              bool isConst)
+{
+    AsmJSCoercion coercion;
+    ParseNode *coercedExpr;
+    if (!CheckTypeAnnotation(m, initNode, &coercion, &coercedExpr))
+        return false;
+    return CheckGlobalVariableImportExpr(m, varName, coercion, coercedExpr, isConst);
+}
+
+static bool
+CheckGlobalVariableInitFloat32(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode,
+                               bool isConst)
+{
+    ParseNode *arg = NULL;
+    if (!CheckFloat32Coercion(m, initNode, &arg, "call must be of the form fround(x)"))
+        return false;
+
+    if (IsNumericLiteral(arg)) {
+        double value;
+        if (!ExtractFRoundableLiteral(arg, &value))
+            return m.fail(arg, "float global initializer needs to be a double literal");
+        return m.addGlobalVarInitConstant(varName, VarType::Float, DoubleValue(value), isConst);
+    }
+
+    return CheckGlobalVariableImportExpr(m, varName, AsmJSCoercion::AsmJS_FRound, arg, isConst);
+}
+
+static bool
 CheckNewArrayView(ModuleCompiler &m, PropertyName *varName, ParseNode *newExpr)
 {
     ParseNode *ctorExpr = ListHead(newExpr);
     if (!ctorExpr->isKind(PNK_DOT))
         return m.fail(ctorExpr, "only valid 'new' import is 'new global.*Array(buf)'");
 
     ParseNode *base = DotBase(ctorExpr);
     PropertyName *field = DotMember(ctorExpr);
@@ -3022,16 +3151,19 @@ CheckModuleGlobal(ModuleCompiler &m, Par
         return m.fail(var, "module import needs initializer");
 
     if (IsNumericLiteral(initNode))
         return CheckGlobalVariableInitConstant(m, var->name(), initNode, isConst);
 
     if (initNode->isKind(PNK_BITOR) || initNode->isKind(PNK_POS))
         return CheckGlobalVariableInitImport(m, var->name(), initNode, isConst);
 
+    if (initNode->isKind(PNK_CALL))
+        return CheckGlobalVariableInitFloat32(m, var->name(), initNode, isConst);
+
     if (initNode->isKind(PNK_NEW))
         return CheckNewArrayView(m, var->name(), initNode);
 
     if (initNode->isKind(PNK_DOT))
         return CheckGlobalDotImport(m, var->name(), initNode);
 
     return m.fail(initNode, "unsupported import expression");
 }
@@ -3053,17 +3185,17 @@ CheckModuleGlobals(ModuleCompiler &m)
 
     return true;
 }
 
 static bool
 ArgFail(FunctionCompiler &f, PropertyName *argName, ParseNode *stmt)
 {
     return f.failName(stmt, "expecting argument type declaration for '%s' of the "
-                      "form 'arg = arg|0' or 'arg = +arg'", argName);
+                      "form 'arg = arg|0' or 'arg = +arg' or 'arg = fround(arg)'", argName);
 }
 
 static bool
 CheckArgumentType(FunctionCompiler &f, ParseNode *stmt, PropertyName *name, VarType *type)
 {
     if (!stmt || !IsExpressionStatement(stmt))
         return ArgFail(f, name, stmt ? stmt : f.fn());
 
@@ -3165,16 +3297,28 @@ CheckVariable(FunctionCompiler &f, Parse
 
     if (!CheckIdentifier(f.m(), var, name))
         return false;
 
     ParseNode *initNode = MaybeDefinitionInitializer(var);
     if (!initNode)
         return f.failName(var, "var '%s' needs explicit type declaration via an initial value", name);
 
+    if (initNode->isKind(PNK_CALL)) {
+        ParseNode *coercedVar = NULL;
+        if (!CheckFloat32Coercion(f.m(), initNode, &coercedVar, "caller in var initializer can only be fround"))
+            return false;
+
+        double value;
+        if (!ExtractFRoundableLiteral(coercedVar, &value))
+            return f.failName(coercedVar, "float initializer for '%s' needs to be a double literal", name);
+
+        return f.addVariable(var, name, VarType::Float, DoubleValue(value));
+    }
+
     if (!IsNumericLiteral(initNode))
         return f.failName(initNode, "initializer for '%s' needs to be a numeric literal", name);
 
     NumLit literal = ExtractNumericLiteral(initNode);
     VarType type;
     switch (literal.which()) {
       case NumLit::Fixnum:
       case NumLit::NegativeInt:
@@ -3411,17 +3555,17 @@ CheckArrayAccess(FunctionCompiler &f, Pa
         *def = pointerDef;
     else
         *def = f.bitwise<MBitAnd>(pointerDef, f.constant(Int32Value(mask)));
 
     return true;
 }
 
 static bool
-CheckArrayLoad(FunctionCompiler &f, ParseNode *elem, MDefinition **def, Type *type)
+CheckLoadArray(FunctionCompiler &f, ParseNode *elem, MDefinition **def, Type *type)
 {
     ArrayBufferView::ViewType viewType;
     MDefinition *pointerDef;
     NeedsBoundsCheck needsBoundsCheck;
     if (!CheckArrayAccess(f, elem, &viewType, &pointerDef, &needsBoundsCheck))
         return false;
 
     *def = f.loadHeap(viewType, pointerDef, needsBoundsCheck);
@@ -3438,25 +3582,40 @@ CheckStoreArray(FunctionCompiler &f, Par
     if (!CheckArrayAccess(f, lhs, &viewType, &pointerDef, &needsBoundsCheck))
         return false;
 
     MDefinition *rhsDef;
     Type rhsType;
     if (!CheckExpr(f, rhs, &rhsDef, &rhsType))
         return false;
 
-    switch (TypedArrayStoreType(viewType)) {
-      case ArrayStore_Intish:
+    switch (viewType) {
+      case ArrayBufferView::TYPE_INT8:
+      case ArrayBufferView::TYPE_INT16:
+      case ArrayBufferView::TYPE_INT32:
+      case ArrayBufferView::TYPE_UINT8:
+      case ArrayBufferView::TYPE_UINT16:
+      case ArrayBufferView::TYPE_UINT32:
         if (!rhsType.isIntish())
             return f.failf(lhs, "%s is not a subtype of intish", rhsType.toChars());
         break;
-      case ArrayStore_Doublish:
-        if (!rhsType.isDoublish())
-            return f.failf(lhs, "%s is not a subtype of doublish", rhsType.toChars());
+      case ArrayBufferView::TYPE_FLOAT32:
+        if (rhsType.isMaybeDouble())
+            rhsDef = f.unary<MToFloat32>(rhsDef);
+        else if (!rhsType.isFloatish())
+            return f.failf(lhs, "%s is not a subtype of double? or floatish", rhsType.toChars());
         break;
+      case ArrayBufferView::TYPE_FLOAT64:
+        if (rhsType.isFloat())
+            rhsDef = f.unary<MToDouble>(rhsDef);
+        else if (!rhsType.isMaybeDouble())
+            return f.failf(lhs, "%s is not a subtype of float or double?", rhsType.toChars());
+        break;
+      default:
+        MOZ_ASSUME_UNREACHABLE("Unexpected view type");
     }
 
     f.storeHeap(viewType, pointerDef, rhsDef, needsBoundsCheck);
 
     *def = rhsDef;
     *type = rhsType;
     return true;
 }
@@ -3559,49 +3718,65 @@ CheckMathAbs(FunctionCompiler &f, ParseN
     if (argType.isSigned()) {
         if (retType != RetType::Signed)
             return f.failf(call, "return type is signed, used as %s", retType.toType().toChars());
         *def = f.unary<MAbs>(argDef, MIRType_Int32);
         *type = Type::Signed;
         return true;
     }
 
-    if (argType.isDoublish()) {
+    if (argType.isMaybeDouble()) {
         if (retType != RetType::Double)
             return f.failf(call, "return type is double, used as %s", retType.toType().toChars());
         *def = f.unary<MAbs>(argDef, MIRType_Double);
         *type = Type::Double;
         return true;
     }
 
-    return f.failf(call, "%s is not a subtype of signed or doublish", argType.toChars());
+    if (argType.isMaybeFloat()) {
+        if (retType != RetType::Float)
+            return f.failf(call, "return type is float, used as %s", retType.toType().toChars());
+        *def = f.unary<MAbs>(argDef, MIRType_Float32);
+        *type = Type::Float;
+        return true;
+    }
+
+    return f.failf(call, "%s is not a subtype of signed, float? or double?", argType.toChars());
 }
 
 static bool
 CheckMathSqrt(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition **def, Type *type)
 {
     if (CallArgListLength(call) != 1)
         return f.fail(call, "Math.sqrt must be passed 1 argument");
 
     ParseNode *arg = CallArgList(call);
 
     MDefinition *argDef;
     Type argType;
     if (!CheckExpr(f, arg, &argDef, &argType))
         return false;
 
-    if (argType.isDoublish()) {
+    if (argType.isMaybeDouble()) {
         if (retType != RetType::Double)
             return f.failf(call, "return type is double, used as %s", retType.toType().toChars());
         *def = f.unary<MSqrt>(argDef, MIRType_Double);
         *type = Type::Double;
         return true;
     }
 
-    return f.failf(call, "%s is not a subtype of doublish", argType.toChars());
+    if (argType.isMaybeFloat()) {
+        if (retType != RetType::Float)
+            return f.failf(call, "return type is float, used as %s", retType.toType().toChars());
+        *def = f.unary<MSqrt>(argDef, MIRType_Float32);
+        *type = Type::Float;
+        return true;
+    }
+
+    return f.failf(call, "%s is neither a subtype of double? nor float?", argType.toChars());
 }
 
 typedef bool (*CheckArgType)(FunctionCompiler &f, ParseNode *argNode, Type type);
 
 static bool
 CheckCallArgs(FunctionCompiler &f, ParseNode *callNode, CheckArgType checkArg,
               FunctionCompiler::Call *call)
 {
@@ -3667,17 +3842,17 @@ CheckFunctionSignature(ModuleCompiler &m
     *func = existing;
     return true;
 }
 
 static bool
 CheckIsVarType(FunctionCompiler &f, ParseNode *argNode, Type type)
 {
     if (!type.isVarType())
-        return f.failf(argNode, "%s is not a subtype of int or double", type.toChars());
+        return f.failf(argNode, "%s is not a subtype of int, float or double", type.toChars());
     return true;
 }
 
 static bool
 CheckInternalCall(FunctionCompiler &f, ParseNode *callNode, PropertyName *calleeName,
                   RetType retType, MDefinition **def, Type *type)
 {
     FunctionCompiler::Call call(f, retType);
@@ -3781,77 +3956,164 @@ CheckIsExternType(FunctionCompiler &f, P
 }
 
 static bool
 CheckFFICall(FunctionCompiler &f, ParseNode *callNode, unsigned ffiIndex, RetType retType,
              MDefinition **def, Type *type)
 {
     PropertyName *calleeName = CallCallee(callNode)->name();
 
+    if (retType == RetType::Float)
+        return f.fail(callNode, "FFI calls can't return float");
+
     FunctionCompiler::Call call(f, retType);
     if (!CheckCallArgs(f, callNode, CheckIsExternType, &call))
         return false;
 
     unsigned exitIndex;
     if (!f.m().addExit(ffiIndex, calleeName, Move(call.sig()), &exitIndex))
         return false;
 
     if (!f.ffiCall(exitIndex, call, retType.toMIRType(), def))
         return false;
 
     *type = retType.toType();
     return true;
 }
 
+static bool CheckCall(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition **def, Type *type);
+
 static bool
-CheckIsDoublish(FunctionCompiler &f, ParseNode *argNode, Type type)
-{
-    if (!type.isDoublish())
-        return f.failf(argNode, "%s is not a subtype of doublish", type.toChars());
+CheckFRoundArg(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type, const char* error)
+{
+    ParseNode *arg = NULL;
+    if (!CheckFloat32Coercion(f.m(), expr, &arg, error))
+        return false;
+
+    if (arg->isKind(PNK_CALL))
+        return CheckCall(f, arg, RetType::Float, def, type);
+
+    if (IsNumericLiteral(arg)) {
+        double value;
+        if (!ExtractFRoundableLiteral(arg, &value))
+            return f.fail(arg, "call to fround with literal expects the literal to be a double");
+
+        *def = f.constantFloat(value);
+        *type = Type::Float;
+        return true;
+    }
+
+    MDefinition *inputDef;
+    Type inputType;
+    if (!CheckExpr(f, arg, &inputDef, &inputType))
+        return false;
+
+    if (inputType.isMaybeDouble() || inputType.isSigned())
+        *def = f.unary<MToFloat32>(inputDef);
+    else if (inputType.isUnsigned())
+        *def = f.unary<MAsmJSUnsignedToFloat32>(inputDef);
+    else if (inputType.isFloatish())
+        *def = inputDef;
+    else
+        return f.failf(arg, "%s is not a subtype of signed, unsigned, double? or floatish", inputType.toChars());
+
+    *type = Type::Float;
+    return true;
+}
+
+static bool
+CheckFRound(FunctionCompiler &f, ParseNode *callNode, RetType retType, MDefinition **def, Type *type)
+{
+    MDefinition *operand;
+    Type operandType;
+    if (!CheckFRoundArg(f, callNode, &operand, &operandType, "coercion to float should use fround"))
+        return false;
+
+    switch (retType.which()) {
+      case RetType::Double:
+        *def = f.unary<MToDouble>(operand);
+        *type = Type::Double;
+        return true;
+      case RetType::Signed:
+        *def = f.unary<MTruncateToInt32>(operand);
+        *type = Type::Signed;
+        return true;
+      case RetType::Float:
+        *def = operand;
+        *type = Type::Float;
+        return true;
+      case RetType::Void:
+        // definition and return types should be ignored by the caller
+        return true;
+    }
+
+    MOZ_ASSUME_UNREACHABLE("return value of fround is ignored");
+}
+
+static bool
+CheckIsMaybeDouble(FunctionCompiler &f, ParseNode *argNode, Type type)
+{
+    if (!type.isMaybeDouble())
+        return f.failf(argNode, "%s is not a subtype of double?", type.toChars());
+    return true;
+}
+
+static bool
+CheckIsMaybeFloat(FunctionCompiler &f, ParseNode *argNode, Type type)
+{
+    if (!type.isMaybeFloat())
+        return f.failf(argNode, "%s is not a subtype of float?", type.toChars());
     return true;
 }
 
 static bool
 CheckMathBuiltinCall(FunctionCompiler &f, ParseNode *callNode, AsmJSMathBuiltin mathBuiltin,
                      RetType retType, MDefinition **def, Type *type)
 {
     unsigned arity = 0;
-    AsmJSImmKind callee;
+    AsmJSImmKind doubleCallee, floatCallee;
     switch (mathBuiltin) {
-      case AsmJSMathBuiltin_imul:  return CheckMathIMul(f, callNode, retType, def, type);
-      case AsmJSMathBuiltin_abs:   return CheckMathAbs(f, callNode, retType, def, type);
-      case AsmJSMathBuiltin_sqrt:  return CheckMathSqrt(f, callNode, retType, def, type);
-      case AsmJSMathBuiltin_sin:   arity = 1; callee = AsmJSImm_SinD;   break;
-      case AsmJSMathBuiltin_cos:   arity = 1; callee = AsmJSImm_CosD;   break;
-      case AsmJSMathBuiltin_tan:   arity = 1; callee = AsmJSImm_TanD;   break;
-      case AsmJSMathBuiltin_asin:  arity = 1; callee = AsmJSImm_ASinD;  break;
-      case AsmJSMathBuiltin_acos:  arity = 1; callee = AsmJSImm_ACosD;  break;
-      case AsmJSMathBuiltin_atan:  arity = 1; callee = AsmJSImm_ATanD;  break;
-      case AsmJSMathBuiltin_ceil:  arity = 1; callee = AsmJSImm_CeilD;  break;
-      case AsmJSMathBuiltin_floor: arity = 1; callee = AsmJSImm_FloorD; break;
-      case AsmJSMathBuiltin_exp:   arity = 1; callee = AsmJSImm_ExpD;   break;
-      case AsmJSMathBuiltin_log:   arity = 1; callee = AsmJSImm_LogD;   break;
-      case AsmJSMathBuiltin_pow:   arity = 2; callee = AsmJSImm_PowD;   break;
-      case AsmJSMathBuiltin_atan2: arity = 2; callee = AsmJSImm_ATan2D; break;
-    }
+      case AsmJSMathBuiltin_imul:   return CheckMathIMul(f, callNode, retType, def, type);
+      case AsmJSMathBuiltin_abs:    return CheckMathAbs(f, callNode, retType, def, type);
+      case AsmJSMathBuiltin_sqrt:   return CheckMathSqrt(f, callNode, retType, def, type);
+      case AsmJSMathBuiltin_fround: return CheckFRound(f, callNode, retType, def, type);
+      case AsmJSMathBuiltin_sin:    arity = 1; doubleCallee = AsmJSImm_SinD; floatCallee = AsmJSImm_SinF;      break;
+      case AsmJSMathBuiltin_cos:    arity = 1; doubleCallee = AsmJSImm_CosD; floatCallee = AsmJSImm_CosF;      break;
+      case AsmJSMathBuiltin_tan:    arity = 1; doubleCallee = AsmJSImm_TanD; floatCallee = AsmJSImm_TanF;      break;
+      case AsmJSMathBuiltin_asin:   arity = 1; doubleCallee = AsmJSImm_ASinD; floatCallee = AsmJSImm_ASinF;    break;
+      case AsmJSMathBuiltin_acos:   arity = 1; doubleCallee = AsmJSImm_ACosD; floatCallee = AsmJSImm_ACosF;    break;
+      case AsmJSMathBuiltin_atan:   arity = 1; doubleCallee = AsmJSImm_ATanD; floatCallee = AsmJSImm_ATanF;    break;
+      case AsmJSMathBuiltin_ceil:   arity = 1; doubleCallee = AsmJSImm_CeilD; floatCallee = AsmJSImm_CeilF;    break;
+      case AsmJSMathBuiltin_floor:  arity = 1; doubleCallee = AsmJSImm_FloorD; floatCallee = AsmJSImm_FloorF;  break;
+      case AsmJSMathBuiltin_exp:    arity = 1; doubleCallee = AsmJSImm_ExpD; floatCallee = AsmJSImm_ExpF;      break;
+      case AsmJSMathBuiltin_log:    arity = 1; doubleCallee = AsmJSImm_LogD; floatCallee = AsmJSImm_LogF;      break;
+      case AsmJSMathBuiltin_pow:    arity = 2; doubleCallee = AsmJSImm_PowD; floatCallee = AsmJSImm_Invalid;   break;
+      case AsmJSMathBuiltin_atan2:  arity = 2; doubleCallee = AsmJSImm_ATan2D; floatCallee = AsmJSImm_Invalid; break;
+    }
+
+    if (retType == RetType::Float && floatCallee == AsmJSImm_Invalid)
+        return f.fail(callNode, "math builtin cannot be used as float");
+    if (retType != RetType::Double && retType != RetType::Float)
+        return f.failf(callNode, "return type of math function is double or float, used as %s", retType.toType().toChars());
 
     FunctionCompiler::Call call(f, retType);
-    if (!CheckCallArgs(f, callNode, CheckIsDoublish, &call))
+    if (retType == RetType::Float && !CheckCallArgs(f, callNode, CheckIsMaybeFloat, &call))
+        return false;
+    if (retType == RetType::Double && !CheckCallArgs(f, callNode, CheckIsMaybeDouble, &call))
         return false;
 
     if (call.sig().args().length() != arity)
         return f.failf(callNode, "call passed %u arguments, expected %u", call.sig().args().length(), arity);
 
-    if (!f.builtinCall(callee, call, MIRType_Double, def))
-        return false;
-
-    if (retType != RetType::Double)
-        return f.failf(callNode, "return type is double, used as %s", retType.toType().toChars());
-
-    *type = Type::Double;
+    if (retType == RetType::Float && !f.builtinCall(floatCallee, call, retType.toMIRType(), def))
+        return false;
+    if (retType == RetType::Double && !f.builtinCall(doubleCallee, call, retType.toMIRType(), def))
+        return false;
+
+    *type = retType.toType();
     return true;
 }
 
 static bool
 CheckCall(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition **def, Type *type)
 {
     JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
 
@@ -3893,24 +4155,24 @@ CheckPos(FunctionCompiler &f, ParseNode 
     if (operand->isKind(PNK_CALL))
         return CheckCall(f, operand, RetType::Double, def, type);
 
     MDefinition *operandDef;
     Type operandType;
     if (!CheckExpr(f, operand, &operandDef, &operandType))
         return false;
 
-    if (operandType.isSigned())
+    if (operandType.isMaybeFloat() || operandType.isSigned())
         *def = f.unary<MToDouble>(operandDef);
     else if (operandType.isUnsigned())
         *def = f.unary<MAsmJSUnsignedToDouble>(operandDef);
-    else if (operandType.isDoublish())
+    else if (operandType.isMaybeDouble())
         *def = operandDef;
     else
-        return f.failf(operand, "%s is not a subtype of signed, unsigned or doublish", operandType.toChars());
+        return f.failf(operand, "%s is not a subtype of signed, unsigned, float or double?", operandType.toChars());
 
     *type = Type::Double;
     return true;
 }
 
 static bool
 CheckNot(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type)
 {
@@ -3942,44 +4204,50 @@ CheckNeg(FunctionCompiler &f, ParseNode 
         return false;
 
     if (operandType.isInt()) {
         *def = f.unary<MAsmJSNeg>(operandDef, MIRType_Int32);
         *type = Type::Intish;
         return true;
     }
 
-    if (operandType.isDoublish()) {
+    if (operandType.isMaybeDouble()) {
         *def = f.unary<MAsmJSNeg>(operandDef, MIRType_Double);
         *type = Type::Double;
         return true;
     }
 
-    return f.failf(operand, "%s is not a subtype of int or doublish", operandType.toChars());
+    if (operandType.isMaybeFloat()) {
+        *def = f.unary<MAsmJSNeg>(operandDef, MIRType_Float32);
+        *type = Type::Floatish;
+        return true;
+    }
+
+    return f.failf(operand, "%s is not a subtype of int, float? or double?", operandType.toChars());
 }
 
 static bool
 CheckCoerceToInt(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type)
 {
     JS_ASSERT(expr->isKind(PNK_BITNOT));
     ParseNode *operand = UnaryKid(expr);
 
     MDefinition *operandDef;
     Type operandType;
     if (!CheckExpr(f, operand, &operandDef, &operandType))
         return false;
 
-    if (operandType.isDoublish()) {
+    if (operandType.isMaybeDouble() || operandType.isMaybeFloat()) {
         *def = f.unary<MTruncateToInt32>(operandDef);
         *type = Type::Signed;
         return true;
     }
 
     if (!operandType.isIntish())
-        return f.failf(operand, "%s is not a subtype of doublish or intish", operandType.toChars());
+        return f.failf(operand, "%s is not a subtype of double?, float? or intish", operandType.toChars());
 
     *def = operandDef;
     *type = Type::Signed;
     return true;
 }
 
 static bool
 CheckBitNot(FunctionCompiler &f, ParseNode *neg, MDefinition **def, Type *type)
@@ -4066,16 +4334,18 @@ CheckConditional(FunctionCompiler &f, Pa
         return false;
 
     f.pushPhiInput(elseDef);
 
     if (thenType.isInt() && elseType.isInt()) {
         *type = Type::Int;
     } else if (thenType.isDouble() && elseType.isDouble()) {
         *type = Type::Double;
+    } else if (thenType.isFloat() && elseType.isFloat()) {
+        *type = Type::Float;
     } else {
         return f.failf(ternary, "then/else branches of conditional must both produce int or double, "
                        "current types are %s and %s", thenType.toChars(), elseType.toChars());
     }
 
     if (!f.joinIfElse(thenBlocks, elseExpr))
         return false;
 
@@ -4125,24 +4395,29 @@ CheckMultiply(FunctionCompiler &f, Parse
     if (lhsType.isInt() && rhsType.isInt()) {
         if (!IsValidIntMultiplyConstant(lhs) && !IsValidIntMultiplyConstant(rhs))
             return f.fail(star, "one arg to int multiply must be a small (-2^20, 2^20) int literal");
         *def = f.mul(lhsDef, rhsDef, MIRType_Int32, MMul::Integer);
         *type = Type::Intish;
         return true;
     }
 
-    if (!lhsType.isDoublish())
-        return f.failf(lhs, "%s is not a subtype of doublish", lhsType.toChars());
-    if (!rhsType.isDoublish())
-        return f.failf(rhs, "%s is not a subtype of doublish", rhsType.toChars());
-
-    *def = f.mul(lhsDef, rhsDef, MIRType_Double, MMul::Normal);
-    *type = Type::Double;
-    return true;
+    if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) {
+        *def = f.mul(lhsDef, rhsDef, MIRType_Double, MMul::Normal);
+        *type = Type::Double;
+        return true;
+    }
+
+    if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) {
+        *def = f.mul(lhsDef, rhsDef, MIRType_Float32, MMul::Normal);
+        *type = Type::Floatish;
+        return true;
+    }
+
+    return f.fail(star, "multiply operands must be both int, both double? or both float?");
 }
 
 static bool
 CheckAddOrSub(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type,
               unsigned *numAddOrSubOut = nullptr)
 {
     JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
 
@@ -4180,23 +4455,28 @@ CheckAddOrSub(FunctionCompiler &f, Parse
     if (numAddOrSub > (1<<20))
         return f.fail(expr, "too many + or - without intervening coercion");
 
     if (lhsType.isInt() && rhsType.isInt()) {
         *def = expr->isKind(PNK_ADD)
                ? f.binary<MAdd>(lhsDef, rhsDef, MIRType_Int32)
                : f.binary<MSub>(lhsDef, rhsDef, MIRType_Int32);
         *type = Type::Intish;
-    } else if (lhsType.isDoublish() && rhsType.isDoublish()) {
+    } else if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) {
         *def = expr->isKind(PNK_ADD)
                ? f.binary<MAdd>(lhsDef, rhsDef, MIRType_Double)
                : f.binary<MSub>(lhsDef, rhsDef, MIRType_Double);
         *type = Type::Double;
+    } else if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) {
+        *def = expr->isKind(PNK_ADD)
+               ? f.binary<MAdd>(lhsDef, rhsDef, MIRType_Float32)
+               : f.binary<MSub>(lhsDef, rhsDef, MIRType_Float32);
+        *type = Type::Floatish;
     } else {
-        return f.failf(expr, "operands to +/- must both be int or doublish, got %s and %s",
+        return f.failf(expr, "operands to + or - must both be int, float? or double?, got %s and %s",
                        lhsType.toChars(), rhsType.toChars());
     }
 
     if (numAddOrSubOut)
         *numAddOrSubOut = numAddOrSub;
     return true;
 }
 
@@ -4209,24 +4489,33 @@ CheckDivOrMod(FunctionCompiler &f, Parse
 
     MDefinition *lhsDef, *rhsDef;
     Type lhsType, rhsType;
     if (!CheckExpr(f, lhs, &lhsDef, &lhsType))
         return false;
     if (!CheckExpr(f, rhs, &rhsDef, &rhsType))
         return false;
 
-    if (lhsType.isDoublish() && rhsType.isDoublish()) {
+    if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) {
         *def = expr->isKind(PNK_DIV)
                ? f.div(lhsDef, rhsDef, MIRType_Double, /* unsignd = */ false)
                : f.mod(lhsDef, rhsDef, MIRType_Double, /* unsignd = */ false);
         *type = Type::Double;
         return true;
     }
 
+    if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) {
+        if (expr->isKind(PNK_DIV))
+            *def = f.div(lhsDef, rhsDef, MIRType_Float32, /* unsignd = */ false);
+        else
+            return f.fail(expr, "modulo cannot receive float arguments");
+        *type = Type::Floatish;
+        return true;
+    }
+
     if (lhsType.isSigned() && rhsType.isSigned()) {
         if (expr->isKind(PNK_DIV))
             *def = f.div(lhsDef, rhsDef, MIRType_Int32, /* unsignd = */ false);
         else
             *def = f.mod(lhsDef, rhsDef, MIRType_Int32, /* unsignd = */ false);
         *type = Type::Intish;
         return true;
     }
@@ -4235,17 +4524,17 @@ CheckDivOrMod(FunctionCompiler &f, Parse
         if (expr->isKind(PNK_DIV))
             *def = f.div(lhsDef, rhsDef, MIRType_Int32, /* unsignd = */ true);
         else
             *def = f.mod(lhsDef, rhsDef, MIRType_Int32, /* unsignd = */ true);
         *type = Type::Intish;
         return true;
     }
 
-    return f.failf(expr, "arguments to / or %% must both be double, signed, or unsigned; "
+    return f.failf(expr, "arguments to / or %% must both be double?, float?, signed, or unsigned; "
                    "%s and %s are given", lhsType.toChars(), rhsType.toChars());
 }
 
 static bool
 CheckComparison(FunctionCompiler &f, ParseNode *comp, MDefinition **def, Type *type)
 {
     JS_ASSERT(comp->isKind(PNK_LT) || comp->isKind(PNK_LE) || comp->isKind(PNK_GT) ||
               comp->isKind(PNK_GE) || comp->isKind(PNK_EQ) || comp->isKind(PNK_NE));
@@ -4269,17 +4558,23 @@ CheckComparison(FunctionCompiler &f, Par
     }
 
     if (lhsType.isDouble() && rhsType.isDouble()) {
         *def = f.compare(lhsDef, rhsDef, comp->getOp(), MCompare::Compare_Double);
         *type = Type::Int;
         return true;
     }
 
-    return f.failf(comp, "arguments to a comparison must both be signed, unsigned or doubles; "
+    if (lhsType.isFloat() && rhsType.isFloat()) {
+        *def = f.compare(lhsDef, rhsDef, comp->getOp(), MCompare::Compare_Float32);
+        *type = Type::Int;
+        return true;
+    }
+
+    return f.failf(comp, "arguments to a comparison must both be signed, unsigned, floats or doubles; "
                    "%s and %s are given", lhsType.toChars(), rhsType.toChars());
 }
 
 static bool
 CheckBitwise(FunctionCompiler &f, ParseNode *bitwise, MDefinition **def, Type *type)
 {
     ParseNode *lhs = BinaryLeft(bitwise);
     ParseNode *rhs = BinaryRight(bitwise);
@@ -4341,41 +4636,50 @@ CheckBitwise(FunctionCompiler &f, ParseN
       case PNK_URSH:   *def = f.bitwise<MUrsh>(lhsDef, rhsDef); break;
       default: MOZ_ASSUME_UNREACHABLE("not a bitwise op");
     }
 
     return true;
 }
 
 static bool
+CheckUncoercedCall(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type)
+{
+    static const char* callError = "all function calls must either be ignored (via "
+                                   "f(); or comma-expression), coerced to signed "
+                                   "(via f()|0), coerced to float (via fround(f()))"
+                                   " or coerced to double (via +f())";
+
+    JS_ASSERT(expr->isKind(PNK_CALL));
+    return CheckFRoundArg(f, expr, def, type, callError);
+}
+
+static bool
 CheckExpr(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type)
 {
     JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
 
     if (!f.mirGen().ensureBallast())
         return false;
 
     if (IsNumericLiteral(expr))
         return CheckNumericLiteral(f, expr, def, type);
 
     switch (expr->getKind()) {
       case PNK_NAME:        return CheckVarRef(f, expr, def, type);
-      case PNK_ELEM:        return CheckArrayLoad(f, expr, def, type);
+      case PNK_ELEM:        return CheckLoadArray(f, expr, def, type);
       case PNK_ASSIGN:      return CheckAssign(f, expr, def, type);
       case PNK_POS:         return CheckPos(f, expr, def, type);
       case PNK_NOT:         return CheckNot(f, expr, def, type);
       case PNK_NEG:         return CheckNeg(f, expr, def, type);
       case PNK_BITNOT:      return CheckBitNot(f, expr, def, type);
       case PNK_COMMA:       return CheckComma(f, expr, def, type);
       case PNK_CONDITIONAL: return CheckConditional(f, expr, def, type);
       case PNK_STAR:        return CheckMultiply(f, expr, def, type);
-
-      case PNK_CALL:        return f.fail(expr, "all function calls must either be ignored (via "
-                                                "f(); or comma-expression), coerced to signed "
-                                                "(via f()|0) or coerced to double (via +f())");
+      case PNK_CALL:        return CheckUncoercedCall(f, expr, def, type);
 
       case PNK_ADD:
       case PNK_SUB:         return CheckAddOrSub(f, expr, def, type);
 
       case PNK_DIV:
       case PNK_MOD:         return CheckDivOrMod(f, expr, def, type);
 
       case PNK_LT:
@@ -4787,16 +5091,18 @@ CheckReturn(FunctionCompiler &f, ParseNo
     if (!CheckExpr(f, expr, &def, &type))
         return false;
 
     RetType retType;
     if (type.isSigned())
         retType = RetType::Signed;
     else if (type.isDouble())
         retType = RetType::Double;
+    else if (type.isFloat())
+        retType = RetType::Float;
     else if (type.isVoid())
         retType = RetType::Void;
     else
         return f.failf(expr, "%s is not a valid return type", type.toChars());
 
     if (!CheckReturnType(f, expr, retType))
         return false;
 
@@ -5600,17 +5906,17 @@ GenerateEntry(ModuleCompiler &m, const A
           case ABIArg::FPU:
             masm.loadDouble(src, iter->fpu());
             break;
           case ABIArg::Stack:
             if (iter.mirType() == MIRType_Int32) {
                 masm.load32(src, scratch);
                 masm.storePtr(scratch, Address(StackPointer, iter->offsetFromArgBase()));
             } else {
-                JS_ASSERT(iter.mirType() == MIRType_Double);
+                JS_ASSERT(iter.mirType() == MIRType_Double || iter.mirType() == MIRType_Float32);
                 masm.loadDouble(src, ScratchFloatReg);
                 masm.storeDouble(ScratchFloatReg, Address(StackPointer, iter->offsetFromArgBase()));
             }
             break;
         }
     }
 
     // Call into the real function.
@@ -5624,16 +5930,19 @@ GenerateEntry(ModuleCompiler &m, const A
 
     // Store the return value in argv[0]
     switch (func.sig().retType().which()) {
       case RetType::Void:
         break;
       case RetType::Signed:
         masm.storeValue(JSVAL_TYPE_INT32, ReturnReg, Address(argv, 0));
         break;
+      case RetType::Float:
+        masm.convertFloatToDouble(ReturnFloatReg, ReturnFloatReg);
+        // Fall through as ReturnFloatReg now contains a Double
       case RetType::Double:
         masm.canonicalizeDouble(ReturnFloatReg);
         masm.storeDouble(ReturnFloatReg, Address(argv, 0));
         break;
     }
 
     // Restore clobbered non-volatile registers of the caller.
     masm.PopRegsInMask(NonVolatileRegs);
@@ -5868,16 +6177,19 @@ GenerateFFIInterpreterExit(ModuleCompile
         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
         masm.unboxInt32(argv, ReturnReg);
         break;
       case RetType::Double:
         masm.call(AsmJSImm_InvokeFromAsmJS_ToNumber);
         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
         masm.loadDouble(argv, ReturnFloatReg);
         break;
+      case RetType::Float:
+        MOZ_ASSUME_UNREACHABLE("Float32 shouldn't be returned from a FFI");
+        break;
     }
 
     // Note: the caller is IonMonkey code which means there are no non-volatile
     // registers to restore.
     masm.freeStack(stackDec);
     masm.ret();
 #else
     const unsigned arrayLength = Max<size_t>(1, exit.sig().args().length());
@@ -5919,16 +6231,19 @@ GenerateFFIInterpreterExit(ModuleCompile
         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
         masm.unboxInt32(argv, ReturnReg);
         break;
       case RetType::Double:
         masm.call(AsmJSImm_InvokeFromAsmJS_ToNumber);
         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
         masm.loadDouble(argv, ReturnFloatReg);
         break;
+      case RetType::Float:
+        MOZ_ASSUME_UNREACHABLE("Float32 shouldn't be returned from a FFI");
+        break;
     }
 
     masm.freeStack(reserveSize + sizeof(int32_t));
     masm.ret();
 #endif
 }
 
 static void
@@ -6123,16 +6438,19 @@ GenerateFFIIonExit(ModuleCompiler &m, co
         break;
       case RetType::Signed:
         masm.convertValueToInt32(JSReturnOperand, ReturnFloatReg, ReturnReg, &oolConvert,
                                  /* -0 check */ false);
         break;
       case RetType::Double:
         masm.convertValueToDouble(JSReturnOperand, ReturnFloatReg, &oolConvert);
         break;
+      case RetType::Float:
+        MOZ_ASSUME_UNREACHABLE("Float shouldn't be returned from a FFI");
+        break;
     }
 
     masm.bind(&done);
     masm.PopRegsInMask(restoreSet);
     masm.ret();
 
     // oolConvert
     if (oolConvert.used()) {
--- a/js/src/jit/AsmJSLink.cpp
+++ b/js/src/jit/AsmJSLink.cpp
@@ -65,37 +65,48 @@ ValidateGlobalVariable(JSContext *cx, co
 {
     JS_ASSERT(global.which() == AsmJSModule::Global::Variable);
 
     void *datum = module.globalVarIndexToGlobalDatum(global.varIndex());
 
     switch (global.varInitKind()) {
       case AsmJSModule::Global::InitConstant: {
         const Value &v = global.varInitConstant();
-        if (v.isInt32())
+        switch (global.varInitCoercion()) {
+          case AsmJS_ToInt32:
             *(int32_t *)datum = v.toInt32();
-        else
+            break;
+          case AsmJS_ToNumber:
             *(double *)datum = v.toDouble();
+            break;
+          case AsmJS_FRound:
+            *(float *)datum = static_cast<float>(v.toDouble());
+            break;
+        }
         break;
       }
       case AsmJSModule::Global::InitImport: {
         RootedPropertyName field(cx, global.varImportField());
         RootedValue v(cx);
         if (!GetDataProperty(cx, importVal, field, &v))
             return false;
 
-        switch (global.varImportCoercion()) {
+        switch (global.varInitCoercion()) {
           case AsmJS_ToInt32:
             if (!ToInt32(cx, v, (int32_t *)datum))
                 return false;
             break;
           case AsmJS_ToNumber:
             if (!ToNumber(cx, v, (double *)datum))
                 return false;
             break;
+          case AsmJS_FRound:
+            if (!RoundFloat32(cx, v, (float *)datum))
+                return false;
+            break;
         }
         break;
       }
     }
 
     return true;
 }
 
@@ -152,16 +163,17 @@ ValidateMathBuiltin(JSContext *cx, AsmJS
       case AsmJSMathBuiltin_floor: native = math_floor; break;
       case AsmJSMathBuiltin_exp: native = math_exp; break;
       case AsmJSMathBuiltin_log: native = math_log; break;
       case AsmJSMathBuiltin_pow: native = js_math_pow; break;
       case AsmJSMathBuiltin_sqrt: native = js_math_sqrt; break;
       case AsmJSMathBuiltin_abs: native = js_math_abs; break;
       case AsmJSMathBuiltin_atan2: native = math_atan2; break;
       case AsmJSMathBuiltin_imul: native = math_imul; break;
+      case AsmJSMathBuiltin_fround: native = math_fround; break;
     }
 
     if (!IsNativeFunction(v, native))
         return LinkFail(cx, "bad Math.* builtin");
 
     return true;
 }
 
@@ -359,16 +371,20 @@ CallAsmJS(JSContext *cx, unsigned argc, 
           case AsmJS_ToInt32:
             if (!ToInt32(cx, v, (int32_t*)&coercedArgs[i]))
                 return false;
             break;
           case AsmJS_ToNumber:
             if (!ToNumber(cx, v, (double*)&coercedArgs[i]))
                 return false;
             break;
+          case AsmJS_FRound:
+            if (!RoundFloat32(cx, v, (float *)&coercedArgs[i]))
+                return false;
+            break;
         }
     }
 
     {
         // Each call into an asm.js module requires an AsmJSActivation record
         // pushed on a stack maintained by the runtime. This record is used for
         // to handle a variety of exceptional things that can happen in asm.js
         // code.
--- a/js/src/jit/AsmJSModule.cpp
+++ b/js/src/jit/AsmJSModule.cpp
@@ -230,38 +230,60 @@ AddressOf(AsmJSImmKind kind, ExclusiveCo
         return FuncCast(__aeabi_idivmod);
       case AsmJSImm_aeabi_uidivmod:
         return FuncCast(__aeabi_uidivmod);
 #endif
       case AsmJSImm_ModD:
         return FuncCast(NumberMod);
       case AsmJSImm_SinD:
         return FuncCast<double (double)>(sin);
+      case AsmJSImm_SinF:
+        return FuncCast<float (float)>(sinf);
       case AsmJSImm_CosD:
         return FuncCast<double (double)>(cos);
+      case AsmJSImm_CosF:
+        return FuncCast<float (float)>(cosf);
       case AsmJSImm_TanD:
         return FuncCast<double (double)>(tan);
+      case AsmJSImm_TanF:
+        return FuncCast<float (float)>(tanf);
       case AsmJSImm_ASinD:
         return FuncCast<double (double)>(asin);
+      case AsmJSImm_ASinF:
+        return FuncCast<float (float)>(asinf);
       case AsmJSImm_ACosD:
         return FuncCast<double (double)>(acos);
+      case AsmJSImm_ACosF:
+        return FuncCast<float (float)>(acosf);
       case AsmJSImm_ATanD:
         return FuncCast<double (double)>(atan);
+      case AsmJSImm_ATanF:
+        return FuncCast<float (float)>(atanf);
       case AsmJSImm_CeilD:
         return FuncCast<double (double)>(ceil);
+      case AsmJSImm_CeilF:
+        return FuncCast<float (float)>(ceilf);
       case AsmJSImm_FloorD:
         return FuncCast<double (double)>(floor);
+      case AsmJSImm_FloorF:
+        return FuncCast<float (float)>(floorf);
       case AsmJSImm_ExpD:
         return FuncCast<double (double)>(exp);
+      case AsmJSImm_ExpF:
+        return FuncCast<float (float)>(expf);
       case AsmJSImm_LogD:
         return FuncCast<double (double)>(log);
+      case AsmJSImm_LogF:
+        return FuncCast<float (float)>(logf);
       case AsmJSImm_PowD:
         return FuncCast(ecmaPow);
       case AsmJSImm_ATan2D:
         return FuncCast(ecmaAtan2);
+      case AsmJSImm_Invalid:
+        break;
     }
 
     MOZ_ASSUME_UNREACHABLE("Bad AsmJSImmKind");
     return nullptr;
 }
 
 void
 AsmJSModule::staticallyLink(const AsmJSStaticLinkData &linkData, ExclusiveContext *cx)
--- a/js/src/jit/AsmJSModule.h
+++ b/js/src/jit/AsmJSModule.h
@@ -24,27 +24,29 @@
 #include "vm/TypedArrayObject.h"
 
 namespace js {
 
 // These EcmaScript-defined coercions form the basis of the asm.js type system.
 enum AsmJSCoercion
 {
     AsmJS_ToInt32,
-    AsmJS_ToNumber
+    AsmJS_ToNumber,
+    AsmJS_FRound
 };
 
 // The asm.js spec recognizes this set of builtin Math functions.
 enum AsmJSMathBuiltin
 {
     AsmJSMathBuiltin_sin, AsmJSMathBuiltin_cos, AsmJSMathBuiltin_tan,
     AsmJSMathBuiltin_asin, AsmJSMathBuiltin_acos, AsmJSMathBuiltin_atan,
     AsmJSMathBuiltin_ceil, AsmJSMathBuiltin_floor, AsmJSMathBuiltin_exp,
     AsmJSMathBuiltin_log, AsmJSMathBuiltin_pow, AsmJSMathBuiltin_sqrt,
-    AsmJSMathBuiltin_abs, AsmJSMathBuiltin_atan2, AsmJSMathBuiltin_imul
+    AsmJSMathBuiltin_abs, AsmJSMathBuiltin_atan2, AsmJSMathBuiltin_imul,
+    AsmJSMathBuiltin_fround
 };
 
 // Static-link data is used to patch a module either after it has been
 // compiled or deserialized with various absolute addresses (of code or
 // data in the process) or relative addresses (of code or data in the same
 // AsmJSModule). Since AsmJSStaticLinkData can be serialized alongside the
 // AsmJSModule and isn't needed after compilation/deserialization, it
 // doesn't need to be stored in the AsmJSModule.
@@ -100,19 +102,19 @@ class AsmJSModule
 
       private:
         struct Pod {
             Which which_;
             union {
                 struct {
                     uint32_t index_;
                     VarInitKind initKind_;
+                    AsmJSCoercion coercion_;
                     union {
                         Value constant_; // will only contain int32/double
-                        AsmJSCoercion coercion_;
                     } init;
                 } var;
                 uint32_t ffiIndex_;
                 ArrayBufferView::ViewType viewType_;
                 AsmJSMathBuiltin mathBuiltin_;
                 double constantValue_;
             } u;
         } pod;
@@ -146,20 +148,19 @@ class AsmJSModule
             JS_ASSERT(pod.which_ == Variable);
             return pod.u.var.initKind_;
         }
         const Value &varInitConstant() const {
             JS_ASSERT(pod.which_ == Variable);
             JS_ASSERT(pod.u.var.initKind_ == InitConstant);
             return pod.u.var.init.constant_;
         }
-        AsmJSCoercion varImportCoercion() const {
+        AsmJSCoercion varInitCoercion() const {
             JS_ASSERT(pod.which_ == Variable);
-            JS_ASSERT(pod.u.var.initKind_ == InitImport);
-            return pod.u.var.init.coercion_;
+            return pod.u.var.coercion_;
         }
         PropertyName *varImportField() const {
             JS_ASSERT(pod.which_ == Variable);
             JS_ASSERT(pod.u.var.initKind_ == InitImport);
             return name_;
         }
         PropertyName *ffiField() const {
             JS_ASSERT(pod.which_ == FFI);
@@ -450,31 +451,32 @@ class AsmJSModule
     void initCharsEnd(uint32_t charsEnd) {
         JS_ASSERT(charsEnd >= charsBegin_);
         pod.charsLength_ = charsEnd - charsBegin_;
     }
     uint32_t charsEnd() const {
         return charsBegin_ + pod.charsLength_;
     }
 
-    bool addGlobalVarInitConstant(const Value &v, uint32_t *globalIndex) {
+    bool addGlobalVarInitConstant(const Value &v, AsmJSCoercion coercion, uint32_t *globalIndex) {
         JS_ASSERT(pod.funcPtrTableAndExitBytes_ == 0);
         if (pod.numGlobalVars_ == UINT32_MAX)
             return false;
         Global g(Global::Variable, nullptr);
         g.pod.u.var.initKind_ = Global::InitConstant;
         g.pod.u.var.init.constant_ = v;
+        g.pod.u.var.coercion_ = coercion;
         g.pod.u.var.index_ = *globalIndex = pod.numGlobalVars_++;
         return globals_.append(g);
     }
     bool addGlobalVarImport(PropertyName *name, AsmJSCoercion coercion, uint32_t *globalIndex) {
         JS_ASSERT(pod.funcPtrTableAndExitBytes_ == 0);
         Global g(Global::Variable, name);
         g.pod.u.var.initKind_ = Global::InitImport;
-        g.pod.u.var.init.coercion_ = coercion;
+        g.pod.u.var.coercion_ = coercion;
         g.pod.u.var.index_ = *globalIndex = pod.numGlobalVars_++;
         return globals_.append(g);
     }
     bool addFFI(PropertyName *field, uint32_t *ffiIndex) {
         if (pod.numFFIs_ == UINT32_MAX)
             return false;
         Global g(Global::FFI, field);
         g.pod.u.ffiIndex_ = *ffiIndex = pod.numFFIs_++;
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -3295,29 +3295,29 @@ LIRGenerator::visitAsmJSLoadFFIFunc(MAsm
 
 bool
 LIRGenerator::visitAsmJSParameter(MAsmJSParameter *ins)
 {
     ABIArg abi = ins->abi();
     if (abi.argInRegister())
         return defineFixed(new(alloc()) LAsmJSParameter, ins, LAllocation(abi.reg()));
 
-    JS_ASSERT(ins->type() == MIRType_Int32 || ins->type() == MIRType_Double);
+    JS_ASSERT(IsNumberType(ins->type()));
     LAllocation::Kind argKind = ins->type() == MIRType_Int32
                                 ? LAllocation::INT_ARGUMENT
                                 : LAllocation::DOUBLE_ARGUMENT;
     return defineFixed(new(alloc()) LAsmJSParameter, ins, LArgument(argKind, abi.offsetFromArgBase()));
 }
 
 bool
 LIRGenerator::visitAsmJSReturn(MAsmJSReturn *ins)
 {
     MDefinition *rval = ins->getOperand(0);
     LAsmJSReturn *lir = new(alloc()) LAsmJSReturn;
-    if (rval->type() == MIRType_Double)
+    if (IsFloatingPointType(rval->type()))
         lir->setOperand(0, useFixed(rval, ReturnFloatReg));
     else if (rval->type() == MIRType_Int32)
         lir->setOperand(0, useFixed(rval, ReturnReg));
     else
         MOZ_ASSUME_UNREACHABLE("Unexpected asm.js return type");
     return add(lir);
 }
 
@@ -3325,17 +3325,17 @@ bool
 LIRGenerator::visitAsmJSVoidReturn(MAsmJSVoidReturn *ins)
 {
     return add(new(alloc()) LAsmJSVoidReturn);
 }
 
 bool
 LIRGenerator::visitAsmJSPassStackArg(MAsmJSPassStackArg *ins)
 {
-    if (ins->arg()->type() == MIRType_Double) {
+    if (IsFloatingPointType(ins->arg()->type())) {
         JS_ASSERT(!ins->arg()->isEmittedAtUses());
         return add(new(alloc()) LAsmJSPassStackArg(useRegisterAtStart(ins->arg())), ins);
     }
 
     return add(new(alloc()) LAsmJSPassStackArg(useRegisterOrConstantAtStart(ins->arg())), ins);
 }
 
 bool
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -9236,17 +9236,19 @@ class MAsmJSHeapAccess
 };
 
 class MAsmJSLoadHeap : public MUnaryInstruction, public MAsmJSHeapAccess
 {
     MAsmJSLoadHeap(ArrayBufferView::ViewType vt, MDefinition *ptr)
       : MUnaryInstruction(ptr), MAsmJSHeapAccess(vt, false)
     {
         setMovable();
-        if (vt == ArrayBufferView::TYPE_FLOAT32 || vt == ArrayBufferView::TYPE_FLOAT64)
+        if (vt == ArrayBufferView::TYPE_FLOAT32)
+            setResultType(MIRType_Float32);
+        else if (vt == ArrayBufferView::TYPE_FLOAT64)
             setResultType(MIRType_Double);
         else
             setResultType(MIRType_Int32);
     }
 
   public:
     INSTRUCTION_HEADER(AsmJSLoadHeap);
 
@@ -9286,17 +9288,17 @@ class MAsmJSStoreHeap : public MBinaryIn
     }
 };
 
 class MAsmJSLoadGlobalVar : public MNullaryInstruction
 {
     MAsmJSLoadGlobalVar(MIRType type, unsigned globalDataOffset, bool isConstant)
       : globalDataOffset_(globalDataOffset), isConstant_(isConstant)
     {
-        JS_ASSERT(type == MIRType_Int32 || type == MIRType_Double);
+        JS_ASSERT(IsNumberType(type));
         setResultType(type);
         setMovable();
     }
 
     unsigned globalDataOffset_;
     bool isConstant_;
 
   public:
--- a/js/src/jit/arm/Assembler-arm.cpp
+++ b/js/src/jit/arm/Assembler-arm.cpp
@@ -40,16 +40,17 @@ ABIArgGenerator::next(MIRType type)
         if (intRegIndex_ == NumIntArgRegs) {
             current_ = ABIArg(stackOffset_);
             stackOffset_ += sizeof(uint32_t);
             break;
         }
         current_ = ABIArg(Register::FromCode(intRegIndex_));
         intRegIndex_++;
         break;
+      case MIRType_Float32:
       case MIRType_Double:
         if (floatRegIndex_ == NumFloatArgRegs) {
             static const int align = sizeof(double) - 1;
             stackOffset_ = (stackOffset_ + align) & ~align;
             current_ = ABIArg(stackOffset_);
             stackOffset_ += sizeof(uint64_t);
             break;
         }
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -1294,27 +1294,22 @@ class Assembler
     {
     }
 
     // We need to wait until an AutoIonContextAlloc is created by the
     // IonMacroAssembler, before allocating any space.
     void initWithAllocator() {
         m_buffer.initWithAllocator();
 
-        // Note that the sizes for the double pools are set to 1020 rather than 1024 to
-        // work around a rare edge case that would otherwise bail out - which is not
-        // possible for Asm.js code and causes a compilation failure.  See the comment at
-        // the fail_bail call within IonAssemberBufferWithConstantPools.h: finishPool().
-
         // Set up the backwards double region
-        new (&pools_[2]) Pool (1020, 8, 4, 8, 8, m_buffer.LifoAlloc_, true);
+        new (&pools_[2]) Pool (1024, 8, 4, 8, 8, m_buffer.LifoAlloc_, true);
         // Set up the backwards 32 bit region
         new (&pools_[3]) Pool (4096, 4, 4, 8, 4, m_buffer.LifoAlloc_, true, true);
         // Set up the forwards double region
-        new (doublePool) Pool (1020, 8, 4, 8, 8, m_buffer.LifoAlloc_, false, false, &pools_[2]);
+        new (doublePool) Pool (1024, 8, 4, 8, 8, m_buffer.LifoAlloc_, false, false, &pools_[2]);
         // Set up the forwards 32 bit region
         new (int32Pool) Pool (4096, 4, 4, 8, 4, m_buffer.LifoAlloc_, false, true, &pools_[3]);
         for (int i = 0; i < 4; i++) {
             if (pools_[i].poolData == nullptr) {
                 m_buffer.fail_oom();
                 return;
             }
         }
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -1964,56 +1964,52 @@ CodeGeneratorARM::visitAsmJSLoadHeap(LAs
     const LAllocation *ptr = ins->ptr();
 
     if (ptr->isConstant()) {
         JS_ASSERT(mir->skipBoundsCheck());
         int32_t ptrImm = ptr->toConstant()->toInt32();
         JS_ASSERT(ptrImm >= 0);
         if (isFloat) {
             VFPRegister vd(ToFloatRegister(ins->output()));
-            if (size == 32) {
+            if (size == 32)
                 masm.ma_vldr(Operand(HeapReg, ptrImm), vd.singleOverlay(), Assembler::Always);
-                masm.as_vcvt(vd, vd.singleOverlay(), false, Assembler::Always);
-            } else {
+            else
                 masm.ma_vldr(Operand(HeapReg, ptrImm), vd, Assembler::Always);
-            }
         }  else {
             masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, Imm32(ptrImm),
                                   ToRegister(ins->output()), Offset, Assembler::Always);
         }
         return true;
     }
 
     Register ptrReg = ToRegister(ptr);
 
     if (mir->skipBoundsCheck()) {
         if (isFloat) {
             VFPRegister vd(ToFloatRegister(ins->output()));
-            if (size == 32) {
+            if (size == 32)
                 masm.ma_vldr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Always);
-                masm.as_vcvt(vd, vd.singleOverlay(), false, Assembler::Always);
-            } else {
+            else
                 masm.ma_vldr(vd, HeapReg, ptrReg, 0, Assembler::Always);
-            }
         } else {
             masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, ptrReg,
                                   ToRegister(ins->output()), Offset, Assembler::Always);
         }
         return true;
     }
 
     BufferOffset bo = masm.ma_BoundsCheck(ptrReg);
     if (isFloat) {
         FloatRegister dst = ToFloatRegister(ins->output());
-        masm.ma_vmov(NANReg, dst, Assembler::AboveOrEqual);
         VFPRegister vd(dst);
         if (size == 32) {
+            masm.convertDoubleToFloat(NANReg, dst, Assembler::AboveOrEqual);
             masm.ma_vldr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Below);
-            masm.as_vcvt(vd, vd.singleOverlay(), false, Assembler::Below);
         } else {
+            masm.ma_vmov(NANReg, dst, Assembler::AboveOrEqual);
             masm.ma_vldr(vd, HeapReg, ptrReg, 0, Assembler::Below);
         }
     } else {
         Register d = ToRegister(ins->output());
         masm.ma_mov(Imm32(0), d, NoSetCond, Assembler::AboveOrEqual);
         masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, ptrReg, d, Offset, Assembler::Below);
     }
     return gen->noteHeapAccess(AsmJSHeapAccess(bo.getOffset()));
@@ -2039,51 +2035,49 @@ CodeGeneratorARM::visitAsmJSStoreHeap(LA
     }
     const LAllocation *ptr = ins->ptr();
     if (ptr->isConstant()) {
         JS_ASSERT(mir->skipBoundsCheck());
         int32_t ptrImm = ptr->toConstant()->toInt32();
         JS_ASSERT(ptrImm >= 0);
         if (isFloat) {
             VFPRegister vd(ToFloatRegister(ins->value()));
-            if (size == 32) {
-                masm.as_vcvt(VFPRegister(ScratchFloatReg).singleOverlay(), vd, false, Assembler::Always);
-                masm.ma_vstr(VFPRegister(ScratchFloatReg).singleOverlay(), Operand(HeapReg, ptrImm), Assembler::Always);
-            } else {
+            if (size == 32)
+                masm.ma_vstr(vd.singleOverlay(), Operand(HeapReg, ptrImm), Assembler::Always);
+            else
                 masm.ma_vstr(vd, Operand(HeapReg, ptrImm), Assembler::Always);
-            }
         } else {
             masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, Imm32(ptrImm),
                                   ToRegister(ins->value()), Offset, Assembler::Always);
         }
         return true;
     }
 
     Register ptrReg = ToRegister(ptr);
 
     if (mir->skipBoundsCheck()) {
         Register ptrReg = ToRegister(ptr);
         if (isFloat) {
             VFPRegister vd(ToFloatRegister(ins->value()));
             if (size == 32)
-                masm.storeFloat(vd, HeapReg, ptrReg, Assembler::Always);
+                masm.ma_vstr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Always);
             else
                 masm.ma_vstr(vd, HeapReg, ptrReg, 0, Assembler::Always);
         } else {
             masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg,
                                   ToRegister(ins->value()), Offset, Assembler::Always);
         }
         return true;
     }
 
     BufferOffset bo = masm.ma_BoundsCheck(ptrReg);
     if (isFloat) {
         VFPRegister vd(ToFloatRegister(ins->value()));
         if (size == 32)
-            masm.storeFloat(vd, HeapReg, ptrReg, Assembler::Below);
+            masm.ma_vstr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Below);
         else
             masm.ma_vstr(vd, HeapReg, ptrReg, 0, Assembler::Below);
     } else {
         masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg,
                               ToRegister(ins->value()), Offset, Assembler::Below);
     }
     return gen->noteHeapAccess(AsmJSHeapAccess(bo.getOffset()));
 }
@@ -2224,35 +2218,43 @@ CodeGeneratorARM::visitEffectiveAddress(
     return true;
 }
 
 bool
 CodeGeneratorARM::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins)
 {
     const MAsmJSLoadGlobalVar *mir = ins->mir();
     unsigned addr = mir->globalDataOffset();
-    if (mir->type() == MIRType_Int32)
+    if (mir->type() == MIRType_Int32) {
         masm.ma_dtr(IsLoad, GlobalReg, Imm32(addr), ToRegister(ins->output()));
-    else
+    } else if (mir->type() == MIRType_Float32) {
+        VFPRegister vd(ToFloatRegister(ins->output()));
+        masm.ma_vldr(Operand(GlobalReg, addr), vd.singleOverlay());
+    } else {
         masm.ma_vldr(Operand(GlobalReg, addr), ToFloatRegister(ins->output()));
+    }
     return true;
 }
 
 bool
 CodeGeneratorARM::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins)
 {
     const MAsmJSStoreGlobalVar *mir = ins->mir();
 
     MIRType type = mir->value()->type();
-    JS_ASSERT(type == MIRType_Int32 || type == MIRType_Double);
+    JS_ASSERT(IsNumberType(type));
     unsigned addr = mir->globalDataOffset();
-    if (mir->value()->type() == MIRType_Int32)
+    if (mir->value()->type() == MIRType_Int32) {
         masm.ma_dtr(IsStore, GlobalReg, Imm32(addr), ToRegister(ins->value()));
-    else
+    } else if (mir->value()->type() == MIRType_Float32) {
+        VFPRegister vd(ToFloatRegister(ins->value()));
+        masm.ma_vstr(vd.singleOverlay(), Operand(GlobalReg, addr));
+    } else {
         masm.ma_vstr(ToFloatRegister(ins->value()), Operand(GlobalReg, addr));
+    }
     return true;
 }
 
 bool
 CodeGeneratorARM::visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr *ins)
 {
     const MAsmJSLoadFuncPtr *mir = ins->mir();
 
--- a/js/src/jit/arm/CodeGenerator-arm.h
+++ b/js/src/jit/arm/CodeGenerator-arm.h
@@ -171,20 +171,29 @@ class CodeGeneratorARM : public CodeGene
     bool visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr *ins);
     bool visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc *ins);
 
     bool visitAsmJSPassStackArg(LAsmJSPassStackArg *ins);
 
     bool generateInvalidateEpilogue();
   protected:
     void postAsmJSCall(LAsmJSCall *lir) {
-#if  !defined(JS_CPU_ARM_HARDFP)
+#ifndef JS_CPU_ARM_HARDFP
         if (lir->mir()->callee().which() == MAsmJSCall::Callee::Builtin) {
-            if (lir->mir()->type() == MIRType_Double)
+            switch (lir->mir()->type()) {
+              case MIRType_Double:
                 masm.ma_vxfer(r0, r1, d0);
+                break;
+              case MIRType_Float32:
+                masm.as_vxfer(r0, InvalidReg, VFPRegister(d0).singleOverlay(),
+                              Assembler::CoreToFloat);
+                break;
+              default:
+                break;
+            }
         }
 #endif
     }
 
     bool visitEffectiveAddress(LEffectiveAddress *ins);
     bool visitUDiv(LUDiv *ins);
     bool visitUMod(LUMod *ins);
     bool visitSoftUDivOrMod(LSoftUDivOrMod *ins);
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -407,16 +407,19 @@ LIRGeneratorARM::lowerUrshD(MUrsh *mir)
 }
 
 bool
 LIRGeneratorARM::visitAsmJSNeg(MAsmJSNeg *ins)
 {
     if (ins->type() == MIRType_Int32)
         return define(new(alloc()) LNegI(useRegisterAtStart(ins->input())), ins);
 
+    if(ins->type() == MIRType_Float32)
+        return define(new(alloc()) LNegF(useRegisterAtStart(ins->input())), ins);
+
     JS_ASSERT(ins->type() == MIRType_Double);
     return define(new(alloc()) LNegD(useRegisterAtStart(ins->input())), ins);
 }
 
 bool
 LIRGeneratorARM::lowerUDiv(MDiv *div)
 {
     MDefinition *lhs = div->getOperand(0);
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -69,19 +69,20 @@ void
 MacroAssemblerARM::convertUInt32ToFloat32(const Register &src, const FloatRegister &dest_)
 {
     // direct conversions aren't possible.
     VFPRegister dest = VFPRegister(dest_);
     as_vxfer(src, InvalidReg, dest.uintOverlay(), CoreToFloat);
     as_vcvt(VFPRegister(dest).singleOverlay(), dest.uintOverlay());
 }
 
-void MacroAssemblerARM::convertDoubleToFloat(const FloatRegister &src, const FloatRegister &dest)
-{
-    as_vcvt(VFPRegister(dest).singleOverlay(), VFPRegister(src));
+void MacroAssemblerARM::convertDoubleToFloat(const FloatRegister &src, const FloatRegister &dest,
+                                             Condition c)
+{
+    as_vcvt(VFPRegister(dest).singleOverlay(), VFPRegister(src), false, c);
 }
 
 // there are two options for implementing emitTruncateDouble.
 // 1) convert the floating point value to an integer, if it did not fit,
 //        then it was clamped to INT_MIN/INT_MAX, and we can test it.
 //        NOTE: if the value really was supposed to be INT_MAX / INT_MIN
 //        then it will be wrong.
 // 2) convert the floating point value to an integer, if it did not fit,
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -45,17 +45,18 @@ class MacroAssemblerARM : public Assembl
         secondScratchReg_ = reg;
     }
 
     void convertBoolToInt32(Register source, Register dest);
     void convertInt32ToDouble(const Register &src, const FloatRegister &dest);
     void convertInt32ToDouble(const Address &src, FloatRegister dest);
     void convertUInt32ToFloat32(const Register &src, const FloatRegister &dest);
     void convertUInt32ToDouble(const Register &src, const FloatRegister &dest);
-    void convertDoubleToFloat(const FloatRegister &src, const FloatRegister &dest);
+    void convertDoubleToFloat(const FloatRegister &src, const FloatRegister &dest,
+                              Condition c = Always);
     void branchTruncateDouble(const FloatRegister &src, const Register &dest, Label *fail);
     void convertDoubleToInt32(const FloatRegister &src, const Register &dest, Label *fail,
                               bool negativeZeroCheck = true);
     void convertFloat32ToInt32(const FloatRegister &src, const Register &dest, Label *fail,
                                bool negativeZeroCheck = true);
 
     void convertFloatToDouble(const FloatRegister &src, const FloatRegister &dest);
     void branchTruncateFloat32(const FloatRegister &src, const Register &dest, Label *fail);
@@ -1489,21 +1490,16 @@ class MacroAssemblerARMCompat : public M
     void ma_storeImm(Imm32 c, const Operand &dest) {
         ma_mov(c, lr);
         ma_str(lr, dest);
     }
     BufferOffset ma_BoundsCheck(Register bounded) {
         return as_cmp(bounded, Imm8(0));
     }
 
-    void storeFloat(VFPRegister src, Register base, Register index, Condition cond) {
-        as_vcvt(VFPRegister(ScratchFloatReg).singleOverlay(), src, false, cond);
-        ma_vstr(VFPRegister(ScratchFloatReg).singleOverlay(), base, index, 0, cond);
-
-    }
     void moveFloat(FloatRegister src, FloatRegister dest) {
         as_vmov(VFPRegister(src).singleOverlay(), VFPRegister(dest).singleOverlay());
     }
 };
 
 typedef MacroAssemblerARMCompat MacroAssemblerSpecific;
 
 } // namespace jit
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -686,27 +686,38 @@ enum AsmJSImmKind
     AsmJSImm_EnableActivationFromAsmJS,
     AsmJSImm_DisableActivationFromAsmJS,
 #if defined(JS_CPU_ARM)
     AsmJSImm_aeabi_idivmod,
     AsmJSImm_aeabi_uidivmod,
 #endif
     AsmJSImm_ModD,
     AsmJSImm_SinD,
+    AsmJSImm_SinF,
     AsmJSImm_CosD,
+    AsmJSImm_CosF,
     AsmJSImm_TanD,
+    AsmJSImm_TanF,
     AsmJSImm_ASinD,
+    AsmJSImm_ASinF,
     AsmJSImm_ACosD,
+    AsmJSImm_ACosF,
     AsmJSImm_ATanD,
+    AsmJSImm_ATanF,
     AsmJSImm_CeilD,
+    AsmJSImm_CeilF,
     AsmJSImm_FloorD,
+    AsmJSImm_FloorF,
     AsmJSImm_ExpD,
+    AsmJSImm_ExpF,
     AsmJSImm_LogD,
+    AsmJSImm_LogF,
     AsmJSImm_PowD,
-    AsmJSImm_ATan2D
+    AsmJSImm_ATan2D,
+    AsmJSImm_Invalid
 };
 
 // Pointer to be embedded as an immediate in asm.js code.
 class AsmJSImmPtr
 {
     AsmJSImmKind kind_;
   public:
     AsmJSImmKind kind() const { return kind_; }
--- a/js/src/jit/shared/Lowering-x86-shared.cpp
+++ b/js/src/jit/shared/Lowering-x86-shared.cpp
@@ -222,16 +222,19 @@ LIRGeneratorX86Shared::lowerModI(MMod *m
 }
 
 bool
 LIRGeneratorX86Shared::visitAsmJSNeg(MAsmJSNeg *ins)
 {
     if (ins->type() == MIRType_Int32)
         return defineReuseInput(new(alloc()) LNegI(useRegisterAtStart(ins->input())), ins, 0);
 
+    if (ins->type() == MIRType_Float32)
+        return defineReuseInput(new(alloc()) LNegF(useRegisterAtStart(ins->input())), ins, 0);
+
     JS_ASSERT(ins->type() == MIRType_Double);
     return defineReuseInput(new(alloc()) LNegD(useRegisterAtStart(ins->input())), ins, 0);
 }
 
 bool
 LIRGeneratorX86Shared::lowerUDiv(MDiv *div)
 {
     // Optimize x/x. The comments in lowerDivI apply here as well.
--- a/js/src/jit/x64/Assembler-x64.cpp
+++ b/js/src/jit/x64/Assembler-x64.cpp
@@ -34,16 +34,17 @@ ABIArgGenerator::next(MIRType type)
         stackOffset_ += sizeof(uint64_t);
         return current_;
     }
     switch (type) {
       case MIRType_Int32:
       case MIRType_Pointer:
         current_ = ABIArg(IntArgRegs[regIndex_++]);
         break;
+      case MIRType_Float32:
       case MIRType_Double:
         current_ = ABIArg(FloatArgRegs[regIndex_++]);
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Unexpected argument type");
     }
     return current_;
 #else
@@ -53,16 +54,17 @@ ABIArgGenerator::next(MIRType type)
         if (intRegIndex_ == NumIntArgRegs) {
             current_ = ABIArg(stackOffset_);
             stackOffset_ += sizeof(uint64_t);
             break;
         }
         current_ = ABIArg(IntArgRegs[intRegIndex_++]);
         break;
       case MIRType_Double:
+      case MIRType_Float32:
         if (floatRegIndex_ == NumFloatArgRegs) {
             current_ = ABIArg(stackOffset_);
             stackOffset_ += sizeof(uint64_t);
             break;
         }
         current_ = ABIArg(FloatArgRegs[floatRegIndex_++]);
         break;
       default:
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -404,33 +404,25 @@ CodeGeneratorX64::visitAsmJSLoadHeap(LAs
         // Note only a positive index is accepted here because a negative offset would
         // not wrap back into the protected area reserved for the heap.
         JS_ASSERT(ptrImm >= 0);
         srcAddr = Operand(HeapReg, ptrImm);
     } else {
         srcAddr = Operand(HeapReg, ToRegister(ptr), TimesOne);
     }
 
-    if (vt == ArrayBufferView::TYPE_FLOAT32) {
-        FloatRegister dest = ToFloatRegister(ins->output());
-        uint32_t before = masm.size();
-        masm.loadFloat(srcAddr, dest);
-        uint32_t after = masm.size();
-        masm.cvtss2sd(dest, dest);
-        return skipNote || gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(ins->output())));
-    }
-
     uint32_t before = masm.size();
     switch (vt) {
       case ArrayBufferView::TYPE_INT8:    masm.movsbl(srcAddr, ToRegister(ins->output())); break;
       case ArrayBufferView::TYPE_UINT8:   masm.movzbl(srcAddr, ToRegister(ins->output())); break;
       case ArrayBufferView::TYPE_INT16:   masm.movswl(srcAddr, ToRegister(ins->output())); break;
       case ArrayBufferView::TYPE_UINT16:  masm.movzwl(srcAddr, ToRegister(ins->output())); break;
       case ArrayBufferView::TYPE_INT32:
       case ArrayBufferView::TYPE_UINT32:  masm.movl(srcAddr, ToRegister(ins->output())); break;
+      case ArrayBufferView::TYPE_FLOAT32: masm.loadFloat(srcAddr, ToFloatRegister(ins->output())); break;
       case ArrayBufferView::TYPE_FLOAT64: masm.loadDouble(srcAddr, ToFloatRegister(ins->output())); break;
       default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
     }
     uint32_t after = masm.size();
     return skipNote || gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(ins->output())));
 }
 
 bool
@@ -448,24 +440,16 @@ CodeGeneratorX64::visitAsmJSStoreHeap(LA
         // Note only a positive index is accepted here because a negative offset would
         // not wrap back into the protected area reserved for the heap.
         JS_ASSERT(ptrImm >= 0);
         dstAddr = Operand(HeapReg, ptrImm);
     } else {
         dstAddr = Operand(HeapReg, ToRegister(ins->ptr()), TimesOne);
     }
 
-    if (vt == ArrayBufferView::TYPE_FLOAT32) {
-        masm.convertDoubleToFloat(ToFloatRegister(ins->value()), ScratchFloatReg);
-        uint32_t before = masm.size();
-        masm.storeFloat(ScratchFloatReg, dstAddr);
-        uint32_t after = masm.size();
-        return skipNote || gen->noteHeapAccess(AsmJSHeapAccess(before, after));
-    }
-
     uint32_t before = masm.size();
     if (ins->value()->isConstant()) {
         switch (vt) {
           case ArrayBufferView::TYPE_INT8:
           case ArrayBufferView::TYPE_UINT8:   masm.movb(Imm32(ToInt32(ins->value())), dstAddr); break;
           case ArrayBufferView::TYPE_INT16:
           case ArrayBufferView::TYPE_UINT16:  masm.movw(Imm32(ToInt32(ins->value())), dstAddr); break;
           case ArrayBufferView::TYPE_INT32:
@@ -475,16 +459,17 @@ CodeGeneratorX64::visitAsmJSStoreHeap(LA
     } else {
         switch (vt) {
           case ArrayBufferView::TYPE_INT8:
           case ArrayBufferView::TYPE_UINT8:   masm.movb(ToRegister(ins->value()), dstAddr); break;
           case ArrayBufferView::TYPE_INT16:
           case ArrayBufferView::TYPE_UINT16:  masm.movw(ToRegister(ins->value()), dstAddr); break;
           case ArrayBufferView::TYPE_INT32:
           case ArrayBufferView::TYPE_UINT32:  masm.movl(ToRegister(ins->value()), dstAddr); break;
+          case ArrayBufferView::TYPE_FLOAT32: masm.storeFloat(ToFloatRegister(ins->value()), dstAddr); break;
           case ArrayBufferView::TYPE_FLOAT64: masm.storeDouble(ToFloatRegister(ins->value()), dstAddr); break;
           default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
         }
     }
     uint32_t after = masm.size();
     return skipNote || gen->noteHeapAccess(AsmJSHeapAccess(before, after));
 }
 
@@ -503,17 +488,17 @@ CodeGeneratorX64::visitAsmJSLoadGlobalVa
 }
 
 bool
 CodeGeneratorX64::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins)
 {
     MAsmJSStoreGlobalVar *mir = ins->mir();
 
     MIRType type = mir->value()->type();
-    JS_ASSERT(type == MIRType_Int32 || type == MIRType_Double);
+    JS_ASSERT(IsNumberType(type));
 
     CodeOffsetLabel label;
     if (type == MIRType_Int32)
         label = masm.storeRipRelativeInt32(ToRegister(ins->value()));
     else
         label = masm.storeRipRelativeDouble(ToFloatRegister(ins->value()));
 
     return gen->noteGlobalAccess(label.offset(), mir->globalDataOffset());
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -273,16 +273,26 @@ class Assembler : public AssemblerX86Sha
           case Operand::MEM_REG_DISP:
             masm.fld32_m(dest.disp(), dest.base());
             break;
           default:
             MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
         }
     }
 
+    void fstp32(const Operand &src) {
+        switch (src.kind()) {
+          case Operand::MEM_REG_DISP:
+            masm.fstp32_m(src.disp(), src.base());
+            break;
+          default:
+            MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
+        }
+    }
+
     void cmpl(const Register src, ImmWord ptr) {
         masm.cmpl_ir(ptr.value, src.code());
     }
     void cmpl(const Register src, ImmPtr imm) {
         cmpl(src, ImmWord(uintptr_t(imm.value)));
     }
     void cmpl(const Register src, ImmGCPtr ptr) {
         masm.cmpl_ir(ptr.value, src.code());
--- a/js/src/jit/x86/CodeGenerator-x86.cpp
+++ b/js/src/jit/x86/CodeGenerator-x86.cpp
@@ -405,94 +405,87 @@ CodeGeneratorX86::visitAsmJSUInt32ToFloa
     return true;
 }
 
 // Load a NaN or zero into a register for an out of bounds AsmJS or static
 // typed array load.
 class jit::OutOfLineLoadTypedArrayOutOfBounds : public OutOfLineCodeBase<CodeGeneratorX86>
 {
     AnyRegister dest_;
+    bool isFloat32Load_;
   public:
-    OutOfLineLoadTypedArrayOutOfBounds(AnyRegister dest) : dest_(dest) {}
+    OutOfLineLoadTypedArrayOutOfBounds(AnyRegister dest, bool isFloat32Load)
+      : dest_(dest), isFloat32Load_(isFloat32Load)
+    {}
+
     const AnyRegister &dest() const { return dest_; }
+    bool isFloat32Load() const { return isFloat32Load_; }
     bool accept(CodeGeneratorX86 *codegen) { return codegen->visitOutOfLineLoadTypedArrayOutOfBounds(this); }
 };
 
 template<typename T>
 void
-CodeGeneratorX86::loadNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr,
-                                                const LDefinition *out)
+CodeGeneratorX86::loadViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr,
+                                      const LDefinition *out)
 {
     switch (vt) {
       case ArrayBufferView::TYPE_INT8:    masm.movsblWithPatch(srcAddr, ToRegister(out)); break;
       case ArrayBufferView::TYPE_UINT8_CLAMPED:
       case ArrayBufferView::TYPE_UINT8:   masm.movzblWithPatch(srcAddr, ToRegister(out)); break;
       case ArrayBufferView::TYPE_INT16:   masm.movswlWithPatch(srcAddr, ToRegister(out)); break;
       case ArrayBufferView::TYPE_UINT16:  masm.movzwlWithPatch(srcAddr, ToRegister(out)); break;
       case ArrayBufferView::TYPE_INT32:
       case ArrayBufferView::TYPE_UINT32:  masm.movlWithPatch(srcAddr, ToRegister(out)); break;
+      case ArrayBufferView::TYPE_FLOAT32: masm.movssWithPatch(srcAddr, ToFloatRegister(out)); break;
       case ArrayBufferView::TYPE_FLOAT64: masm.movsdWithPatch(srcAddr, ToFloatRegister(out)); break;
       default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
     }
 }
 
 template<typename T>
 bool
-CodeGeneratorX86::loadViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr,
-                                      const LDefinition *out)
+CodeGeneratorX86::loadAndNoteViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr,
+                                             const LDefinition *out)
 {
-    if (vt == ArrayBufferView::TYPE_FLOAT32) {
-        FloatRegister dest = ToFloatRegister(out);
-        uint32_t before = masm.size();
-        masm.movssWithPatch(srcAddr, dest);
-        uint32_t after = masm.size();
-        masm.cvtss2sd(dest, dest);
-        return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, AnyRegister(dest)));
-    }
     uint32_t before = masm.size();
-    loadNonFloat32ViewTypeElement(vt, srcAddr, out);
+    loadViewTypeElement(vt, srcAddr, out);
     uint32_t after = masm.size();
     return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(out)));
 }
 
 bool
 CodeGeneratorX86::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic *ins)
 {
     const MLoadTypedArrayElementStatic *mir = ins->mir();
     ArrayBufferView::ViewType vt = mir->viewType();
+    JS_ASSERT_IF(vt == ArrayBufferView::TYPE_FLOAT32, mir->type() == MIRType_Float32);
 
     Register ptr = ToRegister(ins->ptr());
     const LDefinition *out = ins->output();
 
     OutOfLineLoadTypedArrayOutOfBounds *ool = nullptr;
+    bool isFloat32Load = (vt == ArrayBufferView::TYPE_FLOAT32);
     if (!mir->fallible()) {
-        ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out));
+        ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), isFloat32Load);
         if (!addOutOfLineCode(ool))
             return false;
     }
 
     masm.cmpl(ptr, Imm32(mir->length()));
     if (ool)
         masm.j(Assembler::AboveOrEqual, ool->entry());
     else if (!bailoutIf(Assembler::AboveOrEqual, ins->snapshot()))
         return false;
 
     Address srcAddr(ptr, (int32_t) mir->base());
-    if (vt == ArrayBufferView::TYPE_FLOAT32) {
-        JS_ASSERT(mir->type() == MIRType_Float32);
-        FloatRegister dest = ToFloatRegister(out);
-        masm.movssWithPatch(srcAddr, dest);
-        masm.canonicalizeFloat(dest);
-        if (ool)
-            masm.bind(ool->rejoin());
-        return true;
-    }
-    loadNonFloat32ViewTypeElement(vt, srcAddr, out);
+    loadViewTypeElement(vt, srcAddr, out);
     if (vt == ArrayBufferView::TYPE_FLOAT64)
         masm.canonicalizeDouble(ToFloatRegister(out));
+    if (vt == ArrayBufferView::TYPE_FLOAT32)
+        masm.canonicalizeFloat(ToFloatRegister(out));
     if (ool)
         masm.bind(ool->rejoin());
     return true;
 }
 
 bool
 CodeGeneratorX86::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
 {
@@ -502,93 +495,82 @@ CodeGeneratorX86::visitAsmJSLoadHeap(LAs
     const LDefinition *out = ins->output();
 
     if (ptr->isConstant()) {
         // The constant displacement still needs to be added to the as-yet-unknown
         // base address of the heap. For now, embed the displacement as an
         // immediate in the instruction. This displacement will fixed up when the
         // base address is known during dynamic linking (AsmJSModule::initHeap).
         PatchedAbsoluteAddress srcAddr((void *) ptr->toConstant()->toInt32());
-        return loadViewTypeElement(vt, srcAddr, out);
+        return loadAndNoteViewTypeElement(vt, srcAddr, out);
     }
 
     Register ptrReg = ToRegister(ptr);
     Address srcAddr(ptrReg, 0);
 
     if (mir->skipBoundsCheck())
-        return loadViewTypeElement(vt, srcAddr, out);
+        return loadAndNoteViewTypeElement(vt, srcAddr, out);
 
-    OutOfLineLoadTypedArrayOutOfBounds *ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out));
+    bool isFloat32Load = vt == ArrayBufferView::TYPE_FLOAT32;
+    OutOfLineLoadTypedArrayOutOfBounds *ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), isFloat32Load);
     if (!addOutOfLineCode(ool))
         return false;
 
     CodeOffsetLabel cmp = masm.cmplWithPatch(ptrReg, Imm32(0));
     masm.j(Assembler::AboveOrEqual, ool->entry());
 
-    if (vt == ArrayBufferView::TYPE_FLOAT32) {
-        FloatRegister dest = ToFloatRegister(out);
-        uint32_t before = masm.size();
-        masm.movssWithPatch(srcAddr, dest);
-        uint32_t after = masm.size();
-        masm.cvtss2sd(dest, dest);
-        masm.bind(ool->rejoin());
-        return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, AnyRegister(dest), cmp.offset()));
-    }
     uint32_t before = masm.size();
-    loadNonFloat32ViewTypeElement(vt, srcAddr, out);
+    loadViewTypeElement(vt, srcAddr, out);
     uint32_t after = masm.size();
     masm.bind(ool->rejoin());
     return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(out), cmp.offset()));
 }
 
 bool
 CodeGeneratorX86::visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArrayOutOfBounds *ool)
 {
     if (ool->dest().isFloat()) {
-        masm.loadConstantDouble(GenericNaN(), ool->dest().fpu());
+        if (ool->isFloat32Load())
+            masm.loadConstantFloat32(float(GenericNaN()), ool->dest().fpu());
+        else
+            masm.loadConstantDouble(GenericNaN(), ool->dest().fpu());
     } else {
         Register destReg = ool->dest().gpr();
         masm.mov(ImmWord(0), destReg);
     }
     masm.jmp(ool->rejoin());
     return true;
 }
 
 template<typename T>
 void
-CodeGeneratorX86::storeNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
-                                                 const T &dstAddr)
+CodeGeneratorX86::storeViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
+                                       const T &dstAddr)
 {
     switch (vt) {
       case ArrayBufferView::TYPE_INT8:
       case ArrayBufferView::TYPE_UINT8_CLAMPED:
       case ArrayBufferView::TYPE_UINT8:   masm.movbWithPatch(ToRegister(value), dstAddr); break;
       case ArrayBufferView::TYPE_INT16:
       case ArrayBufferView::TYPE_UINT16:  masm.movwWithPatch(ToRegister(value), dstAddr); break;
       case ArrayBufferView::TYPE_INT32:
       case ArrayBufferView::TYPE_UINT32:  masm.movlWithPatch(ToRegister(value), dstAddr); break;
+      case ArrayBufferView::TYPE_FLOAT32: masm.movssWithPatch(ToFloatRegister(value), dstAddr); break;
       case ArrayBufferView::TYPE_FLOAT64: masm.movsdWithPatch(ToFloatRegister(value), dstAddr); break;
       default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
     }
 }
 
 template<typename T>
 bool
-CodeGeneratorX86::storeViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
-                                       const T &dstAddr)
+CodeGeneratorX86::storeAndNoteViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
+                                              const T &dstAddr)
 {
-    if (vt == ArrayBufferView::TYPE_FLOAT32) {
-        uint32_t before = masm.size();
-        masm.convertDoubleToFloat(ToFloatRegister(value), ScratchFloatReg);
-        masm.movssWithPatch(ScratchFloatReg, dstAddr);
-        uint32_t after = masm.size();
-        return gen->noteHeapAccess(AsmJSHeapAccess(before, after));
-    }
     uint32_t before = masm.size();
-    storeNonFloat32ViewTypeElement(vt, value, dstAddr);
+    storeViewTypeElement(vt, value, dstAddr);
     uint32_t after = masm.size();
     return gen->noteHeapAccess(AsmJSHeapAccess(before, after));
 }
 
 bool
 CodeGeneratorX86::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic *ins)
 {
     MStoreTypedArrayElementStatic *mir = ins->mir();
@@ -597,23 +579,17 @@ CodeGeneratorX86::visitStoreTypedArrayEl
     Register ptr = ToRegister(ins->ptr());
     const LAllocation *value = ins->value();
 
     masm.cmpl(ptr, Imm32(mir->length()));
     Label rejoin;
     masm.j(Assembler::AboveOrEqual, &rejoin);
 
     Address dstAddr(ptr, (int32_t) mir->base());
-    if (vt == ArrayBufferView::TYPE_FLOAT32) {
-        JS_ASSERT(mir->value()->type() == MIRType_Float32);
-        masm.movssWithPatch(ToFloatRegister(value), dstAddr);
-        masm.bind(&rejoin);
-        return true;
-    }
-    storeNonFloat32ViewTypeElement(vt, value, dstAddr);
+    storeViewTypeElement(vt, value, dstAddr);
     masm.bind(&rejoin);
     return true;
 }
 
 bool
 CodeGeneratorX86::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
 {
     MAsmJSStoreHeap *mir = ins->mir();
@@ -622,69 +598,67 @@ CodeGeneratorX86::visitAsmJSStoreHeap(LA
     const LAllocation *ptr = ins->ptr();
 
     if (ptr->isConstant()) {
         // The constant displacement still needs to be added to the as-yet-unknown
         // base address of the heap. For now, embed the displacement as an
         // immediate in the instruction. This displacement will fixed up when the
         // base address is known during dynamic linking (AsmJSModule::initHeap).
         PatchedAbsoluteAddress dstAddr((void *) ptr->toConstant()->toInt32());
-        return storeViewTypeElement(vt, value, dstAddr);
+        return storeAndNoteViewTypeElement(vt, value, dstAddr);
     }
 
     Register ptrReg = ToRegister(ptr);
     Address dstAddr(ptrReg, 0);
 
     if (mir->skipBoundsCheck())
-        return storeViewTypeElement(vt, value, dstAddr);
+        return storeAndNoteViewTypeElement(vt, value, dstAddr);
 
     CodeOffsetLabel cmp = masm.cmplWithPatch(ptrReg, Imm32(0));
     Label rejoin;
     masm.j(Assembler::AboveOrEqual, &rejoin);
 
-    if (vt == ArrayBufferView::TYPE_FLOAT32) {
-        masm.convertDoubleToFloat(ToFloatRegister(value), ScratchFloatReg);
-        uint32_t before = masm.size();
-        masm.movssWithPatch(ScratchFloatReg, dstAddr);
-        uint32_t after = masm.size();
-        masm.bind(&rejoin);
-        return gen->noteHeapAccess(AsmJSHeapAccess(before, after, cmp.offset()));
-    }
     uint32_t before = masm.size();
-    storeNonFloat32ViewTypeElement(vt, value, dstAddr);
+    storeViewTypeElement(vt, value, dstAddr);
     uint32_t after = masm.size();
     masm.bind(&rejoin);
     return gen->noteHeapAccess(AsmJSHeapAccess(before, after, cmp.offset()));
 }
 
 bool
 CodeGeneratorX86::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins)
 {
     MAsmJSLoadGlobalVar *mir = ins->mir();
+    MIRType type = mir->type();
+    JS_ASSERT(IsNumberType(type));
 
     CodeOffsetLabel label;
-    if (mir->type() == MIRType_Int32)
+    if (type == MIRType_Int32)
         label = masm.movlWithPatch(PatchedAbsoluteAddress(), ToRegister(ins->output()));
+    else if (type == MIRType_Float32)
+        label = masm.movssWithPatch(PatchedAbsoluteAddress(), ToFloatRegister(ins->output()));
     else
         label = masm.movsdWithPatch(PatchedAbsoluteAddress(), ToFloatRegister(ins->output()));
 
     return gen->noteGlobalAccess(label.offset(), mir->globalDataOffset());
 }
 
 bool
 CodeGeneratorX86::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins)
 {
     MAsmJSStoreGlobalVar *mir = ins->mir();
 
     MIRType type = mir->value()->type();
-    JS_ASSERT(type == MIRType_Int32 || type == MIRType_Double);
+    JS_ASSERT(IsNumberType(type));
 
     CodeOffsetLabel label;
     if (type == MIRType_Int32)
         label = masm.movlWithPatch(ToRegister(ins->value()), PatchedAbsoluteAddress());
+    else if (type == MIRType_Float32)
+        label = masm.movssWithPatch(ToFloatRegister(ins->value()), PatchedAbsoluteAddress());
     else
         label = masm.movsdWithPatch(ToFloatRegister(ins->value()), PatchedAbsoluteAddress());
 
     return gen->noteGlobalAccess(label.offset(), mir->globalDataOffset());
 }
 
 bool
 CodeGeneratorX86::visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr *ins)
@@ -708,23 +682,28 @@ CodeGeneratorX86::visitAsmJSLoadFFIFunc(
 
     return gen->noteGlobalAccess(label.offset(), mir->globalDataOffset());
 }
 
 void
 CodeGeneratorX86::postAsmJSCall(LAsmJSCall *lir)
 {
     MAsmJSCall *mir = lir->mir();
-    if (mir->type() != MIRType_Double || mir->callee().which() != MAsmJSCall::Callee::Builtin)
+    if (!IsFloatingPointType(mir->type()) || mir->callee().which() != MAsmJSCall::Callee::Builtin)
         return;
 
-    masm.reserveStack(sizeof(double));
-    masm.fstp(Operand(esp, 0));
-    masm.loadDouble(Operand(esp, 0), ReturnFloatReg);
-    masm.freeStack(sizeof(double));
+    if (mir->type() == MIRType_Float32) {
+        Operand op(esp, -sizeof(float));
+        masm.fstp32(op);
+        masm.loadFloat(op, ReturnFloatReg);
+    } else {
+        Operand op(esp, -sizeof(double));
+        masm.fstp(op);
+        masm.loadDouble(op, ReturnFloatReg);
+    }
 }
 
 void
 DispatchIonCache::initializeAddCacheState(LInstruction *ins, AddCacheState *addState)
 {
     // On x86, where there is no general purpose scratch register available,
     // child cache classes must manually specify a dispatch scratch register.
     MOZ_ASSUME_UNREACHABLE("x86 needs manual assignment of dispatchScratch");
@@ -1008,17 +987,22 @@ CodeGeneratorX86::visitOutOfLineTruncate
     masm.bind(&fail);
     {
         saveVolatile(output);
 
         masm.push(input);
         masm.setupUnalignedABICall(1, output);
         masm.cvtss2sd(input, input);
         masm.passABIArg(input);
-        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::ToInt32));
+
+        if (gen->compilingAsmJS())
+            masm.callWithABI(AsmJSImm_ToInt32);
+        else
+            masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::ToInt32));
+
         masm.storeCallResult(output);
         masm.pop(input);
 
         restoreVolatile(output);
     }
 
     masm.jump(ool->rejoin());
     return true;
--- a/js/src/jit/x86/CodeGenerator-x86.h
+++ b/js/src/jit/x86/CodeGenerator-x86.h
@@ -25,26 +25,26 @@ class CodeGeneratorX86 : public CodeGene
     }
 
   protected:
     ValueOperand ToValue(LInstruction *ins, size_t pos);
     ValueOperand ToOutValue(LInstruction *ins);
     ValueOperand ToTempValue(LInstruction *ins, size_t pos);
 
     template<typename T>
-    bool loadViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr,
+    bool loadAndNoteViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr,
                              const LDefinition *out);
     template<typename T>
-    void loadNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr,
+    void loadViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr,
                                        const LDefinition *out);
     template<typename T>
-    bool storeViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
+    bool storeAndNoteViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
                               const T &dstAddr);
     template<typename T>
-    void storeNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
+    void storeViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
                                         const T &dstAddr);
     void storeElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType,
                            const Register &elements, const LAllocation *index);
 
   public:
     CodeGeneratorX86(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm);
 
   public:
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -3290,16 +3290,48 @@ js::NewDenseCopiedArray(JSContext *cx, u
     arr->setDenseInitializedLength(values ? length : 0);
 
     if (values)
         arr->initDenseElements(0, values, length);
 
     return arr;
 }
 
+ArrayObject *
+js::NewDenseCopiedArrayWithTemplate(JSContext *cx, uint32_t length, const Value *values,
+                                    JSObject *templateObject)
+{
+    gc::AllocKind allocKind = GuessArrayGCKind(length);
+    JS_ASSERT(CanBeFinalizedInBackground(allocKind, &ArrayObject::class_));
+    allocKind = GetBackgroundAllocKind(allocKind);
+
+    RootedTypeObject type(cx, templateObject->type());
+    if (!type)
+        return nullptr;
+
+    RootedShape shape(cx, templateObject->lastProperty());
+    if (!shape)
+        return nullptr;
+
+    gc::InitialHeap heap = GetInitialHeap(GenericObject, &ArrayObject::class_);
+    Rooted<ArrayObject *> arr(cx, JSObject::createArray(cx, allocKind, heap, shape, type, length));
+    if (!arr)
+        return nullptr;
+
+    if (!EnsureNewArrayElements(cx, arr, length))
+        return nullptr;
+
+    arr->setDenseInitializedLength(length);
+    arr->initDenseElements(0, values, length);
+
+    probes::CreateObject(cx, arr);
+
+    return arr;
+}
+
 #ifdef DEBUG
 bool
 js_ArrayInfo(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JSObject *obj;
 
     for (unsigned i = 0; i < args.length(); i++) {
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -66,16 +66,23 @@ extern ArrayObject *
 NewDenseCopiedArray(JSContext *cx, uint32_t length, HandleObject src, uint32_t elementOffset, JSObject *proto = nullptr);
 
 /* Create a dense array from the given array values, which must be rooted */
 extern ArrayObject *
 NewDenseCopiedArray(JSContext *cx, uint32_t length, const Value *values, JSObject *proto = nullptr,
                     NewObjectKind newKind = GenericObject);
 
 /*
+ * Create a dense array based on templateObject from the given array values,
+ * which must be rooted.
+ */
+extern ArrayObject *
+NewDenseCopiedArrayWithTemplate(JSContext *cx, uint32_t length, const Value *values,
+                                JSObject *templateObject);
+/*
  * Determines whether a write to the given element on |obj| should fail because
  * |obj| is an Array with a non-writable length, and writing that element would
  * increase the length of the array.
  */
 extern bool
 WouldDefinePastNonwritableLength(ThreadSafeContext *cx,
                                  HandleObject obj, uint32_t index, bool strict,
                                  bool *definesPast);
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -455,31 +455,40 @@ js::math_imul(JSContext *cx, unsigned ar
 
     uint32_t product = a * b;
     args.rval().setInt32(product > INT32_MAX
                          ? int32_t(INT32_MIN + (product - INT32_MAX - 1))
                          : int32_t(product));
     return true;
 }
 
+// Implements Math.fround (20.2.2.16) up to step 3
+bool
+js::RoundFloat32(JSContext *cx, Handle<Value> v, float *out)
+{
+    double d;
+    bool success = ToNumber(cx, v, &d);
+    *out = static_cast<float>(d);
+    return success;
+}
+
 bool
 js::math_fround(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() == 0) {
         args.rval().setNaN();
         return true;
     }
 
-    double x;
-    if (!ToNumber(cx, args[0], &x))
+    float f;
+    if (!RoundFloat32(cx, args[0], &f))
         return false;
 
-    float f = x;
     args.rval().setDouble(static_cast<double>(f));
     return true;
 }
 
 #if defined(SOLARIS) && defined(__GNUC__)
 #define LOG_IF_OUT_OF_RANGE(x) if (x < 0) return GenericNaN();
 #else
 #define LOG_IF_OUT_OF_RANGE(x)
--- a/js/src/jsmath.h
+++ b/js/src/jsmath.h
@@ -80,16 +80,19 @@ extern bool
 js_math_pow(JSContext *cx, unsigned argc, js::Value *vp);
 
 namespace js {
 
 extern bool
 math_imul(JSContext *cx, unsigned argc, js::Value *vp);
 
 extern bool
+RoundFloat32(JSContext *cx, Handle<Value> v, float *out);
+
+extern bool
 math_fround(JSContext *cx, unsigned argc, js::Value *vp);
 
 extern bool
 math_log(JSContext *cx, unsigned argc, js::Value *vp);
 
 extern double
 math_log_impl(MathCache *cache, double x);
 
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -489,28 +489,47 @@ JSObject::createArray(js::ExclusiveConte
     JS_ASSERT(shape->numFixedSlots() == 0);
 
     /*
      * The array initially stores its elements inline, there must be enough
      * space for an elements header.
      */
     JS_ASSERT(js::gc::GetGCKindSlots(kind) >= js::ObjectElements::VALUES_PER_HEADER);
 
-    uint32_t capacity = js::gc::GetGCKindSlots(kind) - js::ObjectElements::VALUES_PER_HEADER;
+    js::HeapSlot *slots = nullptr;
+    if (size_t nDynamicSlots = dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan())) {
+        slots = cx->pod_malloc<js::HeapSlot>(nDynamicSlots);
+        if (!slots)
+            return nullptr;
+        js::Debug_SetSlotRangeToCrashOnTouch(slots, nDynamicSlots);
+    }
 
     JSObject *obj = js_NewGCObject<js::CanGC>(cx, kind, heap);
-    if (!obj)
+    if (!obj) {
+        js_free(slots);
         return nullptr;
+    }
+
+#ifdef JSGC_GENERATIONAL
+    if (slots && heap != js::gc::TenuredHeap)
+        cx->asJSContext()->runtime()->gcNursery.notifyInitialSlots(obj, slots);
+#endif
+
+    uint32_t capacity = js::gc::GetGCKindSlots(kind) - js::ObjectElements::VALUES_PER_HEADER;
 
     obj->shape_.init(shape);
     obj->type_.init(type);
-    obj->slots = nullptr;
+    obj->slots = slots;
     obj->setFixedElements();
     new (obj->getElementsHeader()) js::ObjectElements(capacity, length);
 
+    size_t span = shape->slotSpan();
+    if (span)
+        obj->initializeSlotRange(0, span);
+
     return &obj->as<js::ArrayObject>();
 }
 
 inline void
 JSObject::finish(js::FreeOp *fop)
 {
     if (hasDynamicSlots())
         fop->free_(slots);
--- a/js/src/jsweakmap.cpp
+++ b/js/src/jsweakmap.cpp
@@ -264,27 +264,36 @@ TryPreserveReflector(JSContext *cx, Hand
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_WEAKMAP_KEY);
             return false;
         }
     }
     return true;
 }
 
 static inline void
-WeakMapPostWriteBarrier(JSRuntime *rt, ObjectValueMap *map, JSObject *key)
+WeakMapPostWriteBarrier(JSRuntime *rt, ObjectValueMap *weakMap, JSObject *key)
 {
 #ifdef JSGC_GENERATIONAL
     /*
      * Strip the barriers from the type before inserting into the store buffer.
      * This will automatically ensure that barriers do not fire during GC.
+     *
+     * Some compilers complain about instantiating the WeakMap class for
+     * unbarriered type arguments, so we cast to a HashMap instead.  Because of
+     * WeakMap's multiple inheritace, We need to do this in two stages, first to
+     * the HashMap base class and then to the unbarriered version.
      */
-    typedef WeakMap<JSObject *, Value> UnbarrieredObjectValueMap;
-    typedef gc::HashKeyRef<UnbarrieredObjectValueMap, JSObject *> Ref;
+    ObjectValueMap::Base *baseHashMap = static_cast<ObjectValueMap::Base *>(weakMap);
+
+    typedef HashMap<JSObject *, Value> UnbarrieredMap;
+    UnbarrieredMap *unbarrieredMap = reinterpret_cast<UnbarrieredMap *>(baseHashMap);
+
+    typedef gc::HashKeyRef<UnbarrieredMap, JSObject *> Ref;
     if (key && IsInsideNursery(rt, key))
-        rt->gcStoreBuffer.putGeneric(Ref(reinterpret_cast<UnbarrieredObjectValueMap *>(map), key));
+        rt->gcStoreBuffer.putGeneric(Ref((unbarrieredMap), key));
 #endif
 }
 
 JS_ALWAYS_INLINE bool
 WeakMap_set_impl(JSContext *cx, CallArgs args)
 {
     JS_ASSERT(IsWeakMap(args.thisv()));
 
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -15,16 +15,18 @@
 #include "vm/Xdr.h"
 #include "yarr/YarrSyntaxChecker.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/Shape-inl.h"
 
 using namespace js;
+
+using mozilla::DebugOnly;
 using js::frontend::TokenStream;
 
 JS_STATIC_ASSERT(IgnoreCaseFlag == JSREG_FOLD);
 JS_STATIC_ASSERT(GlobalFlag == JSREG_GLOB);
 JS_STATIC_ASSERT(MultilineFlag == JSREG_MULTILINE);
 JS_STATIC_ASSERT(StickyFlag == JSREG_STICKY);
 
 /* RegExpObjectBuilder */
@@ -625,25 +627,58 @@ RegExpShared::executeMatchOnly(JSContext
 
     *lastIndex = match.limit;
     return RegExpRunStatus_Success;
 }
 
 /* RegExpCompartment */
 
 RegExpCompartment::RegExpCompartment(JSRuntime *rt)
-  : map_(rt), inUse_(rt)
+  : map_(rt), inUse_(rt), matchResultTemplateObject_(nullptr)
 {}
 
 RegExpCompartment::~RegExpCompartment()
 {
     JS_ASSERT(map_.empty());
     JS_ASSERT(inUse_.empty());
 }
 
+HeapPtrObject &
+RegExpCompartment::getOrCreateMatchResultTemplateObject(JSContext *cx)
+{
+    if (matchResultTemplateObject_)
+        return matchResultTemplateObject_;
+
+    /* Create template array object */
+    RootedObject templateObject(cx, NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject));
+
+    /* Set dummy index property */
+    RootedValue index(cx, Int32Value(0));
+    if (!baseops::DefineProperty(cx, templateObject, cx->names().index, index,
+                                 JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE))
+        return matchResultTemplateObject_; // = nullptr
+
+    /* Set dummy input property */
+    RootedValue inputVal(cx, StringValue(cx->runtime()->emptyString));
+    if (!baseops::DefineProperty(cx, templateObject, cx->names().input, inputVal,
+                                 JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE))
+        return matchResultTemplateObject_; // = nullptr
+
+    // Make sure that the properties are in the right slots.
+    DebugOnly<Shape *> shape = templateObject->lastProperty();
+    JS_ASSERT(shape->previous()->slot() == 0 &&
+              shape->previous()->propidRef() == NameToId(cx->names().index));
+    JS_ASSERT(shape->slot() == 1 &&
+              shape->propidRef() == NameToId(cx->names().input));
+
+    matchResultTemplateObject_ = templateObject;
+
+    return matchResultTemplateObject_;
+}
+
 bool
 RegExpCompartment::init(JSContext *cx)
 {
     if (!map_.init(0) || !inUse_.init(0)) {
         if (cx)
             js_ReportOutOfMemory(cx);
         return false;
     }
@@ -664,16 +699,18 @@ RegExpCompartment::sweep(JSRuntime *rt)
 
     for (PendingSet::Enum e(inUse_); !e.empty(); e.popFront()) {
         RegExpShared *shared = e.front();
         if (shared->activeUseCount == 0 && shared->gcNumberWhenUsed < rt->gcStartNumber) {
             js_delete(shared);
             e.removeFront();
         }
     }
+
+    matchResultTemplateObject_ = nullptr;
 }
 
 void
 RegExpCompartment::clearTables()
 {
     JS_ASSERT(inUse_.empty());
     map_.clear();
 }
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -313,29 +313,39 @@ class RegExpCompartment
     /*
      * The set of all RegExpShareds in the compartment. On every GC, every
      * RegExpShared that is not actively being used is deleted and removed from
      * the set.
      */
     typedef HashSet<RegExpShared *, DefaultHasher<RegExpShared*>, RuntimeAllocPolicy> PendingSet;
     PendingSet inUse_;
 
+    /*
+     * This is the template object where the result of re.exec() is based on,
+     * if there is a result. This is used in CreateRegExpMatchResult to set
+     * the input/index properties faster.
+     */
+    HeapPtrObject matchResultTemplateObject_;
+
   public:
     RegExpCompartment(JSRuntime *rt);
     ~RegExpCompartment();
 
     bool init(JSContext *cx);
     void sweep(JSRuntime *rt);
     void clearTables();
 
     bool get(ExclusiveContext *cx, JSAtom *source, RegExpFlag flags, RegExpGuard *g);
 
     /* Like 'get', but compile 'maybeOpt' (if non-null). */
     bool get(JSContext *cx, HandleAtom source, JSString *maybeOpt, RegExpGuard *g);
 
+    /* Get or create template object used to base the result of .exec() on. */
+    HeapPtrObject &getOrCreateMatchResultTemplateObject(JSContext *cx);
+
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
 };
 
 class RegExpObject : public JSObject
 {
     static const unsigned LAST_INDEX_SLOT          = 0;
     static const unsigned SOURCE_SLOT              = 1;
     static const unsigned GLOBAL_FLAG_SLOT         = 2;
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -1530,21 +1530,30 @@ js_IsDebugScopeSlow(ProxyObject *proxy)
 /* static */ JS_ALWAYS_INLINE void
 DebugScopes::proxiedScopesPostWriteBarrier(JSRuntime *rt, ObjectWeakMap *map,
                                            const EncapsulatedPtr<JSObject> &key)
 {
 #ifdef JSGC_GENERATIONAL
     /*
      * Strip the barriers from the type before inserting into the store buffer.
      * This will automatically ensure that barriers do not fire during GC.
+     *
+     * Some compilers complain about instantiating the WeakMap class for
+     * unbarriered type arguments, so we cast to a HashMap instead.  Because of
+     * WeakMap's multiple inheritace, We need to do this in two stages, first to
+     * the HashMap base class and then to the unbarriered version.
      */
-    typedef WeakMap<JSObject *, JSObject *> UnbarrieredMap;
+    ObjectWeakMap::Base *baseHashMap = static_cast<ObjectWeakMap::Base *>(map);
+
+    typedef HashMap<JSObject *, JSObject *> UnbarrieredMap;
+    UnbarrieredMap *unbarrieredMap = reinterpret_cast<UnbarrieredMap *>(baseHashMap);
+
     typedef gc::HashKeyRef<UnbarrieredMap, JSObject *> Ref;
     if (key && IsInsideNursery(rt, key))
-        rt->gcStoreBuffer.putGeneric(Ref(reinterpret_cast<UnbarrieredMap *>(map), key.get()));
+        rt->gcStoreBuffer.putGeneric(Ref(unbarrieredMap, key.get()));
 #endif
 }
 
 /* static */ JS_ALWAYS_INLINE void
 DebugScopes::liveScopesPostWriteBarrier(JSRuntime *rt, LiveScopeMap *map, ScopeObject *key)
 {
 #ifdef JSGC_GENERATIONAL
     if (key && IsInsideNursery(rt, key))
@@ -1586,18 +1595,38 @@ void
 DebugScopes::sweep(JSRuntime *rt)
 {
     /*
      * Note: missingScopes points to debug scopes weakly not just so that debug
      * scopes can be released more eagerly, but, more importantly, to avoid
      * creating an uncollectable cycle with suspended generator frames.
      */
     for (MissingScopeMap::Enum e(missingScopes); !e.empty(); e.popFront()) {
-        if (IsObjectAboutToBeFinalized(e.front().value().unsafeGet()))
+        DebugScopeObject **debugScope = e.front().value().unsafeGet();
+        if (IsObjectAboutToBeFinalized(debugScope)) {
+            /*
+             * Note that onPopCall and onPopBlock rely on missingScopes to find
+             * scope objects that we synthesized for the debugger's sake, and
+             * clean up the synthetic scope objects' entries in liveScopes. So
+             * if we remove an entry frcom missingScopes here, we must also
+             * remove the corresponding liveScopes entry.
+             *
+             * Since the DebugScopeObject is the only thing using its scope
+             * object, and the DSO is about to be finalized, you might assume
+             * that the synthetic SO is also about to be finalized too, and thus
+             * the loop below will take care of things. But complex GC behavior
+             * means that marks are only conservative approximations of
+             * liveness; we should assume that anything could be marked.
+             *
+             * Thus, we must explicitly remove the entries from both liveScopes
+             * and missingScopes here.
+             */
+            liveScopes.remove(&(*debugScope)->scope());
             e.removeFront();
+        }
     }
 
     for (LiveScopeMap::Enum e(liveScopes); !e.empty(); e.popFront()) {
         ScopeObject *scope = e.front().key();
         AbstractFramePtr frame = e.front().value();
 
         /*
          * Scopes can be finalized when a debugger-synthesized ScopeObject is
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -1669,18 +1669,17 @@ ContainerState::SetFixedPositionLayerDat
   // been set.
   nsSize viewportSize = viewportFrame->GetSize();
   if (presContext->PresShell()->IsScrollPositionClampingScrollPortSizeSet()) {
     viewportSize = presContext->PresShell()->
       GetScrollPositionClampingScrollPortSize();
   }
 
   nsLayoutUtils::SetFixedPositionLayerData(aLayer,
-      viewportFrame, viewportSize, aFixedPosFrame, mContainerReferenceFrame,
-      presContext, mParameters);
+      viewportFrame, viewportSize, aFixedPosFrame, presContext, mParameters);
 }
 
 void
 ContainerState::PopThebesLayerData()
 {
   NS_ASSERTION(!mThebesLayerDataStack.IsEmpty(), "Can't pop");
 
   int32_t lastIndex = mThebesLayerDataStack.Length() - 1;
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -44,17 +44,17 @@
 #include "nsCSSAnonBoxes.h"
 #include "nsTextFragment.h"
 #include "nsIAnonymousContentCreator.h"
 #include "nsBindingManager.h"
 #include "nsXBLBinding.h"
 #include "nsContentUtils.h"
 #include "nsIScriptError.h"
 #ifdef XP_MACOSX
-#include "nsIDocShellTreeItem.h"
+#include "nsIDocShell.h"
 #endif
 #include "ChildIterator.h"
 #include "nsError.h"
 #include "nsLayoutUtils.h"
 #include "nsAutoPtr.h"
 #include "nsBoxFrame.h"
 #include "nsBoxLayout.h"
 #include "nsFlexContainerFrame.h"
@@ -4089,32 +4089,29 @@ nsCSSFrameConstructor::FindXULDescriptio
 }
 
 #ifdef XP_MACOSX
 /* static */
 const nsCSSFrameConstructor::FrameConstructionData*
 nsCSSFrameConstructor::FindXULMenubarData(Element* aElement,
                                           nsStyleContext* aStyleContext)
 {
-  nsCOMPtr<nsISupports> container =
-    aStyleContext->PresContext()->GetContainer();
-  if (container) {
-    nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(container));
-    if (treeItem) {
-      int32_t type;
-      treeItem->GetItemType(&type);
-      if (nsIDocShellTreeItem::typeChrome == type) {
-        nsCOMPtr<nsIDocShellTreeItem> parent;
-        treeItem->GetParent(getter_AddRefs(parent));
-        if (!parent) {
-          // This is the root.  Suppress the menubar, since on Mac
-          // window menus are not attached to the window.
-          static const FrameConstructionData sSuppressData = SUPPRESS_FCDATA();
-          return &sSuppressData;
-        }
+  nsCOMPtr<nsIDocShell> treeItem =
+    aStyleContext->PresContext()->GetDocShell();
+  if (treeItem) {
+    int32_t type;
+    treeItem->GetItemType(&type);
+    if (nsIDocShellTreeItem::typeChrome == type) {
+      nsCOMPtr<nsIDocShellTreeItem> parent;
+      treeItem->GetParent(getter_AddRefs(parent));
+      if (!parent) {
+        // This is the root.  Suppress the menubar, since on Mac
+        // window menus are not attached to the window.
+        static const FrameConstructionData sSuppressData = SUPPRESS_FCDATA();
+        return &sSuppressData;
       }
     }
   }
 
   static const FrameConstructionData sMenubarData =
     SIMPLE_XUL_FCDATA(NS_NewMenuBarFrame);
   return &sMenubarData;
 }
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -3358,19 +3358,18 @@ nsDisplayStickyPosition::BuildLayer(nsDi
   nsSize scrollFrameSize = scrollFrame->GetSize();
   if (scrollFrame == presContext->PresShell()->GetRootScrollFrame() &&
       presContext->PresShell()->IsScrollPositionClampingScrollPortSizeSet()) {
     scrollFrameSize = presContext->PresShell()->
       GetScrollPositionClampingScrollPortSize();
   }
 
   nsLayoutUtils::SetFixedPositionLayerData(layer, scrollFrame, scrollFrameSize,
-                                           mStickyPosFrame, ReferenceFrame(),
-                                           presContext,
-                                           aContainerParameters);
+                                           mStickyPosFrame,
+                                           presContext, aContainerParameters);
 
   ViewID scrollId = nsLayoutUtils::FindOrCreateIDFor(
     stickyScrollContainer->ScrollFrame()->GetScrolledFrame()->GetContent());
 
   float factor = presContext->AppUnitsPerDevPixel();
   nsRect outer;
   nsRect inner;
   stickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner);
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -612,17 +612,17 @@ nsDocumentViewer::SyncParentSubDocMap()
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocumentViewer::SetContainer(nsIDocShell* aContainer)
 {
   mContainer = static_cast<nsDocShell*>(aContainer)->asWeakPtr();
   if (mPresContext) {
-    mPresContext->SetContainer(aContainer);
+    mPresContext->SetContainer(mContainer);
   }
 
   // We're loading a new document into the window where this document
   // viewer lives, sync the parent document's frame element -> sub
   // document map
 
   return SyncParentSubDocMap();
 }
@@ -882,17 +882,17 @@ nsDocumentViewer::InitInternal(nsIWidget
 
   nsCOMPtr<nsIInterfaceRequestor> requestor(mContainer);
   if (requestor) {
     if (mPresContext) {
       nsCOMPtr<nsILinkHandler> linkHandler;
       requestor->GetInterface(NS_GET_IID(nsILinkHandler),
                               getter_AddRefs(linkHandler));
 
-      mPresContext->SetContainer(requestor);
+      mPresContext->SetContainer(mContainer);
       mPresContext->SetLinkHandler(linkHandler);
     }
 
     // Set script-context-owner in the document
 
     nsCOMPtr<nsPIDOMWindow> window;
     requestor->GetInterface(NS_GET_IID(nsPIDOMWindow),
                             getter_AddRefs(window));
@@ -1304,23 +1304,23 @@ AttachContainerRecurse(nsIDocShell* aShe
   if (viewer) {
     nsIDocument* doc = viewer->GetDocument();
     if (doc) {
       doc->SetContainer(static_cast<nsDocShell*>(aShell));
     }
     nsRefPtr<nsPresContext> pc;
     viewer->GetPresContext(getter_AddRefs(pc));
     if (pc) {
-      pc->SetContainer(aShell);
+      pc->SetContainer(static_cast<nsDocShell*>(aShell));
       pc->SetLinkHandler(nsCOMPtr<nsILinkHandler>(do_QueryInterface(aShell)));
     }
     nsCOMPtr<nsIPresShell> presShell;
     viewer->GetPresShell(getter_AddRefs(presShell));
     if (presShell) {
-      presShell->SetForwardingContainer(nullptr);
+      presShell->SetForwardingContainer(WeakPtr<nsDocShell>());
     }
   }
 
   // Now recurse through the children
   int32_t childCount;
   aShell->GetChildCount(&childCount);
   for (int32_t i = 0; i < childCount; ++i) {
     nsCOMPtr<nsIDocShellTreeItem> childItem;
@@ -1338,17 +1338,17 @@ nsDocumentViewer::Open(nsISupports *aSta
     mDocument->SetContainer(mContainer);
 
   nsresult rv = InitInternal(mParentWidget, aState, mBounds, false);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mHidden = false;
 
   if (mPresShell)
-    mPresShell->SetForwardingContainer(nullptr);
+    mPresShell->SetForwardingContainer(WeakPtr<nsDocShell>());
 
   // Rehook the child presentations.  The child shells are still in
   // session history, so get them from there.
 
   if (aSHEntry) {
     nsCOMPtr<nsIDocShellTreeItem> item;
     int32_t itemIndex = 0;
     while (NS_SUCCEEDED(aSHEntry->ChildShellAt(itemIndex+