Merge m-c to fx-team. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 23 Oct 2014 14:07:56 -0400
changeset 211981 cda97cb2f4ac695d3451f61189fdd83a31cacfb6
parent 211980 c667ca8f779a6706cb0b8f9f8aff441ae482f840 (current diff)
parent 211954 d8de0d7e52e09b884e11c441975692a63758349e (diff)
child 211982 6cc96113e8a3cbd7f3df060d2d3e6fe24379dce5
push id27694
push userkwierso@gmail.com
push dateFri, 24 Oct 2014 00:17:24 +0000
treeherdermozilla-central@a97b95030a94 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone36.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 m-c to fx-team. a=merge
b2g/components/YoutubeProtocolHandler.js
browser/components/loop/test/mochitest/loop_fxa.sjs
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
media/libcubeb/README
media/libcubeb/README.md
testing/web-platform/meta/dom/nodes/MutationObserver-attributes.html.ini
testing/web-platform/meta/dom/nodes/MutationObserver-characterData.html.ini
--- a/accessible/generic/Accessible.h
+++ b/accessible/generic/Accessible.h
@@ -1175,17 +1175,17 @@ private:
  * This class makes sure required tasks are done before and after tree
  * mutations. Currently this only includes group info invalidation. You must
  * have an object of this class on the stack when calling methods that mutate
  * the accessible tree.
  */
 class AutoTreeMutation
 {
 public:
-  AutoTreeMutation(Accessible* aRoot, bool aInvalidationRequired = true) :
+  explicit AutoTreeMutation(Accessible* aRoot, bool aInvalidationRequired = true) :
     mInvalidationRequired(aInvalidationRequired), mRoot(aRoot)
   {
     MOZ_ASSERT(!(mRoot->mStateFlags & Accessible::eSubtreeMutating));
     mRoot->mStateFlags |= Accessible::eSubtreeMutating;
   }
   ~AutoTreeMutation()
   {
     if (mInvalidationRequired)
--- a/accessible/generic/BaseAccessibles.h
+++ b/accessible/generic/BaseAccessibles.h
@@ -113,17 +113,17 @@ protected:
 
 /**
  * A wrapper accessible around native accessible to connect it with
  * crossplatform accessible tree.
  */
 class DummyAccessible : public AccessibleWrap
 {
 public:
-  DummyAccessible(DocAccessible* aDocument = nullptr) :
+  explicit DummyAccessible(DocAccessible* aDocument = nullptr) :
     AccessibleWrap(nullptr, aDocument) { }
 
   virtual uint64_t NativeState() MOZ_OVERRIDE MOZ_FINAL;
   virtual uint64_t NativeInteractiveState() const MOZ_OVERRIDE MOZ_FINAL;
   virtual uint64_t NativeLinkState() const MOZ_OVERRIDE MOZ_FINAL;
   virtual bool NativelyUnavailable() const MOZ_OVERRIDE MOZ_FINAL;
   virtual void ApplyARIAState(uint64_t* aState) const MOZ_OVERRIDE MOZ_FINAL;
 
--- a/accessible/ipc/DocAccessibleChild.h
+++ b/accessible/ipc/DocAccessibleChild.h
@@ -17,17 +17,17 @@ class AccShowEvent;
 
   /*
    * These objects handle content side communication for an accessible document,
    * and their lifetime is the same as the document they represent.
    */
 class DocAccessibleChild : public PDocAccessibleChild
 {
 public:
-  DocAccessibleChild(DocAccessible* aDoc) :
+  explicit DocAccessibleChild(DocAccessible* aDoc) :
     mDoc(aDoc)
   { MOZ_COUNT_CTOR(DocAccessibleChild); }
   ~DocAccessibleChild()
   {
     mDoc->SetIPCDoc(nullptr);
     MOZ_COUNT_DTOR(DocAccessibleChild);
   }
 
--- a/accessible/ipc/DocAccessibleParent.h
+++ b/accessible/ipc/DocAccessibleParent.h
@@ -96,17 +96,17 @@ public:
     mAccessibles.RemoveEntry(aAccessible->ID());
   }
 
 private:
 
   class ProxyEntry : public PLDHashEntryHdr
   {
   public:
-    ProxyEntry(const void*) : mProxy(nullptr) {}
+    explicit ProxyEntry(const void*) : mProxy(nullptr) {}
     ProxyEntry(ProxyEntry&& aOther) :
       mProxy(aOther.mProxy) { aOther.mProxy = nullptr; }
     ~ProxyEntry() { delete mProxy; }
 
     typedef uint64_t KeyType;
     typedef const void* KeyTypePointer;
 
     bool KeyEquals(const void* aKey) const
--- a/accessible/xpcom/xpcAccessibleApplication.h
+++ b/accessible/xpcom/xpcAccessibleApplication.h
@@ -16,17 +16,18 @@ namespace a11y {
 
 /**
  * XPCOM wrapper around ApplicationAccessible class.
  */
 class xpcAccessibleApplication : public xpcAccessibleGeneric,
                                  public nsIAccessibleApplication
 {
 public:
-  xpcAccessibleApplication(Accessible* aIntl) : xpcAccessibleGeneric(aIntl) { }
+  explicit xpcAccessibleApplication(Accessible* aIntl) :
+    xpcAccessibleGeneric(aIntl) { }
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessibleApplication
   NS_IMETHOD GetAppName(nsAString& aName) MOZ_FINAL;
   NS_IMETHOD GetAppVersion(nsAString& aVersion) MOZ_FINAL;
   NS_IMETHOD GetPlatformName(nsAString& aName) MOZ_FINAL;
   NS_IMETHOD GetPlatformVersion(nsAString& aVersion) MOZ_FINAL;
--- a/accessible/xpcom/xpcAccessibleDocument.h
+++ b/accessible/xpcom/xpcAccessibleDocument.h
@@ -19,17 +19,17 @@ namespace a11y {
 
 /**
  * XPCOM wrapper around DocAccessible class.
  */
 class xpcAccessibleDocument : public xpcAccessibleHyperText,
                               public nsIAccessibleDocument
 {
 public:
-  xpcAccessibleDocument(DocAccessible* aIntl) :
+  explicit xpcAccessibleDocument(DocAccessible* aIntl) :
     xpcAccessibleHyperText(aIntl), mCache(kDefaultCacheLength) { }
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(xpcAccessibleDocument,
                                            xpcAccessibleGeneric)
 
   // nsIAccessibleDocument
   NS_IMETHOD GetURL(nsAString& aURL) MOZ_FINAL;
--- a/accessible/xpcom/xpcAccessibleGeneric.h
+++ b/accessible/xpcom/xpcAccessibleGeneric.h
@@ -21,17 +21,17 @@ namespace a11y {
  * XPCOM wrapper around Accessible class.
  */
 class xpcAccessibleGeneric : public xpcAccessible,
                              public xpcAccessibleHyperLink,
                              public xpcAccessibleSelectable,
                              public xpcAccessibleValue
 {
 public:
-  xpcAccessibleGeneric(Accessible* aInternal) :
+  explicit xpcAccessibleGeneric(Accessible* aInternal) :
     mIntl(aInternal), mSupportedIfaces(0)
   {
     if (mIntl->IsSelect())
       mSupportedIfaces |= eSelectable;
     if (mIntl->HasNumericValue())
       mSupportedIfaces |= eValue;
     if (mIntl->IsLink())
       mSupportedIfaces |= eHyperLink;
--- a/accessible/xpcom/xpcAccessibleHyperText.h
+++ b/accessible/xpcom/xpcAccessibleHyperText.h
@@ -18,17 +18,18 @@ namespace mozilla {
 namespace a11y {
 
 class xpcAccessibleHyperText : public xpcAccessibleGeneric,
                                public nsIAccessibleText,
                                public nsIAccessibleEditableText,
                                public nsIAccessibleHyperText
 {
 public:
-  xpcAccessibleHyperText(Accessible* aIntl) : xpcAccessibleGeneric(aIntl)
+  explicit xpcAccessibleHyperText(Accessible* aIntl) :
+    xpcAccessibleGeneric(aIntl)
   {
     if (mIntl->IsHyperText() && mIntl->AsHyperText()->IsTextRole())
       mSupportedIfaces |= eText;
   }
 
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_DECL_NSIACCESSIBLETEXT
--- a/accessible/xpcom/xpcAccessibleImage.h
+++ b/accessible/xpcom/xpcAccessibleImage.h
@@ -13,17 +13,18 @@
 
 namespace mozilla {
 namespace a11y {
 
 class xpcAccessibleImage : public xpcAccessibleGeneric,
                            public nsIAccessibleImage
 {
 public:
-  xpcAccessibleImage(Accessible* aIntl) : xpcAccessibleGeneric(aIntl) { }
+  explicit xpcAccessibleImage(Accessible* aIntl) :
+    xpcAccessibleGeneric(aIntl) { }
 
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_IMETHOD GetImagePosition(uint32_t aCoordType,
                               int32_t* aX, int32_t* aY) MOZ_FINAL;
   NS_IMETHOD GetImageSize(int32_t* aWidth, int32_t* aHeight) MOZ_FINAL;
 
 protected:
--- a/accessible/xpcom/xpcAccessibleTable.h
+++ b/accessible/xpcom/xpcAccessibleTable.h
@@ -15,17 +15,18 @@ namespace a11y {
 
 /**
  * XPCOM wrapper around TableAccessible class.
  */
 class xpcAccessibleTable : public xpcAccessibleGeneric,
                            public nsIAccessibleTable
 {
 public:
-  xpcAccessibleTable(Accessible* aIntl) : xpcAccessibleGeneric(aIntl) { }
+  explicit xpcAccessibleTable(Accessible* aIntl) :
+    xpcAccessibleGeneric(aIntl) { }
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessibleTable
   NS_IMETHOD GetCaption(nsIAccessible** aCaption) MOZ_FINAL;
   NS_IMETHOD GetSummary(nsAString& aSummary) MOZ_FINAL;
   NS_IMETHOD GetColumnCount(int32_t* aColumnCount) MOZ_FINAL;
   NS_IMETHOD GetRowCount(int32_t* aRowCount) MOZ_FINAL;
--- a/accessible/xpcom/xpcAccessibleTableCell.h
+++ b/accessible/xpcom/xpcAccessibleTableCell.h
@@ -16,17 +16,18 @@ namespace a11y {
 
 /**
  * XPCOM wrapper around TableAccessibleCell class.
  */
 class xpcAccessibleTableCell : public xpcAccessibleHyperText,
                                public nsIAccessibleTableCell
 {
 public:
-  xpcAccessibleTableCell(Accessible* aIntl) : xpcAccessibleHyperText(aIntl) { }
+  explicit xpcAccessibleTableCell(Accessible* aIntl) :
+    xpcAccessibleHyperText(aIntl) { }
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessibleTableCell
   NS_IMETHOD GetTable(nsIAccessibleTable** aTable) MOZ_FINAL;
   NS_IMETHOD GetColumnIndex(int32_t* aColIdx) MOZ_FINAL;
   NS_IMETHOD GetRowIndex(int32_t* aRowIdx) MOZ_FINAL;
   NS_IMETHOD GetColumnExtent(int32_t* aExtent) MOZ_FINAL;
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -171,17 +171,17 @@ Components.utils.import('resource://gre/
       'deviceinfo.hardware': hardware_info,
       'deviceinfo.firmware_revision': firmware_revision,
       'deviceinfo.product_model': product_model
     }
     lock.set(setting);
   }
 })();
 
-// =================== DevTools HUD ====================
+// =================== DevTools ====================
 
 let developerHUD;
 SettingsListener.observe('devtools.overlay', false, (value) => {
   if (value) {
     if (!developerHUD) {
       let scope = {};
       Services.scriptloader.loadSubScript('chrome://b2g/content/devtools/hud.js', scope);
       developerHUD = scope.developerHUD;
@@ -277,16 +277,46 @@ function setUpdateTrackingId() {
       Services.prefs.setCharPref('app.update.custom', trackingId);
     }
   } catch(e) {
     dump('Error getting tracking ID ' + e + '\n');
   }
 }
 setUpdateTrackingId();
 
+(function syncUpdatePrefs() {
+  // The update service reads the prefs from the default branch. This is by
+  // design, as explained in bug 302721 comment 43. If we are to successfully
+  // modify them, that's where we need to make our changes.
+  let defaultBranch = Services.prefs.getDefaultBranch(null);
+
+  function syncCharPref(prefName) {
+    SettingsListener.observe(prefName, null, function(value) {
+      // If set, propagate setting value to pref.
+      if (value) {
+        defaultBranch.setCharPref(prefName, value);
+        return;
+      }
+      // If unset, initialize setting to pref value.
+      try {
+        let value = defaultBranch.getCharPref(prefName);
+        if (value) {
+          let setting = {};
+          setting[prefName] = value;
+          window.navigator.mozSettings.createLock().set(setting);
+        }
+      } catch(e) {
+        console.log('Unable to read pref ' + prefName + ': ' + e);
+      }
+    });
+  }
+
+  syncCharPref('app.update.url');
+  syncCharPref('app.update.channel');
+})();
 
 // ================ Debug ================
 (function Composer2DSettingToPref() {
   //layers.composer.enabled can be enabled in three ways
   //In order of precedence they are:
   //
   //1. mozSettings "layers.composer.enabled"
   //2. a gecko pref "layers.composer.enabled"
@@ -462,23 +492,17 @@ let settingsToObserve = {
     resetToPref: true,
     defaultValue: ''
   },
   'accessibility.screenreader_quicknav_index': {
     prefName: 'accessibility.accessfu.quicknav_index',
     resetToPref: true,
     defaultValue: 0
   },
-  'app.update.channel': {
-    resetToPref: true
-  },
   'app.update.interval': 86400,
-  'app.update.url': {
-    resetToPref: true
-  },
   'apz.force-enable': {
     prefName: 'dom.browser_frames.useAsyncPanZoom',
     defaultValue: false
   },
   'apz.overscroll.enabled': true,
   'debug.fps.enabled': {
     prefName: 'layers.acceleration.draw-fps',
     defaultValue: false
--- a/b2g/components/B2GComponents.manifest
+++ b/b2g/components/B2GComponents.manifest
@@ -53,20 +53,16 @@ contract @mozilla.org/network/protocol;1
 # SmsProtocolHandler.js
 component {81ca20cb-0dad-4e32-8566-979c8998bd73} SmsProtocolHandler.js
 contract @mozilla.org/network/protocol;1?name=sms {81ca20cb-0dad-4e32-8566-979c8998bd73}
 
 # MailtoProtocolHandler.js
 component {50777e53-0331-4366-a191-900999be386c} MailtoProtocolHandler.js
 contract @mozilla.org/network/protocol;1?name=mailto {50777e53-0331-4366-a191-900999be386c}
 
-# YoutubeProtocolHandler.js
-component {c3f1b945-7e71-49c8-95c7-5ae9cc9e2bad} YoutubeProtocolHandler.js
-contract @mozilla.org/network/protocol;1?name=vnd.youtube {c3f1b945-7e71-49c8-95c7-5ae9cc9e2bad}
-
 # RecoveryService.js
 component {b3caca5d-0bb0-48c6-912b-6be6cbf08832} RecoveryService.js
 contract @mozilla.org/recovery-service;1 {b3caca5d-0bb0-48c6-912b-6be6cbf08832}
 
 # B2GAboutRedirector
 component {920400b1-cf8f-4760-a9c4-441417b15134} B2GAboutRedirector.js
 contract @mozilla.org/network/protocol/about;1?what=certerror {920400b1-cf8f-4760-a9c4-441417b15134}
 contract @mozilla.org/network/protocol/about;1?what=neterror {920400b1-cf8f-4760-a9c4-441417b15134}
deleted file mode 100644
--- a/b2g/components/YoutubeProtocolHandler.js
+++ /dev/null
@@ -1,64 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cu = Components.utils;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-
-XPCOMUtils.defineLazyGetter(this, "cpmm", function() {
-  return Cc["@mozilla.org/childprocessmessagemanager;1"]
-           .getService(Ci.nsIMessageSender);
-});
-
-function YoutubeProtocolHandler() {
-}
-
-YoutubeProtocolHandler.prototype = {
-  classID: Components.ID("{c3f1b945-7e71-49c8-95c7-5ae9cc9e2bad}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler]),
-
-  scheme: "vnd.youtube",
-  defaultPort: -1,
-  protocolFlags: Ci.nsIProtocolHandler.URI_NORELATIVE |
-                 Ci.nsIProtocolHandler.URI_NOAUTH |
-                 Ci.nsIProtocolHandler.URI_LOADABLE_BY_ANYONE,
-
-  // Sample URL:
-  // vnd.youtube:iNuKL2Gy_QM?vndapp=youtube_mobile&vndclient=mv-google&vndel=watch&vnddnc=1
-  // Note that there is no hostname, so we use URLTYPE_NO_AUTHORITY
-  newURI: function yt_phNewURI(aSpec, aOriginCharset, aBaseURI) {
-    let uri = Cc["@mozilla.org/network/standard-url;1"]
-              .createInstance(Ci.nsIStandardURL);
-    uri.init(Ci.nsIStandardURL.URLTYPE_NO_AUTHORITY, this.defaultPort,
-             aSpec, aOriginCharset, aBaseURI);
-    return uri.QueryInterface(Ci.nsIURI);
-  },
-
-  newChannel: function yt_phNewChannel(aURI) {
-    /*
-     * This isn't a real protocol handler. Instead of creating a channel
-     * we just send a message and throw an exception. This 'content-handler'
-     * message is handled in b2g/chrome/content/shell.js where it starts
-     * an activity request that will open the Video app. The video app
-     * includes code to handle this fake 'video/youtube' mime type
-     */
-    cpmm.sendAsyncMessage("content-handler", {
-      type: 'video/youtube', // A fake MIME type for the activity handler
-      url: aURI.spec         // The path component of this URL is the video id
-    });
-
-    throw Components.results.NS_ERROR_ILLEGAL_VALUE;
-  },
-
-  allowPort: function yt_phAllowPort(aPort, aScheme) {
-    return false;
-  }
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([YoutubeProtocolHandler]);
--- a/b2g/components/moz.build
+++ b/b2g/components/moz.build
@@ -20,17 +20,16 @@ EXTRA_COMPONENTS += [
     'MobileIdentityUIGlue.js',
     'OMAContentHandler.js',
     'PaymentGlue.js',
     'ProcessGlobal.js',
     'SmsProtocolHandler.js',
     'SystemMessageGlue.js',
     'TelProtocolHandler.js',
     'WebappsUpdateTimer.js',
-    'YoutubeProtocolHandler.js',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
     EXTRA_COMPONENTS += [
       'CommandLine.js',
       'OopCommandLine.js',
       'SimulatorScreen.js'
     ]
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="27a1d1baaa8e375b70e043efee67d5f2206c330b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="f46d56d812480bff7f3b35e8cacbedfa4d49edc5"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="314f305d3163cc094e6fe7701d95a98fc180b639"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a7631963e5f702a0c6027f5575e5e0deeededea6"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b9fe09fe470f7c22d70c98cc0458e49fdd4618c2"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="83760d213fb3bec7b4117d266fcfbf6fe2ba14ab"/>
   <project name="device/common" path="device/common" revision="6a2995683de147791e516aae2ccb31fdfbe2ad30"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,23 +14,23 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="70eb0cb0977d6295e7da8896f9efb9f3ca1c13ea">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="27a1d1baaa8e375b70e043efee67d5f2206c330b"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f46d56d812480bff7f3b35e8cacbedfa4d49edc5"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="314f305d3163cc094e6fe7701d95a98fc180b639"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c058843242068d0df7c107e09da31b53d2e08fa6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a7631963e5f702a0c6027f5575e5e0deeededea6"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b9fe09fe470f7c22d70c98cc0458e49fdd4618c2"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="52a1a862a8bac319652b8f82d9541ba40bfa45ce"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8986df0f82e15ac2798df0b6c2ee3435400677ac">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="27a1d1baaa8e375b70e043efee67d5f2206c330b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="f46d56d812480bff7f3b35e8cacbedfa4d49edc5"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="314f305d3163cc094e6fe7701d95a98fc180b639"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a7631963e5f702a0c6027f5575e5e0deeededea6"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b9fe09fe470f7c22d70c98cc0458e49fdd4618c2"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="27a1d1baaa8e375b70e043efee67d5f2206c330b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="f46d56d812480bff7f3b35e8cacbedfa4d49edc5"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="314f305d3163cc094e6fe7701d95a98fc180b639"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a7631963e5f702a0c6027f5575e5e0deeededea6"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b9fe09fe470f7c22d70c98cc0458e49fdd4618c2"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="1950e4760fa14688b83cdbb5acaa1af9f82ef434"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="ac6eb97a37035c09fb5ede0852f0881e9aadf9ad"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="737f591c5f95477148d26602c7be56cbea0cdeb9"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="51da9b1981be481b92a59a826d4d78dc73d0989a"/>
   <project name="device/common" path="device/common" revision="798a3664597e6041985feab9aef42e98d458bc3d"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,23 +14,23 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="70eb0cb0977d6295e7da8896f9efb9f3ca1c13ea">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="27a1d1baaa8e375b70e043efee67d5f2206c330b"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f46d56d812480bff7f3b35e8cacbedfa4d49edc5"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="314f305d3163cc094e6fe7701d95a98fc180b639"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c058843242068d0df7c107e09da31b53d2e08fa6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a7631963e5f702a0c6027f5575e5e0deeededea6"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b9fe09fe470f7c22d70c98cc0458e49fdd4618c2"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="52a1a862a8bac319652b8f82d9541ba40bfa45ce"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="27a1d1baaa8e375b70e043efee67d5f2206c330b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="f46d56d812480bff7f3b35e8cacbedfa4d49edc5"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="314f305d3163cc094e6fe7701d95a98fc180b639"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a7631963e5f702a0c6027f5575e5e0deeededea6"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b9fe09fe470f7c22d70c98cc0458e49fdd4618c2"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="a32003194f707f66a2d8cdb913ed1869f1926c5d"/>
   <project name="device/common" path="device/common" revision="96d4d2006c4fcb2f19a3fa47ab10cb409faa017b"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8986df0f82e15ac2798df0b6c2ee3435400677ac">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="27a1d1baaa8e375b70e043efee67d5f2206c330b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="f46d56d812480bff7f3b35e8cacbedfa4d49edc5"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="314f305d3163cc094e6fe7701d95a98fc180b639"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a7631963e5f702a0c6027f5575e5e0deeededea6"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b9fe09fe470f7c22d70c98cc0458e49fdd4618c2"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "8d483aad9a257678544e70bdfadee0e576cc3239", 
+    "revision": "9420e20475daffd4ff3f8e4b1537a4800346bcb6", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,22 +12,22 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="70eb0cb0977d6295e7da8896f9efb9f3ca1c13ea">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="27a1d1baaa8e375b70e043efee67d5f2206c330b"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f46d56d812480bff7f3b35e8cacbedfa4d49edc5"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="314f305d3163cc094e6fe7701d95a98fc180b639"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a7631963e5f702a0c6027f5575e5e0deeededea6"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b9fe09fe470f7c22d70c98cc0458e49fdd4618c2"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="746bc48f34f5060f90801925dcdd964030c1ab6d"/>
   <project name="platform/development" path="development" revision="2460485184bc8535440bb63876d4e63ec1b4770c"/>
   <project name="device/common" path="device/common" revision="0dcc1e03659db33b77392529466f9eb685cdd3c7"/>
   <project name="device/sample" path="device/sample" revision="68b1cb978a20806176123b959cb05d4fa8adaea4"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="70eb0cb0977d6295e7da8896f9efb9f3ca1c13ea">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="27a1d1baaa8e375b70e043efee67d5f2206c330b"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f46d56d812480bff7f3b35e8cacbedfa4d49edc5"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="314f305d3163cc094e6fe7701d95a98fc180b639"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8986df0f82e15ac2798df0b6c2ee3435400677ac">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="27a1d1baaa8e375b70e043efee67d5f2206c330b"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="f46d56d812480bff7f3b35e8cacbedfa4d49edc5"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="314f305d3163cc094e6fe7701d95a98fc180b639"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a7631963e5f702a0c6027f5575e5e0deeededea6"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b9fe09fe470f7c22d70c98cc0458e49fdd4618c2"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,22 +12,22 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="70eb0cb0977d6295e7da8896f9efb9f3ca1c13ea">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="27a1d1baaa8e375b70e043efee67d5f2206c330b"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f46d56d812480bff7f3b35e8cacbedfa4d49edc5"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="314f305d3163cc094e6fe7701d95a98fc180b639"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a7631963e5f702a0c6027f5575e5e0deeededea6"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b9fe09fe470f7c22d70c98cc0458e49fdd4618c2"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="e0a9ac010df3afaa47ba107192c05ac8b5516435"/>
   <project name="platform/development" path="development" revision="a384622f5fcb1d2bebb9102591ff7ae91fe8ed2d"/>
   <project name="device/common" path="device/common" revision="7c65ea240157763b8ded6154a17d3c033167afb7"/>
   <project name="device/sample" path="device/sample" revision="c328f3d4409db801628861baa8d279fb8855892f"/>
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -838,17 +838,16 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DL
 @BINPATH@/components/UpdatePrompt.js
 #endif
 @BINPATH@/components/WebappsUpdateTimer.js
 @BINPATH@/components/DirectoryProvider.js
 @BINPATH@/components/ActivitiesGlue.js
 @BINPATH@/components/ProcessGlobal.js
 @BINPATH@/components/OMAContentHandler.js
 @BINPATH@/components/PaymentGlue.js
-@BINPATH@/components/YoutubeProtocolHandler.js
 @BINPATH@/components/RecoveryService.js
 @BINPATH@/components/MailtoProtocolHandler.js
 @BINPATH@/components/SmsProtocolHandler.js
 @BINPATH@/components/TelProtocolHandler.js
 @BINPATH@/components/B2GAboutRedirector.js
 @BINPATH@/components/FilePicker.js
 @BINPATH@/components/HelperAppDialog.js
 @BINPATH@/components/DownloadsUI.js
--- a/browser/locales/all-locales
+++ b/browser/locales/all-locales
@@ -20,17 +20,16 @@ ja
 ja-JP-mac
 kk
 ko
 lt
 lv
 nb-NO
 nl
 nn-NO
-oc
 pl
 pt-BR
 pt-PT
 ru
 sk
 sl
 sv-SE
 th
--- a/content/base/src/Element.cpp
+++ b/content/base/src/Element.cpp
@@ -958,16 +958,26 @@ Element::CreateShadowRoot(ErrorResult& a
   nsXBLPrototypeBinding* protoBinding = new nsXBLPrototypeBinding();
   aError = protoBinding->Init(NS_LITERAL_CSTRING("shadowroot"),
                               docInfo, this, true);
   if (aError.Failed()) {
     delete protoBinding;
     return nullptr;
   }
 
+  nsIDocument* doc = GetCrossShadowCurrentDoc();
+  nsIContent* destroyedFramesFor = nullptr;
+  if (doc) {
+    nsIPresShell* shell = doc->GetShell();
+    if (shell) {
+      shell->DestroyFramesFor(this, &destroyedFramesFor);
+    }
+  }
+  MOZ_ASSERT(!GetPrimaryFrame());
+
   // Unlike for XBL, false is the default for inheriting style.
   protoBinding->SetInheritsStyle(false);
 
   // Calling SetPrototypeBinding takes ownership of protoBinding.
   docInfo->SetPrototypeBinding(NS_LITERAL_CSTRING("shadowroot"), protoBinding);
 
   nsRefPtr<ShadowRoot> shadowRoot = new ShadowRoot(this, nodeInfo.forget(),
                                                    protoBinding);
@@ -993,21 +1003,21 @@ Element::CreateShadowRoot(ErrorResult& a
   nsRefPtr<nsXBLBinding> xblBinding = new nsXBLBinding(shadowRoot, protoBinding);
   shadowRoot->SetAssociatedBinding(xblBinding);
   xblBinding->SetBoundElement(this);
 
   SetXBLBinding(xblBinding);
 
   // Recreate the frame for the bound content because binding a ShadowRoot
   // changes how things are rendered.
-  nsIDocument* doc = GetCrossShadowCurrentDoc();
   if (doc) {
-    nsIPresShell *shell = doc->GetShell();
+    MOZ_ASSERT(doc == GetCrossShadowCurrentDoc());
+    nsIPresShell* shell = doc->GetShell();
     if (shell) {
-      shell->RecreateFramesFor(this);
+      shell->CreateFramesFor(destroyedFramesFor);
     }
   }
 
   return shadowRoot.forget();
 }
 
 NS_IMPL_CYCLE_COLLECTION(DestinationInsertionPointList, mParent, mDestinationPoints)
 
--- a/content/base/src/nsDOMMutationObserver.cpp
+++ b/content/base/src/nsDOMMutationObserver.cpp
@@ -448,56 +448,92 @@ nsDOMMutationObserver::RescheduleForRun(
 }
 
 void
 nsDOMMutationObserver::Observe(nsINode& aTarget,
                                const mozilla::dom::MutationObserverInit& aOptions,
                                mozilla::ErrorResult& aRv)
 {
 
-  if (!(aOptions.mChildList || aOptions.mAttributes || aOptions.mCharacterData)) {
-    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+  bool childList = aOptions.mChildList;
+  bool attributes =
+    aOptions.mAttributes.WasPassed() &&
+    aOptions.mAttributes.Value();
+  bool characterData =
+    aOptions.mCharacterData.WasPassed() &&
+    aOptions.mCharacterData.Value();
+  bool subtree = aOptions.mSubtree;
+  bool attributeOldValue =
+    aOptions.mAttributeOldValue.WasPassed() &&
+    aOptions.mAttributeOldValue.Value();
+  bool characterDataOldValue =
+    aOptions.mCharacterDataOldValue.WasPassed() &&
+    aOptions.mCharacterDataOldValue.Value();
+
+  if (!aOptions.mAttributes.WasPassed() &&
+      (aOptions.mAttributeOldValue.WasPassed() ||
+       aOptions.mAttributeFilter.WasPassed())) {
+    attributes = true;
+  }
+
+  if (!aOptions.mCharacterData.WasPassed() &&
+      aOptions.mCharacterDataOldValue.WasPassed()) {
+    characterData = true;
+  }
+
+  if (!(childList || attributes || characterData)) {
+    aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
     return;
   }
-  if (aOptions.mAttributeOldValue && !aOptions.mAttributes) {
-    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+
+  if (aOptions.mAttributeOldValue.WasPassed() &&
+      aOptions.mAttributeOldValue.Value() &&
+      aOptions.mAttributes.WasPassed() &&
+      !aOptions.mAttributes.Value()) {
+    aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
     return;
   }
-  if (aOptions.mCharacterDataOldValue && !aOptions.mCharacterData) {
-    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+
+  if (aOptions.mAttributeFilter.WasPassed() &&
+      aOptions.mAttributes.WasPassed() &&
+      !aOptions.mAttributes.Value()) {
+    aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
+    return;
+  }
+
+  if (aOptions.mCharacterDataOldValue.WasPassed() &&
+      aOptions.mCharacterDataOldValue.Value() &&
+      aOptions.mCharacterData.WasPassed() &&
+      !aOptions.mCharacterData.Value()) {
+    aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
     return;
   }
 
   nsCOMArray<nsIAtom> filters;
   bool allAttrs = true;
   if (aOptions.mAttributeFilter.WasPassed()) {
     allAttrs = false;
     const mozilla::dom::Sequence<nsString>& filtersAsString =
       aOptions.mAttributeFilter.Value();
     uint32_t len = filtersAsString.Length();
-
-    if (len != 0 && !aOptions.mAttributes) {
-      aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
-      return;
-    }
     filters.SetCapacity(len);
 
     for (uint32_t i = 0; i < len; ++i) {
       nsCOMPtr<nsIAtom> a = do_GetAtom(filtersAsString[i]);
       filters.AppendObject(a);
     }
   }
 
   nsMutationReceiver* r = GetReceiverFor(&aTarget, true);
-  r->SetChildList(aOptions.mChildList);
-  r->SetAttributes(aOptions.mAttributes);
-  r->SetCharacterData(aOptions.mCharacterData);
-  r->SetSubtree(aOptions.mSubtree);
-  r->SetAttributeOldValue(aOptions.mAttributeOldValue);
-  r->SetCharacterDataOldValue(aOptions.mCharacterDataOldValue);
+  r->SetChildList(childList);
+  r->SetAttributes(attributes);
+  r->SetCharacterData(characterData);
+  r->SetSubtree(subtree);
+  r->SetAttributeOldValue(attributeOldValue);
+  r->SetCharacterDataOldValue(characterDataOldValue);
   r->SetAttributeFilter(filters);
   r->SetAllAttributes(allAttrs);
   r->RemoveClones();
 
 #ifdef DEBUG
   for (int32_t i = 0; i < mReceivers.Count(); ++i) {
     NS_WARN_IF_FALSE(mReceivers[i]->Target(),
                      "All the receivers should have a target!");
@@ -536,21 +572,21 @@ nsDOMMutationObserver::TakeRecords(
 void
 nsDOMMutationObserver::GetObservingInfo(nsTArray<Nullable<MutationObservingInfo> >& aResult)
 {
   aResult.SetCapacity(mReceivers.Count());
   for (int32_t i = 0; i < mReceivers.Count(); ++i) {
     MutationObservingInfo& info = aResult.AppendElement()->SetValue();
     nsMutationReceiver* mr = mReceivers[i];
     info.mChildList = mr->ChildList();
-    info.mAttributes = mr->Attributes();
-    info.mCharacterData = mr->CharacterData();
+    info.mAttributes.Construct(mr->Attributes());
+    info.mCharacterData.Construct(mr->CharacterData());
     info.mSubtree = mr->Subtree();
-    info.mAttributeOldValue = mr->AttributeOldValue();
-    info.mCharacterDataOldValue = mr->CharacterDataOldValue();
+    info.mAttributeOldValue.Construct(mr->AttributeOldValue());
+    info.mCharacterDataOldValue.Construct(mr->CharacterDataOldValue());
     nsCOMArray<nsIAtom>& filters = mr->AttributeFilter();
     if (filters.Count()) {
       info.mAttributeFilter.Construct();
       mozilla::dom::Sequence<nsString>& filtersAsStrings =
         info.mAttributeFilter.Value();
       for (int32_t j = 0; j < filters.Count(); ++j) {
         filtersAsStrings.AppendElement(nsDependentAtomString(filters[j]));
       }
--- a/content/base/test/test_mutationobservers.html
+++ b/content/base/test/test_mutationobservers.html
@@ -96,56 +96,49 @@ function runTest() {
 
   var e = null;
   try {
     m.observe(document, {});
   } catch (ex) {
     e = ex;
   }
   ok(e, "Should have thrown an exception");
-  is(e.name, "SyntaxError", "Should have thrown SyntaxError");
-  is(e.code, DOMException.SYNTAX_ERR, "Should have thrown DOMException.SYNTAX_ERR");
+  is(e.name, "TypeError", "Should have thrown TypeError");
 
   e = null;
   try {
     m.observe(document, { childList: true, attributeOldValue: true });
   } catch (ex) {
     e = ex;
   }
-  ok(e, "Should have thrown an exception");
-  is(e.name, "SyntaxError", "Should have thrown SyntaxError");
-  is(e.code, DOMException.SYNTAX_ERR, "Should have thrown DOMException.SYNTAX_ERR");
+  ok(!e, "Shouldn't have thrown an exception");
 
   e = null;
   try {
     m.observe(document, { childList: true, attributeFilter: ["foo"] });
   } catch (ex) {
     e = ex;
   }
-  ok(e, "Should have thrown an exception");
-  is(e.name, "SyntaxError", "Should have thrown SyntaxError");
-  is(e.code, DOMException.SYNTAX_ERR, "Should have thrown DOMException.SYNTAX_ERR");
+  ok(!e, "Shouldn't have thrown an exception");
 
   e = null;
   try {
     m.observe(document, { childList: true, characterDataOldValue: true });
   } catch (ex) {
     e = ex;
   }
-  ok(e, "Should have thrown an exception");
-  is(e.name, "SyntaxError", "Should have thrown SyntaxError");
-  is(e.code, DOMException.SYNTAX_ERR, "Should have thrown DOMException.SYNTAX_ERR");
+  ok(!e, "Shouldn't have thrown an exception");
 
   e = null;
   try {
     m.observe(document);
   } catch (ex) {
     e = ex;
   }
-  ok(e, "Should have thrown an exception");  
+  ok(e, "Should have thrown an exception");
 
   m = new M(function(records, observer) {
       is(observer, m, "2nd parameter should be the mutation observer");
       is(observer, this, "2nd parameter should be 'this'");
       is(records.length, 1, "Should have one record.");
       is(records[0].type, "attributes", "Should have got attributes record");
       is(records[0].target, div, "Should have got div as target");
       is(records[0].attributeName, "foo", "Should have got record about foo attribute");
--- a/content/media/fmp4/MP4Reader.cpp
+++ b/content/media/fmp4/MP4Reader.cpp
@@ -589,16 +589,22 @@ MP4Reader::ResetDecode()
 void
 MP4Reader::Output(TrackType aTrack, MediaData* aSample)
 {
 #ifdef LOG_SAMPLE_DECODE
   LOG("Decoded %s sample time=%lld dur=%lld",
       TrackTypeToStr(aTrack), aSample->mTime, aSample->mDuration);
 #endif
 
+  if (!aSample) {
+    NS_WARNING("MP4Reader::Output() passed a null sample");
+    Error(aTrack);
+    return;
+  }
+
   DecoderData& data = GetDecoderData(aTrack);
   // Don't accept output while we're flushing.
   MonitorAutoLock mon(data.mMonitor);
   if (data.mIsFlushing) {
     delete aSample;
     LOG("MP4Reader produced output while flushing, discarding.");
     mon.NotifyAll();
     return;
--- a/content/media/fmp4/eme/EMEH264Decoder.cpp
+++ b/content/media/fmp4/eme/EMEH264Decoder.cpp
@@ -151,25 +151,26 @@ EMEH264Decoder::Decoded(GMPVideoi420Fram
   // V plane (Cr)
   b.mPlanes[2].mData = aDecodedFrame->Buffer(kGMPVPlane);
   b.mPlanes[2].mStride = aDecodedFrame->Stride(kGMPVPlane);
   b.mPlanes[2].mHeight = height / 2;
   b.mPlanes[2].mWidth = width / 2;
   b.mPlanes[2].mOffset = 0;
   b.mPlanes[2].mSkip = 0;
 
+  gfx::IntRect pictureRegion(0, 0, width, height);
   VideoData *v = VideoData::Create(mVideoInfo,
-                                    mImageContainer,
-                                    mLastStreamOffset,
-                                    aDecodedFrame->Timestamp(),
-                                    aDecodedFrame->Duration(),
-                                    b,
-                                    false,
-                                    -1,
-                                    ToIntRect(mPictureRegion));
+                                   mImageContainer,
+                                   mLastStreamOffset,
+                                   aDecodedFrame->Timestamp(),
+                                   aDecodedFrame->Duration(),
+                                   b,
+                                   false,
+                                   -1,
+                                   pictureRegion);
   aDecodedFrame->Destroy();
   mCallback->Output(v);
 }
 
 void
 EMEH264Decoder::ReceivedDecodedReferenceFrame(const uint64_t aPictureId)
 {
   // Ignore.
@@ -256,17 +257,16 @@ EMEH264Decoder::GmpInit()
   rv = mGMP->InitDecode(codec,
                         codecSpecific,
                         this,
                         PR_GetNumberOfProcessors());
   NS_ENSURE_SUCCESS(rv, rv);
 
   mVideoInfo.mDisplay = nsIntSize(mConfig.display_width, mConfig.display_height);
   mVideoInfo.mHasVideo = true;
-  mPictureRegion = nsIntRect(0, 0, mConfig.display_width, mConfig.display_height);
 
   return NS_OK;
 }
 
 nsresult
 EMEH264Decoder::GmpInput(MP4Sample* aSample)
 {
   MOZ_ASSERT(IsOnGMPThread());
--- a/content/media/fmp4/eme/EMEH264Decoder.h
+++ b/content/media/fmp4/eme/EMEH264Decoder.h
@@ -94,17 +94,16 @@ private:
 
   nsCOMPtr<mozIGeckoMediaPluginService> mMPS;
   nsCOMPtr<nsIThread> mGMPThread;
   nsRefPtr<CDMProxy> mProxy;
   GMPVideoDecoderProxy* mGMP;
   GMPVideoHost* mHost;
 
   VideoInfo mVideoInfo;
-  nsIntRect mPictureRegion;
   const mp4_demuxer::VideoDecoderConfig& mConfig;
   nsRefPtr<layers::ImageContainer> mImageContainer;
   nsRefPtr<MediaTaskQueue> mTaskQueue;
   MediaDataDecoderCallback* mCallback;
   int64_t mLastStreamOffset;
   Monitor mMonitor;
   bool mFlushComplete;
 };
--- a/content/media/gmp/GMPParent.cpp
+++ b/content/media/gmp/GMPParent.cpp
@@ -354,31 +354,54 @@ GMPParent::Shutdown()
   if (!mDeleteProcessOnlyOnUnload) {
     // Destroy ourselves and rise from the fire to save memory
     nsRefPtr<GMPParent> self(this);
     mService->ReAddOnGMPThread(self);
   } // else we've been asked to die and stay dead
   MOZ_ASSERT(mState == GMPStateNotLoaded);
 }
 
+class NotifyGMPShutdownTask : public nsRunnable {
+public:
+  NotifyGMPShutdownTask(const nsAString& aNodeId)
+    : mNodeId(aNodeId)
+  {
+  }
+  NS_IMETHOD Run() {
+    MOZ_ASSERT(NS_IsMainThread());
+    nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
+    MOZ_ASSERT(obsService);
+    if (obsService) {
+      obsService->NotifyObservers(nullptr, "gmp-shutdown", mNodeId.get());
+    }
+    return NS_OK;
+  }
+  nsString mNodeId;
+};
+
 void
 GMPParent::DeleteProcess()
 {
   LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
 
   if (mState != GMPStateClosing) {
     // Don't Close() twice!
     // Probably remove when bug 1043671 is resolved
     mState = GMPStateClosing;
     Close();
   }
   mProcess->Delete();
   LOGD(("%s::%s: Shut down process %p", __CLASS__, __FUNCTION__, (void *) mProcess));
   mProcess = nullptr;
   mState = GMPStateNotLoaded;
+
+  NS_DispatchToMainThread(
+    new NotifyGMPShutdownTask(NS_ConvertUTF8toUTF16(mNodeId)),
+    NS_DISPATCH_NORMAL);
+
 }
 
 void
 GMPParent::VideoDecoderDestroyed(GMPVideoDecoderParent* aDecoder)
 {
   MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
 
   // If the constructor fails, we'll get called before it's added
@@ -979,16 +1002,20 @@ GMPParent::SetNodeId(const nsACString& a
   MOZ_ASSERT(CanBeUsedFrom(aNodeId));
   mNodeId = aNodeId;
 }
 
 bool
 GMPParent::RecvAsyncShutdownRequired()
 {
   LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
+  if (mAsyncShutdownRequired) {
+    NS_WARNING("Received AsyncShutdownRequired message more than once!");
+    return true;
+  }
   mAsyncShutdownRequired = true;
   mService->AsyncShutdownNeeded(this);
   return true;
 }
 
 bool
 GMPParent::RecvAsyncShutdownComplete()
 {
--- a/content/media/gmp/GMPService.cpp
+++ b/content/media/gmp/GMPService.cpp
@@ -513,16 +513,17 @@ GeckoMediaPluginService::GetGMPDecryptor
 }
 
 void
 GeckoMediaPluginService::AsyncShutdownNeeded(GMPParent* aParent)
 {
   LOGD(("%s::%s %p", __CLASS__, __FUNCTION__, aParent));
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
 
+  MOZ_ASSERT(!mAsyncShutdownPlugins.Contains(aParent));
   mAsyncShutdownPlugins.AppendElement(aParent);
 }
 
 void
 GeckoMediaPluginService::AsyncShutdownComplete(GMPParent* aParent)
 {
   LOGD(("%s::%s %p", __CLASS__, __FUNCTION__, aParent));
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
--- a/content/media/gtest/TestGMPCrossOrigin.cpp
+++ b/content/media/gtest/TestGMPCrossOrigin.cpp
@@ -101,16 +101,76 @@ GMPTestRunner::RunTestGMPCrossOrigin()
               encoder1->ParentID() == encoder2->ParentID());
 
   if (decoder1) decoder1->Close();
   if (decoder2) decoder2->Close();
   if (encoder1) encoder1->Close();
   if (encoder2) encoder2->Close();
 }
 
+static already_AddRefed<nsIThread>
+GetGMPThread()
+{
+  nsRefPtr<GeckoMediaPluginService> service =
+    GeckoMediaPluginService::GetGeckoMediaPluginService();
+  nsCOMPtr<nsIThread> thread;
+  EXPECT_TRUE(NS_SUCCEEDED(service->GetThread(getter_AddRefs(thread))));
+  return thread.forget();
+}
+
+class GMPShutdownObserver : public nsIRunnable
+                          , public nsIObserver {
+public:
+  GMPShutdownObserver(nsIRunnable* aShutdownTask,
+                      nsIRunnable* Continuation,
+                      const nsACString& aNodeId)
+    : mShutdownTask(aShutdownTask)
+    , mContinuation(Continuation)
+    , mNodeId(NS_ConvertUTF8toUTF16(aNodeId))
+  {}
+
+  NS_DECL_THREADSAFE_ISUPPORTS
+
+  NS_IMETHOD Run() MOZ_OVERRIDE {
+    MOZ_ASSERT(NS_IsMainThread());
+    nsCOMPtr<nsIObserverService> observerService =
+        mozilla::services::GetObserverService();
+    EXPECT_TRUE(observerService);
+    observerService->AddObserver(this, "gmp-shutdown", false);
+
+    nsCOMPtr<nsIThread> thread(GetGMPThread());
+    thread->Dispatch(mShutdownTask, NS_DISPATCH_NORMAL);
+    return NS_OK;
+  }
+
+  NS_IMETHOD Observe(nsISupports* aSubject,
+                     const char* aTopic,
+                     const char16_t* aSomeData) MOZ_OVERRIDE
+  {
+    if (!strcmp(aTopic, "gmp-shutdown") &&
+        mNodeId.Equals(nsDependentString(aSomeData))) {
+      nsCOMPtr<nsIObserverService> observerService =
+          mozilla::services::GetObserverService();
+      EXPECT_TRUE(observerService);
+      observerService->RemoveObserver(this, "gmp-shutdown");
+      nsCOMPtr<nsIThread> thread(GetGMPThread());
+      thread->Dispatch(mContinuation, NS_DISPATCH_NORMAL);
+    }
+    return NS_OK;
+  }
+
+private:
+  virtual ~GMPShutdownObserver() {}
+  nsRefPtr<nsIRunnable> mShutdownTask;
+  nsRefPtr<nsIRunnable> mContinuation;
+  const nsString mNodeId;
+};
+
+NS_IMPL_ISUPPORTS(GMPShutdownObserver, nsIRunnable, nsIObserver)
+
 class NotifyObserversTask : public nsRunnable {
 public:
   NotifyObserversTask(const char* aTopic)
     : mTopic(aTopic)
   {}
   NS_IMETHOD Run() {
     MOZ_ASSERT(NS_IsMainThread());
     nsCOMPtr<nsIObserverService> observerService =
@@ -225,25 +285,16 @@ AssertIsOnGMPThread()
   service->GetThread(getter_AddRefs(thread));
   MOZ_ASSERT(thread);
   nsCOMPtr<nsIThread> currentThread;
   DebugOnly<nsresult> rv = NS_GetCurrentThread(getter_AddRefs(currentThread));
   MOZ_ASSERT(NS_SUCCEEDED(rv));
   MOZ_ASSERT(currentThread == thread);
 }
 
-static already_AddRefed<nsIThread>
-GetGMPThread()
-{
-  nsRefPtr<GeckoMediaPluginService> service =
-    GeckoMediaPluginService::GetGeckoMediaPluginService();
-  nsCOMPtr<nsIThread> thread;
-  EXPECT_TRUE(NS_SUCCEEDED(service->GetThread(getter_AddRefs(thread))));
-  return thread.forget();
-}
 class GMPStorageTest : public GMPDecryptorProxyCallback
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GMPStorageTest)
 
   void DoTest(void (GMPStorageTest::*aTestMethod)()) {
     EnsureNSSInitializedChromeOrContent();
     nsCOMPtr<nsIThread> thread(GetGMPThread());
     ClearGMPStorage(NS_NewRunnableMethod(this, aTestMethod), thread);
@@ -318,25 +369,23 @@ class GMPStorageTest : public GMPDecrypt
 
   void CreateDecryptor(const nsAString& aOrigin,
                        const nsAString& aTopLevelOrigin,
                        bool aInPBMode) {
     nsRefPtr<GeckoMediaPluginService> service =
       GeckoMediaPluginService::GetGeckoMediaPluginService();
     EXPECT_TRUE(service);
 
-    const nsCString nodeId = GetNodeId(aOrigin,
-                                       aTopLevelOrigin,
-                                       aInPBMode);
-    EXPECT_TRUE(!nodeId.IsEmpty());
+    mNodeId = GetNodeId(aOrigin, aTopLevelOrigin, aInPBMode);
+    EXPECT_TRUE(!mNodeId.IsEmpty());
 
     nsTArray<nsCString> tags;
     tags.AppendElement(NS_LITERAL_CSTRING("fake"));
 
-    nsresult rv = service->GetGMPDecryptor(&tags, nodeId, &mDecryptor);
+    nsresult rv = service->GetGMPDecryptor(&tags, mNodeId, &mDecryptor);
     EXPECT_TRUE(NS_SUCCEEDED(rv));
     EXPECT_TRUE(!!mDecryptor);
 
     if (mDecryptor) {
       mDecryptor->Init(this);
     }
   }
 
@@ -391,17 +440,16 @@ class GMPStorageTest : public GMPDecrypt
                     false);
 
     Expect(NS_LITERAL_CSTRING("retrieve crossOriginTestRecordId succeeded (length 0 bytes)"),
            NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
     Update(NS_LITERAL_CSTRING("retrieve crossOriginTestRecordId"));
   }
 
   void TestPBStorage() {
-
     // Open decryptor on one, origin, write a record, close decryptor,
     // open another, and test that record can be read, close decryptor,
     // then send pb-last-context-closed notification, then open decryptor
     // and check that it can't read that data; it should have been purged.
     CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
                     NS_LITERAL_STRING("pb2.com"),
                     true);
 
@@ -435,34 +483,109 @@ class GMPStorageTest : public GMPDecrypt
     CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
                     NS_LITERAL_STRING("pb2.com"),
                     true);
 
     Expect(NS_LITERAL_CSTRING("retrieve pbdata succeeded (length 0 bytes)"),
            NS_NewRunnableMethod(this,
               &GMPStorageTest::SetFinished));
     Update(NS_LITERAL_CSTRING("retrieve pbdata"));
+  }
 
+  void CreateAsyncShutdownTimeoutGMP(const nsAString& aOrigin1,
+                                     const nsAString& aOrigin2) {
+    CreateDecryptor(aOrigin1, aOrigin2, false);
+    Update(NS_LITERAL_CSTRING("shutdown-mode timeout"));
+    Shutdown();
+  }
+
+  void TestAsyncShutdownTimeout() {
+    // Create decryptors that timeout in their async shutdown.
+    // If the gtest hangs on shutdown, test fails!
+    CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("example7.com"),
+                                  NS_LITERAL_STRING("example8.com"));
+    CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("example9.com"),
+                                  NS_LITERAL_STRING("example10.com"));
+    CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("example11.com"),
+                                  NS_LITERAL_STRING("example12.com"));
+    SetFinished();
+  };
+
+  void TestAsyncShutdownStorage() {
+    // Test that a GMP can write to storage during shutdown, and retrieve
+    // that written data in a subsequent session.
+    CreateDecryptor(NS_LITERAL_STRING("example13.com"),
+                    NS_LITERAL_STRING("example14.com"),
+                    false);
+
+    // Instruct the GMP to write a token (the current timestamp, so it's
+    // unique) during async shutdown, then shutdown the plugin, re-create
+    // it, and check that the token was successfully stored.
+    auto t = time(0);
+    nsCString update("shutdown-mode token ");
+    nsCString token;
+    token.AppendInt((int64_t)t);
+    update.Append(token);
+
+    // Wait for a response from the GMP, so we know it's had time to receive
+    // the token.
+    nsCString response("shutdown-token received ");
+    response.Append(token);
+    Expect(response, NS_NewRunnableMethodWithArg<nsCString>(this,
+      &GMPStorageTest::TestAsyncShutdownStorage_ReceivedShutdownToken, token));
+
+    Update(update);
+  }
+
+  void TestAsyncShutdownStorage_ReceivedShutdownToken(const nsCString& aToken) {
+    ShutdownThen(NS_NewRunnableMethodWithArg<nsCString>(this,
+      &GMPStorageTest::TestAsyncShutdownStorage_AsyncShutdownComplete, aToken));
+  }
+
+  void TestAsyncShutdownStorage_AsyncShutdownComplete(const nsCString& aToken) {
+    // Create a new instance of the plugin, retrieve the token written
+    // during shutdown and verify it is correct.
+    CreateDecryptor(NS_LITERAL_STRING("example13.com"),
+                    NS_LITERAL_STRING("example14.com"),
+                    false);
+    nsCString response("retrieved shutdown-token ");
+    response.Append(aToken);
+    Expect(response,
+           NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
+    Update(NS_LITERAL_CSTRING("retrieve-shutdown-token"));
   }
 
   void Expect(const nsCString& aMessage, nsIRunnable* aContinuation) {
     mExpected.AppendElement(ExpectedMessage(aMessage, aContinuation));
   }
 
   void AwaitFinished() {
     while (!mFinished) {
       NS_ProcessNextEvent(nullptr, true);
     }
     mFinished = false;
   }
 
+  void ShutdownThen(nsIRunnable* aContinuation) {
+    EXPECT_TRUE(!!mDecryptor);
+    if (!mDecryptor) {
+      return;
+    }
+    EXPECT_FALSE(mNodeId.IsEmpty());
+    nsRefPtr<GMPShutdownObserver> task(
+      new GMPShutdownObserver(NS_NewRunnableMethod(this, &GMPStorageTest::Shutdown),
+                              aContinuation, mNodeId));
+    NS_DispatchToMainThread(task, NS_DISPATCH_NORMAL);
+  }
+
   void Shutdown() {
     if (mDecryptor) {
       mDecryptor->Close();
       mDecryptor = nullptr;
+      mNodeId = EmptyCString();
     }
   }
 
   void Dummy() {
   }
 
   void SetFinished() {
     mFinished = true;
@@ -526,16 +649,17 @@ private:
     nsRefPtr<nsIRunnable> mContinuation;
   };
 
   nsTArray<ExpectedMessage> mExpected;
 
   GMPDecryptorProxy* mDecryptor;
   Monitor mMonitor;
   Atomic<bool> mFinished;
+  nsCString mNodeId;
 };
 
 void
 GMPTestRunner::DoTest(void (GMPTestRunner::*aTestMethod)())
 {
   nsRefPtr<GeckoMediaPluginService> service =
     GeckoMediaPluginService::GetGeckoMediaPluginService();
   nsCOMPtr<nsIThread> thread;
@@ -568,8 +692,18 @@ TEST(GeckoMediaPlugins, GMPStorageCrossO
   nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
   runner->DoTest(&GMPStorageTest::TestCrossOriginStorage);
 }
 
 TEST(GeckoMediaPlugins, GMPStoragePrivateBrowsing) {
   nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
   runner->DoTest(&GMPStorageTest::TestPBStorage);
 }
+
+TEST(GeckoMediaPlugins, GMPStorageAsyncShutdownTimeout) {
+  nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
+  runner->DoTest(&GMPStorageTest::TestAsyncShutdownTimeout);
+}
+
+TEST(GeckoMediaPlugins, GMPStorageAsyncShutdownStorage) {
+  nsRefPtr<GMPStorageTest> runner = new GMPStorageTest();
+  runner->DoTest(&GMPStorageTest::TestAsyncShutdownStorage);
+}
--- a/dom/base/WindowNamedPropertiesHandler.h
+++ b/dom/base/WindowNamedPropertiesHandler.h
@@ -15,40 +15,39 @@ namespace dom {
 class WindowNamedPropertiesHandler : public BaseDOMProxyHandler
 {
 public:
   MOZ_CONSTEXPR WindowNamedPropertiesHandler()
     : BaseDOMProxyHandler(nullptr, /* hasPrototype = */ true)
   {
   }
   virtual bool
-  preventExtensions(JSContext* aCx, JS::Handle<JSObject*> aProxy) const MOZ_OVERRIDE
-  {
-    // Throw a TypeError, per WebIDL.
-    JS_ReportErrorNumber(aCx, js_GetErrorMessage, nullptr,
-                         JSMSG_CANT_CHANGE_EXTENSIBILITY);
-    return false;
-  }
-  virtual bool
   getOwnPropDescriptor(JSContext* aCx, JS::Handle<JSObject*> aProxy,
                        JS::Handle<jsid> aId,
                        bool /* unused */,
                        JS::MutableHandle<JSPropertyDescriptor> aDesc)
                        const MOZ_OVERRIDE;
   virtual bool
   defineProperty(JSContext* aCx, JS::Handle<JSObject*> aProxy,
                  JS::Handle<jsid> aId,
                  JS::MutableHandle<JSPropertyDescriptor> aDesc) const MOZ_OVERRIDE;
   virtual bool
   ownPropNames(JSContext* aCx, JS::Handle<JSObject*> aProxy, unsigned flags,
                JS::AutoIdVector& aProps) const MOZ_OVERRIDE;
   virtual bool
   delete_(JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
           bool* aBp) const MOZ_OVERRIDE;
   virtual bool
+  preventExtensions(JSContext* aCx, JS::Handle<JSObject*> aProxy,
+                    bool *succeeded) const MOZ_OVERRIDE
+  {
+    *succeeded = false;
+    return true;
+  }
+  virtual bool
   isExtensible(JSContext* aCx, JS::Handle<JSObject*> aProxy,
                bool* aIsExtensible) const MOZ_OVERRIDE
   {
     *aIsExtensible = true;
     return true;
   }
   virtual const char*
   className(JSContext *aCx, JS::Handle<JSObject*> aProxy) const MOZ_OVERRIDE
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -615,20 +615,21 @@ public:
   virtual bool ownPropertyKeys(JSContext *cx,
                                JS::Handle<JSObject*> proxy,
                                JS::AutoIdVector &props) const MOZ_OVERRIDE;
   virtual bool delete_(JSContext *cx, JS::Handle<JSObject*> proxy,
                        JS::Handle<jsid> id,
                        bool *bp) const MOZ_OVERRIDE;
   virtual bool enumerate(JSContext *cx, JS::Handle<JSObject*> proxy,
                          JS::AutoIdVector &props) const MOZ_OVERRIDE;
+  virtual bool preventExtensions(JSContext *cx,
+                                 JS::Handle<JSObject*> proxy,
+                                 bool *succeeded) const MOZ_OVERRIDE;
   virtual bool isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible)
                             const MOZ_OVERRIDE;
-  virtual bool preventExtensions(JSContext *cx,
-                                 JS::Handle<JSObject*> proxy) const MOZ_OVERRIDE;
   virtual bool has(JSContext *cx, JS::Handle<JSObject*> proxy,
                    JS::Handle<jsid> id, bool *bp) const MOZ_OVERRIDE;
   virtual bool get(JSContext *cx, JS::Handle<JSObject*> proxy,
                    JS::Handle<JSObject*> receiver,
                    JS::Handle<jsid> id,
                    JS::MutableHandle<JS::Value> vp) const MOZ_OVERRIDE;
   virtual bool set(JSContext *cx, JS::Handle<JSObject*> proxy,
                    JS::Handle<JSObject*> receiver,
@@ -701,37 +702,16 @@ const js::Class OuterWindowProxyClass =
         PROXY_MAKE_EXT(
             nullptr, /* outerObject */
             js::proxy_innerObject,
             nullptr, /* iteratorObject */
             false,   /* isWrappedNative */
             nsOuterWindowProxy::ObjectMoved
         ));
 
-bool
-nsOuterWindowProxy::isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy,
-                                 bool *extensible) const
-{
-  // If [[Extensible]] could be false, then navigating a window could navigate
-  // to a window that's [[Extensible]] after being at one that wasn't: an
-  // invariant violation.  So always report true for this.
-  *extensible = true;
-  return true;
-}
-
-bool
-nsOuterWindowProxy::preventExtensions(JSContext *cx,
-                                      JS::Handle<JSObject*> proxy) const
-{
-  // See above.
-  JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
-                       JSMSG_CANT_CHANGE_EXTENSIBILITY);
-  return false;
-}
-
 const char *
 nsOuterWindowProxy::className(JSContext *cx, JS::Handle<JSObject*> proxy) const
 {
     MOZ_ASSERT(js::IsProxy(proxy));
 
     return "Window";
 }
 
@@ -860,16 +840,37 @@ nsOuterWindowProxy::enumerate(JSContext 
   JS::AutoIdVector innerProps(cx);
   if (!js::Wrapper::enumerate(cx, proxy, innerProps)) {
     return false;
   }
   return js::AppendUnique(cx, props, innerProps);
 }
 
 bool
+nsOuterWindowProxy::preventExtensions(JSContext *cx,
+                                      JS::Handle<JSObject*> proxy,
+                                      bool *succeeded) const
+{
+  // If [[Extensible]] could be false, then navigating a window could navigate
+  // to a window that's [[Extensible]] after being at one that wasn't: an
+  // invariant violation.  So never change a window's extensibility.
+  *succeeded = false;
+  return true;
+}
+
+bool
+nsOuterWindowProxy::isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy,
+                                 bool *extensible) const
+{
+  // See above.
+  *extensible = true;
+  return true;
+}
+
+bool
 nsOuterWindowProxy::has(JSContext *cx, JS::Handle<JSObject*> proxy,
                         JS::Handle<jsid> id, bool *bp) const
 {
   if (nsCOMPtr<nsIDOMWindow> frame = GetSubframeWindow(cx, proxy, id)) {
     *bp = true;
     return true;
   }
 
--- a/dom/bindings/DOMJSProxyHandler.cpp
+++ b/dom/bindings/DOMJSProxyHandler.cpp
@@ -136,30 +136,29 @@ DOMProxyHandler::EnsureExpandoObject(JSC
 
   cache->SetPreservingWrapper(true);
   js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, ObjectValue(*expando));
 
   return expando;
 }
 
 bool
-DOMProxyHandler::isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible) const
+DOMProxyHandler::preventExtensions(JSContext *cx, JS::Handle<JSObject*> proxy,
+                                   bool *succeeded) const
 {
   // always extensible per WebIDL
-  *extensible = true;
+  *succeeded = false;
   return true;
 }
 
 bool
-DOMProxyHandler::preventExtensions(JSContext *cx, JS::Handle<JSObject*> proxy) const
+DOMProxyHandler::isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible) const
 {
-  // Throw a TypeError, per WebIDL.
-  JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
-                       JSMSG_CANT_CHANGE_EXTENSIBILITY);
-  return false;
+  *extensible = true;
+  return true;
 }
 
 bool
 BaseDOMProxyHandler::getPropertyDescriptor(JSContext* cx,
                                            JS::Handle<JSObject*> proxy,
                                            JS::Handle<jsid> id,
                                            MutableHandle<JSPropertyDescriptor> desc) const
 {
--- a/dom/bindings/DOMJSProxyHandler.h
+++ b/dom/bindings/DOMJSProxyHandler.h
@@ -110,19 +110,20 @@ public:
     bool unused;
     return defineProperty(cx, proxy, id, desc, &unused);
   }
   virtual bool defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
                               JS::MutableHandle<JSPropertyDescriptor> desc, bool* defined)
                               const;
   bool delete_(JSContext* cx, JS::Handle<JSObject*> proxy,
                JS::Handle<jsid> id, bool* bp) const MOZ_OVERRIDE;
+  bool preventExtensions(JSContext *cx, JS::Handle<JSObject*> proxy,
+                         bool *succeeded) const MOZ_OVERRIDE;
   bool isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible)
                     const MOZ_OVERRIDE;
-  bool preventExtensions(JSContext *cx, JS::Handle<JSObject*> proxy) const MOZ_OVERRIDE;
   bool has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
            bool* bp) const MOZ_OVERRIDE;
   bool set(JSContext *cx, JS::Handle<JSObject*> proxy, JS::Handle<JSObject*> receiver,
            JS::Handle<jsid> id, bool strict, JS::MutableHandle<JS::Value> vp)
            const MOZ_OVERRIDE;
 
   /*
    * If assigning to proxy[id] hits a named setter with OverrideBuiltins or
--- a/dom/contacts/fallback/ContactDB.jsm
+++ b/dom/contacts/fallback/ContactDB.jsm
@@ -939,59 +939,51 @@ ContactDB.prototype = {
   },
 
   removeObjectFromCache: function CDB_removeObjectFromCache(aObjectId, aCallback, aFailureCb) {
     if (DEBUG) debug("removeObjectFromCache: " + aObjectId);
     if (!aObjectId) {
       if (DEBUG) debug("No object ID passed");
       return;
     }
-    this.newTxn("readwrite", SAVED_GETALL_STORE_NAME, function(txn, store) {
+    this.newTxn("readwrite", STORE_NAME, function(txn, store) {
       store.openCursor().onsuccess = function(e) {
         let cursor = e.target.result;
         if (cursor) {
           for (let i = 0; i < cursor.value.length; ++i) {
             if (cursor.value[i] == aObjectId) {
               if (DEBUG) debug("id matches cache");
               cursor.value.splice(i, 1);
               cursor.update(cursor.value);
               break;
             }
           }
           cursor.continue();
         } else {
-          aCallback();
+          aCallback(txn);
         }
       }.bind(this);
     }.bind(this), null,
     function(errorMsg) {
       aFailureCb(errorMsg);
     });
   },
 
-  // Invalidate the entire cache. It will be incrementally regenerated on demand
-  // See getCacheForQuery
-  invalidateCache: function CDB_invalidateCache(aErrorCb) {
-    if (DEBUG) debug("invalidate cache");
-    this.newTxn("readwrite", SAVED_GETALL_STORE_NAME, function (txn, store) {
-      store.clear();
-    }, null, aErrorCb);
-  },
-
   incrementRevision: function CDB_incrementRevision(txn) {
     let revStore = txn.objectStore(REVISION_STORE);
     revStore.get(REVISION_KEY).onsuccess = function(e) {
       revStore.put(parseInt(e.target.result, 10) + 1, REVISION_KEY);
     };
   },
 
   saveContact: function CDB_saveContact(aContact, successCb, errorCb) {
     let contact = this.makeImport(aContact);
-    this.newTxn("readwrite", STORE_NAME, function (txn, store) {
+    this.newTxn("readwrite", this.dbStoreNames, function (txn, stores) {
       if (DEBUG) debug("Going to update" + JSON.stringify(contact));
+      let store = txn.objectStore(STORE_NAME);
 
       // Look up the existing record and compare the update timestamp.
       // If no record exists, just add the new entry.
       let newRequest = store.get(contact.id);
       newRequest.onsuccess = function (event) {
         if (!event.target.result) {
           if (DEBUG) debug("new record!")
           this.updateRecordMetadata(contact);
@@ -1004,32 +996,35 @@ ContactDB.prototype = {
             return;
           } else {
             if (DEBUG) debug("rev check OK");
             contact.published = event.target.result.published;
             contact.updated = new Date();
             store.put(contact);
           }
         }
-        this.invalidateCache(errorCb);
+        // Invalidate the entire cache. It will be incrementally regenerated on demand
+        // See getCacheForQuery
+        let (getAllStore = txn.objectStore(SAVED_GETALL_STORE_NAME)) {
+          getAllStore.clear().onerror = errorCb;
+        }
       }.bind(this);
 
       this.incrementRevision(txn);
     }.bind(this), successCb, errorCb);
   },
 
   removeContact: function removeContact(aId, aSuccessCb, aErrorCb) {
     if (DEBUG) debug("removeContact: " + aId);
-    this.removeObjectFromCache(aId, function() {
-      this.newTxn("readwrite", STORE_NAME, function(txn, store) {
-        store.delete(aId).onsuccess = function() {
-          aSuccessCb();
-        };
-        this.incrementRevision(txn);
-      }.bind(this), null, aErrorCb);
+    this.removeObjectFromCache(aId, function(txn) {
+      let store = txn.objectStore(STORE_NAME)
+      store.delete(aId).onsuccess = function() {
+        aSuccessCb();
+      };
+      this.incrementRevision(txn);
     }.bind(this), aErrorCb);
   },
 
   clear: function clear(aSuccessCb, aErrorCb) {
     this.newTxn("readwrite", STORE_NAME, function (txn, store) {
       if (DEBUG) debug("Going to clear all!");
       store.clear();
       this.incrementRevision(txn);
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -77,16 +77,17 @@
 #include "SandboxHal.h"
 #include "nsDebugImpl.h"
 #include "nsHashPropertyBag.h"
 #include "nsLayoutStylesheetCache.h"
 #include "nsIJSRuntimeService.h"
 #include "nsThreadManager.h"
 #include "nsAnonymousTemporaryFile.h"
 #include "nsISpellChecker.h"
+#include "nsClipboardProxy.h"
 
 #include "IHistory.h"
 #include "nsNetUtil.h"
 
 #include "base/message_loop.h"
 #include "base/process_util.h"
 #include "base/task.h"
 
@@ -715,19 +716,25 @@ ContentChild::InitXPCOM()
         return;
     }
 
     mConsoleListener = new ConsoleListener(this);
     if (NS_FAILED(svc->RegisterListener(mConsoleListener)))
         NS_WARNING("Couldn't register console listener for child process");
 
     bool isOffline;
-    SendGetXPCOMProcessAttributes(&isOffline, &mAvailableDictionaries);
+    ClipboardCapabilities clipboardCaps;
+    SendGetXPCOMProcessAttributes(&isOffline, &mAvailableDictionaries, &clipboardCaps);
     RecvSetOffline(isOffline);
 
+    nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1"));
+    if (nsCOMPtr<nsIClipboardProxy> clipboardProxy = do_QueryInterface(clipboard)) {
+        clipboardProxy->SetCapabilities(clipboardCaps);
+    }
+
     DebugOnly<FileUpdateDispatcher*> observer = FileUpdateDispatcher::GetSingleton();
     NS_ASSERTION(observer, "FileUpdateDispatcher is null");
 
     // This object is held alive by the observer service.
     nsRefPtr<SystemMessageHandledObserver> sysMsgObserver =
         new SystemMessageHandledObserver();
     sysMsgObserver->Init();
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -2497,18 +2497,23 @@ ContentParent::RecvAddNewProcess(const u
     // Resend pref updates to the forked child.
     for (int i = 0; i < numNuwaPrefUpdates; i++) {
         content->SendPreferenceUpdate(sNuwaPrefUpdates->ElementAt(i));
     }
 
     // Update offline settings.
     bool isOffline;
     InfallibleTArray<nsString> unusedDictionaries;
-    RecvGetXPCOMProcessAttributes(&isOffline, &unusedDictionaries);
+    ClipboardCapabilities clipboardCaps;
+    RecvGetXPCOMProcessAttributes(&isOffline, &unusedDictionaries,
+                                  &clipboardCaps);
     content->SendSetOffline(isOffline);
+    MOZ_ASSERT(!clipboardCaps.supportsSelectionClipboard() &&
+               !clipboardCaps.supportsFindClipboard(),
+               "Unexpected values");
 
     PreallocatedProcessManager::PublishSpareProcess(content);
     return true;
 #else
     NS_ERROR("ContentParent::RecvAddNewProcess() not implemented!");
     return false;
 #endif
 }
@@ -2782,28 +2787,38 @@ ContentParent::RecvGetProcessAttributes(
     *aIsForApp = IsForApp();
     *aIsForBrowser = mIsForBrowser;
 
     return true;
 }
 
 bool
 ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline,
-                                             InfallibleTArray<nsString>* dictionaries)
+                                             InfallibleTArray<nsString>* dictionaries,
+                                             ClipboardCapabilities* clipboardCaps)
 {
     nsCOMPtr<nsIIOService> io(do_GetIOService());
     MOZ_ASSERT(io, "No IO service?");
     DebugOnly<nsresult> rv = io->GetOffline(aIsOffline);
     MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting offline?");
 
     nsCOMPtr<nsISpellChecker> spellChecker(do_GetService(NS_SPELLCHECKER_CONTRACTID));
     MOZ_ASSERT(spellChecker, "No spell checker?");
 
     spellChecker->GetDictionaryList(dictionaries);
 
+    nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1"));
+    MOZ_ASSERT(clipboard, "No clipboard?");
+
+    rv = clipboard->SupportsSelectionClipboard(&clipboardCaps->supportsSelectionClipboard());
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+    rv = clipboard->SupportsFindClipboard(&clipboardCaps->supportsFindClipboard());
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+
     return true;
 }
 
 mozilla::jsipc::PJavaScriptParent *
 ContentParent::AllocPJavaScriptParent()
 {
     MOZ_ASSERT(!ManagedPJavaScriptParent().Length());
     return nsIContentParent::AllocPJavaScriptParent();
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -408,17 +408,18 @@ private:
     PBackgroundParent*
     AllocPBackgroundParent(Transport* aTransport, ProcessId aOtherProcess)
                            MOZ_OVERRIDE;
 
     virtual bool RecvGetProcessAttributes(uint64_t* aId,
                                           bool* aIsForApp,
                                           bool* aIsForBrowser) MOZ_OVERRIDE;
     virtual bool RecvGetXPCOMProcessAttributes(bool* aIsOffline,
-                                               InfallibleTArray<nsString>* dictionaries)
+                                               InfallibleTArray<nsString>* dictionaries,
+                                               ClipboardCapabilities* clipboardCaps)
         MOZ_OVERRIDE;
 
     virtual bool DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*) MOZ_OVERRIDE;
 
     virtual bool DeallocPRemoteSpellcheckEngineParent(PRemoteSpellcheckEngineParent*) MOZ_OVERRIDE;
     virtual PBrowserParent* AllocPBrowserParent(const IPCTabContext& aContext,
                                                 const uint32_t& aChromeFlags,
                                                 const uint64_t& aId,
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -310,16 +310,21 @@ struct VolumeInfo {
   int32_t mountGeneration;
   bool isMediaPresent;
   bool isSharing;
   bool isFormatting;
   bool isFake;
   bool isUnmounting;
 };
 
+struct ClipboardCapabilities {
+  bool supportsSelectionClipboard;
+  bool supportsFindClipboard;
+};
+
 union MaybeFileDesc {
     FileDescriptor;
     void_t;
 };
 
 prio(normal upto high) intr protocol PContent
 {
     parent opens PCompositor;
@@ -512,17 +517,18 @@ parent:
      * |isForBrowser|, we're loading <browser>.  When |!isForApp &&
      * !isForBrowser|, we're probably loading <xul:browser remote>.
      *
      * Keep the return values in sync with PBrowser()!
      */
     sync GetProcessAttributes()
         returns (uint64_t id, bool isForApp, bool isForBrowser);
     sync GetXPCOMProcessAttributes()
-        returns (bool isOffline, nsString[] dictionaries);
+        returns (bool isOffline, nsString[] dictionaries,
+                 ClipboardCapabilities clipboardCaps);
 
     sync CreateChildProcess(IPCTabContext context,
                             ProcessPriority priority)
         returns (uint64_t id, bool isForApp, bool isForBrowser);
     intr BridgeToChildProcess(uint64_t id);
 
     async PJavaScript();
 
--- a/dom/media/gmp-plugin/gmp-fake.cpp
+++ b/dom/media/gmp-plugin/gmp-fake.cpp
@@ -397,17 +397,20 @@ extern "C" {
   GMPGetAPI (const char* aApiName, void* aHostAPI, void** aPluginApi) {
     if (!strcmp (aApiName, "decode-video")) {
       *aPluginApi = new FakeVideoDecoder (static_cast<GMPVideoHost*> (aHostAPI));
       return GMPNoErr;
     } else if (!strcmp (aApiName, "encode-video")) {
       *aPluginApi = new FakeVideoEncoder (static_cast<GMPVideoHost*> (aHostAPI));
       return GMPNoErr;
     } else if (!strcmp (aApiName, "eme-decrypt")) {
-      *aPluginApi = new FakeDecryptor(static_cast<GMPDecryptorHost*> (aHostAPI));
+      *aPluginApi = new FakeDecryptor();
+      return GMPNoErr;
+    } else if (!strcmp (aApiName, "async-shutdown")) {
+      *aPluginApi = new TestAsyncShutdown(static_cast<GMPAsyncShutdownHost*> (aHostAPI));
       return GMPNoErr;
     }
     return GMPGenericErr;
   }
 
   PUBLIC_FUNC void
   GMPShutdown (void) {
     g_platform_api = NULL;
--- a/dom/media/gmp-plugin/gmp-test-decryptor.cpp
+++ b/dom/media/gmp-plugin/gmp-test-decryptor.cpp
@@ -28,19 +28,18 @@ static bool sMultiClientTest = false;
 void
 MaybeFinish()
 {
   if (sFinishedTruncateTest && sFinishedReplaceTest && sMultiClientTest) {
     FakeDecryptor::Message("test-storage complete");
   }
 }
 
-FakeDecryptor::FakeDecryptor(GMPDecryptorHost* aHost)
-  : mHost(aHost)
-  , mCallback(nullptr)
+FakeDecryptor::FakeDecryptor()
+  : mCallback(nullptr)
 {
   assert(!sInstance);
   sInstance = this;
 }
 
 void FakeDecryptor::DecryptingComplete()
 {
   sInstance = nullptr;
@@ -75,18 +74,18 @@ public:
     , mThen(aThen)
   {}
   void Run() MOZ_OVERRIDE {
     ReadRecord(mId, mThen);
   }
   void Destroy() MOZ_OVERRIDE {
     delete this;
   }
+  string mId;
   ReadContinuation* mThen;
-  string mId;
 };
 
 class TestEmptyContinuation : public ReadContinuation {
 public:
   void ReadComplete(GMPErr aErr, const std::string& aData) MOZ_OVERRIDE {
     if (aData != "") {
       FakeDecryptor::Message("FAIL TestEmptyContinuation record was not truncated");
     }
@@ -173,17 +172,17 @@ public:
   virtual void OpenComplete(GMPErr aStatus, GMPRecord* aRecord) MOZ_OVERRIDE {
     if (GMP_FAILED(aStatus)) {
       FakeDecryptor::Message("FAIL OpenAgainContinuation to open record initially.");
       sMultiClientTest = true;
       MaybeFinish();
       return;
     }
 
-    auto err = GMPOpenRecord(OpenAgainRecordId, new OpenedSecondTimeContinuation(aRecord));
+    GMPOpenRecord(OpenAgainRecordId, new OpenedSecondTimeContinuation(aRecord));
 
     delete this;
   }
 };
 
 void
 FakeDecryptor::TestStorage()
 {
@@ -263,16 +262,41 @@ public:
       FakeDecryptor::Message("retrieve " + mRecordId + " succeeded (length " +
                              len + " bytes)");
     }
     delete this;
   }
   string mRecordId;
 };
 
+class ReportReadRecordContinuation : public ReadContinuation {
+public:
+  ReportReadRecordContinuation(const string& aRecordId)
+    : mRecordId(aRecordId)
+  {}
+  void ReadComplete(GMPErr aErr, const std::string& aData) MOZ_OVERRIDE {
+    if (GMP_FAILED(aErr)) {
+      FakeDecryptor::Message("retrieved " + mRecordId + " failed");
+    } else {
+      FakeDecryptor::Message("retrieved " + mRecordId + " " + aData);
+    }
+    delete this;
+  }
+  string mRecordId;
+};
+
+enum ShutdownMode {
+  ShutdownNormal,
+  ShutdownTimeout,
+  ShutdownStoreToken
+};
+
+static ShutdownMode sShutdownMode = ShutdownNormal;
+static string sShutdownToken = "";
+
 void
 FakeDecryptor::UpdateSession(uint32_t aPromiseId,
                              const char* aSessionId,
                              uint32_t aSessionIdLength,
                              const uint8_t* aResponse,
                              uint32_t aResponseSize)
 {
   std::string response((const char*)aResponse, (const char*)(aResponse)+aResponseSize);
@@ -285,10 +309,53 @@ FakeDecryptor::UpdateSession(uint32_t aP
     const string& id = tokens[1];
     const string& value = tokens[2];
     WriteRecord(id,
                 value,
                 new ReportWritten(id, value));
   } else if (task == "retrieve") {
     const string& id = tokens[1];
     ReadRecord(id, new ReportReadStatusContinuation(id));
+  } else if (task == "shutdown-mode") {
+    const string& mode = tokens[1];
+    if (mode == "timeout") {
+      sShutdownMode = ShutdownTimeout;
+    } else if (mode == "token") {
+      sShutdownMode = ShutdownStoreToken;
+      sShutdownToken = tokens[2];
+      Message("shutdown-token received " + sShutdownToken);
+    }
+  } else if (task == "retrieve-shutdown-token") {
+    ReadRecord("shutdown-token", new ReportReadRecordContinuation("shutdown-token"));
   }
 }
+
+class CompleteShutdownTask : public GMPTask {
+public:
+  CompleteShutdownTask(GMPAsyncShutdownHost* aHost)
+    : mHost(aHost)
+  {
+  }
+  virtual void Run() {
+    mHost->ShutdownComplete();
+  }
+  virtual void Destroy() { delete this; }
+  GMPAsyncShutdownHost* mHost;
+};
+
+void
+TestAsyncShutdown::BeginShutdown() {
+  switch (sShutdownMode) {
+    case ShutdownNormal:
+      mHost->ShutdownComplete();
+      break;
+    case ShutdownTimeout:
+      // Don't do anything; wait for timeout, Gecko should kill
+      // the plugin and recover.
+      break;
+    case ShutdownStoreToken:
+      // Store message, then shutdown.
+      WriteRecord("shutdown-token",
+                  sShutdownToken,
+                  new CompleteShutdownTask(mHost));
+      break;
+  }
+}
--- a/dom/media/gmp-plugin/gmp-test-decryptor.h
+++ b/dom/media/gmp-plugin/gmp-test-decryptor.h
@@ -9,17 +9,17 @@
 #include "gmp-decryption.h"
 #include "gmp-async-shutdown.h"
 #include <string>
 #include "mozilla/Attributes.h"
 
 class FakeDecryptor : public GMPDecryptor {
 public:
 
-  FakeDecryptor(GMPDecryptorHost* aHost);
+  FakeDecryptor();
 
   virtual void Init(GMPDecryptorCallback* aCallback) MOZ_OVERRIDE {
     mCallback = aCallback;
   }
 
   virtual void CreateSession(uint32_t aPromiseId,
                              const char* aInitDataType,
                              uint32_t aInitDataTypeSize,
@@ -65,17 +65,28 @@ public:
   }
 
   virtual void DecryptingComplete() MOZ_OVERRIDE;
 
   static void Message(const std::string& aMessage);
 
 private:
 
+  virtual ~FakeDecryptor() {}
   static FakeDecryptor* sInstance;
 
   void TestStorage();
 
   GMPDecryptorCallback* mCallback;
-  GMPDecryptorHost* mHost;
+};
+
+class TestAsyncShutdown : public GMPAsyncShutdown {
+public:
+  TestAsyncShutdown(GMPAsyncShutdownHost* aHost)
+    : mHost(aHost)
+  {
+  }
+  virtual void BeginShutdown() MOZ_OVERRIDE;
+private:
+  GMPAsyncShutdownHost* mHost;
 };
 
 #endif
--- a/dom/media/gmp-plugin/gmp-test-storage.h
+++ b/dom/media/gmp-plugin/gmp-test-storage.h
@@ -7,16 +7,17 @@
 #define TEST_GMP_STORAGE_H__
 
 #include "gmp-errors.h"
 #include "gmp-platform.h"
 #include <string>
 
 class ReadContinuation {
 public:
+  virtual ~ReadContinuation() {}
   virtual void ReadComplete(GMPErr aErr, const std::string& aData) = 0;
 };
 
 // Reads a record to storage using GMPRecord.
 // Calls ReadContinuation with read data.
 GMPErr
 ReadRecord(const std::string& aRecordName,
            ReadContinuation* aContinuation);
@@ -40,16 +41,17 @@ GMPOpenRecord(const char* aName,
               GMPRecord** aOutRecord,
               GMPRecordClient* aClient);
 
 GMPErr
 GMPRunOnMainThread(GMPTask* aTask);
 
 class OpenContinuation {
 public:
+  virtual ~OpenContinuation() {}
   virtual void OpenComplete(GMPErr aStatus, GMPRecord* aRecord) = 0;
 };
 
 GMPErr
 GMPOpenRecord(const std::string& aRecordName,
            OpenContinuation* aContinuation);
 
 #endif // TEST_GMP_STORAGE_H__
--- a/dom/nfc/nsNfc.js
+++ b/dom/nfc/nsNfc.js
@@ -21,31 +21,27 @@ Cu.import("resource://gre/modules/Servic
 XPCOMUtils.defineLazyServiceGetter(this,
                                    "appsService",
                                    "@mozilla.org/AppsService;1",
                                    "nsIAppsService");
 
 /**
  * NFCTag
  */
-function MozNFCTag() {
+function MozNFCTag(aWindow, aSessionToken) {
   debug("In MozNFCTag Constructor");
   this._nfcContentHelper = Cc["@mozilla.org/nfc/content-helper;1"]
                              .getService(Ci.nsINfcContentHelper);
-  this.session = null;
+  this._window = aWindow;
+  this.session = aSessionToken;
 }
 MozNFCTag.prototype = {
   _nfcContentHelper: null,
   _window: null,
 
-  initialize: function(aWindow, aSessionToken) {
-    this._window = aWindow;
-    this.session = aSessionToken;
-  },
-
   // NFCTag interface:
   readNDEF: function readNDEF() {
     return this._nfcContentHelper.readNDEF(this._window, this.session);
   },
   writeNDEF: function writeNDEF(records) {
     return this._nfcContentHelper.writeNDEF(this._window, records, this.session);
   },
   makeReadOnlyNDEF: function makeReadOnlyNDEF() {
@@ -56,32 +52,29 @@ MozNFCTag.prototype = {
   contractID: "@mozilla.org/nfc/NFCTag;1",
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
                                          Ci.nsIDOMGlobalPropertyInitializer]),
 };
 
 /**
  * NFCPeer
  */
-function MozNFCPeer() {
+function MozNFCPeer(aWindow, aSessionToken) {
   debug("In MozNFCPeer Constructor");
   this._nfcContentHelper = Cc["@mozilla.org/nfc/content-helper;1"]
                              .getService(Ci.nsINfcContentHelper);
-  this.session = null;
+
+  this._window = aWindow;
+  this.session = aSessionToken;
 }
 MozNFCPeer.prototype = {
   _nfcContentHelper: null,
   _window: null,
   _isLost: false,
 
-  initialize: function(aWindow, aSessionToken) {
-    this._window = aWindow;
-    this.session = aSessionToken;
-  },
-
   // NFCPeer interface:
   sendNDEF: function sendNDEF(records) {
     if (this._isLost) {
       throw new this._window.DOMError("InvalidStateError", "NFCPeer object is invalid");
     }
 
     // Just forward sendNDEF to writeNDEF
     return this._nfcContentHelper.writeNDEF(this._window, records, this.session);
@@ -165,32 +158,30 @@ mozNfc.prototype = {
     return this._nfcContentHelper.stopPoll(this._window);
   },
 
   powerOff: function powerOff() {
     return this._nfcContentHelper.powerOff(this._window);
   },
 
   getNFCTag: function getNFCTag(sessionToken) {
-    let obj = new MozNFCTag();
-    obj.initialize(this._window, sessionToken);
+    let obj = new MozNFCTag(this._window, sessionToken);
     if (this._nfcContentHelper.checkSessionToken(sessionToken)) {
       return this._window.MozNFCTag._create(this._window, obj);
     }
     return null;
   },
 
   getNFCPeer: function getNFCPeer(sessionToken) {
     if (!sessionToken || !this._nfcContentHelper.checkSessionToken(sessionToken)) {
       return null;
     }
 
     if (!this.nfcObject || this.nfcObject.session != sessionToken) {
-      let obj = new MozNFCPeer();
-      obj.initialize(this._window, sessionToken);
+      let obj = new MozNFCPeer(this._window, sessionToken);
       this.nfcObject = obj;
       this.nfcObject.contentObject = this._window.MozNFCPeer._create(this._window, obj);
     }
 
     return this.nfcObject.contentObject;
   },
 
   // get/set onpeerready
--- a/dom/system/gonk/RILContentHelper.js
+++ b/dom/system/gonk/RILContentHelper.js
@@ -110,26 +110,28 @@ IccInfo.prototype = {
   mnc: null,
   spn: null,
   isDisplayNetworkNameRequired: null,
   isDisplaySpnRequired: null
 };
 
 function GsmIccInfo() {}
 GsmIccInfo.prototype = {
+  __proto__: IccInfo.prototype,
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIGsmIccInfo,
                                          Ci.nsIIccInfo]),
 
   // nsIGsmIccInfo
 
   msisdn: null
 };
 
 function CdmaIccInfo() {}
 CdmaIccInfo.prototype = {
+  __proto__: IccInfo.prototype,
   QueryInterface: XPCOMUtils.generateQI([Ci.nsICdmaIccInfo,
                                          Ci.nsIIccInfo]),
 
   // nsICdmaIccInfo
 
   mdn: null,
   prlVersion: 0
 };
@@ -203,18 +205,16 @@ RILContentHelper.prototype = {
       if (newInfo.iccType === "ruim" || newInfo.iccType === "csim") {
         rilContext.iccInfo = new CdmaIccInfo();
       } else if (newInfo.iccType === "sim" || newInfo.iccType === "usim") {
         rilContext.iccInfo = new GsmIccInfo();
       } else {
         rilContext.iccInfo = new IccInfo();
       }
     }
-    let changed = (rilContext.iccInfo.iccid != newInfo.iccid) ?
-      true : false;
 
     this.updateInfo(newInfo, rilContext.iccInfo);
   },
 
   _windowsMap: null,
 
   rilContexts: null,
 
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -1982,17 +1982,17 @@ RilObject.prototype = {
         options.success = false;
         options.errorMsg = GECKO_ERROR_GENERIC_FAILURE;
         this.sendChromeMessage(options);
         return;
       }
       call.hangUpLocal = true;
       this.sendHangUpRequest(1);
     } else {
-      if (this.currentConference.state === CALL_STATE_ACTIVE) {
+      if (this.currentConferenceState === CALL_STATE_ACTIVE) {
         this.sendHangUpForegroundRequest(options);
       } else {
         this.sendHangUpBackgroundRequest(options);
       }
     }
   },
 
   holdConference: function() {
--- a/dom/telephony/Telephony.cpp
+++ b/dom/telephony/Telephony.cpp
@@ -60,28 +60,27 @@ public:
     MOZ_ASSERT(mTelephony);
     mTelephony = nullptr;
   }
 };
 
 class Telephony::EnumerationAck : public nsRunnable
 {
   nsRefPtr<Telephony> mTelephony;
-  nsString mType;
 
 public:
-  EnumerationAck(Telephony* aTelephony, const nsAString& aType)
-  : mTelephony(aTelephony), mType(aType)
+  EnumerationAck(Telephony* aTelephony)
+  : mTelephony(aTelephony)
   {
     MOZ_ASSERT(mTelephony);
   }
 
   NS_IMETHOD Run()
   {
-    mTelephony->NotifyEvent(mType);
+    mTelephony->NotifyEvent(NS_LITERAL_STRING("ready"));
     return NS_OK;
   }
 };
 
 Telephony::Telephony(nsPIDOMWindow* aOwner)
   : DOMEventTargetHelper(aOwner), mEnumerated(false)
 {
 }
@@ -463,21 +462,18 @@ Telephony::ConferenceGroup() const
   return group.forget();
 }
 
 // EventTarget
 
 void
 Telephony::EventListenerAdded(nsIAtom* aType)
 {
-  if (aType == nsGkAtoms::oncallschanged) {
-    // Fire oncallschanged on the next tick if the calls array is ready.
-    EnqueueEnumerationAck(NS_LITERAL_STRING("callschanged"));
-  } else if (aType == nsGkAtoms::onready) {
-    EnqueueEnumerationAck(NS_LITERAL_STRING("ready"));
+  if (aType == nsGkAtoms::onready) {
+    EnqueueEnumerationAck();
   }
 }
 
 // nsITelephonyListener
 
 NS_IMETHODIMP
 Telephony::CallStateChanged(uint32_t aServiceId, uint32_t aCallIndex,
                             uint16_t aCallState, const nsAString& aNumber,
@@ -568,20 +564,16 @@ Telephony::EnumerateCallStateComplete()
   }
 
   mEnumerated = true;
 
   if (NS_FAILED(NotifyEvent(NS_LITERAL_STRING("ready")))) {
     NS_WARNING("Failed to notify ready!");
   }
 
-  if (NS_FAILED(NotifyCallsChanged(nullptr))) {
-    NS_WARNING("Failed to notify calls changed!");
-  }
-
   if (NS_FAILED(mService->RegisterListener(mListener))) {
     NS_WARNING("Failed to register listener!");
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 Telephony::EnumerateCallState(uint32_t aServiceId, uint32_t aCallIndex,
@@ -699,23 +691,23 @@ Telephony::DispatchCallEvent(const nsASt
   init.mCall = aCall;
 
   nsRefPtr<CallEvent> event = CallEvent::Constructor(this, aType, init);
 
   return DispatchTrustedEvent(event);
 }
 
 void
-Telephony::EnqueueEnumerationAck(const nsAString& aType)
+Telephony::EnqueueEnumerationAck()
 {
   if (!mEnumerated) {
     return;
   }
 
-  nsCOMPtr<nsIRunnable> task = new EnumerationAck(this, aType);
+  nsCOMPtr<nsIRunnable> task = new EnumerationAck(this);
   if (NS_FAILED(NS_DispatchToCurrentThread(task))) {
     NS_WARNING("Failed to dispatch to current thread!");
   }
 }
 
 already_AddRefed<nsITelephonyService>
 NS_CreateTelephonyService()
 {
--- a/dom/telephony/Telephony.h
+++ b/dom/telephony/Telephony.h
@@ -194,17 +194,17 @@ private:
 
   nsresult
   NotifyCallsChanged(TelephonyCall* aCall);
 
   nsresult
   DispatchCallEvent(const nsAString& aType, TelephonyCall* aCall);
 
   void
-  EnqueueEnumerationAck(const nsAString& aType);
+  EnqueueEnumerationAck();
 
   already_AddRefed<TelephonyCall>
   GetCall(uint32_t aServiceId, uint32_t aCallIndex);
 
   already_AddRefed<TelephonyCall>
   GetCallFromEverywhere(uint32_t aServiceId, uint32_t aCallIndex);
 };
 
--- a/dom/telephony/test/marionette/head.js
+++ b/dom/telephony/test/marionette/head.js
@@ -271,25 +271,26 @@ let emulator = (function() {
    *        An array of calls.
    * @param callback
    *        A callback function.
    */
   function check_oncallschanged(container, containerName, expectedCalls,
                                 callback) {
     container.oncallschanged = function(event) {
       log("Received 'callschanged' event for the " + containerName);
-      if (event.call) {
-        let index = expectedCalls.indexOf(event.call);
-        ok(index != -1);
-        expectedCalls.splice(index, 1);
+
+      ok(event.call);
 
-        if (expectedCalls.length === 0) {
-          container.oncallschanged = null;
-          callback();
-        }
+      let index = expectedCalls.indexOf(event.call);
+      ok(index != -1);
+      expectedCalls.splice(index, 1);
+
+      if (expectedCalls.length === 0) {
+        container.oncallschanged = null;
+        callback();
       }
     };
   }
 
   /**
    * Convenient helper to handle *.ongroupchange event.
    *
    * @param call
--- a/dom/telephony/test/marionette/test_incoming_answer_hangup_oncallschanged.js
+++ b/dom/telephony/test/marionette/test_incoming_answer_hangup_oncallschanged.js
@@ -8,20 +8,18 @@ let number = "5555552368";
 let incoming;
 
 function simulateIncoming() {
   log("Simulating an incoming call.");
 
   telephony.oncallschanged = function oncallschanged(event) {
     log("Received 'callschanged' event.");
 
-    if (!event.call) {
-      log("Notifying calls array is loaded. No call information accompanies.");
-      return;
-    }
+    // Check whether the 'calls' array has changed
+    ok(event.call, "undesired callschanged event");
 
     telephony.oncallschanged = null;
 
     incoming = event.call;
     ok(incoming);
     is(incoming.id.number, number);
     is(incoming.state, "incoming");
 
@@ -82,20 +80,18 @@ function hangUp() {
     is(incoming, event.call);
     is(incoming.state, "disconnecting");
     gotDisconnecting = true;
   };
 
   telephony.oncallschanged = function oncallschanged(event) {
     log("Received 'callschanged' event.");
 
-    if (!event.call) {
-      log("Notifying calls array is loaded. No call information accompanies.");
-      return;
-    }
+    // Check whether the 'calls' array has changed
+    ok(event.call, "undesired callschanged event");
 
     is(incoming, event.call);
     is(incoming.state, "disconnected");
     is(telephony.active, null);
     is(telephony.calls.length, 0);
     gotCallschanged = true;
   };
 
--- a/dom/telephony/test/marionette/test_outgoing_answer_hangup_oncallschanged.js
+++ b/dom/telephony/test/marionette/test_outgoing_answer_hangup_oncallschanged.js
@@ -8,20 +8,18 @@ let number = "5555552368";
 let outgoing;
 
 function dial() {
   log("Make an outgoing call.");
 
   telephony.oncallschanged = function oncallschanged(event) {
     log("Received 'callschanged' call event.");
 
-    if (!event.call) {
-      log("Notifying calls array is loaded. No call information accompanies.");
-      return;
-    }
+    // Check whether the 'calls' array has changed
+    ok(event.call, "undesired callschanged event");
 
     let expected_states = ["dialing", "disconnected"];
     ok(expected_states.indexOf(event.call.state) != -1,
       "Unexpected call state: " + event.call.state);
 
     if (event.call.state == "dialing") {
       outgoing = event.call;
       ok(outgoing);
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -80,16 +80,17 @@ var ecmaGlobals =
     {name: "SharedUint8ClampedArray", nightly: true},
     {name: "SharedInt16Array", nightly: true},
     {name: "SharedUint16Array", nightly: true},
     {name: "SharedInt32Array", nightly: true},
     {name: "SharedUint32Array", nightly: true},
     {name: "SharedFloat32Array", nightly: true},
     {name: "SharedFloat64Array", nightly: true},
     {name: "SIMD", nightly: true},
+    {name: "Atomics", nightly: true},
     "StopIteration",
     "String",
     "SyntaxError",
     {name: "TypedObject", nightly: true},
     "TypeError",
     "Uint16Array",
     "Uint32Array",
     "Uint8Array",
--- a/dom/webidl/MutationObserver.webidl
+++ b/dom/webidl/MutationObserver.webidl
@@ -42,20 +42,20 @@ interface MutationObserver {
   [ChromeOnly]
   readonly attribute MutationCallback mutationCallback;
 };
 
 callback MutationCallback = void (sequence<MutationRecord> mutations, MutationObserver observer);
 
 dictionary MutationObserverInit {
   boolean childList = false;
-  boolean attributes = false;
-  boolean characterData = false;
+  boolean attributes;
+  boolean characterData;
   boolean subtree = false;
-  boolean attributeOldValue = false;
-  boolean characterDataOldValue = false;
+  boolean attributeOldValue;
+  boolean characterDataOldValue;
   sequence<DOMString> attributeFilter;
 };
 
 dictionary MutationObservingInfo : MutationObserverInit
 {
   Node? observedNode = null;
 };
--- a/dom/workers/test/test_worker_interfaces.js
+++ b/dom/workers/test/test_worker_interfaces.js
@@ -54,16 +54,17 @@ var ecmaGlobals =
     {name: "SharedUint8ClampedArray", nightly: true},
     {name: "SharedInt16Array", nightly: true},
     {name: "SharedUint16Array", nightly: true},
     {name: "SharedInt32Array", nightly: true},
     {name: "SharedUint32Array", nightly: true},
     {name: "SharedFloat32Array", nightly: true},
     {name: "SharedFloat64Array", nightly: true},
     {name: "SIMD", nightly: true},
+    {name: "Atomics", nightly: true},
     "StopIteration",
     "String",
     "SyntaxError",
     {name: "TypedObject", nightly: true},
     "TypeError",
     "Uint16Array",
     "Uint32Array",
     "Uint8Array",
--- a/dom/xbl/nsXBLService.cpp
+++ b/dom/xbl/nsXBLService.cpp
@@ -111,41 +111,49 @@ public:
   void DocumentLoaded(nsIDocument* aBindingDoc)
   {
     // We only need the document here to cause frame construction, so
     // we need the current doc, not the owner doc.
     nsIDocument* doc = mBoundElement->GetCurrentDoc();
     if (!doc)
       return;
 
+    // Destroy the frames for mBoundElement.
+    nsIContent* destroyedFramesFor = nullptr;
+    nsIPresShell* shell = doc->GetShell();
+    if (shell) {
+      shell->DestroyFramesFor(mBoundElement, &destroyedFramesFor);
+    }
+    MOZ_ASSERT(!mBoundElement->GetPrimaryFrame());
+
     // Get the binding.
     bool ready = false;
     nsXBLService::GetInstance()->BindingReady(mBoundElement, mBindingURI, &ready);
     if (!ready)
       return;
 
     // If |mBoundElement| is (in addition to having binding |mBinding|)
     // also a descendant of another element with binding |mBinding|,
     // then we might have just constructed it due to the
     // notification of its parent.  (We can know about both if the
     // binding loads were triggered from the DOM rather than frame
     // construction.)  So we have to check both whether the element
     // has a primary frame and whether it's in the undisplayed map
     // before sending a ContentInserted notification, or bad things
     // will happen.
-    nsIPresShell *shell = doc->GetShell();
+    MOZ_ASSERT(shell == doc->GetShell());
     if (shell) {
       nsIFrame* childFrame = mBoundElement->GetPrimaryFrame();
       if (!childFrame) {
         // Check to see if it's in the undisplayed content map.
         nsStyleContext* sc =
           shell->FrameManager()->GetUndisplayedContent(mBoundElement);
 
         if (!sc) {
-          shell->RecreateFramesFor(mBoundElement);
+          shell->CreateFramesFor(destroyedFramesFor);
         }
       }
     }
   }
 
   nsXBLBindingRequest(nsIURI* aURI, nsIContent* aBoundElement)
     : mBindingURI(aURI),
       mBoundElement(aBoundElement)
--- a/extensions/spellcheck/src/mozInlineSpellWordUtil.cpp
+++ b/extensions/spellcheck/src/mozInlineSpellWordUtil.cpp
@@ -662,17 +662,17 @@ mozInlineSpellWordUtil::MapDOMPositionTo
 namespace {
 
 template<class T>
 class FirstLargerOffset
 {
   int32_t mSoftTextOffset;
 
 public:
-  FirstLargerOffset(int32_t aSoftTextOffset) : mSoftTextOffset(aSoftTextOffset) {}
+  explicit FirstLargerOffset(int32_t aSoftTextOffset) : mSoftTextOffset(aSoftTextOffset) {}
   int operator()(const T& t) const {
   // We want the first larger offset, so never return 0 (which would
   // short-circuit evaluation before finding the last such offset).
     return mSoftTextOffset < t.mSoftTextOffset ? -1 : 1;
   }
 };
 
 template<class T>
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -351,19 +351,16 @@ public:
   ScreenToParentLayerScale mTransformScale;
 
   // The conversion factor between CSS pixels and device pixels for this frame.
   // This can vary based on a variety of things, such as reflowing-zoom. The
   // conversion factor for device pixels to layers pixels is just the
   // resolution.
   CSSToLayoutDeviceScale mDevPixelsPerCSSPixel;
 
-  // Whether or not this frame may have touch listeners.
-  bool mMayHaveTouchListeners;
-
 public:
   void SetIsRoot(bool aIsRoot)
   {
     mIsRoot = aIsRoot;
   }
 
   bool GetIsRoot() const
   {
@@ -532,20 +529,33 @@ public:
     return mMayHaveTouchCaret;
   }
 
   void SetMayHaveTouchCaret(bool aMayHaveTouchCaret)
   {
     mMayHaveTouchCaret = aMayHaveTouchCaret;
   }
 
+  bool GetMayHaveTouchListeners() const
+  {
+    return mMayHaveTouchListeners;
+  }
+
+  void SetMayHaveTouchListeners(bool aMayHaveTouchListeners)
+  {
+    mMayHaveTouchListeners = aMayHaveTouchListeners;
+  }
+
 private:
   // New fields from now on should be made private and old fields should
   // be refactored to be private.
 
+  // Whether or not this frame may have a touch listeners.
+  bool mMayHaveTouchListeners;
+
   // Whether or not this frame may have a touch caret.
   bool mMayHaveTouchCaret;
 
   // Whether or not this is the root scroll frame for the root content document.
   bool mIsRoot;
 
   // Whether or not this frame is for an element marked 'scrollgrab'.
   bool mHasScrollgrab;
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -1031,17 +1031,17 @@ nsEventStatus AsyncPanZoomController::Re
       if (block->GetOverscrollHandoffChain()->HasFastMovingApzc()) {
         // If we're already in a fast fling, then we want the touch event to stop the fling
         // and to disallow the touch event from being used as part of a fling.
         block->DisallowSingleTap();
       }
       block->GetOverscrollHandoffChain()->CancelAnimations();
     }
 
-    if (mFrameMetrics.mMayHaveTouchListeners || mFrameMetrics.GetMayHaveTouchCaret()) {
+    if (mFrameMetrics.GetMayHaveTouchListeners() || mFrameMetrics.GetMayHaveTouchCaret()) {
       // Content may intercept the touch events and prevent-default them. So we schedule
       // a timeout to give content time to do that.
       ScheduleContentResponseTimeout();
     } else {
       // Content won't prevent-default this, so we can just pretend like we scheduled
       // a timeout and it expired. Note that we will still receive a ContentReceivedTouch
       // callback for this block, and so we need to make sure we adjust the touch balance.
       APZC_LOG("%p not waiting for content response on block %p\n", this, block);
@@ -2671,17 +2671,17 @@ void AsyncPanZoomController::NotifyLayer
   AssertOnCompositorThread();
 
   ReentrantMonitorAutoEnter lock(mMonitor);
   bool isDefault = mFrameMetrics.IsDefault();
 
   mLastContentPaintMetrics = aLayerMetrics;
   UpdateTransformScale();
 
-  mFrameMetrics.mMayHaveTouchListeners = aLayerMetrics.mMayHaveTouchListeners;
+  mFrameMetrics.SetMayHaveTouchListeners(aLayerMetrics.GetMayHaveTouchListeners());
   mFrameMetrics.SetMayHaveTouchCaret(aLayerMetrics.GetMayHaveTouchCaret());
   mFrameMetrics.SetScrollParentId(aLayerMetrics.GetScrollParentId());
   APZC_LOG_FM(aLayerMetrics, "%p got a NotifyLayersUpdated with aIsFirstPaint=%d", this, aIsFirstPaint);
 
   LogRendertraceRect(GetGuid(), "page", "brown", aLayerMetrics.mScrollableRect);
   LogRendertraceRect(GetGuid(), "painted displayport", "lightgreen",
     aLayerMetrics.mDisplayPort + aLayerMetrics.GetScrollOffset());
   if (!aLayerMetrics.mCriticalDisplayPort.IsEmpty()) {
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -61,60 +61,16 @@ LayerHasCheckerboardingAPZC(Layer* aLaye
       }
       return true;
     }
     break;
   }
   return false;
 }
 
-/**
- * Returns a rectangle of content painted opaquely by aLayer. Very consertative;
- * bails by returning an empty rect in any tricky situations.
- */
-static nsIntRect
-GetOpaqueRect(Layer* aLayer)
-{
-  nsIntRect result;
-  gfx::Matrix matrix;
-  bool is2D = aLayer->AsLayerComposite()->GetShadowTransform().Is2D(&matrix);
-
-  // Just bail if there's anything difficult to handle.
-  if (!is2D || aLayer->GetMaskLayer() ||
-    aLayer->GetIsFixedPosition() ||
-    aLayer->GetIsStickyPosition() ||
-    aLayer->GetEffectiveOpacity() != 1.0f ||
-    matrix.HasNonIntegerTranslation()) {
-    return result;
-  }
-
-  if (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) {
-    result = aLayer->GetEffectiveVisibleRegion().GetLargestRectangle();
-  } else {
-    // Drill down into RefLayers because that's what we particularly care about;
-    // layer construction for aLayer will not have known about the opaqueness
-    // of any RefLayer subtrees.
-    RefLayer* refLayer = aLayer->AsRefLayer();
-    if (refLayer && refLayer->GetFirstChild()) {
-      result = GetOpaqueRect(refLayer->GetFirstChild());
-    }
-  }
-
-  // Translate our opaque region to cover the child
-  gfx::Point point = matrix.GetTranslation();
-  result.MoveBy(static_cast<int>(point.x), static_cast<int>(point.y));
-
-  const nsIntRect* clipRect = aLayer->GetEffectiveClipRect();
-  if (clipRect) {
-    result.IntersectRect(result, *clipRect);
-  }
-
-  return result;
-}
-
 static void DrawLayerInfo(const RenderTargetIntRect& aClipRect,
                           LayerManagerComposite* aManager,
                           Layer* aLayer)
 {
 
   if (aLayer->GetType() == Layer::LayerType::TYPE_CONTAINER) {
     // XXX - should figure out a way to render this, but for now this
     // is hard to do, since it will often get superimposed over the first
@@ -157,22 +113,20 @@ static void PrintUniformityInfo(Layer* a
   LayerTranslationPayload* payload = new LayerTranslationPayload(aLayer, translation);
   PROFILER_MARKER_PAYLOAD("LayerTranslation", payload);
 #endif
 }
 
 /* all of the per-layer prepared data we need to maintain */
 struct PreparedLayer
 {
-  PreparedLayer(LayerComposite *aLayer, RenderTargetIntRect aClipRect, bool aRestoreVisibleRegion, nsIntRegion &aVisibleRegion) :
-    mLayer(aLayer), mClipRect(aClipRect), mRestoreVisibleRegion(aRestoreVisibleRegion), mSavedVisibleRegion(aVisibleRegion) {}
+  PreparedLayer(LayerComposite *aLayer, RenderTargetIntRect aClipRect) :
+    mLayer(aLayer), mClipRect(aClipRect) {}
   LayerComposite* mLayer;
   RenderTargetIntRect mClipRect;
-  bool mRestoreVisibleRegion;
-  nsIntRegion mSavedVisibleRegion;
 };
 
 /* all of the prepared data that we need in RenderLayer() */
 struct PreparedData
 {
   RefPtr<CompositingRenderTarget> mTmpTarget;
   nsAutoTArray<PreparedLayer, 12> mLayers;
   bool mNeedsSurfaceCopy;
@@ -218,48 +172,18 @@ ContainerPrepare(ContainerT* aContainer,
         !quad.Intersects(compositor->ClipRectInLayersCoordinates(layerToRender->GetLayer(), clipRect)) &&
         !LayerHasCheckerboardingAPZC(layerToRender->GetLayer(), nullptr)) {
       CULLING_LOG("Sublayer %p is clipped entirely\n", layerToRender->GetLayer());
       continue;
     }
 
     CULLING_LOG("Preparing sublayer %p\n", layerToRender->GetLayer());
 
-    nsIntRegion savedVisibleRegion;
-    bool restoreVisibleRegion = false;
-    gfx::Matrix matrix;
-    bool is2D = layerToRender->GetLayer()->GetBaseTransform().Is2D(&matrix);
-    if (i + 1 < children.Length() &&
-        is2D && !matrix.HasNonIntegerTranslation()) {
-      LayerComposite* nextLayer = static_cast<LayerComposite*>(children.ElementAt(i + 1)->ImplData());
-      CULLING_LOG("Culling against %p\n", nextLayer->GetLayer());
-      nsIntRect nextLayerOpaqueRect;
-      if (nextLayer && nextLayer->GetLayer()) {
-        nextLayerOpaqueRect = GetOpaqueRect(nextLayer->GetLayer());
-        gfx::Point point = matrix.GetTranslation();
-        nextLayerOpaqueRect.MoveBy(static_cast<int>(-point.x), static_cast<int>(-point.y));
-        CULLING_LOG("  point %i, %i\n", static_cast<int>(-point.x), static_cast<int>(-point.y));
-        CULLING_LOG("  opaque rect %i, %i, %i, %i\n", nextLayerOpaqueRect.x, nextLayerOpaqueRect.y, nextLayerOpaqueRect.width, nextLayerOpaqueRect.height);
-      }
-      if (!nextLayerOpaqueRect.IsEmpty()) {
-        CULLING_LOG("  draw\n");
-        savedVisibleRegion = layerToRender->GetShadowVisibleRegion();
-        nsIntRegion visibleRegion;
-        visibleRegion.Sub(savedVisibleRegion, nextLayerOpaqueRect);
-        if (visibleRegion.IsEmpty()) {
-          continue;
-        }
-        layerToRender->SetShadowVisibleRegion(visibleRegion);
-        restoreVisibleRegion = true;
-      } else {
-        CULLING_LOG("  skip\n");
-      }
-    }
     layerToRender->Prepare(clipRect);
-    aContainer->mPrepared->mLayers.AppendElement(PreparedLayer(layerToRender, clipRect, restoreVisibleRegion, savedVisibleRegion));
+    aContainer->mPrepared->mLayers.AppendElement(PreparedLayer(layerToRender, clipRect));
   }
 
   CULLING_LOG("Preparing container layer %p\n", aContainer->GetLayer());
 
   /**
    * Setup our temporary surface for rendering the contents of this container.
    */
 
@@ -320,21 +244,16 @@ RenderLayers(ContainerT* aContainer,
         gfx::Rect fbRect(clearRect.x, clearRect.y, clearRect.width, clearRect.height);
         compositor->ClearRect(fbRect);
         layerToRender->SetClearRect(nsIntRect(0, 0, 0, 0));
       }
     } else {
       layerToRender->RenderLayer(RenderTargetPixel::ToUntyped(clipRect));
     }
 
-    if (preparedData.mRestoreVisibleRegion) {
-      // Restore the region in case it's not covered by opaque content next time
-      layerToRender->SetShadowVisibleRegion(preparedData.mSavedVisibleRegion);
-    }
-
     if (gfxPrefs::UniformityInfo()) {
       PrintUniformityInfo(layer);
     }
 
     if (gfxPrefs::DrawLayerInfo()) {
       DrawLayerInfo(clipRect, aManager, layer);
     }
 
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -192,16 +192,65 @@ LayerManagerComposite::BeginTransactionW
   }
 
   mIsCompositorReady = true;
   mCompositor->SetTargetContext(aTarget, aRect);
   mTarget = aTarget;
   mTargetBounds = aRect;
 }
 
+void
+LayerManagerComposite::ApplyOcclusionCulling(Layer* aLayer, nsIntRegion& aOpaqueRegion)
+{
+  nsIntRegion localOpaque;
+  Matrix transform2d;
+  bool isTranslation = false;
+  // If aLayer has a simple transform (only an integer translation) then we
+  // can easily convert aOpaqueRegion into pre-transform coordinates and include
+  // that region.
+  if (aLayer->GetLocalTransform().Is2D(&transform2d)) {
+    if (transform2d.IsIntegerTranslation()) {
+      isTranslation = true;
+      localOpaque = aOpaqueRegion;
+      localOpaque.MoveBy(-transform2d._31, -transform2d._32);
+    }
+  }
+
+  // Subtract any areas that we know to be opaque from our
+  // visible region.
+  LayerComposite *composite = aLayer->AsLayerComposite();
+  if (!localOpaque.IsEmpty()) {
+    nsIntRegion visible = composite->GetShadowVisibleRegion();
+    visible.Sub(visible, localOpaque);
+    composite->SetShadowVisibleRegion(visible);
+  }
+
+  // Compute occlusions for our descendants (in front-to-back order) and allow them to
+  // contribute to localOpaque.
+  for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) {
+    ApplyOcclusionCulling(child, localOpaque);
+  }
+
+  // If we have a simple transform, then we can add our opaque area into
+  // aOpaqueRegion.
+  if (isTranslation &&
+      !aLayer->GetMaskLayer() &&
+      aLayer->GetLocalOpacity() == 1.0f) {
+    if (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) {
+      localOpaque.Or(localOpaque, composite->GetShadowVisibleRegion());
+    }
+    localOpaque.MoveBy(transform2d._31, transform2d._32);
+    const nsIntRect* clip = aLayer->GetEffectiveClipRect();
+    if (clip) {
+      localOpaque.And(localOpaque, *clip);
+    }
+    aOpaqueRegion.Or(aOpaqueRegion, localOpaque);
+  }
+}
+
 bool
 LayerManagerComposite::EndEmptyTransaction(EndTransactionFlags aFlags)
 {
   NS_ASSERTION(mInTransaction, "Didn't call BeginTransaction?");
   if (!mRoot) {
     mInTransaction = false;
     mIsCompositorReady = false;
     return false;
@@ -252,16 +301,19 @@ LayerManagerComposite::EndTransaction(Dr
       // properties.
       mRoot->ApplyPendingUpdatesToSubtree();
     }
 
     // The results of our drawing always go directly into a pixel buffer,
     // so we don't need to pass any global transform here.
     mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4());
 
+    nsIntRegion opaque;
+    ApplyOcclusionCulling(mRoot, opaque);
+
     Render();
     mGeometryChanged = false;
   } else {
     // Modified layer tree
     mGeometryChanged = true;
   }
 
   mCompositor->ClearTargetContext();
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -161,16 +161,23 @@ public:
   virtual bool AreComponentAlphaLayersEnabled() MOZ_OVERRIDE;
 
   virtual TemporaryRef<DrawTarget>
     CreateOptimalMaskDrawTarget(const IntSize &aSize) MOZ_OVERRIDE;
 
   virtual const char* Name() const MOZ_OVERRIDE { return ""; }
 
   /**
+   * Restricts the shadow visible region of layers that are covered with
+   * opaque content. aOpaqueRegion is the region already known to be covered
+   * with opaque content, in the post-transform coordinate space of aLayer.
+   */
+  void ApplyOcclusionCulling(Layer* aLayer, nsIntRegion& aOpaqueRegion);
+
+  /**
    * RAII helper class to add a mask effect with the compositable from aMaskLayer
    * to the EffectChain aEffect and notify the compositable when we are done.
    */
   class AutoAddMaskEffect
   {
   public:
     AutoAddMaskEffect(Layer* aMaskLayer,
                       EffectChain& aEffect,
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -409,19 +409,18 @@ CompositorD3D11::CreateRenderTarget(cons
   if (aRect.width * aRect.height == 0) {
     return nullptr;
   }
 
   CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, aRect.width, aRect.height, 1, 1,
                              D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET);
 
   RefPtr<ID3D11Texture2D> texture;
-  mDevice->CreateTexture2D(&desc, nullptr, byRef(texture));
-  NS_ASSERTION(texture, "Could not create texture");
-  if (!texture) {
+  HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, byRef(texture));
+  if (Failed(hr) || !texture) {
     return nullptr;
   }
 
   RefPtr<CompositingRenderTargetD3D11> rt = new CompositingRenderTargetD3D11(texture, aRect.TopLeft());
   rt->SetSize(IntSize(aRect.width, aRect.height));
 
   if (aInit == INIT_MODE_CLEAR) {
     FLOAT clear[] = { 0, 0, 0, 0 };
@@ -442,19 +441,19 @@ CompositorD3D11::CreateRenderTargetFromS
     return nullptr;
   }
 
   CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM,
                              aRect.width, aRect.height, 1, 1,
                              D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET);
 
   RefPtr<ID3D11Texture2D> texture;
-  mDevice->CreateTexture2D(&desc, nullptr, byRef(texture));
+  HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, byRef(texture));
   NS_ASSERTION(texture, "Could not create texture");
-  if (!texture) {
+  if (Failed(hr) || !texture) {
     return nullptr;
   }
 
   if (aSource) {
     const CompositingRenderTargetD3D11* sourceD3D11 =
       static_cast<const CompositingRenderTargetD3D11*>(aSource);
 
     D3D11_BOX srcBox;
@@ -596,17 +595,22 @@ CompositorD3D11::DrawQuad(const gfx::Rec
     TextureSourceD3D11* source = maskEffect->mMaskTexture->AsSourceD3D11();
 
     if (!source) {
       NS_WARNING("Missing texture source!");
       return;
     }
 
     RefPtr<ID3D11ShaderResourceView> view;
-    mDevice->CreateShaderResourceView(source->GetD3D11Texture(), nullptr, byRef(view));
+    HRESULT hr = mDevice->CreateShaderResourceView(source->GetD3D11Texture(), nullptr, byRef(view));
+    if (Failed(hr)) {
+      // XXX - There's a chance we won't be able to render anything, should we
+      // just crash release builds?
+      return;
+    }
 
     ID3D11ShaderResourceView* srView = view;
     mContext->PSSetShaderResources(3, 1, &srView);
 
     const gfx::Matrix4x4& maskTransform = maskEffect->mMaskTransform;
     NS_ASSERTION(maskTransform.Is2D(), "How did we end up with a 3D transform here?!");
     Rect bounds = Rect(Point(), Size(maskEffect->mSize));
 
@@ -649,17 +653,22 @@ CompositorD3D11::DrawQuad(const gfx::Rec
       if (!source) {
         NS_WARNING("Missing texture source!");
         return;
       }
 
       SetPSForEffect(aEffectChain.mPrimaryEffect, maskType, texturedEffect->mTexture->GetFormat());
 
       RefPtr<ID3D11ShaderResourceView> view;
-      mDevice->CreateShaderResourceView(source->GetD3D11Texture(), nullptr, byRef(view));
+      HRESULT hr = mDevice->CreateShaderResourceView(source->GetD3D11Texture(), nullptr, byRef(view));
+      if (Failed(hr)) {
+        // XXX - There's a chance we won't be able to render anything, should we
+        // just crash release builds?
+        return;
+      }
 
       ID3D11ShaderResourceView* srView = view;
       mContext->PSSetShaderResources(0, 1, &srView);
 
       if (!texturedEffect->mPremultiplied) {
         mContext->OMSetBlendState(mAttachments->mNonPremulBlendState, sBlendFactor, 0xFFFFFFFF);
         restoreBlendMode = true;
       }
@@ -690,23 +699,37 @@ CompositorD3D11::DrawQuad(const gfx::Rec
         // because of unsupported dimensions (we don't tile YCbCr textures).
         return;
       }
 
       TextureSourceD3D11* sourceY  = source->GetSubSource(Y)->AsSourceD3D11();
       TextureSourceD3D11* sourceCb = source->GetSubSource(Cb)->AsSourceD3D11();
       TextureSourceD3D11* sourceCr = source->GetSubSource(Cr)->AsSourceD3D11();
 
+      HRESULT hr;
+
       RefPtr<ID3D11ShaderResourceView> views[3];
-      mDevice->CreateShaderResourceView(sourceY->GetD3D11Texture(),
-                                        nullptr, byRef(views[0]));
-      mDevice->CreateShaderResourceView(sourceCb->GetD3D11Texture(),
-                                        nullptr, byRef(views[1]));
-      mDevice->CreateShaderResourceView(sourceCr->GetD3D11Texture(),
-                                        nullptr, byRef(views[2]));
+
+      hr = mDevice->CreateShaderResourceView(sourceY->GetD3D11Texture(),
+                                             nullptr, byRef(views[0]));
+      if (Failed(hr)) {
+        return;
+      }
+
+      hr = mDevice->CreateShaderResourceView(sourceCb->GetD3D11Texture(),
+                                             nullptr, byRef(views[1]));
+      if (Failed(hr)) {
+        return;
+      }
+
+      hr = mDevice->CreateShaderResourceView(sourceCr->GetD3D11Texture(),
+                                             nullptr, byRef(views[2]));
+      if (Failed(hr)) {
+        return;
+      }
 
       ID3D11ShaderResourceView* srViews[3] = { views[0], views[1], views[2] };
       mContext->PSSetShaderResources(0, 3, srViews);
     }
     break;
   case EffectTypes::COMPONENT_ALPHA:
     {
       MOZ_ASSERT(gfxPrefs::ComponentAlphaEnabled());
@@ -723,18 +746,27 @@ CompositorD3D11::DrawQuad(const gfx::Rec
       }
 
       SetPSForEffect(aEffectChain.mPrimaryEffect, maskType, effectComponentAlpha->mOnWhite->GetFormat());
 
       SetSamplerForFilter(effectComponentAlpha->mFilter);
 
       mVSConstants.textureCoords = effectComponentAlpha->mTextureCoords;
       RefPtr<ID3D11ShaderResourceView> views[2];
-      mDevice->CreateShaderResourceView(sourceOnBlack->GetD3D11Texture(), nullptr, byRef(views[0]));
-      mDevice->CreateShaderResourceView(sourceOnWhite->GetD3D11Texture(), nullptr, byRef(views[1]));
+
+      HRESULT hr;
+
+      hr = mDevice->CreateShaderResourceView(sourceOnBlack->GetD3D11Texture(), nullptr, byRef(views[0]));
+      if (Failed(hr)) {
+        return;
+      }
+      hr = mDevice->CreateShaderResourceView(sourceOnWhite->GetD3D11Texture(), nullptr, byRef(views[1]));
+      if (Failed(hr)) {
+        return;
+      }
 
       ID3D11ShaderResourceView* srViews[2] = { views[0], views[1] };
       mContext->PSSetShaderResources(0, 2, srViews);
 
       mContext->OMSetBlendState(mAttachments->mComponentBlendState, sBlendFactor, 0xFFFFFFFF);
       restoreBlendMode = true;
     }
     break;
@@ -900,17 +932,17 @@ CompositorD3D11::UpdateRenderTarget()
     return;
   }
 
   HRESULT hr;
 
   nsRefPtr<ID3D11Texture2D> backBuf;
 
   hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)backBuf.StartAssignment());
-  if (FAILED(hr)) {
+  if (Failed(hr)) {
     return;
   }
 
   mDefaultRT = new CompositingRenderTargetD3D11(backBuf, IntPoint(0, 0));
   mDefaultRT->SetSize(mSize.ToIntSize());
 }
 
 bool
@@ -967,61 +999,32 @@ CompositorD3D11::CreateShaders()
                                   byRef(mAttachments->mRGBAShader[MaskType::Mask3d]));
   if (FAILED(hr)) {
     return false;
   }
 
   return true;
 }
 
-static
-bool ShouldRecoverFromMapFailure(HRESULT hr, ID3D11Device* device)
-{
-  // XXX - it would be nice to use gfxCriticalError, but it needs to
-  // be made to work off the main thread first.
-  if (SUCCEEDED(hr)) {
-    return true;
-  }
-  if (hr == DXGI_ERROR_DEVICE_REMOVED) {
-    switch (device->GetDeviceRemovedReason()) {
-      case DXGI_ERROR_DEVICE_HUNG:
-      case DXGI_ERROR_DEVICE_REMOVED:
-      case DXGI_ERROR_DEVICE_RESET:
-      case DXGI_ERROR_DRIVER_INTERNAL_ERROR:
-        return true;
-      case DXGI_ERROR_INVALID_CALL:
-      default:
-        return false;
-    }
-  }
-  return false;
-}
-
 bool
 CompositorD3D11::UpdateConstantBuffers()
 {
   HRESULT hr;
   D3D11_MAPPED_SUBRESOURCE resource;
 
   hr = mContext->Map(mAttachments->mVSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
-  if (FAILED(hr)) {
-    if (ShouldRecoverFromMapFailure(hr, GetDevice())) {
-      return false;
-    }
-    MOZ_CRASH();
+  if (Failed(hr)) {
+    return false;
   }
   *(VertexShaderConstants*)resource.pData = mVSConstants;
   mContext->Unmap(mAttachments->mVSConstantBuffer, 0);
 
   hr = mContext->Map(mAttachments->mPSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
-  if (FAILED(hr)) {
-    if (ShouldRecoverFromMapFailure(hr, GetDevice())) {
-      return false;
-    }
-    MOZ_CRASH();
+  if (Failed(hr)) {
+    return false;
   }
   *(PixelShaderConstants*)resource.pData = mPSConstants;
   mContext->Unmap(mAttachments->mPSConstantBuffer, 0);
 
   ID3D11Buffer *buffer = mAttachments->mVSConstantBuffer;
 
   mContext->VSSetConstantBuffers(0, 1, &buffer);
 
@@ -1077,10 +1080,51 @@ CompositorD3D11::PaintToTarget()
                                              SurfaceFormat::B8G8R8A8);
   mTarget->CopySurface(sourceSurface,
                        IntRect(0, 0, bbDesc.Width, bbDesc.Height),
                        IntPoint(-mTargetBounds.x, -mTargetBounds.y));
   mTarget->Flush();
   mContext->Unmap(readTexture, 0);
 }
 
+void
+CompositorD3D11::HandleError(HRESULT hr, Severity aSeverity)
+{
+  // XXX - It would be nice to use gfxCriticalError, but it needs to
+  // be made to work off the main thread first.
+  MOZ_ASSERT(aSeverity != DebugAssert);
+
+  if (aSeverity == Critical) {
+    MOZ_CRASH("Unrecoverable D3D11 error");
+  }
+
+  if (mDevice && hr == DXGI_ERROR_DEVICE_REMOVED) {
+    hr = mDevice->GetDeviceRemovedReason();
+  }
+
+  // Always crash if we are making invalid calls
+  if (hr == DXGI_ERROR_INVALID_CALL) {
+    MOZ_CRASH("Invalid D3D11 api call");
+  }
+
+  if (aSeverity == Recoverable) {
+    NS_WARNING("Encountered a recoverable D3D11 error");
+  }
+}
+
+bool
+CompositorD3D11::Failed(HRESULT hr, Severity aSeverity)
+{
+  if (FAILED(hr)) {
+    HandleError(hr, aSeverity);
+    return true;
+  }
+  return false;
+}
+
+bool
+CompositorD3D11::Succeeded(HRESULT hr, Severity aSeverity)
+{
+  return !Failed(hr, aSeverity);
+}
+
 }
 }
--- a/gfx/layers/d3d11/CompositorD3D11.h
+++ b/gfx/layers/d3d11/CompositorD3D11.h
@@ -138,16 +138,26 @@ public:
 
   virtual nsIWidget* GetWidget() const MOZ_OVERRIDE { return mWidget; }
 
   ID3D11Device* GetDevice() { return mDevice; }
 
   ID3D11DeviceContext* GetDC() { return mContext; }
 
 private:
+  enum Severity {
+    Recoverable,
+    DebugAssert,
+    Critical,
+  };
+
+  void HandleError(HRESULT hr, Severity aSeverity);
+  bool Failed(HRESULT hr, Severity aSeverity = DebugAssert);
+  bool Succeeded(HRESULT hr, Severity aSeverity = DebugAssert);
+
   // ensure mSize is up to date with respect to mWidget
   void EnsureSize();
   void VerifyBufferSize();
   void UpdateRenderTarget();
   bool CreateShaders();
   bool UpdateConstantBuffers();
   void SetSamplerForFilter(gfx::Filter aFilter);
   void SetPSForEffect(Effect *aEffect, MaskType aMaskType, gfx::SurfaceFormat aFormat);
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -787,16 +787,36 @@ CompositorParent::CompositeCallback(Time
   } else {
     mLastCompose = TimeStamp::Now();
   }
 
   mCurrentCompositeTask = nullptr;
   CompositeToTarget(nullptr);
 }
 
+// Go down the composite layer tree, setting properties to match their
+// content-side counterparts.
+static void
+SetShadowProperties(Layer* aLayer)
+{
+  // FIXME: Bug 717688 -- Do these updates in LayerTransactionParent::RecvUpdate.
+  LayerComposite* layerComposite = aLayer->AsLayerComposite();
+  // Set the layerComposite's base transform to the layer's base transform.
+  layerComposite->SetShadowTransform(aLayer->GetBaseTransform());
+  layerComposite->SetShadowTransformSetByAnimation(false);
+  layerComposite->SetShadowVisibleRegion(aLayer->GetVisibleRegion());
+  layerComposite->SetShadowClipRect(aLayer->GetClipRect());
+  layerComposite->SetShadowOpacity(aLayer->GetOpacity());
+
+  for (Layer* child = aLayer->GetFirstChild();
+      child; child = child->GetNextSibling()) {
+    SetShadowProperties(child);
+  }
+}
+
 void
 CompositorParent::CompositeToTarget(DrawTarget* aTarget, const nsIntRect* aRect)
 {
   profiler_tracing("Paint", "Composite", TRACING_INTERVAL_START);
   PROFILER_LABEL("CompositorParent", "Composite",
     js::ProfileEntry::Category::GRAPHICS);
 
   MOZ_ASSERT(IsInCompositorThread(),
@@ -819,16 +839,18 @@ CompositorParent::CompositeToTarget(Draw
   AutoResolveRefLayers resolve(mCompositionManager);
 
   if (aTarget) {
     mLayerManager->BeginTransactionWithDrawTarget(aTarget, *aRect);
   } else {
     mLayerManager->BeginTransaction();
   }
 
+  SetShadowProperties(mLayerManager->GetRoot());
+
   if (mForceCompositionTask && !mOverrideComposeReadiness) {
     if (mCompositionManager->ReadyForCompose()) {
       mForceCompositionTask->Cancel();
       mForceCompositionTask = nullptr;
     } else {
       return;
     }
   }
@@ -899,36 +921,16 @@ CompositorParent::ForceComposeToTarget(D
 bool
 CompositorParent::CanComposite()
 {
   return mLayerManager &&
          mLayerManager->GetRoot() &&
          !mPaused;
 }
 
-// Go down the composite layer tree, setting properties to match their
-// content-side counterparts.
-static void
-SetShadowProperties(Layer* aLayer)
-{
-  // FIXME: Bug 717688 -- Do these updates in LayerTransactionParent::RecvUpdate.
-  LayerComposite* layerComposite = aLayer->AsLayerComposite();
-  // Set the layerComposite's base transform to the layer's base transform.
-  layerComposite->SetShadowTransform(aLayer->GetBaseTransform());
-  layerComposite->SetShadowTransformSetByAnimation(false);
-  layerComposite->SetShadowVisibleRegion(aLayer->GetVisibleRegion());
-  layerComposite->SetShadowClipRect(aLayer->GetClipRect());
-  layerComposite->SetShadowOpacity(aLayer->GetOpacity());
-
-  for (Layer* child = aLayer->GetFirstChild();
-      child; child = child->GetNextSibling()) {
-    SetShadowProperties(child);
-  }
-}
-
 void
 CompositorParent::ScheduleRotationOnCompositorThread(const TargetConfig& aTargetConfig,
                                                      bool aIsFirstPaint)
 {
   MOZ_ASSERT(IsInCompositorThread());
 
   if (!aIsFirstPaint &&
       !mCompositionManager->IsFirstPaint() &&
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -168,16 +168,19 @@ CompositorOGL::CleanupResources()
 
   ctx->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
 
   if (mQuadVBO) {
     ctx->fDeleteBuffers(1, &mQuadVBO);
     mQuadVBO = 0;
   }
 
+  mGLContext->MakeCurrent();
+  mContextStateTracker.DestroyOGL(mGLContext);
+
   // On the main thread the Widget will be destroyed soon and calling MakeCurrent
   // after that could cause a crash (at least with GLX, see bug 1059793), unless
   // context is marked as destroyed.
   // There may be some textures still alive that will try to call MakeCurrent on
   // the context so let's make sure it is marked destroyed now.
   mGLContext->MarkDestroyed();
 
   mGLContext = nullptr;
@@ -659,16 +662,18 @@ CompositorOGL::CreateRenderTargetFromSou
 void
 CompositorOGL::SetRenderTarget(CompositingRenderTarget *aSurface)
 {
   MOZ_ASSERT(aSurface);
   CompositingRenderTargetOGL* surface
     = static_cast<CompositingRenderTargetOGL*>(aSurface);
   if (mCurrentRenderTarget != surface) {
     mCurrentRenderTarget = surface;
+    mContextStateTracker.PopOGLSection(gl(), "Frame");
+    mContextStateTracker.PushOGLSection(gl(), "Frame");
     surface->BindRenderTarget();
   }
 }
 
 CompositingRenderTarget*
 CompositorOGL::GetCurrentRenderTarget() const
 {
   return mCurrentRenderTarget;
@@ -763,16 +768,18 @@ CompositorOGL::BeginFrame(const nsIntReg
 #if MOZ_WIDGET_ANDROID
   TexturePoolOGL::Fill(gl());
 #endif
 
   mCurrentRenderTarget =
     CompositingRenderTargetOGL::RenderTargetForWindow(this,
                                                       IntSize(width, height));
   mCurrentRenderTarget->BindRenderTarget();
+
+  mContextStateTracker.PushOGLSection(gl(), "Frame");
 #ifdef DEBUG
   mWindowRenderTarget = mCurrentRenderTarget;
 #endif
 
   // Default blend function implements "OVER"
   mGLContext->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
                                  LOCAL_GL_ONE, LOCAL_GL_ONE);
   mGLContext->fEnable(LOCAL_GL_BLEND);
@@ -1340,16 +1347,18 @@ CompositorOGL::EndFrame()
     }
     RefPtr<DrawTarget> target = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(rect.width, rect.height), SurfaceFormat::B8G8R8A8);
     CopyToTarget(target, nsIntPoint(), Matrix());
 
     WriteSnapshotToDumpFile(this, target);
   }
 #endif
 
+  mContextStateTracker.PopOGLSection(gl(), "Frame");
+
   mFrameInProgress = false;
 
   if (mTarget) {
     CopyToTarget(mTarget, mTargetBounds.TopLeft(), Matrix());
     mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
     mCurrentRenderTarget = nullptr;
     return;
   }
--- a/gfx/layers/opengl/CompositorOGL.h
+++ b/gfx/layers/opengl/CompositorOGL.h
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 20; 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_GFX_COMPOSITOROGL_H
 #define MOZILLA_GFX_COMPOSITOROGL_H
 
+#include "ContextStateTracker.h"
 #include "gfx2DGlue.h"
 #include "GLContextTypes.h"             // for GLContext, etc
 #include "GLDefs.h"                     // for GLuint, LOCAL_GL_TEXTURE_2D, etc
 #include "OGLShaderProgram.h"           // for ShaderProgramOGL, etc
 #include "Units.h"                      // for ScreenPoint
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/Attributes.h"         // for MOZ_OVERRIDE, MOZ_FINAL
 #include "mozilla/RefPtr.h"             // for TemporaryRef, RefPtr
@@ -382,16 +383,18 @@ private:
    * pointing upwards, but the layers/compositor coordinate system has the
    * y-axis pointing downwards, for good reason as Web pages are typically
    * scrolled downwards. So, some flipping has to take place; FlippedY does it.
    */
   GLint FlipY(GLint y) const { return mHeight - y; }
 
   RefPtr<CompositorTexturePoolOGL> mTexturePool;
 
+  ContextStateTrackerOGL mContextStateTracker;
+
   bool mDestroyed;
 
   /**
    * Height of the OpenGL context's primary framebuffer in pixels. Used by
    * FlipY for the y-flipping calculation.
    */
   GLint mHeight;
 
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -209,17 +209,17 @@ protected:
 
   virtual void TearDown()
   {
     apzc->Destroy();
   }
 
   void SetMayHaveTouchListeners()
   {
-    apzc->GetFrameMetrics().mMayHaveTouchListeners = true;
+    apzc->GetFrameMetrics().SetMayHaveTouchListeners(true);
   }
 
   void MakeApzcZoomable()
   {
     apzc->UpdateZoomConstraints(ZoomConstraints(true, true, CSSToScreenScale(0.25f), CSSToScreenScale(4.0f)));
   }
 
   void MakeApzcUnzoomable()
@@ -2171,17 +2171,17 @@ protected:
 TEST_F(APZOverscrollHandoffTester, DeferredInputEventProcessing) {
   // Set up the APZC tree.
   CreateOverscrollHandoffLayerTree1();
 
   TestAsyncPanZoomController* childApzc = ApzcOf(layers[1]);
 
   // Enable touch-listeners so that we can separate the queueing of input
   // events from them being processed.
-  childApzc->GetFrameMetrics().mMayHaveTouchListeners = true;
+  childApzc->GetFrameMetrics().SetMayHaveTouchListeners(true);
 
   // Queue input events for a pan.
   int time = 0;
   ApzcPanNoFling(childApzc, time, 90, 30);
 
   // Allow the pan to be processed.
   childApzc->ContentReceivedTouch(false);
 
@@ -2198,27 +2198,27 @@ TEST_F(APZOverscrollHandoffTester, Defer
 TEST_F(APZOverscrollHandoffTester, LayerStructureChangesWhileEventsArePending) {
   // Set up an initial APZC tree.
   CreateOverscrollHandoffLayerTree1();
 
   TestAsyncPanZoomController* childApzc = ApzcOf(layers[1]);
 
   // Enable touch-listeners so that we can separate the queueing of input
   // events from them being processed.
-  childApzc->GetFrameMetrics().mMayHaveTouchListeners = true;
+  childApzc->GetFrameMetrics().SetMayHaveTouchListeners(true);
 
   // Queue input events for a pan.
   int time = 0;
   ApzcPanNoFling(childApzc, time, 90, 30);
 
   // Modify the APZC tree to insert a new APZC 'middle' into the handoff chain
   // between the child and the root.
   CreateOverscrollHandoffLayerTree2();
   nsRefPtr<Layer> middle = layers[1];
-  childApzc->GetFrameMetrics().mMayHaveTouchListeners = true;
+  childApzc->GetFrameMetrics().SetMayHaveTouchListeners(true);
   TestAsyncPanZoomController* middleApzc = ApzcOf(middle);
 
   // Queue input events for another pan.
   ApzcPanNoFling(childApzc, time, 30, 90);
 
   // Allow the first pan to be processed.
   childApzc->ContentReceivedTouch(false);
 
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/ContextStateTracker.cpp
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ContextStateTracker.h"
+#include "GLContext.h"
+#ifdef MOZ_ENABLE_PROFILER_SPS
+#include "ProfilerMarkers.h"
+#endif
+
+namespace mozilla {
+
+void
+ContextStateTrackerOGL::PushOGLSection(GLContext* aGL, const char* aSectionName)
+{
+  if (!profiler_feature_active("gpu")) {
+    return;
+  }
+
+  if (!aGL->IsSupported(gl::GLFeature::query_objects)) {
+    return;
+  }
+
+  if (mSectionStack.Length() > 0) {
+    // We need to end the query since we're starting a new section and restore it
+    // when this section is finished.
+    aGL->fEndQuery(LOCAL_GL_TIME_ELAPSED);
+    Top().mCpuTimeEnd = TimeStamp::Now();
+  }
+
+  ContextState newSection(aSectionName);
+
+  GLuint queryObject;
+  aGL->fGenQueries(1, &queryObject);
+  newSection.mStartQueryHandle = queryObject;
+  newSection.mCpuTimeStart = TimeStamp::Now();
+
+  aGL->fBeginQuery(LOCAL_GL_TIME_ELAPSED_EXT, queryObject);
+
+  mSectionStack.AppendElement(newSection);
+}
+
+void
+ContextStateTrackerOGL::PopOGLSection(GLContext* aGL, const char* aSectionName)
+{
+  // We might have ignored a section start if we started profiling
+  // in the middle section. If so we will ignore this unmatched end.
+  if (mSectionStack.Length() == 0) {
+    return;
+  }
+
+  int i = mSectionStack.Length() - 1;
+  MOZ_ASSERT(strcmp(mSectionStack[i].mSectionName, aSectionName) == 0);
+  aGL->fEndQuery(LOCAL_GL_TIME_ELAPSED);
+  mSectionStack[i].mCpuTimeEnd = TimeStamp::Now();
+  mCompletedSections.AppendElement(mSectionStack[i]);
+  mSectionStack.RemoveElementAt(i);
+
+  if (i - 1 >= 0) {
+    const char* sectionToRestore = Top().mSectionName;
+
+    // We need to restore the outer section
+    // Well do this by completing this section and adding a new
+    // one with the same name
+    mCompletedSections.AppendElement(Top());
+    mSectionStack.RemoveElementAt(i - 1);
+
+    ContextState newSection(sectionToRestore);
+
+    GLuint queryObject;
+    aGL->fGenQueries(1, &queryObject);
+    newSection.mStartQueryHandle = queryObject;
+    newSection.mCpuTimeStart = TimeStamp::Now();
+
+    aGL->fBeginQuery(LOCAL_GL_TIME_ELAPSED_EXT, queryObject);
+
+    mSectionStack.AppendElement(newSection);
+  }
+
+  Flush(aGL);
+}
+
+void
+ContextStateTrackerOGL::Flush(GLContext* aGL)
+{
+  TimeStamp now = TimeStamp::Now();
+
+  while (mCompletedSections.Length() != 0) {
+    // On mac we see QUERY_RESULT_AVAILABLE cause a GL flush if we query it
+    // too early. For profiling we rather have the last 200ms of data missing
+    // while causing let's measurement distortions.
+    if (mCompletedSections[0].mCpuTimeEnd + TimeDuration::FromMilliseconds(200) > now) {
+      break;
+    }
+
+    GLuint handle = mCompletedSections[0].mStartQueryHandle;
+
+    // We've waiting 200ms, content rendering at > 20 FPS will be ready. We
+    // shouldn't see any flushes now.
+    GLuint returned = 0;
+    aGL->fGetQueryObjectuiv(handle, LOCAL_GL_QUERY_RESULT_AVAILABLE, &returned);
+
+    if (!returned) {
+      break;
+    }
+
+    GLuint gpuTime = 0;
+    aGL->fGetQueryObjectuiv(handle, LOCAL_GL_QUERY_RESULT, &gpuTime);
+
+    aGL->fDeleteQueries(1, &handle);
+
+#ifdef MOZ_ENABLE_PROFILER_SPS
+    PROFILER_MARKER_PAYLOAD("gpu_timer_query", new GPUMarkerPayload(
+      mCompletedSections[0].mCpuTimeStart,
+      mCompletedSections[0].mCpuTimeEnd,
+      0,
+      gpuTime
+    ));
+#endif
+
+    mCompletedSections.RemoveElementAt(0);
+  }
+}
+
+void
+ContextStateTrackerOGL::DestroyOGL(GLContext* aGL)
+{
+  while (mCompletedSections.Length() != 0) {
+    GLuint handle = (GLuint)mCompletedSections[0].mStartQueryHandle;
+    aGL->fDeleteQueries(1, &handle);
+    mCompletedSections.RemoveElementAt(0);
+  }
+}
+
+}
+
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/ContextStateTracker.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_CONTEXTSTATETRACKER_H
+#define GFX_CONTEXTSTATETRACKER_H
+
+#include "GLTypes.h"
+#include "mozilla/TimeStamp.h"
+#include "nsTArray.h"
+#include <string.h>
+
+namespace mozilla {
+namespace gl {
+class GLContext;
+}
+
+/**
+ * This class tracks the state of the context for debugging and profiling.
+ * Each section pushes a new stack entry and must be matched by an end section.
+ * All nested section must be ended before ending a parent section.
+ */
+class ContextStateTracker {
+public:
+  ContextStateTracker() {}
+
+private:
+
+  bool IsProfiling() { return true; }
+
+protected:
+  typedef GLuint TimerQueryHandle;
+
+  class ContextState {
+  public:
+    ContextState(const char* aSectionName)
+      : mSectionName(aSectionName)
+    {}
+
+    const char* mSectionName;
+    mozilla::TimeStamp mCpuTimeStart;
+    mozilla::TimeStamp mCpuTimeEnd;
+    TimerQueryHandle mStartQueryHandle;
+  };
+
+  ContextState& Top() {
+    MOZ_ASSERT(mSectionStack.Length());
+    return mSectionStack[mSectionStack.Length() - 1];
+  }
+
+  nsTArray<ContextState> mCompletedSections;
+  nsTArray<ContextState> mSectionStack;
+};
+
+/*
+class ID3D11DeviceContext;
+
+class ContextStateTrackerD3D11 MOZ_FINAL : public ContextStateTracker {
+public:
+  // TODO Implement me
+  void PushD3D11Section(ID3D11DeviceContext* aCtxt, const char* aSectionName) {}
+  void PopD3D11Section(ID3D11DeviceContext* aCtxt, const char* aSectionName) {}
+  void DestroyD3D11(ID3D11DeviceContext* aCtxt) {}
+
+private:
+  void Flush();
+};
+*/
+
+class ContextStateTrackerOGL MOZ_FINAL : public ContextStateTracker {
+  typedef mozilla::gl::GLContext GLContext;
+public:
+  void PushOGLSection(GLContext* aGL, const char* aSectionName);
+  void PopOGLSection(GLContext* aGL, const char* aSectionName);
+  void DestroyOGL(GLContext* aGL);
+private:
+  void Flush(GLContext* aGL);
+};
+
+}
+#endif
+
--- a/gfx/thebes/moz.build
+++ b/gfx/thebes/moz.build
@@ -1,15 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS += [
+    'ContextStateTracker.h',
     'DrawMode.h',
     'gfx2DGlue.h',
     'gfx3DMatrix.h',
     'gfxAlphaRecovery.h',
     'gfxASurface.h',
     'gfxBaseSharedMemorySurface.h',
     'gfxBlur.h',
     'gfxColor.h',
@@ -191,16 +192,17 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'wi
 # Are we targeting x86 or x64?  If so, build gfxAlphaRecoverySSE2.cpp.
 if CONFIG['INTEL_ARCHITECTURE']:
     SOURCES += ['gfxAlphaRecoverySSE2.cpp']
     # The file uses SSE2 intrinsics, so it needs special compile flags on some
     # compilers.
     SOURCES['gfxAlphaRecoverySSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
 
 SOURCES += [
+    'ContextStateTracker.cpp',
     # Includes mac system header conflicting with point/size,
     # and includes glxXlibSurface.h which drags in Xrender.h
     'gfxASurface.cpp',
     # on X11, gfxDrawable.cpp includes X headers for an old workaround which
     # we could consider removing soon (affects Ubuntus older than 10.04 LTS)
     # which currently prevent it from joining UNIFIED_SOURCES.
     'gfxDrawable.cpp',
     # gfxPlatform.cpp includes mac system header conflicting with point/size
--- a/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
+++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
@@ -378,24 +378,30 @@ bool Channel::ChannelImpl::EnqueueHelloM
     Close();
     return false;
   }
 
   OutputQueuePush(msg.release());
   return true;
 }
 
-static void
-ClearAndShrink(std::string& s, size_t capacity)
+void Channel::ChannelImpl::ClearAndShrinkInputOverflowBuf()
 {
-  // This swap trick is the closest thing C++ has to a guaranteed way to
-  // shrink the capacity of a string.
-  std::string tmp;
-  tmp.reserve(capacity);
-  s.swap(tmp);
+  // If input_overflow_buf_ has grown, shrink it back to its normal size.
+  static size_t previousCapacityAfterClearing = 0;
+  if (input_overflow_buf_.capacity() > previousCapacityAfterClearing) {
+    // This swap trick is the closest thing C++ has to a guaranteed way
+    // to shrink the capacity of a string.
+    std::string tmp;
+    tmp.reserve(Channel::kReadBufferSize);
+    input_overflow_buf_.swap(tmp);
+    previousCapacityAfterClearing = input_overflow_buf_.capacity();
+  } else {
+    input_overflow_buf_.clear();
+  }
 }
 
 bool Channel::ChannelImpl::Connect() {
   if (mode_ == MODE_SERVER && uses_fifo_) {
     if (server_listen_pipe_ == -1) {
       return false;
     }
     MessageLoopForIO::current()->WatchFileDescriptor(
@@ -514,17 +520,17 @@ bool Channel::ChannelImpl::ProcessIncomi
     const char *end;
     if (input_overflow_buf_.empty()) {
       overflowp = NULL;
       p = input_buf_;
       end = p + bytes_read;
     } else {
       if (input_overflow_buf_.size() >
          static_cast<size_t>(kMaximumMessageSize - bytes_read)) {
-        ClearAndShrink(input_overflow_buf_, Channel::kReadBufferSize);
+        ClearAndShrinkInputOverflowBuf();
         CHROMIUM_LOG(ERROR) << "IPC message is too big";
         return false;
       }
       input_overflow_buf_.append(input_buf_, bytes_read);
       overflowp = p = input_overflow_buf_.data();
       end = p + input_overflow_buf_.size();
     }
 
@@ -623,17 +629,17 @@ bool Channel::ChannelImpl::ProcessIncomi
         }
         p = message_tail;
       } else {
         // Last message is partial.
         break;
       }
     }
     if (end == p) {
-      ClearAndShrink(input_overflow_buf_, Channel::kReadBufferSize);
+      ClearAndShrinkInputOverflowBuf();
     } else if (!overflowp) {
       // p is from input_buf_
       input_overflow_buf_.assign(p, end - p);
     } else if (p > overflowp) {
       // p is from input_overflow_buf_
       input_overflow_buf_.erase(0, p - overflowp);
     }
     input_overflow_fds_ = std::vector<int>(&fds[fds_i], &fds[num_fds]);
--- a/ipc/chromium/src/chrome/common/ipc_channel_posix.h
+++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.h
@@ -54,16 +54,18 @@ class Channel::ChannelImpl : public Mess
  private:
   void Init(Mode mode, Listener* listener);
   bool CreatePipe(const std::wstring& channel_id, Mode mode);
   bool EnqueueHelloMessage();
 
   bool ProcessIncomingMessages();
   bool ProcessOutgoingMessages();
 
+  void ClearAndShrinkInputOverflowBuf();
+
   // MessageLoopForIO::Watcher implementation.
   virtual void OnFileCanReadWithoutBlocking(int fd);
   virtual void OnFileCanWriteWithoutBlocking(int fd);
 
 #if defined(OS_MACOSX)
   void CloseDescriptors(uint32_t pending_fd_id);
 #endif
 
--- a/js/ipc/JavaScriptBase.h
+++ b/js/ipc/JavaScriptBase.h
@@ -30,18 +30,20 @@ class JavaScriptBase : public WrapperOwn
     virtual ~JavaScriptBase() {}
 
     virtual void ActorDestroy(WrapperOwner::ActorDestroyReason why) {
         WrapperOwner::ActorDestroy(why);
     }
 
     /*** IPC handlers ***/
 
-    bool RecvPreventExtensions(const uint64_t &objId, ReturnStatus *rs) {
-        return Answer::RecvPreventExtensions(ObjectId::deserialize(objId), rs);
+    bool RecvPreventExtensions(const uint64_t &objId, ReturnStatus *rs,
+                               bool *succeeded) {
+        return Answer::RecvPreventExtensions(ObjectId::deserialize(objId), rs,
+                                             succeeded);
     }
     bool RecvGetPropertyDescriptor(const uint64_t &objId, const JSIDVariant &id,
                                      ReturnStatus *rs,
                                      PPropertyDescriptor *out) {
         return Answer::RecvGetPropertyDescriptor(ObjectId::deserialize(objId), id, rs, out);
     }
     bool RecvGetOwnPropertyDescriptor(const uint64_t &objId,
                                         const JSIDVariant &id,
@@ -126,18 +128,19 @@ class JavaScriptBase : public WrapperOwn
         return Answer::RecvDropObject(ObjectId::deserialize(objId));
     }
 
     /*** Dummy call handlers ***/
 
     bool SendDropObject(const ObjectId &objId) {
         return Base::SendDropObject(objId.serialize());
     }
-    bool SendPreventExtensions(const ObjectId &objId, ReturnStatus *rs) {
-        return Base::SendPreventExtensions(objId.serialize(), rs);
+    bool SendPreventExtensions(const ObjectId &objId, ReturnStatus *rs,
+                               bool *succeeded) {
+        return Base::SendPreventExtensions(objId.serialize(), rs, succeeded);
     }
     bool SendGetPropertyDescriptor(const ObjectId &objId, const JSIDVariant &id,
                                      ReturnStatus *rs,
                                      PPropertyDescriptor *out) {
         return Base::SendGetPropertyDescriptor(objId.serialize(), id, rs, out);
     }
     bool SendGetOwnPropertyDescriptor(const ObjectId &objId,
                                       const JSIDVariant &id,
--- a/js/ipc/PJavaScript.ipdl
+++ b/js/ipc/PJavaScript.ipdl
@@ -19,17 +19,17 @@ prio(normal upto high) sync protocol PJa
 {
     manager PContent or PContentBridge;
 
 both:
     // Sent when a CPOW has been finalized and table entries can be freed up.
     async DropObject(uint64_t objId);
 
     // These roughly map to the ProxyHandler hooks that CPOWs need.
-    prio(high) sync PreventExtensions(uint64_t objId) returns (ReturnStatus rs);
+    prio(high) sync PreventExtensions(uint64_t objId) returns (ReturnStatus rs, bool result);
     prio(high) sync GetPropertyDescriptor(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, PPropertyDescriptor result);
     prio(high) sync GetOwnPropertyDescriptor(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, PPropertyDescriptor result);
     prio(high) sync DefineProperty(uint64_t objId, JSIDVariant id, PPropertyDescriptor descriptor) returns (ReturnStatus rs);
     prio(high) sync Delete(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, bool successful);
 
     prio(high) sync Has(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, bool has);
     prio(high) sync HasOwn(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, bool has);
     prio(high) sync Get(uint64_t objId, ObjectVariant receiver, JSIDVariant id) returns (ReturnStatus rs, JSVariant result);
--- a/js/ipc/WrapperAnswer.cpp
+++ b/js/ipc/WrapperAnswer.cpp
@@ -53,27 +53,30 @@ WrapperAnswer::fail(JSContext *cx, Retur
 bool
 WrapperAnswer::ok(ReturnStatus *rs)
 {
     *rs = ReturnStatus(ReturnSuccess());
     return true;
 }
 
 bool
-WrapperAnswer::RecvPreventExtensions(const ObjectId &objId, ReturnStatus *rs)
+WrapperAnswer::RecvPreventExtensions(const ObjectId &objId, ReturnStatus *rs,
+                                     bool *succeeded)
 {
     AutoSafeJSContext cx;
     JSAutoRequest request(cx);
 
+    *succeeded = false;
+
     RootedObject obj(cx, findObjectById(cx, objId));
     if (!obj)
         return fail(cx, rs);
 
     JSAutoCompartment comp(cx, obj);
-    if (!JS_PreventExtensions(cx, obj))
+    if (!JS_PreventExtensions(cx, obj, succeeded))
         return fail(cx, rs);
 
     LOG("%s.preventExtensions()", ReceiverObj(objId));
 
     return ok(rs);
 }
 
 static void
--- a/js/ipc/WrapperAnswer.h
+++ b/js/ipc/WrapperAnswer.h
@@ -13,17 +13,18 @@
 namespace mozilla {
 namespace jsipc {
 
 class WrapperAnswer : public virtual JavaScriptShared
 {
   public:
     explicit WrapperAnswer(JSRuntime *rt) : JavaScriptShared(rt) {}
 
-    bool RecvPreventExtensions(const ObjectId &objId, ReturnStatus *rs);
+    bool RecvPreventExtensions(const ObjectId &objId, ReturnStatus *rs,
+                               bool *succeeded);
     bool RecvGetPropertyDescriptor(const ObjectId &objId, const JSIDVariant &id,
                                    ReturnStatus *rs,
                                    PPropertyDescriptor *out);
     bool RecvGetOwnPropertyDescriptor(const ObjectId &objId,
                                       const JSIDVariant &id,
                                       ReturnStatus *rs,
                                       PPropertyDescriptor *out);
     bool RecvDefineProperty(const ObjectId &objId, const JSIDVariant &id,
--- a/js/ipc/WrapperOwner.cpp
+++ b/js/ipc/WrapperOwner.cpp
@@ -66,18 +66,18 @@ class CPOWProxyHandler : public BaseProx
     virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                           MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
                                 MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool ownPropertyKeys(JSContext *cx, HandleObject proxy,
                                  AutoIdVector &props) const MOZ_OVERRIDE;
     virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
     virtual bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE;
+    virtual bool preventExtensions(JSContext *cx, HandleObject proxy, bool *succeeded) const MOZ_OVERRIDE;
     virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
-    virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
     virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
     virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver,
                      HandleId id, MutableHandleValue vp) const MOZ_OVERRIDE;
     virtual bool set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
                      JS::HandleId id, bool strict, JS::MutableHandleValue vp) const MOZ_OVERRIDE;
     virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
     virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
 
@@ -108,36 +108,16 @@ const CPOWProxyHandler CPOWProxyHandler:
     WrapperOwner *owner = OwnerOf(proxy);                               \
     if (!owner->active()) {                                             \
         JS_ReportError(cx, "cannot use a CPOW whose process is gone");  \
         return false;                                                   \
     }                                                                   \
     return owner->call args;
 
 bool
-CPOWProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy) const
-{
-    FORWARD(preventExtensions, (cx, proxy));
-}
-
-bool
-WrapperOwner::preventExtensions(JSContext *cx, HandleObject proxy)
-{
-    ObjectId objId = idOf(proxy);
-
-    ReturnStatus status;
-    if (!SendPreventExtensions(objId, &status))
-        return ipcfail(cx);
-
-    LOG_STACK();
-
-    return ok(cx, status);
-}
-
-bool
 CPOWProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                         MutableHandle<JSPropertyDescriptor> desc) const
 {
     FORWARD(getPropertyDescriptor, (cx, proxy, id, desc));
 }
 
 bool
 WrapperOwner::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
@@ -471,16 +451,36 @@ CPOWProxyHandler::getOwnEnumerableProper
 
 bool
 WrapperOwner::getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
 {
     return getPropertyKeys(cx, proxy, JSITER_OWNONLY, props);
 }
 
 bool
+CPOWProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy, bool *succeeded) const
+{
+    FORWARD(preventExtensions, (cx, proxy, succeeded));
+}
+
+bool
+WrapperOwner::preventExtensions(JSContext *cx, HandleObject proxy, bool *succeeded)
+{
+    ObjectId objId = idOf(proxy);
+
+    ReturnStatus status;
+    if (!SendPreventExtensions(objId, &status, succeeded))
+        return ipcfail(cx);
+
+    LOG_STACK();
+
+    return ok(cx, status);
+}
+
+bool
 CPOWProxyHandler::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const
 {
     FORWARD(isExtensible, (cx, proxy, extensible));
 }
 
 bool
 WrapperOwner::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible)
 {
--- a/js/ipc/WrapperOwner.h
+++ b/js/ipc/WrapperOwner.h
@@ -35,18 +35,18 @@ class WrapperOwner : public virtual Java
     // (The traps should be in the same order like js/src/jsproxy.h)
     bool getOwnPropertyDescriptor(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
                                   JS::MutableHandle<JSPropertyDescriptor> desc);
     bool defineProperty(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
                         JS::MutableHandle<JSPropertyDescriptor> desc);
     bool ownPropertyKeys(JSContext *cx, JS::HandleObject proxy, JS::AutoIdVector &props);
     bool delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp);
     bool enumerate(JSContext *cx, JS::HandleObject proxy, JS::AutoIdVector &props);
+    bool preventExtensions(JSContext *cx, JS::HandleObject proxy, bool *succeeded);
     bool isExtensible(JSContext *cx, JS::HandleObject proxy, bool *extensible);
-    bool preventExtensions(JSContext *cx, JS::HandleObject proxy);
     bool has(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp);
     bool get(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
              JS::HandleId id, JS::MutableHandleValue vp);
     bool set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
              JS::HandleId id, bool strict, JS::MutableHandleValue vp);
     bool callOrConstruct(JSContext *cx, JS::HandleObject proxy, const JS::CallArgs &args,
                          bool construct);
 
@@ -101,17 +101,18 @@ class WrapperOwner : public virtual Java
     // Check whether a return status is okay, and if not, propagate its error.
     bool ok(JSContext *cx, const ReturnStatus &status);
 
     bool inactive_;
 
     /*** Dummy call handlers ***/
   public:
     virtual bool SendDropObject(const ObjectId &objId) = 0;
-    virtual bool SendPreventExtensions(const ObjectId &objId, ReturnStatus *rs) = 0;
+    virtual bool SendPreventExtensions(const ObjectId &objId, ReturnStatus *rs,
+                                       bool *succeeded) = 0;
     virtual bool SendGetPropertyDescriptor(const ObjectId &objId, const JSIDVariant &id,
                                            ReturnStatus *rs,
                                            PPropertyDescriptor *out) = 0;
     virtual bool SendGetOwnPropertyDescriptor(const ObjectId &objId,
                                               const JSIDVariant &id,
                                               ReturnStatus *rs,
                                               PPropertyDescriptor *out) = 0;
     virtual bool SendDefineProperty(const ObjectId &objId, const JSIDVariant &id,
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -670,45 +670,16 @@ class RetType
           case Float32x4: return MIRType_Float32x4;
         }
         MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected return type");
     }
     bool operator==(RetType rhs) const { return which_ == rhs.which_; }
     bool operator!=(RetType rhs) const { return which_ != rhs.which_; }
 };
 
-// Represents the subset of Type that can be used as a return type of a builtin
-// Math function.
-class MathRetType
-{
-  public:
-    enum Which {
-        Double   = Type::Double,
-        Float    = Type::Float,
-        Floatish = Type::Floatish,
-        Signed   = Type::Signed,
-        Unsigned = Type::Unsigned
-    };
-
-  private:
-    Which which_;
-
-  public:
-    MathRetType() : which_(Which(-1)) {}
-    MOZ_IMPLICIT MathRetType(Which w) : which_(w) {}
-
-    Type toType() const {
-        return Type(Type::Which(which_));
-    }
-
-    Which which() const {
-        return which_;
-    }
-};
-
 namespace {
 
 // Represents the subset of Type that can be used as a variable or
 // argument's type. Note: AsmJSCoercion and VarType are kept separate to
 // make very clear the signed/int distinction: a coercion may explicitly sign
 // an *expression* but, when stored as a variable, this signedness information
 // is explicitly thrown away by the asm.js type system. E.g., in
 //
@@ -4403,17 +4374,17 @@ CheckAssign(FunctionCompiler &f, ParseNo
 
     if (lhs->getKind() == PNK_NAME)
         return CheckAssignName(f, lhs, rhs, def, type);
 
     return f.fail(assign, "left-hand side of assignment must be a variable or array access");
 }
 
 static bool
-CheckMathIMul(FunctionCompiler &f, ParseNode *call, MDefinition **def, MathRetType *type)
+CheckMathIMul(FunctionCompiler &f, ParseNode *call, MDefinition **def, Type *type)
 {
     if (CallArgListLength(call) != 2)
         return f.fail(call, "Math.imul must be passed 2 arguments");
 
     ParseNode *lhs = CallArgList(call);
     ParseNode *rhs = NextNode(lhs);
 
     MDefinition *lhsDef;
@@ -4427,143 +4398,141 @@ CheckMathIMul(FunctionCompiler &f, Parse
         return false;
 
     if (!lhsType.isIntish())
         return f.failf(lhs, "%s is not a subtype of intish", lhsType.toChars());
     if (!rhsType.isIntish())
         return f.failf(rhs, "%s is not a subtype of intish", rhsType.toChars());
 
     *def = f.mul(lhsDef, rhsDef, MIRType_Int32, MMul::Integer);
-    *type = MathRetType::Signed;
+    *type = Type::Signed;
     return true;
 }
 
 static bool
-CheckMathClz32(FunctionCompiler &f, ParseNode *call, MDefinition **def, MathRetType *type)
+CheckMathClz32(FunctionCompiler &f, ParseNode *call, MDefinition **def, Type *type)
 {
     if (CallArgListLength(call) != 1)
         return f.fail(call, "Math.clz32 must be passed 1 argument");
 
     ParseNode *arg = CallArgList(call);
 
     MDefinition *argDef;
     Type argType;
     if (!CheckExpr(f, arg, &argDef, &argType))
         return false;
 
     if (!argType.isIntish())
         return f.failf(arg, "%s is not a subtype of intish", argType.toChars());
 
     *def = f.unary<MClz>(argDef);
-    *type = MathRetType::Signed;
+    *type = Type::Fixnum;
     return true;
 }
 
 static bool
-CheckMathAbs(FunctionCompiler &f, ParseNode *call, MDefinition **def, MathRetType *type)
+CheckMathAbs(FunctionCompiler &f, ParseNode *call, MDefinition **def, Type *type)
 {
     if (CallArgListLength(call) != 1)
         return f.fail(call, "Math.abs must be passed 1 argument");
 
     ParseNode *arg = CallArgList(call);
 
     MDefinition *argDef;
     Type argType;
     if (!CheckExpr(f, arg, &argDef, &argType))
         return false;
 
     if (argType.isSigned()) {
         *def = f.unary<MAbs>(argDef, MIRType_Int32);
-        *type = MathRetType::Unsigned;
+        *type = Type::Unsigned;
         return true;
     }
 
     if (argType.isMaybeDouble()) {
         *def = f.unary<MAbs>(argDef, MIRType_Double);
-        *type = MathRetType::Double;
+        *type = Type::Double;
         return true;
     }
 
     if (argType.isMaybeFloat()) {
         *def = f.unary<MAbs>(argDef, MIRType_Float32);
-        *type = MathRetType::Floatish;
+        *type = Type::Floatish;
         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, MDefinition **def, MathRetType *type)
+CheckMathSqrt(FunctionCompiler &f, ParseNode *call, 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.isMaybeDouble()) {
         *def = f.unary<MSqrt>(argDef, MIRType_Double);
-        *type = MathRetType::Double;
+        *type = Type::Double;
         return true;
     }
 
     if (argType.isMaybeFloat()) {
         *def = f.unary<MSqrt>(argDef, MIRType_Float32);
-        *type = MathRetType::Floatish;
+        *type = Type::Floatish;
         return true;
     }
 
     return f.failf(call, "%s is neither a subtype of double? nor float?", argType.toChars());
 }
 
 static bool
-CheckMathMinMax(FunctionCompiler &f, ParseNode *callNode, MDefinition **def, bool isMax,
-                MathRetType *type)
+CheckMathMinMax(FunctionCompiler &f, ParseNode *callNode, MDefinition **def, bool isMax, Type *type)
 {
     if (CallArgListLength(callNode) < 2)
         return f.fail(callNode, "Math.min/max must be passed at least 2 arguments");
 
     ParseNode *firstArg = CallArgList(callNode);
     MDefinition *firstDef;
     Type firstType;
     if (!CheckExpr(f, firstArg, &firstDef, &firstType))
         return false;
 
     if (firstType.isMaybeDouble()) {
-        *type = MathRetType::Double;
+        *type = Type::Double;
         firstType = Type::MaybeDouble;
     } else if (firstType.isMaybeFloat()) {
-        *type = MathRetType::Float;
+        *type = Type::Float;
         firstType = Type::MaybeFloat;
-    } else if (firstType.isInt()) {
-        *type = MathRetType::Signed;
-        firstType = Type::Int;
+    } else if (firstType.isSigned()) {
+        *type = Type::Signed;
+        firstType = Type::Signed;
     } else {
         return f.failf(firstArg, "%s is not a subtype of double?, float? or int",
                        firstType.toChars());
     }
 
-    MIRType opType = firstType.toMIRType();
     MDefinition *lastDef = firstDef;
     ParseNode *nextArg = NextNode(firstArg);
     for (unsigned i = 1; i < CallArgListLength(callNode); i++, nextArg = NextNode(nextArg)) {
         MDefinition *nextDef;
         Type nextType;
         if (!CheckExpr(f, nextArg, &nextDef, &nextType))
             return false;
 
         if (!(nextType <= firstType))
             return f.failf(nextArg, "%s is not a subtype of %s", nextType.toChars(), firstType.toChars());
 
-        lastDef = f.minMax(lastDef, nextDef, opType, isMax);
+        lastDef = f.minMax(lastDef, nextDef, firstType.toMIRType(), isMax);
     }
 
     *def = lastDef;
     return true;
 }
 
 typedef bool (*CheckArgType)(FunctionCompiler &f, ParseNode *argNode, Type type);
 
@@ -4829,36 +4798,36 @@ CheckCoercionArg(FunctionCompiler &f, Pa
         MOZ_CRASH("not call coercions");
     }
 
     *type = retType.toType();
     return true;
 }
 
 static bool
-CheckMathFRound(FunctionCompiler &f, ParseNode *callNode, MDefinition **def, MathRetType *type)
+CheckMathFRound(FunctionCompiler &f, ParseNode *callNode, MDefinition **def, Type *type)
 {
     if (CallArgListLength(callNode) != 1)
         return f.fail(callNode, "Math.fround must be passed 1 argument");
 
     ParseNode *argNode = CallArgList(callNode);
     MDefinition *argDef;
     Type argType;
     if (!CheckCoercionArg(f, argNode, AsmJS_FRound, &argDef, &argType))
         return false;
 
     MOZ_ASSERT(argType == Type::Float);
     *def = argDef;
-    *type = MathRetType::Float;
+    *type = Type::Float;
     return true;
 }
 
 static bool
 CheckMathBuiltinCall(FunctionCompiler &f, ParseNode *callNode, AsmJSMathBuiltinFunction func,
-                     MDefinition **def, MathRetType *type)
+                     MDefinition **def, Type *type)
 {
     unsigned arity = 0;
     AsmJSImmKind doubleCallee, floatCallee;
     switch (func) {
       case AsmJSMathBuiltin_imul:   return CheckMathIMul(f, callNode, def, type);
       case AsmJSMathBuiltin_clz32:  return CheckMathClz32(f, callNode, def, type);
       case AsmJSMathBuiltin_abs:    return CheckMathAbs(f, callNode, def, type);
       case AsmJSMathBuiltin_sqrt:   return CheckMathSqrt(f, callNode, def, type);
@@ -4921,23 +4890,23 @@ CheckMathBuiltinCall(FunctionCompiler &f
     }
 
     f.finishCallArgs(&call);
 
     AsmJSImmKind callee = opIsDouble ? doubleCallee : floatCallee;
     if (!f.builtinCall(callee, call, varType.toMIRType(), def))
         return false;
 
-    *type = MathRetType(opIsDouble ? MathRetType::Double : MathRetType::Floatish);
+    *type = opIsDouble ? Type::Double : Type::Floatish;
     return true;
 }
 
 typedef Vector<MDefinition*, 4, SystemAllocPolicy> DefinitionVector;
 
-namespace {  
+namespace {
 // Include CheckSimdCallArgs in unnamed namespace to avoid MSVC name lookup bug.
 
 template<class CheckArgOp>
 static bool
 CheckSimdCallArgs(FunctionCompiler &f, ParseNode *call, unsigned expectedArity,
                   const CheckArgOp &checkArg, DefinitionVector *defs)
 {
     unsigned numArgs = CallArgListLength(call);
@@ -5343,24 +5312,18 @@ CheckSimdCtorCall(FunctionCompiler &f, P
 
 static bool
 CheckUncoercedCall(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type)
 {
     MOZ_ASSERT(expr->isKind(PNK_CALL));
 
     const ModuleCompiler::Global *global;
     if (IsCallToGlobal(f.m(), expr, &global)) {
-        if (global->isMathFunction()) {
-            MathRetType mathRetType;
-            if (!CheckMathBuiltinCall(f, expr, global->mathBuiltinFunction(), def, &mathRetType))
-                return false;
-            *type = mathRetType.toType();
-            return true;
-        }
-
+        if (global->isMathFunction())
+            return CheckMathBuiltinCall(f, expr, global->mathBuiltinFunction(), def, type);
         if (global->isSimdCtor())
             return CheckSimdCtorCall(f, expr, global, def, type);
         if (global->isSimdOperation())
             return CheckSimdOperationCall(f, expr, global, def, type);
     }
 
     return f.fail(expr, "all function calls must either be calls to standard lib math functions, "
                         "ignored (via f(); or comma-expression), coerced to signed (via f()|0), "
@@ -5430,21 +5393,21 @@ CoerceResult(FunctionCompiler &f, ParseN
 
     return true;
 }
 
 static bool
 CheckCoercedMathBuiltinCall(FunctionCompiler &f, ParseNode *callNode, AsmJSMathBuiltinFunction func,
                             RetType retType, MDefinition **def, Type *type)
 {
-    MDefinition *result;
-    MathRetType resultType;
-    if (!CheckMathBuiltinCall(f, callNode, func, &result, &resultType))
-        return false;
-    return CoerceResult(f, callNode, retType, result, resultType.toType(), def, type);
+    MDefinition *resultDef;
+    Type resultType;
+    if (!CheckMathBuiltinCall(f, callNode, func, &resultDef, &resultType))
+        return false;
+    return CoerceResult(f, callNode, retType, resultDef, resultType, def, type);
 }
 
 static bool
 CheckCoercedSimdCall(FunctionCompiler &f, ParseNode *call, const ModuleCompiler::Global *global,
                      RetType retType, MDefinition **def, Type *type)
 {
     if (global->isSimdCtor()) {
         if (!CheckSimdCtorCall(f, call, global, def, type))
new file mode 100644
--- /dev/null
+++ b/js/src/builtin/AtomicsObject.cpp
@@ -0,0 +1,1090 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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/. */
+
+/*
+ * JS Atomics pseudo-module.
+ *
+ * See "Spec: JavaScript Shared Memory, Atomics, and Locks" for the
+ * full specification.
+ *
+ * In addition to what is specified there, we throw an Error object if
+ * the futex API hooks have not been installed on the runtime.
+ * Essentially that is an implementation error at a higher level.
+ *
+ *
+ * Note on the current implementation of atomic operations.
+ *
+ * The Mozilla atomics are not sufficient to implement these APIs
+ * because we need to support 8-bit, 16-bit, and 32-bit data: the
+ * Mozilla atomics only support 32-bit data.
+ *
+ * At the moment we include mozilla/Atomics.h, which will define
+ * MOZ_HAVE_CXX11_ATOMICS and include <atomic> if we have C++11
+ * atomics.
+ *
+ * If MOZ_HAVE_CXX11_ATOMICS is set we'll use C++11 atomics.
+ *
+ * Otherwise, if the compiler has them we'll fall back on gcc/Clang
+ * intrinsics.
+ *
+ * Otherwise, if we're on VC++2012, we'll use C++11 atomics even if
+ * MOZ_HAVE_CXX11_ATOMICS is not defined.  The compiler has the
+ * atomics but they are disabled in Mozilla due to a performance bug.
+ * That performance bug does not affect the Atomics code.  See
+ * mozilla/Atomics.h for further comments on that bug.
+ *
+ * Otherwise, if we're on VC++2010 or VC++2008, we'll emulate the
+ * gcc/Clang intrinsics with simple code below using the VC++
+ * intrinsics, like the VC++2012 solution this is a stopgap since
+ * we're about to start using VC++2013 anyway.
+ *
+ * If none of those options are available then the build must disable
+ * shared memory, or compilation will fail with a predictable error.
+ */
+
+#include "builtin/AtomicsObject.h"
+
+#include "mozilla/Atomics.h"
+
+#include "jsapi.h"
+#include "jsfriendapi.h"
+
+#include "vm/GlobalObject.h"
+#include "vm/SharedTypedArrayObject.h"
+#include "vm/TypedArrayObject.h"
+
+#include "jsobjinlines.h"
+
+using namespace js;
+
+#if defined(MOZ_HAVE_CXX11_ATOMICS)
+# define CXX11_ATOMICS
+#elif defined(__clang__) || defined(__GNUC__)
+# define GNU_ATOMICS
+#elif _MSC_VER >= 1700 && _MSC_VER < 1800
+// Visual Studion 2012
+# define CXX11_ATOMICS
+# include <atomic>
+#elif defined(_MSC_VER)
+// Visual Studio 2010
+# define GNU_ATOMICS
+static inline void
+__sync_synchronize()
+{
+# if JS_BITS_PER_WORD == 32
+    // If configured for SSE2+ we can use the MFENCE instruction, available
+    // through the _mm_mfence intrinsic.  But for non-SSE2 systems we have
+    // to do something else.  Linux uses "lock add [esp], 0", so why not?
+    __asm lock add [esp], 0;
+# else
+    _mm_mfence();
+# endif
+}
+
+# define MSC_CAS(T, U, cmpxchg) \
+    static inline T \
+    __sync_val_compare_and_swap(T *addr, T oldval, T newval) { \
+        return (T)cmpxchg((U volatile*)addr, (U)oldval, (U)newval); \
+    }
+
+MSC_CAS(int8_t, char, _InterlockedCompareExchange8)
+MSC_CAS(uint8_t, char, _InterlockedCompareExchange8)
+MSC_CAS(int16_t, short, _InterlockedCompareExchange16)
+MSC_CAS(uint16_t, short, _InterlockedCompareExchange16)
+MSC_CAS(int32_t, long, _InterlockedCompareExchange)
+MSC_CAS(uint32_t, long, _InterlockedCompareExchange)
+
+# define MSC_FETCHADDOP(T, U, xadd) \
+    static inline T \
+    __sync_fetch_and_add(T *addr, T val) { \
+        return (T)xadd((U volatile*)addr, (U)val); \
+    } \
+    static inline T \
+    __sync_fetch_and_sub(T *addr, T val) { \
+        return (T)xadd((U volatile*)addr, (U)-val); \
+    }
+
+MSC_FETCHADDOP(int8_t, char, _InterlockedExchangeAdd8)
+MSC_FETCHADDOP(uint8_t, char, _InterlockedExchangeAdd8)
+MSC_FETCHADDOP(int16_t, short, _InterlockedExchangeAdd16)
+MSC_FETCHADDOP(uint16_t, short, _InterlockedExchangeAdd16)
+MSC_FETCHADDOP(int32_t, long, _InterlockedExchangeAdd)
+MSC_FETCHADDOP(uint32_t, long, _InterlockedExchangeAdd)
+
+# define MSC_FETCHBITOP(T, U, andop, orop, xorop) \
+    static inline T \
+    __sync_fetch_and_and(T *addr, T val) { \
+        return (T)andop((U volatile*)addr, (U)val);  \
+    } \
+    static inline T \
+    __sync_fetch_and_or(T *addr, T val) { \
+        return (T)orop((U volatile*)addr, (U)val);  \
+    } \
+    static inline T \
+    __sync_fetch_and_xor(T *addr, T val) { \
+        return (T)xorop((U volatile*)addr, (U)val);  \
+    } \
+
+MSC_FETCHBITOP(int8_t, char, _InterlockedAnd8, _InterlockedOr8, _InterlockedXor8)
+MSC_FETCHBITOP(uint8_t, char, _InterlockedAnd8, _InterlockedOr8, _InterlockedXor8)
+MSC_FETCHBITOP(int16_t, short, _InterlockedAnd16, _InterlockedOr16, _InterlockedXor16)
+MSC_FETCHBITOP(uint16_t, short, _InterlockedAnd16, _InterlockedOr16, _InterlockedXor16)
+MSC_FETCHBITOP(int32_t, long,  _InterlockedAnd, _InterlockedOr, _InterlockedXor)
+MSC_FETCHBITOP(uint32_t, long, _InterlockedAnd, _InterlockedOr, _InterlockedXor)
+
+# undef MSC_CAS
+# undef MSC_FETCHADDOP
+# undef MSC_FETCHBITOP
+
+#elif defined(ENABLE_SHARED_ARRAY_BUFFER)
+# error "Either disable JS shared memory or use a compiler that supports C++11 atomics or GCC/clang atomics"
+#endif
+
+const Class AtomicsObject::class_ = {
+    "Atomics",
+    JSCLASS_HAS_CACHED_PROTO(JSProto_Atomics),
+    JS_PropertyStub,
+    JS_DeletePropertyStub,
+    JS_PropertyStub,
+    JS_StrictPropertyStub,
+    JS_EnumerateStub,
+    JS_ResolveStub,
+    JS_ConvertStub,
+    nullptr,                 // finalize
+    nullptr,                 // call
+    nullptr,                 // hasInstance
+    nullptr,                 // construct
+    nullptr                  // trace
+};
+
+static bool
+ReportBadArrayType(JSContext *cx)
+{
+    JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ATOMICS_BAD_ARRAY);
+    return false;
+}
+
+static bool
+ReportNoFutexes(JSContext *cx)
+{
+    JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ATOMICS_NOT_INSTALLED);
+    return false;
+}
+
+static bool
+GetSharedTypedArray(JSContext *cx, HandleValue v,
+                    MutableHandle<SharedTypedArrayObject *> viewp)
+{
+    if (!v.isObject())
+        return ReportBadArrayType(cx);
+    if (!v.toObject().is<SharedTypedArrayObject>())
+        return ReportBadArrayType(cx);
+    viewp.set(&v.toObject().as<SharedTypedArrayObject>());
+    return true;
+}
+
+// Returns true so long as the conversion succeeds, and then *inRange
+// is set to false if the index is not in range.
+static bool
+GetSharedTypedArrayIndex(JSContext *cx, HandleValue v, Handle<SharedTypedArrayObject *> view,
+                         uint32_t* offset, bool* inRange)
+{
+    RootedId id(cx);
+    if (!ValueToId<CanGC>(cx, v, &id))
+        return false;
+    uint64_t index;
+    if (!IsTypedArrayIndex(id, &index) || index >= view->length()) {
+        *inRange = false;
+    } else {
+        *offset = (uint32_t)index;
+        *inRange = true;
+    }
+    return true;
+}
+
+void
+js::atomics_fullMemoryBarrier()
+{
+#if defined(CXX11_ATOMICS)
+    std::atomic_thread_fence(std::memory_order_seq_cst);
+#elif defined(GNU_ATOMICS)
+    __sync_synchronize();
+#endif
+}
+
+static bool
+atomics_fence_impl(JSContext *cx, MutableHandleValue r)
+{
+    atomics_fullMemoryBarrier();
+    r.setUndefined();
+    return true;
+}
+
+bool
+js::atomics_fence(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    return atomics_fence_impl(cx, args.rval());
+}
+
+bool
+js::atomics_compareExchange(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    HandleValue objv = args.get(0);
+    HandleValue idxv = args.get(1);
+    HandleValue oldv = args.get(2);
+    HandleValue newv = args.get(3);
+    MutableHandleValue r = args.rval();
+
+    Rooted<SharedTypedArrayObject *> view(cx, nullptr);
+    if (!GetSharedTypedArray(cx, objv, &view))
+        return false;
+    uint32_t offset;
+    bool inRange;
+    if (!GetSharedTypedArrayIndex(cx, idxv, view, &offset, &inRange))
+        return false;
+    int32_t oldCandidate;
+    if (!ToInt32(cx, oldv, &oldCandidate))
+        return false;
+    int32_t newCandidate;
+    if (!ToInt32(cx, newv, &newCandidate))
+        return false;
+
+    if (!inRange)
+        return atomics_fence_impl(cx, r);
+
+    // CAS always sets oldval to the old value of the cell.
+    // addr must be a T*, and oldval and newval should be variables of type T
+
+#if defined(CXX11_ATOMICS)
+# define CAS(T, addr, oldval, newval)                                    \
+    do {                                                                \
+        std::atomic_compare_exchange_strong(reinterpret_cast<std::atomic<T>*>(addr), &oldval, newval); \
+    } while(0)
+#elif defined(GNU_ATOMICS)
+# define CAS(T, addr, oldval, newval)                                    \
+    do {                                                                \
+        oldval = __sync_val_compare_and_swap(addr, (oldval), (newval)); \
+    } while(0)
+#else
+# define CAS(a, b, c, newval)  (void)newval
+#endif
+
+    switch (view->type()) {
+      case Scalar::Int8: {
+          int8_t oldval = (int8_t)oldCandidate;
+          int8_t newval = (int8_t)newCandidate;
+          CAS(int8_t, (int8_t*)view->viewData() + offset, oldval, newval);
+          r.setInt32(oldval);
+          return true;
+      }
+      case Scalar::Uint8: {
+          uint8_t oldval = (uint8_t)oldCandidate;
+          uint8_t newval = (uint8_t)newCandidate;
+          CAS(uint8_t, (uint8_t*)view->viewData() + offset, oldval, newval);
+          r.setInt32(oldval);
+          return true;
+      }
+      case Scalar::Uint8Clamped: {
+          uint8_t oldval = ClampIntForUint8Array(oldCandidate);
+          uint8_t newval = ClampIntForUint8Array(newCandidate);
+          CAS(uint8_t, (uint8_t*)view->viewData() + offset, oldval, newval);
+          r.setInt32(oldval);
+          return true;
+      }
+      case Scalar::Int16: {
+          int16_t oldval = (int16_t)oldCandidate;
+          int16_t newval = (int16_t)newCandidate;
+          CAS(int16_t, (int16_t*)view->viewData() + offset, oldval, newval);
+          r.setInt32(oldval);
+          return true;
+      }
+      case Scalar::Uint16: {
+          uint16_t oldval = (uint16_t)oldCandidate;
+          uint16_t newval = (uint16_t)newCandidate;
+          CAS(uint16_t, (uint16_t*)view->viewData() + offset, oldval, newval);
+          r.setInt32(oldval);
+          return true;
+      }
+      case Scalar::Int32: {
+          int32_t oldval = oldCandidate;
+          int32_t newval = newCandidate;
+          CAS(int32_t, (int32_t*)view->viewData() + offset, oldval, newval);
+          r.setInt32(oldval);
+          return true;
+      }
+      case Scalar::Uint32: {
+          uint32_t oldval = (uint32_t)oldCandidate;
+          uint32_t newval = (uint32_t)newCandidate;
+          CAS(uint32_t, (uint32_t*)view->viewData() + offset, oldval, newval);
+          r.setNumber((double)oldval);
+          return true;
+      }
+      default:
+        return ReportBadArrayType(cx);
+    }
+
+    // Do not undef CAS, it is used later
+}
+
+bool
+js::atomics_load(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    HandleValue objv = args.get(0);
+    HandleValue idxv = args.get(1);
+    MutableHandleValue r = args.rval();
+
+    Rooted<SharedTypedArrayObject *> view(cx, nullptr);
+    if (!GetSharedTypedArray(cx, objv, &view))
+        return false;
+    uint32_t offset;
+    bool inRange;
+    if (!GetSharedTypedArrayIndex(cx, idxv, view, &offset, &inRange))
+        return false;
+
+    if (!inRange)
+        return atomics_fence_impl(cx, r);
+
+    // LOAD sets v to the value of *addr
+    // addr must be a T*, and v must be a variable of type T
+
+#if defined(CXX11_ATOMICS)
+# define LOAD(T, addr, v)                                                \
+    do {                                                                \
+        v = std::atomic_load(reinterpret_cast<std::atomic<T>*>(addr));  \
+    } while(0)
+#elif defined(GNU_ATOMICS)
+# define LOAD(T, addr, v)                        \
+    do {                                        \
+        __sync_synchronize();                   \
+        v = *(addr);                            \
+        __sync_synchronize();                   \
+    } while(0)
+#else
+# define LOAD(a, b, v)  v = 0
+#endif
+
+    switch (view->type()) {
+      case Scalar::Uint8:
+      case Scalar::Uint8Clamped: {
+          uint8_t v;
+          LOAD(uint8_t, (uint8_t*)view->viewData() + offset, v);
+          r.setInt32(v);
+          return true;
+      }
+      case Scalar::Int8: {
+          int8_t v;
+          LOAD(int8_t, (int8_t*)view->viewData() + offset, v);
+          r.setInt32(v);
+          return true;
+      }
+      case Scalar::Int16: {
+          int16_t v;
+          LOAD(int16_t, (int16_t*)view->viewData() + offset, v);
+          r.setInt32(v);
+          return true;
+      }
+      case Scalar::Uint16: {
+          uint16_t v;
+          LOAD(uint16_t, (uint16_t*)view->viewData() + offset, v);
+          r.setInt32(v);
+          return true;
+      }
+      case Scalar::Int32: {
+          int32_t v;
+          LOAD(int32_t, (int32_t*)view->viewData() + offset, v);
+          r.setInt32(v);
+          return true;
+      }
+      case Scalar::Uint32: {
+          uint32_t v;
+          LOAD(uint32_t, (uint32_t*)view->viewData() + offset, v);
+          r.setNumber(v);
+          return true;
+      }
+      default:
+          return ReportBadArrayType(cx);
+    }
+
+#undef LOAD
+
+}
+
+bool
+js::atomics_store(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    HandleValue objv = args.get(0);
+    HandleValue idxv = args.get(1);
+    HandleValue valv = args.get(2);
+    MutableHandleValue r = args.rval();
+
+    Rooted<SharedTypedArrayObject *> view(cx, nullptr);
+    if (!GetSharedTypedArray(cx, objv, &view))
+        return false;
+    uint32_t offset;
+    bool inRange;
+    if (!GetSharedTypedArrayIndex(cx, idxv, view, &offset, &inRange))
+        return false;
+    int32_t numberValue;
+    if (!ToInt32(cx, valv, &numberValue))
+        return false;
+
+    if (!inRange) {
+        atomics_fullMemoryBarrier();
+        r.set(valv);
+        return true;
+    }
+
+    // STORE stores value in *addr
+    // addr must be a T*, and value should be of type T
+
+#if defined(CXX11_ATOMICS)
+# define STORE(T, addr, value)                                           \
+    do {                                                                \
+        std::atomic_store(reinterpret_cast<std::atomic<T>*>(addr), (T)value); \
+} while(0)
+#elif defined(GNU_ATOMICS)
+# define STORE(T, addr, value)                   \
+    do {                                        \
+        __sync_synchronize();                   \
+        *(addr) = value;                        \
+        __sync_synchronize();                   \
+    } while(0)
+#else
+# define STORE(a, b, c)  (void)0
+#endif
+
+    switch (view->type()) {
+      case Scalar::Int8: {
+          int8_t value = (int8_t)numberValue;
+          STORE(int8_t, (int8_t*)view->viewData() + offset, value);
+          r.setInt32(value);
+          return true;
+      }
+      case Scalar::Uint8: {
+          uint8_t value = (uint8_t)numberValue;
+          STORE(uint8_t, (uint8_t*)view->viewData() + offset, value);
+          r.setInt32(value);
+          return true;
+      }
+      case Scalar::Uint8Clamped: {
+          uint8_t value = ClampIntForUint8Array(numberValue);
+          STORE(uint8_t, (uint8_t*)view->viewData() + offset, value);
+          r.setInt32(value);
+          return true;
+      }
+      case Scalar::Int16: {
+          int16_t value = (int16_t)numberValue;
+          STORE(int16_t, (int16_t*)view->viewData() + offset, value);
+          r.setInt32(value);
+          return true;
+      }
+      case Scalar::Uint16: {
+          uint16_t value = (uint16_t)numberValue;
+          STORE(uint16_t, (uint16_t*)view->viewData() + offset, value);
+          r.setInt32(value);
+          return true;
+      }
+      case Scalar::Int32: {
+          int32_t value = numberValue;
+          STORE(int32_t, (int32_t*)view->viewData() + offset, value);
+          r.setInt32(value);
+          return true;
+      }
+      case Scalar::Uint32: {
+          uint32_t value = (uint32_t)numberValue;
+          STORE(uint32_t, (uint32_t*)view->viewData() + offset, value);
+          r.setNumber((double)value);
+          return true;
+      }
+      default:
+        return ReportBadArrayType(cx);
+    }
+
+#undef STORE
+}
+
+template<typename T>
+static bool
+atomics_binop_impl(JSContext *cx, HandleValue objv, HandleValue idxv, HandleValue valv,
+                   MutableHandleValue r)
+{
+    Rooted<SharedTypedArrayObject *> view(cx, nullptr);
+    if (!GetSharedTypedArray(cx, objv, &view))
+        return false;
+    uint32_t offset;
+    bool inRange;
+    if (!GetSharedTypedArrayIndex(cx, idxv, view, &offset, &inRange))
+        return false;
+    int32_t numberValue;
+    if (!ToInt32(cx, valv, &numberValue))
+        return false;
+
+    if (!inRange)
+        return atomics_fence_impl(cx, r);
+
+    switch (view->type()) {
+      case Scalar::Int8: {
+          int8_t v = (int8_t)numberValue;
+          r.setInt32(T::operate((int8_t*)view->viewData() + offset, v));
+          return true;
+      }
+      case Scalar::Uint8: {
+          uint8_t v = (uint8_t)numberValue;
+          r.setInt32(T::operate((uint8_t*)view->viewData() + offset, v));
+          return true;
+      }
+      case Scalar::Uint8Clamped: {
+          // Spec says:
+          //  - clamp the input value
+          //  - perform the operation
+          //  - clamp the result
+          //  - store the result
+          // This requires a CAS loop.
+          int32_t value = ClampIntForUint8Array(numberValue);
+          uint8_t* loc = (uint8_t*)view->viewData() + offset;
+          for (;;) {
+              uint8_t old = *loc;
+              uint8_t result = (uint8_t)ClampIntForUint8Array(T::perform(old, value));
+              uint8_t tmp = old;  // tmp is overwritten by CAS
+              CAS(uint8_t, loc, tmp, result);
+              if (tmp == old) {
+                  r.setInt32(old);
+                  break;
+              }
+          }
+          return true;
+      }
+      case Scalar::Int16: {
+          int16_t v = (int16_t)numberValue;
+          r.setInt32(T::operate((int16_t*)view->viewData() + offset, v));
+          return true;
+      }
+      case Scalar::Uint16: {
+          uint16_t v = (uint16_t)numberValue;
+          r.setInt32(T::operate((uint16_t*)view->viewData() + offset, v));
+          return true;
+      }
+      case Scalar::Int32: {
+          int32_t v = numberValue;
+          r.setInt32(T::operate((int32_t*)view->viewData() + offset, v));
+          return true;
+      }
+      case Scalar::Uint32: {
+          uint32_t v = (uint32_t)numberValue;
+          r.setNumber((double)T::operate((uint32_t*)view->viewData() + offset, v));
+          return true;
+      }
+      default:
+        return ReportBadArrayType(cx);
+    }
+}
+
+#define INTEGRAL_TYPES_FOR_EACH(NAME, TRANSFORM) \
+    static int8_t operate(int8_t* addr, int8_t v) { return NAME(TRANSFORM(int8_t, addr), v); } \
+    static uint8_t operate(uint8_t* addr, uint8_t v) { return NAME(TRANSFORM(uint8_t, addr), v); } \
+    static int16_t operate(int16_t* addr, int16_t v) { return NAME(TRANSFORM(int16_t, addr), v); } \
+    static uint16_t operate(uint16_t* addr, uint16_t v) { return NAME(TRANSFORM(uint16_t, addr), v); } \
+    static int32_t operate(int32_t* addr, int32_t v) { return NAME(TRANSFORM(int32_t, addr), v); } \
+    static uint32_t operate(uint32_t* addr, uint32_t v) { return NAME(TRANSFORM(uint32_t, addr), v); }
+
+#define CAST_ATOMIC(t, v) reinterpret_cast<std::atomic<t>*>(v)
+#define DO_NOTHING(t, v) v
+#define ZERO(t, v) 0
+
+class do_add
+{
+public:
+#if defined(CXX11_ATOMICS)
+    INTEGRAL_TYPES_FOR_EACH(std::atomic_fetch_add, CAST_ATOMIC)
+#elif defined(GNU_ATOMICS)
+    INTEGRAL_TYPES_FOR_EACH(__sync_fetch_and_add, DO_NOTHING)
+#else
+    INTEGRAL_TYPES_FOR_EACH(ZERO, DO_NOTHING)
+#endif
+    static int32_t perform(int32_t x, int32_t y) { return x + y; }
+};
+
+bool
+js::atomics_add(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    return atomics_binop_impl<do_add>(cx, args.get(0), args.get(1), args.get(2), args.rval());
+}
+
+class do_sub
+{
+public:
+#if defined(CXX11_ATOMICS)
+    INTEGRAL_TYPES_FOR_EACH(std::atomic_fetch_sub, CAST_ATOMIC)
+#elif defined(GNU_ATOMICS)
+    INTEGRAL_TYPES_FOR_EACH(__sync_fetch_and_sub, DO_NOTHING)
+#else
+    INTEGRAL_TYPES_FOR_EACH(ZERO, DO_NOTHING)
+#endif
+    static int32_t perform(int32_t x, int32_t y) { return x - y; }
+};
+
+bool
+js::atomics_sub(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    return atomics_binop_impl<do_sub>(cx, args.get(0), args.get(1), args.get(2), args.rval());
+}
+
+class do_and
+{
+public:
+#if defined(CXX11_ATOMICS)
+    INTEGRAL_TYPES_FOR_EACH(std::atomic_fetch_and, CAST_ATOMIC)
+#elif defined(GNU_ATOMICS)
+    INTEGRAL_TYPES_FOR_EACH(__sync_fetch_and_and, DO_NOTHING)
+#else
+    INTEGRAL_TYPES_FOR_EACH(ZERO, DO_NOTHING)
+#endif
+    static int32_t perform(int32_t x, int32_t y) { return x & y; }
+};
+
+bool
+js::atomics_and(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    return atomics_binop_impl<do_and>(cx, args.get(0), args.get(1), args.get(2), args.rval());
+}
+
+class do_or
+{
+public:
+#if defined(CXX11_ATOMICS)
+    INTEGRAL_TYPES_FOR_EACH(std::atomic_fetch_or, CAST_ATOMIC)
+#elif defined(GNU_ATOMICS)
+    INTEGRAL_TYPES_FOR_EACH(__sync_fetch_and_or, DO_NOTHING)
+#else
+    INTEGRAL_TYPES_FOR_EACH(ZERO, DO_NOTHING)
+#endif
+    static int32_t perform(int32_t x, int32_t y) { return x | y; }
+};
+
+bool
+js::atomics_or(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    return atomics_binop_impl<do_or>(cx, args.get(0), args.get(1), args.get(2), args.rval());
+}
+
+class do_xor
+{
+public:
+#if defined(CXX11_ATOMICS)
+    INTEGRAL_TYPES_FOR_EACH(std::atomic_fetch_xor, CAST_ATOMIC)
+#elif defined(GNU_ATOMICS)
+    INTEGRAL_TYPES_FOR_EACH(__sync_fetch_and_xor, DO_NOTHING)
+#else
+    INTEGRAL_TYPES_FOR_EACH(ZERO, DO_NOTHING)
+#endif
+    static int32_t perform(int32_t x, int32_t y) { return x ^ y; }
+};
+
+bool
+js::atomics_xor(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    return atomics_binop_impl<do_xor>(cx, args.get(0), args.get(1), args.get(2), args.rval());
+}
+
+#undef INTEGRAL_TYPES_FOR_EACH
+#undef CAST_ATOMIC
+#undef DO_NOTHING
+#undef ZERO
+
+namespace js {
+
+// Represents one waiting worker.
+//
+// The type is declared opaque in SharedArrayObject.h.  Instances of
+// js::FutexWaiter are stack-allocated and linked onto a list across a
+// call to JS::PerRuntimeFutexAPI::wait().
+//
+// The 'waiters' field of the SharedArrayRawBuffer points to the highest
+// priority waiter in the list, and lower priority nodes are linked through
+// the 'lower_pri' field.  The 'back' field goes the other direction.
+// The list is circular, so the 'lower_pri' field of the lowest priority
+// node points to the first node in the list.  The list has no dedicated
+// header node.
+
+class FutexWaiter
+{
+  public:
+    FutexWaiter(uint32_t offset, JS::PerRuntimeFutexAPI *fx)
+      : offset(offset),
+        fx(fx),
+        lower_pri(nullptr),
+        back(nullptr)
+    {
+    }
+
+    bool        waiting;                // Set to true when the worker is on the list and not woken
+    uint32_t    offset;                 // int32 element index within the SharedArrayBuffer
+    JS::PerRuntimeFutexAPI *fx;         // ...
+    FutexWaiter *lower_pri;             // Lower priority nodes in circular doubly-linked list of waiters
+    FutexWaiter *back;                  // Other direction
+};
+
+class AutoLockFutexAPI
+{
+    JS::PerRuntimeFutexAPI * const fx;
+  public:
+    AutoLockFutexAPI(JS::PerRuntimeFutexAPI *fx) : fx(fx) {
+        fx->lock();
+    }
+    ~AutoLockFutexAPI() {
+        fx->unlock();
+    }
+};
+
+} // namespace js
+
+bool
+js::atomics_futexWait(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    HandleValue objv = args.get(0);
+    HandleValue idxv = args.get(1);
+    HandleValue valv = args.get(2);
+    HandleValue timeoutv = args.get(3);
+    MutableHandleValue r = args.rval();
+
+    JS::PerRuntimeFutexAPI* fx = cx->runtime()->futexAPI_;
+    if (!fx)
+        return ReportNoFutexes(cx);
+
+    Rooted<SharedTypedArrayObject *> view(cx, nullptr);
+    if (!GetSharedTypedArray(cx, objv, &view))
+        return false;
+    if (view->type() != Scalar::Int32)
+        return ReportBadArrayType(cx);
+    uint32_t offset;
+    bool inRange;
+    if (!GetSharedTypedArrayIndex(cx, idxv, view, &offset, &inRange))
+        return false;
+    int32_t value;
+    if (!ToInt32(cx, valv, &value))
+        return false;
+    double timeout;
+    if (!ToInteger(cx, timeoutv, &timeout))
+        return false;
+    if (timeout < 0)
+        timeout = 0;
+
+    if (!inRange) {
+        atomics_fullMemoryBarrier();
+        r.setUndefined();
+        return true;
+    }
+
+    // This lock also protects the "waiters" field on SharedArrayRawBuffer,
+    // and it provides the necessary memory fence.
+    AutoLockFutexAPI lock(fx);
+
+    int32_t* addr = (int32_t*)view->viewData() + offset;
+    if (*addr != value) {
+        r.setInt32(AtomicsObject::FutexNotequal);
+        return true;
+    }
+
+    Rooted<SharedArrayBufferObject *> sab(cx, &view->buffer()->as<SharedArrayBufferObject>());
+    SharedArrayRawBuffer *sarb = sab->rawBufferObject();
+
+    FutexWaiter w(offset, fx);
+    w.waiting = true;
+    if (FutexWaiter *waiters = sarb->waiters()) {
+        w.lower_pri = waiters;
+        w.back = waiters->back;
+        waiters->back->lower_pri = &w;
+        waiters->back = &w;
+    } else {
+        w.lower_pri = w.back = &w;
+        sarb->setWaiters(&w);
+    }
+
+    bool retval = true;
+    switch (fx->wait(timeout)) {
+      case JS::PerRuntimeFutexAPI::Woken:
+        r.setInt32(AtomicsObject::FutexOK);
+        break;
+      case JS::PerRuntimeFutexAPI::Timedout:
+        r.setInt32(AtomicsObject::FutexTimedout);
+        break;
+      case JS::PerRuntimeFutexAPI::ErrorTooLong:
+        // This is a hack, but it's serviceable.
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ATOMICS_TOO_LONG);
+        retval = false;
+        break;
+      case JS::PerRuntimeFutexAPI::InterruptForTerminate:
+        // Throw an uncatchable exception.
+        JS_ClearPendingException(cx);
+        retval = false;
+        break;
+    }
+
+    if (w.lower_pri == &w) {
+        sarb->setWaiters(nullptr);
+    } else {
+        w.lower_pri->back = w.back;
+        w.back->lower_pri = w.lower_pri;
+        if (sarb->waiters() == &w)
+            sarb->setWaiters(w.lower_pri);
+    }
+    return retval;
+}
+
+bool
+js::atomics_futexWake(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    HandleValue objv = args.get(0);
+    HandleValue idxv = args.get(1);
+    HandleValue countv = args.get(2);
+    MutableHandleValue r = args.rval();
+
+    Rooted<SharedTypedArrayObject *> view(cx, nullptr);
+    if (!GetSharedTypedArray(cx, objv, &view))
+        return false;
+    if (view->type() != Scalar::Int32)
+        return ReportBadArrayType(cx);
+    uint32_t offset;
+    bool inRange;
+    if (!GetSharedTypedArrayIndex(cx, idxv, view, &offset, &inRange))
+        return false;
+    if (!inRange) {
+        atomics_fullMemoryBarrier();
+        r.setUndefined();
+        return true;
+    }
+    double count;
+    if (!ToInteger(cx, countv, &count))
+        return false;
+    if (count < 0)
+        count = 0;
+
+    JS::PerRuntimeFutexAPI* fx = cx->runtime()->futexAPI_;
+    if (!fx)
+        return ReportNoFutexes(cx);
+
+    AutoLockFutexAPI lock(fx);
+
+    Rooted<SharedArrayBufferObject *> sab(cx, &view->buffer()->as<SharedArrayBufferObject>());
+    SharedArrayRawBuffer *sarb = sab->rawBufferObject();
+    int32_t woken = 0;
+
+    FutexWaiter *waiters = sarb->waiters();
+    if (waiters && count > 0) {
+        FutexWaiter *iter = waiters;
+        do {
+            FutexWaiter *c = iter;
+            iter = iter->lower_pri;
+            if (c->offset != offset || !c->waiting)
+                continue;
+            c->fx->wake();
+            c->waiting = false;
+            ++woken;
+            --count;
+        } while (count > 0 && iter != waiters);
+    }
+
+    r.setInt32(woken);
+    return true;
+}
+
+bool
+js::atomics_futexWakeOrRequeue(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    HandleValue objv = args.get(0);
+    HandleValue idx1v = args.get(1);
+    HandleValue countv = args.get(2);
+    HandleValue valv = args.get(3);
+    HandleValue idx2v = args.get(4);
+    MutableHandleValue r = args.rval();
+
+    Rooted<SharedTypedArrayObject *> view(cx, nullptr);
+    if (!GetSharedTypedArray(cx, objv, &view))
+        return false;
+    if (view->type() != Scalar::Int32)
+        return ReportBadArrayType(cx);
+    uint32_t offset1;
+    bool inRange1;
+    if (!GetSharedTypedArrayIndex(cx, idx1v, view, &offset1, &inRange1))
+        return false;
+    double count;
+    if (!ToInteger(cx, countv, &count))
+        return false;
+    if (count < 0)
+        count = 0;
+    int32_t value;
+    if (!ToInt32(cx, valv, &value))
+        return false;
+    uint32_t offset2;
+    bool inRange2;
+    if (!GetSharedTypedArrayIndex(cx, idx2v, view, &offset2, &inRange2))
+        return false;
+    if (!(inRange1 && inRange2)) {
+        atomics_fullMemoryBarrier();
+        r.setUndefined();
+        return true;
+    }
+
+    JS::PerRuntimeFutexAPI* fx = cx->runtime()->futexAPI_;
+    if (!fx)
+        return ReportNoFutexes(cx);
+
+    AutoLockFutexAPI lock(fx);
+
+    int32_t* addr = (int32_t*)view->viewData() + offset1;
+    if (*addr != value) {
+        r.setInt32(AtomicsObject::FutexNotequal);
+        return true;
+    }
+
+    Rooted<SharedArrayBufferObject *> sab(cx, &view->buffer()->as<SharedArrayBufferObject>());
+    SharedArrayRawBuffer *sarb = sab->rawBufferObject();
+
+    // Walk the list of waiters looking for those waiting on offset1.
+    // Wake some and requeue the others.  There may already be other
+    // waiters on offset2, so those that are requeued must be moved to
+    // the back of the list.  Offset1 may equal offset2.  The list's
+    // first node may change, and the list may be emptied out by the
+    // operation.
+
+    FutexWaiter *waiters = sarb->waiters();
+    if (!waiters) {
+        r.setInt32(0);
+        return true;
+    }
+
+    int32_t woken = 0;
+    FutexWaiter whead((uint32_t)-1, nullptr); // Header node for waiters
+    FutexWaiter *first = waiters;
+    FutexWaiter *last = waiters->back;
+    whead.lower_pri = first;
+    whead.back = last;
+    first->back = &whead;
+    last->lower_pri = &whead;
+
+    FutexWaiter rhead((uint32_t)-1, nullptr); // Header node for requeued
+    rhead.lower_pri = rhead.back = &rhead;
+
+    FutexWaiter *iter = whead.lower_pri;
+    while (iter != &whead) {
+        FutexWaiter *c = iter;
+        iter = iter->lower_pri;
+        if (!c->waiting || c->offset != offset1)
+            continue;
+        if (count > 0) {
+            c->fx->wake();
+            c->waiting = false;
+            ++woken;
+            --count;
+        } else {
+            c->offset = offset2;
+
+            // Remove the node from the waiters list.
+            c->back->lower_pri = c->lower_pri;
+            c->lower_pri->back = c->back;
+
+            // Insert the node at the back of the requeuers list.
+            c->lower_pri = &rhead;
+            c->back = rhead.back;
+            rhead.back->lower_pri = c;
+            rhead.back = c;
+        }
+    }
+
+    // If there are any requeuers, append them to the waiters.
+    if (rhead.lower_pri != &rhead) {
+        whead.back->lower_pri = rhead.lower_pri;
+        rhead.lower_pri->back = whead.back;
+
+        whead.back = rhead.back;
+        rhead.back->lower_pri = &whead;
+    }
+
+    // Make the final list and install it.
+    waiters = nullptr;
+    if (whead.lower_pri != &whead) {
+        whead.back->lower_pri = whead.lower_pri;
+        whead.lower_pri->back = whead.back;
+        waiters = whead.lower_pri;
+    }
+    sarb->setWaiters(waiters);
+
+    r.setInt32(woken);
+    return true;
+}
+
+const JSFunctionSpec AtomicsMethods[] = {
+    JS_FN("compareExchange",    atomics_compareExchange,    4,0),
+    JS_FN("load",               atomics_load,               2,0),
+    JS_FN("store",              atomics_store,              3,0),
+    JS_FN("fence",              atomics_fence,              0,0),
+    JS_FN("add",                atomics_add,                3,0),
+    JS_FN("sub",                atomics_sub,                3,0),
+    JS_FN("and",                atomics_and,                3,0),
+    JS_FN("or",                 atomics_or,                 3,0),
+    JS_FN("xor",                atomics_xor,                3,0),
+    JS_FN("futexWait",          atomics_futexWait,          4,0),
+    JS_FN("futexWake",          atomics_futexWake,          3,0),
+    JS_FN("futexWakeOrRequeue", atomics_futexWakeOrRequeue, 5,0),
+    JS_FS_END
+};
+
+static const JSConstDoubleSpec AtomicsConstants[] = {
+    {"OK",       AtomicsObject::FutexOK},
+    {"TIMEDOUT", AtomicsObject::FutexTimedout},
+    {"NOTEQUAL", AtomicsObject::FutexNotequal},
+    {0,          0}
+};
+
+JSObject *
+AtomicsObject::initClass(JSContext *cx, Handle<GlobalObject *> global)
+{
+    // Create Atomics Object.
+    RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
+    if (!objProto)
+        return nullptr;
+    RootedObject Atomics(cx, NewObjectWithGivenProto(cx, &AtomicsObject::class_, objProto,
+                                                     global, SingletonObject));
+    if (!Atomics)
+        return nullptr;
+
+    if (!JS_DefineFunctions(cx, Atomics, AtomicsMethods))
+        return nullptr;
+    if (!JS_DefineConstDoubles(cx, Atomics, AtomicsConstants))
+        return nullptr;
+
+    RootedValue AtomicsValue(cx, ObjectValue(*Atomics));
+
+    // Everything is set up, install Atomics on the global object.
+    if (!JSObject::defineProperty(cx, global, cx->names().Atomics, AtomicsValue, nullptr, nullptr, 0))
+        return nullptr;
+
+    global->setConstructor(JSProto_Atomics, AtomicsValue);
+    return Atomics;
+}
+
+JSObject *
+js_InitAtomicsClass(JSContext *cx, HandleObject obj)
+{
+    MOZ_ASSERT(obj->is<GlobalObject>());
+    Rooted<GlobalObject *> global(cx, &obj->as<GlobalObject>());
+    return AtomicsObject::initClass(cx, global);
+}
+
+#undef CXX11_ATOMICS
+#undef GNU_ATOMICS
new file mode 100644
--- /dev/null
+++ b/js/src/builtin/AtomicsObject.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 builtin_AtomicsObject_h
+#define builtin_AtomicsObject_h
+
+#include "jsobj.h"
+
+namespace js {
+
+class AtomicsObject : public JSObject
+{
+  public:
+    static const Class class_;
+    static JSObject* initClass(JSContext *cx, Handle<GlobalObject *> global);
+    static bool toString(JSContext *cx, unsigned int argc, jsval *vp);
+
+    static const int FutexOK = 0;
+
+    // The error values must be negative because APIs such as futexWaitOrRequeue
+    // return a value that is either the number of tasks woken or an error code.
+    static const int FutexNotequal = -1;
+    static const int FutexTimedout = -2;
+
+    // Internal signals; negative for the same reason.
+    static const int FutexInterrupted = -1000;
+};
+
+void atomics_fullMemoryBarrier();
+
+bool atomics_compareExchange(JSContext *cx, unsigned argc, Value *vp);
+bool atomics_load(JSContext *cx, unsigned argc, Value *vp);
+bool atomics_store(JSContext *cx, unsigned argc, Value *vp);
+bool atomics_fence(JSContext *cx, unsigned argc, Value *vp);
+bool atomics_add(JSContext *cx, unsigned argc, Value *vp);
+bool atomics_sub(JSContext *cx, unsigned argc, Value *vp);
+bool atomics_and(JSContext *cx, unsigned argc, Value *vp);
+bool atomics_or(JSContext *cx, unsigned argc, Value *vp);
+bool atomics_xor(JSContext *cx, unsigned argc, Value *vp);
+bool atomics_futexWait(JSContext *cx, unsigned argc, Value *vp);
+bool atomics_futexWake(JSContext *cx, unsigned argc, Value *vp);
+bool atomics_futexWakeOrRequeue(JSContext *cx, unsigned argc, Value *vp);
+
+}  /* namespace js */
+
+JSObject *
+js_InitAtomicsClass(JSContext *cx, js::HandleObject obj);
+
+#endif /* builtin_AtomicsObject_h */
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -2,32 +2,33 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 "builtin/Object.h"
 
 #include "mozilla/ArrayUtils.h"
+#include "mozilla/UniquePtr.h"
 
 #include "jscntxt.h"
 
 #include "frontend/BytecodeCompiler.h"
 #include "vm/StringBuffer.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 using namespace js::types;
 
 using js::frontend::IsIdentifier;
 using mozilla::ArrayLength;
-
+using mozilla::UniquePtr;
 
 bool
 js::obj_construct(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     RootedObject obj(cx, nullptr);
     if (args.length() > 0 && !args[0].isNullOrUndefined()) {
@@ -604,17 +605,22 @@ obj_setPrototypeOf(JSContext *cx, unsign
     RootedObject newProto(cx, args[1].toObjectOrNull());
 
     bool success;
     if (!JSObject::setProto(cx, obj, newProto, &success))
         return false;
 
     /* Step 7. */
     if (!success) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_OBJECT_NOT_EXTENSIBLE, "object");
+        UniquePtr<char[], JS::FreePolicy> bytes(DecompileValueGenerator(cx, JSDVG_SEARCH_STACK,
+                                                                        args[0], NullPtr()));
+        if (!bytes)
+            return false;
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SETPROTOTYPEOF_FAIL,
+                             bytes.get());
         return false;
     }
 
     /* Step 8. */
     args.rval().set(args[0]);
     return true;
 }
 
@@ -1001,31 +1007,42 @@ obj_isExtensible(JSContext *cx, unsigned
         RootedObject obj(cx, &args.get(0).toObject());
         if (!JSObject::isExtensible(cx, obj, &extensible))
             return false;
     }
     args.rval().setBoolean(extensible);
     return true;
 }
 
-// ES6 draft rev27 (2014/08/24) 19.1.2.15 Object.preventExtensions(O)
+// ES6 20141014 draft 19.1.2.15 Object.preventExtensions(O)
 static bool
 obj_preventExtensions(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().set(args.get(0));
 
     // Step 1.
     if (!args.get(0).isObject())
         return true;
 
-    // Steps 2-5.
+    // Steps 2-3.
     RootedObject obj(cx, &args.get(0).toObject());
 
-    return JSObject::preventExtensions(cx, obj);
+    bool status;
+    if (!JSObject::preventExtensions(cx, obj, &status))
+        return false;
+
+    // Step 4.
+    if (!status) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CHANGE_EXTENSIBILITY);
+        return false;
+    }
+
+    // Step 5.
+    return true;
 }
 
 // ES6 draft rev27 (2014/08/24) 19.1.2.5 Object.freeze(O)
 static bool
 obj_freeze(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().set(args.get(0));
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -2073,16 +2073,35 @@ ByteSize(JSContext *cx, unsigned argc, V
     JS::ubi::Node node = args.get(0);
     if (node)
         args.rval().set(NumberValue(node.size(mallocSizeOf)));
     else
         args.rval().setUndefined();
     return true;
 }
 
+static bool
+SetImmutablePrototype(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (!args.get(0).isObject()) {
+        JS_ReportError(cx, "setImmutablePrototype: object expected");
+        return false;
+    }
+
+    RootedObject obj(cx, &args[0].toObject());
+
+    bool succeeded;
+    if (!JSObject::setImmutablePrototype(cx, obj, &succeeded))
+        return false;
+
+    args.rval().setBoolean(succeeded);
+    return true;
+}
+
 static const JSFunctionSpecWithHelp TestingFunctions[] = {
     JS_FN_HELP("gc", ::GC, 0, 0,
 "gc([obj] | 'compartment' [, 'shrinking'])",
 "  Run the garbage collector. When obj is given, GC only its compartment.\n"
 "  If 'compartment' is given, GC any compartments that were scheduled for\n"
 "  GC via schedulegc.\n"
 "  If 'shrinking' is passes as the optional second argument, perform a\n"
 "  shrinking GC rather than a normal GC."),
@@ -2395,16 +2414,24 @@ gc::ZealModeHelpText),
 "    options.locals - show local variables in each frame\n"
 "    options.thisprops - show the properties of the 'this' object of each frame\n"),
 
     JS_FN_HELP("byteSize", ByteSize, 1, 0,
 "byteSize(value)",
 "  Return the size in bytes occupied by |value|, or |undefined| if value\n"
 "  is not allocated in memory.\n"),
 
+    JS_FN_HELP("setImmutablePrototype", SetImmutablePrototype, 1, 0,
+"setImmutablePrototype(obj)",
+"  Try to make obj's [[Prototype]] immutable, such that subsequent attempts to\n"
+"  change it will fail.  Return true if obj's [[Prototype]] was successfully made\n"
+"  immutable (or if it already was immutable), false otherwise.  Throws in case\n"
+"  of internal error, or if the operation doesn't even make sense (for example,\n"
+"  because the object is a revoked proxy)."),
+
     JS_FS_HELP_END
 };
 
 static const JSPropertySpec TestingProperties[] = {
     JS_PSG("timesAccessed", TimesAccessed, 0),
     JS_PS_END
 };
 
--- a/js/src/gc/StoreBuffer.cpp
+++ b/js/src/gc/StoreBuffer.cpp
@@ -21,16 +21,20 @@ using mozilla::ReentrancyGuard;
 
 /*** Edges ***/
 
 void
 StoreBuffer::SlotsEdge::mark(JSTracer *trc)
 {
     NativeObject *obj = object();
 
+    // Beware JSObject::swap exchanging a native object for a non-native one.
+    if (!obj->isNative())
+        return;
+
     if (IsInsideNursery(obj))
         return;
 
     if (kind() == ElementKind) {
         int32_t initLen = obj->getDenseInitializedLength();
         int32_t clampedStart = Min(start_, initLen);
         int32_t clampedEnd = Min(start_ + count_, initLen);
         gc::MarkArraySlots(trc, clampedEnd - clampedStart,
--- a/js/src/jit-test/tests/asm.js/testMathLib.js
+++ b/js/src/jit-test/tests/asm.js/testMathLib.js
@@ -59,68 +59,73 @@ testUnary(asmLink(asmCompile('glob', USE
 
 var f = asmLink(asmCompile('glob', USE_ASM + 'var abs=glob.Math.abs; function f(i) { i=i|0; return abs(i|0)|0 } return f'), this);
 for (n of [-Math.pow(2,31)-1, -Math.pow(2,31), -Math.pow(2,31)+1, -1, 0, 1, Math.pow(2,31)-2, Math.pow(2,31)-1, Math.pow(2,31)])
     assertEq(f(n), Math.abs(n|0)|0);
 
 var f = asmLink(asmCompile('glob', USE_ASM + 'var clz32=glob.Math.clz32; function f(i) { i=i|0; return clz32(i)|0 } return f'), this);
 for (n of [0, 1, 2, 15, 16, Math.pow(2,31)-1, Math.pow(2,31), Math.pow(2,31)+1, Math.pow(2,32)-1, Math.pow(2,32), Math.pow(2,32)+1])
     assertEq(f(n), Math.clz32(n|0));
+assertEq(asmLink(asmCompile('glob', USE_ASM + 'var clz32=glob.Math.clz32; function f(i, j) { i=i|0;j=j|0; return (clz32(i) < (j|0))|0 } return f'), this)(0x1, 30), 0);
+assertEq(asmLink(asmCompile('glob', USE_ASM + 'var clz32=glob.Math.clz32; function f(i, j) { i=i|0;j=j|0; return (clz32(i) < (j>>>0))|0 } return f'), this)(0x1, 30), 0);
 
 var doubleNumbers = [NaN, Infinity, -Infinity, -10000, -3.4, -0, 0, 3.4, 10000];
 var floatNumbers = [];
 for (var x of doubleNumbers) floatNumbers.push(Math.fround(x));
-var intNumbers = [-10000, -3, -1, 0, 3, 10000];
+var intNumbers = [-Math.pow(2,31), -10000, -3, -1, 0, 3, 10000, Math.pow(2,31), Math.pow(2,31)+1];
 
 function testBinary(f, g, numbers) {
     for (n of numbers)
         for (o of numbers)
             assertEq(f(n,o), g(n,o));
 }
 
 assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var po=glob.Math.pow; function f(d,e) { d=+d;e=+e; return +po(d,e) } return f'), {Math:{pow:Math.sin}});
 assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var po=glob.Math.pow; function f(d,e) { d=+d;e=+e; return +po(d,e) } return f'), {Math:{pow:null}});
 testBinary(asmLink(asmCompile('glob', USE_ASM + 'var po=glob.Math.pow; function f(d,e) { d=+d;e=+e; return +po(d,e) } return f'), {Math:{pow:Math.pow}}), Math.pow, doubleNumbers);
 
 assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan2; function f(d,e) { d=+d;e=+e; return +at(d,e) } return f'), {Math:{atan2:Math.sin}});
 assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan2; function f(d,e) { d=+d;e=+e; return +at(d,e) } return f'), {Math:{atan2:null}});
 testBinary(asmLink(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan2; function f(d,e) { d=+d;e=+e; return +at(d,e) } return f'), {Math:{atan2:Math.atan2}}), Math.atan2, doubleNumbers);
 
+function coercedMin(...args) { for (var i = 0; i < args.length; i++) args[i] = args[i]|0; return Math.min(...args) }
+function coercedMax(...args) { for (var i = 0; i < args.length; i++) args[i] = args[i]|0; return Math.max(...args) }
 assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(d) { d=+d; return +min(d) } return f');
 assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + 'var i32=new glob.Int32Array(heap); var min=glob.Math.min; function f() { return min(i32[0], 5)|0 } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(x) { x=x|0; return min(3 + x, 5)|0 } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(x) { x=x|0; return min(5, 3 + x)|0 } return f');
+assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(x) { x=x|0; return min(x, 1)|0 } return f');
 
 assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e) { d=+d;e=+e; return +min(d,e) } return f'), {Math:{min:Math.sin}});
 assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e) { d=+d;e=+e; return +min(d,e) } return f'), {Math:{min:null}});
 testBinary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e) { d=+d;e=+e; return +min(d,e) } return f'), {Math:{min:Math.min}}), Math.min, doubleNumbers);
 testBinary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; var f32=glob.Math.fround; function f(d,e) { d=f32(d);e=f32(e); return f32(min(d,e)) } return f'), this), Math.min, floatNumbers);
-testBinary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e) { d=d|0;e=e|0; return min(d,e)|0} return f'), {Math:{min:Math.min}}), Math.min, intNumbers);
+testBinary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e) { d=d|0;e=e|0; return min(d|0,e|0)|0} return f'), {Math:{min:Math.min}}), coercedMin, intNumbers);
 
 assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e) { d=+d;e=+e; return +max(d,e) } return f'), {Math:{max:Math.sin}});
 assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e) { d=+d;e=+e; return +max(d,e) } return f'), {Math:{max:null}});
 testBinary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e) { d=+d;e=+e; return +max(d,e) } return f'), {Math:{max:Math.max}}), Math.max, doubleNumbers);
 testBinary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; var f32=glob.Math.fround; function f(d,e) { d=f32(d);e=f32(e); return f32(max(d,e)) } return f'), this), Math.max, floatNumbers);
-testBinary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e) { d=d|0;e=e|0; return max(d,e)|0} return f'), {Math:{max:Math.max}}), Math.max, intNumbers);
+testBinary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e) { d=d|0;e=e|0; return max(d|0,e|0)|0} return f'), {Math:{max:Math.max}}), coercedMax, intNumbers);
 
 function testTernary(f, g, numbers) {
     for (n of numbers)
         for (o of numbers)
             for (p of numbers)
                 assertEq(f(n,o,p), g(n,o,p));
 }
 
 assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e,g) { d=+d;e=+e;g=g|0; return +min(d,e,g) } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e,g) { d=d|0;e=e|0;g=+g; return max(d,e,g)|0 } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e,g) { d=+d;e=+e;g=+g; return min(d,e,g)|0 } return f');
-testTernary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e,g) { d=d|0;e=e|0;g=g|0; return +max(d,e,g) } return f'), {Math:{max:Math.max}}), Math.max, intNumbers);
-testTernary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e,g) { d=d|0;e=e|0;g=g|0; return max(d,e,g)|0 } return f'), {Math:{max:Math.max}}), Math.max, intNumbers);
+testTernary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e,g) { d=d|0;e=e|0;g=g|0; return +max(d|0,e|0,g|0) } return f'), {Math:{max:Math.max}}), coercedMax, intNumbers);
+testTernary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e,g) { d=d|0;e=e|0;g=g|0; return max(d|0,e|0,g|0)|0 } return f'), {Math:{max:Math.max}}), coercedMax, intNumbers);
 testTernary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e,g) { d=+d;e=+e;g=+g; return +max(d,e,g) } return f'), {Math:{max:Math.max}}), Math.max, doubleNumbers);
 testTernary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; var _=glob.Math.fround; function f(d,e,g) { d=_(d);e=_(e);g=_(g); return _(max(d,e,g)) } return f'), this), Math.max, floatNumbers);
-testTernary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e,g) { d=d|0;e=e|0;g=g|0; return min(d,e,g)|0 } return f'), {Math:{min:Math.min}}), Math.min, intNumbers);
+testTernary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e,g) { d=d|0;e=e|0;g=g|0; return min(d|0,e|0,g|0)|0 } return f'), {Math:{min:Math.min}}), coercedMin, intNumbers);
 testTernary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e,g) { d=+d;e=+e;g=+g; return +min(d,e,g) } return f'), {Math:{min:Math.min}}), Math.min, doubleNumbers);
 testTernary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; var _=glob.Math.fround; function f(d,e,g) { d=_(d);e=_(e);g=_(g); return _(min(d,e,g)) } return f'), this), Math.min, floatNumbers);
 
 // Implicit return coercions of math functions
 assertEq(asmLink(asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i) { i=i|0; i = im(i,i); return i|0 } return f'), this)(3), 9);
 assertAsmTypeFail('glob', USE_ASM + 'var im=glob.Math.imul; function f(d) { d=+d; d = im(d, d) } return f');
 assertAsmTypeFail('glob', USE_ASM + FROUND + 'var im=glob.Math.imul; function f(d) { d=fround(d); d = im(d, d) } return f');
 
@@ -134,17 +139,17 @@ assertAsmTypeFail('glob', USE_ASM + 'var
 assertEq(asmLink(asmCompile('glob', USE_ASM + 'var sqrt=glob.Math.sqrt; function f(d) { d=+d; d = sqrt(d); return +d } return f'), this)(256), 16);
 assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var sqrt=glob.Math.sqrt; function f(d) { d=fround(d); d = fround(sqrt(d)); return +d } return f'), this)(13.37), Math.fround(Math.sqrt(Math.fround(13.37))));
 assertAsmTypeFail('glob', USE_ASM + FROUND + 'var sqrt=glob.Math.sqrt; function f(d) { d=fround(d); d = sqrt(d); return fround(d) } return f');
 assertAsmTypeFail('glob', USE_ASM + FROUND + 'var sqrt=glob.Math.sqrt; function f(d) { d=fround(d); d = sqrt(d); return d } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var sqrt=glob.Math.sqrt; function f(n) { n=n|0; var d=0.; d = sqrt(n|0) } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var sqrt=glob.Math.sqrt; function f(n) { n=n|0; var d=3.; n = sqrt(d)|0 } return f');
 
 assertEq(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d) { d=+d; d = min(d, 13.); return +d } return f'), this)(12), 12);
-assertEq(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d) { d=d|0; d = min(d, 11); return d|0 } return f'), this)(12), 11);
+assertEq(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d) { d=d|0; d = min(d|0, 11); return d|0 } return f'), this)(12), 11);
 assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var min=glob.Math.min; function f(d) { d=fround(d); d = min(d, fround(13.37)); return fround(d) } return f'), this)(14), Math.fround(13.37));
 
 assertEq(asmLink(asmCompile('glob', USE_ASM + 'var sin=glob.Math.sin; function f(d) { d=+d; d = sin(d); return +d } return f'), this)(Math.PI), Math.sin(Math.PI));
 assertAsmTypeFail('glob', USE_ASM + FROUND + 'var sin=glob.Math.sin; function f(d) { d=fround(d); d = sin(d) } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var sin=glob.Math.sin; function f(d) { d=d|0; d = sin(d) } return f');
 
 assertEq(asmLink(asmCompile('glob', USE_ASM + 'var pow=glob.Math.pow; function f(d) { d=+d; d = pow(d,d); return +d } return f'), this)(3), 27);
 assertAsmTypeFail('glob', USE_ASM + FROUND + 'var pow=glob.Math.pow; function f(d) { d=fround(d); d = pow(d, d) } return f');
@@ -153,20 +158,20 @@ assertAsmTypeFail('glob', USE_ASM + 'var
 assertAsmTypeFail('glob', USE_ASM + 'var sin=glob.Math.sin; function f(d) { d=+d; var i=0; i = sin(d)|0; } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var pow=glob.Math.pow; function f(d) { d=+d; var i=0; i = pow(d,d)|0; } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var atan2=glob.Math.atan2; function f(d) { d=+d; var i=0; i = atan2(d,d)|0; } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var sqrt=glob.Math.sqrt; function f(d) { d=+d; sqrt(d)|0; } return f');
 assertAsmTypeFail('glob', USE_ASM + 'var abs=glob.Math.abs; function f(d) { d=+d; abs(d)|0; } return f');
 
 assertEq(asmLink(asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i) { i=i|0; var d=0.0; d = +im(i,i); return +d } return f'), this)(42), Math.imul(42, 42));
 assertEq(asmLink(asmCompile('glob', USE_ASM + 'var abs=glob.Math.abs; function f(i) { i=i|0; var d=0.0; d = +abs(i|0); return +d } return f'), this)(-42), 42);
-assertEq(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(i) { i=i|0; var d=0.0; d = +min(i, 0); return +d } return f'), this)(-42), -42);
-assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var min=glob.Math.min; function f(i) { i=i|0; var d=fround(0); d = fround(min(i, 0)); return +d } return f'), this)(-42), -42);
-assertEq(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(i) { i=i|0; var d=0.0; d = +max(i, 0); return +d } return f'), this)(-42), 0);
-assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var max=glob.Math.max; function f(i) { i=i|0; var d=fround(0); d = fround(max(i, 0)); return +d } return f'), this)(-42), 0);
+assertEq(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(i) { i=i|0; var d=0.0; d = +min(i|0, 0); return +d } return f'), this)(-42), -42);
+assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var min=glob.Math.min; function f(i) { i=i|0; var d=fround(0); d = fround(min(i|0, 0)); return +d } return f'), this)(-42), -42);
+assertEq(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(i) { i=i|0; var d=0.0; d = +max(i|0, 0); return +d } return f'), this)(-42), 0);
+assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var max=glob.Math.max; function f(i) { i=i|0; var d=fround(0); d = fround(max(i|0, 0)); return +d } return f'), this)(-42), 0);
 assertEq(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d) { d=+d; var i=0; i = ~~min(d, 0.)|0; return i|0 } return f'), this)(-42), -42);
 assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var min=glob.Math.min; function f(d) { d=fround(d); var i=0; i = ~~min(d, fround(0))|0; return i|0 } return f'), this)(-42), -42);
 assertEq(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d) { d=+d; var i=0; i = ~~max(d, 0.)|0; return i|0 } return f'), this)(-42), 0);
 assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var max=glob.Math.max; function f(d) { d=fround(d); var i=0; i = ~~max(d, fround(0))|0; return i|0 } return f'), this)(-42), 0);
 
 assertEq(asmLink(asmCompile('glob', USE_ASM + 'var abs=glob.Math.abs; function f(i) { i=i|0; var d=0.0; return +d; +abs(i|0); return 3.0;} return f'), this)(-42), 0);
 
 assertAsmTypeFail('glob', USE_ASM + 'var tau=glob.Math.TAU; function f() {} return f');
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/basic-tests.js
@@ -0,0 +1,416 @@
+// Basic functional tests for the Atomics primitives.
+//
+// These do not test atomicity, just that calling and coercions and
+// indexing and exception behavior all work right.
+//
+// These do not test the futex operations.
+
+var DEBUG = false;		// Set to true for useful printouts
+
+function dprint(...xs) {
+    if (!DEBUG)
+	return;
+    var s = "";
+    for ( var x in xs )
+	s += String(xs[x]);
+    print(s);
+}
+
+function testMethod(a, ...indices) {
+    dprint("Method: " + a.constructor.name);
+    var poison;
+    switch (a.BYTES_PER_ELEMENT) {
+    case 1: poison = 0x5A; break;
+    case 2: poison = 0x5A5A; break;
+    case 4: poison = 0x5A5A5A5A; break;
+    }
+    for ( var i=0 ; i < indices.length ; i++ ) {
+	var x = indices[i];
+	if (x > 0)
+	    a[x-1] = poison;
+	if (x < a.length-1)
+	    a[x+1] = poison;
+
+	// val = 0
+	assertEq(Atomics.compareExchange(a, x, 0, 37), 0);
+	// val = 37
+	assertEq(Atomics.compareExchange(a, x, 37, 5), 37);
+	// val = 5
+	assertEq(Atomics.compareExchange(a, x, 7, 8), 5); // ie should fail
+	// val = 5
+	assertEq(Atomics.compareExchange(a, x, 5, 9), 5);
+	// val = 9
+	assertEq(Atomics.compareExchange(a, x, 5, 0), 9); // should also fail
+
+	// val = 9
+	assertEq(Atomics.load(a, x), 9);
+	// val = 9
+	assertEq(Atomics.store(a, x, 14), 14); // What about coercion?
+	// val = 14
+	assertEq(Atomics.load(a, x), 14);
+	// val = 14
+	Atomics.store(a, x, 0);
+	// val = 0
+
+	Atomics.fence();
+
+	// val = 0
+	assertEq(Atomics.add(a, x, 3), 0);
+	// val = 3
+	assertEq(Atomics.sub(a, x, 2), 3);
+	// val = 1
+	assertEq(Atomics.or(a, x, 6), 1);
+	// val = 7
+	assertEq(Atomics.and(a, x, 14), 7);
+	// val = 6
+	assertEq(Atomics.xor(a, x, 5), 6);
+	// val = 3
+	assertEq(Atomics.load(a, x), 3);
+	// val = 3
+	Atomics.store(a, x, 0);
+	// val = 0
+
+	// Check adjacent elements were not affected
+	if (x > 0) {
+	    assertEq(a[x-1], poison);
+	    a[x-1] = 0;
+	}
+	if (x < a.length-1) {
+	    assertEq(a[x+1], poison);
+	    a[x+1] = 0;
+	}
+    }
+}
+
+function testFunction(a, ...indices) {
+    dprint("Function: " + a.constructor.name);
+    var poison;
+    switch (a.BYTES_PER_ELEMENT) {
+    case 1: poison = 0x5A; break;
+    case 2: poison = 0x5A5A; break;
+    case 4: poison = 0x5A5A5A5A; break;
+    }
+    for ( var i=0 ; i < indices.length ; i++ ) {
+	var x = indices[i];
+	if (x > 0)
+	    a[x-1] = poison;
+	if (x < a.length-1)
+	    a[x+1] = poison;
+
+	// val = 0
+	assertEq(gAtomics_compareExchange(a, x, 0, 37), 0);
+	// val = 37
+	assertEq(gAtomics_compareExchange(a, x, 37, 5), 37);
+	// val = 5
+	assertEq(gAtomics_compareExchange(a, x, 7, 8), 5); // ie should fail
+	// val = 5
+	assertEq(gAtomics_compareExchange(a, x, 5, 9), 5);
+	// val = 9
+	assertEq(gAtomics_compareExchange(a, x, 5, 0), 9); // should also fail
+
+	// val = 9
+	assertEq(gAtomics_load(a, x), 9);
+	// val = 9
+	assertEq(gAtomics_store(a, x, 14), 14); // What about coercion?
+	// val = 14
+	assertEq(gAtomics_load(a, x), 14);
+	// val = 14
+	gAtomics_store(a, x, 0);
+	// val = 0
+
+	gAtomics_fence();
+
+	// val = 0
+	assertEq(gAtomics_add(a, x, 3), 0);
+	// val = 3
+	assertEq(gAtomics_sub(a, x, 2), 3);
+	// val = 1
+	assertEq(gAtomics_or(a, x, 6), 1);
+	// val = 7
+	assertEq(gAtomics_and(a, x, 14), 7);
+	// val = 6
+	assertEq(gAtomics_xor(a, x, 5), 6);
+	// val = 3
+	assertEq(gAtomics_load(a, x), 3);
+	// val = 3
+	gAtomics_store(a, x, 0);
+	// val = 0
+
+	// Check adjacent elements were not affected
+	if (x > 0) {
+	    assertEq(a[x-1], poison);
+	    a[x-1] = 0;
+	}
+	if (x < a.length-1) {
+	    assertEq(a[x+1], poison);
+	    a[x+1] = 0;
+	}
+    }
+}
+
+function testTypeCAS(a) {
+    dprint("Type: " + a.constructor.name);
+
+    var thrown = false;
+    try {
+	Atomics.compareExchange([0], 0, 0, 1);
+    }
+    catch (e) {
+	thrown = true;
+	assertEq(e instanceof TypeError, true);
+    }
+    assertEq(thrown, true);
+
+    // All these variants should be OK
+    Atomics.compareExchange(a, 0, 0.7, 1.8);
+    Atomics.compareExchange(a, 0, "0", 1);
+    Atomics.compareExchange(a, 0, 0, "1");
+    Atomics.compareExchange(a, 0, 0);
+}
+
+function testTypeBinop(a, op) {
+    dprint("Type: " + a.constructor.name);
+
+    var thrown = false;
+    try {
+	op([0], 0, 1);
+    }
+    catch (e) {
+	thrown = true;
+	assertEq(e instanceof TypeError, true);
+    }
+    assertEq(thrown, true);
+
+    // These are all OK
+    op(a, 0, 0.7);
+    op(a, 0, "0");
+    op(a, 0);
+}
+
+function testRangeCAS(a) {
+    dprint("Range: " + a.constructor.name);
+
+    assertEq(Atomics.compareExchange(a, -1, 0, 1), undefined); // out of range => undefined, no effect
+    assertEq(a[0], 0);
+    a[0] = 0;
+
+    assertEq(Atomics.compareExchange(a, "hi", 0, 1), undefined); // invalid => undefined, no effect
+    assertEq(a[0], 0);
+    a[0] = 0;
+
+    assertEq(Atomics.compareExchange(a, a.length + 5, 0, 1), undefined); // out of range => undefined, no effect
+    assertEq(a[0], 0);
+}
+
+// Ad-hoc tests for extreme and out-of-range values 
+// None of these should throw
+
+function testInt8Extremes(a) {
+    dprint("Int8 extremes");
+
+    a[10] = 0;
+    a[11] = 0;
+
+    Atomics.store(a, 10, 255);
+    assertEq(a[10], -1);
+    assertEq(Atomics.load(a, 10), -1);
+
+    Atomics.add(a, 10, 255); // should coerce to -1
+    assertEq(a[10], -2);
+    assertEq(Atomics.load(a, 10), -2);
+
+    Atomics.add(a, 10, -1);
+    assertEq(a[10], -3);
+    assertEq(Atomics.load(a, 10), -3);
+
+    Atomics.sub(a, 10, 255);	// should coerce to -1
+    assertEq(a[10], -2);
+    assertEq(Atomics.load(a, 10), -2);
+
+    Atomics.sub(a, 10, 256);	// should coerce to 0
+    assertEq(a[10], -2);
+    assertEq(Atomics.load(a, 10), -2);
+
+    Atomics.and(a, 10, -1);	// Preserve all
+    assertEq(a[10], -2);
+    assertEq(Atomics.load(a, 10), -2);
+
+    Atomics.and(a, 10, 256);	// Preserve none
+    assertEq(a[10], 0);
+    assertEq(Atomics.load(a, 10), 0);
+    
+    assertEq(a[11], 0);
+}
+
+function testUint8Extremes(a) {
+    dprint("Uint8 extremes");
+
+    a[10] = 0;
+    a[11] = 0;
+
+    Atomics.store(a, 10, 255);
+    assertEq(a[10], 255);
+    assertEq(Atomics.load(a, 10), 255);
+
+    Atomics.add(a, 10, 255);
+    assertEq(a[10], 254);
+    assertEq(Atomics.load(a, 10), 254);
+
+    Atomics.add(a, 10, -1);
+    assertEq(a[10], 253);
+    assertEq(Atomics.load(a, 10), 253);
+
+    Atomics.sub(a, 10, 255);
+    assertEq(a[10], 254);
+    assertEq(Atomics.load(a, 10), 254);
+
+    Atomics.and(a, 10, -1);	// Preserve all
+    assertEq(a[10], 254);
+    assertEq(Atomics.load(a, 10), 254);
+
+    Atomics.and(a, 10, 256);	// Preserve none
+    assertEq(a[10], 0);
+    assertEq(Atomics.load(a, 10), 0);
+    
+    assertEq(a[11], 0);
+}
+
+function testInt16Extremes(a) {
+    dprint("Int16 extremes");
+
+    a[10] = 0;
+    a[11] = 0;
+
+    Atomics.store(a, 10, 65535);
+    assertEq(a[10], -1);
+    assertEq(Atomics.load(a, 10), -1);
+
+    Atomics.add(a, 10, 65535); // should coerce to -1
+    assertEq(a[10], -2);
+    assertEq(Atomics.load(a, 10), -2);
+
+    Atomics.add(a, 10, -1);
+    assertEq(a[10], -3);
+    assertEq(Atomics.load(a, 10), -3);
+
+    Atomics.sub(a, 10, 65535);	// should coerce to -1
+    assertEq(a[10], -2);
+    assertEq(Atomics.load(a, 10), -2);
+
+    Atomics.sub(a, 10, 65536);	// should coerce to 0
+    assertEq(a[10], -2);
+    assertEq(Atomics.load(a, 10), -2);
+
+    Atomics.and(a, 10, -1);	// Preserve all
+    assertEq(a[10], -2);
+    assertEq(Atomics.load(a, 10), -2);
+
+    Atomics.and(a, 10, 65536);	// Preserve none
+    assertEq(a[10], 0);
+    assertEq(Atomics.load(a, 10), 0);
+
+    assertEq(a[11], 0);
+}
+
+function testUint32(a) {
+    var k = 0;
+    for ( var i=0 ; i < 20 ; i++ ) {
+	a[i] = i+5;
+	k += a[i];
+    }
+
+    var sum = 0;
+    for ( var i=0 ; i < 20 ; i++ )
+	sum += Atomics.add(a, i, 1);
+
+    assertEq(sum, k);
+}
+
+function isLittleEndian() {
+    var xxx = new ArrayBuffer(2);
+    var xxa = new Int16Array(xxx);
+    var xxb = new Int8Array(xxx);
+    xxa[0] = 37;
+    var is_little = xxb[0] == 37;
+    return is_little;
+}
+
+function runTests() {
+    var is_little = isLittleEndian();
+
+    // Currently the SharedArrayBuffer needs to be a multiple of 4K bytes in size.
+    var sab = new SharedArrayBuffer(4096);
+
+    // Test that two arrays created on the same storage alias
+    var t1 = new SharedInt8Array(sab);
+    var t2 = new SharedUint16Array(sab);
+
+    assertEq(t1[0], 0);
+    assertEq(t2[0], 0);
+    t1[0] = 37;
+    if (is_little)
+	assertEq(t2[0], 37);
+    else
+	assertEq(t2[0], 37 << 16);
+    t1[0] = 0;
+
+    // Test that invoking as Atomics.whatever() works, on correct arguments
+    testMethod(new SharedInt8Array(sab), 0, 42, 4095);
+    testMethod(new SharedUint8Array(sab), 0, 42, 4095);
+    testMethod(new SharedUint8ClampedArray(sab), 0, 42, 4095);
+    testMethod(new SharedInt16Array(sab), 0, 42, 2047);
+    testMethod(new SharedUint16Array(sab), 0, 42, 2047);
+    testMethod(new SharedInt32Array(sab), 0, 42, 1023);
+    testMethod(new SharedUint32Array(sab), 0, 42, 1023);
+
+    // Test that invoking as v = Atomics.whatever; v() works, on correct arguments
+    gAtomics_compareExchange = Atomics.compareExchange;
+    gAtomics_load = Atomics.load;
+    gAtomics_store = Atomics.store;
+    gAtomics_fence = Atomics.fence;
+    gAtomics_add = Atomics.add;
+    gAtomics_sub = Atomics.sub;
+    gAtomics_and = Atomics.and;
+    gAtomics_or = Atomics.or;
+    gAtomics_xor = Atomics.xor;
+
+    testFunction(new SharedInt8Array(sab), 0, 42, 4095);
+    testFunction(new SharedUint8Array(sab), 0, 42, 4095);
+    testFunction(new SharedUint8ClampedArray(sab), 0, 42, 4095);
+    testFunction(new SharedInt16Array(sab), 0, 42, 2047);
+    testFunction(new SharedUint16Array(sab), 0, 42, 2047);
+    testFunction(new SharedInt32Array(sab), 0, 42, 1023);
+    testFunction(new SharedUint32Array(sab), 0, 42, 1023);
+
+    // Test various range and type conditions
+    var v8 = new SharedInt8Array(sab);
+    var v32 = new SharedInt32Array(sab);
+
+    testTypeCAS(v8);
+    testTypeCAS(v32);
+
+    testTypeBinop(v8, Atomics.add);
+    testTypeBinop(v8, Atomics.sub);
+    testTypeBinop(v8, Atomics.and);
+    testTypeBinop(v8, Atomics.or);
+    testTypeBinop(v8, Atomics.xor);
+
+    testTypeBinop(v32, Atomics.add);
+    testTypeBinop(v32, Atomics.sub);
+    testTypeBinop(v32, Atomics.and);
+    testTypeBinop(v32, Atomics.or);
+    testTypeBinop(v32, Atomics.xor);
+
+    // Test out-of-range references
+    testRangeCAS(v8);
+    testRangeCAS(v32);
+
+    // Test extreme values
+    testInt8Extremes(new SharedInt8Array(sab));
+    testUint8Extremes(new SharedUint8Array(sab));
+    testInt16Extremes(new SharedInt16Array(sab));
+    testUint32(new SharedUint32Array(sab));
+}
+
+if (this.Atomics && this.SharedArrayBuffer && this.SharedInt32Array)
+    runTests();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/inline-add.js
@@ -0,0 +1,31 @@
+// |jit-test| slow;
+//
+// This is intended to be run manually with IONFLAGS=logs and
+// postprocessing by iongraph to verify manually (by inspecting the
+// MIR) that:
+//
+//  - the add operation is inlined as it should be
+//  - loads and stores are not moved across the add
+//
+// Be sure to run with --ion-eager --ion-offthread-compile=off.
+
+function add(ta) {
+    var x = ta[0];
+    Atomics.add(ta, 86, 6);
+    var y = ta[1];
+    var z = y + 1;
+    var w = x + z;
+    return w;
+}
+
+if (!this.SharedArrayBuffer || !this.Atomics || !this.SharedInt32Array)
+    quit(0);
+
+var sab = new SharedArrayBuffer(4096);
+var ia = new SharedInt32Array(sab);
+for ( var i=0, limit=ia.length ; i < limit ; i++ )
+    ia[i] = 37;
+var v = 0;
+for ( var i=0 ; i < 1000 ; i++ )
+    v += add(ia);
+//print(v);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/inline-add2.js
@@ -0,0 +1,31 @@
+// |jit-test| slow;
+//
+// Like inline-add, but with SharedUint32Array, which is a special
+// case because the value is representable only as a Number.
+// All this tests is that the Uint32 path is being triggered.
+//
+// This is intended to be run manually with IONFLAGS=logs and
+// postprocessing by iongraph to verify manually (by inspecting the
+// MIR) that:
+//
+//  - the add operation is inlined as it should be, with
+//    a return type 'Double'
+//  - loads and stores are not moved across the add
+//
+// Be sure to run with --ion-eager --ion-offthread-compile=off.
+
+function add(ta) {
+    return Atomics.add(ta, 86, 6);
+}
+
+if (!this.SharedArrayBuffer || !this.Atomics || !this.SharedUint32Array)
+    quit(0);
+
+var sab = new SharedArrayBuffer(4096);
+var ia = new SharedUint32Array(sab);
+for ( var i=0, limit=ia.length ; i < limit ; i++ )
+    ia[i] = 0xdeadbeef;		// Important: Not an int32-capable value
+var v = 0;
+for ( var i=0 ; i < 1000 ; i++ )
+    v += add(ia);
+//print(v);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/inline-cmpxchg.js
@@ -0,0 +1,31 @@
+// |jit-test| slow;
+//
+// This is intended to be run manually with IONFLAGS=logs and
+// postprocessing by iongraph to verify manually (by inspecting the
+// MIR) that:
+//
+//  - the cmpxchg operation is inlined as it should be
+//  - loads and stores are not moved across the cmpxchg
+//
+// Be sure to run with --ion-eager --ion-offthread-compile=off.
+
+function cmpxchg(ta) {
+    var x = ta[0];
+    Atomics.compareExchange(ta, 86, 37, 42);
+    var y = ta[1];
+    var z = y + 1;
+    var w = x + z;
+    return w;
+}
+
+if (!this.SharedArrayBuffer || !this.Atomics || !this.SharedInt32Array)
+    quit(0);
+
+var sab = new SharedArrayBuffer(4096);
+var ia = new SharedInt32Array(sab);
+for ( var i=0, limit=ia.length ; i < limit ; i++ )
+    ia[i] = 37;
+var v = 0;
+for ( var i=0 ; i < 1000 ; i++ )
+    v += cmpxchg(ia);
+//print(v);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/inline-fence.js
@@ -0,0 +1,31 @@
+// |jit-test| slow;
+//
+// This is intended to be run manually with IONFLAGS=logs and
+// postprocessing by iongraph to verify manually (by inspecting the
+// MIR) that:
+//
+//  - the fence operation is inlined as it should be
+//  - loads and stores are not moved across the fence
+//
+// Be sure to run with --ion-eager --ion-offthread-compile=off.
+
+function fence(ta) {
+    var x = ta[0];
+    Atomics.fence();
+    var y = ta[1];
+    var z = y + 1;
+    var w = x + z;
+    return w;
+}
+
+if (!this.SharedArrayBuffer || !this.Atomics || !this.SharedInt32Array)
+    quit(0);
+
+var sab = new SharedArrayBuffer(4096);
+var ia = new SharedInt32Array(sab);
+for ( var i=0, limit=ia.length ; i < limit ; i++ )
+    ia[i] = 37;
+var v = 0;
+for ( var i=0 ; i < 1000 ; i++ )
+    v += fence(ia);
+//print(v);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1085298.js
@@ -0,0 +1,7 @@
+function f(x, y) {
+        return (y | 0 && x ? y | 0 : 0)
+}
+m = [1]
+assertEq(f(m[0], m[0]), 1)
+assertEq(f(m[1], m[0]), 0)
+assertEq(f(m[2], m[0]), 0)
new file mode 100644
--- /dev/null
+++ b/js/src/jit/AtomicOp.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 jit_AtomicOp_h
+#define jit_AtomicOp_h
+
+namespace js {
+namespace jit {
+
+// Types of atomic operation, shared by MIR and LIR.
+
+enum AtomicOp {
+    AtomicFetchAddOp,
+    AtomicFetchSubOp,
+    AtomicFetchAndOp,
+    AtomicFetchOrOp,
+    AtomicFetchXorOp
+};
+
+// Memory barrier types, shared by MIR and LIR.
+//
+// MembarSynchronizing is here because some platforms can make the
+// distinction (DSB vs DMB on ARM, SYNC vs parameterized SYNC on MIPS)
+// but there's been no reason to use it yet.
+
+enum MemoryBarrierBits {
+    MembarLoadLoad = 1,
+    MembarLoadStore = 2,
+    MembarStoreStore = 4,
+    MembarStoreLoad = 8,
+
+    MembarSynchronizing = 16,
+
+    // For validity testing
+    MembarAllbits = 31,
+};
+
+// Standard barrier bits for a full barrier.
+static const int MembarFull = MembarLoadLoad|MembarLoadStore|MembarStoreLoad|MembarStoreStore;
+
+// Standard sets of barrier bits for atomic loads and stores.
+// See http://gee.cs.oswego.edu/dl/jmm/cookbook.html for more.
+static const int MembarBeforeLoad = 0;
+static const int MembarAfterLoad = MembarLoadLoad|MembarLoadStore;
+static const int MembarBeforeStore = MembarStoreStore;
+static const int MembarAfterStore = MembarStoreLoad;
+
+} // namespace jit
+} // namespace js
+
+#endif /* jit_AtomicOp_h */
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -6155,19 +6155,17 @@ JitCode *
 JitRuntime::generateLazyLinkStub(JSContext *cx)
 {
     MacroAssembler masm(cx);
 
     Label call;
     GeneralRegisterSet regs = GeneralRegisterSet::Volatile();
     Register temp0 = regs.takeAny();
 
-    uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_IonJS);
-    masm.Push(Imm32(descriptor));
-    masm.call(&call);
+    masm.callWithExitFrame(&call);
     masm.jump(ReturnReg);
 
     masm.bind(&call);
     masm.enterExitFrame();
     masm.setupUnalignedABICall(1, temp0);
     masm.loadJSContext(temp0);
     masm.passABIArg(temp0);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, LazyLinkTopActivation));
@@ -8959,16 +8957,78 @@ CodeGenerator::visitStoreTypedArrayEleme
     }
     if (guardLength)
         masm.bind(&skip);
 
     return true;
 }
 
 bool
+CodeGenerator::visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayElement *lir)
+{
+    Register elements = ToRegister(lir->elements());
+    AnyRegister output = ToAnyRegister(lir->output());
+    Register temp = lir->temp()->isBogusTemp() ? InvalidReg : ToRegister(lir->temp());
+
+    MOZ_ASSERT(lir->oldval()->isRegister());
+    MOZ_ASSERT(lir->newval()->isRegister());
+
+    Register oldval = ToRegister(lir->oldval());
+    Register newval = ToRegister(lir->newval());
+
+    Scalar::Type arrayType = lir->mir()->arrayType();
+    int width = Scalar::byteSize(arrayType);
+
+    if (lir->index()->isConstant()) {
+        Address dest(elements, ToInt32(lir->index()) * width);
+        masm.compareExchangeToTypedIntArray(arrayType, dest, oldval, newval, temp, output);
+    } else {
+        BaseIndex dest(elements, ToRegister(lir->index()), ScaleFromElemWidth(width));
+        masm.compareExchangeToTypedIntArray(arrayType, dest, oldval, newval, temp, output);
+    }
+
+    return true;
+}
+
+template <typename T>
+static inline void
+AtomicBinopToTypedArray(MacroAssembler &masm, AtomicOp op,
+                        Scalar::Type arrayType, const LAllocation *value, const T &mem,
+                        Register temp1, Register temp2, AnyRegister output)
+{
+    if (value->isConstant())
+        masm.atomicBinopToTypedIntArray(op, arrayType, Imm32(ToInt32(value)), mem, temp1, temp2, output);
+    else
+        masm.atomicBinopToTypedIntArray(op, arrayType, ToRegister(value), mem, temp1, temp2, output);
+}
+
+bool
+CodeGenerator::visitAtomicTypedArrayElementBinop(LAtomicTypedArrayElementBinop *lir)
+{
+    AnyRegister output = ToAnyRegister(lir->output());
+    Register elements = ToRegister(lir->elements());
+    Register temp1 = lir->temp1()->isBogusTemp() ? InvalidReg : ToRegister(lir->temp1());
+    Register temp2 = lir->temp2()->isBogusTemp() ? InvalidReg : ToRegister(lir->temp2());
+    const LAllocation* value = lir->value();
+
+    Scalar::Type arrayType = lir->mir()->arrayType();
+    int width = Scalar::byteSize(arrayType);
+
+    if (lir->index()->isConstant()) {
+        Address mem(elements, ToInt32(lir->index()) * width);
+        AtomicBinopToTypedArray(masm, lir->mir()->operation(), arrayType, value, mem, temp1, temp2, output);
+    } else {
+        BaseIndex mem(elements, ToRegister(lir->index()), ScaleFromElemWidth(width));
+        AtomicBinopToTypedArray(masm, lir->mir()->operation(), arrayType, value, mem, temp1, temp2, output);
+    }
+
+    return true;
+}
+
+bool
 CodeGenerator::visitClampIToUint8(LClampIToUint8 *lir)
 {
     Register output = ToRegister(lir->output());
     MOZ_ASSERT(output == ToRegister(lir->input()));
     masm.clampIntToUint8(output);
     return true;
 }
 
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -260,16 +260,18 @@ class CodeGenerator : public CodeGenerat
     bool visitArrayPushV(LArrayPushV *lir);
     bool visitArrayPushT(LArrayPushT *lir);
     bool visitArrayConcat(LArrayConcat *lir);
     bool visitArrayJoin(LArrayJoin *lir);
     bool visitLoadTypedArrayElement(LLoadTypedArrayElement *lir);
     bool visitLoadTypedArrayElementHole(LLoadTypedArrayElementHole *lir);
     bool visitStoreTypedArrayElement(LStoreTypedArrayElement *lir);
     bool visitStoreTypedArrayElementHole(LStoreTypedArrayElementHole *lir);
+    bool visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayElement *lir);
+    bool visitAtomicTypedArrayElementBinop(LAtomicTypedArrayElementBinop *lir);
     bool visitClampIToUint8(LClampIToUint8 *lir);
     bool visitClampDToUint8(LClampDToUint8 *lir);
     bool visitClampVToUint8(LClampVToUint8 *lir);
     bool visitCallIteratorStart(LCallIteratorStart *lir);
     bool visitIteratorStart(LIteratorStart *lir);
     bool visitIteratorMore(LIteratorMore *lir);
     bool visitIsNoIterAndBranch(LIsNoIterAndBranch *lir);
     bool visitIteratorEnd(LIteratorEnd *lir);
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1869,19 +1869,16 @@ AttachFinishedCompilations(JSContext *cx
                 builder = testBuilder;
                 HelperThreadState().remove(finished, &i);
                 break;
             }
         }
         if (!builder)
             break;
 
-// TODO bug 1047346: Enable lazy linking for other architectures again by
-//                   fixing the lazy link stub.
-#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
         // Try to defer linking if the script is on the stack, to postpone
         // invalidating them.
         if (builder->info().executionMode() == SequentialExecution &&
             builder->script()->hasIonScript())
         {
             bool onStack = false;
             for (JitActivationIterator iter(cx->runtime()); !iter.done(); ++iter) {
                 for (JitFrameIterator it(iter); !it.done(); ++it) {
@@ -1901,17 +1898,16 @@ AttachFinishedCompilations(JSContext *cx
             }
 
             if (onStack) {
                 builder->script()->setPendingIonBuilder(cx, builder);
                 HelperThreadState().ionLazyLinkList().insertFront(builder);
                 continue;
             }
         }
-#endif
 
         if (CodeGenerator *codegen = builder->backgroundCodegen()) {
             RootedScript script(cx, builder->script());
             IonContext ictx(cx, &builder->alloc());
             AutoTraceLog logScript(logger, TraceLogCreateTextId(logger, script));
             AutoTraceLog logLink(logger, TraceLogger::IonLinking);
 
             // Root the assembler until the builder is finished below. As it
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -722,16 +722,23 @@ class IonBuilder
     InliningStatus inlineStrFromCharCode(CallInfo &callInfo);
     InliningStatus inlineStrCharAt(CallInfo &callInfo);
     InliningStatus inlineStrReplace(CallInfo &callInfo);
 
     // RegExp natives.
     InliningStatus inlineRegExpExec(CallInfo &callInfo);
     InliningStatus inlineRegExpTest(CallInfo &callInfo);
 
+    // Atomics natives.
+    InliningStatus inlineAtomicsCompareExchange(CallInfo &callInfo);
+    InliningStatus inlineAtomicsLoad(CallInfo &callInfo);
+    InliningStatus inlineAtomicsStore(CallInfo &callInfo);
+    InliningStatus inlineAtomicsFence(CallInfo &callInfo);
+    InliningStatus inlineAtomicsBinop(CallInfo &callInfo, JSFunction *target);
+
     // Array intrinsics.
     InliningStatus inlineUnsafePutElements(CallInfo &callInfo);
     bool inlineUnsafeSetDenseArrayElement(CallInfo &callInfo, uint32_t base);
     bool inlineUnsafeSetTypedArrayElement(CallInfo &callInfo, uint32_t base,
                                           ScalarTypeDescr::Type arrayType);
     bool inlineUnsafeSetTypedObjectArrayElement(CallInfo &callInfo, uint32_t base,
                                                 ScalarTypeDescr::Type arrayType);
     InliningStatus inlineNewDenseArray(CallInfo &callInfo);
@@ -786,16 +793,19 @@ class IonBuilder
 
     // Inlining helpers.
     bool inlineGenericFallback(JSFunction *target, CallInfo &callInfo, MBasicBlock *dispatchBlock,
                                bool clonedAtCallsite);
     bool inlineTypeObjectFallback(CallInfo &callInfo, MBasicBlock *dispatchBlock,
                                   MTypeObjectDispatch *dispatch, MGetPropertyCache *cache,
                                   MBasicBlock **fallbackTarget);
 
+    bool atomicsMeetsPreconditions(CallInfo &callInfo, Scalar::Type *arrayElementType);
+    void atomicsCheckBounds(CallInfo &callInfo, MInstruction **elements, MDefinition **index);
+
     bool testNeedsArgumentCheck(JSFunction *target, CallInfo &callInfo);
 
     MDefinition *makeCallsiteClone(JSFunction *target, MDefinition *fun);
     MCall *makeCallHelper(JSFunction *target, CallInfo &callInfo, bool cloneAtCallsite);
     bool makeCall(JSFunction *target, CallInfo &callInfo, bool cloneAtCallsite);
 
     MDefinition *patchInlinedReturn(CallInfo &callInfo, MBasicBlock *exit, MBasicBlock *bottom);
     MDefinition *patchInlinedReturns(CallInfo &callInfo, MIRGraphReturns &returns,
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -6,16 +6,17 @@
 
 #include "jit/IonMacroAssembler.h"
 
 #include "jsinfer.h"
 #include "jsprf.h"
 
 #include "builtin/TypedObject.h"
 #include "gc/GCTrace.h"
+#include "jit/AtomicOp.h"
 #include "jit/Bailouts.h"
 #include "jit/BaselineFrame.h"
 #include "jit/BaselineIC.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Lowering.h"
 #include "jit/MIR.h"
 #include "jit/ParallelFunctions.h"
 #include "vm/ForkJoin.h"
@@ -392,16 +393,221 @@ MacroAssembler::loadFromTypedArray(Scala
     }
 }
 
 template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const Address &src, const ValueOperand &dest,
                                                  bool allowDouble, Register temp, Label *fail);
 template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const BaseIndex &src, const ValueOperand &dest,
                                                  bool allowDouble, Register temp, Label *fail);
 
+template<typename T>
+void
+MacroAssembler::compareExchangeToTypedIntArray(Scalar::Type arrayType, const T &mem,
+                                               Register oldval, Register newval,
+                                               Register temp, AnyRegister output)
+{
+    switch (arrayType) {
+      case Scalar::Int8:
+        compareExchange8SignExtend(mem, oldval, newval, output.gpr());
+        break;
+      case Scalar::Uint8:
+        compareExchange8ZeroExtend(mem, oldval, newval, output.gpr());
+        break;
+      case Scalar::Uint8Clamped:
+        compareExchange8ZeroExtend(mem, oldval, newval, output.gpr());
+        break;
+      case Scalar::Int16:
+        compareExchange16SignExtend(mem, oldval, newval, output.gpr());
+        break;
+      case Scalar::Uint16:
+        compareExchange16ZeroExtend(mem, oldval, newval, output.gpr());
+        break;
+      case Scalar::Int32:
+        compareExchange32(mem, oldval, newval, output.gpr());
+        break;
+      case Scalar::Uint32:
+        // At the moment, the code in MCallOptimize.cpp requires the output
+        // type to be double for uint32 arrays.  See bug 1077305.
+        MOZ_ASSERT(output.isFloat());
+        compareExchange32(mem, oldval, newval, temp);
+        convertUInt32ToDouble(temp, output.fpu());
+        break;
+      default:
+        MOZ_CRASH("Invalid typed array type");
+    }
+}
+
+template void
+MacroAssembler::compareExchangeToTypedIntArray(Scalar::Type arrayType, const Address &mem,
+                                               Register oldval, Register newval, Register temp,
+                                               AnyRegister output);
+template void
+MacroAssembler::compareExchangeToTypedIntArray(Scalar::Type arrayType, const BaseIndex &mem,
+                                               Register oldval, Register newval, Register temp,
+                                               AnyRegister output);
+
+template<typename S, typename T>
+void
+MacroAssembler::atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType, const S &value,
+                                           const T &mem, Register temp1, Register temp2, AnyRegister output)
+{
+    // Uint8Clamped is explicitly not supported here
+    switch (arrayType) {
+      case Scalar::Int8:
+        switch (op) {
+          case AtomicFetchAddOp:
+            atomicFetchAdd8SignExtend(value, mem, temp1, output.gpr());
+            break;
+          case AtomicFetchSubOp:
+            atomicFetchSub8SignExtend(value, mem, temp1, output.gpr());
+            break;
+          case AtomicFetchAndOp:
+            atomicFetchAnd8SignExtend(value, mem, temp1, output.gpr());
+            break;
+          case AtomicFetchOrOp:
+            atomicFetchOr8SignExtend(value, mem, temp1, output.gpr());
+            break;
+          case AtomicFetchXorOp:
+            atomicFetchXor8SignExtend(value, mem, temp1, output.gpr());
+            break;
+          default:
+            MOZ_CRASH("Invalid typed array atomic operation");
+        }
+        break;
+      case Scalar::Uint8:
+        switch (op) {
+          case AtomicFetchAddOp:
+            atomicFetchAdd8ZeroExtend(value, mem, temp1, output.gpr());
+            break;
+          case AtomicFetchSubOp:
+            atomicFetchSub8ZeroExtend(value, mem, temp1, output.gpr());
+            break;
+          case AtomicFetchAndOp:
+            atomicFetchAnd8ZeroExtend(value, mem, temp1, output.gpr());
+            break;
+          case AtomicFetchOrOp:
+            atomicFetchOr8ZeroExtend(value, mem, temp1, output.gpr());
+            break;
+          case AtomicFetchXorOp:
+            atomicFetchXor8ZeroExtend(value, mem, temp1, output.gpr());
+            break;
+          default:
+            MOZ_CRASH("Invalid typed array atomic operation");
+        }
+        break;
+      case Scalar::Int16:
+        switch (op) {
+          case AtomicFetchAddOp:
+            atomicFetchAdd16SignExtend(value, mem, temp1, output.gpr());
+            break;
+          case AtomicFetchSubOp:
+            atomicFetchSub16SignExtend(value, mem, temp1, output.gpr());
+            break;
+          case AtomicFetchAndOp:
+            atomicFetchAnd16SignExtend(value, mem, temp1, output.gpr());
+            break;
+          case AtomicFetchOrOp:
+            atomicFetchOr16SignExtend(value, mem, temp1, output.gpr());
+            break;
+          case AtomicFetchXorOp:
+            atomicFetchXor16SignExtend(value, mem, temp1, output.gpr());
+            break;
+          default:
+            MOZ_CRASH("Invalid typed array atomic operation");
+        }
+        break;
+      case Scalar::Uint16:
+        switch (op) {
+          case AtomicFetchAddOp:
+            atomicFetchAdd16ZeroExtend(value, mem, temp1, output.gpr());
+            break;
+          case AtomicFetchSubOp:
+            atomicFetchSub16ZeroExtend(value, mem, temp1, output.gpr());
+            break;
+          case AtomicFetchAndOp:
+            atomicFetchAnd16ZeroExtend(value, mem, temp1, output.gpr());
+            break;
+          case AtomicFetchOrOp:
+            atomicFetchOr16ZeroExtend(value, mem, temp1, output.gpr());
+            break;
+          case AtomicFetchXorOp:
+            atomicFetchXor16ZeroExtend(value, mem, temp1, output.gpr());
+            break;
+          default:
+            MOZ_CRASH("Invalid typed array atomic operation");
+        }
+        break;
+      case Scalar::Int32:
+        switch (op) {
+          case AtomicFetchAddOp:
+            atomicFetchAdd32(value, mem, temp1, output.gpr());
+            break;
+          case AtomicFetchSubOp:
+            atomicFetchSub32(value, mem, temp1, output.gpr());
+            break;
+          case AtomicFetchAndOp:
+            atomicFetchAnd32(value, mem, temp1, output.gpr());
+            break;
+          case AtomicFetchOrOp:
+            atomicFetchOr32(value, mem, temp1, output.gpr());
+            break;
+          case AtomicFetchXorOp:
+            atomicFetchXor32(value, mem, temp1, output.gpr());
+            break;
+          default:
+            MOZ_CRASH("Invalid typed array atomic operation");
+        }
+        break;
+      case Scalar::Uint32:
+        // At the moment, the code in MCallOptimize.cpp requires the output
+        // type to be double for uint32 arrays.  See bug 1077305.
+        MOZ_ASSERT(output.isFloat());
+        switch (op) {
+          case AtomicFetchAddOp:
+            atomicFetchAdd32(value, mem, InvalidReg, temp1);
+            break;
+          case AtomicFetchSubOp:
+            atomicFetchSub32(value, mem, InvalidReg, temp1);
+            break;
+          case AtomicFetchAndOp:
+            atomicFetchAnd32(value, mem, temp2, temp1);
+            break;
+          case AtomicFetchOrOp:
+            atomicFetchOr32(value, mem, temp2, temp1);
+            break;
+          case AtomicFetchXorOp:
+            atomicFetchXor32(value, mem, temp2, temp1);
+            break;
+          default:
+            MOZ_CRASH("Invalid typed array atomic operation");
+        }
+        convertUInt32ToDouble(temp1, output.fpu());
+        break;
+      default:
+        MOZ_CRASH("Invalid typed array type");
+    }
+}
+
+template void
+MacroAssembler::atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType,
+                                           const Imm32 &value, const Address &mem,
+                                           Register temp1, Register temp2, AnyRegister output);
+template void
+MacroAssembler::atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType,
+                                           const Imm32 &value, const BaseIndex &mem,
+                                           Register temp1, Register temp2, AnyRegister output);
+template void
+MacroAssembler::atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType,
+                                           const Register &value, const Address &mem,
+                                           Register temp1, Register temp2, AnyRegister output);
+template void
+MacroAssembler::atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType,
+                                           const Register &value, const BaseIndex &mem,
+                                           Register temp1, Register temp2, AnyRegister output);
+
 // Inlined version of gc::CheckAllocatorState that checks the bare essentials
 // and bails for anything that cannot be handled with our jit allocators.
 void
 MacroAssembler::checkAllocatorState(Label *fail)
 {
     // Don't execute the inline path if we are tracing allocations.
     if (js::gc::TraceEnabled())
         jump(fail);
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -17,16 +17,17 @@
 # include "jit/arm/MacroAssembler-arm.h"
 #elif defined(JS_CODEGEN_MIPS)
 # include "jit/mips/MacroAssembler-mips.h"
 #elif defined(JS_CODEGEN_NONE)
 # include "jit/none/MacroAssembler-none.h"
 #else
 # error "Unknown architecture!"
 #endif
+#include "jit/AtomicOp.h"
 #include "jit/IonInstrumentation.h"
 #include "jit/JitCompartment.h"
 #include "jit/VMFunctions.h"
 #include "vm/ProxyObject.h"
 #include "vm/Shape.h"
 
 #ifdef IS_LITTLE_ENDIAN
 #define IMM32_16ADJ(X) X << 16
@@ -733,16 +734,24 @@ class MacroAssembler : public MacroAssem
           case Scalar::Uint32:
             store32(value, dest);
             break;
           default:
             MOZ_CRASH("Invalid typed array type");
         }
     }
 
+    template<typename T>
+    void compareExchangeToTypedIntArray(Scalar::Type arrayType, const T &mem, Register oldval, Register newval,
+                                        Register temp, AnyRegister output);
+
+    template<typename S, typename T>
+    void atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType, const S &value,
+                                    const T &mem, Register temp1, Register temp2, AnyRegister output);
+
     void storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value, const BaseIndex &dest);
     void storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value, const Address &dest);
 
     Register extractString(const Address &address, Register scratch) {
         return extractObject(address, scratch);
     }
     Register extractString(const ValueOperand &value, Register scratch) {
         return extractObject(value, scratch);
@@ -939,16 +948,25 @@ class MacroAssembler : public MacroAssem
         leaveSPSFrame();
         MacroAssemblerSpecific::callIon(callee);
         uint32_t ret = currentOffset();
         reenterSPSFrame();
         return ret;
     }
 
     // see above comment for what is returned
+    uint32_t callWithExitFrame(Label *target) {
+        leaveSPSFrame();
+        MacroAssemblerSpecific::callWithExitFrame(target);
+        uint32_t ret = currentOffset();
+        reenterSPSFrame();
+        return ret;
+    }
+
+    // see above comment for what is returned
     uint32_t callWithExitFrame(JitCode *target) {
         leaveSPSFrame();
         MacroAssemblerSpecific::callWithExitFrame(target);
         uint32_t ret = currentOffset();
         reenterSPSFrame();
         return ret;
     }
 
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -4866,16 +4866,90 @@ class LStoreTypedArrayElementStatic : pu
     const LAllocation *ptr() {
         return getOperand(0);
     }
     const LAllocation *value() {
         return getOperand(1);
     }
 };
 
+class LCompareExchangeTypedArrayElement : public LInstructionHelper<1, 4, 1>
+{
+  public:
+    LIR_HEADER(CompareExchangeTypedArrayElement)
+
+    LCompareExchangeTypedArrayElement(const LAllocation &elements, const LAllocation &index,
+                                      const LAllocation &oldval, const LAllocation &newval,
+                                      const LDefinition &temp)
+    {
+        setOperand(0, elements);
+        setOperand(1, index);
+        setOperand(2, oldval);
+        setOperand(3, newval);
+        setTemp(0, temp);
+    }
+
+    const LAllocation *elements() {
+        return getOperand(0);
+    }
+    const LAllocation *index() {
+        return getOperand(1);
+    }
+    const LAllocation *oldval() {
+        return getOperand(2);
+    }
+    const LAllocation *newval() {
+        return getOperand(3);
+    }
+    const LDefinition *temp() {
+        return getTemp(0);
+    }
+
+    const MCompareExchangeTypedArrayElement *mir() const {
+        return mir_->toCompareExchangeTypedArrayElement();
+    }
+};
+
+class LAtomicTypedArrayElementBinop : public LInstructionHelper<1, 3, 2>
+{
+  public:
+    LIR_HEADER(AtomicTypedArrayElementBinop)
+
+    LAtomicTypedArrayElementBinop(const LAllocation &elements, const LAllocation &index,
+                                  const LAllocation &value, const LDefinition &temp1,
+                                  const LDefinition &temp2)
+    {
+        setOperand(0, elements);
+        setOperand(1, index);
+        setOperand(2, value);
+        setTemp(0, temp1);
+        setTemp(1, temp2);
+    }
+
+    const LAllocation *elements() {
+        return getOperand(0);
+    }
+    const LAllocation *index() {
+        return getOperand(1);
+    }
+    const LAllocation *value() {
+        return getOperand(2);
+    }
+    const LDefinition *temp1() {
+        return getTemp(0);
+    }
+    const LDefinition *temp2() {
+        return getTemp(1);
+    }
+
+    const MAtomicTypedArrayElementBinop *mir() const {
+        return mir_->toAtomicTypedArrayElementBinop();
+    }
+};
+
 class LEffectiveAddress : public LInstructionHelper<1, 2, 0>
 {
   public:
     LIR_HEADER(EffectiveAddress);
 
     LEffectiveAddress(const LAllocation &base, const LAllocation &index) {
         setOperand(0, base);
         setOperand(1, index);
@@ -6623,12 +6697,36 @@ class LThrowUninitializedLexical : publi
   public:
     LIR_HEADER(ThrowUninitializedLexical)
 
     MLexicalCheck *mir() {
         return mir_->toLexicalCheck();
     }
 };
 
+class LMemoryBarrier : public LInstructionHelper<0, 0, 0>
+{
+  private:
+    const int type_;
+
+  public:
+    LIR_HEADER(MemoryBarrier)
+
+    // The parameter 'type' is a bitwise 'or' of the barrier types needed,
+    // see AtomicOp.h.
+    explicit LMemoryBarrier(int type) : type_(type)
+    {
+        MOZ_ASSERT((type_ & ~MembarAllbits) == 0);
+    }
+
+    int type() const {
+        return type_;
+    }
+
+    const MMemoryBarrier *mir() const {
+        return mir_->toMemoryBarrier();
+    }
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_LIR_Common_h */
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -653,17 +653,18 @@ class LNode
     virtual void setDef(size_t index, const LDefinition &def) = 0;
 
     // Returns information about operands.
     virtual size_t numOperands() const = 0;
     virtual LAllocation *getOperand(size_t index) = 0;
     virtual void setOperand(size_t index, const LAllocation &a) = 0;
 
     // Returns information about temporary registers needed. Each temporary
-    // register is an LUse with a TEMPORARY policy, or a fixed register.
+    // register is an LDefinition with a fixed or virtual register and
+    // either GENERAL, FLOAT32, or DOUBLE type.
     virtual size_t numTemps() const = 0;
     virtual LDefinition *getTemp(size_t index) = 0;
     virtual void setTemp(size_t index, const LDefinition &a) = 0;
 
     // Returns the number of successors of this instruction, if it is a control
     // transfer instruction, or zero otherwise.
     virtual size_t numSuccessors() const = 0;
     virtual MBasicBlock *getSuccessor(size_t i) const = 0;
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -229,16 +229,18 @@
     _(StoreElementHoleV)            \
     _(StoreElementHoleT)            \
     _(LoadTypedArrayElement)        \
     _(LoadTypedArrayElementHole)    \
     _(LoadTypedArrayElementStatic)  \
     _(StoreTypedArrayElement)       \
     _(StoreTypedArrayElementHole)   \
     _(StoreTypedArrayElementStatic) \
+    _(CompareExchangeTypedArrayElement) \
+    _(AtomicTypedArrayElementBinop) \
     _(EffectiveAddress)             \
     _(ClampIToUint8)                \
     _(ClampDToUint8)                \
     _(ClampVToUint8)                \
     _(LoadFixedSlotV)               \
     _(LoadFixedSlotT)               \
     _(StoreFixedSlotV)              \
     _(StoreFixedSlotT)              \
@@ -322,16 +324,17 @@
     _(AsmJSLoadFFIFunc)             \
     _(AsmJSParameter)               \
     _(AsmJSReturn)                  \
     _(AsmJSVoidReturn)              \
     _(AsmJSPassStackArg)            \
     _(AsmJSCall)                    \
     _(InterruptCheckPar)            \
     _(RecompileCheck)               \
+    _(MemoryBarrier)                \
     _(AssertRangeI)                 \
     _(AssertRangeD)                 \
     _(AssertRangeF)                 \
     _(AssertRangeV)                 \
     _(LexicalCheck)                 \
     _(ThrowUninitializedLexical)
 
 #if defined(JS_CODEGEN_X86)
--- a/js/src/jit/LinearScan.cpp
+++ b/js/src/jit/LinearScan.cpp
@@ -206,17 +206,17 @@ LinearScanAllocator::allocateRegisters()
 
 /*
  * This function iterates over control flow edges in the function and resolves
  * conflicts wherein two predecessors of a block have different allocations
  * for a virtual register than the block itself. It also turns phis into moves.
  *
  * The algorithm is based on the one published in "Linear Scan Register
  * Allocation on SSA Form" by C. Wimmer et al., for which the full citation
- * appears above.
+ * appears in LiveRangeAllocator.cpp.
  */
 bool
 LinearScanAllocator::resolveControlFlow()
 {
     for (size_t i = 0; i < graph.numBlocks(); i++) {
         if (mir->shouldCancel("LSRA Resolve Control Flow (main loop)"))
             return false;
 
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2854,20 +2854,32 @@ LIRGenerator::visitLoadTypedArrayElement
 
     MOZ_ASSERT(IsNumberType(ins->type()));
 
     // We need a temp register for Uint32Array with known double result.
     LDefinition tempDef = LDefinition::BogusTemp();
     if (ins->arrayType() == Scalar::Uint32 && IsFloatingPointType(ins->type()))
         tempDef = temp();
 
+    if (ins->requiresMemoryBarrier()) {
+        LMemoryBarrier *fence = new(alloc()) LMemoryBarrier(MembarBeforeLoad);
+        if (!add(fence, ins))
+            return false;
+    }
     LLoadTypedArrayElement *lir = new(alloc()) LLoadTypedArrayElement(elements, index, tempDef);
     if (ins->fallible() && !assignSnapshot(lir, Bailout_Overflow))
         return false;
-    return define(lir, ins);
+    if (!define(lir, ins))
+        return false;
+    if (ins->requiresMemoryBarrier()) {
+        LMemoryBarrier *fence = new(alloc()) LMemoryBarrier(MembarAfterLoad);
+        if (!add(fence, ins))
+            return false;
+    }
+    return true;
 }
 
 bool
 LIRGenerator::visitClampToUint8(MClampToUint8 *ins)
 {
     MDefinition *in = ins->input();
 
     switch (in->type()) {
@@ -2941,17 +2953,34 @@ LIRGenerator::visitStoreTypedArrayElemen
     LAllocation index = useRegisterOrConstant(ins->index());
     LAllocation value;
 
     // For byte arrays, the value has to be in a byte register on x86.
     if (ins->isByteArray())
         value = useByteOpRegisterOrNonDoubleConstant(ins->value());
     else
         value = useRegisterOrNonDoubleConstant(ins->value());
-    return add(new(alloc()) LStoreTypedArrayElement(elements, index, value), ins);
+
+    // Optimization opportunity for atomics: on some platforms there
+    // is a store instruction that incorporates the necessary
+    // barriers, and we could use that instead of separate barrier and
+    // store instructions.  See bug #1077027.
+    if (ins->requiresMemoryBarrier()) {
+        LMemoryBarrier *fence = new(alloc()) LMemoryBarrier(MembarBeforeStore);
+        if (!add(fence, ins))
+            return false;
+    }
+    if (!add(new(alloc()) LStoreTypedArrayElement(elements, index, value), ins))
+        return false;
+    if (ins->requiresMemoryBarrier()) {
+        LMemoryBarrier *fence = new(alloc()) LMemoryBarrier(MembarAfterStore);
+        if (!add(fence, ins))
+            return false;
+    }
+    return true;
 }
 
 bool
 LIRGenerator::visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole *ins)
 {
     MOZ_ASSERT(ins->elements()->type() == MIRType_Elements);
     MOZ_ASSERT(ins->index()->type() == MIRType_Int32);
     MOZ_ASSERT(ins->length()->type() == MIRType_Int32);
@@ -3709,16 +3738,23 @@ LIRGenerator::visitRecompileCheck(MRecom
 {
     LRecompileCheck *lir = new(alloc()) LRecompileCheck(temp());
     if (!add(lir, ins))
         return false;
     return assignSafepoint(lir, ins);
 }
 
 bool
+LIRGenerator::visitMemoryBarrier(MMemoryBarrier *ins)
+{
+    LMemoryBarrier *lir = new(alloc()) LMemoryBarrier(ins->type());
+    return add(lir, ins);
+}
+
+bool
 LIRGenerator::visitSimdConstant(MSimdConstant *ins)
 {
     MOZ_ASSERT(IsSimdType(ins->type()));
 
     if (ins->type() == MIRType_Int32x4)
         return define(new(alloc()) LInt32x4(), ins);
     if (ins->type() == MIRType_Float32x4)
         return define(new(alloc()) LFloat32x4(), ins);
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -264,16 +264,17 @@ class LIRGenerator : public LIRGenerator
     bool visitAsmJSReturn(MAsmJSReturn *ins);
     bool visitAsmJSVoidReturn(MAsmJSVoidReturn *ins);
     bool visitAsmJSPassStackArg(MAsmJSPassStackArg *ins);
     bool visitAsmJSCall(MAsmJSCall *ins);
     bool visitSetDOMProperty(MSetDOMProperty *ins);
     bool visitGetDOMProperty(MGetDOMProperty *ins);
     bool visitGetDOMMember(MGetDOMMember *ins);
     bool visitRecompileCheck(MRecompileCheck *ins);
+    bool visitMemoryBarrier(MMemoryBarrier *ins);
     bool visitSimdExtractElement(MSimdExtractElement *ins);
     bool visitSimdInsertElement(MSimdInsertElement *ins);
     bool visitSimdSignMask(MSimdSignMask *ins);
     bool visitSimdSwizzle(MSimdSwizzle *ins);
     bool visitSimdShuffle(MSimdShuffle *ins);
     bool visitSimdUnaryArith(MSimdUnaryArith *ins);
     bool visitSimdBinaryComp(MSimdBinaryComp *ins);
     bool visitSimdBinaryArith(MSimdBinaryArith *ins);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 "jsmath.h"
 
+#include "builtin/AtomicsObject.h"
 #include "builtin/TestingFunctions.h"
 #include "builtin/TypedObject.h"
 #include "jit/BaselineInspector.h"
 #include "jit/IonBuilder.h"
 #include "jit/Lowering.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 #include "vm/ArgumentsObject.h"
@@ -29,16 +30,34 @@ IonBuilder::InliningStatus
 IonBuilder::inlineNativeCall(CallInfo &callInfo, JSFunction *target)
 {
     MOZ_ASSERT(target->isNative());
     JSNative native = target->native();
 
     if (!optimizationInfo().inlineNative())
         return InliningStatus_NotInlined;
 
+    // Atomic natives.
+    if (native == atomics_compareExchange)
+        return inlineAtomicsCompareExchange(callInfo);
+    if (native == atomics_load)
+        return inlineAtomicsLoad(callInfo);
+    if (native == atomics_store)
+        return inlineAtomicsStore(callInfo);
+    if (native == atomics_fence)
+        return inlineAtomicsFence(callInfo);
+    if (native == atomics_add ||
+        native == atomics_sub ||
+        native == atomics_and ||
+        native == atomics_or ||
+        native == atomics_xor)
+    {
+        return inlineAtomicsBinop(callInfo, target);
+    }
+
     // Array natives.
     if (native == js_Array)
         return inlineArray(callInfo);
     if (native == js::array_pop)
         return inlineArrayPopShift(callInfo, MArrayPopShift::Pop);
     if (native == js::array_shift)
         return inlineArrayPopShift(callInfo, MArrayPopShift::Shift);
     if (native == js::array_push)
@@ -2231,16 +2250,235 @@ IonBuilder::inlineBoundFunction(CallInfo
 
     if (!makeCall(scriptedTarget, callInfo, false))
         return InliningStatus_Error;
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
+IonBuilder::inlineAtomicsCompareExchange(CallInfo &callInfo)
+{
+    if (callInfo.argc() != 4 || callInfo.constructing())
+        return InliningStatus_NotInlined;
+
+    Scalar::Type arrayType;
+    if (!atomicsMeetsPreconditions(callInfo, &arrayType))
+        return InliningStatus_NotInlined;
+
+    MDefinition *oldval = callInfo.getArg(2);
+    if (!(oldval->type() == MIRType_Int32 || oldval->type() == MIRType_Double))
+        return InliningStatus_NotInlined;
+
+    MDefinition *newval = callInfo.getArg(3);
+    if (!(newval->type() == MIRType_Int32 || newval->type() == MIRType_Double))
+        return InliningStatus_NotInlined;
+
+    callInfo.setImplicitlyUsedUnchecked();
+
+    MInstruction *elements;
+    MDefinition *index;
+    atomicsCheckBounds(callInfo, &elements, &index);
+
+    MDefinition *oldvalToWrite = oldval;
+    if (oldval->type() == MIRType_Double) {
+        oldvalToWrite = MTruncateToInt32::New(alloc(), oldval);
+        current->add(oldvalToWrite->toInstruction());
+    }
+
+    MDefinition *newvalToWrite = newval;
+    if (newval->type() == MIRType_Double) {
+        newvalToWrite = MTruncateToInt32::New(alloc(), newval);
+        current->add(newvalToWrite->toInstruction());
+    }
+
+    MCompareExchangeTypedArrayElement *cas =
+        MCompareExchangeTypedArrayElement::New(alloc(), elements, index, arrayType,
+                                               oldvalToWrite, newvalToWrite);
+    cas->setResultType(getInlineReturnType());
+    current->add(cas);
+    current->push(cas);
+
+    return InliningStatus_Inlined;
+}
+
+IonBuilder::InliningStatus
+IonBuilder::inlineAtomicsLoad(CallInfo &callInfo)
+{
+    if (callInfo.argc() != 2 || callInfo.constructing())
+        return InliningStatus_NotInlined;
+
+    Scalar::Type arrayType;
+    if (!atomicsMeetsPreconditions(callInfo, &arrayType))
+        return InliningStatus_NotInlined;
+
+    callInfo.setImplicitlyUsedUnchecked();
+
+    MInstruction *elements;
+    MDefinition *index;
+    atomicsCheckBounds(callInfo, &elements, &index);
+
+    MLoadTypedArrayElement *load =
+        MLoadTypedArrayElement::New(alloc(), elements, index, arrayType,
+                                    DoesRequireMemoryBarrier);
+    load->setResultType(getInlineReturnType());
+    current->add(load);
+    current->push(load);
+
+    return InliningStatus_Inlined;
+}
+
+IonBuilder::InliningStatus
+IonBuilder::inlineAtomicsStore(CallInfo &callInfo)
+{
+    if (callInfo.argc() != 3 || callInfo.constructing())
+        return InliningStatus_NotInlined;
+
+    Scalar::Type arrayType;
+    if (!atomicsMeetsPreconditions(callInfo, &arrayType))
+        return InliningStatus_NotInlined;
+
+    MDefinition *value = callInfo.getArg(2);
+    if (!(value->type() == MIRType_Int32 || value->type() == MIRType_Double))
+        return InliningStatus_NotInlined;
+
+    callInfo.setImplicitlyUsedUnchecked();
+
+    MInstruction *elements;
+    MDefinition *index;
+    atomicsCheckBounds(callInfo, &elements, &index);
+
+    MDefinition *toWrite = value;
+    if (value->type() == MIRType_Double) {
+        toWrite = MTruncateToInt32::New(alloc(), value);
+        current->add(toWrite->toInstruction());
+    }
+    MStoreTypedArrayElement *store =
+        MStoreTypedArrayElement::New(alloc(), elements, index, toWrite, arrayType,
+                                     DoesRequireMemoryBarrier);
+    current->add(store);
+    current->push(value);
+
+    return InliningStatus_Inlined;
+}
+
+IonBuilder::InliningStatus
+IonBuilder::inlineAtomicsFence(CallInfo &callInfo)
+{
+    if (callInfo.argc() != 0 || callInfo.constructing())
+        return InliningStatus_NotInlined;
+
+    callInfo.setImplicitlyUsedUnchecked();
+
+    MMemoryBarrier *fence = MMemoryBarrier::New(alloc());
+    current->add(fence);
+    pushConstant(UndefinedValue());
+
+    return InliningStatus_Inlined;
+}
+
+IonBuilder::InliningStatus
+IonBuilder::inlineAtomicsBinop(CallInfo &callInfo, JSFunction *target)
+{
+    if (callInfo.argc() != 3 || callInfo.constructing())
+        return InliningStatus_NotInlined;
+
+    Scalar::Type arrayType;
+    if (!atomicsMeetsPreconditions(callInfo, &arrayType))
+        return InliningStatus_NotInlined;
+
+    MDefinition *value = callInfo.getArg(2);
+    if (!(value->type() == MIRType_Int32 || value->type() == MIRType_Double))
+        return InliningStatus_NotInlined;
+
+    callInfo.setImplicitlyUsedUnchecked();
+
+    MInstruction *elements;
+    MDefinition *index;
+    atomicsCheckBounds(callInfo, &elements, &index);
+
+    JSNative native = target->native();
+    AtomicOp k = AtomicFetchAddOp;
+    if (native == atomics_add)
+        k = AtomicFetchAddOp;
+    else if (native == atomics_sub)
+        k = AtomicFetchSubOp;
+    else if (native == atomics_and)
+        k = AtomicFetchAndOp;
+    else if (native == atomics_or)
+        k = AtomicFetchOrOp;
+    else if (native == atomics_xor)
+        k = AtomicFetchXorOp;
+    else
+        MOZ_CRASH("Bad atomic operation");
+
+    MDefinition *toWrite = value;
+    if (value->type() == MIRType_Double) {
+        toWrite = MTruncateToInt32::New(alloc(), value);
+        current->add(toWrite->toInstruction());
+    }
+    MAtomicTypedArrayElementBinop *binop =
+        MAtomicTypedArrayElementBinop::New(alloc(), k, elements, index, arrayType, toWrite);
+    binop->setResultType(getInlineReturnType());
+    current->add(binop);
+    current->push(binop);
+
+    return InliningStatus_Inlined;
+}
+
+bool
+IonBuilder::atomicsMeetsPreconditions(CallInfo &callInfo, Scalar::Type *arrayType)
+{
+    if (callInfo.getArg(0)->type() != MIRType_Object)
+        return false;
+
+    if (callInfo.getArg(1)->type() != MIRType_Int32)
+        return false;
+
+    // Ensure that the first argument is a valid SharedTypedArray.
+    //
+    // Then check both that the element type is something we can
+    // optimize and that the return type is suitable for that element
+    // type.
+
+    types::TemporaryTypeSet *arg0Types = callInfo.getArg(0)->resultTypeSet();
+    if (!arg0Types)
+        return false;
+
+    *arrayType = arg0Types->getSharedTypedArrayType();
+    switch (*arrayType) {
+      case Scalar::Int8:
+      case Scalar::Uint8:
+      case Scalar::Int16:
+      case Scalar::Uint16:
+      case Scalar::Int32:
+        return getInlineReturnType() == MIRType_Int32;
+      case Scalar::Uint32:
+        // Bug 1077305: it would be attractive to allow inlining even
+        // if the inline return type is Int32, which it will frequently
+        // be.
+        return getInlineReturnType() == MIRType_Double;
+      default:
+        // Excludes floating types and Uint8Clamped
+        return false;
+    }
+}
+
+void
+IonBuilder::atomicsCheckBounds(CallInfo &callInfo, MInstruction **elements, MDefinition **index)
+{
+    // Perform bounds checking and extract the elements vector.
+    MDefinition *obj = callInfo.getArg(0);
+    MInstruction *length = nullptr;
+    *index = callInfo.getArg(1);
+    *elements = nullptr;
+    addTypedArrayLengthAndData(obj, DoBoundsCheck, index, &length, elements);
+}
+
+IonBuilder::InliningStatus
 IonBuilder::inlineIsConstructing(CallInfo &callInfo)
 {
     MOZ_ASSERT(!callInfo.constructing());
     MOZ_ASSERT(callInfo.argc() == 0);
     MOZ_ASSERT(script()->functionNonDelazifying(),
                "isConstructing() should only be called in function scripts");
 
     if (getInlineReturnType() != MIRType_Boolean)
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -1260,24 +1260,24 @@ MPhi::foldsTernary()
 
     MBasicBlock *pred = block()->immediateDominator();
     if (!pred || !pred->lastIns()->isTest())
         return nullptr;
 
     MTest *test = pred->lastIns()->toTest();
 
     // True branch may only dominate one edge of MPhi.
-    if (test->ifTrue()->dominates(block()->getPredecessor(0)) &&
+    if (test->ifTrue()->dominates(block()->getPredecessor(0)) ==
         test->ifTrue()->dominates(block()->getPredecessor(1)))
     {
         return nullptr;
     }
 
     // False branch may only dominate one edge of MPhi.
-    if (test->ifFalse()->dominates(block()->getPredecessor(0)) &&
+    if (test->ifFalse()->dominates(block()->getPredecessor(0)) ==
         test->ifFalse()->dominates(block()->getPredecessor(1)))
     {
         return nullptr;
     }
 
     // True and false branch must dominate different edges of MPhi.
     if (test->ifTrue()->dominates(block()->getPredecessor(0)) ==
         test->ifFalse()->dominates(block()->getPredecessor(0)))
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -10,16 +10,17 @@
  */
 
 #ifndef jit_MIR_h
 #define jit_MIR_h
 
 #include "mozilla/Array.h"
 #include "mozilla/DebugOnly.h"
 
+#include "jit/AtomicOp.h"
 #include "jit/FixedList.h"
 #include "jit/InlineList.h"
 #include "jit/IonAllocPolicy.h"
 #include "jit/IonMacroAssembler.h"
 #include "jit/MOpcodes.h"
 #include "jit/TypedObjectPrediction.h"
 #include "jit/TypePolicy.h"
 #include "vm/ArrayObject.h"
@@ -8032,59 +8033,85 @@ class MArrayJoin
         return true;
     }
     virtual AliasSet getAliasSet() const {
         return AliasSet::Load(AliasSet::Element | AliasSet::ObjectFields);
     }
     MDefinition *foldsTo(TempAllocator &alloc);
 };
 
+// See comments above MMemoryBarrier, below.
+
+enum MemoryBarrierRequirement
+{
+    DoesNotRequireMemoryBarrier,
+    DoesRequireMemoryBarrier
+};
+
+// Also see comments above MMemoryBarrier, below.
+
 class MLoadTypedArrayElement
   : public MBinaryInstruction
 {
     Scalar::Type arrayType_;
+    bool requiresBarrier_;
 
     MLoadTypedArrayElement(MDefinition *elements, MDefinition *index,
-                           Scalar::Type arrayType)
-      : MBinaryInstruction(elements, index), arrayType_(arrayType)
+                           Scalar::Type arrayType, MemoryBarrierRequirement requiresBarrier)
+      : MBinaryInstruction(elements, index),
+        arrayType_(arrayType),
+        requiresBarrier_(requiresBarrier == DoesRequireMemoryBarrier)
     {
         setResultType(MIRType_Value);
-        setMovable();
+        if (requiresBarrier_)
+            setGuard();         // Not removable or movable
+        else
+            setMovable();
         MOZ_ASSERT(elements->type() == MIRType_Elements);
         MOZ_ASSERT(index->type() == MIRType_Int32);
         MOZ_ASSERT(arrayType >= 0 && arrayType < Scalar::TypeMax);
     }
 
   public:
     INSTRUCTION_HEADER(LoadTypedArrayElement)
 
     static MLoadTypedArrayElement *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index,
-                                       Scalar::Type arrayType)
-    {
-        return new(alloc) MLoadTypedArrayElement(elements, index, arrayType);
+                                       Scalar::Type arrayType,
+                                       MemoryBarrierRequirement requiresBarrier=DoesNotRequireMemoryBarrier)
+    {
+        return new(alloc) MLoadTypedArrayElement(elements, index, arrayType, requiresBarrier);
     }
 
     Scalar::Type arrayType() const {
         return arrayType_;
     }
     bool fallible() const {
         // Bailout if the result does not fit in an int32.
         return arrayType_ == Scalar::Uint32 && type() == MIRType_Int32;
     }
+    bool requiresMemoryBarrier() const {
+        return requiresBarrier_;
+    }
     MDefinition *elements() const {
         return getOperand(0);
     }
     MDefinition *index() const {
         return getOperand(1);
     }
     AliasSet getAliasSet() const {
+        // When a barrier is needed make the instruction effectful by
+        // giving it a "store" effect.
+        if (requiresBarrier_)
+            return AliasSet::Store(AliasSet::TypedArrayElement);
         return AliasSet::Load(AliasSet::TypedArrayElement);
     }
 
     bool congruentTo(const MDefinition *ins) const {
+        if (requiresBarrier_)
+            return false;
         if (!ins->isLoadTypedArrayElement())
             return false;
         const MLoadTypedArrayElement *other = ins->toLoadTypedArrayElement();
         if (arrayType_ != other->arrayType_)
             return false;
         return congruentIfOperandsEqual(other);
     }
 
@@ -8209,37 +8236,46 @@ class MLoadTypedArrayElementStatic
     bool canProduceFloat32() const { return viewType() == Scalar::Float32; }
 };
 
 class MStoreTypedArrayElement
   : public MTernaryInstruction,
     public StoreTypedArrayPolicy::Data
 {
     Scalar::Type arrayType_;
+    bool requiresBarrier_;
 
     // See note in MStoreElementCommon.
     bool racy_;
 
     MStoreTypedArrayElement(MDefinition *elements, MDefinition *index, MDefinition *value,
-                            Scalar::Type arrayType)
-      : MTernaryInstruction(elements, index, value), arrayType_(arrayType), racy_(false)
-    {
-        setMovable();
+                            Scalar::Type arrayType, MemoryBarrierRequirement requiresBarrier)
+      : MTernaryInstruction(elements, index, value),
+        arrayType_(arrayType),
+        requiresBarrier_(requiresBarrier == DoesRequireMemoryBarrier),
+        racy_(false)
+    {
+        if (requiresBarrier_)
+            setGuard();         // Not removable or movable
+        else
+            setMovable();
         MOZ_ASSERT(elements->type() == MIRType_Elements);
         MOZ_ASSERT(index->type() == MIRType_Int32);
         MOZ_ASSERT(arrayType >= 0 && arrayType < Scalar::TypeMax);
     }
 
   public:
     INSTRUCTION_HEADER(StoreTypedArrayElement)
 
     static MStoreTypedArrayElement *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index,
-                                        MDefinition *value, Scalar::Type arrayType)
-    {
-        return new(alloc) MStoreTypedArrayElement(elements, index, value, arrayType);
+                                        MDefinition *value, Scalar::Type arrayType,
+                                        MemoryBarrierRequirement requiresBarrier = DoesNotRequireMemoryBarrier)
+    {
+        return new(alloc) MStoreTypedArrayElement(elements, index, value, arrayType,
+                                                  requiresBarrier);
     }
 
     Scalar::Type arrayType() const {
         return arrayType_;
     }
     bool isByteArray() const {
         return arrayType_ == Scalar::Int8 ||
                arrayType_ == Scalar::Uint8 ||
@@ -8256,16 +8292,19 @@ class MStoreTypedArrayElement
         return getOperand(1);
     }
     MDefinition *value() const {
         return getOperand(2);
     }
     AliasSet getAliasSet() const {
         return AliasSet::Store(AliasSet::TypedArrayElement);
     }
+    bool requiresMemoryBarrier() const {
+        return requiresBarrier_;
+    }
     bool racy() const {
         return racy_;
     }
     void setRacy() {
         racy_ = true;
     }
     TruncateKind operandTruncateKind(size_t index) const;
 
@@ -11447,16 +11486,169 @@ class MRecompileCheck : public MNullaryI
         return increaseWarmUpCounter_;
     }
 
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
+// All barriered operations - MMemoryBarrier, MCompareExchangeTypedArrayElement,
+// and MAtomicTypedArrayElementBinop, as well as MLoadTypedArrayElement and
+// MStoreTypedArrayElement when they are marked as requiring a memory barrer - have
+// the following attributes:
+//
+// - Not movable
+// - Not removable
+// - Not congruent with any other instruction
+// - Effectful (they alias every TypedArray store)
+//
+// The intended effect of those constraints is to prevent all loads
+// and stores preceding the barriered operation from being moved to
+// after the barriered operation, and vice versa, and to prevent the
+// barriered operation from being removed or hoisted.
+
+class MMemoryBarrier
+  : public MNullaryInstruction
+{
+    // The type is a combination of the memory barrier types in AtomicOp.h.
+    const int type_;
+
+    explicit MMemoryBarrier(int type)
+      : type_(type)
+    {
+        MOZ_ASSERT((type_ & ~MembarAllbits) == 0);
+        setGuard();             // Not removable
+    }
+
+  public:
+    INSTRUCTION_HEADER(MemoryBarrier);
+
+    static MMemoryBarrier *New(TempAllocator &alloc, int type=MembarFull) {
+        return new(alloc) MMemoryBarrier(type);
+    }
+    int type() const {
+        return type_;
+    }
+
+    AliasSet getAliasSet() const {
+        return AliasSet::Store(AliasSet::TypedArrayElement);
+    }
+};
+
+class MCompareExchangeTypedArrayElement
+  : public MAryInstruction<4>,
+    public MixPolicy< MixPolicy<ObjectPolicy<0>, IntPolicy<1> >, MixPolicy<IntPolicy<2>, IntPolicy<3> > >
+{
+    Scalar::Type arrayType_;
+
+    explicit MCompareExchangeTypedArrayElement(MDefinition *elements, MDefinition *index,
+                                               Scalar::Type arrayType, MDefinition *oldval,
+                                               MDefinition *newval)
+      : arrayType_(arrayType)
+    {
+        initOperand(0, elements);
+        initOperand(1, index);
+        initOperand(2, oldval);
+        initOperand(3, newval);
+        setGuard();             // Not removable
+    }
+
+  public:
+    INSTRUCTION_HEADER(CompareExchangeTypedArrayElement);
+
+    static MCompareExchangeTypedArrayElement *New(TempAllocator &alloc, MDefinition *elements,
+                                                  MDefinition *index, Scalar::Type arrayType,
+                                                  MDefinition *oldval, MDefinition *newval)
+    {
+        return new(alloc) MCompareExchangeTypedArrayElement(elements, index, arrayType, oldval, newval);
+    }
+    bool isByteArray() const {
+        return (arrayType_ == Scalar::Int8 ||
+                arrayType_ == Scalar::Uint8 ||
+                arrayType_ == Scalar::Uint8Clamped);
+    }
+    MDefinition *elements() {
+        return getOperand(0);
+    }
+    MDefinition *index() {
+        return getOperand(1);
+    }
+    MDefinition *oldval() {
+        return getOperand(2);
+    }
+    int oldvalOperand() {
+        return 2;
+    }
+    MDefinition *newval() {
+        return getOperand(3);
+    }
+    Scalar::Type arrayType() const {
+        return arrayType_;
+    }
+    AliasSet getAliasSet() const {
+        return AliasSet::Store(AliasSet::TypedArrayElement);
+    }
+};
+
+class MAtomicTypedArrayElementBinop
+    : public MAryInstruction<3>,
+      public Mix3Policy< ObjectPolicy<0>, IntPolicy<1>, IntPolicy<2> >
+{
+  private:
+    AtomicOp op_;
+    Scalar::Type arrayType_;
+
+  protected:
+    explicit MAtomicTypedArrayElementBinop(AtomicOp op, MDefinition *elements, MDefinition *index,
+                                           Scalar::Type arrayType, MDefinition *value)
+      : op_(op),
+        arrayType_(arrayType)
+    {
+        initOperand(0, elements);
+        initOperand(1, index);
+        initOperand(2, value);
+        setGuard();             // Not removable
+    }
+
+  public:
+    INSTRUCTION_HEADER(AtomicTypedArrayElementBinop);
+
+    static MAtomicTypedArrayElementBinop *New(TempAllocator &alloc, AtomicOp op,
+                                              MDefinition *elements, MDefinition *index,
+                                              Scalar::Type arrayType, MDefinition *value)
+    {
+        return new(alloc) MAtomicTypedArrayElementBinop(op, elements, index, arrayType, value);
+    }
+
+    bool isByteArray() const {
+        return (arrayType_ == Scalar::Int8 ||
+                arrayType_ == Scalar::Uint8 ||
+                arrayType_ == Scalar::Uint8Clamped);
+    }
+    AtomicOp operation() const {
+        return op_;
+    }
+    Scalar::Type arrayType() const {
+        return arrayType_;
+    }
+    MDefinition *elements() {
+        return getOperand(0);
+    }
+    MDefinition *index() {
+        return getOperand(1);
+    }
+    MDefinition *value() {
+        return getOperand(2);
+    }
+    AliasSet getAliasSet() const {
+        return AliasSet::Store(AliasSet::TypedArrayElement);
+    }
+};
+
 class MAsmJSNeg : public MUnaryInstruction
 {
     MAsmJSNeg(MDefinition *op, MIRType type)
       : MUnaryInstruction(op)
     {
         setResultType(type);
         setMovable();
     }
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -179,16 +179,18 @@ namespace jit {
     _(ArrayConcat)                                                          \
     _(ArrayJoin)                                                            \
     _(LoadTypedArrayElement)                                                \
     _(LoadTypedArrayElementHole)                                            \
     _(LoadTypedArrayElementStatic)                                          \
     _(StoreTypedArrayElement)                                               \
     _(StoreTypedArrayElementHole)                                           \
     _(StoreTypedArrayElementStatic)                                         \
+    _(CompareExchangeTypedArrayElement)                                     \
+    _(AtomicTypedArrayElementBinop)                                         \
     _(EffectiveAddress)                                                     \
     _(ClampToUint8)                                                         \
     _(LoadFixedSlot)                                                        \
     _(StoreFixedSlot)                                                       \
     _(CallGetProperty)                                                      \
     _(GetNameCache)                                                         \
     _(CallGetIntrinsicValue)                                                \
     _(CallsiteCloneCache)                                                   \
@@ -246,16 +248,17 @@ namespace jit {
     _(NewDerivedTypedObject)                                                \
     _(LambdaPar)                                                            \
     _(RestPar)                                                              \
     _(ForkJoinContext)                                                      \
     _(ForkJoinGetSlice)                                                     \
     _(GuardThreadExclusive)                                                 \
     _(InterruptCheckPar)                                                    \
     _(RecompileCheck)                                                       \
+    _(MemoryBarrier)                                                        \
     _(UnknownValue)                                                         \
     _(LexicalCheck)                                                         \
     _(ThrowUninitializedLexical)
 
 // Forward declarations of MIR types.
 #define FORWARD_DECLARE(op) class M##op;
  MIR_OPCODE_LIST(FORWARD_DECLARE)
 #undef FORWARD_DECLARE
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -343,21 +343,24 @@ class ParallelSafetyVisitor : public MDe
     UNSAFE_OP(AsmJSLoadFuncPtr)
     UNSAFE_OP(AsmJSLoadFFIFunc)
     UNSAFE_OP(AsmJSReturn)
     UNSAFE_OP(AsmJSVoidReturn)
     UNSAFE_OP(AsmJSPassStackArg)
     UNSAFE_OP(AsmJSParameter)
     UNSAFE_OP(AsmJSCall)
     DROP_OP(RecompileCheck)
+    UNSAFE_OP(CompareExchangeTypedArrayElement)
+    UNSAFE_OP(AtomicTypedArrayElementBinop)
+    UNSAFE_OP(MemoryBarrier)
     UNSAFE_OP(UnknownValue)
     UNSAFE_OP(LexicalCheck)
     UNSAFE_OP(ThrowUninitializedLexical)
 
-    // It looks like this could easily be made safe:
+    // It looks like these could easily be made safe:
     UNSAFE_OP(ConvertElementsToDoubles)
     UNSAFE_OP(MaybeCopyElementsForWrite)
 };
 
 static void
 TransplantResumePoint(MInstruction *oldInstruction, MInstruction *replacementInstruction)
 {
     MOZ_ASSERT(!oldInstruction->isDiscarded());
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -419,16 +419,17 @@ IntPolicy<Op>::staticAdjustInputs(TempAl
     def->replaceOperand(Op, replace);
 
     return replace->typePolicy()->adjustInputs(alloc, replace);
 }
 
 template bool IntPolicy<0>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
 template bool IntPolicy<1>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
 template bool IntPolicy<2>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
+template bool IntPolicy<3>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def);
 
 template <unsigned Op>
 bool
 ConvertToInt32Policy<Op>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def)
 {
     MDefinition *in = def->getOperand(Op);
     if (in->type() == MIRType_Int32)
         return true;
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -2242,8 +2242,29 @@ CodeGeneratorARM::visitForkJoinGetSlice(
     MOZ_CRASH("NYI");
 }
 
 JitCode *
 JitRuntime::generateForkJoinGetSliceStub(JSContext *cx)
 {
     MOZ_CRASH("NYI");
 }
+
+void
+CodeGeneratorARM::memoryBarrier(int barrier)
+{
+    // On ARMv6 the optional argument (BarrierST, etc) is ignored.
+    if (barrier == (MembarStoreStore|MembarSynchronizing))
+        masm.ma_dsb(masm.BarrierST);
+    else if (barrier & MembarSynchronizing)
+        masm.ma_dsb();
+    else if (barrier == MembarStoreStore)
+        masm.ma_dmb(masm.BarrierST);
+    else if (barrier)
+        masm.ma_dmb();
+}
+
+bool
+CodeGeneratorARM::visitMemoryBarrier(LMemoryBarrier *ins)
+{
+    memoryBarrier(ins->type());
+    return true;
+}
--- a/js/src/jit/arm/CodeGenerator-arm.h
+++ b/js/src/jit/arm/CodeGenerator-arm.h
@@ -170,16 +170,18 @@ class CodeGeneratorARM : public CodeGene
     // Functions for LTestVAndBranch.
     Register splitTagForTest(const ValueOperand &value);
 
     bool divICommon(MDiv *mir, Register lhs, Register rhs, Register output, LSnapshot *snapshot,
                     Label &done);
     bool modICommon(MMod *mir, Register lhs, Register rhs, Register output, LSnapshot *snapshot,
                     Label &done);
 
+    void memoryBarrier(int barrier);
+
   public:
     CodeGeneratorARM(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm);
 
   public:
     bool visitBox(LBox *box);
     bool visitBoxFloatingPoint(LBoxFloatingPoint *box);
     bool visitUnbox(LUnbox *unbox);
     bool visitValue(LValue *value);
@@ -201,16 +203,18 @@ class CodeGeneratorARM : public CodeGene
     bool visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins);
     bool visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins);
     bool visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr *ins);
     bool visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc *ins);
     bool visitAsmJSPassStackArg(LAsmJSPassStackArg *ins);
 
     bool visitForkJoinGetSlice(LForkJoinGetSlice *ins);
 
+    bool visitMemoryBarrier(LMemoryBarrier *ins);
+
     bool generateInvalidateEpilogue();
 
   protected:
     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
@@ -565,9 +565,74 @@ LIRGeneratorARM::visitSimdSplatX4(MSimdS
 }
 
 bool
 LIRGeneratorARM::visitSimdValueX4(MSimdValueX4 *ins)
 {
     MOZ_CRASH("NYI");
 }
 
-//__aeabi_uidiv
+bool
+LIRGeneratorARM::visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop *ins)
+{
+    MOZ_ASSERT(ins->arrayType() != Scalar::Uint8Clamped);
+    MOZ_ASSERT(ins->arrayType() != Scalar::Float32);
+    MOZ_ASSERT(ins->arrayType() != Scalar::Float64);
+
+    MOZ_ASSERT(ins->elements()->type() == MIRType_Elements);
+    MOZ_ASSERT(ins->index()->type() == MIRType_Int32);
+
+    const LUse elements = useRegister(ins->elements());
+    const LAllocation index = useRegisterOrConstant(ins->index());
+
+    // For most operations we don't need any temps because there are
+    // enough scratch registers.  tempDef2 is never needed on ARM.
+    //
+    // For a Uint32Array with a known double result we need a temp for
+    // the intermediate output, this is tempDef1.
+    //
+    // Optimization opportunity (bug 1077317): We can do better by
+    // allowing 'value' to remain as an imm32 if it is small enough to
+    // fit in an instruction.
+
+    LDefinition tempDef1 = LDefinition::BogusTemp();
+    LDefinition tempDef2 = LDefinition::BogusTemp();
+
+    const LAllocation value = useRegister(ins->value());
+    if (ins->arrayType() == Scalar::Uint32 && IsFloatingPointType(ins->type()))
+        tempDef1 = temp();
+
+    LAtomicTypedArrayElementBinop *lir =
+        new(alloc()) LAtomicTypedArrayElementBinop(elements, index, value, tempDef1, tempDef2);
+
+    return define(lir, ins);
+}
+
+bool
+LIRGeneratorARM::visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement *ins)
+{
+    MOZ_ASSERT(ins->arrayType() != Scalar::Float32);
+    MOZ_ASSERT(ins->arrayType() != Scalar::Float64);
+
+    MOZ_ASSERT(ins->elements()->type() == MIRType_Elements);
+    MOZ_ASSERT(ins->index()->type() == MIRType_Int32);
+
+    const LUse elements = useRegister(ins->elements());
+    const LAllocation index = useRegisterOrConstant(ins->index());
+
+    // If the target is a floating register then we need a temp at the
+    // CodeGenerator level for creating the result.
+    //
+    // Optimization opportunity (bug 1077317): We could do better by
+    // allowing oldval to remain an immediate, if it is small enough
+    // to fit in an instruction.
+
+    const LAllocation newval = useRegister(ins->newval());
+    const LAllocation oldval = useRegister(ins->oldval());
+    LDefinition tempDef = LDefinition::BogusTemp();
+    if (ins->arrayType() == Scalar::Uint32 && IsFloatingPointType(ins->type()))
+        tempDef = temp();
+
+    LCompareExchangeTypedArrayElement *lir =
+        new(alloc()) LCompareExchangeTypedArrayElement(elements, index, oldval, newval, tempDef);
+
+    return define(lir, ins);
+}
--- a/js/src/jit/arm/Lowering-arm.h
+++ b/js/src/jit/arm/Lowering-arm.h
@@ -101,16 +101,18 @@ class LIRGeneratorARM : public LIRGenera
     bool visitAsmJSLoadHeap(MAsmJSLoadHeap *ins);
     bool visitAsmJSStoreHeap(MAsmJSStoreHeap *ins);
     bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins);
     bool visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins);
     bool visitForkJoinGetSlice(MForkJoinGetSlice *ins);
     bool visitSimdTernaryBitwise(MSimdTernaryBitwise *ins);
     bool visitSimdSplatX4(MSimdSplatX4 *ins);
     bool visitSimdValueX4(MSimdValueX4 *ins);
+    bool visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement *ins);
+    bool visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop *ins);
 };
 
 typedef LIRGeneratorARM LIRGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_arm_Lowering_arm_h */
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -1800,16 +1800,25 @@ MacroAssemblerARMCompat::buildOOLFakeExi
 
     Push(Imm32(descriptor)); // descriptor_
     Push(ImmPtr(fakeReturnAddr));
 
     return true;
 }
 
 void
+MacroAssemblerARMCompat::callWithExitFrame(Label *target)
+{
+    uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
+    Push(Imm32(descriptor)); // descriptor
+
+    ma_callIonHalfPush(target);
+}
+
+void
 MacroAssemblerARMCompat::callWithExitFrame(JitCode *target)
 {
     uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
     Push(Imm32(descriptor)); // descriptor
 
     addPendingJump(m_buffer.nextOffset(), ImmPtr(target->raw()), Relocation::JITCODE);
     RelocStyle rs;
     if (HasMOVWT())
@@ -3715,16 +3724,27 @@ MacroAssemblerARM::ma_callIonHalfPush(co
     // the stack before the call, when we return the pc is poped and the stack
     // is restored to its unaligned state.
     AutoForbidPools afp(this, 2);
     ma_push(pc);
     as_blx(r);
 }
 
 void
+MacroAssemblerARM::ma_callIonHalfPush(Label *label)
+{
+    // The stack is unaligned by 4 bytes. We push the pc to the stack to align
+    // the stack before the call, when we return the pc is poped and the stack
+    // is restored to its unaligned state.
+    AutoForbidPools afp(this, 2);
+    ma_push(pc);
+    as_bl(label, Always);
+}
+
+void
 MacroAssemblerARM::ma_call(ImmPtr dest)
 {
     RelocStyle rs;
     if (HasMOVWT())
         rs = L_MOVWT;
     else
         rs = L_LDR;
 
@@ -4685,9 +4705,285 @@ MacroAssemblerARMCompat::branchValueIsNu
     Label done;
 
     branchTestObject(Assembler::NotEqual, value, cond == Assembler::Equal ? &done : label);
     branchPtrInNurseryRange(cond, value.payloadReg(), temp, label);
 
     bind(&done);
 }
 
+namespace js {
+namespace jit {
+
+template<>
+Register
+MacroAssemblerARMCompat::computePointer<BaseIndex>(const BaseIndex &src, Register r)
+{
+    Register base = src.base;
+    Register index = src.index;
+    uint32_t scale = Imm32::ShiftOf(src.scale).value;
+    int32_t offset = src.offset;
+    as_add(r, base, lsl(index, scale));
+    if (offset != 0)
+        ma_add(r, Imm32(offset), r);
+    return r;
+}
+
+template<>
+Register
+MacroAssemblerARMCompat::computePointer<Address>(const Address &src, Register r)
+{
+    if (src.offset == 0)
+        return src.base;
+    ma_add(src.base, Imm32(src.offset), r);
+    return r;
+}
+
+} // namespace jit
+} // namespace js
+
+template<typename T>
+void
+MacroAssemblerARMCompat::compareExchange(int nbytes, bool signExtend, const T &mem,
+                                         Register oldval, Register newval, Register output)
+{
+    // If LDREXB/H and STREXB/H are not available we use the
+    // word-width operations with read-modify-add.  That does not
+    // abstract well, so fork.
+    //
+    // Bug 1077321: We may further optimize for ARMv8 here.
+    if (nbytes < 4 && !HasLDSTREXBHD())
+        compareExchangeARMv6(nbytes, signExtend, mem, oldval, newval, output);
+    else
+        compareExchangeARMv7(nbytes, signExtend, mem, oldval, newval, output);
+}
+
+// General algorithm:
+//
+//     ...    ptr, <addr>         ; compute address of item
+//     dmb
+// L0  ldrex* output, [ptr]
+//     sxt*   output, output, 0   ; sign-extend if applicable
+//     *xt*   tmp, oldval, 0      ; sign-extend or zero-extend if applicable
+//     cmp    output, tmp
+//     bne    L1                  ; failed - values are different
+//     strex* tmp, newval, [ptr]
+//     cmp    tmp, 1
+//     beq    L0                  ; failed - location is dirty, retry
+// L1  dmb
+//
+// Discussion here:  http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html.
+// However note that that discussion uses 'isb' as the trailing fence.
+// I've not quite figured out why, and I've gone with dmb here which
+// is safe.  Also see the LLVM source, which uses 'dmb ish' generally.
+// (Apple's Swift CPU apparently handles ish in a non-default, faster
+// way.)
+
+template<typename T>
+void
+MacroAssemblerARMCompat::compareExchangeARMv7(int nbytes, bool signExtend, const T &mem,
+                                              Register oldval, Register newval, Register output)
+{
+    Label Lagain;
+    Label Ldone;
+    ma_dmb(BarrierST);
+    Register ptr = computePointer(mem, secondScratchReg_);
+    bind(&Lagain);
+    switch (nbytes) {
+      case 1:
+        as_ldrexb(output, ptr);
+        if (signExtend) {
+            as_sxtb(output, output, 0);
+            as_sxtb(ScratchRegister, oldval, 0);
+        } else {
+            as_uxtb(ScratchRegister, oldval, 0);
+        }
+        break;
+      case 2:
+        as_ldrexh(output, ptr);
+        if (signExtend) {
+            as_sxth(output, output, 0);
+            as_sxth(ScratchRegister, oldval, 0);
+        } else {
+            as_uxth(ScratchRegister, oldval, 0);
+        }
+        break;
+      case 4:
+        MOZ_ASSERT(!signExtend);
+        as_ldrex(output, ptr);
+        break;
+    }
+    if (nbytes < 4)
+        as_cmp(output, O2Reg(ScratchRegister));
+    else
+        as_cmp(output, O2Reg(oldval));
+    as_b(&Ldone, NotEqual);
+    switch (nbytes) {
+      case 1:
+        as_strexb(ScratchRegister, newval, ptr);
+        break;
+      case 2:
+        as_strexh(ScratchRegister, newval, ptr);
+        break;
+      case 4:
+        as_strex(ScratchRegister, newval, ptr);
+        break;
+    }
+    as_cmp(ScratchRegister, Imm8(1));
+    as_b(&Lagain, Equal);
+    bind(&Ldone);
+    ma_dmb();
+}
+
+template<typename T>
+void
+MacroAssemblerARMCompat::compareExchangeARMv6(int nbytes, bool signExtend, const T &mem,
+                                              Register oldval, Register newval, Register output)
+{
+    // Bug 1077318: Must use read-modify-write with LDREX / STREX.
+    MOZ_ASSERT(nbytes == 1 || nbytes == 2);
+    MOZ_CRASH("NYI");
+}
+
+template void
+js::jit::MacroAssemblerARMCompat::compareExchange(int nbytes, bool signExtend,
+                                                  const Address &address, Register oldval,
+                                                  Register newval, Register output);
+template void
+js::jit::MacroAssemblerARMCompat::compareExchange(int nbytes, bool signExtend,
+                                                  const BaseIndex &address, Register oldval,
+                                                  Register newval, Register output);
+
+template<typename T>
+void
+MacroAssemblerARMCompat::atomicFetchOp(int nbytes, bool signExtend, AtomicOp op, const Imm32 &value,
+                                       const T &mem, Register temp, Register output)
+{
+    // The Imm32 value case is not needed yet because lowering always
+    // forces the value into a register at present (bug 1077317).  But
+    // the method must be present for the platform-independent code to
+    // link.
+    MOZ_CRASH("Feature NYI");
+}
+
+// General algorithm:
+//
+//     ...    ptr, <addr>         ; compute address of item
+//     dmb
+// L0  ldrex* output, [ptr]
+//     sxt*   output, output, 0   ; sign-extend if applicable
+//     OP     tmp, output, value  ; compute value to store
+//     strex* tmp, tmp, [ptr]
+//     cmp    tmp, 1
+//     beq    L0                  ; failed - location is dirty, retry
+//     dmb                        ; ordering barrier required
+//
+// Also see notes above at compareExchange re the barrier strategy.
+//
+// Observe that the value being operated into the memory element need
+// not be sign-extended because no OP will make use of bits to the
+// left of the bits indicated by the width of the element, and neither
+// output nor the bits stored are affected by OP.
+
+template<typename T>
+void
+MacroAssemblerARMCompat::atomicFetchOp(int nbytes, bool signExtend, AtomicOp op,
+                                       const Register &value, const T &mem, Register temp,
+                                       Register output)
+{
+    // Fork for non-word operations on ARMv6.
+    //
+    // Bug 1077321: We may further optimize for ARMv8 here.
+    if (nbytes < 4 && !HasLDSTREXBHD())
+        atomicFetchOpARMv6(nbytes, signExtend, op, value, mem, temp, output);
+    else {
+        MOZ_ASSERT(temp == InvalidReg);
+        atomicFetchOpARMv7(nbytes, signExtend, op, value, mem, output);
+    }
+}
+
+template<typename T>
+void
+MacroAssemblerARMCompat::atomicFetchOpARMv7(int nbytes, bool signExtend, AtomicOp op,
+                                            const Register &value, const T &mem, Register output)
+{
+    Label Lagain;
+    Register ptr = computePointer(mem, secondScratchReg_);
+    ma_dmb();
+    bind(&Lagain);
+    switch (nbytes) {
+      case 1:
+        as_ldrexb(output, ptr);
+        if (signExtend)
+            as_sxtb(output, output, 0);
+        break;
+      case 2:
+        as_ldrexh(output, ptr);
+        if (signExtend)
+            as_sxth(output, output, 0);
+        break;
+      case 4:
+        MOZ_ASSERT(!signExtend);
+        as_ldrex(output, ptr);
+        break;
+    }
+    switch (op) {
+      case AtomicFetchAddOp:
+        as_add(ScratchRegister, output, O2Reg(value));
+        break;
+      case AtomicFetchSubOp:
+        as_sub(ScratchRegister, output, O2Reg(value));
+        break;
+      case AtomicFetchAndOp:
+        as_and(ScratchRegister, output, O2Reg(value));
+        break;
+      case AtomicFetchOrOp:
+        as_orr(ScratchRegister, output, O2Reg(value));
+        break;
+      case AtomicFetchXorOp:
+        as_eor(ScratchRegister, output, O2Reg(value));
+        break;
+    }
+    switch (nbytes) {
+      case 1:
+        as_strexb(ScratchRegister, ScratchRegister, ptr);
+        break;
+      case 2:
+        as_strexh(ScratchRegister, ScratchRegister, ptr);
+        break;
+      case 4:
+        as_strex(ScratchRegister, ScratchRegister, ptr);
+        break;
+    }
+    as_cmp(ScratchRegister, Imm8(1));
+    as_b(&Lagain, Equal);
+    ma_dmb();
+}
+
+template<typename T>
+void
+MacroAssemblerARMCompat::atomicFetchOpARMv6(int nbytes, bool signExtend, AtomicOp op,
+                                            const Register &value, const T &mem, Register temp,
+                                            Register output)
+{
+    // Bug 1077318: Must use read-modify-write with LDREX / STREX.
+    MOZ_ASSERT(nbytes == 1 || nbytes == 2);
+    MOZ_CRASH("NYI");
+}
+
+template void
+js::jit::MacroAssemblerARMCompat::atomicFetchOp(int nbytes, bool signExtend, AtomicOp op,
+                                                const Imm32 &value, const Address &mem,
+                                                Register temp, Register output);
+template void
+js::jit::MacroAssemblerARMCompat::atomicFetchOp(int nbytes, bool signExtend, AtomicOp op,
+                                                const Imm32 &value, const BaseIndex &mem,
+                                                Register temp, Register output);
+template void
+js::jit::MacroAssemblerARMCompat::atomicFetchOp(int nbytes, bool signExtend, AtomicOp op,
+                                                const Register &value, const Address &mem,
+                                                Register temp, Register output);
+template void
+js::jit::MacroAssemblerARMCompat::atomicFetchOp(int nbytes, bool signExtend, AtomicOp op,
+                                                const Register &value, const BaseIndex &mem,
+                                                Register temp, Register output);
+
 #endif
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -7,16 +7,17 @@
 #ifndef jit_arm_MacroAssembler_arm_h
 #define jit_arm_MacroAssembler_arm_h
 
 #include "mozilla/DebugOnly.h"
 
 #include "jsopcode.h"
 
 #include "jit/arm/Assembler-arm.h"
+#include "jit/AtomicOp.h"
 #include "jit/IonCaches.h"
 #include "jit/IonFrames.h"
 #include "jit/MoveResolver.h"
 
 using mozilla::DebugOnly;
 
 namespace js {
 namespace jit {
@@ -401,16 +402,19 @@ class MacroAssemblerARM : public Assembl
     // Calls an Ion function, assumes that the stack is untouched (8 byte
     // aligned).
     void ma_callIon(const Register reg);
     // Calls an Ion function, assuming that sp has already been decremented.
     void ma_callIonNoPush(const Register reg);
     // Calls an ion function, assuming that the stack is currently not 8 byte
     // aligned.
     void ma_callIonHalfPush(const Register reg);
+    // Calls an ion function, assuming that the stack is currently not 8 byte
+    // aligned.
+    void ma_callIonHalfPush(Label *label);
 
     void ma_call(ImmPtr dest);
 
     // Float registers can only be loaded/stored in continuous runs when using
     // vstm/vldm. This function breaks set into continuous runs and loads/stores
     // them at [rm]. rm will be modified and left in a state logically suitable
     // for the next load/store. Returns the offset from [dm] for the logical
     // next load/store.
@@ -1270,16 +1274,17 @@ class MacroAssemblerARMCompat : public M
     void setFramePushed(uint32_t framePushed) {
         framePushed_ = framePushed;
     }
 
     // Builds an exit frame on the stack, with a return address to an internal
     // non-function. Returns offset to be passed to markSafepointAt().
     bool buildFakeExitFrame(Register scratch, uint32_t *offset);
 
+    void callWithExitFrame(Label *target);
     void callWithExitFrame(JitCode *target);
     void callWithExitFrame(JitCode *target, Register dynStack);
 
     // Makes an Ion call using the only two methods that it is sane for
     // independent code to make a call.
     void callIon(Register callee);
     void callIonFromAsmJS(Register callee);
 
@@ -1415,16 +1420,182 @@ class MacroAssemblerARMCompat : public M
     }
     void storeFloat32(FloatRegister src, BaseIndex addr) {
         // Harder cases not handled yet.
         MOZ_ASSERT(addr.offset == 0);
         uint32_t scale = Imm32::ShiftOf(addr.scale).value;
         ma_vstr(VFPRegister(src).singleOverlay(), addr.base, addr.index, scale);
     }
 
+  private:
+    template<typename T>
+    Register computePointer(const T &src, Register r);
+
+    template<typename T>
+    void compareExchangeARMv6(int nbytes, bool signExtend, const T &mem, Register oldval,
+                              Register newval, Register output);
+
+    template<typename T>
+    void compareExchangeARMv7(int nbytes, bool signExtend, const T &mem, Register oldval,
+                              Register newval, Register output);
+
+    template<typename T>
+    void compareExchange(int nbytes, bool signExtend, const T &address, Register oldval,
+                         Register newval, Register output);
+
+    template<typename T>
+    void atomicFetchOpARMv6(int nbytes, bool signExtend, AtomicOp op, const Register &value,
+                            const T &mem, Register temp, Register output);
+
+    template<typename T>
+    void atomicFetchOpARMv7(int nbytes, bool signExtend, AtomicOp op, const Register &value,
+                            const T &mem, Register output);
+
+    template<typename T>
+    void atomicFetchOp(int nbytes, bool signExtend, AtomicOp op, const Imm32 &value,
+                       const T &address, Register temp, Register output);
+
+    template<typename T>
+    void atomicFetchOp(int nbytes, bool signExtend, AtomicOp op, const Register &value,
+                       const T &address, Register temp, Register output);
+
+  public:
+    // T in {Address,BaseIndex}
+    // S in {Imm32,Register}
+
+    template<typename T>
+    void compareExchange8SignExtend(const T &mem, Register oldval, Register newval, Register output)
+    {
+        compareExchange(1, true, mem, oldval, newval, output);
+    }
+    template<typename T>
+    void compareExchange8ZeroExtend(const T &mem, Register oldval, Register newval, Register output)
+    {
+        compareExchange(1, false, mem, oldval, newval, output);
+    }
+    template<typename T>
+    void compareExchange16SignExtend(const T &mem, Register oldval, Register newval, Register output)
+    {
+        compareExchange(2, true, mem, oldval, newval, output);
+    }
+    template<typename T>
+    void compareExchange16ZeroExtend(const T &mem, Register oldval, Register newval, Register output)
+    {
+        compareExchange(2, false, mem, oldval, newval, output);
+    }
+    template<typename T>
+    void compareExchange32(const T &mem, Register oldval, Register newval, Register output)  {
+        compareExchange(4, false, mem, oldval, newval, output);
+    }
+
+    template<typename T, typename S>
+    void atomicFetchAdd8SignExtend(const S &value, const T &mem, Register temp, Register output) {
+        atomicFetchOp(1, true, AtomicFetchAddOp, value, mem, temp, output);
+    }
+    template<typename T, typename S>
+    void atomicFetchAdd8ZeroExtend(const S &value, const T &mem, Register temp, Register output) {
+        atomicFetchOp(1, false, AtomicFetchAddOp, value, mem, temp, output);
+    }
+    template<typename T, typename S>
+    void atomicFetchAdd16SignExtend(const S &value, const T &mem, Register temp, Register output) {
+        atomicFetchOp(2, true, AtomicFetchAddOp, value, mem, temp, output);
+    }
+    template<typename T, typename S>
+    void atomicFetchAdd16ZeroExtend(const S &value, const T &mem, Register temp, Register output) {
+        atomicFetchOp(2, false, AtomicFetchAddOp, value, mem, temp, output);
+    }
+    template<typename T, typename S>
+    void atomicFetchAdd32(const S &value, const T &mem, Register temp, Register output) {
+        atomicFetchOp(4, false, AtomicFetchAddOp, value, mem, temp, output);
+    }
+
+    template<typename T, typename S>
+    void atomicFetchSub8SignExtend(const S &value, const T &mem, Register temp, Register output) {
+        atomicFetchOp(1, true, AtomicFetchSubOp, value, mem, temp, output);
+    }
+    template<typename T, typename S>
+    void atomicFetchSub8ZeroExtend(const S &value, const T &mem, Register temp, Register output) {
+        atomicFetchOp(1, false, AtomicFetchSubOp, value, mem, temp, output);
+    }
+    template<typename T, typename S>
+    void atomicFetchSub16SignExtend(const S &value, const T &mem, Register temp, Register output) {
+        atomicFetchOp(2, true, AtomicFetchSubOp, value, mem, temp, output);
+    }
+    template<typename T, typename S>
+    void atomicFetchSub16ZeroExtend(const S &value, const T &mem, Register temp, Register output) {
+        atomicFetchOp(2, false, AtomicFetchSubOp, value, mem, temp, output);
+    }
+    template<typename T, typename S>
+    void atomicFetchSub32(const S &value, const T &mem, Register temp, Register output) {
+        atomicFetchOp(4, false, AtomicFetchSubOp, value, mem, temp, output);
+    }
+
+    template<typename T, typename S>
+    void atomicFetchAnd8SignExtend(const S &value, const T &mem, Register temp, Register output) {
+        atomicFetchOp(1, true, AtomicFetchAndOp, value, mem, temp, output);
+    }
+    template<typename T, typename S>
+    void atomicFetchAnd8ZeroExtend(const S &value, const T &mem, Register temp, Register output) {
+        atomicFetchOp(1, false, AtomicFetchAndOp, value, mem, temp, output);
+    }
+    template<typename T, typename S>
+    void atomicFetchAnd16SignExtend(const S &value, const T &mem, Register temp, Register output) {
+        atomicFetchOp(2, true, AtomicFetchAndOp, value, mem, temp, output);
+    }
+    template<typename T, typename S>
+    void atomicFetchAnd16ZeroExtend(const S &value, const T &mem, Register temp, Register output) {
+        atomicFetchOp(2, false, AtomicFetchAndOp, value, mem, temp, output);
+    }
+    template<typename T, typename S>
+    void atomicFetchAnd32(const S &value, const T &mem, Register temp, Register output) {
+        atomicFetchOp(4, false, AtomicFetchAndOp, value, mem, temp, output);
+    }
+
+    template<typename T, typename S>
+    void atomicFetchOr8SignExtend(const S &value, const T &mem, Register temp, Register output) {
+        atomicFetchOp(1, true, AtomicFetchOrOp, value, mem, temp, output);
+    }
+    template<typename T, typename S>
+    void atomicFetchOr8ZeroExtend(const S &value, const T &mem, Register temp, Register output) {
+        atomicFetchOp(1, false, AtomicFetchOrOp, value, mem, temp, output);
+    }
+    template<typename T, typename S>
+    void atomicFetchOr16SignExtend(const S &value, const T &mem, Register temp, Register output) {
+        atomicFetchOp(2, true, AtomicFetchOrOp, value, mem, temp, output);
+    }
+    template<typename T, typename S>
+    void atomicFetchOr16ZeroExtend(const S &value, const T &mem, Register temp, Register output) {
+        atomicFetchOp(2, false, AtomicFetchOrOp, value, mem, temp, output);
+    }
+    template<typename T, typename S>
+    void atomicFetchOr32(const S &value, const T &mem, Register temp, Register output) {
+        atomicFetchOp(4, false, AtomicFetchOrOp, value, mem, temp, output);
+    }
+
+    template<typename T, typename S>
+    void atomicFetchXor8SignExtend(const S &value, const T &mem, Register temp, Register output) {
+        atomicFetchOp(1, true, AtomicFetchXorOp, value, mem, temp, output);
+    }
+    template<typename T, typename S>
+    void atomicFetchXor8ZeroExtend(const S &value, const T &mem, Register temp, Register output) {
+        atomicFetchOp(1, false, AtomicFetchXorOp, value, mem, temp, output);
+    }
+    template<typename T, typename S>
+    void atomicFetchXor16SignExtend(const S &value, const T &mem, Register temp, Register output) {
+        atomicFetchOp(2, true, AtomicFetchXorOp, value, mem, temp, output);
+    }
+    template<typename T, typename S>
+    void atomicFetchXor16ZeroExtend(const S &value, const T &mem, Register temp, Register output) {
+        atomicFetchOp(2, false, AtomicFetchXorOp, value, mem, temp, output);
+    }
+    template<typename T, typename S>
+    void atomicFetchXor32(const S &value, const T &mem, Register temp, Register output) {
+        atomicFetchOp(4, false, AtomicFetchXorOp, value, mem, temp, output);
+    }
+
     void clampIntToUint8(Register reg) {
         // Look at (reg >> 8) if it is 0, then reg shouldn't be clamped if it is
         // <0, then we want to clamp to 0, otherwise, we wish to clamp to 255
         as_mov(ScratchRegister, asr(reg, 8), SetCond);
         ma_mov(Imm32(0xff), reg, NoSetCond, NotEqual);
         ma_mov(Imm32(0), reg, NoSetCond, Signed);
     }
 
--- a/js/src/jit/mips/MacroAssembler-mips.cpp
+++ b/js/src/jit/mips/MacroAssembler-mips.cpp
@@ -919,42 +919,53 @@ MacroAssemblerMIPS::ma_b(Address addr, I
 
 void
 MacroAssemblerMIPS::ma_b(Label *label, JumpKind jumpKind)
 {
     branchWithCode(getBranchCode(BranchIsJump), label, jumpKind);
 }
 
 void
-MacroAssemblerMIPS::ma_bal(Label *label, JumpKind jumpKind)
-{
-    branchWithCode(getBranchCode(BranchIsCall), label, jumpKind);
+MacroAssemblerMIPS::ma_bal(Label *label, DelaySlotFill delaySlotFill)
+{
+    if (label->bound()) {
+        // Generate the long jump for calls because return address has to be
+        // the address after the reserved block.
+        addLongJump(nextOffset());
+        ma_liPatchable(ScratchRegister, Imm32(label->offset()));
+        as_jalr(ScratchRegister);
+        if (delaySlotFill == FillDelaySlot)
+            as_nop();
+        return;
+    }
+
+    // Second word holds a pointer to the next branch in label's chain.
+    uint32_t nextInChain = label->used() ? label->offset() : LabelBase::INVALID_OFFSET;
+
+    // Make the whole branch continous in the buffer.
+    m_buffer.ensureSpace(4 * sizeof(uint32_t));
+
+    BufferOffset bo = writeInst(getBranchCode(BranchIsCall).encode());
+    writeInst(nextInChain);
+    label->use(bo.getOffset());
+    // Leave space for long jump.
+    as_nop();
+    if (delaySlotFill == FillDelaySlot)
+        as_nop();
 }
 
 void
 MacroAssemblerMIPS::branchWithCode(InstImm code, Label *label, JumpKind jumpKind)
 {
-    InstImm inst_bgezal = InstImm(op_regimm, zero, rt_bgezal, BOffImm16(0));
+    MOZ_ASSERT(code.encode() != InstImm(op_regimm, zero, rt_bgezal, BOffImm16(0)).encode());
     InstImm inst_beq = InstImm(op_beq, zero, zero, BOffImm16(0));
 
     if (label->bound()) {
         int32_t offset = label->offset() - m_buffer.nextOffset().getOffset();
 
-        // Generate the long jump for calls because return address has to be
-        // the address after the reserved block.
-        if (code.encode() == inst_bgezal.encode()) {
-            MOZ_ASSERT(jumpKind != ShortJump);
-            // Handle long call
-            addLongJump(nextOffset());
-            ma_liPatchable(ScratchRegister, Imm32(label->offset()));
-            as_jalr(ScratchRegister);
-            as_nop();
-            return;
-        }
-
         if (BOffImm16::IsInRange(offset))
             jumpKind = ShortJump;
 
         if (jumpKind == ShortJump) {
             MOZ_ASSERT(BOffImm16::IsInRange(offset));
             code.setBOffImm16(BOffImm16(offset));
             writeInst(code.encode());
             as_nop();
@@ -992,18 +1003,17 @@ MacroAssemblerMIPS::branchWithCode(InstI
         // Indicate that this is short jump with offset 4.
         code.setBOffImm16(BOffImm16(4));
         BufferOffset bo = writeInst(code.encode());
         writeInst(nextInChain);
         label->use(bo.getOffset());
         return;
     }
 
-    bool conditional = (code.encode() != inst_bgezal.encode() &&
-                        code.encode() != inst_beq.encode());
+    bool conditional = code.encode() != inst_beq.encode();
 
     // Make the whole branch continous in the buffer.
     m_buffer.ensureSpace((conditional ? 5 : 4) * sizeof(uint32_t));
 
     BufferOffset bo = writeInst(code.encode());
     writeInst(nextInChain);
     label->use(bo.getOffset());
     // Leave space for potential long jump.
@@ -1485,16 +1495,25 @@ MacroAssemblerMIPSCompat::buildOOLFakeEx
 
     Push(Imm32(descriptor)); // descriptor_
     Push(ImmPtr(fakeReturnAddr));
 
     return true;
 }
 
 void
+MacroAssemblerMIPSCompat::callWithExitFrame(Label *target)
+{
+    uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
+    Push(Imm32(descriptor)); // descriptor
+
+    ma_callIonHalfPush(target);
+}
+
+void
 MacroAssemblerMIPSCompat::callWithExitFrame(JitCode *target)
 {
     uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS);
     Push(Imm32(descriptor)); // descriptor
 
     addPendingJump(m_buffer.nextOffset(), ImmPtr(target->raw()), Relocation::JITCODE);
     ma_liPatchable(ScratchRegister, ImmPtr(target->raw()));
     ma_callIonHalfPush(ScratchRegister);
@@ -3083,16 +3102,27 @@ void
 MacroAssemblerMIPS::ma_callIonHalfPush(const Register r)
 {
     // This is a MIPS hack to push return address during jalr delay slot.
     as_addiu(StackPointer, StackPointer, -sizeof(intptr_t));
     as_jalr(r);
     as_sw(ra, StackPointer, 0);
 }
 
+// This macrosintruction calls the ion code and pushes the return address to
+// the stack in the case when stack is not alligned.
+void
+MacroAssemblerMIPS::ma_callIonHalfPush(Label *label)
+{
+    // This is a MIPS hack to push return address during jalr delay slot.
+    as_addiu(StackPointer, StackPointer, -sizeof(intptr_t));
+    // TODO
+    // TODO
+}
+
 void
 MacroAssemblerMIPS::ma_call(ImmPtr dest)
 {
     ma_liPatchable(CallReg, dest);
     as_jalr(CallReg);
     as_nop();
 }
 
--- a/js/src/jit/mips/MacroAssembler-mips.h
+++ b/js/src/jit/mips/MacroAssembler-mips.h
@@ -32,16 +32,22 @@ enum LoadStoreExtension
 };
 
 enum JumpKind
 {
     LongJump = 0,
     ShortJump = 1
 };
 
+enum DelaySlotFill
+{
+    DontFillDelaySlot = 0,
+    FillDelaySlot = 1
+};
+
 struct ImmTag : public Imm32
 {
     ImmTag(JSValueTag mask)
       : Imm32(int32_t(mask))
     { }
 };
 
 struct ImmType : public ImmTag
@@ -228,17 +234,17 @@ class MacroAssemblerMIPS : public Assemb
     void ma_b(Address addr, Imm32 imm, Label *l, Condition c, JumpKind jumpKind = LongJump);
     void ma_b(Address addr, Register rhs, Label *l, Condition c, JumpKind jumpKind = LongJump) {
         MOZ_ASSERT(rhs != ScratchRegister);
         ma_lw(ScratchRegister, addr);
         ma_b(ScratchRegister, rhs, l, c, jumpKind);
     }
 
     void ma_b(Label *l, JumpKind jumpKind = LongJump);
-    void ma_bal(Label *l, JumpKind jumpKind = LongJump);
+    void ma_bal(Label *l, DelaySlotFill delaySlotFill = FillDelaySlot);
 
     // fp instructions
     void ma_lis(FloatRegister dest, float value);
     void ma_lid(FloatRegister dest, double value);
     void ma_liNegZero(FloatRegister dest);
 
     void ma_mv(FloatRegister src, ValueOperand dest);
     void ma_mv(ValueOperand src, FloatRegister dest);
@@ -972,16 +978,17 @@ public:
     void setFramePushed(uint32_t framePushed) {
         framePushed_ = framePushed;
     }
 
     // Builds an exit frame on the stack, with a return address to an internal
     // non-function. Returns offset to be passed to markSafepointAt().
     bool buildFakeExitFrame(Register scratch, uint32_t *offset);
 
+    void callWithExitFrame(Label *target);
     void callWithExitFrame(JitCode *target);
     void callWithExitFrame(JitCode *target, Register dynStack);
 
     // Makes an Ion call using the only two methods that it is sane for
     // indep code to make a call
     void callIon(Register callee);
     void callIonFromAsmJS(Register callee);
 
--- a/js/src/jit/none/Lowering-none.h
+++ b/js/src/jit/none/Lowering-none.h
@@ -73,16 +73,18 @@ class LIRGeneratorNone : public LIRGener
     bool visitGuardObjectType(MGuardObjectType *ins) { MOZ_CRASH(); }
     bool visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins) { MOZ_CRASH(); }
     bool visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32 *ins) { MOZ_CRASH(); }
     bool visitAsmJSLoadHeap(MAsmJSLoadHeap *ins) { MOZ_CRASH(); }
     bool visitAsmJSStoreHeap(MAsmJSStoreHeap *ins) { MOZ_CRASH(); }
     bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins) { MOZ_CRASH(); }
     bool visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins) { MOZ_CRASH(); }
     bool visitForkJoinGetSlice(MForkJoinGetSlice *ins) { MOZ_CRASH(); }
+    bool visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop *ins) { MOZ_CRASH(); }
+    bool visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement *ins) { MOZ_CRASH(); }
 
     LTableSwitch *newLTableSwitch(LAllocation, LDefinition, MTableSwitch *) { MOZ_CRASH(); }
     LTableSwitchV *newLTableSwitchV(MTableSwitch *) { MOZ_CRASH(); }
     bool visitSimdTernaryBitwise(MSimdTernaryBitwise *ins) { MOZ_CRASH(); }
     bool visitSimdSplatX4(MSimdSplatX4 *ins) { MOZ_CRASH(); }
     bool visitSimdValueX4(MSimdValueX4 *lir) { MOZ_CRASH(); }
 };
 
--- a/js/src/jit/none/MacroAssembler-none.h
+++ b/js/src/jit/none/MacroAssembler-none.h
@@ -180,16 +180,17 @@ class MacroAssemblerNone : public Assemb
     template <typename T> void call(T) { MOZ_CRASH(); }
     template <typename T, typename S> void call(T, S) { MOZ_CRASH(); }
     template <typename T> void callWithABI(T, MoveOp::Type v = MoveOp::GENERAL) { MOZ_CRASH(); }
 
     void setupAlignedABICall(uint32_t) { MOZ_CRASH(); }
     void setupUnalignedABICall(uint32_t, Register) { MOZ_CRASH(); }
     template <typename T> void passABIArg(T, MoveOp::Type v = MoveOp::GENERAL) { MOZ_CRASH(); }
 
+    void callWithExitFrame(Label *) { MOZ_CRASH(); }
     void callWithExitFrame(JitCode *) { MOZ_CRASH(); }
     void callWithExitFrame(JitCode *, Register) { MOZ_CRASH(); }
 
     void callIon(Register callee) { MOZ_CRASH(); }
     void callIonFromAsmJS(Register callee) { MOZ_CRASH(); }
 
     void nop() { MOZ_CRASH(); }
     void breakpoint() { MOZ_CRASH(); }
@@ -291,16 +292,47 @@ class MacroAssemblerNone : public Assemb
     template <typename T, typename S> void storeUnalignedInt32x4(T, S) { MOZ_CRASH(); }
     template <typename T, typename S> void storeAlignedFloat32x4(T, S) { MOZ_CRASH(); }
     template <typename T, typename S> void storeUnalignedFloat32x4(T, S) { MOZ_CRASH(); }
     template <typename T, typename S> void store8(T, S) { MOZ_CRASH(); }
     template <typename T, typename S> void store16(T, S) { MOZ_CRASH(); }
 
     template <typename T> void computeEffectiveAddress(T, Register) { MOZ_CRASH(); }
 
+    template <typename T> void compareExchange8SignExtend(const T &mem, Register oldval, Register newval, Register output) { MOZ_CRASH(); }
+    template <typename T> void compareExchange8ZeroExtend(const T &mem, Register oldval, Register newval, Register output) { MOZ_CRASH(); }
+    template <typename T> void compareExchange16SignExtend(const T &mem, Register oldval, Register newval, Register output) { MOZ_CRASH(); }
+    template <typename T> void compareExchange16ZeroExtend(const T &mem, Register oldval, Register newval, Register output) { MOZ_CRASH(); }
+    template <typename T> void compareExchange32(const T &mem, Register oldval, Register newval, Register output) { MOZ_CRASH(); }
+    template <typename T, typename S> void atomicFetchAdd8SignExtend(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
+    template <typename T, typename S> void atomicFetchAdd8ZeroExtend(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
+    template <typename T, typename S> void atomicFetchAdd16SignExtend(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
+    template <typename T, typename S> void atomicFetchAdd16ZeroExtend(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
+    template <typename T, typename S> void atomicFetchAdd32(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
+    template <typename T, typename S> void atomicFetchSub8SignExtend(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
+    template <typename T, typename S> void atomicFetchSub8ZeroExtend(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
+    template <typename T, typename S> void atomicFetchSub16SignExtend(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
+    template <typename T, typename S> void atomicFetchSub16ZeroExtend(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
+    template <typename T, typename S> void atomicFetchSub32(const T &value, const S &mem, Register output) { MOZ_CRASH(); }
+    template <typename T, typename S> void atomicFetchAnd8SignExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
+    template <typename T, typename S> void atomicFetchAnd8ZeroExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
+    template <typename T, typename S> void atomicFetchAnd16SignExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
+    template <typename T, typename S> void atomicFetchAnd16ZeroExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
+    template <typename T, typename S> void atomicFetchAnd32(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
+    template <typename T, typename S> void atomicFetchOr8SignExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
+    template <typename T, typename S> void atomicFetchOr8ZeroExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
+    template <typename T, typename S> void atomicFetchOr16SignExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
+    template <typename T, typename S> void atomicFetchOr16ZeroExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
+    template <typename T, typename S> void atomicFetchOr32(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
+    template <typename T, typename S> void atomicFetchXor8SignExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
+    template <typename T, typename S> void atomicFetchXor8ZeroExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
+    template <typename T, typename S> void atomicFetchXor16SignExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
+    template <typename T, typename S> void atomicFetchXor16ZeroExtend(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
+    template <typename T, typename S> void atomicFetchXor32(const T &value, const S &mem, Register temp, Register output) { MOZ_CRASH(); }
+
     void clampIntToUint8(Register) { MOZ_CRASH(); }
 
     Register splitTagForTest(ValueOperand) { MOZ_CRASH(); }
 
     template <typename T> void branchTestUndefined(Condition, T, Label *) { MOZ_CRASH(); }
     template <typename T> void branchTestInt32(Condition, T, Label *) { MOZ_CRASH(); }
     template <typename T> void branchTestBoolean(Condition, T, Label *) { MOZ_CRASH(); }
     template <typename T> void branchTestDouble(Condition, T, Label *) { MOZ_CRASH(); }
--- a/js/src/jit/shared/Assembler-x86-shared.h
+++ b/js/src/jit/shared/Assembler-x86-shared.h
@@ -624,28 +624,46 @@ class AssemblerX86Shared : public Assemb
             break;
           case Operand::MEM_SCALE:
             masm.movzbl_mr(src.disp(), src.base(), src.index(), src.scale(), dest.code());
             break;
           default:
             MOZ_CRASH("unexpected operand kind");
         }
     }
+    void movsbl(Register src, Register dest) {
+        masm.movsbl_rr(src.code(), dest.code());
+    }
     void movsbl(const Operand &src, Register dest) {
         switch (src.kind()) {
           case Operand::MEM_REG_DISP:
             masm.movsbl_mr(src.disp(), src.base(), dest.code());
             break;
           case Operand::MEM_SCALE:
             masm.movsbl_mr(src.disp(), src.base(), src.index(), src.scale(), dest.code());
             break;
           default:
             MOZ_CRASH("unexpected operand kind");
         }
     }
+    void movb(const Operand &src, Register dest) {
+        switch (src.kind()) {
+          case Operand::MEM_REG_DISP:
+            masm.movb_mr(src.disp(), src.base(), dest.code());
+            break;
+          case Operand::MEM_SCALE:
+            masm.movb_mr(src.disp(), src.base(), src.index(), src.scale(), dest.code());
+            break;
+          default:
+            MOZ_CRASH("unexpected operand kind");
+        }
+    }
+    void movb(Imm32 src, Register dest) {
+        masm.movb_i8r(src.value & 255, dest.code());
+    }
     void movb(Register src, const Operand &dest) {
         switch (dest.kind()) {
           case Operand::MEM_REG_DISP:
             masm.movb_rm(src.code(), dest.disp(), dest.base());
             break;
           case Operand::MEM_SCALE:
             masm.movb_rm(src.code(), dest.disp(), dest.base(), dest.index(), dest.scale());
             break;
@@ -678,16 +696,24 @@ class AssemblerX86Shared : public Assemb
             break;
           default:
             MOZ_CRASH("unexpected operand kind");
         }
     }
     void movzwl(Register src, Register dest) {
         masm.movzwl_rr(src.code(), dest.code());
     }
+    void movw(const Operand &src, Register dest) {
+        masm.prefix_16_for_32();
+        movl(src, dest);
+    }
+    void movw(Imm32 src, Register dest) {
+        masm.prefix_16_for_32();
+        movl(src, dest);
+    }
     void movw(Register src, const Operand &dest) {
         switch (dest.kind()) {
           case Operand::MEM_REG_DISP:
             masm.movw_rm(src.code(), dest.disp(), dest.base());
             break;
           case Operand::MEM_SCALE:
             masm.movw_rm(src.code(), dest.disp(), dest.base(), dest.index(), dest.scale());
             break;
@@ -702,16 +728,19 @@ class AssemblerX86Shared : public Assemb
             break;
           case Operand::MEM_SCALE:
             masm.movw_i16m(src.value, dest.disp(), dest.base(), dest.index(), dest.scale());
             break;
           default:
             MOZ_CRASH("unexpected operand kind");
         }
     }
+    void movswl(Register src, Register dest) {
+        masm.movswl_rr(src.code(), dest.code());
+    }
     void movswl(const Operand &src, Register dest) {
         switch (src.kind()) {
           case Operand::MEM_REG_DISP:
             masm.movswl_mr(src.disp(), src.base(), dest.code());
             break;
           case Operand::MEM_SCALE:
             masm.movswl_mr(src.disp(), src.base(), src.index(), src.scale(), dest.code());
             break;
@@ -916,19 +945,17 @@ class AssemblerX86Shared : public Assemb
             MOZ_CRASH("unexpected operand kind");
         }
     }
 
     void breakpoint() {
         masm.int3();
     }
 
-#ifdef DEBUG
     static bool HasSSE2() { return CPUInfo::IsSSE2Present(); }
-#endif
     static bool HasSSE3() { return CPUInfo::IsSSE3Present(); }
     static bool HasSSE41() { return CPUInfo::IsSSE41Present(); }
     static bool SupportsFloatingPoint() { return CPUInfo::IsSSE2Present(); }
     static bool SupportsSimd() { return CPUInfo::IsSSE2Present(); }
 
     // The below cmpl methods switch the lhs and rhs when it invokes the
     // macroassembler to conform with intel standard.  When calling this
     // function put the left operand on the left as you would expect.
@@ -1055,16 +1082,22 @@ class AssemblerX86Shared : public Assemb
             break;
           case Operand::MEM_ADDRESS32:
             masm.addl_im(imm.value, op.address());
             break;
           default:
             MOZ_CRASH("unexpected operand kind");
         }
     }
+    // Note, lock_addl() is used for a memory barrier on non-SSE2 systems.
+    // Do not optimize, replace by XADDL, or similar.
+    void lock_addl(Imm32 imm, const Operand &op) {
+        masm.prefix_lock();
+        addl(imm, op);
+    }
     void subl(Imm32 imm, Register dest) {
         masm.subl_ir(imm.value, dest.code());
     }
     void subl(Imm32 imm, const Operand &op) {
         switch (op.kind()) {
           case Operand::REG:
             masm.subl_ir(imm.value, op.reg());
             break;
@@ -1306,34 +1339,79 @@ class AssemblerX86Shared : public Assemb
             MOZ_CRASH("unexpected operand kind");
         }
     }
     void lock_decl(const Operand &op) {
         masm.prefix_lock();
         decl(op);
     }
 
-    void lock_cmpxchg32(Register src, const Operand &op) {
+    void lock_cmpxchg8(Register src, const Operand &mem) {
+        masm.prefix_lock();
+        switch (mem.kind()) {
+          case Operand::MEM_REG_DISP:
+            masm.cmpxchg8(src.code(), mem.disp(), mem.base());
+            break;
+          case Operand::MEM_SCALE:
+            masm.cmpxchg8(src.code(), mem.disp(), mem.base(), mem.index(), mem.scale());
+            break;
+          default:
+            MOZ_CRASH("unexpected operand kind");
+        }
+    }
+    void lock_cmpxchg16(Register src, const Operand &mem) {
         masm.prefix_lock();
-        switch (op.kind()) {
+        switch (mem.kind()) {
           case Operand::MEM_REG_DISP:
-            masm.cmpxchg32(src.code(), op.disp(), op.base());
+            masm.cmpxchg16(src.code(), mem.disp(), mem.base());
+            break;
+          case Operand::MEM_SCALE:
+            masm.cmpxchg16(src.code(), mem.disp(), mem.base(), mem.index(), mem.scale());
+            break;
+          default:
+            MOZ_CRASH("unexpected operand kind");
+        }
+    }
+    void lock_cmpxchg32(Register src, const Operand &mem) {
+        masm.prefix_lock();
+        switch (mem.kind()) {
+          case Operand::MEM_REG_DISP:
+            masm.cmpxchg32(src.code(), mem.disp(), mem.base());
+            break;
+          case Operand::MEM_SCALE:
+            masm.cmpxchg32(src.code(), mem.disp(), mem.base(), mem.index(), mem.scale());
             break;
           default:
             MOZ_CRASH("unexpected operand kind");
         }
     }
 
-    void xaddl(Register srcdest, const Operand &mem) {
+    void lock_xaddb(Register srcdest, const Operand &mem) {
         switch (mem.kind()) {
           case Operand::MEM_REG_DISP:
-            masm.xaddl_rm(srcdest.code(), mem.disp(), mem.base());
+            masm.lock_xaddb_rm(srcdest.code(), mem.disp(), mem.base());
             break;
           case Operand::MEM_SCALE:
-            masm.xaddl_rm(srcdest.code(), mem.disp(), mem.base(), mem.index(), mem.scale());
+            masm.lock_xaddb_rm(srcdest.code(), mem.disp(), mem.base(), mem.index(), mem.scale());
+            break;
+          default:
+            MOZ_CRASH("unexpected operand kind");
+        }
+    }
+    void lock_xaddw(Register srcdest, const Operand &mem) {
+        masm.prefix_16_for_32();
+        lock_xaddl(srcdest, mem);
+    }
+    void lock_xaddl(Register srcdest, const Operand &mem) {
+        switch (mem.kind()) {
+          case Operand::MEM_REG_DISP:
+            masm.lock_xaddl_rm(srcdest.code(), mem.disp(), mem.base());
+            break;
+          case Operand::MEM_SCALE:
+            masm.lock_xaddl_rm(srcdest.code(), mem.disp(), mem.base(), mem.index(), mem.scale());
             break;
           default:
             MOZ_CRASH("unexpected operand kind");
         }
     }
 
     void push(const Imm32 imm) {
         masm.push_i32(imm.value);
--- a/js/src/jit/shared/BaseAssembler-x86-shared.h
+++ b/js/src/jit/shared/BaseAssembler-x86-shared.h
@@ -211,26 +211,31 @@ public:
         RoundToNearest = 0x0,
         RoundDown      = 0x1,
         RoundUp        = 0x2,
         RoundToZero    = 0x3
     } RoundingMode;
 
 private:
     typedef enum {
+        OP_ADD_EbGb                     = 0x00,
         OP_ADD_EvGv                     = 0x01,
         OP_ADD_GvEv                     = 0x03,
+        OP_OR_EbGb                      = 0x08,
         OP_OR_EvGv                      = 0x09,
         OP_OR_GvEv                      = 0x0B,
         OP_2BYTE_ESCAPE                 = 0x0F,
+        OP_AND_EbGb                     = 0x20,
         OP_AND_EvGv                     = 0x21,
         OP_AND_GvEv                     = 0x23,
+        OP_SUB_EbGb                     = 0x28,
         OP_SUB_EvGv                     = 0x29,
         OP_SUB_GvEv                     = 0x2B,
         PRE_PREDICT_BRANCH_NOT_TAKEN    = 0x2E,
+        OP_XOR_EbGb                     = 0x30,
         OP_XOR_EvGv                     = 0x31,
         OP_XOR_GvEv                     = 0x33,
         OP_CMP_EvGv                     = 0x39,
         OP_CMP_GvEv                     = 0x3B,
         OP_CMP_EAXIv                    = 0x3D,
 #ifdef JS_CODEGEN_X64
         PRE_REX                         = 0x40,
 #endif
@@ -250,16 +255,17 @@ private:
         OP_GROUP1_EbIb                  = 0x80,
         OP_GROUP1_EvIz                  = 0x81,
         OP_GROUP1_EvIb                  = 0x83,
         OP_TEST_EbGb                    = 0x84,
         OP_TEST_EvGv                    = 0x85,
         OP_XCHG_EvGv                    = 0x87,
         OP_MOV_EbGv                     = 0x88,
         OP_MOV_EvGv                     = 0x89,
+        OP_MOV_GvEb                     = 0x8A,
         OP_MOV_GvEv                     = 0x8B,
         OP_LEA                          = 0x8D,
         OP_GROUP1A_Ev                   = 0x8F,
         OP_NOP                          = 0x90,
         OP_PUSHFLAGS                    = 0x9C,
         OP_POPFLAGS                     = 0x9D,
         OP_CDQ                          = 0x99,
         OP_MOV_EAXOv                    = 0xA1,
@@ -344,23 +350,26 @@ private:
         OP2_PSRLD_UdqIb     = 0x72,
         OP2_PSRLDQ_Vd       = 0x73,
         OP2_PCMPEQW         = 0x75,
         OP2_PCMPEQD_VdqWdq  = 0x76,
         OP2_MOVD_EdVd       = 0x7E,
         OP2_MOVDQ_WdqVdq    = 0x7F,
         OP2_JCC_rel32       = 0x80,
         OP_SETCC            = 0x90,
+        OP_FENCE            = 0xAE,
         OP2_IMUL_GvEv       = 0xAF,
+        OP2_CMPXCHG_GvEb    = 0xB0,
         OP2_CMPXCHG_GvEw    = 0xB1,
         OP2_BSR_GvEv        = 0xBD,
         OP2_MOVSX_GvEb      = 0xBE,
         OP2_MOVSX_GvEw      = 0xBF,
         OP2_MOVZX_GvEb      = 0xB6,
         OP2_MOVZX_GvEw      = 0xB7,
+        OP2_XADD_EbGb       = 0xC0,
         OP2_XADD_EvGv       = 0xC1,
         OP2_CMPPS_VpsWps    = 0xC2,
         OP2_PEXTRW_GdUdIb   = 0xC5,
         OP2_SHUFPS_VpsWpsIb = 0xC6,
         OP2_PSRLD_VdqWdq    = 0xD2,
         OP2_PSRAD_VdqWdq    = 0xE2,
         OP2_PXORDQ_VdqWdq   = 0xEF,
         OP2_PSLLD_VdqWdq    = 0xF2,
@@ -678,25 +687,42 @@ public:
             m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_ADD, addr);
             m_formatter.immediate8(imm);
         } else {
             m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_ADD, addr);
             m_formatter.immediate32(imm);
         }
     }
 
-    void xaddl_rm(RegisterID srcdest, int offset, RegisterID base)
+    void lock_xaddb_rm(RegisterID srcdest, int offset, RegisterID base)
+    {
+        spew("lock xaddl %s, %s0x%x(%s)",
+            nameIReg(1, srcdest), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
+        m_formatter.oneByteOp(PRE_LOCK);
+        m_formatter.twoByteOp(OP2_XADD_EbGb, srcdest, base, offset);
+    }
+
+    void lock_xaddb_rm(RegisterID srcdest, int offset, RegisterID base, RegisterID index, int scale)
+    {
+        spew("lock xaddl %s, %s0x%x(%s,%s,%d)",
+            nameIReg(1, srcdest), PRETTY_PRINT_OFFSET(offset),
+            nameIReg(base), nameIReg(index), 1<<scale);
+        m_formatter.oneByteOp(PRE_LOCK);
+        m_formatter.twoByteOp(OP2_XADD_EbGb, srcdest, base, index, scale, offset);
+    }
+
+    void lock_xaddl_rm(RegisterID srcdest, int offset, RegisterID base)
     {
         spew("lock xaddl %s, %s0x%x(%s)",
             nameIReg(4,srcdest), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp(PRE_LOCK);
         m_formatter.twoByteOp(OP2_XADD_EvGv, srcdest, base, offset);
     }
 
-    void xaddl_rm(RegisterID srcdest, int offset, RegisterID base, RegisterID index, int scale)
+    void lock_xaddl_rm(RegisterID srcdest, int offset, RegisterID base, RegisterID index, int scale)
     {
         spew("lock xaddl %s, %s0x%x(%s,%s,%d)",
             nameIReg(4, srcdest), PRETTY_PRINT_OFFSET(offset),
             nameIReg(base), nameIReg(index), 1<<scale);
         m_formatter.oneByteOp(PRE_LOCK);
         m_formatter.twoByteOp(OP2_XADD_EvGv, srcdest, base, index, scale, offset);
     }
 
@@ -1422,37 +1448,76 @@ public:
     }
 
     void prefix_lock()
     {
         spew("lock");
         m_formatter.oneByteOp(PRE_LOCK);
     }
 
+    void prefix_16_for_32()
+    {
+        m_formatter.prefix(PRE_OPERAND_SIZE);
+    }
+
     void incl_m32(int offset, RegisterID base)
     {
         spew("incl       %s0x%x(%s)", PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_INC, base, offset);
     }
 
     void decl_m32(int offset, RegisterID base)
     {
         spew("decl       %s0x%x(%s)", PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_DEC, base, offset);
     }
 
+    // Note that CMPXCHG performs comparison against REG = %al/%ax/%eax.
+    // If %REG == [%base+offset], then %src -> [%base+offset].
+    // Otherwise, [%base+offset] -> %REG.
+    // For the 8-bit operations src must also be an 8-bit register.
+
+    void cmpxchg8(RegisterID src, int offset, RegisterID base)
+    {
+        spew("cmpxchg8    %s, %s0x%x(%s)",
+             nameIReg(src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
+        m_formatter.twoByteOp(OP2_CMPXCHG_GvEb, src, base, offset);
+    }
+    void cmpxchg8(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
+    {
+        spew("cmpxchg8    %s, %s0x%x(%s,%s,%d)",
+             nameIReg(src), PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(index), 1<<scale);
+        m_formatter.twoByteOp(OP2_CMPXCHG_GvEb, src, base, index, scale, offset);
+    }
+    void cmpxchg16(RegisterID src, int offset, RegisterID base)
+    {
+        spew("cmpxchg16    %s, %s0x%x(%s)",
+             nameIReg(src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
+        m_formatter.prefix(PRE_OPERAND_SIZE);
+        m_formatter.twoByteOp(OP2_CMPXCHG_GvEw, src, base, offset);
+    }
+    void cmpxchg16(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
+    {
+        spew("cmpxchg16    %s, %s0x%x(%s,%s,%d)",
+             nameIReg(src), PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(index), 1<<scale);
+        m_formatter.prefix(PRE_OPERAND_SIZE);
+        m_formatter.twoByteOp(OP2_CMPXCHG_GvEw, src, base, index, scale, offset);
+    }
     void cmpxchg32(RegisterID src, int offset, RegisterID base)
     {
-        // Note that 32-bit CMPXCHG performs comparison against %eax.
-        // If %eax == [%base+offset], then %src -> [%base+offset].
-        // Otherwise, [%base+offset] -> %eax.
-        spew("cmpxchg    %s, %s0x%x(%s)",
+        spew("cmpxchg32    %s, %s0x%x(%s)",
              nameIReg(src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.twoByteOp(OP2_CMPXCHG_GvEw, src, base, offset);
     }
+    void cmpxchg32(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
+    {
+        spew("cmpxchg32    %s, %s0x%x(%s,%s,%d)",
+             nameIReg(src), PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(index), 1<<scale);
+        m_formatter.twoByteOp(OP2_CMPXCHG_GvEw, src, base, index, scale, offset);
+    }
 
 
     // Comparisons:
 
     void cmpl_rr(RegisterID src, RegisterID dst)
     {
         spew("cmpl       %s, %s",
              nameIReg(4, src), nameIReg(4, dst));
@@ -1980,16 +2045,24 @@ public:
     void movl_i32r(int imm, RegisterID dst)
     {
         spew("movl       $0x%x, %s",
              imm, nameIReg(4, dst));
         m_formatter.oneByteOp(OP_MOV_EAXIv, dst);
         m_formatter.immediate32(imm);
     }
 
+    void movb_i8r(int imm, RegisterID reg)
+    {
+        spew("movb       $0x%x, %s",
+             imm, nameIReg(1, reg));
+        m_formatter.oneByteOp(OP_MOV_EbGv, reg);
+        m_formatter.immediate8(imm);
+    }
+
     void movb_i8m(int imm, int offset, RegisterID base)
     {
         spew("movb       $0x%x, %s0x%x(%s)",
              imm, PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp(OP_GROUP11_EvIb, GROUP11_MOV, base, offset);
         m_formatter.immediate8(imm);
     }
 
@@ -2270,16 +2343,30 @@ public:
 
     void movb_rm(RegisterID src, const void* addr)
     {
         spew("movb       %s, %p",
              nameIReg(1, src), addr);
         m_formatter.oneByteOp8(OP_MOV_EbGv, src, addr);
     }
 
+    void movb_mr(int offset, RegisterID base, RegisterID dst)
+    {
+        spew("movb       %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(1, dst));
+        m_formatter.oneByteOp(OP_MOV_GvEb, dst, base, offset);
+    }
+
+    void movb_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
+    {
+        spew("movb       %d(%s,%s,%d), %s",
+             offset, nameIReg(base), nameIReg(index), 1<<scale, nameIReg(1, dst));
+        m_formatter.oneByteOp(OP_MOV_GvEb, dst, base, index, scale, offset);
+    }
+
     void movzbl_mr(int offset, RegisterID base, RegisterID dst)
     {
         spew("movzbl     %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(4, dst));
         m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, base, offset);
     }
 
     void movzbl_mr_disp32(int offset, RegisterID base, RegisterID dst)
@@ -2298,16 +2385,23 @@ public:
 
     void movzbl_mr(const void* addr, RegisterID dst)
     {
         spew("movzbl     %p, %s",
              addr, nameIReg(dst));
         m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, addr);
     }
 
+    void movsbl_rr(RegisterID src, RegisterID dst)
+    {
+        spew("movsbl     %s, %s",
+             nameIReg(1,src), nameIReg(4,dst));
+        m_formatter.twoByteOp8_movx(OP2_MOVSX_GvEb, dst, src);
+    }
+
     void movsbl_mr(int offset, RegisterID base, RegisterID dst)
     {
         spew("movsbl     %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(4, dst));
         m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, base, offset);
     }
 
     void movsbl_mr_disp32(int offset, RegisterID base, RegisterID dst)
@@ -2361,16 +2455,23 @@ public:
 
     void movzwl_mr(const void* addr, RegisterID dst)
     {
         spew("movzwl     %p, %s",
              addr, nameIReg(4, dst));
         m_formatter.twoByteOp(OP2_MOVZX_GvEw, dst, addr);
     }
 
+    void movswl_rr(RegisterID src, RegisterID dst)
+    {
+        spew("movswl     %s, %s",
+             nameIReg(2, src), nameIReg(4, dst));
+        m_formatter.twoByteOp(OP2_MOVSX_GvEw, dst, src);
+    }
+
     void movswl_mr(int offset, RegisterID base, RegisterID dst)
     {
         spew("movswl     %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(4, dst));
         m_formatter.twoByteOp(OP2_MOVSX_GvEw, dst, base, offset);
     }
 
     void movswl_mr_disp32(int offset, RegisterID base, RegisterID dst)
@@ -3898,16 +3999,21 @@ public:
 
     void popa()
     {
         spew("popa");
         m_formatter.oneByteOp(OP_POPA);
     }
 #endif
 
+    void mfence() {
+        spew("mfence");
+        m_formatter.twoByteOp(OP_FENCE, (int)6, (RegisterID)0);
+    }
+
     // Assembler admin methods:
 
     JmpDst label()
     {
         JmpDst r = JmpDst(m_formatter.size());
         spew("#label     ((%d))", r.m_offset);
         return r;
     }
--- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp
@@ -3076,10 +3076,18 @@ JitRuntime::generateForkJoinGetSliceStub
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "ForkJoinGetSliceStub");
 #endif
 
     return code;
 }
 
+bool
+CodeGeneratorX86Shared::visitMemoryBarrier(LMemoryBarrier *ins)
+{
+    if (ins->type() & MembarStoreLoad)
+        masm.storeLoadFence();
+    return true;
+}
+
 } // namespace jit
 } // namespace js
--- a/js/src/jit/shared/CodeGenerator-x86-shared.h
+++ b/js/src/jit/shared/Code