Merge mozilla-central to fx-team
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 11 Nov 2014 13:27:49 +0100
changeset 241491 4523f5b9bbcfeaf7b3fc54028ca2688964f677b7
parent 241490 b9f8b0d3a7d7ec4696caa41fb9abc5dfde3ce1ac (current diff)
parent 241406 c60fc2c11c0ea6794546fe108bb5bd7c29a294cc (diff)
child 241492 942aec7a1572dca9e2f5ebecf84a879d0eb354b9
push id660
push userraliiev@mozilla.com
push dateWed, 18 Feb 2015 20:30:48 +0000
treeherdermozilla-release@49e493494178 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
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 mozilla-central to fx-team
configure.in
--- a/accessible/jsat/AccessFu.jsm
+++ b/accessible/jsat/AccessFu.jsm
@@ -229,19 +229,16 @@ this.AccessFu = { // jshint ignore:line
         }
         break;
       case 'AccessFu:Present':
         this._output(aMessage.json, aMessage.target);
         break;
       case 'AccessFu:Input':
         this.Input.setEditState(aMessage.json);
         break;
-      case 'AccessFu:ActivateContextMenu':
-        this.Input.activateContextMenu(aMessage.json);
-        break;
       case 'AccessFu:DoScroll':
         this.Input.doScroll(aMessage.json);
         break;
     }
   },
 
   _output: function _output(aPresentationData, aBrowser) {
     if (!Utils.isAliveAndVisible(
@@ -278,25 +275,23 @@ this.AccessFu = { // jshint ignore:line
         {method: 'start', buildApp: Utils.MozBuildApp});
     }
   },
 
   _addMessageListeners: function _addMessageListeners(aMessageManager) {
     aMessageManager.addMessageListener('AccessFu:Present', this);
     aMessageManager.addMessageListener('AccessFu:Input', this);
     aMessageManager.addMessageListener('AccessFu:Ready', this);
-    aMessageManager.addMessageListener('AccessFu:ActivateContextMenu', this);
     aMessageManager.addMessageListener('AccessFu:DoScroll', this);
   },
 
   _removeMessageListeners: function _removeMessageListeners(aMessageManager) {
     aMessageManager.removeMessageListener('AccessFu:Present', this);
     aMessageManager.removeMessageListener('AccessFu:Input', this);
     aMessageManager.removeMessageListener('AccessFu:Ready', this);
-    aMessageManager.removeMessageListener('AccessFu:ActivateContextMenu', this);
     aMessageManager.removeMessageListener('AccessFu:DoScroll', this);
   },
 
   _handleMessageManager: function _handleMessageManager(aMessageManager) {
     if (this._enabled) {
       this._addMessageListeners(aMessageManager);
     }
     this._loadFrameScript(aMessageManager);
@@ -668,19 +663,16 @@ var Input = {
       case 'dwell1':
       case 'explore1':
         this.moveToPoint('Simple', aGesture.touches[0].x,
           aGesture.touches[0].y);
         break;
       case 'doubletap1':
         this.activateCurrent();
         break;
-      case 'taphold1':
-        this.sendContextMenuMessage();
-        break;
       case 'doubletaphold1':
         Utils.dispatchChromeEvent('accessibility-control', 'quicknav-menu');
         break;
       case 'swiperight1':
         this.moveCursor('moveNext', 'Simple', 'gestures');
         break;
       case 'swipeleft1':
         this.moveCursor('movePrevious', 'Simple', 'gesture');
@@ -878,25 +870,16 @@ var Input = {
                         {offset: offset, activateIfKey: aActivateIfKey});
   },
 
   sendContextMenuMessage: function sendContextMenuMessage() {
     let mm = Utils.getMessageManager(Utils.CurrentBrowser);
     mm.sendAsyncMessage('AccessFu:ContextMenu', {});
   },
 
-  activateContextMenu: function activateContextMenu(aDetails) {
-    if (Utils.MozBuildApp === 'mobile/android') {
-      let p = AccessFu.adjustContentBounds(aDetails.bounds,
-                                           Utils.CurrentBrowser, true).center();
-      Services.obs.notifyObservers(null, 'Gesture:LongPress',
-                                   JSON.stringify({x: p.x, y: p.y}));
-    }
-  },
-
   setEditState: function setEditState(aEditState) {
     Logger.debug(() => { return ['setEditState', JSON.stringify(aEditState)] });
     this.editState = aEditState;
   },
 
   // XXX: This is here for backwards compatability with screen reader simulator
   // it should be removed when the extension is updated on amo.
   scroll: function scroll(aPage, aHorizontal) {
--- a/accessible/jsat/content-script.js
+++ b/accessible/jsat/content-script.js
@@ -64,18 +64,23 @@ function forwardToChild(aMessage, aListe
   }
   mm.sendAsyncMessage(aMessage.name, newJSON);
   return true;
 }
 
 function activateContextMenu(aMessage) {
   let position = Utils.getVirtualCursor(content.document).position;
   if (!forwardToChild(aMessage, activateContextMenu, position)) {
-    sendAsyncMessage('AccessFu:ActivateContextMenu',
-      { bounds: Utils.getBounds(position, true) });
+    let center = Utils.getBounds(position, true).center();
+
+    let evt = content.document.createEvent('HTMLEvents');
+    evt.initEvent('contextmenu', true, true);
+    evt.clientX = center.x;
+    evt.clientY = center.y;
+    position.DOMNode.dispatchEvent(evt);
   }
 }
 
 function presentCaretChange(aText, aOldOffset, aNewOffset) {
   if (aOldOffset !== aNewOffset) {
     let msg = Presentation.textSelectionChanged(aText, aNewOffset, aNewOffset,
                                                 aOldOffset, aOldOffset, true);
     sendAsyncMessage('AccessFu:Present', msg);
--- a/b2g/chrome/content/devtools/adb.js
+++ b/b2g/chrome/content/devtools/adb.js
@@ -95,21 +95,21 @@ let AdbController = {
   updateStorageState: function(storageIndex) {
     if (storageIndex >= this.storages.length) {
       // We've iterated through all of the storage objects, now we can
       // really do updateStateInternal.
       this.updateStateInternal();
       return;
     }
     let storage = this.storages[storageIndex];
-    DEBUG && debug("Checking availability of storage: '" + storage.storageName);
+    DEBUG && debug("Checking availability of storage: '" + storage.storageName + "'");
 
     let req = storage.available();
     req.onsuccess = function(e) {
-      DEBUG && debug("Storage: '" + storage.storageName + "' is '" + e.target.result);
+      DEBUG && debug("Storage: '" + storage.storageName + "' is '" + e.target.result + "'");
       if (e.target.result == 'shared') {
         // We've found a storage area that's being shared with the PC.
         // We can stop looking now.
         this.umsActive = true;
         this.updateStateInternal();
         return;
       }
       this.updateStorageState(storageIndex + 1);
@@ -211,30 +211,40 @@ let AdbController = {
                    " remoteDebuggerEnabled = " + this.remoteDebuggerEnabled +
                    " lockEnabled = " + this.lockEnabled +
                    " locked = " + this.locked +
                    " usbFuncActive = " + usbFuncActive);
 
     // Configure adb.
     let currentConfig = libcutils.property_get("persist.sys.usb.config");
     let configFuncs = currentConfig.split(",");
+    if (currentConfig == "" || currentConfig == "none") {
+      // We want to treat none like the empty string.
+      // "".split(",") yields [""] and not []
+      configFuncs = [];
+    }
     let adbIndex = configFuncs.indexOf("adb");
 
     if (enableAdb) {
       // Add adb to the list of functions, if not already present
       if (adbIndex < 0) {
         configFuncs.push("adb");
       }
     } else {
       // Remove adb from the list of functions, if present
       if (adbIndex >= 0) {
         configFuncs.splice(adbIndex, 1);
       }
     }
     let newConfig = configFuncs.join(",");
+    if (newConfig == "") {
+      // Convert the empty string back into none, since that's what init.rc
+      // needs.
+      newConfig = "none";
+    }
     if (newConfig != currentConfig) {
       DEBUG && debug("updateState: currentConfig = " + currentConfig);
       DEBUG && debug("updateState:     newConfig = " + newConfig);
       try {
         libcutils.property_set("persist.sys.usb.config", newConfig);
       } catch(e) {
         Cu.reportError("Error configuring adb: " + e);
       }
--- 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="3ab0d9c70f0b2e1ededc679112c392303f037361">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="a98528f9a69dae06cbeba9b602c3d9839724250d"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="6af3a8a833eb8bb651e8b188cb3f3c3a43bb4184"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <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="e9cf0dc485a2af12353b41e9f1e41b23f3f07b41"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="ed238f7f8824973ea16e69aef08c6a1d58c7165f"/>
   <!-- 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="df362ace56338da8173d30d3e09e08c42c1accfa">
     <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="a98528f9a69dae06cbeba9b602c3d9839724250d"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="6af3a8a833eb8bb651e8b188cb3f3c3a43bb4184"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <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="67f2907bc340bad250b4ea6ce2902b52896c9ef0"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e9cf0dc485a2af12353b41e9f1e41b23f3f07b41"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="ed238f7f8824973ea16e69aef08c6a1d58c7165f"/>
   <!-- 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="0e94c080bee081a50aa2097527b0b40852f9143f">
     <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="a98528f9a69dae06cbeba9b602c3d9839724250d"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="6af3a8a833eb8bb651e8b188cb3f3c3a43bb4184"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e9cf0dc485a2af12353b41e9f1e41b23f3f07b41"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="ed238f7f8824973ea16e69aef08c6a1d58c7165f"/>
   <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="3ab0d9c70f0b2e1ededc679112c392303f037361">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="a98528f9a69dae06cbeba9b602c3d9839724250d"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="6af3a8a833eb8bb651e8b188cb3f3c3a43bb4184"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <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="e9cf0dc485a2af12353b41e9f1e41b23f3f07b41"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="ed238f7f8824973ea16e69aef08c6a1d58c7165f"/>
   <!-- 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="df362ace56338da8173d30d3e09e08c42c1accfa">
     <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="a98528f9a69dae06cbeba9b602c3d9839724250d"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="6af3a8a833eb8bb651e8b188cb3f3c3a43bb4184"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <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="67f2907bc340bad250b4ea6ce2902b52896c9ef0"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e9cf0dc485a2af12353b41e9f1e41b23f3f07b41"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="ed238f7f8824973ea16e69aef08c6a1d58c7165f"/>
   <!-- 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="3ab0d9c70f0b2e1ededc679112c392303f037361">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="a98528f9a69dae06cbeba9b602c3d9839724250d"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="6af3a8a833eb8bb651e8b188cb3f3c3a43bb4184"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <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="e9cf0dc485a2af12353b41e9f1e41b23f3f07b41"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="ed238f7f8824973ea16e69aef08c6a1d58c7165f"/>
   <!-- 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="0e94c080bee081a50aa2097527b0b40852f9143f">
     <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="a98528f9a69dae06cbeba9b602c3d9839724250d"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="6af3a8a833eb8bb651e8b188cb3f3c3a43bb4184"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e9cf0dc485a2af12353b41e9f1e41b23f3f07b41"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="ed238f7f8824973ea16e69aef08c6a1d58c7165f"/>
   <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": "65b7d35ad078b90133481983950c64ad8fd27dd4", 
-    "repo_path": "/integration/gaia-central"
+    "revision": "a7c053f5e582c15255c49645b07a19d72e4e5cb2", 
+    "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="df362ace56338da8173d30d3e09e08c42c1accfa">
     <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="a98528f9a69dae06cbeba9b602c3d9839724250d"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="6af3a8a833eb8bb651e8b188cb3f3c3a43bb4184"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <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="e9cf0dc485a2af12353b41e9f1e41b23f3f07b41"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="ed238f7f8824973ea16e69aef08c6a1d58c7165f"/>
   <!-- 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="df362ace56338da8173d30d3e09e08c42c1accfa">
     <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="a98528f9a69dae06cbeba9b602c3d9839724250d"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="6af3a8a833eb8bb651e8b188cb3f3c3a43bb4184"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <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="0e94c080bee081a50aa2097527b0b40852f9143f">
     <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="a98528f9a69dae06cbeba9b602c3d9839724250d"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="6af3a8a833eb8bb651e8b188cb3f3c3a43bb4184"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e9cf0dc485a2af12353b41e9f1e41b23f3f07b41"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="ed238f7f8824973ea16e69aef08c6a1d58c7165f"/>
   <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="df362ace56338da8173d30d3e09e08c42c1accfa">
     <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="a98528f9a69dae06cbeba9b602c3d9839724250d"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="6af3a8a833eb8bb651e8b188cb3f3c3a43bb4184"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <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="e9cf0dc485a2af12353b41e9f1e41b23f3f07b41"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="ed238f7f8824973ea16e69aef08c6a1d58c7165f"/>
   <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/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6677,21 +6677,21 @@ var gIdentityHandler = {
     this._encryptionLabel = {};
     this._encryptionLabel[this.IDENTITY_MODE_DOMAIN_VERIFIED] =
       gNavigatorBundle.getString("identity.encrypted2");
     this._encryptionLabel[this.IDENTITY_MODE_IDENTIFIED] =
       gNavigatorBundle.getString("identity.encrypted2");
     this._encryptionLabel[this.IDENTITY_MODE_UNKNOWN] =
       gNavigatorBundle.getString("identity.unencrypted");
     this._encryptionLabel[this.IDENTITY_MODE_MIXED_DISPLAY_LOADED] =
-      gNavigatorBundle.getString("identity.mixed_display_loaded");
+      gNavigatorBundle.getString("identity.broken_loaded");
     this._encryptionLabel[this.IDENTITY_MODE_MIXED_ACTIVE_LOADED] =
       gNavigatorBundle.getString("identity.mixed_active_loaded2");
     this._encryptionLabel[this.IDENTITY_MODE_MIXED_DISPLAY_LOADED_ACTIVE_BLOCKED] =
-      gNavigatorBundle.getString("identity.mixed_display_loaded");
+      gNavigatorBundle.getString("identity.broken_loaded");
     return this._encryptionLabel;
   },
   get _identityPopup () {
     delete this._identityPopup;
     return this._identityPopup = document.getElementById("identity-popup");
   },
   get _identityBox () {
     delete this._identityBox;
--- a/browser/base/content/pageinfo/security.js
+++ b/browser/base/content/pageinfo/security.js
@@ -249,17 +249,17 @@ function securityOnLoad() {
   /* Set the Technical Detail section messages */
   const pkiBundle = document.getElementById("pkiBundle");
   var hdr;
   var msg1;
   var msg2;
 
   if (info.isBroken) {
     hdr = pkiBundle.getString("pageInfo_MixedContent");
-    msg1 = pkiBundle.getString("pageInfo_Privacy_Mixed1");
+    msg1 = pkiBundle.getString("pageInfo_Privacy_Broken1");
     msg2 = pkiBundle.getString("pageInfo_Privacy_None2");
   }
   else if (info.encryptionStrength > 0) {
     hdr = pkiBundle.getFormattedString("pageInfo_EncryptionWithBitsAndProtocol",
                                        [info.encryptionAlgorithm,
                                         info.encryptionStrength + "",
                                         info.version]);
     msg1 = pkiBundle.getString("pageInfo_Privacy_Encrypted1");
--- a/browser/devtools/debugger/test/browser.ini
+++ b/browser/devtools/debugger/test/browser.ini
@@ -425,43 +425,43 @@ skip-if = e10s
 skip-if = e10s
 [browser_dbg_tabactor-01.js]
 skip-if = e10s
 [browser_dbg_tabactor-02.js]
 skip-if = e10s
 [browser_dbg_terminate-on-tab-close.js]
 skip-if = e10s
 [browser_dbg_tracing-01.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_tracing-02.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_tracing-03.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_tracing-04.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_tracing-05.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_tracing-06.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_tracing-07.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_tracing-08.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_variables-view-01.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_variables-view-02.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_variables-view-03.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_variables-view-04.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_variables-view-05.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_variables-view-06.js]
-skip-if = e10s
+skip-if = e10s && debug
 [browser_dbg_variables-view-accessibility.js]
 skip-if = e10s
 [browser_dbg_variables-view-data.js]
 skip-if = e10s
 [browser_dbg_variables-view-edit-cancel.js]
 skip-if = e10s
 [browser_dbg_variables-view-edit-click.js]
 skip-if = e10s || (os == 'mac' || os == 'win') && (debug == false) # Bug 986166
--- a/browser/devtools/debugger/test/browser_dbg_tracing-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_tracing-01.js
@@ -2,23 +2,22 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test that we get the expected frame enter/exit logs in the tracer view.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_tracing-01.html";
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 
 function test() {
   SpecialPowers.pushPrefEnv({'set': [["devtools.debugger.tracer", true]]}, () => {
-    initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+    initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
       gTab = aTab;
-      gDebuggee = aDebuggee;
       gPanel = aPanel;
       gDebugger = gPanel.panelWin;
 
       waitForSourceShown(gPanel, "code_tracing-01.js")
         .then(() => startTracing(gPanel))
         .then(clickButton)
         .then(() => waitForClientEvents(aPanel, "traces"))
         .then(testTraceLogs)
@@ -32,19 +31,17 @@ function test() {
         .then(null, aError => {
           ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
         });
     });
   });
 }
 
 function clickButton() {
-  EventUtils.sendMouseEvent({ type: "click" },
-                            gDebuggee.document.querySelector("button"),
-                            gDebuggee);
+  sendMouseClickToTab(gTab, content.document.querySelector("button"));
 }
 
 function testTraceLogs() {
   const onclickLogs = filterTraces(gPanel,
                                    t => t.querySelector(".trace-name[value=onclick]"));
   is(onclickLogs.length, 2, "Should have two logs from 'onclick'");
   ok(onclickLogs[0].querySelector(".trace-call"),
      "The first 'onclick' log should be a call.");
@@ -98,12 +95,11 @@ function testTraceLogs() {
   ok(throwerLogs[0].querySelector(".trace-call"),
      "The first 'thrower' log should be a call.");
   ok(throwerLogs[1].querySelector(".trace-throw",
      "The second 'thrower' log should be a throw."));
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_tracing-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_tracing-02.js
@@ -2,23 +2,22 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test that we highlight matching calls and returns on hover.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_tracing-01.html";
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 
 function test() {
   SpecialPowers.pushPrefEnv({'set': [["devtools.debugger.tracer", true]]}, () => {
-    initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+    initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
       gTab = aTab;
-      gDebuggee = aDebuggee;
       gPanel = aPanel;
       gDebugger = gPanel.panelWin;
 
       waitForSourceShown(gPanel, "code_tracing-01.js")
         .then(() => startTracing(gPanel))
         .then(clickButton)
         .then(() => waitForClientEvents(aPanel, "traces"))
         .then(highlightCall)
@@ -35,19 +34,17 @@ function test() {
         .then(null, aError => {
           ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
         });
     });
   });
 }
 
 function clickButton() {
-  EventUtils.sendMouseEvent({ type: "click" },
-                            gDebuggee.document.querySelector("button"),
-                            gDebuggee);
+  sendMouseClickToTab(gTab, content.document.querySelector("button"));
 }
 
 function highlightCall() {
   const callTrace = filterTraces(gPanel, t => t.querySelector(".trace-name[value=main]"))[0];
   EventUtils.sendMouseEvent({ type: "mouseover" },
                             callTrace,
                             gDebugger);
 }
@@ -67,12 +64,11 @@ function unhighlightCall() {
 
 function testNoneHighlighted() {
   const highlightedTraces = filterTraces(gPanel, t => t.querySelector(".selected-matching"));
   is(highlightedTraces.length, 0, "Shouldn't have any highlighted traces");
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_tracing-03.js
+++ b/browser/devtools/debugger/test/browser_dbg_tracing-03.js
@@ -2,23 +2,22 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test that we can jump to function definitions by clicking on logs.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_tracing-01.html";
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 
 function test() {
   SpecialPowers.pushPrefEnv({'set': [["devtools.debugger.tracer", true]]}, () => {
-    initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+    initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
       gTab = aTab;
-      gDebuggee = aDebuggee;
       gPanel = aPanel;
       gDebugger = gPanel.panelWin;
 
       waitForSourceShown(gPanel, "code_tracing-01.js")
         .then(() => startTracing(gPanel))
         .then(clickButton)
         .then(() => waitForClientEvents(aPanel, "traces"))
         .then(() => {
@@ -43,28 +42,25 @@ function test() {
         .then(null, aError => {
           ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
         });
     });
   });
 }
 
 function clickButton() {
-  EventUtils.sendMouseEvent({ type: "click" },
-                            gDebuggee.document.querySelector("button"),
-                            gDebuggee);
+  sendMouseClickToTab(gTab, content.document.querySelector("button"));
 }
 
 function clickTraceLog() {
   filterTraces(gPanel, t => t.querySelector(".trace-name[value=main]"))[0].click();
 }
 
 function testCorrectLine() {
   is(gDebugger.DebuggerView.editor.getCursor().line, 18,
      "The editor should have the function definition site's line selected.");
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_tracing-04.js
+++ b/browser/devtools/debugger/test/browser_dbg_tracing-04.js
@@ -2,23 +2,22 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test that when we click on logs, we get the parameters/return value in the variables view.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_tracing-01.html";
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 
 function test() {
   SpecialPowers.pushPrefEnv({'set': [["devtools.debugger.tracer", true]]}, () => {
-    initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+    initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
       gTab = aTab;
-      gDebuggee = aDebuggee;
       gPanel = aPanel;
       gDebugger = gPanel.panelWin;
 
       waitForSourceShown(gPanel, "code_tracing-01.js")
         .then(() => startTracing(gPanel))
         .then(clickButton)
         .then(() => waitForClientEvents(aPanel, "traces"))
         .then(clickTraceCall)
@@ -36,19 +35,17 @@ function test() {
           DevToolsUtils.reportException("browser_dbg_tracing-04.js", aError);
           ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
         });
     });
   });
 }
 
 function clickButton() {
-  EventUtils.sendMouseEvent({ type: "click" },
-                            gDebuggee.document.querySelector("button"),
-                            gDebuggee);
+  sendMouseClickToTab(gTab, content.document.querySelector("button"));
 }
 
 function clickTraceCall() {
   filterTraces(gPanel, t => t.querySelector(".trace-name[value=factorial]"))[0]
     .click();
 }
 
 function testParams() {
@@ -73,12 +70,11 @@ function testReturn() {
 
   const value = gDebugger.document.querySelector(".variables-view-variable .value.token-number");
   ok(value, "Should have a variable value");
   is(value.getAttribute("value"), "120", "The variable value should be 120");
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_tracing-05.js
+++ b/browser/devtools/debugger/test/browser_dbg_tracing-05.js
@@ -2,24 +2,23 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test that text describing the tracing state is correctly displayed.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_tracing-01.html";
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 let gTracer, gL10N;
 
 function test() {
   SpecialPowers.pushPrefEnv({'set': [["devtools.debugger.tracer", true]]}, () => {
-    initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+    initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
       gTab = aTab;
-      gDebuggee = aDebuggee;
       gPanel = aPanel;
       gDebugger = gPanel.panelWin;
       gTracer = gDebugger.DebuggerView.Tracer;
       gL10N = gDebugger.L10N;
 
       waitForSourceShown(gPanel, "code_tracing-01.js")
         .then(testTracingNotStartedText)
         .then(() => gTracer._onStartTracing())
@@ -65,21 +64,18 @@ function testFunctionCallsUnavailableTex
 
 function testNoEmptyText() {
   let label = gDebugger.document.querySelector("#tracer-tabpanel .fast-list-widget-empty-text");
   ok(!label,
     "No label should be displayed in the tracer tabpanel.");
 }
 
 function clickButton() {
-  EventUtils.sendMouseEvent({ type: "click" },
-                            gDebuggee.document.querySelector("button"),
-                            gDebuggee);
+  sendMouseClickToTab(gTab, content.document.querySelector("button"));
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
   gTracer = null;
   gL10N = null;
 });
--- a/browser/devtools/debugger/test/browser_dbg_tracing-06.js
+++ b/browser/devtools/debugger/test/browser_dbg_tracing-06.js
@@ -3,37 +3,35 @@
 
 /**
  * Test that the tracer doesn't connect to the backend when tracing is disabled.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_tracing-01.html";
 const TRACER_PREF = "devtools.debugger.tracer";
 
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
 let gOriginalPref = Services.prefs.getBoolPref(TRACER_PREF);
 Services.prefs.setBoolPref(TRACER_PREF, false);
 
 function test() {
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
 
     waitForSourceShown(gPanel, "code_tracing-01.js")
       .then(() => {
         ok(!gDebugger.DebuggerController.traceClient, "Should not have a trace client");
         closeDebuggerAndFinish(gPanel);
       })
       .then(null, aError => {
         ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
       });
   });
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
   gDebugger = null;
   Services.prefs.setBoolPref(TRACER_PREF, gOriginalPref);
 });
--- a/browser/devtools/debugger/test/browser_dbg_tracing-07.js
+++ b/browser/devtools/debugger/test/browser_dbg_tracing-07.js
@@ -3,23 +3,23 @@
 
 /**
  * Execute code both before and after blackboxing and test that we get
  * appropriately styled traces.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_tracing-01.html";
 
-let gTab, gDebuggee, gPanel;
+let gTab, gPanel;
 
 function test() {
   Task.async(function*() {
     yield pushPref();
 
-    [gTab, gDebuggee, gPanel] = yield initDebugger(TAB_URL);
+    [gTab,, gPanel] = yield initDebugger(TAB_URL);
 
     yield startTracing(gPanel);
     yield clickButton();
     yield waitForClientEvents(gPanel, "traces");
 
     /**
      * Test that there are some traces which are not blackboxed.
      */
@@ -55,19 +55,17 @@ function test() {
     finish();
   })().catch(e => {
     ok(false, "Got an error: " + e.message + "\n" + e.stack);
     finish();
   });
 }
 
 function clickButton() {
-  EventUtils.sendMouseEvent({ type: "click" },
-                            gDebuggee.document.querySelector("button"),
-                            gDebuggee);
+  sendMouseClickToTab(gTab, content.document.querySelector("button"));
 }
 
 function pushPref() {
   let deferred = promise.defer();
   SpecialPowers.pushPrefEnv({'set': [["devtools.debugger.tracer", true]]},
     deferred.resolve);
   return deferred.promise;
 }
@@ -75,12 +73,11 @@ function pushPref() {
 function popPref() {
   let deferred = promise.defer();
   SpecialPowers.popPrefEnv(deferred.resolve);
   return deferred.promise;
 }
 
 registerCleanupFunction(function() {
   gTab = null;
-  gDebuggee = null;
   gPanel = null;
 });
 
--- a/browser/devtools/debugger/test/browser_dbg_variables-view-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_variables-view-01.js
@@ -4,17 +4,17 @@
 /**
  * Tests that creating, collpasing and expanding scopes in the
  * variables view works as expected.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_recursion-stack.html";
 
 function test() {
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     let variables = aPanel.panelWin.DebuggerView.Variables;
     let testScope = variables.addScope("test");
 
     ok(testScope,
       "Should have created a scope.");
     ok(testScope.id.contains("test"),
       "The newly created scope should have the default id set.");
     is(testScope.name, "test",
--- a/browser/devtools/debugger/test/browser_dbg_variables-view-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_variables-view-02.js
@@ -4,17 +4,17 @@
 /**
  * Tests that creating, collpasing and expanding variables in the
  * variables view works as expected.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_recursion-stack.html";
 
 function test() {
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     let variables = aPanel.panelWin.DebuggerView.Variables;
     let testScope = variables.addScope("test");
     let testVar = testScope.addItem("something");
     let duplVar = testScope.addItem("something");
 
     info("Scope id: " + testScope.id);
     info("Scope name: " + testScope.name);
     info("Variable id: " + testVar.id);
--- a/browser/devtools/debugger/test/browser_dbg_variables-view-03.js
+++ b/browser/devtools/debugger/test/browser_dbg_variables-view-03.js
@@ -4,17 +4,17 @@
 /**
  * Tests that recursively creating properties in the variables view works
  * as expected.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_recursion-stack.html";
 
 function test() {
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     let variables = aPanel.panelWin.DebuggerView.Variables;
     let testScope = variables.addScope("test");
 
     is(testScope.target.querySelectorAll(".variables-view-element-details.enum").length, 1,
       "One enumerable container should be present in the scope.");
     is(testScope.target.querySelectorAll(".variables-view-element-details.nonenum").length, 1,
       "One non-enumerable container should be present in the scope.");
     is(testScope.target.querySelector(".variables-view-element-details.enum").childNodes.length, 0,
--- a/browser/devtools/debugger/test/browser_dbg_variables-view-04.js
+++ b/browser/devtools/debugger/test/browser_dbg_variables-view-04.js
@@ -3,17 +3,17 @@
 
 /**
  * Tests that grips are correctly applied to variables.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_recursion-stack.html";
 
 function test() {
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     let variables = aPanel.panelWin.DebuggerView.Variables;
     let testScope = variables.addScope("test");
     let testVar = testScope.addItem("something");
 
     testVar.setGrip(1.618);
 
     is(testVar.target.querySelector(".value").getAttribute("value"), "1.618",
       "The grip information for the variable wasn't set correctly (1).");
--- a/browser/devtools/debugger/test/browser_dbg_variables-view-05.js
+++ b/browser/devtools/debugger/test/browser_dbg_variables-view-05.js
@@ -3,17 +3,17 @@
 
 /**
  * Tests that grips are correctly applied to variables and properties.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_recursion-stack.html";
 
 function test() {
-  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     let variables = aPanel.panelWin.DebuggerView.Variables;
 
     let globalScope = variables.addScope("Test-Global");
     let localScope = variables.addScope("Test-Local");
 
     ok(globalScope, "The globalScope hasn't been created correctly.");
     ok(localScope, "The localScope hasn't been created correctly.");
 
--- a/browser/devtools/debugger/test/browser_dbg_variables-view-06.js
+++ b/browser/devtools/debugger/test/browser_dbg_variables-view-06.js
@@ -3,21 +3,21 @@
 
 /**
  * Test that Promises get their internal state added as psuedo properties.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_promise.html";
 
 const test = Task.async(function* () {
-  const [tab, debuggee, panel] = yield initDebugger(TAB_URL);
+  const [tab,, panel] = yield initDebugger(TAB_URL);
   yield ensureSourceIs(panel, "doc_promise.html", true);
 
   const scopes = waitForCaretAndScopes(panel, 21);
-  executeSoon(debuggee.doPause);
+  callInTab(tab, "doPause");
   yield scopes;
 
   const variables = panel.panelWin.DebuggerView.Variables;
   ok(variables, "Should get the variables view.");
 
   const scope = [...variables][0];
   ok(scope, "Should get the current function's scope.");
 
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -290,17 +290,17 @@ offlineApps.manageUsageAccessKey=S
 # %2$S a number of megabytes.
 indexedDB.usage=This website (%1$S) is attempting to store more than %2$S MB of data on your computer for offline use.
 
 identity.identified.verifier=Verified by: %S
 identity.identified.verified_by_you=You have added a security exception for this site.
 identity.identified.state_and_country=%S, %S
 
 identity.encrypted2=The connection to this website is secure.
-identity.mixed_display_loaded=The connection to this website is not fully secure because it contains unencrypted elements (such as images).
+identity.broken_loaded=The connection to this website is not fully secure because it contains unencrypted elements (such as images) or the encryption is not strong enough.
 identity.mixed_active_loaded2=This website contains interactive content that isn't encrypted (such as scripts). Other people can view your information or modify the website's behavior.
 identity.unencrypted=Your connection to this website is not encrypted.
 
 identity.unknown.tooltip=This website does not supply identity information.
 
 # LOCALIZATION NOTE (identity.chrome): %S is replaced with the brandShortName.
 identity.chrome=This is a secure %S page.
 
--- a/configure.in
+++ b/configure.in
@@ -3292,34 +3292,16 @@ AC_CACHE_CHECK(whether C++ requires impl
                                X x;,
                                ac_cv_cpp_unused_required=no,
                                ac_cv_cpp_unused_required=yes)])
 if test "$ac_cv_cpp_unused_required" = yes ; then
    AC_DEFINE(NEED_CPP_UNUSED_IMPLEMENTATIONS)
 fi
 
 
-dnl Some compilers have trouble comparing a constant reference to a templatized
-dnl class to zero, and require an explicit operator==() to be defined that takes
-dnl an int. This test separates the strong from the weak.
-
-AC_CACHE_CHECK(for trouble comparing to zero near std::operator!=(),
-               ac_cv_trouble_comparing_to_zero,
-               [AC_TRY_COMPILE([#include <algorithm>
-                                template <class T> class Foo {};
-                                class T2;
-                                template <class T> int operator==(const T2*, const T&) { return 0; }
-                                template <class T> int operator!=(const T2*, const T&) { return 0; }],
-                               [Foo<int> f; return (0 != f);],
-                               ac_cv_trouble_comparing_to_zero=no,
-                               ac_cv_trouble_comparing_to_zero=yes)])
-if test "$ac_cv_trouble_comparing_to_zero" = yes ; then
-  AC_DEFINE(HAVE_CPP_TROUBLE_COMPARING_TO_ZERO)
-fi
-
 # try harder, when checking for __thread support, see bug 521750 comment #33 and below
 # We pass MOZ_OPTIMIZE_LDFLAGS to the linker because if dead_strip is
 # enabled, the linker in xcode 4.1 will crash. Without this it would crash when
 # linking XUL.
 _SAVE_LDFLAGS=$LDFLAGS
 LDFLAGS="$LDFLAGS $DSO_PIC_CFLAGS $DSO_LDOPTS $MOZ_OPTIMIZE_LDFLAGS"
 AC_CACHE_CHECK(for __thread keyword for TLS variables,
                ac_cv_thread_keyword,
@@ -9034,17 +9016,16 @@ dnl Spit out some output
 dnl ========================================================
 
 dnl The following defines are used by xpcom
 _NON_GLOBAL_ACDEFINES="$_NON_GLOBAL_ACDEFINES
 CPP_THROW_NEW
 HAVE_CPP_AMBIGUITY_RESOLVING_USING
 HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
 HAVE_CPP_PARTIAL_SPECIALIZATION
-HAVE_CPP_TROUBLE_COMPARING_TO_ZERO
 NEED_CPP_UNUSED_IMPLEMENTATIONS
 HAVE_GETPAGESIZE
 HAVE_ICONV
 HAVE_ICONV_WITH_CONST_INPUT
 HAVE_MBRTOWC
 HAVE_WCRTOMB
 HAVE_STATVFS64
 HAVE_STATVFS
--- a/dom/apps/tests/mochitest.ini
+++ b/dom/apps/tests/mochitest.ini
@@ -21,24 +21,26 @@ support-files =
   signed_app_template.webapp
   signed/*
   test_packaged_app_common.js
   marketplace/*
   pkg_install_iframe.html
 
 [test_app_enabled.html]
 [test_app_update.html]
+skip-if = os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app
 [test_bug_795164.html]
 [test_import_export.html]
 [test_install_multiple_apps_origin.html]
 [test_install_receipts.html]
 [test_marketplace_pkg_install.html]
 skip-if = buildapp == "b2g" || toolkit == "android" # see bug 989806
 [test_packaged_app_install.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
 [test_packaged_app_update.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
 [test_receipt_operations.html]
 [test_signed_pkg_install.html]
 [test_uninstall_errors.html]
 [test_theme_role.html]
 [test_web_app_install.html]
 [test_widget.html]
+skip-if = os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app
--- a/dom/base/nsContentAreaDragDrop.cpp
+++ b/dom/base/nsContentAreaDragDrop.cpp
@@ -510,49 +510,38 @@ DragDataProducer::Produce(DataTransfer* 
         mHtmlString.AppendLiteral("</a>");
 
         dragNode = draggedNode;
       } else if (image) {
         mIsAnchor = true;
         // grab the href as the url, use alt text as the title of the
         // area if it's there.  the drag data is the image tag and src
         // attribute.
-        nsCOMPtr<nsIURI> imageURI;
-        image->GetCurrentURI(getter_AddRefs(imageURI));
-        if (imageURI) {
-          nsAutoCString spec;
-          imageURI->GetSpec(spec);
-          CopyUTF8toUTF16(spec, mUrlString);
-        }
-
         nsCOMPtr<nsIDOMElement> imageElement(do_QueryInterface(image));
         // XXXbz Shouldn't we use the "title" attr for title?  Using
         // "alt" seems very wrong....
         if (imageElement) {
           imageElement->GetAttribute(NS_LITERAL_STRING("alt"), mTitleString);
         }
 
-        if (mTitleString.IsEmpty()) {
-          mTitleString = mUrlString;
-        }
-
-        nsCOMPtr<imgIRequest> imgRequest;
+        mUrlString.Truncate();
 
         // grab the image data, and its request.
+        nsCOMPtr<imgIRequest> imgRequest;
         nsCOMPtr<imgIContainer> img =
           nsContentUtils::GetImageFromContent(image,
                                               getter_AddRefs(imgRequest));
 
         nsCOMPtr<nsIMIMEService> mimeService =
           do_GetService("@mozilla.org/mime;1");
 
         // Fix the file extension in the URL if necessary
         if (imgRequest && mimeService) {
           nsCOMPtr<nsIURI> imgUri;
-          imgRequest->GetURI(getter_AddRefs(imgUri));
+          imgRequest->GetCurrentURI(getter_AddRefs(imgUri));
 
           nsCOMPtr<nsIURL> imgUrl(do_QueryInterface(imgUri));
 
           if (imgUrl) {
             nsAutoCString extension;
             imgUrl->GetFileExtension(extension);
 
             nsXPIDLCString mimeType;
@@ -563,16 +552,17 @@ DragDataProducer::Produce(DataTransfer* 
                                                  getter_AddRefs(mimeInfo));
 
             if (mimeInfo) {
               nsAutoCString spec;
               imgUrl->GetSpec(spec);
 
               // pass out the image source string
               CopyUTF8toUTF16(spec, mImageSourceString);
+              mUrlString = mImageSourceString;
 
               bool validExtension;
               if (extension.IsEmpty() || 
                   NS_FAILED(mimeInfo->ExtensionExists(extension,
                                                       &validExtension)) ||
                   !validExtension) {
                 // Fix the file extension in the URL
                 nsresult rv = imgUrl->Clone(getter_AddRefs(imgUri));
@@ -597,16 +587,28 @@ DragDataProducer::Produce(DataTransfer* 
 
               CopyUTF8toUTF16(fileName, mImageDestFileName);
 
               // and the image object
               mImage = img;
             }
           }
         }
+        if (mUrlString.IsEmpty()) {
+          nsCOMPtr<nsIURI> imageURI;
+          image->GetCurrentURI(getter_AddRefs(imageURI));
+          if (imageURI) {
+            nsAutoCString spec;
+            imageURI->GetSpec(spec);
+            CopyUTF8toUTF16(spec, mUrlString);
+          }
+        }
+        if (mTitleString.IsEmpty()) {
+          mTitleString = mUrlString;
+        }
 
         if (parentLink) {
           // If we are dragging around an image in an anchor, then we
           // are dragging the entire anchor
           linkNode = parentLink;
           nodeToSerialize = linkNode;
         } else {
           nodeToSerialize = do_QueryInterface(draggedNode);
--- a/dom/bluetooth/bluedroid/BluetoothOppManager.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothOppManager.cpp
@@ -526,16 +526,17 @@ void
 BluetoothOppManager::AfterOppDisconnected()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   mConnected = false;
   mLastCommand = 0;
   mPutPacketReceivedLength = 0;
   mDsFile = nullptr;
+  mDummyDsFile = nullptr;
 
   // We can't reset mSuccessFlag here since this function may be called
   // before we send system message of transfer complete
   // mSuccessFlag = false;
 
   if (mInputStream) {
     mInputStream->Close();
     mInputStream = nullptr;
@@ -553,53 +554,97 @@ BluetoothOppManager::AfterOppDisconnecte
   // Release the Mount lock if file transfer completed
   if (mMountLock) {
     // The mount lock will be implicitly unlocked
     mMountLock = nullptr;
   }
 }
 
 void
+BluetoothOppManager::RecoverFileName()
+{
+  // Remove the trailing ".part" file name from mDsFile by two steps
+  // 1. mDsFile->SetPath() so that the notification sent to Gaia will carry
+  //    correct information of the file.
+  // 2. mDsFile->mFile->RenameTo() so that the file name would actually be
+  //    changed in file system.
+  if (mDsFile && mDsFile->mFile) {
+    nsString path;
+    path.AssignLiteral(TARGET_SUBDIR);
+    path.Append(mFileName);
+
+    mDsFile->SetPath(path);
+    mDsFile->mFile->RenameTo(nullptr, mFileName);
+  }
+}
+
+void
+BluetoothOppManager::DeleteDummyFile()
+{
+  // Remove the empty temp file
+  if (mDummyDsFile && mDummyDsFile->mFile) {
+    mDummyDsFile->mFile->Remove(false);
+    mDummyDsFile = nullptr;
+  }
+}
+
+void
 BluetoothOppManager::DeleteReceivedFile()
 {
   if (mOutputStream) {
     mOutputStream->Close();
     mOutputStream = nullptr;
   }
 
   if (mDsFile && mDsFile->mFile) {
     mDsFile->mFile->Remove(false);
     mDsFile = nullptr;
   }
+
+  DeleteDummyFile();
 }
 
 bool
 BluetoothOppManager::CreateFile()
 {
   MOZ_ASSERT(mPutPacketReceivedLength == mPacketLength);
 
+  // Create one dummy file to be a placeholder for the target file name, and
+  // create another file with a meaningless file extension to write the received
+  // data. By doing this, we can prevent applications from parsing incomplete
+  // data in the middle of the receiving process.
   nsString path;
   path.AssignLiteral(TARGET_SUBDIR);
   path.Append(mFileName);
 
-  mDsFile = DeviceStorageFile::CreateUnique(
-              path, nsIFile::NORMAL_FILE_TYPE, 0644);
+  // Use an empty dummy file object to occupy the file name, so that after the
+  // whole file has been received successfully by using mDsFile, we could just
+  // remove mDummyDsFile and rename mDsFile to the file name of mDummyDsFile.
+  mDummyDsFile =
+    DeviceStorageFile::CreateUnique(path, nsIFile::NORMAL_FILE_TYPE, 0644);
+  NS_ENSURE_TRUE(mDummyDsFile, false);
+
+  // The function CreateUnique() may create a file with a different file
+  // name from the original mFileName. Therefore we have to retrieve
+  // the file name again.
+  mDummyDsFile->mFile->GetLeafName(mFileName);
+
+  BT_LOGR("mFileName: %s", NS_ConvertUTF16toUTF8(mFileName).get());
+
+  // Prepare the entire file path for the .part file
+  path.Truncate();
+  path.AssignLiteral(TARGET_SUBDIR);
+  path.Append(mFileName);
+  path.AppendLiteral(".part");
+
+  mDsFile =
+    DeviceStorageFile::CreateUnique(path, nsIFile::NORMAL_FILE_TYPE, 0644);
   NS_ENSURE_TRUE(mDsFile, false);
 
-  nsCOMPtr<nsIFile> f;
-  mDsFile->mFile->Clone(getter_AddRefs(f));
-
-  /*
-   * The function CreateUnique() may create a file with a different file
-   * name from the original mFileName. Therefore we have to retrieve
-   * the file name again.
-   */
-  f->GetLeafName(mFileName);
-
-  NS_NewLocalFileOutputStream(getter_AddRefs(mOutputStream), f);
+  NS_NewLocalFileOutputStream(getter_AddRefs(mOutputStream), mDsFile->mFile);
   NS_ENSURE_TRUE(mOutputStream, false);
 
   return true;
 }
 
 bool
 BluetoothOppManager::WriteToFile(const uint8_t* aData, int aDataLength)
 {
@@ -906,16 +951,20 @@ BluetoothOppManager::ServerDataHandler(U
     if (mSentFileLength > kUpdateProgressBase * mUpdateProgressCounter) {
       UpdateProgress();
       mUpdateProgressCounter = mSentFileLength / kUpdateProgressBase + 1;
     }
 
     // Success to receive a file and notify completion
     if (mPutFinalFlag) {
       mSuccessFlag = true;
+
+      DeleteDummyFile();
+      RecoverFileName();
+
       FileTransferComplete();
       NotifyAboutFileChange();
     }
   } else if (opCode == ObexRequestCode::Get ||
              opCode == ObexRequestCode::GetFinal ||
              opCode == ObexRequestCode::SetPath) {
     ReplyError(ObexResponseCode::BadRequest);
     BT_WARNING("Unsupported ObexRequestCode");
--- a/dom/bluetooth/bluedroid/BluetoothOppManager.h
+++ b/dom/bluetooth/bluedroid/BluetoothOppManager.h
@@ -80,16 +80,18 @@ private:
 
   void StartFileTransfer();
   void StartSendingNextFile();
   void FileTransferComplete();
   void UpdateProgress();
   void ReceivingFileConfirmation();
   bool CreateFile();
   bool WriteToFile(const uint8_t* aData, int aDataLength);
+  void RecoverFileName();
+  void DeleteDummyFile();
   void DeleteReceivedFile();
   void ReplyToConnect();
   void ReplyToDisconnectOrAbort();
   void ReplyToPut(bool aFinal, bool aContinue);
   void ReplyError(uint8_t aError);
   void AfterOppConnected();
   void AfterFirstPut();
   void AfterOppDisconnected();
@@ -204,16 +206,17 @@ private:
    * A seperate member thread is required because our read calls can block
    * execution, which is not allowed to happen on the IOThread.
    */
   nsCOMPtr<nsIThread> mReadFileThread;
   nsCOMPtr<nsIOutputStream> mOutputStream;
   nsCOMPtr<nsIInputStream> mInputStream;
   nsCOMPtr<nsIVolumeMountLock> mMountLock;
   nsRefPtr<DeviceStorageFile> mDsFile;
+  nsRefPtr<DeviceStorageFile> mDummyDsFile;
 
   // If a connection has been established, mSocket will be the socket
   // communicating with the remote socket. We maintain the invariant that if
   // mSocket is non-null, mServerSocket must be null (and vice versa).
   nsRefPtr<BluetoothSocket> mSocket;
 
   // Server sockets. Once an inbound connection is established, it will hand
   // over the ownership to mSocket, and get a new server socket while Listen()
--- a/dom/bluetooth/bluez/BluetoothOppManager.cpp
+++ b/dom/bluetooth/bluez/BluetoothOppManager.cpp
@@ -548,16 +548,17 @@ void
 BluetoothOppManager::AfterOppDisconnected()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   mConnected = false;
   mLastCommand = 0;
   mPutPacketReceivedLength = 0;
   mDsFile = nullptr;
+  mDummyDsFile = nullptr;
 
   // We can't reset mSuccessFlag here since this function may be called
   // before we send system message of transfer complete
   // mSuccessFlag = false;
 
   if (mInputStream) {
     mInputStream->Close();
     mInputStream = nullptr;
@@ -575,53 +576,97 @@ BluetoothOppManager::AfterOppDisconnecte
   // Release the Mount lock if file transfer completed
   if (mMountLock) {
     // The mount lock will be implicitly unlocked
     mMountLock = nullptr;
   }
 }
 
 void
+BluetoothOppManager::RecoverFileName()
+{
+  // Remove the trailing ".part" file name from mDsFile by two steps
+  // 1. mDsFile->SetPath() so that the notification sent to Gaia will carry
+  //    correct information of the file.
+  // 2. mDsFile->mFile->RenameTo() so that the file name would actually be
+  //    changed in file system.
+  if (mDsFile && mDsFile->mFile) {
+    nsString path;
+    path.AssignLiteral(TARGET_SUBDIR);
+    path.Append(mFileName);
+
+    mDsFile->SetPath(path);
+    mDsFile->mFile->RenameTo(nullptr, mFileName);
+  }
+}
+
+void
+BluetoothOppManager::DeleteDummyFile()
+{
+  // Remove the empty temp file
+  if (mDummyDsFile && mDummyDsFile->mFile) {
+    mDummyDsFile->mFile->Remove(false);
+    mDummyDsFile = nullptr;
+  }
+}
+
+void
 BluetoothOppManager::DeleteReceivedFile()
 {
   if (mOutputStream) {
     mOutputStream->Close();
     mOutputStream = nullptr;
   }
 
   if (mDsFile && mDsFile->mFile) {
     mDsFile->mFile->Remove(false);
     mDsFile = nullptr;
   }
+
+  DeleteDummyFile();
 }
 
 bool
 BluetoothOppManager::CreateFile()
 {
   MOZ_ASSERT(mPutPacketReceivedLength == mPacketLength);
 
+  // Create one dummy file to be a placeholder for the target file name, and
+  // create another file with a meaningless file extension to write the received
+  // data. By doing this, we can prevent applications from parsing incomplete
+  // data in the middle of the receiving process.
   nsString path;
   path.AssignLiteral(TARGET_SUBDIR);
   path.Append(mFileName);
 
-  mDsFile = DeviceStorageFile::CreateUnique(
-              path, nsIFile::NORMAL_FILE_TYPE, 0644);
+  // Use an empty dummy file object to occupy the file name, so that after the
+  // whole file has been received successfully by using mDsFile, we could just
+  // remove mDummyDsFile and rename mDsFile to the file name of mDummyDsFile.
+  mDummyDsFile =
+    DeviceStorageFile::CreateUnique(path, nsIFile::NORMAL_FILE_TYPE, 0644);
+  NS_ENSURE_TRUE(mDummyDsFile, false);
+
+  // The function CreateUnique() may create a file with a different file
+  // name from the original mFileName. Therefore we have to retrieve
+  // the file name again.
+  mDummyDsFile->mFile->GetLeafName(mFileName);
+
+  BT_LOGR("mFileName: %s", NS_ConvertUTF16toUTF8(mFileName).get());
+
+  // Prepare the entire file path for the .part file
+  path.Truncate();
+  path.AssignLiteral(TARGET_SUBDIR);
+  path.Append(mFileName);
+  path.AppendLiteral(".part");
+
+  mDsFile =
+    DeviceStorageFile::CreateUnique(path, nsIFile::NORMAL_FILE_TYPE, 0644);
   NS_ENSURE_TRUE(mDsFile, false);
 
-  nsCOMPtr<nsIFile> f;
-  mDsFile->mFile->Clone(getter_AddRefs(f));
-
-  /*
-   * The function CreateUnique() may create a file with a different file
-   * name from the original mFileName. Therefore we have to retrieve
-   * the file name again.
-   */
-  f->GetLeafName(mFileName);
-
-  NS_NewLocalFileOutputStream(getter_AddRefs(mOutputStream), f);
+  NS_NewLocalFileOutputStream(getter_AddRefs(mOutputStream), mDsFile->mFile);
   NS_ENSURE_TRUE(mOutputStream, false);
 
   return true;
 }
 
 bool
 BluetoothOppManager::WriteToFile(const uint8_t* aData, int aDataLength)
 {
@@ -927,16 +972,20 @@ BluetoothOppManager::ServerDataHandler(U
     if (mSentFileLength > kUpdateProgressBase * mUpdateProgressCounter) {
       UpdateProgress();
       mUpdateProgressCounter = mSentFileLength / kUpdateProgressBase + 1;
     }
 
     // Success to receive a file and notify completion
     if (mPutFinalFlag) {
       mSuccessFlag = true;
+
+      DeleteDummyFile();
+      RecoverFileName();
+
       FileTransferComplete();
       NotifyAboutFileChange();
     }
   } else if (opCode == ObexRequestCode::Get ||
              opCode == ObexRequestCode::GetFinal ||
              opCode == ObexRequestCode::SetPath) {
     ReplyError(ObexResponseCode::BadRequest);
     BT_WARNING("Unsupported ObexRequestCode");
--- a/dom/bluetooth/bluez/BluetoothOppManager.h
+++ b/dom/bluetooth/bluez/BluetoothOppManager.h
@@ -80,16 +80,18 @@ private:
 
   void StartFileTransfer();
   void StartSendingNextFile();
   void FileTransferComplete();
   void UpdateProgress();
   void ReceivingFileConfirmation();
   bool CreateFile();
   bool WriteToFile(const uint8_t* aData, int aDataLength);
+  void RecoverFileName();
+  void DeleteDummyFile();
   void DeleteReceivedFile();
   void ReplyToConnect();
   void ReplyToDisconnectOrAbort();
   void ReplyToPut(bool aFinal, bool aContinue);
   void ReplyError(uint8_t aError);
   void AfterOppConnected();
   void AfterFirstPut();
   void AfterOppDisconnected();
@@ -204,16 +206,17 @@ private:
    * A seperate member thread is required because our read calls can block
    * execution, which is not allowed to happen on the IOThread.
    */
   nsCOMPtr<nsIThread> mReadFileThread;
   nsCOMPtr<nsIOutputStream> mOutputStream;
   nsCOMPtr<nsIInputStream> mInputStream;
   nsCOMPtr<nsIVolumeMountLock> mMountLock;
   nsRefPtr<DeviceStorageFile> mDsFile;
+  nsRefPtr<DeviceStorageFile> mDummyDsFile;
 
   // If a connection has been established, mSocket will be the socket
   // communicating with the remote socket. We maintain the invariant that if
   // mSocket is non-null, mRfcommSocket and mL2capSocket must be null (and vice
   // versa).
   nsRefPtr<BluetoothSocket> mSocket;
 
   // Server sockets. Once an inbound connection is established, it will hand
--- a/dom/bluetooth2/bluedroid/BluetoothOppManager.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothOppManager.cpp
@@ -526,16 +526,17 @@ void
 BluetoothOppManager::AfterOppDisconnected()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   mConnected = false;
   mLastCommand = 0;
   mPutPacketReceivedLength = 0;
   mDsFile = nullptr;
+  mDummyDsFile = nullptr;
 
   // We can't reset mSuccessFlag here since this function may be called
   // before we send system message of transfer complete
   // mSuccessFlag = false;
 
   if (mInputStream) {
     mInputStream->Close();
     mInputStream = nullptr;
@@ -553,53 +554,97 @@ BluetoothOppManager::AfterOppDisconnecte
   // Release the Mount lock if file transfer completed
   if (mMountLock) {
     // The mount lock will be implicitly unlocked
     mMountLock = nullptr;
   }
 }
 
 void
+BluetoothOppManager::RecoverFileName()
+{
+  // Remove the trailing ".part" file name from mDsFile by two steps
+  // 1. mDsFile->SetPath() so that the notification sent to Gaia will carry
+  //    correct information of the file.
+  // 2. mDsFile->mFile->RenameTo() so that the file name would actually be
+  //    changed in file system.
+  if (mDsFile && mDsFile->mFile) {
+    nsString path;
+    path.AssignLiteral(TARGET_SUBDIR);
+    path.Append(mFileName);
+
+    mDsFile->SetPath(path);
+    mDsFile->mFile->RenameTo(nullptr, mFileName);
+  }
+}
+
+void
+BluetoothOppManager::DeleteDummyFile()
+{
+  // Remove the empty temp file
+  if (mDummyDsFile && mDummyDsFile->mFile) {
+    mDummyDsFile->mFile->Remove(false);
+    mDummyDsFile = nullptr;
+  }
+}
+
+void
 BluetoothOppManager::DeleteReceivedFile()
 {
   if (mOutputStream) {
     mOutputStream->Close();
     mOutputStream = nullptr;
   }
 
   if (mDsFile && mDsFile->mFile) {
     mDsFile->mFile->Remove(false);
     mDsFile = nullptr;
   }
+
+  DeleteDummyFile();
 }
 
 bool
 BluetoothOppManager::CreateFile()
 {
   MOZ_ASSERT(mPutPacketReceivedLength == mPacketLength);
 
+  // Create one dummy file to be a placeholder for the target file name, and
+  // create another file with a meaningless file extension to write the received
+  // data. By doing this, we can prevent applications from parsing incomplete
+  // data in the middle of the receiving process.
   nsString path;
   path.AssignLiteral(TARGET_SUBDIR);
   path.Append(mFileName);
 
-  mDsFile = DeviceStorageFile::CreateUnique(
-              path, nsIFile::NORMAL_FILE_TYPE, 0644);
+  // Use an empty dummy file object to occupy the file name, so that after the
+  // whole file has been received successfully by using mDsFile, we could just
+  // remove mDummyDsFile and rename mDsFile to the file name of mDummyDsFile.
+  mDummyDsFile =
+    DeviceStorageFile::CreateUnique(path, nsIFile::NORMAL_FILE_TYPE, 0644);
+  NS_ENSURE_TRUE(mDummyDsFile, false);
+
+  // The function CreateUnique() may create a file with a different file
+  // name from the original mFileName. Therefore we have to retrieve
+  // the file name again.
+  mDummyDsFile->mFile->GetLeafName(mFileName);
+
+  BT_LOGR("mFileName: %s", NS_ConvertUTF16toUTF8(mFileName).get());
+
+  // Prepare the entire file path for the .part file
+  path.Truncate();
+  path.AssignLiteral(TARGET_SUBDIR);
+  path.Append(mFileName);
+  path.AppendLiteral(".part");
+
+  mDsFile =
+    DeviceStorageFile::CreateUnique(path, nsIFile::NORMAL_FILE_TYPE, 0644);
   NS_ENSURE_TRUE(mDsFile, false);
 
-  nsCOMPtr<nsIFile> f;
-  mDsFile->mFile->Clone(getter_AddRefs(f));
-
-  /*
-   * The function CreateUnique() may create a file with a different file
-   * name from the original mFileName. Therefore we have to retrieve
-   * the file name again.
-   */
-  f->GetLeafName(mFileName);
-
-  NS_NewLocalFileOutputStream(getter_AddRefs(mOutputStream), f);
+  NS_NewLocalFileOutputStream(getter_AddRefs(mOutputStream), mDsFile->mFile);
   NS_ENSURE_TRUE(mOutputStream, false);
 
   return true;
 }
 
 bool
 BluetoothOppManager::WriteToFile(const uint8_t* aData, int aDataLength)
 {
@@ -906,16 +951,20 @@ BluetoothOppManager::ServerDataHandler(U
     if (mSentFileLength > kUpdateProgressBase * mUpdateProgressCounter) {
       UpdateProgress();
       mUpdateProgressCounter = mSentFileLength / kUpdateProgressBase + 1;
     }
 
     // Success to receive a file and notify completion
     if (mPutFinalFlag) {
       mSuccessFlag = true;
+
+      DeleteDummyFile();
+      RecoverFileName();
+
       FileTransferComplete();
       NotifyAboutFileChange();
     }
   } else if (opCode == ObexRequestCode::Get ||
              opCode == ObexRequestCode::GetFinal ||
              opCode == ObexRequestCode::SetPath) {
     ReplyError(ObexResponseCode::BadRequest);
     BT_WARNING("Unsupported ObexRequestCode");
--- a/dom/bluetooth2/bluedroid/BluetoothOppManager.h
+++ b/dom/bluetooth2/bluedroid/BluetoothOppManager.h
@@ -80,16 +80,18 @@ private:
 
   void StartFileTransfer();
   void StartSendingNextFile();
   void FileTransferComplete();
   void UpdateProgress();
   void ReceivingFileConfirmation();
   bool CreateFile();
   bool WriteToFile(const uint8_t* aData, int aDataLength);
+  void RecoverFileName();
+  void DeleteDummyFile();
   void DeleteReceivedFile();
   void ReplyToConnect();
   void ReplyToDisconnectOrAbort();
   void ReplyToPut(bool aFinal, bool aContinue);
   void ReplyError(uint8_t aError);
   void AfterOppConnected();
   void AfterFirstPut();
   void AfterOppDisconnected();
@@ -204,16 +206,17 @@ private:
    * A seperate member thread is required because our read calls can block
    * execution, which is not allowed to happen on the IOThread.
    */
   nsCOMPtr<nsIThread> mReadFileThread;
   nsCOMPtr<nsIOutputStream> mOutputStream;
   nsCOMPtr<nsIInputStream> mInputStream;
   nsCOMPtr<nsIVolumeMountLock> mMountLock;
   nsRefPtr<DeviceStorageFile> mDsFile;
+  nsRefPtr<DeviceStorageFile> mDummyDsFile;
 
   // If a connection has been established, mSocket will be the socket
   // communicating with the remote socket. We maintain the invariant that if
   // mSocket is non-null, mServerSocket must be null (and vice versa).
   nsRefPtr<BluetoothSocket> mSocket;
 
   // Server sockets. Once an inbound connection is established, it will hand
   // over the ownership to mSocket, and get a new server socket while Listen()
--- a/dom/bluetooth2/bluez/BluetoothOppManager.cpp
+++ b/dom/bluetooth2/bluez/BluetoothOppManager.cpp
@@ -548,16 +548,17 @@ void
 BluetoothOppManager::AfterOppDisconnected()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   mConnected = false;
   mLastCommand = 0;
   mPutPacketReceivedLength = 0;
   mDsFile = nullptr;
+  mDummyDsFile = nullptr;
 
   // We can't reset mSuccessFlag here since this function may be called
   // before we send system message of transfer complete
   // mSuccessFlag = false;
 
   if (mInputStream) {
     mInputStream->Close();
     mInputStream = nullptr;
@@ -575,53 +576,97 @@ BluetoothOppManager::AfterOppDisconnecte
   // Release the Mount lock if file transfer completed
   if (mMountLock) {
     // The mount lock will be implicitly unlocked
     mMountLock = nullptr;
   }
 }
 
 void
+BluetoothOppManager::RecoverFileName()
+{
+  // Remove the trailing ".part" file name from mDsFile by two steps
+  // 1. mDsFile->SetPath() so that the notification sent to Gaia will carry
+  //    correct information of the file.
+  // 2. mDsFile->mFile->RenameTo() so that the file name would actually be
+  //    changed in file system.
+  if (mDsFile && mDsFile->mFile) {
+    nsString path;
+    path.AssignLiteral(TARGET_SUBDIR);
+    path.Append(mFileName);
+
+    mDsFile->SetPath(path);
+    mDsFile->mFile->RenameTo(nullptr, mFileName);
+  }
+}
+
+void
+BluetoothOppManager::DeleteDummyFile()
+{
+  // Remove the empty temp file
+  if (mDummyDsFile && mDummyDsFile->mFile) {
+    mDummyDsFile->mFile->Remove(false);
+    mDummyDsFile = nullptr;
+  }
+}
+
+void
 BluetoothOppManager::DeleteReceivedFile()
 {
   if (mOutputStream) {
     mOutputStream->Close();
     mOutputStream = nullptr;
   }
 
   if (mDsFile && mDsFile->mFile) {
     mDsFile->mFile->Remove(false);
     mDsFile = nullptr;
   }
+
+  DeleteDummyFile();
 }
 
 bool
 BluetoothOppManager::CreateFile()
 {
   MOZ_ASSERT(mPutPacketReceivedLength == mPacketLength);
 
+  // Create one dummy file to be a placeholder for the target file name, and
+  // create another file with a meaningless file extension to write the received
+  // data. By doing this, we can prevent applications from parsing incomplete
+  // data in the middle of the receiving process.
   nsString path;
   path.AssignLiteral(TARGET_SUBDIR);
   path.Append(mFileName);
 
-  mDsFile = DeviceStorageFile::CreateUnique(
-              path, nsIFile::NORMAL_FILE_TYPE, 0644);
+  // Use an empty dummy file object to occupy the file name, so that after the
+  // whole file has been received successfully by using mDsFile, we could just
+  // remove mDummyDsFile and rename mDsFile to the file name of mDummyDsFile.
+  mDummyDsFile =
+    DeviceStorageFile::CreateUnique(path, nsIFile::NORMAL_FILE_TYPE, 0644);
+  NS_ENSURE_TRUE(mDummyDsFile, false);
+
+  // The function CreateUnique() may create a file with a different file
+  // name from the original mFileName. Therefore we have to retrieve
+  // the file name again.
+  mDummyDsFile->mFile->GetLeafName(mFileName);
+
+  BT_LOGR("mFileName: %s", NS_ConvertUTF16toUTF8(mFileName).get());
+
+  // Prepare the entire file path for the .part file
+  path.Truncate();
+  path.AssignLiteral(TARGET_SUBDIR);
+  path.Append(mFileName);
+  path.AppendLiteral(".part");
+
+  mDsFile =
+    DeviceStorageFile::CreateUnique(path, nsIFile::NORMAL_FILE_TYPE, 0644);
   NS_ENSURE_TRUE(mDsFile, false);
 
-  nsCOMPtr<nsIFile> f;
-  mDsFile->mFile->Clone(getter_AddRefs(f));
-
-  /*
-   * The function CreateUnique() may create a file with a different file
-   * name from the original mFileName. Therefore we have to retrieve
-   * the file name again.
-   */
-  f->GetLeafName(mFileName);
-
-  NS_NewLocalFileOutputStream(getter_AddRefs(mOutputStream), f);
+  NS_NewLocalFileOutputStream(getter_AddRefs(mOutputStream), mDsFile->mFile);
   NS_ENSURE_TRUE(mOutputStream, false);
 
   return true;
 }
 
 bool
 BluetoothOppManager::WriteToFile(const uint8_t* aData, int aDataLength)
 {
@@ -927,16 +972,20 @@ BluetoothOppManager::ServerDataHandler(U
     if (mSentFileLength > kUpdateProgressBase * mUpdateProgressCounter) {
       UpdateProgress();
       mUpdateProgressCounter = mSentFileLength / kUpdateProgressBase + 1;
     }
 
     // Success to receive a file and notify completion
     if (mPutFinalFlag) {
       mSuccessFlag = true;
+
+      DeleteDummyFile();
+      RecoverFileName();
+
       FileTransferComplete();
       NotifyAboutFileChange();
     }
   } else if (opCode == ObexRequestCode::Get ||
              opCode == ObexRequestCode::GetFinal ||
              opCode == ObexRequestCode::SetPath) {
     ReplyError(ObexResponseCode::BadRequest);
     BT_WARNING("Unsupported ObexRequestCode");
--- a/dom/bluetooth2/bluez/BluetoothOppManager.h
+++ b/dom/bluetooth2/bluez/BluetoothOppManager.h
@@ -80,16 +80,18 @@ private:
 
   void StartFileTransfer();
   void StartSendingNextFile();
   void FileTransferComplete();
   void UpdateProgress();
   void ReceivingFileConfirmation();
   bool CreateFile();
   bool WriteToFile(const uint8_t* aData, int aDataLength);
+  void RecoverFileName();
+  void DeleteDummyFile();
   void DeleteReceivedFile();
   void ReplyToConnect();
   void ReplyToDisconnectOrAbort();
   void ReplyToPut(bool aFinal, bool aContinue);
   void ReplyError(uint8_t aError);
   void AfterOppConnected();
   void AfterFirstPut();
   void AfterOppDisconnected();
@@ -204,16 +206,17 @@ private:
    * A seperate member thread is required because our read calls can block
    * execution, which is not allowed to happen on the IOThread.
    */
   nsCOMPtr<nsIThread> mReadFileThread;
   nsCOMPtr<nsIOutputStream> mOutputStream;
   nsCOMPtr<nsIInputStream> mInputStream;
   nsCOMPtr<nsIVolumeMountLock> mMountLock;
   nsRefPtr<DeviceStorageFile> mDsFile;
+  nsRefPtr<DeviceStorageFile> mDummyDsFile;
 
   // If a connection has been established, mSocket will be the socket
   // communicating with the remote socket. We maintain the invariant that if
   // mSocket is non-null, mRfcommSocket and mL2capSocket must be null (and vice
   // versa).
   nsRefPtr<BluetoothSocket> mSocket;
 
   // Server sockets. Once an inbound connection is established, it will hand
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/browserElement_DisallowEmbedAppsInOOP.js
@@ -0,0 +1,42 @@
+/* Any copyright is dedicated to the public domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Bug 1059662 - Only allow a app to embed others apps when it runs
+// in-process.
+//
+// The "inproc" version of this test should successfully embed the
+// app, and the "oop" version of this test should fail to embed the
+// app.
+
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+browserElementTestHelpers.setEnabledPref(true);
+browserElementTestHelpers.addPermission();
+
+SpecialPowers.setAllAppsLaunchable(true);
+
+function runTest() {
+  var canEmbedApp = !browserElementTestHelpers.getOOPByDefaultPref();
+  var iframe = document.createElement('iframe');
+  iframe.setAttribute('mozbrowser', 'true');
+
+  iframe.addEventListener('mozbrowsershowmodalprompt', function(e) {
+    is(e.detail.message == 'app', canEmbedApp, e.detail.message);
+    SimpleTest.finish();
+  });
+
+  document.body.appendChild(iframe);
+
+  var context = { 'url': 'http://example.org',
+                  'appId': SpecialPowers.Ci.nsIScriptSecurityManager.NO_APP_ID,
+                  'isInBrowserElement': true };
+  SpecialPowers.pushPermissions([
+    {'type': 'browser', 'allow': 1, 'context': context},
+    {'type': 'embed-apps', 'allow': 1, 'context': context}
+  ], function() {
+    iframe.src = 'http://example.org/tests/dom/browser-element/mochitest/file_browserElement_DisallowEmbedAppsInOOP.html';
+  });
+}
+
+addEventListener('testready', runTest);
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/file_browserElement_DisallowEmbedAppsInOOP.html
@@ -0,0 +1,19 @@
+<html>
+<head>
+<script type="text/javascript">
+  addEventListener('load', function(e) {
+    var iframe = document.createElement('iframe');
+    iframe.setAttribute('mozbrowser', 'true');
+    iframe.setAttribute('remote', 'false');
+    iframe.setAttribute('mozapp', 'http://example.org/manifest.webapp');
+    iframe.addEventListener('mozbrowsershowmodalprompt', function(e) {
+      alert(e.detail.message);
+    });
+    document.body.appendChild(iframe);
+    iframe.src = 'http://example.org/tests/dom/browser-element/mochitest/file_browserElement_AppFramePermission.html';
+  });
+</script>
+</head>
+<body>
+</body>
+</html>
--- a/dom/browser-element/mochitest/mochitest-oop.ini
+++ b/dom/browser-element/mochitest/mochitest-oop.ini
@@ -26,16 +26,17 @@ skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_BrowserWindowNamespace.html]
 skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_BrowserWindowResize.html]
 [test_browserElement_oop_Close.html]
 [test_browserElement_oop_CookiesNotThirdParty.html]
 [test_browserElement_oop_CopyPaste.html]
 [test_browserElement_oop_DOMRequestError.html]
 [test_browserElement_oop_DataURI.html]
+[test_browserElement_oop_DisallowEmbedAppsInOOP.html]
 [test_browserElement_oop_DocumentFirstPaint.html]
 [test_browserElement_oop_Download.html]
 disabled = bug 1022281
 [test_browserElement_oop_ErrorSecurity.html]
 skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_FirstPaint.html]
 [test_browserElement_oop_ForwardName.html]
 [test_browserElement_oop_FrameWrongURI.html]
--- a/dom/browser-element/mochitest/mochitest.ini
+++ b/dom/browser-element/mochitest/mochitest.ini
@@ -17,16 +17,17 @@ support-files =
   browserElement_Close.js
   browserElement_CloseApp.js
   browserElement_CloseFromOpener.js
   browserElement_ContextmenuEvents.js
   browserElement_CookiesNotThirdParty.js
   browserElement_CopyPaste.js
   browserElement_DOMRequestError.js
   browserElement_DataURI.js
+  browserElement_DisallowEmbedAppsInOOP.js
   browserElement_DocumentFirstPaint.js
   browserElement_Download.js
   browserElement_ErrorSecurity.js
   browserElement_ExposableURI.js
   browserElement_FirstPaint.js
   browserElement_ForwardName.js
   browserElement_FrameWrongURI.js
   browserElement_GetScreenshot.js
@@ -72,16 +73,17 @@ support-files =
   file_browserElement_AlertInFrame_Inner.html
   file_browserElement_AppFramePermission.html
   file_browserElement_AppWindowNamespace.html
   file_browserElement_ThemeColor.html
   file_browserElement_BrowserWindowNamespace.html
   file_browserElement_CloseApp.html
   file_browserElement_CloseFromOpener.html
   file_browserElement_CookiesNotThirdParty.html
+  file_browserElement_DisallowEmbedAppsInOOP.html
   file_browserElement_ForwardName.html
   file_browserElement_FrameWrongURI.html
   file_browserElement_LoadEvents.html
   file_browserElement_Metachange.sjs
   file_browserElement_NextPaint.html
   file_browserElement_Open1.html
   file_browserElement_Open2.html
   file_browserElement_OpenNamed.html
@@ -138,16 +140,18 @@ skip-if = buildapp == 'b2g'
 skip-if = toolkit == 'android' || buildapp == 'b2g' # android(FAILS, bug 796982) androidx86(FAILS, bug 796982)
 [test_browserElement_inproc_CloseFromOpener.html]
 skip-if = buildapp == 'b2g'
 [test_browserElement_inproc_ContextmenuEvents.html]
 [test_browserElement_inproc_CookiesNotThirdParty.html]
 [test_browserElement_inproc_CopyPaste.html]
 [test_browserElement_inproc_DOMRequestError.html]
 [test_browserElement_inproc_DataURI.html]
+[test_browserElement_inproc_DisallowEmbedAppsInOOP.html]
+skip-if = os == "android" || toolkit == 'gonk' # embed-apps doesn't work in the mochitest app
 [test_browserElement_inproc_DocumentFirstPaint.html]
 [test_browserElement_inproc_Download.html]
 disabled = bug 1022281
 [test_browserElement_inproc_ExposableURI.html]
 [test_browserElement_inproc_FirstPaint.html]
 [test_browserElement_inproc_ForwardName.html]
 [test_browserElement_inproc_FrameWrongURI.html]
 skip-if = (toolkit == 'gonk' && !debug)
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserElement_inproc_DisallowEmbedAppsInOOP.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Bug 1059662</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script type="application/javascript;version=1.7" src="browserElement_DisallowEmbedAppsInOOP.js">
+</script>
+</body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserElement_oop_DisallowEmbedAppsInOOP.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Bug 1059662</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script type="application/javascript;version=1.7" src="browserElement_DisallowEmbedAppsInOOP.js">
+</script>
+</body>
+</html>
\ No newline at end of file
--- a/dom/datastore/tests/mochitest.ini
+++ b/dom/datastore/tests/mochitest.ini
@@ -29,16 +29,17 @@ support-files =
   file_transactions.html
   file_basic_common.js
   file_sync_common.js
   file_bug1008044.html
   file_bug957086.html
   file_notify_system_message.html
 
 [test_app_install.html]
+skip-if = toolkit == 'gonk' # embed-apps doesn't work in the mochitest app
 [test_readonly.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 936226
 [test_basic.html]
 [test_basic_worker.html]
 [test_changes.html]
 [test_arrays.html]
 [test_oop.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 936226
--- a/dom/html/nsGenericHTMLFrameElement.cpp
+++ b/dom/html/nsGenericHTMLFrameElement.cpp
@@ -2,16 +2,17 @@
 /* vim:set tw=80 expandtab softtabstop=2 ts=2 sw=2: */
 
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsGenericHTMLFrameElement.h"
 
+#include "mozilla/dom/ContentChild.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ErrorResult.h"
 #include "GeckoProfiler.h"
 #include "mozIApplication.h"
 #include "nsAttrValueInlines.h"
 #include "nsContentUtils.h"
 #include "nsIAppsService.h"
 #include "nsIDocShell.h"
@@ -432,16 +433,21 @@ nsGenericHTMLFrameElement::GetAppManifes
 {
   aOut.Truncate();
 
   // At the moment, you can't be an app without being a browser.
   if (!nsIMozBrowserFrame::GetReallyIsBrowserOrApp()) {
     return NS_OK;
   }
 
+  if (XRE_GetProcessType() != GeckoProcessType_Default) {
+    NS_WARNING("Can't embed-apps. Embed-apps is restricted to in-proc apps, see bug 1059662");
+    return NS_OK;
+  }
+
   nsAutoString appManifestURL;
   nsAutoString widgetManifestURL;
 
   GetManifestURLByType(nsGkAtoms::mozapp, appManifestURL);
 
   if (WidgetsEnabled()) {
     GetManifestURLByType(nsGkAtoms::mozwidget, widgetManifestURL);
   }
--- a/dom/indexedDB/test/unit/xpcshell-parent-process.ini
+++ b/dom/indexedDB/test/unit/xpcshell-parent-process.ini
@@ -1,30 +1,31 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 [DEFAULT]
 dupe-manifest =
 head = xpcshell-head-parent-process.js
 tail =
-skip-if = toolkit == 'android' || toolkit == 'gonk'
+skip-if = toolkit == 'gonk'
 support-files =
   bug1056939.zip
   GlobalObjectsChild.js
   GlobalObjectsComponent.js
   GlobalObjectsComponent.manifest
   GlobalObjectsModule.jsm
   GlobalObjectsSandbox.js
   xpcshell-shared.ini
 
 [include:xpcshell-shared.ini]
 
 [test_blob_file_backed.js]
 [test_bug1056939.js]
 [test_globalObjects_ipc.js]
+skip-if = toolkit == 'android'
 [test_invalidate.js]
 # disabled for the moment.
 skip-if = true
 [test_lowDiskSpace.js]
 [test_temporary_storage.js]
 # bug 951017: intermittent failure on Android x86 emulator
 skip-if = os == "android" && processor == "x86"
--- a/dom/indexedDB/test/unit/xpcshell-shared.ini
+++ b/dom/indexedDB/test/unit/xpcshell-shared.ini
@@ -1,13 +1,11 @@
 # 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/.
-[DEFAULT]
-skip-if = toolkit == 'gonk'
 
 [test_add_put.js]
 [test_add_twice_failure.js]
 [test_advance.js]
 [test_autoIncrement.js]
 [test_autoIncrement_indexes.js]
 [test_blocked_order.js]
 [test_clear.js]
--- a/dom/media/MediaCache.cpp
+++ b/dom/media/MediaCache.cpp
@@ -1168,16 +1168,17 @@ MediaCache::Update()
 
       MediaCacheStream* stream = mStreams[i];
       if (stream->mClosed)
         continue;
 
       // Figure out where we should be reading from. It's the first
       // uncached byte after the current mStreamOffset.
       int64_t dataOffset = stream->GetCachedDataEndInternal(stream->mStreamOffset);
+      MOZ_ASSERT(dataOffset >= 0);
 
       // Compute where we'd actually seek to to read at readOffset
       int64_t desiredOffset = dataOffset;
       if (stream->mIsTransportSeekable) {
         if (desiredOffset > stream->mChannelOffset &&
             desiredOffset <= stream->mChannelOffset + SEEK_VS_READ_THRESHOLD) {
           // Assume it's more efficient to just keep reading up to the
           // desired position instead of trying to seek
@@ -1700,16 +1701,17 @@ MediaCacheStream::NotifyDataLength(int64
 void
 MediaCacheStream::NotifyDataStarted(int64_t aOffset)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   ReentrantMonitorAutoEnter mon(gMediaCache->GetReentrantMonitor());
   NS_WARN_IF_FALSE(aOffset == mChannelOffset,
                    "Server is giving us unexpected offset");
+  MOZ_ASSERT(aOffset >= 0);
   mChannelOffset = aOffset;
   if (mStreamLength >= 0) {
     // If we started reading at a certain offset, then for sure
     // the stream is at least that long.
     mStreamLength = std::max(mStreamLength, mChannelOffset);
   }
 }
 
@@ -2129,33 +2131,38 @@ MediaCacheStream::Seek(int32_t aWhence, 
 {
   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
 
   ReentrantMonitorAutoEnter mon(gMediaCache->GetReentrantMonitor());
   if (mClosed)
     return NS_ERROR_FAILURE;
 
   int64_t oldOffset = mStreamOffset;
+  int64_t newOffset = mStreamOffset;
   switch (aWhence) {
   case PR_SEEK_END:
     if (mStreamLength < 0)
       return NS_ERROR_FAILURE;
-    mStreamOffset = mStreamLength + aOffset;
+    newOffset = mStreamLength + aOffset;
     break;
   case PR_SEEK_CUR:
-    mStreamOffset += aOffset;
+    newOffset += aOffset;
     break;
   case PR_SEEK_SET:
-    mStreamOffset = aOffset;
+    newOffset = aOffset;
     break;
   default:
     NS_ERROR("Unknown whence");
     return NS_ERROR_FAILURE;
   }
 
+  if (newOffset < 0)
+    return NS_ERROR_FAILURE;
+  mStreamOffset = newOffset;
+
   CACHE_LOG(PR_LOG_DEBUG, ("Stream %p Seek to %lld", this, (long long)mStreamOffset));
   gMediaCache->NoteSeek(this, oldOffset);
 
   gMediaCache->QueueUpdate();
   return NS_OK;
 }
 
 int64_t
@@ -2187,21 +2194,20 @@ MediaCacheStream::Read(char* aBuffer, ui
     if (mStreamLength >= 0) {
       // Don't try to read beyond the end of the stream
       int64_t bytesRemaining = mStreamLength - mStreamOffset;
       if (bytesRemaining <= 0) {
         // Get out of here and return NS_OK
         break;
       }
       size = std::min(size, bytesRemaining);
-      // Clamp size until 64-bit file size issues (bug 500784) are fixed.
+      // Clamp size until 64-bit file size issues are fixed.
       size = std::min(size, int64_t(INT32_MAX));
     }
 
-    int32_t bytes;
     int32_t cacheBlock = streamBlock < mBlocks.Length() ? mBlocks[streamBlock] : -1;
     if (cacheBlock < 0) {
       // We don't have a complete cached block here.
 
       if (count > 0) {
         // Some data has been read, so return what we've got instead of
         // blocking or trying to find a stream with a partial block.
         break;
@@ -2219,17 +2225,20 @@ MediaCacheStream::Read(char* aBuffer, ui
           streamWithPartialBlock = stream;
           break;
         }
       }
       if (streamWithPartialBlock) {
         // We can just use the data in mPartialBlockBuffer. In fact we should
         // use it rather than waiting for the block to fill and land in
         // the cache.
-        bytes = std::min<int64_t>(size, streamWithPartialBlock->mChannelOffset - mStreamOffset);
+        int64_t bytes = std::min<int64_t>(size, streamWithPartialBlock->mChannelOffset - mStreamOffset);
+        // Clamp bytes until 64-bit file size issues are fixed.
+        bytes = std::min(bytes, int64_t(INT32_MAX));
+        NS_ABORT_IF_FALSE(bytes >= 0 && bytes <= aCount, "Bytes out of range.");
         memcpy(aBuffer,
           reinterpret_cast<char*>(streamWithPartialBlock->mPartialBlockBuffer.get()) + offsetInStreamBlock, bytes);
         if (mCurrentMode == MODE_METADATA) {
           streamWithPartialBlock->mMetadataInPartialBlockBuffer = true;
         }
         mStreamOffset += bytes;
         count = bytes;
         break;
@@ -2243,16 +2252,17 @@ MediaCacheStream::Read(char* aBuffer, ui
         return NS_ERROR_FAILURE;
       }
       continue;
     }
 
     gMediaCache->NoteBlockUsage(this, cacheBlock, mCurrentMode, TimeStamp::Now());
 
     int64_t offset = cacheBlock*BLOCK_SIZE + offsetInStreamBlock;
+    int32_t bytes;
     NS_ABORT_IF_FALSE(size >= 0 && size <= INT32_MAX, "Size out of range.");
     nsresult rv = gMediaCache->ReadCacheFile(offset, aBuffer + count, int32_t(size), &bytes);
     if (NS_FAILED(rv)) {
       if (count == 0)
         return rv;
       // If we did successfully read some data, may as well return it
       break;
     }
@@ -2279,19 +2289,17 @@ MediaCacheStream::ReadAt(int64_t aOffset
 
   ReentrantMonitorAutoEnter mon(gMediaCache->GetReentrantMonitor());
   nsresult rv = Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
   if (NS_FAILED(rv)) return rv;
   return Read(aBuffer, aCount, aBytes);
 }
 
 nsresult
-MediaCacheStream::ReadFromCache(char* aBuffer,
-                                  int64_t aOffset,
-                                  int64_t aCount)
+MediaCacheStream::ReadFromCache(char* aBuffer, int64_t aOffset, int64_t aCount)
 {
   ReentrantMonitorAutoEnter mon(gMediaCache->GetReentrantMonitor());
   if (mClosed)
     return NS_ERROR_FAILURE;
 
   // Read one block (or part of a block) at a time
   uint32_t count = 0;
   int64_t streamOffset = aOffset;
@@ -2303,28 +2311,31 @@ MediaCacheStream::ReadFromCache(char* aB
 
     if (mStreamLength >= 0) {
       // Don't try to read beyond the end of the stream
       int64_t bytesRemaining = mStreamLength - streamOffset;
       if (bytesRemaining <= 0) {
         return NS_ERROR_FAILURE;
       }
       size = std::min(size, bytesRemaining);
-      // Clamp size until 64-bit file size issues (bug 500784) are fixed.
+      // Clamp size until 64-bit file size issues are fixed.
       size = std::min(size, int64_t(INT32_MAX));
     }
 
     int32_t bytes;
     uint32_t channelBlock = uint32_t(mChannelOffset/BLOCK_SIZE);
     int32_t cacheBlock = streamBlock < mBlocks.Length() ? mBlocks[streamBlock] : -1;
     if (channelBlock == streamBlock && streamOffset < mChannelOffset) {
       // We can just use the data in mPartialBlockBuffer. In fact we should
       // use it rather than waiting for the block to fill and land in
       // the cache.
-      bytes = std::min<int64_t>(size, mChannelOffset - streamOffset);
+      // Clamp bytes until 64-bit file size issues are fixed.
+      int64_t toCopy = std::min<int64_t>(size, mChannelOffset - streamOffset);
+      bytes = std::min(toCopy, int64_t(INT32_MAX));
+      NS_ABORT_IF_FALSE(bytes >= 0 && bytes <= toCopy, "Bytes out of range.");
       memcpy(aBuffer + count,
         reinterpret_cast<char*>(mPartialBlockBuffer.get()) + offsetInStreamBlock, bytes);
     } else {
       if (cacheBlock < 0) {
         // We expect all blocks to be cached! Fail!
         return NS_ERROR_FAILURE;
       }
       int64_t offset = cacheBlock*BLOCK_SIZE + offsetInStreamBlock;
--- a/dom/media/fmp4/wmf/WMFAudioMFTManager.cpp
+++ b/dom/media/fmp4/wmf/WMFAudioMFTManager.cpp
@@ -216,19 +216,16 @@ WMFAudioMFTManager::Output(int64_t aStre
   hr = sample->ConvertToContiguousBuffer(byRef(buffer));
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
   BYTE* data = nullptr; // Note: *data will be owned by the IMFMediaBuffer, we don't need to free it.
   DWORD maxLength = 0, currentLength = 0;
   hr = buffer->Lock(&data, &maxLength, &currentLength);
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
-  int32_t numSamples = currentLength / mAudioBytesPerSample;
-  int32_t numFrames = numSamples / mAudioChannels;
-
   // Sometimes when starting decoding, the AAC decoder gives us samples
   // with a negative timestamp. AAC does usually have preroll (or encoder
   // delay) encoded into its bitstream, but the amount encoded to the stream
   // is variable, and it not signalled in-bitstream. There is sometimes
   // signalling in the MP4 container what the preroll amount, but it's
   // inconsistent. It looks like WMF's AAC encoder may take this into
   // account, so strip off samples with a negative timestamp to get us
   // to a 0-timestamp start. This seems to maintain A/V sync, so we can run
@@ -240,38 +237,40 @@ WMFAudioMFTManager::Output(int64_t aStre
 
   // If this sample block comes after a discontinuity (i.e. a gap or seek)
   // reset the frame counters, and capture the timestamp. Future timestamps
   // will be offset from this block's timestamp.
   UINT32 discontinuity = false;
   int32_t numFramesToStrip = 0;
   sample->GetUINT32(MFSampleExtension_Discontinuity, &discontinuity);
   if (mMustRecaptureAudioPosition || discontinuity) {
+    // Update the output type, in case this segment has a different
+    // rate. This also triggers on the first sample, which can have a
+    // different rate than is advertised in the container, and sometimes we
+    // don't get a MF_E_TRANSFORM_STREAM_CHANGE when the rate changes.
+    hr = UpdateOutputType();
+    NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
     mAudioFrameSum = 0;
     LONGLONG timestampHns = 0;
     hr = sample->GetSampleTime(&timestampHns);
     NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
     hr = HNsToFrames(timestampHns, mAudioRate, &mAudioFrameOffset);
     NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
     if (mAudioFrameOffset < 0) {
       // First sample has a negative timestamp. Strip off the samples until
       // we reach positive territory.
       numFramesToStrip = -mAudioFrameOffset;
       mAudioFrameOffset = 0;
     }
     mMustRecaptureAudioPosition = false;
-
-    // Also update the output type, in case this segment has a different
-    // rate. This also triggers on the first sample, which can have a
-    // different rate than is advertised in the container, and sometimes
-    // we don't get a MF_E_TRANSFORM_STREAM_CHANGE when the rate changes.
-    hr = UpdateOutputType();
-    NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
   }
   MOZ_ASSERT(numFramesToStrip >= 0);
+  int32_t numSamples = currentLength / mAudioBytesPerSample;
+  int32_t numFrames = numSamples / mAudioChannels;
   int32_t offset = std::min<int32_t>(numFramesToStrip, numFrames);
   numFrames -= offset;
   numSamples -= offset * mAudioChannels;
   MOZ_ASSERT(numFrames >= 0);
   MOZ_ASSERT(numSamples >= 0);
   if (numFrames == 0) {
     // All data from this chunk stripped, loop back and try to output the next
     // frame, if possible.
--- a/dom/media/gmp/GMPChild.cpp
+++ b/dom/media/gmp/GMPChild.cpp
@@ -336,18 +336,16 @@ GMPChild::RecvStartPlugin()
 // Pre-load DLLs that need to be used by the EME plugin but that can't be
 // loaded after the sandbox has started
 bool
 GMPChild::PreLoadLibraries(const std::string& aPluginPath)
 {
   // This must be in sorted order and lowercase!
   static const char* whitelist[] =
     {
-       "bcrypt.dll", // Used for OutputProtectionManager handshake
-       "crypt32.dll", // Used for OutputProtectionManager handshake
        "d3d9.dll", // Create an `IDirect3D9` to get adapter information
        "dxva2.dll", // Get monitor information
        "msauddecmft.dll", // AAC decoder (on Windows 8)
        "msmpeg2adec.dll", // AAC decoder (on Windows 7)
        "msmpeg2vdec.dll", // H.264 decoder
     };
   static const int whitelistLen = sizeof(whitelist) / sizeof(whitelist[0]);
 
--- a/dom/media/omx/OmxDecoder.cpp
+++ b/dom/media/omx/OmxDecoder.cpp
@@ -28,16 +28,20 @@
 #include "MPAPI.h"
 #include "prlog.h"
 
 #include "GonkNativeWindow.h"
 #include "GonkNativeWindowClient.h"
 #include "OMXCodecProxy.h"
 #include "OmxDecoder.h"
 
+#define LOG_TAG "OmxDecoder"
+#include <android/log.h>
+#define ALOG(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+
 #ifdef PR_LOGGING
 PRLogModuleInfo *gOmxDecoderLog;
 #define LOG(type, msg...) PR_LOG(gOmxDecoderLog, type, (msg))
 #else
 #define LOG(x...)
 #endif
 
 using namespace MPAPI;
@@ -61,17 +65,18 @@ OmxDecoder::OmxDecoder(MediaResource *aR
   mAudioChannels(-1),
   mAudioSampleRate(-1),
   mDurationUs(-1),
   mVideoBuffer(nullptr),
   mAudioBuffer(nullptr),
   mIsVideoSeeking(false),
   mAudioMetadataRead(false),
   mAudioPaused(false),
-  mVideoPaused(false)
+  mVideoPaused(false),
+  mVideoLastFrameTime(-1)
 {
   mLooper = new ALooper;
   mLooper->setName("OmxDecoder");
 
   mReflector = new AHandlerReflector<OmxDecoder>(this);
   // Register AMessage handler to ALooper.
   mLooper->registerHandler(mReflector);
   // Start ALooper thread.
@@ -551,24 +556,53 @@ bool OmxDecoder::ReadVideo(VideoFrame *a
 
   if (aDoSeek) {
     {
       Mutex::Autolock autoLock(mSeekLock);
       ReleaseAllPendingVideoBuffersLocked();
       mIsVideoSeeking = true;
     }
     MediaSource::ReadOptions options;
-    options.setSeekTo(aTimeUs, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
-    err = mVideoSource->read(&mVideoBuffer, &options);
-    {
-      Mutex::Autolock autoLock(mSeekLock);
-      mIsVideoSeeking = false;
-      PostReleaseVideoBuffer(nullptr, FenceHandle());
+    MediaSource::ReadOptions::SeekMode seekMode;
+    // If the last timestamp of decoded frame is smaller than seekTime,
+    // seek to next key frame. Otherwise seek to the previos one.
+    if (mVideoLastFrameTime > aTimeUs || mVideoLastFrameTime == -1) {
+      seekMode = MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC;
+    } else {
+      seekMode = MediaSource::ReadOptions::SEEK_NEXT_SYNC;
     }
 
+    bool findNextBuffer = true;
+    while (findNextBuffer) {
+      options.setSeekTo(aTimeUs, seekMode);
+      findNextBuffer = false;
+      err = mVideoSource->read(&mVideoBuffer, &options);
+      {
+        Mutex::Autolock autoLock(mSeekLock);
+        mIsVideoSeeking = false;
+        PostReleaseVideoBuffer(nullptr, FenceHandle());
+      }
+      // If there is no next Keyframe, jump to the previous key frame.
+      if (err == ERROR_END_OF_STREAM && seekMode == MediaSource::ReadOptions::SEEK_NEXT_SYNC) {
+         seekMode = MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC;
+	 findNextBuffer = true;
+	 {
+	   Mutex::Autolock autoLock(mSeekLock);
+	   mIsVideoSeeking = true;
+	 }
+	 continue;
+      } else if (err != OK) {
+	ALOG("Unexpected error when seeking to %lld", aTimeUs);
+        break;
+      }
+      if (mVideoBuffer->range_length() == 0) {
+        ReleaseVideoBuffer();
+	findNextBuffer = true;
+      }
+    }
     aDoSeek = false;
   } else {
     err = mVideoSource->read(&mVideoBuffer);
   }
 
   aFrame->mSize = 0;
 
   if (err == OK) {
@@ -630,16 +664,17 @@ bool OmxDecoder::ReadVideo(VideoFrame *a
       if (!ToVideoFrame(aFrame, timeUs, data, length, keyFrame)) {
         return false;
       }
     }
     // Check if this frame is valid or not. If not, skip it.
     if ((aKeyframeSkip && timeUs < aTimeUs) || length == 0) {
       aFrame->mShouldSkip = true;
     }
+    mVideoLastFrameTime = timeUs;
   }
   else if (err == INFO_FORMAT_CHANGED) {
     // If the format changed, update our cached info.
     if (!SetVideoFormat()) {
       return false;
     } else {
       return ReadVideo(aFrame, aTimeUs, aKeyframeSkip, aDoSeek);
     }
--- a/dom/media/omx/OmxDecoder.h
+++ b/dom/media/omx/OmxDecoder.h
@@ -55,16 +55,18 @@ class OmxDecoder : public OMXCodecProxy:
   int32_t mVideoHeight;
   int32_t mVideoColorFormat;
   int32_t mVideoStride;
   int32_t mVideoSliceHeight;
   int32_t mVideoRotation;
   int32_t mAudioChannels;
   int32_t mAudioSampleRate;
   int64_t mDurationUs;
+  int64_t mVideoLastFrameTime;
+
   VideoFrame mVideoFrame;
   AudioFrame mAudioFrame;
   MP3FrameParser mMP3FrameParser;
   bool mIsMp3;
 
   // Lifetime of these should be handled by OMXCodec, as long as we release
   //   them after use: see ReleaseVideoBuffer(), ReleaseAudioBuffer()
   MediaBuffer *mVideoBuffer;
--- a/dom/messages/SystemMessagePermissionsChecker.jsm
+++ b/dom/messages/SystemMessagePermissionsChecker.jsm
@@ -118,19 +118,16 @@ this.SystemMessagePermissionsTable = {
     "nfc-manager": []
   },
   "nfc-manager-tech-lost": {
     "nfc-manager": []
   },
   "nfc-manager-send-file": {
     "nfc-manager": []
   },
-  "nfc-powerlevel-change": {
-    "settings": ["read", "write"]
-  },
   "wifip2p-pairing-request": { },
   "first-run-with-sim": {
     "settings": ["read", "write"]
   }
 };
 
 
 this.SystemMessagePermissionsChecker = {
--- a/dom/system/gonk/AutoMounter.cpp
+++ b/dom/system/gonk/AutoMounter.cpp
@@ -97,16 +97,17 @@ USING_MTP_NAMESPACE
 namespace mozilla {
 namespace system {
 
 #define SYS_USB_CONFIG          "sys.usb.config"
 #define PERSIST_SYS_USB_CONFIG  "persist.sys.usb.config"
 
 #define USB_FUNC_ADB    "adb"
 #define USB_FUNC_MTP    "mtp"
+#define USB_FUNC_NONE   "none"
 #define USB_FUNC_RNDIS  "rndis"
 #define USB_FUNC_UMS    "mass_storage"
 
 class AutoMounter;
 
 static void SetAutoMounterStatus(int32_t aStatus);
 
 /***************************************************************************/
@@ -523,16 +524,23 @@ IsUsbFunctionEnabled(const char* aConfig
 }
 
 static void
 SetUsbFunction(const char* aUsbFunc)
 {
   char oldSysUsbConfig[PROPERTY_VALUE_MAX];
   property_get(SYS_USB_CONFIG, oldSysUsbConfig, "");
 
+  if (strcmp(oldSysUsbConfig, USB_FUNC_NONE) == 0) {
+    // It's quite possible that sys.usb.config may have the value "none". We
+    // convert that to an empty string here, and at the end we convert the
+    // empty string back to "none".
+    oldSysUsbConfig[0] = '\0';
+  }
+
   if (IsUsbFunctionEnabled(oldSysUsbConfig, aUsbFunc)) {
     // The function is already configured. Nothing else to do.
     DBG("SetUsbFunction('%s') - already set - nothing to do", aUsbFunc);
     return;
   }
 
   char newSysUsbConfig[PROPERTY_VALUE_MAX];
 
@@ -578,16 +586,20 @@ SetUsbFunction(const char* aUsbFunc)
   // to newSysUsbConfig. So we need to check for that.
 
   if (strcmp(oldSysUsbConfig, newSysUsbConfig) == 0) {
     DBG("SetUsbFunction('%s') %s is already set to '%s' - nothing to do",
         aUsbFunc, SYS_USB_CONFIG, newSysUsbConfig);
     return;
   }
 
+  if (newSysUsbConfig[0] == '\0') {
+    // Convert the empty string back to the string "none"
+    strlcpy(newSysUsbConfig, USB_FUNC_NONE, sizeof(newSysUsbConfig));
+  }
   LOG("SetUsbFunction(%s) %s from '%s' to '%s'", aUsbFunc, SYS_USB_CONFIG,
       oldSysUsbConfig, newSysUsbConfig);
   property_set(SYS_USB_CONFIG, newSysUsbConfig);
 }
 
 bool
 AutoMounter::StartMtpServer()
 {
--- a/dom/tests/mochitest/localstorage/mochitest.ini
+++ b/dom/tests/mochitest/localstorage/mochitest.ini
@@ -12,17 +12,17 @@ support-files =
   frameSlaveEqual.html
   frameSlaveNotEqual.html
   interOriginFrame.js
   interOriginTest.js
   interOriginTest2.js
   localStorageCommon.js
 
 [test_appIsolation.html]
-skip-if = buildapp == 'b2g' || toolkit == 'android' #bug 793211 # b2g(needs https to work) b2g-debug(needs https to work) b2g-desktop(needs https to work)
+skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s #bug 793211 # b2g(needs https to work) b2g-debug(needs https to work) b2g-desktop(needs https to work)
 [test_brokenUTF-16.html]
 [test_bug600307-DBOps.html]
 [test_bug746272-1.html]
 [test_bug746272-2.html]
 skip-if = os == "android" || toolkit == 'gonk' # bug 962029
 [test_cookieBlock.html]
 skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) # b2g-debug(bug 913706) b2g-desktop(bug 913706)
 [test_cookieSession.html]
--- a/dom/tv/test/mochitest/mochitest.ini
+++ b/dom/tv/test/mochitest/mochitest.ini
@@ -1,10 +1,11 @@
 [DEFAULT]
 skip-if = os == "linux" && e10s && debug # bug 1091322 - wallpaper over an e10s crash
+skip-if = os == "android" || toolkit == "gonk" || e10s
 support-files =
   file_app.sjs
   file_app.template.webapp
   file_tv_non_permitted_app.html
   file_tv_permitted_app.html
   file_tv_get_tuners.html
   file_tv_get_sources.html
   file_tv_get_channels.html
--- a/dom/workers/XMLHttpRequest.cpp
+++ b/dom/workers/XMLHttpRequest.cpp
@@ -1274,16 +1274,20 @@ EventRunnable::WorkerRun(JSContext* aCx,
       mProxy->mSeenLoadStart = true;
     }
   }
   else if (mType.EqualsASCII(sEventStrings[STRING_loadend])) {
     if (mUploadEvent) {
       mProxy->mSeenUploadLoadStart = false;
     }
     else {
+      if (!mProxy->mSeenLoadStart) {
+        // We've already dispatched premature abort events.
+        return true;
+      }
       mProxy->mSeenLoadStart = false;
     }
   }
   else if (mType.EqualsASCII(sEventStrings[STRING_abort])) {
     if ((mUploadEvent && !mProxy->mSeenUploadLoadStart) ||
         (!mUploadEvent && !mProxy->mSeenLoadStart)) {
       // We've already dispatched premature abort events.
       return true;
@@ -2399,17 +2403,19 @@ XMLHttpRequest::GetResponseText(nsAStrin
   aResponseText = mStateData.mResponseText;
 }
 
 void
 XMLHttpRequest::UpdateState(const StateData& aStateData,
                             bool aUseCachedArrayBufferResponse)
 {
   if (aUseCachedArrayBufferResponse) {
-    MOZ_ASSERT(JS_IsArrayBufferObject(mStateData.mResponse.toObjectOrNull()));
+    MOZ_ASSERT(mStateData.mResponse.isObject() &&
+               JS_IsArrayBufferObject(&mStateData.mResponse.toObject()));
+
     JS::Rooted<JS::Value> response(mWorkerPrivate->GetJSContext(),
                                    mStateData.mResponse);
     mStateData = aStateData;
     mStateData.mResponse = response;
   }
   else {
     mStateData = aStateData;
   }
--- a/editor/libeditor/nsHTMLEditRules.cpp
+++ b/editor/libeditor/nsHTMLEditRules.cpp
@@ -442,17 +442,18 @@ nsHTMLEditRules::AfterEditInner(EditActi
     // Note that this won't prevent explicit selection setting from working.
     NS_ENSURE_STATE(mHTMLEditor);
     nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
    
     // expand the "changed doc range" as needed
     res = PromoteRange(mDocChangeRange, action);
     NS_ENSURE_SUCCESS(res, res);
 
-    // if we did a ranged deletion, make sure we have a place to put caret.
+    // if we did a ranged deletion or handling backspace key, make sure we have
+    // a place to put caret.
     // Note we only want to do this if the overall operation was deletion,
     // not if deletion was done along the way for EditAction::loadHTML, EditAction::insertText, etc.
     // That's why this is here rather than DidDeleteSelection().
     if ((action == EditAction::deleteSelection) && mDidRangedDelete)
     {
       res = InsertBRIfNeeded(selection);
       NS_ENSURE_SUCCESS(res, res);
     }  
@@ -2040,16 +2041,20 @@ nsHTMLEditRules::WillDeleteSelection(Sel
       NS_ENSURE_STATE(mHTMLEditor);
       res = mHTMLEditor->DeleteText(*nodeAsText, std::min(so, eo),
                                     DeprecatedAbs(eo - so));
       *aHandled = true;
       NS_ENSURE_SUCCESS(res, res);
       res = InsertBRIfNeeded(aSelection);
       NS_ENSURE_SUCCESS(res, res);
 
+      // Remember that we did a ranged delete for the benefit of
+      // AfterEditInner().
+      mDidRangedDelete = true;
+
       return NS_OK;
     }
     
     if (wsType == WSType::special || wsType == WSType::br ||
         visNode->Tag() == nsGkAtoms::hr) {
       // Short circuit for invisible breaks.  delete them and recurse.
       if (visNode->Tag() == nsGkAtoms::br &&
           (!mHTMLEditor || !mHTMLEditor->IsVisBreak(visNode))) {
--- a/editor/libeditor/tests/mochitest.ini
+++ b/editor/libeditor/tests/mochitest.ini
@@ -135,16 +135,17 @@ skip-if = toolkit == 'android' || e10s
 [test_bug858918.html]
 [test_bug966155.html]
 skip-if = os != "win"
 [test_bug966552.html]
 skip-if = os != "win"
 [test_bug998188.html]
 [test_bug1026397.html]
 [test_bug1067255.html]
+[test_bug1094000.html]
 skip-if = e10s
 [test_CF_HTML_clipboard.html]
 [test_contenteditable_focus.html]
 [test_dom_input_event_on_htmleditor.html]
 skip-if = toolkit == 'android' # bug 1054087
 [test_dom_input_event_on_texteditor.html]
 [test_keypress_untrusted_event.html]
 [test_root_element_replacement.html]
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/tests/test_bug1094000.html
@@ -0,0 +1,104 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1094000
+-->
+<head>
+  <title>Test for Bug 1094000</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1094000">Mozilla Bug 1094000</a>
+<p id="display"></p>
+<div id="content">
+  <div id="editor0" contenteditable></div>
+  <div id="editor1" contenteditable><br></div>
+  <div id="editor2" contenteditable>a</div>
+  <div id="editor3" contenteditable>b</div>
+  <div id="editor4" contenteditable>c</div>
+  <div id="editor5" contenteditable>d</div>
+  <div id="editor6" contenteditable>e</div>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 1094000 **/
+
+SimpleTest.waitForExplicitFinish();
+
+const kIsLinux = navigator.platform.indexOf("Linux") == 0;
+
+function runTests()
+{
+  var editor0 = document.getElementById("editor0");
+  var editor1 = document.getElementById("editor1");
+  var editor2 = document.getElementById("editor2");
+  var editor3 = document.getElementById("editor3");
+  var editor4 = document.getElementById("editor4");
+  var editor5 = document.getElementById("editor5");
+  var editor6 = document.getElementById("editor6");
+
+  ok(editor1.getBoundingClientRect().height - editor0.getBoundingClientRect().height > 1,
+        "an editor having a <br> element and an empty editor shouldn't be same height");
+  ok(Math.abs(editor1.getBoundingClientRect().height - editor2.getBoundingClientRect().height) <= 1,
+     "an editor having only a <br> element and an editor having \"a\" should be same height");
+
+  editor2.focus();
+  synthesizeKey("VK_RIGHT", {});
+  synthesizeKey("VK_BACK_SPACE", {});
+  is(editor2.innerHTML, "<br>",
+     "an editor which had \"a\" should have only <br> element after Backspace keypress");
+  ok(Math.abs(editor2.getBoundingClientRect().height - editor1.getBoundingClientRect().height) <= 1,
+     "an editor whose content was removed by Backspace key should have a place to put a caret");
+
+  editor3.focus();
+  synthesizeKey("VK_LEFT", {});
+  synthesizeKey("VK_DELETE", {});
+  is(editor3.innerHTML, "<br>",
+     "an editor which had \"b\" should have only <br> element after Delete keypress");
+  ok(Math.abs(editor3.getBoundingClientRect().height - editor1.getBoundingClientRect().height) <= 1,
+     "an editor whose content was removed by Delete key should have a place to put a caret");
+
+  editor4.focus();
+  window.getSelection().selectAllChildren(editor4);
+  synthesizeKey("VK_BACK_SPACE", {});
+  is(editor4.innerHTML, "<br>",
+     "an editor which had \"c\" should have only <br> element after removing selected text with Backspace key");
+  ok(Math.abs(editor4.getBoundingClientRect().height - editor1.getBoundingClientRect().height) <= 1,
+     "an editor whose content was selected and removed by Backspace key should have a place to put a caret");
+
+  editor5.focus();
+  window.getSelection().selectAllChildren(editor5);
+  synthesizeKey("VK_DELETE", {});
+  is(editor5.innerHTML, "<br>",
+     "an editor which had \"d\" should have only <br> element after removing selected text with Delete key");
+  ok(Math.abs(editor5.getBoundingClientRect().height - editor1.getBoundingClientRect().height) <= 1,
+     "an editor whose content was selected and removed by Delete key should have a place to put a caret");
+
+  editor6.focus();
+  window.getSelection().selectAllChildren(editor6);
+  synthesizeKey("x", { accelKey: true });
+  is(editor6.innerHTML, "<br>",
+     "an editor which had \"e\" should have only <br> element after removing selected text by \"Cut\"");
+  ok(Math.abs(editor6.getBoundingClientRect().height - editor1.getBoundingClientRect().height) <= 1,
+     "an editor whose content was selected and removed by \"Cut\" should have a place to put a caret");
+
+  editor0.focus();
+  synthesizeKey("VK_BACK_SPACE", {});
+  is(editor0.innerHTML, "",
+     "an empty editor should keep being empty even if Backspace key is pressed");
+  synthesizeKey("VK_DELETE", {});
+  is(editor0.innerHTML, "",
+     "an empty editor should keep being empty even if Delete key is pressed");
+
+  SimpleTest.finish();
+}
+
+SimpleTest.waitForFocus(runTests);
+
+</script>
+</pre>
+</body>
+</html>
--- a/gfx/doc/AsyncPanZoom.md
+++ b/gfx/doc/AsyncPanZoom.md
@@ -143,25 +143,140 @@ As described above, there are two copies
 Usually for architectures like this, there is a single "source of truth" value and the other value is simply a copy.
 However, in this case that is not easily possible to do.
 The reason is that both of these values can be legitimately modified.
 On the compositor side, the input events the user is triggering modify the scroll position, which is then propagated to the main thread.
 However, on the main thread, web content might be running Javascript code that programatically sets the scroll position (via window.scrollTo, for example).
 Scroll changes driven from the main thread are just as legitimate and need to be propagated to the compositor thread, so that the visual display updates in response.
 
 Because the cross-thread messaging is asynchronous, reconciling the two types of scroll changes is a tricky problem.
-Our design solves this using various flags and generation counters that ensures compositor-driven scroll changes never overwrite content-driven scroll changes - this behaviour is usually the desired outcome.
+Our design solves this using various flags and generation counters.
+The general heuristic we have is that content-driven scroll position changes (e.g. scrollTo from JS) are never lost.
+For instance, if the user is doing an async scroll with their finger and content does a scrollTo in the middle, then some of the async scroll would occur before the "jump" and the rest after the "jump".
 
 ### Content preventing default behaviour of input events
 
 Another problem that we need to deal with is that web content is allowed to intercept touch events and prevent the "default behaviour" of scrolling.
 This ability is defined in web standards and is non-negotiable.
 Touch event listeners in web content are allowed call preventDefault() on the touchstart or first touchmove event for a touch point; doing this is supposed to "consume" the event and prevent touch-based panning.
 As we saw in a previous section, the input event needs to be untransformed by the APZ code before it can be delivered to content.
 But, because of the preventDefault problem, we cannot fully process the touch event in the APZ code until content has had a chance to handle it.
 Web browsers in general solve this problem by inserting a delay of up to 300ms before processing the input - that is, web content is allowed up to 300ms to process the event and call preventDefault on it.
 If web content takes longer than 300ms, or if it completes handling of the event without calling preventDefault, then the browser immediately starts processing the events.
 The touch-action CSS property from the pointer-events spec is intended to allow eliminating this 300ms delay, although for backwards compatibility it will still be needed for a while.
 
 The way the APZ implementation deals with this is that upon receiving a touch event, it immediately returns an untransformed version that can be dispatched to content.
 It also schedules a 300ms timeout during which content is allowed to prevent scrolling.
 There is an API that allows the main-thread event dispatching code to notify the APZ as to whether or not the default action should be prevented.
 If the APZ content response timeout expires, or if the main-thread event dispatching code notifies the APZ of the preventDefault status, then the APZ continues with the processing of the events (which may involve discarding the events).
+
+## Technical details
+
+This section describes various pieces of the APZ code, and goes into more specific detail on APIs and code than the previous sections.
+The primary purpose of this section is to help people who plan on making changes to the code, while also not going into so much detail that it needs to be updated with every patch.
+
+### Overall flow of input events
+
+This section describes how input events flow through the APZ code.
+<ol>
+<li value="1">
+Input events arrive from the hardware/widget code into the APZ via APZCTreeManager::ReceiveInputEvent.
+The thread that invokes this is called the input thread, and may or may not be the same as the Gecko main thread.
+</li>
+<li value="2">
+Conceptually the first thing that the APZCTreeManager does is to group these events into "input blocks".
+An input block is a contiguous set of events that get handled together.
+For example with touch events, all events following a touchstart up to but not including the next touchstart are in the same block.
+All of the events in a given block will go to the same APZC instance and will either all be processed or all be dropped.
+</li>
+<li value="3">
+Using the first event in the input block, the APZCTreeManager does a hit-test to see which APZC it hits.
+If no APZC is hit, the events are discarded and we jump to step 6.
+Otherwise, the input block is tagged with the APZC and put into a global APZ input queue.
+</li>
+<li value="4">
+ <ol>
+  <li value="i">
+   If the input events are not touch events, or if the APZC is for content without touch listeners, any available events in the input block are processed.
+   These may trigger behaviours like scrolling or tap gestures.
+  </li>
+  <li value="ii">
+   If the input events are touch events and the APZC is for content with touch listeners, the events are left in the queue and a 300ms timeout is initiated.
+   If the timeout expires before step 9 is completed, the APZ assumes the input block was not cancelled and processes them as part of step 10.
+  </li>
+ </ol>
+</li>
+<li value="5">
+The call stack unwinds back to APZCTreeManager::ReceiveInputEvent, which does an in-place modification of the input event so that any async transforms are removed.
+</li>
+<li value="6">
+The call stack unwinds back to the widget code that called ReceiveInputEvent.
+This code now has the event in the coordinate space Gecko is expecting, and so can dispatch it to the Gecko main thread.
+</li>
+<li value="7">
+Gecko performs its own usual hit-testing and event dispatching for the event.
+As part of this, it records whether any touch listeners cancelled the input block by calling preventDefault().
+</li>
+<li value="8">
+The call stack unwinds back to the widget code, which sends a notification to the APZ code by calling APZCTreeManager::ContentReceivedTouch on the input thread.
+This happens only once per input block.
+</li>
+<li value="9">
+ <ol>
+  <li value="i">
+   If the events were processed as part of step 4(i), the call to ContentReceivedTouch is ignored and step 10 is skipped.
+  </li>
+  <li value="ii">
+   If events were queued as part of step 4(ii), and steps 5-8 take less than 300ms, the ContentReceivedTouch call marks the input block ready for processing.
+  </li>
+  <li value="iii">
+   If events were queued as part of step 4(ii), but steps 5-8 take longer than 300ms, the ContentReceivedTouch will be ignored and step 10 will already have happened.
+  </li>
+ </ol>
+</li>
+<li value="10">
+If events were queued as part of step 4(ii) they are now either processed (if the input block was not cancelled, or if the timeout expired) or dropped (if it was cancelled).
+Processing the events may trigger behaviours like scrolling or tap gestures.
+</li>
+</ol>
+
+If the CSS touch-action property is enabled, the above steps are modified as follows:
+<ul>
+<li>
+ In step 4, the APZC also requires the allowed touch-action behaviours for the input event. This is not available yet, so the events are always queued.
+</li>
+<li>
+ In step 6, the widget code determines the content element at the point under the input element, and notifies the APZ code of the allowed touch-action behaviours.
+ This is done via a call to APZCTreeManager::SetAllowedTouchBehavior on the input thread.
+</li>
+</ul>
+
+#### Threading considerations
+
+The bulk of the input processing in the APZ code happens on what we call "the input thread".
+In practice the input thread could be the Gecko main thread, the compositor thread, or some other thread.
+There are obvious downsides to using the Gecko main thread - that is, "asynchronous" panning and zooming is not really asynchronous as input events can only be processed while Gecko is idle.
+However, this is the current state of things on B2G and Metro.
+Using the compositor thread as the input thread could work on some platforms, but may be inefficient on others.
+For example, on Android (Fennec) we receive input events from the system on a dedicated UI thread.
+We would have to redispatch the input events to the compositor thread if we wanted to the input thread to be the same as the compositor thread.
+This introduces a potential for higher latency, particularly if the compositor does any blocking operations - blocking SwapBuffers operations, for example.
+As a result, the APZ code itself does not assume that the input thread will be the same as the Gecko main thread or the compositor thread.
+
+#### Active vs. inactive scrollframes
+
+The number of scrollframes on a page is potentially unbounded.
+However, we do not want to create a separate layer for each scrollframe right away, as this would require large amounts of memory.
+Therefore, scrollframes as designated as either "active" or "inactive".
+Active scrollframes are the ones that do have their contents put on a separate layer (or set of layers), and inactive ones do not.
+
+Consider a page with a scrollframe that is initially inactive.
+When layout generates the layers for this page, it inserts a "scrollinfo" layer into the layer tree to let the APZ know that there is potentially scrollable content there.
+The scrollinfo layer is an empty ContainerLayer, which does not require much extra memory.
+The composition bounds of this scrollinfo layer are used on the compositor for hit-testing, and a "placeholder" APZC is created for this scrollframe.
+When the user starts interacting with that content, the hit-test in the APZ code finds the placeholder APZC and starts routing it the events as usual.
+The APZC eventually sends a repaint request to the main thread, and that repaint request sets a displayport on the scrollframe.
+Setting the displayport activates the scrollframe and causes it to get pushed onto a separate layer (or set of layers).
+
+This model imples that when the user initially attempts to scroll an inactive scrollframe, it will not initially scroll visually.
+(This is because although the APZC is tracking the events and updating scroll position, there is no layer to which it can apply the async scroll offset.)
+Only after the round-trip to the gecko thread is complete is there a layer for async scrolling to actually occur.
+At that point the scrollframe will visually "jump" to the correct scroll offset.
--- a/gfx/layers/basic/BasicLayerManager.cpp
+++ b/gfx/layers/basic/BasicLayerManager.cpp
@@ -480,36 +480,36 @@ BasicLayerManager::EndTransactionInterna
   NS_ASSERTION(InConstruction(), "Should be in construction phase");
   mPhase = PHASE_DRAWING;
 
   RenderTraceLayers(mRoot, "FF00");
 
   mTransactionIncomplete = false;
 
   if (mRoot) {
+    if (aFlags & END_NO_COMPOSITE) {
+      // Apply pending tree updates before recomputing effective
+      // properties.
+      mRoot->ApplyPendingUpdatesToSubtree();
+    }
+
     // Need to do this before we call ApplyDoubleBuffering,
     // which depends on correct effective transforms
     if (mTarget) {
       mSnapEffectiveTransforms =
         !mTarget->GetDrawTarget()->GetUserData(&sDisablePixelSnapping);
     } else {
       mSnapEffectiveTransforms = true;
     }
     mRoot->ComputeEffectiveTransforms(mTarget ? Matrix4x4::From2D(ToMatrix(mTarget->CurrentMatrix())) : Matrix4x4());
 
     ToData(mRoot)->Validate(aCallback, aCallbackData, nullptr);
     if (mRoot->GetMaskLayer()) {
       ToData(mRoot->GetMaskLayer())->Validate(aCallback, aCallbackData, nullptr);
     }
-
-    if (aFlags & END_NO_COMPOSITE) {
-      // Apply pending tree updates before recomputing effective
-      // properties.
-      mRoot->ApplyPendingUpdatesToSubtree();
-    }
   }
 
   if (mTarget && mRoot &&
       !(aFlags & END_NO_IMMEDIATE_REDRAW) &&
       !(aFlags & END_NO_COMPOSITE)) {
     nsIntRect clipRect;
 
     {
--- a/gfx/thebes/gfxDWriteFonts.cpp
+++ b/gfx/thebes/gfxDWriteFonts.cpp
@@ -579,17 +579,17 @@ gfxDWriteFont::Measure(gfxTextRun *aText
 bool
 gfxDWriteFont::ProvidesGlyphWidths() const
 {
     return !mUseSubpixelPositions ||
            (mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD);
 }
 
 int32_t
-gfxDWriteFont::GetGlyphWidth(gfxContext *aCtx, uint16_t aGID)
+gfxDWriteFont::GetGlyphWidth(DrawTarget& aDrawTarget, uint16_t aGID)
 {
     if (!mGlyphWidths) {
         mGlyphWidths = new nsDataHashtable<nsUint32HashKey,int32_t>(128);
     }
 
     int32_t width = -1;
     if (mGlyphWidths->Get(aGID, &width)) {
         return width;
--- a/gfx/thebes/gfxDWriteFonts.h
+++ b/gfx/thebes/gfxDWriteFonts.h
@@ -49,17 +49,17 @@ public:
                                uint32_t aStart, uint32_t aEnd,
                                BoundingBoxType aBoundingBoxType,
                                gfxContext *aContextForTightBoundingBox,
                                Spacing *aSpacing,
                                uint16_t aOrientation);
 
     virtual bool ProvidesGlyphWidths() const;
 
-    virtual int32_t GetGlyphWidth(gfxContext *aCtx, uint16_t aGID);
+    virtual int32_t GetGlyphWidth(DrawTarget& aDrawTarget, uint16_t aGID);
 
     virtual mozilla::TemporaryRef<mozilla::gfx::GlyphRenderingOptions>
         GetGlyphRenderingOptions(const TextRunDrawParams* aRunParams = nullptr) MOZ_OVERRIDE;
 
     virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontCacheSizes* aSizes) const;
     virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontCacheSizes* aSizes) const;
--- a/gfx/thebes/gfxFT2FontBase.cpp
+++ b/gfx/thebes/gfxFT2FontBase.cpp
@@ -163,17 +163,17 @@ gfxFT2FontBase::GetGlyph(uint32_t unicod
             unicode = id;
         }
     }
 
     return GetGlyph(unicode);
 }
 
 int32_t
-gfxFT2FontBase::GetGlyphWidth(gfxContext *aCtx, uint16_t aGID)
+gfxFT2FontBase::GetGlyphWidth(DrawTarget& aDrawTarget, uint16_t aGID)
 {
     cairo_text_extents_t extents;
     GetGlyphExtents(aGID, &extents);
     // convert to 16.16 fixed point
     return NS_lround(0x10000 * extents.x_advance);
 }
 
 bool
--- a/gfx/thebes/gfxFT2FontBase.h
+++ b/gfx/thebes/gfxFT2FontBase.h
@@ -20,17 +20,17 @@ public:
 
     uint32_t GetGlyph(uint32_t aCharCode);
     void GetGlyphExtents(uint32_t aGlyph,
                          cairo_text_extents_t* aExtents);
     virtual uint32_t GetSpaceGlyph();
     virtual bool ProvidesGetGlyph() const { return true; }
     virtual uint32_t GetGlyph(uint32_t unicode, uint32_t variation_selector);
     virtual bool ProvidesGlyphWidths() const { return true; }
-    virtual int32_t GetGlyphWidth(gfxContext *aCtx, uint16_t aGID);
+    virtual int32_t GetGlyphWidth(DrawTarget& aDrawTarget, uint16_t aGID);
 
     cairo_scaled_font_t *CairoScaledFont() { return mScaledFont; };
     virtual bool SetupCairoFont(gfxContext *aContext);
 
     virtual FontType GetType() const { return FONT_TYPE_FT2; }
 
     mozilla::gfx::FontOptions* GetFontOptions() { return &mFontOptions; }
 
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -786,32 +786,32 @@ gfxFont::~gfxFont()
 
 gfxFloat
 gfxFont::GetGlyphHAdvance(gfxContext *aCtx, uint16_t aGID)
 {
     if (!SetupCairoFont(aCtx)) {
         return 0;
     }
     if (ProvidesGlyphWidths()) {
-        return GetGlyphWidth(aCtx, aGID) / 65536.0;
+        return GetGlyphWidth(*aCtx->GetDrawTarget(), aGID) / 65536.0;
     }
     if (mFUnitsConvFactor == 0.0f) {
         GetMetrics(eHorizontal);
     }
     NS_ASSERTION(mFUnitsConvFactor > 0.0f,
                  "missing font unit conversion factor");
     if (!mHarfBuzzShaper) {
         mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
     }
     gfxHarfBuzzShaper* shaper =
         static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
     if (!shaper->Initialize()) {
         return 0;
     }
-    return shaper->GetGlyphHAdvance(aCtx, aGID) / 65536.0;
+    return shaper->GetGlyphHAdvance(aGID) / 65536.0;
 }
 
 /*static*/
 PLDHashOperator
 gfxFont::AgeCacheEntry(CacheHashEntry *aEntry, void *aUserData)
 {
     if (!aEntry->mShapedWord) {
         NS_ASSERTION(aEntry->mShapedWord, "cache entry has no gfxShapedWord!");
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -1262,16 +1262,19 @@ class GlyphBufferAzure;
 struct TextRunDrawParams;
 struct FontDrawParams;
 
 class gfxFont {
 
     friend class gfxHarfBuzzShaper;
     friend class gfxGraphiteShaper;
 
+protected:
+    typedef mozilla::gfx::DrawTarget DrawTarget;
+
 public:
     nsrefcnt AddRef(void) {
         NS_PRECONDITION(int32_t(mRefCnt) >= 0, "illegal refcnt");
         if (mExpirationState.IsTracked()) {
             gfxFontCache::GetCache()->RemoveObject(this);
         }
         ++mRefCnt;
         NS_LOG_ADDREF(this, mRefCnt, "gfxFont", sizeof(*this));
@@ -1704,17 +1707,17 @@ public:
         FONT_TYPE_FT2,
         FONT_TYPE_MAC,
         FONT_TYPE_OS2,
         FONT_TYPE_CAIRO
     } FontType;
 
     virtual FontType GetType() const = 0;
 
-    virtual mozilla::TemporaryRef<mozilla::gfx::ScaledFont> GetScaledFont(mozilla::gfx::DrawTarget *aTarget)
+    virtual mozilla::TemporaryRef<mozilla::gfx::ScaledFont> GetScaledFont(DrawTarget* aTarget)
     { return gfxPlatform::GetPlatform()->GetScaledFontForFont(aTarget, this); }
 
     bool KerningDisabled() {
         return mKerningSet && !mKerningEnabled;
     }
 
     /**
      * Subclass this object to be notified of glyph changes. Delete the object
@@ -1815,17 +1818,17 @@ protected:
     // if they do not override this, harfbuzz will use unhinted widths
     // derived from the font tables
     virtual bool ProvidesGlyphWidths() const {
         return false;
     }
 
     // The return value is interpreted as a horizontal advance in 16.16 fixed
     // point format.
-    virtual int32_t GetGlyphWidth(gfxContext *aCtx, uint16_t aGID) {
+    virtual int32_t GetGlyphWidth(DrawTarget& aDrawTarget, uint16_t aGID) {
         return -1;
     }
 
     void AddGlyphChangeObserver(GlyphChangeObserver *aObserver);
     void RemoveGlyphChangeObserver(GlyphChangeObserver *aObserver);
 
     // whether font contains substitution lookups containing spaces
     bool HasSubstitutionRulesWithSpaceLookups(int32_t aRunScript);
--- a/gfx/thebes/gfxGDIFont.cpp
+++ b/gfx/thebes/gfxGDIFont.cpp
@@ -455,28 +455,28 @@ gfxGDIFont::GetGlyph(uint32_t aUnicode, 
         glyph = 0;
     }
 
     mGlyphIDs->Put(aUnicode, glyph);
     return glyph;
 }
 
 int32_t
-gfxGDIFont::GetGlyphWidth(gfxContext *aCtx, uint16_t aGID)
+gfxGDIFont::GetGlyphWidth(DrawTarget& aDrawTarget, uint16_t aGID)
 {
     if (!mGlyphWidths) {
         mGlyphWidths = new nsDataHashtable<nsUint32HashKey,int32_t>(128);
     }
 
     int32_t width;
     if (mGlyphWidths->Get(aGID, &width)) {
         return width;
     }
 
-    DCFromContext dc(aCtx);
+    DCFromDrawTarget dc(aDrawTarget);
     AutoSelectFont fs(dc, GetHFONT());
 
     int devWidth;
     if (GetCharWidthI(dc, aGID, 1, nullptr, &devWidth)) {
         // clamp value to range [0..0x7fff], and convert to 16.16 fixed-point
         devWidth = std::min(std::max(0, devWidth), 0x7fff);
         width = devWidth << 16;
         mGlyphWidths->Put(aGID, width);
--- a/gfx/thebes/gfxGDIFont.h
+++ b/gfx/thebes/gfxGDIFont.h
@@ -55,17 +55,17 @@ public:
         return !mFontEntry->HasCmapTable();
     }
 
     virtual uint32_t GetGlyph(uint32_t aUnicode, uint32_t aVarSelector);
 
     virtual bool ProvidesGlyphWidths() const { return true; }
 
     // get hinted glyph width in pixels as 16.16 fixed-point value
-    virtual int32_t GetGlyphWidth(gfxContext *aCtx, uint16_t aGID);
+    virtual int32_t GetGlyphWidth(DrawTarget& aDrawTarget, uint16_t aGID);
 
     virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontCacheSizes* aSizes) const;
     virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontCacheSizes* aSizes) const;
 
     virtual FontType GetType() const { return FONT_TYPE_GDI; }
 
--- a/gfx/thebes/gfxGraphiteShaper.cpp
+++ b/gfx/thebes/gfxGraphiteShaper.cpp
@@ -45,17 +45,18 @@ gfxGraphiteShaper::~gfxGraphiteShaper()
     mFont->GetFontEntry()->ReleaseGrFace(mGrFace);
 }
 
 /*static*/ float
 gfxGraphiteShaper::GrGetAdvance(const void* appFontHandle, uint16_t glyphid)
 {
     const CallbackData *cb =
         static_cast<const CallbackData*>(appFontHandle);
-    return FixedToFloat(cb->mFont->GetGlyphWidth(cb->mContext, glyphid));
+    return FixedToFloat(cb->mFont->GetGlyphWidth(*cb->mContext->GetDrawTarget(),
+                                                 glyphid));
 }
 
 static inline uint32_t
 MakeGraphiteLangTag(uint32_t aTag)
 {
     uint32_t grLangTag = aTag;
     // replace trailing space-padding with NULs for graphite
     uint32_t mask = 0x000000FF;
--- a/gfx/thebes/gfxHarfBuzzShaper.cpp
+++ b/gfx/thebes/gfxHarfBuzzShaper.cpp
@@ -179,18 +179,17 @@ struct LongMetric {
 
 struct GlyphMetrics {
     LongMetric           metrics[1]; // actually numberOfLongMetrics
 // the variable-length metrics[] array is immediately followed by:
 //  AutoSwap_PRUint16    leftSideBearing[];
 };
 
 hb_position_t
-gfxHarfBuzzShaper::GetGlyphHAdvance(gfxContext *aContext,
-                                    hb_codepoint_t glyph) const
+gfxHarfBuzzShaper::GetGlyphHAdvance(hb_codepoint_t glyph) const
 {
     // font did not implement GetGlyphWidth, so get an unhinted value
     // directly from the font tables
 
     NS_ASSERTION((mNumLongHMetrics > 0) && mHmtxTable != nullptr,
                  "font is lacking metrics, we shouldn't be here");
 
     if (glyph >= uint32_t(mNumLongHMetrics)) {
@@ -203,18 +202,17 @@ gfxHarfBuzzShaper::GetGlyphHAdvance(gfxC
     const GlyphMetrics* metrics =
         reinterpret_cast<const GlyphMetrics*>(hb_blob_get_data(mHmtxTable,
                                                                nullptr));
     return FloatToFixed(mFont->FUnitsToDevUnitsFactor() *
                         uint16_t(metrics->metrics[glyph].advanceWidth));
 }
 
 hb_position_t
-gfxHarfBuzzShaper::GetGlyphVAdvance(gfxContext *aContext,
-                                    hb_codepoint_t glyph) const
+gfxHarfBuzzShaper::GetGlyphVAdvance(hb_codepoint_t glyph) const
 {
     if (!mVmtxTable) {
         // Must be a "vertical" font that doesn't actually have vertical metrics;
         // use a fixed advance.
         return FloatToFixed(mFont->GetMetrics(gfxFont::eVertical).aveCharWidth);
     }
 
     NS_ASSERTION(mNumLongVMetrics > 0,
@@ -238,35 +236,33 @@ gfxHarfBuzzShaper::GetGlyphVAdvance(gfxC
 hb_position_t
 gfxHarfBuzzShaper::HBGetGlyphHAdvance(hb_font_t *font, void *font_data,
                                       hb_codepoint_t glyph, void *user_data)
 {
     const gfxHarfBuzzShaper::FontCallbackData *fcd =
         static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
     gfxFont *gfxfont = fcd->mShaper->GetFont();
     if (gfxfont->ProvidesGlyphWidths()) {
-        return gfxfont->GetGlyphWidth(fcd->mContext, glyph);
-    } else {
-        return fcd->mShaper->GetGlyphHAdvance(fcd->mContext, glyph);
+        return gfxfont->GetGlyphWidth(*fcd->mContext->GetDrawTarget(), glyph);
     }
+    return fcd->mShaper->GetGlyphHAdvance(glyph);
 }
 
 /* static */
 hb_position_t
 gfxHarfBuzzShaper::HBGetGlyphVAdvance(hb_font_t *font, void *font_data,
                                       hb_codepoint_t glyph, void *user_data)
 {
     const gfxHarfBuzzShaper::FontCallbackData *fcd =
         static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
     gfxFont *gfxfont = fcd->mShaper->GetFont();
     if (gfxfont->ProvidesGlyphWidths()) {
-        return gfxfont->GetGlyphWidth(fcd->mContext, glyph);
-    } else {
-        return fcd->mShaper->GetGlyphVAdvance(fcd->mContext, glyph);
+        return gfxfont->GetGlyphWidth(*fcd->mContext->GetDrawTarget(), glyph);
     }
+    return fcd->mShaper->GetGlyphVAdvance(glyph);
 }
 
 /* static */
 hb_bool_t
 gfxHarfBuzzShaper::HBGetGlyphHOrigin(hb_font_t *font, void *font_data,
                                      hb_codepoint_t glyph,
                                      hb_position_t *x, hb_position_t *y,
                                      void *user_data)
@@ -291,25 +287,25 @@ struct VORGrec {
 hb_bool_t
 gfxHarfBuzzShaper::HBGetGlyphVOrigin(hb_font_t *font, void *font_data,
                                      hb_codepoint_t glyph,
                                      hb_position_t *x, hb_position_t *y,
                                      void *user_data)
 {
     const gfxHarfBuzzShaper::FontCallbackData *fcd =
         static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
-    fcd->mShaper->GetGlyphVOrigin(fcd->mContext, glyph, x, y);
+    fcd->mShaper->GetGlyphVOrigin(glyph, x, y);
     return true;
 }
 
 void
-gfxHarfBuzzShaper::GetGlyphVOrigin(gfxContext *aContext, hb_codepoint_t aGlyph,
+gfxHarfBuzzShaper::GetGlyphVOrigin(hb_codepoint_t aGlyph,
                                    hb_position_t *aX, hb_position_t *aY) const
 {
-    *aX = -0.5 * GetGlyphHAdvance(aContext, aGlyph);
+    *aX = -0.5 * GetGlyphHAdvance(aGlyph);
 
     if (mVORGTable) {
         // We checked in Initialize() that the VORG table is safely readable,
         // so no length/bounds-check needed here.
         const VORG* vorg =
             reinterpret_cast<const VORG*>(hb_blob_get_data(mVORGTable, nullptr));
 
         const VORGrec *lo = reinterpret_cast<const VORGrec*>(vorg + 1);
--- a/gfx/thebes/gfxHarfBuzzShaper.h
+++ b/gfx/thebes/gfxHarfBuzzShaper.h
@@ -37,23 +37,21 @@ public:
     // get a given font table in harfbuzz blob form
     hb_blob_t * GetFontTable(hb_tag_t aTag) const;
 
     // map unicode character to glyph ID
     hb_codepoint_t GetGlyph(hb_codepoint_t unicode,
                             hb_codepoint_t variation_selector) const;
 
     // get harfbuzz glyph advance, in font design units
-    hb_position_t GetGlyphHAdvance(gfxContext *aContext,
-                                   hb_codepoint_t glyph) const;
+    hb_position_t GetGlyphHAdvance(hb_codepoint_t glyph) const;
 
-    hb_position_t GetGlyphVAdvance(gfxContext *aContext,
-                                   hb_codepoint_t glyph) const;
+    hb_position_t GetGlyphVAdvance(hb_codepoint_t glyph) const;
 
-    void GetGlyphVOrigin(gfxContext *aContext, hb_codepoint_t aGlyph,
+    void GetGlyphVOrigin(hb_codepoint_t aGlyph,
                          hb_position_t *aX, hb_position_t *aY) const;
 
     // get harfbuzz horizontal advance in 16.16 fixed point format.
     static hb_position_t
     HBGetGlyphHAdvance(hb_font_t *font, void *font_data,
                        hb_codepoint_t glyph, void *user_data);
 
     // get harfbuzz vertical advance in 16.16 fixed point format.
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -1,17 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* vim: set ts=8 sts=4 et sw=4 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "mozilla/ArrayUtils.h"
+#include "gfxWindowsPlatform.h"
 
-#include "gfxWindowsPlatform.h"
+#include "cairo.h"
+#include "mozilla/ArrayUtils.h"
 
 #include "gfxImageSurface.h"
 #include "gfxWindowsSurface.h"
 
 #include "nsUnicharUtils.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/WindowsVersion.h"
@@ -75,16 +76,41 @@
 #endif
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 using namespace mozilla::widget;
 using namespace mozilla::image;
 
+DCFromDrawTarget::DCFromDrawTarget(DrawTarget& aDrawTarget)
+{
+  mDC = nullptr;
+  if (aDrawTarget.GetBackendType() == BackendType::CAIRO) {
+    cairo_surface_t *surf = (cairo_surface_t*)
+        aDrawTarget.GetNativeSurface(NativeSurfaceType::CAIRO_SURFACE);
+    cairo_surface_type_t surfaceType = cairo_surface_get_type(surf);
+    if (surfaceType == CAIRO_SURFACE_TYPE_WIN32 ||
+        surfaceType == CAIRO_SURFACE_TYPE_WIN32_PRINTING) {
+      mDC = cairo_win32_surface_get_dc(surf);
+      mNeedsRelease = false;
+      SaveDC(mDC);
+      cairo_t* ctx = (cairo_t*)
+          aDrawTarget.GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT);
+      cairo_scaled_font_t* scaled = cairo_get_scaled_font(ctx);
+      cairo_win32_scaled_font_select_font(scaled, mDC);
+    }
+    if (!mDC) {
+      mDC = GetDC(nullptr);
+      SetGraphicsMode(mDC, GM_ADVANCED);
+      mNeedsRelease = true;
+    }
+  }
+}
+
 #ifdef CAIRO_HAS_D2D_SURFACE
 
 static const char *kFeatureLevelPref =
   "gfx.direct3d.last_used_feature_level_idx";
 static const int kSupportedFeatureLevels[] =
   { D3D10_FEATURE_LEVEL_10_1, D3D10_FEATURE_LEVEL_10_0,
     D3D10_FEATURE_LEVEL_9_3 };
 
@@ -1526,17 +1552,17 @@ bool DoesD3D11DeviceWork(ID3D11Device *d
     gfxWindowsPlatform::GetDLLVersion(L"dlumd32.dll", displayLinkModuleVersionString);
     uint64_t displayLinkModuleVersion;
     if (!ParseDriverVersion(displayLinkModuleVersionString, &displayLinkModuleVersion)) {
 #if defined(MOZ_CRASHREPORTER)
       CrashReporter::AppendAppNotesToCrashReport(NS_LITERAL_CSTRING("DisplayLink: could not parse version\n"));
 #endif
       return false;
     }
-    if (displayLinkModuleVersion <= GFX_DRIVER_VERSION(8,6,1,36484)) {
+    if (displayLinkModuleVersion <= V(8,6,1,36484)) {
 #if defined(MOZ_CRASHREPORTER)
       CrashReporter::AppendAppNotesToCrashReport(NS_LITERAL_CSTRING("DisplayLink: too old version\n"));
 #endif
       return false;
     }
   }
 
   if (GetModuleHandleW(L"atidxx32.dll")) {
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -15,18 +15,18 @@
 
 #include "gfxFontUtils.h"
 #include "gfxWindowsSurface.h"
 #include "gfxFont.h"
 #ifdef CAIRO_HAS_DWRITE_FONT
 #include "gfxDWriteFonts.h"
 #endif
 #include "gfxPlatform.h"
-#include "gfxContext.h"
-
+#include "gfxTypes.h"
+#include "mozilla/Attributes.h"
 #include "nsTArray.h"
 #include "nsDataHashtable.h"
 
 #include "mozilla/RefPtr.h"
 
 #include <windows.h>
 #include <objbase.h>
 
@@ -39,65 +39,55 @@
 // Win 8.0 SDK types we'll need when building using older sdks.
 #if !defined(D3D_FEATURE_LEVEL_11_1) // defined in the 8.0 SDK only
 #define D3D_FEATURE_LEVEL_11_1 static_cast<D3D_FEATURE_LEVEL>(0xb100)
 #define D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION 2048
 #define D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION 4096
 #endif
 
 namespace mozilla {
+namespace gfx {
+class DrawTarget;
+}
 namespace layers {
 class DeviceManagerD3D9;
 class ReadbackManagerD3D11;
 }
 }
 struct IDirect3DDevice9;
 struct ID3D11Device;
 struct IDXGIAdapter1;
 
 class nsIMemoryReporter;
 
-// Utility to get a Windows HDC from a thebes context,
-// used by both GDI and Uniscribe font shapers
-struct DCFromContext {
-    DCFromContext(gfxContext *aContext) {
-        dc = nullptr;
-        nsRefPtr<gfxASurface> aSurface = aContext->CurrentSurface();
-        if (aSurface &&
-            (aSurface->GetType() == gfxSurfaceType::Win32 ||
-             aSurface->GetType() == gfxSurfaceType::Win32Printing))
-        {
-            dc = static_cast<gfxWindowsSurface*>(aSurface.get())->GetDC();
-            needsRelease = false;
-            SaveDC(dc);
-            cairo_scaled_font_t* scaled =
-                cairo_get_scaled_font(aContext->GetCairo());
-            cairo_win32_scaled_font_select_font(scaled, dc);
-        }
-        if (!dc) {
-            dc = GetDC(nullptr);
-            SetGraphicsMode(dc, GM_ADVANCED);
-            needsRelease = true;
-        }
-    }
+/**
+ * Utility to get a Windows HDC from a Moz2D DrawTarget.  If the DrawTarget is
+ * not backed by a HDC this will get the HDC for the screen device context
+ * instead.
+ */
+class MOZ_STACK_CLASS DCFromDrawTarget MOZ_FINAL
+{
+public:
+    DCFromDrawTarget(mozilla::gfx::DrawTarget& aDrawTarget);
 
-    ~DCFromContext() {
-        if (needsRelease) {
-            ReleaseDC(nullptr, dc);
+    ~DCFromDrawTarget() {
+        if (mNeedsRelease) {
+            ReleaseDC(nullptr, mDC);
         } else {
-            RestoreDC(dc, -1);
+            RestoreDC(mDC, -1);
         }
     }
 
     operator HDC () {
-        return dc;
+        return mDC;
     }
 
-    HDC dc;
-    bool needsRelease;
+private:
+    HDC mDC;
+    bool mNeedsRelease;
 };
 
 // ClearType parameters set by running ClearType tuner
 struct ClearTypeParameterInfo {
     ClearTypeParameterInfo() :
         gamma(-1), pixelStructure(-1), clearTypeLevel(-1), enhancedContrast(-1)
     { }
 
--- a/image/public/imgIRequest.idl
+++ b/image/public/imgIRequest.idl
@@ -14,17 +14,17 @@ interface nsIPrincipal;
 
 /**
  * imgIRequest interface
  *
  * @author Stuart Parmenter <stuart@mozilla.com>
  * @version 0.1
  * @see imagelib2
  */
-[scriptable, builtinclass, uuid(710f22f0-558b-11e4-8ed6-0800200c9a66)]
+[scriptable, builtinclass, uuid(dc61f0ea-4139-4c2a-ae69-cec82d33e089)]
 interface imgIRequest : nsIRequest
 {
   /**
    * the image container...
    * @return the image object associated with the request.
    * @attention NEED DOCS
    */
   readonly attribute imgIContainer image;
@@ -78,16 +78,21 @@ interface imgIRequest : nsIRequest
 
   /**
    * The URI the image load was started with.  Note that this might not be the
    * actual URI for the image (e.g. if HTTP redirects happened during the
    * load).
    */
   readonly attribute nsIURI URI;
 
+  /**
+   * The URI of the resource we ended up loading after all redirects, etc.
+   */
+  readonly attribute nsIURI currentURI;
+
   readonly attribute imgINotificationObserver notificationObserver;
 
   readonly attribute string mimeType;
 
   /**
    * Clone this request; the returned request will have aObserver as the
    * observer.  aObserver will be notified synchronously (before the clone()
    * call returns) with all the notifications that have already been dispatched
--- a/image/src/Decoder.cpp
+++ b/image/src/Decoder.cpp
@@ -241,45 +241,16 @@ Decoder::AllocateFrame()
   // Mark ourselves as not needing another frame before talking to anyone else
   // so they can tell us if they need yet another.
   mNeedsNewFrame = false;
 
   return rv;
 }
 
 void
-Decoder::FlushInvalidations()
-{
-  NS_ABORT_IF_FALSE(!HasDecoderError(),
-                    "Not allowed to make more decoder calls after error!");
-
-  // If we've got an empty invalidation rect, we have nothing to do
-  if (mInvalidRect.IsEmpty())
-    return;
-
-  if (mObserver) {
-#ifdef XP_MACOSX
-    // Bug 703231
-    // Because of high quality down sampling on mac we show scan lines while decoding.
-    // Bypass this problem by redrawing the border.
-    if (mImageMetadata.HasSize()) {
-      nsIntRect mImageBound(0, 0, mImageMetadata.GetWidth(), mImageMetadata.GetHeight());
-
-      mInvalidRect.Inflate(1);
-      mInvalidRect = mInvalidRect.Intersect(mImageBound);
-    }
-#endif
-    mObserver->FrameChanged(&mInvalidRect);
-  }
-
-  // Clear the invalidation rectangle
-  mInvalidRect.SetEmpty();
-}
-
-void
 Decoder::SetSizeOnImage()
 {
   MOZ_ASSERT(mImageMetadata.HasSize(), "Should have size");
   MOZ_ASSERT(mImageMetadata.HasOrientation(), "Should have orientation");
 
   mImage.SetSize(mImageMetadata.GetWidth(),
                  mImageMetadata.GetHeight(),
                  mImageMetadata.GetOrientation());
@@ -315,35 +286,25 @@ Decoder::PostSize(int32_t aWidth,
 }
 
 void
 Decoder::PostFrameStart()
 {
   // We shouldn't already be mid-frame
   NS_ABORT_IF_FALSE(!mInFrame, "Starting new frame but not done with old one!");
 
-  // We should take care of any invalidation region when wrapping up the
-  // previous frame
-  NS_ABORT_IF_FALSE(mInvalidRect.IsEmpty(),
-                    "Start image frame with non-empty invalidation region!");
-
   // Update our state to reflect the new frame
   mFrameCount++;
   mInFrame = true;
 
   // Decoder implementations should only call this method if they successfully
   // appended the frame to the image. So mFrameCount should always match that
   // reported by the Image.
   NS_ABORT_IF_FALSE(mFrameCount == mImage.GetNumFrames(),
                     "Decoder frame count doesn't match image's!");
-
-  // Fire notifications
-  if (mObserver) {
-    mObserver->OnStartFrame();
-  }
 }
 
 void
 Decoder::PostFrameStop(FrameBlender::FrameAlpha aFrameAlpha /* = FrameBlender::kFrameHasAlpha */,
                        FrameBlender::FrameDisposalMethod aDisposalMethod /* = FrameBlender::kDisposeKeep */,
                        int32_t aTimeout /* = 0 */,
                        FrameBlender::FrameBlendMethod aBlendMethod /* = FrameBlender::kBlendOver */)
 {
@@ -358,19 +319,16 @@ Decoder::PostFrameStop(FrameBlender::Fra
     mCurrentFrame->SetHasNoAlpha();
   }
 
   mCurrentFrame->SetFrameDisposalMethod(aDisposalMethod);
   mCurrentFrame->SetRawTimeout(aTimeout);
   mCurrentFrame->SetBlendMethod(aBlendMethod);
   mCurrentFrame->ImageUpdated(mCurrentFrame->GetRect());
 
-  // Flush any invalidations before we finish the frame
-  FlushInvalidations();
-
   // Fire notifications
   if (mObserver) {
     mObserver->OnStopFrame();
     if (mFrameCount > 1 && !mIsAnimated) {
       mIsAnimated = true;
       mObserver->OnImageIsAnimated();
     }
   }
--- a/image/src/Decoder.h
+++ b/image/src/Decoder.h
@@ -64,24 +64,27 @@ public:
    * Informs the shared decoder that all the data has been written.
    * Should only be used if InitSharedDecoder was useed
    *
    * Notifications Sent: TODO
    */
   void FinishSharedDecoder();
 
   /**
-   * Tells the decoder to flush any pending invalidations. This informs the image
-   * frame of its decoded region, and sends the appropriate OnDataAvailable call
-   * to consumers.
-   *
-   * This can be called any time when we're midway through decoding a frame,
-   * and must be called after finishing a frame (before starting a new one).
+   * Gets the invalidation region accumulated by the decoder so far, and clears
+   * the decoder's invalidation region. This means that each call to
+   * TakeInvalidRect() returns only the invalidation region accumulated since
+   * the last call to TakeInvalidRect().
    */
-  void FlushInvalidations();
+  nsIntRect TakeInvalidRect()
+  {
+    nsIntRect invalidRect = mInvalidRect;
+    mInvalidRect.SetEmpty();
+    return invalidRect;
+  }
 
   // We're not COM-y, so we don't get refcounts by default
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Decoder)
 
   /*
    * State.
    */
 
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -479,20 +479,20 @@ RasterImage::RequestRefresh(const TimeSt
     // once for all frames that we've now passed (if AdvanceFrame() was called
     // more than once).
     #ifdef DEBUG
       mFramesNotified++;
     #endif
 
     UpdateImageContainer();
 
-    // Explicitly call this on mStatusTracker so we're sure to not interfere
-    // with the decoding process
-    if (mStatusTracker)
-      mStatusTracker->FrameChanged(&res.dirtyRect);
+    if (mStatusTracker) {
+      mStatusTracker->SyncNotifyDifference(ImageStatusDiff::NoChange(),
+                                           res.dirtyRect);
+    }
   }
 
   if (res.animationFinished) {
     mAnimationFinished = true;
     EvaluateAnimation();
   }
 }
 
@@ -650,16 +650,27 @@ RasterImage::GetCurrentFrameIndex() cons
 }
 
 uint32_t
 RasterImage::GetRequestedFrameIndex(uint32_t aWhichFrame) const
 {
   return aWhichFrame == FRAME_FIRST ? 0 : GetCurrentFrameIndex();
 }
 
+nsIntRect
+RasterImage::GetFirstFrameRect()
+{
+  if (mAnim) {
+    return mAnim->GetFirstFrameRefreshArea();
+  }
+
+  // Fall back to our size. This is implicitly zero-size if !mHasSize.
+  return nsIntRect(nsIntPoint(0,0), mSize);
+}
+
 //******************************************************************************
 /* [notxpcom] boolean frameIsOpaque(in uint32_t aWhichFrame); */
 NS_IMETHODIMP_(bool)
 RasterImage::FrameIsOpaque(uint32_t aWhichFrame)
 {
   if (aWhichFrame > FRAME_MAX_VALUE) {
     NS_WARNING("aWhichFrame outside valid range!");
     return false;
@@ -1452,17 +1463,17 @@ RasterImage::ResetAnimation()
   UpdateImageContainer();
 
   // Note - We probably want to kick off a redecode somewhere around here when
   // we fix bug 500402.
 
   // Update display
   if (mStatusTracker) {
     nsIntRect rect = mAnim->GetFirstFrameRefreshArea();
-    mStatusTracker->FrameChanged(&rect);
+    mStatusTracker->SyncNotifyDifference(ImageStatusDiff::NoChange(), rect);
   }
 
   // Start the animation again. It may not have been running before, if
   // mAnimationFinished was true before entering this function.
   EvaluateAnimation();
 
   return NS_OK;
 }
@@ -1553,24 +1564,16 @@ RasterImage::AddSourceData(const char *a
 
   // If we're not storing source data and we've previously gotten the size,
   // write the data directly to the decoder. (If we haven't gotten the size,
   // we'll queue up the data and write it out when we do.)
   if (!StoringSourceData() && mHasSize) {
     rv = WriteToDecoder(aBuffer, aCount, DECODE_SYNC);
     CONTAINER_ENSURE_SUCCESS(rv);
 
-    // We're not storing source data, so this data is probably coming straight
-    // from the network. In this case, we want to display data as soon as we
-    // get it, so we want to flush invalidations after every write.
-    nsRefPtr<Decoder> kungFuDeathGrip = mDecoder;
-    mInDecoder = true;
-    mDecoder->FlushInvalidations();
-    mInDecoder = false;
-
     rv = FinishedSomeDecoding();
     CONTAINER_ENSURE_SUCCESS(rv);
   }
 
   // Otherwise, we're storing data in the source buffer
   else {
 
     // Store the data
@@ -2411,25 +2414,16 @@ RasterImage::SyncDecode()
 
   MOZ_ASSERT(mDecoder);
 
   // Write everything we have
   rv = DecodeSomeData(mSourceData.Length() - mDecoder->BytesDecoded(),
                       DECODE_SYNC);
   CONTAINER_ENSURE_SUCCESS(rv);
 
-  // When we're doing a sync decode, we want to get as much information from the
-  // image as possible. We've send the decoder all of our data, so now's a good
-  // time  to flush any invalidations (in case we don't have all the data and what
-  // we got left us mid-frame).
-  nsRefPtr<Decoder> kungFuDeathGrip = mDecoder;
-  mInDecoder = true;
-  mDecoder->FlushInvalidations();
-  mInDecoder = false;
-
   rv = FinishedSomeDecoding();
   CONTAINER_ENSURE_SUCCESS(rv);
   
   // If our decoder's still open, there's still work to be done.
   if (mDecoder) {
     DecodePool::Singleton()->RequestDecode(this);
   }
 
@@ -2493,19 +2487,18 @@ RasterImage::CanScale(GraphicsFilter aFi
 }
 
 void
 RasterImage::NotifyNewScaledFrame()
 {
   if (mStatusTracker) {
     // Send an invalidation so observers will repaint and can take advantage of
     // the new scaled frame if possible.
-    // XXX(seth): Why does FrameChanged take a pointer and not a reference?
-    nsIntRect invalidationRect(0, 0, mSize.width, mSize.height);
-    mStatusTracker->FrameChanged(&invalidationRect);
+    nsIntRect rect(0, 0, mSize.width, mSize.height);
+    mStatusTracker->SyncNotifyDifference(ImageStatusDiff::NoChange(), rect);
   }
 }
 
 void
 RasterImage::RequestScale(imgFrame* aFrame,
                           uint32_t aFlags,
                           const nsIntSize& aSize)
 {
@@ -2996,19 +2989,22 @@ RasterImage::FinishedSomeDecoding(eShutd
   }
 
   // Ensure that, if the decoder is the last reference to the image, we don't
   // destroy it by destroying the decoder.
   nsRefPtr<RasterImage> image(this);
 
   bool done = false;
   bool wasSize = false;
+  nsIntRect invalidRect;
   nsresult rv = NS_OK;
 
   if (image->mDecoder) {
+    invalidRect = image->mDecoder->TakeInvalidRect();
+
     if (request && request->mChunkCount && !image->mDecoder->IsSizeDecode()) {
       Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS, request->mChunkCount);
     }
 
     if (!image->mHasSize && image->mDecoder->HasSize()) {
       image->mDecoder->SetSizeOnImage();
     }
 
@@ -3041,42 +3037,57 @@ RasterImage::FinishedSomeDecoding(eShutd
       // decoding routines have been finished.
       rv = image->ShutdownDecoder(aIntent);
       if (NS_FAILED(rv)) {
         image->DoError();
       }
     }
   }
 
+  if (GetCurrentFrameIndex() > 0) {
+    // Don't send invalidations for animated frames after the first; let
+    // RequestRefresh take care of that.
+    invalidRect = nsIntRect();
+  }
+  if (mHasBeenDecoded && !invalidRect.IsEmpty()) {
+    // Don't send partial invalidations if we've been decoded before.
+    invalidRect = mDecoded ? GetFirstFrameRect()
+                           : nsIntRect();
+  }
+
   ImageStatusDiff diff =
     request ? image->mStatusTracker->Difference(request->mStatusTracker)
             : image->mStatusTracker->DecodeStateAsDifference();
   image->mStatusTracker->ApplyDifference(diff);
 
   if (mNotifying) {
     // Accumulate the status changes. We don't permit recursive notifications
     // because they cause subtle concurrency bugs, so we'll delay sending out
     // the notifications until we pop back to the lowest invocation of
     // FinishedSomeDecoding on the stack.
     NS_WARNING("Recursively notifying in RasterImage::FinishedSomeDecoding!");
     mStatusDiff.Combine(diff);
+    mInvalidRect.Union(invalidRect);
   } else {
     MOZ_ASSERT(mStatusDiff.IsNoChange(), "Shouldn't have an accumulated change at this point");
-
-    while (!diff.IsNoChange()) {
+    MOZ_ASSERT(mInvalidRect.IsEmpty(), "Shouldn't have an accumulated invalidation rect here");
+
+    while (!diff.IsNoChange() || !invalidRect.IsEmpty()) {
       // Tell the observers what happened.
       mNotifying = true;
-      image->mStatusTracker->SyncNotifyDifference(diff);
+      image->mStatusTracker->SyncNotifyDifference(diff, invalidRect);
       mNotifying = false;
 
       // Gather any status changes that may have occurred as a result of sending
       // out the previous notifications. If there were any, we'll send out
       // notifications for them next.
       diff = mStatusDiff;
       mStatusDiff = ImageStatusDiff::NoChange();
+      invalidRect = mInvalidRect;
+      mInvalidRect = nsIntRect();
     }
   }
 
   return RequestDecodeIfNeeded(rv, aIntent, done, wasSize);
 }
 
 already_AddRefed<imgIContainer>
 RasterImage::Unwrap()
@@ -3479,41 +3490,16 @@ RasterImage::DecodePool::DecodeSomeOfIma
       break;
   }
 
   if (aImg->mDecodeRequest) {
     aImg->mDecodeRequest->mDecodeTime += (TimeStamp::Now() - start);
     aImg->mDecodeRequest->mChunkCount += chunkCount;
   }
 
-  // Flush invalidations (and therefore paint) now that we've decoded all the
-  // chunks we're going to.
-  //
-  // However, don't paint if:
-  //
-  //  * This was an until-size decode.  Until-size decodes are always followed
-  //    by normal decodes, so don't bother painting.
-  //
-  //  * The decoder flagged an error.  The decoder may have written garbage
-  //    into the output buffer; don't paint it to the screen.
-  //
-  //  * We have all the source data.  This disables progressive display of
-  //    previously-decoded images, thus letting us finish decoding faster,
-  //    since we don't waste time painting while we decode.
-  //    Decoder::PostFrameStop() will flush invalidations once the decode is
-  //    done.
-
-  if (aDecodeType != DECODE_TYPE_UNTIL_SIZE &&
-      !aImg->mDecoder->HasError() &&
-      !aImg->mHasSourceData) {
-    aImg->mInDecoder = true;
-    aImg->mDecoder->FlushInvalidations();
-    aImg->mInDecoder = false;
-  }
-
   return NS_OK;
 }
 
 RasterImage::DecodeDoneWorker::DecodeDoneWorker(RasterImage* image, DecodeRequest* request)
  : mImage(image)
  , mRequest(request)
 {}
 
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -547,16 +547,18 @@ private:
                                                     uint32_t aFlags,
                                                     bool aShouldSyncNotify = true);
 
   already_AddRefed<imgFrame> LookupFrameNoDecode(uint32_t aFrameNum);
   DrawableFrameRef LookupFrame(uint32_t aFrameNum, uint32_t aFlags, bool aShouldSyncNotify = true);
   uint32_t GetCurrentFrameIndex() const;
   uint32_t GetRequestedFrameIndex(uint32_t aWhichFrame) const;
 
+  nsIntRect GetFirstFrameRect();
+
   size_t SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation,
                                                  MallocSizeOf aMallocSizeOf) const;
 
   void EnsureAnimExists();
 
   nsresult InternalAddFrameHelper(uint32_t framenum, imgFrame *frame,
                                   uint8_t **imageData, uint32_t *imageLength,
                                   uint32_t **paletteData, uint32_t *paletteLength,
@@ -657,16 +659,17 @@ private: // data
   nsRefPtr<Decoder>          mDecoder;
   nsRefPtr<DecodeRequest>    mDecodeRequest;
 
   bool                       mInDecoder;
   // END LOCKED MEMBER VARIABLES
 
   // Notification state. Used to avoid recursive notifications.
   ImageStatusDiff            mStatusDiff;
+  nsIntRect                  mInvalidRect;
   bool                       mNotifying:1;
 
   // Boolean flags (clustered together to conserve space):
   bool                       mHasSize:1;       // Has SetSize() been called?
   bool                       mDecodeOnDraw:1;  // Decoding on draw?
   bool                       mMultipart:1;     // Multipart?
   bool                       mDiscardable:1;   // Is container discardable?
   bool                       mHasSourceData:1; // Do we have source data?
--- a/image/src/VectorImage.cpp
+++ b/image/src/VectorImage.cpp
@@ -566,18 +566,20 @@ VectorImage::SendInvalidationNotificatio
   // called for them. Ordinarily this isn't needed, since we send out
   // invalidation notifications in OnSVGDocumentLoaded, but in rare cases the
   // SVG document may not be 100% ready to render at that time. In those cases
   // we would miss the subsequent invalidations if we didn't send out the
   // notifications directly in |InvalidateObservers...|.
 
   if (mStatusTracker) {
     SurfaceCache::Discard(this);
-    mStatusTracker->FrameChanged(&nsIntRect::GetMaxSizedIntRect());
-    mStatusTracker->OnStopFrame();
+    ImageStatusDiff diff;
+    diff.diffState = FLAG_FRAME_STOPPED;
+    mStatusTracker->ApplyDifference(diff);
+    mStatusTracker->SyncNotifyDifference(diff, nsIntRect::GetMaxSizedIntRect());
   }
 }
 
 NS_IMETHODIMP_(nsIntRect)
 VectorImage::GetImageSpaceInvalidationRect(const nsIntRect& aRect)
 {
   return aRect;
 }
@@ -1114,27 +1116,21 @@ VectorImage::OnSVGDocumentLoaded()
   mIsFullyLoaded = true;
   mHaveAnimations = mSVGDocumentWrapper->IsAnimated();
 
   // Start listening to our image for rendering updates.
   mRenderingObserver = new SVGRootRenderingObserver(mSVGDocumentWrapper, this);
 
   // Tell *our* observers that we're done loading.
   if (mStatusTracker) {
-    nsRefPtr<imgStatusTracker> clone = mStatusTracker->CloneForRecording();
-    imgDecoderObserver* observer = clone->GetDecoderObserver();
-
-    observer->OnStartContainer(); // Signal that width/height are available.
-    observer->FrameChanged(&nsIntRect::GetMaxSizedIntRect());
-    observer->OnStopFrame();
-    observer->OnStopDecode(NS_OK); // Unblock page load.
-
-    ImageStatusDiff diff = mStatusTracker->Difference(clone);
+    ImageStatusDiff diff;
+    diff.diffState = FLAG_HAS_SIZE | FLAG_FRAME_STOPPED | FLAG_DECODE_STOPPED |
+                     FLAG_ONLOAD_UNBLOCKED;
     mStatusTracker->ApplyDifference(diff);
-    mStatusTracker->SyncNotifyDifference(diff);
+    mStatusTracker->SyncNotifyDifference(diff, nsIntRect::GetMaxSizedIntRect());
   }
 
   EvaluateAnimation();
 }
 
 void
 VectorImage::OnSVGDocumentError()
 {
--- a/image/src/imgDecoderObserver.h
+++ b/image/src/imgDecoderObserver.h
@@ -64,30 +64,16 @@ public:
    * called, the size has been set on the container and STATUS_SIZE_AVAILABLE
    * has been set on the associated imgRequest.
    */
   virtual void OnStartContainer() = 0;
 
   /**
    * Decode notification.
    *
-   * Called when we know a frame has begun decoding.
-   */
-  virtual void OnStartFrame() = 0;
-
-  /**
-   * Decode notification.
-   *
-   * called when there is more to paint.
-   */
-  virtual void FrameChanged(const nsIntRect * aDirtyRect) = 0;
-
-  /**
-   * Decode notification.
-   *
    * called when a frame is finished decoding.
    */
   virtual void OnStopFrame() = 0;
 
   /**
    * Notification for when an image is known to be animated. This should be
    * fired at the earliest possible time.
    */
--- a/image/src/imgRequest.cpp
+++ b/image/src/imgRequest.cpp
@@ -35,24 +35,27 @@
 #include "nsNetUtil.h"
 #include "nsIProtocolHandler.h"
 #include "imgIRequest.h"
 
 using namespace mozilla;
 using namespace mozilla::image;
 
 #if defined(PR_LOGGING)
-PRLogModuleInfo *
+PRLogModuleInfo*
 GetImgLog()
 {
-  static PRLogModuleInfo *sImgLog;
+  static PRLogModuleInfo* sImgLog;
   if (!sImgLog)
     sImgLog = PR_NewLogModule("imgRequest");
   return sImgLog;
 }
+#define LOG_TEST(level) (GetImgLog() && PR_LOG_TEST(GetImgLog(), (level)))
+#else
+#define LOG_TEST(level) false
 #endif
 
 NS_IMPL_ISUPPORTS(imgRequest,
                   nsIStreamListener, nsIRequestObserver,
                   nsIThreadRetargetableStreamListener,
                   nsIChannelEventSink,
                   nsIInterfaceRequestor,
                   nsIAsyncVerifyRedirectCallback)
@@ -362,16 +365,31 @@ nsresult imgRequest::GetURI(ImageURL **a
     *aURI = mURI;
     NS_ADDREF(*aURI);
     return NS_OK;
   }
 
   return NS_ERROR_FAILURE;
 }
 
+nsresult imgRequest::GetCurrentURI(nsIURI **aURI)
+{
+  MOZ_ASSERT(aURI);
+
+  LOG_FUNC(GetImgLog(), "imgRequest::GetCurrentURI");
+
+  if (mCurrentURI) {
+    *aURI = mCurrentURI;
+    NS_ADDREF(*aURI);
+    return NS_OK;
+  }
+
+  return NS_ERROR_FAILURE;
+}
+
 nsresult imgRequest::GetImageErrorCode()
 {
   return mImageErrorCode;
 }
 
 nsresult imgRequest::GetSecurityInfo(nsISupports **aSecurityInfo)
 {
   LOG_FUNC(GetImgLog(), "imgRequest::GetSecurityInfo");
@@ -1059,26 +1077,34 @@ imgRequest::OnRedirectVerifyCallback(nsr
       mNewRedirectChannel = nullptr;
       return NS_OK;
   }
 
   mChannel = mNewRedirectChannel;
   mTimedChannel = do_QueryInterface(mChannel);
   mNewRedirectChannel = nullptr;
 
-#if defined(PR_LOGGING)
-  nsAutoCString oldspec;
-  if (mCurrentURI)
-    mCurrentURI->GetSpec(oldspec);
-  LOG_MSG_WITH_PARAM(GetImgLog(), "imgRequest::OnChannelRedirect", "old", oldspec.get());
-#endif
+  if (LOG_TEST(PR_LOG_DEBUG)) {
+    nsAutoCString spec;
+    if (mCurrentURI)
+      mCurrentURI->GetSpec(spec);
+    LOG_MSG_WITH_PARAM(GetImgLog(), "imgRequest::OnChannelRedirect", "old", spec.get());
+  }
 
   // make sure we have a protocol that returns data rather than opens
   // an external application, e.g. mailto:
   mChannel->GetURI(getter_AddRefs(mCurrentURI));
+
+  if (LOG_TEST(PR_LOG_DEBUG)) {
+    nsAutoCString spec;
+    if (mCurrentURI)
+      mCurrentURI->GetSpec(spec);
+    LOG_MSG_WITH_PARAM(GetImgLog(), "imgRequest::OnChannelRedirect", "new", spec.get());
+  }
+
   bool doesNotReturnData = false;
   nsresult rv =
     NS_URIChainHasFlags(mCurrentURI, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
                         &doesNotReturnData);
 
   if (NS_SUCCEEDED(rv) && doesNotReturnData)
     rv = NS_ERROR_ABORT;
 
--- a/image/src/imgRequest.h
+++ b/image/src/imgRequest.h
@@ -132,16 +132,17 @@ public:
   // Resize the cache entry to 0 if it exists
   void ResetCacheEntry();
 
   // Update the cache entry size based on the image container
   void UpdateCacheEntrySize();
 
   // OK to use on any thread.
   nsresult GetURI(ImageURL **aURI);
+  nsresult GetCurrentURI(nsIURI **aURI);
 
   nsresult GetImageErrorCode(void);
 
 private:
   friend class imgCacheEntry;
   friend class imgRequestProxy;
   friend class imgLoader;
   friend class imgCacheValidator;
--- a/image/src/imgRequestProxy.cpp
+++ b/image/src/imgRequestProxy.cpp
@@ -532,16 +532,24 @@ NS_IMETHODIMP imgRequestProxy::GetImageE
 NS_IMETHODIMP imgRequestProxy::GetURI(nsIURI **aURI)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread to convert URI");
   nsCOMPtr<nsIURI> uri = mURI->ToIURI();
   uri.forget(aURI);
   return NS_OK;
 }
 
+nsresult imgRequestProxy::GetCurrentURI(nsIURI **aURI)
+{
+  if (!GetOwner())
+    return NS_ERROR_FAILURE;
+
+  return GetOwner()->GetCurrentURI(aURI);
+}
+
 nsresult imgRequestProxy::GetURI(ImageURL **aURI)
 {
   if (!mURI)
     return NS_ERROR_FAILURE;
 
   NS_ADDREF(*aURI = mURI);
 
   return NS_OK;
--- a/image/src/imgStatusTracker.cpp
+++ b/image/src/imgStatusTracker.cpp
@@ -57,32 +57,16 @@ public:
   {
     LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStartContainer");
     nsRefPtr<imgStatusTracker> tracker = mTracker.get();
     if (!tracker) { return; }
     nsRefPtr<Image> image = tracker->GetImage();;
     tracker->RecordStartContainer(image);
   }
 
-  virtual void OnStartFrame() MOZ_OVERRIDE
-  {
-    LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStartFrame");
-    nsRefPtr<imgStatusTracker> tracker = mTracker.get();
-    if (!tracker) { return; }
-    tracker->RecordStartFrame();
-  }
-
-  virtual void FrameChanged(const nsIntRect* dirtyRect) MOZ_OVERRIDE
-  {
-    LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::FrameChanged");
-    nsRefPtr<imgStatusTracker> tracker = mTracker.get();
-    if (!tracker) { return; }
-    tracker->RecordFrameChanged(dirtyRect);
-  }
-
   virtual void OnStopFrame() MOZ_OVERRIDE
   {
     LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStopFrame");
     nsRefPtr<imgStatusTracker> tracker = mTracker.get();
     if (!tracker) { return; }
     tracker->RecordStopFrame();
     tracker->RecordUnblockOnload();
   }
@@ -154,17 +138,16 @@ imgStatusTracker::imgStatusTracker(const
   : mImage(aOther.mImage)
   , mState(aOther.mState)
     // Note: we explicitly don't copy several fields:
     //  - mRequestRunnable, because it won't be nulled out when the
     //    mRequestRunnable's Run function eventually gets called.
     //  - mProperties, because we don't need it and it'd just point at the same
     //    object
     //  - mConsumers, because we don't need to talk to consumers
-    //  - mInvalidRect, because the point of it is to be fired off and reset
 {
   mTrackerObserver = new imgStatusTrackerObserver(this);
 }
 
 imgStatusTracker::~imgStatusTracker()
 {}
 
 imgStatusTrackerInit::imgStatusTrackerInit(mozilla::image::Image* aImage,
@@ -365,102 +348,86 @@ imgStatusTracker::NotifyCurrentState(img
 
   // We don't keep track of
   nsCOMPtr<nsIRunnable> ev = new imgStatusNotifyRunnable(this, proxy);
   NS_DispatchToCurrentThread(ev);
 }
 
 #define NOTIFY_IMAGE_OBSERVERS(func) \
   do { \
-    ProxyArray::ForwardIterator iter(proxies); \
+    ProxyArray::ForwardIterator iter(aProxies); \
     while (iter.HasMore()) { \
       nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get(); \
       if (proxy && !proxy->NotificationsDeferred()) { \
         proxy->func; \
       } \
     } \
   } while (false);
 
 /* static */ void
-imgStatusTracker::SyncNotifyState(ProxyArray& proxies,
-                                  bool hasImage, uint32_t state,
-                                  nsIntRect& dirtyRect)
+imgStatusTracker::SyncNotifyState(ProxyArray& aProxies,
+                                  bool aHasImage, uint32_t aState,
+                                  const nsIntRect& aDirtyRect)
 {
   MOZ_ASSERT(NS_IsMainThread());
   // OnStartRequest
-  if (state & FLAG_REQUEST_STARTED)
+  if (aState & FLAG_REQUEST_STARTED)
     NOTIFY_IMAGE_OBSERVERS(OnStartRequest());
 
   // OnStartContainer
-  if (state & FLAG_HAS_SIZE)
+  if (aState & FLAG_HAS_SIZE)
     NOTIFY_IMAGE_OBSERVERS(OnStartContainer());
 
   // OnStartDecode
-  if (state & FLAG_DECODE_STARTED)
+  if (aState & FLAG_DECODE_STARTED)
     NOTIFY_IMAGE_OBSERVERS(OnStartDecode());
 
   // BlockOnload
-  if (state & FLAG_ONLOAD_BLOCKED)
+  if (aState & FLAG_ONLOAD_BLOCKED)
     NOTIFY_IMAGE_OBSERVERS(BlockOnload());
 
-  if (hasImage) {
+  if (aHasImage) {
     // OnFrameUpdate
     // If there's any content in this frame at all (always true for
     // vector images, true for raster images that have decoded at
     // least one frame) then send OnFrameUpdate.
-    if (!dirtyRect.IsEmpty())
-      NOTIFY_IMAGE_OBSERVERS(OnFrameUpdate(&dirtyRect));
+    if (!aDirtyRect.IsEmpty())
+      NOTIFY_IMAGE_OBSERVERS(OnFrameUpdate(&aDirtyRect));
 
-    if (state & FLAG_FRAME_STOPPED)
+    if (aState & FLAG_FRAME_STOPPED)
       NOTIFY_IMAGE_OBSERVERS(OnStopFrame());
 
     // OnImageIsAnimated
-    if (state & FLAG_IS_ANIMATED)
+    if (aState & FLAG_IS_ANIMATED)
       NOTIFY_IMAGE_OBSERVERS(OnImageIsAnimated());
   }
 
   // Send UnblockOnload before OnStopDecode and OnStopRequest. This allows
   // observers that can fire events when they receive those notifications to do
   // so then, instead of being forced to wait for UnblockOnload.
-  if (state & FLAG_ONLOAD_UNBLOCKED) {
+  if (aState & FLAG_ONLOAD_UNBLOCKED) {
     NOTIFY_IMAGE_OBSERVERS(UnblockOnload());
   }
 
-  if (state & FLAG_DECODE_STOPPED) {
-    NS_ABORT_IF_FALSE(hasImage, "stopped decoding without ever having an image?");
+  if (aState & FLAG_DECODE_STOPPED) {
+    MOZ_ASSERT(aHasImage, "Stopped decoding without ever having an image?");
     NOTIFY_IMAGE_OBSERVERS(OnStopDecode());
   }
 
-  if (state & FLAG_REQUEST_STOPPED) {
-    NOTIFY_IMAGE_OBSERVERS(OnStopRequest(state & FLAG_MULTIPART_STOPPED));
+  if (aState & FLAG_REQUEST_STOPPED) {
+    NOTIFY_IMAGE_OBSERVERS(OnStopRequest(aState & FLAG_MULTIPART_STOPPED));
   }
 }
 
 ImageStatusDiff
 imgStatusTracker::Difference(imgStatusTracker* aOther) const
 {
   MOZ_ASSERT(aOther, "aOther cannot be null");
   ImageStatusDiff diff;
   diff.diffState = ~mState & aOther->mState;
-
-  // Only record partial invalidations if we haven't been decoded before.
-  // When images are re-decoded after discarding, we don't want to display
-  // partially decoded versions to the user.
-  const uint32_t combinedState = mState | aOther->mState;
-  const bool doInvalidations = !(mState & FLAG_DECODE_STOPPED) ||
-                               aOther->mState & FLAG_DECODE_STOPPED ||
-                               combinedState & FLAG_HAS_ERROR;
-
-  // Record and reset the invalid rectangle.
-  // XXX(seth): We shouldn't be resetting anything here; see bug 910441.
-  if (doInvalidations) {
-    diff.invalidRect = aOther->mInvalidRect;
-    aOther->mInvalidRect.SetEmpty();
-  }
-
   return diff;
 }
 
 ImageStatusDiff
 imgStatusTracker::DecodeStateAsDifference() const
 {
   ImageStatusDiff diff;
   // XXX(seth): Is FLAG_REQUEST_STARTED really the only non-"decode state" flag?
@@ -471,28 +438,25 @@ imgStatusTracker::DecodeStateAsDifferenc
 void
 imgStatusTracker::ApplyDifference(const ImageStatusDiff& aDiff)
 {
   LOG_SCOPE(GetImgLog(), "imgStatusTracker::ApplyDifference");
   mState |= aDiff.diffState;
 }
 
 void
-imgStatusTracker::SyncNotifyDifference(const ImageStatusDiff& diff)
+imgStatusTracker::SyncNotifyDifference(const ImageStatusDiff& aDiff,
+                                       const nsIntRect& aInvalidRect /* = nsIntRect() */)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Use mConsumers on main thread only");
   LOG_SCOPE(GetImgLog(), "imgStatusTracker::SyncNotifyDifference");
 
-  nsIntRect invalidRect = mInvalidRect.Union(diff.invalidRect);
-
-  SyncNotifyState(mConsumers, !!mImage, diff.diffState, invalidRect);
+  SyncNotifyState(mConsumers, !!mImage, aDiff.diffState, aInvalidRect);
 
-  mInvalidRect.SetEmpty();
-
-  if (diff.diffState & FLAG_HAS_ERROR) {
+  if (aDiff.diffState & FLAG_HAS_ERROR) {
     FireFailureNotification();
   }
 }
 
 already_AddRefed<imgStatusTracker>
 imgStatusTracker::CloneForRecording()
 {
   // Grab a ref to this to ensure it isn't deleted.
@@ -644,24 +608,16 @@ void
 imgStatusTracker::SendStartContainer(imgRequestProxy* aProxy)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!aProxy->NotificationsDeferred())
     aProxy->OnStartContainer();
 }
 
 void
-imgStatusTracker::RecordStartFrame()
-{
-  mInvalidRect.SetEmpty();
-}
-
-// No SendStartFrame since it's not observed below us.
-
-void
 imgStatusTracker::RecordStopFrame()
 {
   NS_ABORT_IF_FALSE(mImage, "RecordStopFrame called before we have an Image");
   mState |= FLAG_FRAME_STOPPED;
 }
 
 void
 imgStatusTracker::SendStopFrame(imgRequestProxy* aProxy)
@@ -741,33 +697,16 @@ imgStatusTracker::OnUnlockedDraw()
   while (iter.HasMore()) {
     nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get();
     if (proxy) {
       SendUnlockedDraw(proxy);
     }
   }
 }
 
-void
-imgStatusTracker::RecordFrameChanged(const nsIntRect* aDirtyRect)
-{
-  NS_ABORT_IF_FALSE(mImage,
-                    "RecordFrameChanged called before we have an Image");
-  mInvalidRect = mInvalidRect.Union(*aDirtyRect);
-}
-
-void
-imgStatusTracker::SendFrameChanged(imgRequestProxy* aProxy,
-                                   const nsIntRect* aDirtyRect)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  if (!aProxy->NotificationsDeferred())
-    aProxy->OnFrameUpdate(aDirtyRect);
-}
-
 /* non-virtual sort-of-nsIRequestObserver methods */
 void
 imgStatusTracker::RecordStartRequest()
 {
   // We're starting a new load, so clear any status and state bits indicating
   // load/decode.
   // XXX(seth): Are these really the only flags we want to clear?
   mState &= ~FLAG_REQUEST_STARTED;
@@ -891,32 +830,16 @@ imgStatusTracker::OnDiscard()
     nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get();
     if (proxy) {
       SendDiscard(proxy);
     }
   }
 }
 
 void
-imgStatusTracker::FrameChanged(const nsIntRect* aDirtyRect)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  RecordFrameChanged(aDirtyRect);
-
-  /* notify the kids */
-  ProxyArray::ForwardIterator iter(mConsumers);
-  while (iter.HasMore()) {
-    nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get();
-    if (proxy) {
-      SendFrameChanged(proxy, aDirtyRect);
-    }
-  }
-}
-
-void
 imgStatusTracker::OnStopFrame()
 {
   MOZ_ASSERT(NS_IsMainThread());
   RecordStopFrame();
 
   /* notify the kids */
   ProxyArray::ForwardIterator iter(mConsumers);
   while (iter.HasMore()) {
--- a/image/src/imgStatusTracker.h
+++ b/image/src/imgStatusTracker.h
@@ -41,36 +41,32 @@ enum {
   FLAG_IS_MULTIPART       = 1u << 9,
   FLAG_MULTIPART_STOPPED  = 1u << 10,
   FLAG_HAS_ERROR          = 1u << 11  // STATUS_ERROR
 };
 
 struct ImageStatusDiff
 {
   ImageStatusDiff()
-    : invalidRect()
-    , diffState(0)
+    : diffState(0)
   { }
 
   static ImageStatusDiff NoChange() { return ImageStatusDiff(); }
   bool IsNoChange() const { return *this == NoChange(); }
 
   bool operator!=(const ImageStatusDiff& aOther) const { return !(*this == aOther); }
   bool operator==(const ImageStatusDiff& aOther) const {
-    return aOther.invalidRect == invalidRect
-        && aOther.diffState == diffState;
+    return aOther.diffState == diffState;
   }
 
   void Combine(const ImageStatusDiff& aOther) {
-    invalidRect = invalidRect.Union(aOther.invalidRect);
     diffState |= aOther.diffState;
   }
 
-  nsIntRect invalidRect;
-  uint32_t  diffState;
+  uint32_t diffState;
 };
 
 } // namespace image
 } // namespace mozilla
 
 /*
  * The image status tracker is a class that encapsulates all the loading and
  * decoding status about an Image, and makes it possible to send notifications
@@ -174,30 +170,26 @@ public:
   // Call when the request is being cancelled.
   void RecordCancel();
 
   // Shorthand for recording all the load notifications: StartRequest,
   // StartContainer, StopRequest.
   void RecordLoaded();
 
   // Shorthand for recording all the decode notifications: StartDecode,
-  // StartFrame, DataAvailable, StopFrame, StopDecode.
+  // DataAvailable, StopFrame, StopDecode.
   void RecordDecoded();
 
   /* non-virtual imgDecoderObserver methods */
   // Functions with prefix Send- are main thread only, since they contain calls
   // to imgRequestProxy functions, which are expected on the main thread.
   void RecordStartDecode();
   void SendStartDecode(imgRequestProxy* aProxy);
   void RecordStartContainer(imgIContainer* aContainer);
   void SendStartContainer(imgRequestProxy* aProxy);
-  void RecordStartFrame();
-  // No SendStartFrame since it's not observed below us.
-  void RecordFrameChanged(const nsIntRect* aDirtyRect);
-  void SendFrameChanged(imgRequestProxy* aProxy, const nsIntRect* aDirtyRect);
   void RecordStopFrame();
   void SendStopFrame(imgRequestProxy* aProxy);
   void RecordStopDecode(nsresult statusg);
   void SendStopDecode(imgRequestProxy* aProxy, nsresult aStatus);
   void SendDiscard(imgRequestProxy* aProxy);
   void RecordUnlockedDraw();
   void SendUnlockedDraw(imgRequestProxy* aProxy);
   void RecordImageIsAnimated();
@@ -214,17 +206,16 @@ public:
   // All main thread only because they call functions (like SendStartRequest)
   // which are expected to be called on the main thread.
   void OnStartRequest();
   // OnDataAvailable will dispatch a call to itself onto the main thread if not
   // called there.
   void OnDataAvailable();
   void OnStopRequest(bool aLastPart, nsresult aStatus);
   void OnDiscard();
-  void FrameChanged(const nsIntRect* aDirtyRect);
   void OnUnlockedDraw();
   // This is called only by VectorImage, and only to ensure tests work
   // properly. Do not use it.
   void OnStopFrame();
 
   /* non-virtual imgIOnloadBlocker methods */
   // NB: If UnblockOnload is sent, and then we are asked to replay the
   // notifications, we will not send a BlockOnload/UnblockOnload pair.  This
@@ -260,43 +251,38 @@ public:
   mozilla::image::ImageStatusDiff DecodeStateAsDifference() const;
 
   // Update our state to incorporate the changes in aDiff.
   void ApplyDifference(const mozilla::image::ImageStatusDiff& aDiff);
 
   // Notify for the changes captured in an ImageStatusDiff. Because this may
   // result in recursive notifications, no decoding locks may be held.
   // Called on the main thread only.
-  void SyncNotifyDifference(const mozilla::image::ImageStatusDiff& aDiff);
-
-  nsIntRect GetInvalidRect() const { return mInvalidRect; }
+  void SyncNotifyDifference(const mozilla::image::ImageStatusDiff& aDiff,
+                            const nsIntRect& aInvalidRect = nsIntRect());
 
 private:
   typedef nsTObserverArray<mozilla::WeakPtr<imgRequestProxy>> ProxyArray;
   friend class imgStatusNotifyRunnable;
   friend class imgRequestNotifyRunnable;
   friend class imgStatusTrackerObserver;
   friend class imgStatusTrackerInit;
   imgStatusTracker(const imgStatusTracker& aOther);
 
   // Main thread only because it deals with the observer service.
   void FireFailureNotification();
 
   // Main thread only, since imgRequestProxy calls are expected on the main
   // thread, and mConsumers is not threadsafe.
-  static void SyncNotifyState(ProxyArray& proxies,
-                              bool hasImage, uint32_t state,
-                              nsIntRect& dirtyRect);
+  static void SyncNotifyState(ProxyArray& aProxies,
+                              bool aHasImage, uint32_t aState,
+                              const nsIntRect& aInvalidRect);
 
   nsCOMPtr<nsIRunnable> mRequestRunnable;
 
-  // The invalid area of the most recent frame we know about. (All previous
-  // frames are assumed to be fully valid.)
-  nsIntRect mInvalidRect;
-
   // This weak ref should be set null when the image goes out of scope.
   mozilla::image::Image* mImage;
 
   // List of proxies attached to the image. Each proxy represents a consumer
   // using the image. Array and/or individual elements should only be accessed
   // on the main thread.
   ProxyArray mConsumers;
 
--- a/image/test/mochitest/test_synchronized_animation.html
+++ b/image/test/mochitest/test_synchronized_animation.html
@@ -28,16 +28,17 @@ const Ci = Components.interfaces;
 const Cu = Components.utils;
 const gContent = document.getElementById("content");
 
 var gDispatched = false;
 var gRanEvent = false;
 var gObserver;
 var gImg1;
 var gImg2;
+var gFirstImageLoaded = false;
 var gOuter;
 var gFinished = false;
 var gFirstRequest = null;
 
 function cleanUpAndFinish() {
   if (gFinished) {
     return;
   }
@@ -66,41 +67,59 @@ function frameUpdate(aRequest) {
   }
 }
 
 function failTest() {
   ok(false, "timing out after " + FAILURE_TIMEOUT + "ms.  ");
   cleanUpAndFinish();
 }
 
+function waitForLoadAndTest(image) {
+  return () => {
+    // Draw the image into a canvas to ensure it's decoded.
+    var canvas = document.createElement('canvas');
+    var context = canvas.getContext('2d');
+    context.drawImage(image, 0, 0);
+
+    // Attach the observer.
+    var imgLoadingContent = image.QueryInterface(Ci.nsIImageLoadingContent);
+    imgLoadingContent.addObserver(gOuter);
+
+    // If the other image already loaded, add both images to the document, which
+    // begins the real test.
+    if (gFirstImageLoaded) {
+      gContent.appendChild(gImg1);
+      gContent.appendChild(gImg2);
+    } else {
+      gFirstImageLoaded = true;
+    }
+  };
+}
+
 function main() {
   gImg1 = new Image();
   gImg2 = new Image();
 
-  // Create, customize & attach decoder observer
+  // Create and customize decoder observer
   var obs = new ImageDecoderObserverStub();
   obs.frameUpdate = frameUpdate;
 
   gOuter = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools).createScriptedObserver(obs);
-  var imgLoadingContent = gImg1.QueryInterface(Ci.nsIImageLoadingContent);
-  imgLoadingContent.addObserver(gOuter);
-
-  imgLoadingContent = gImg2.QueryInterface(Ci.nsIImageLoadingContent);
-  imgLoadingContent.addObserver(gOuter);
 
   // We want to test the cold loading behavior, so clear cache in case an
   // earlier test got our image in there already.
   clearImageCache();
 
   // These are two copies of the same image; hence, they have the same frame rate.
   gImg1.src = "animated1.gif";
   gImg2.src = "animated2.gif";
 
-  gContent.appendChild(gImg1);
-  gContent.appendChild(gImg2);
+  // Wait for each image to load.
+  gImg1.addEventListener('load', waitForLoadAndTest(gImg1));
+  gImg2.addEventListener('load', waitForLoadAndTest(gImg2));
 
   // In case something goes wrong, fail earlier than mochitest timeout,
   // and with more information.
   setTimeout(failTest, FAILURE_TIMEOUT);
 }
 
 window.onload = main;
 
--- a/js/src/asmjs/AsmJSModule.cpp
+++ b/js/src/asmjs/AsmJSModule.cpp
@@ -495,17 +495,17 @@ OnDetached()
     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
 }
 
 static bool
 AsmJSHandleExecutionInterrupt()
 {
     AsmJSActivation *act = PerThreadData::innermostAsmJSActivation();
     act->module().setInterrupted(true);
-    bool ret = HandleExecutionInterrupt(act->cx());
+    bool ret = CheckForInterrupt(act->cx());
     act->module().setInterrupted(false);
     return ret;
 }
 
 static int32_t
 CoerceInPlace_ToInt32(MutableHandleValue val)
 {
     JSContext *cx = PerThreadData::innermostAsmJSActivation()->cx();
@@ -668,18 +668,18 @@ RedirectCall(void *fun, ABIFunctionType 
 }
 
 static void *
 AddressOf(AsmJSImmKind kind, ExclusiveContext *cx)
 {
     switch (kind) {
       case AsmJSImm_Runtime:
         return cx->runtimeAddressForJit();
-      case AsmJSImm_RuntimeInterrupt:
-        return cx->runtimeAddressOfInterrupt();
+      case AsmJSImm_RuntimeInterruptUint32:
+        return cx->runtimeAddressOfInterruptUint32();
       case AsmJSImm_StackLimit:
         return cx->stackLimitAddressForJitCode(StackForUntrustedScript);
       case AsmJSImm_ReportOverRecursed:
         return RedirectCall(FuncCast(AsmJSReportOverRecursed), Args_General0);
       case AsmJSImm_OnDetached:
         return RedirectCall(FuncCast(OnDetached), Args_General0);
       case AsmJSImm_HandleExecutionInterrupt:
         return RedirectCall(FuncCast(AsmJSHandleExecutionInterrupt), Args_General0);
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -2659,34 +2659,16 @@ AC_CACHE_CHECK(whether C++ requires impl
                                X x;,
                                ac_cv_cpp_unused_required=no,
                                ac_cv_cpp_unused_required=yes)])
 if test "$ac_cv_cpp_unused_required" = yes ; then
    AC_DEFINE(NEED_CPP_UNUSED_IMPLEMENTATIONS)
 fi
 
 
-dnl Some compilers have trouble comparing a constant reference to a templatized
-dnl class to zero, and require an explicit operator==() to be defined that takes
-dnl an int. This test separates the strong from the weak.
-
-AC_CACHE_CHECK(for trouble comparing to zero near std::operator!=(),
-               ac_cv_trouble_comparing_to_zero,
-               [AC_TRY_COMPILE([#include <algorithm>
-                                template <class T> class Foo {};
-                                class T2;
-                                template <class T> int operator==(const T2*, const T&) { return 0; }
-                                template <class T> int operator!=(const T2*, const T&) { return 0; }],
-                               [Foo<int> f; return (0 != f);],
-                               ac_cv_trouble_comparing_to_zero=no,
-                               ac_cv_trouble_comparing_to_zero=yes)])
-if test "$ac_cv_trouble_comparing_to_zero" = yes ; then
-  AC_DEFINE(HAVE_CPP_TROUBLE_COMPARING_TO_ZERO)
-fi
-
 # try harder, when checking for __thread support, see bug 521750 comment #33 and below
 # We pass MOZ_OPTIMIZE_LDFLAGS to the linker because if dead_strip is
 # enabled, the linker in xcode 4.1 will crash. Without this it would crash when
 # linking XUL.
 _SAVE_LDFLAGS=$LDFLAGS
 LDFLAGS="$LDFLAGS $DSO_PIC_CFLAGS $DSO_LDOPTS $MOZ_OPTIMIZE_LDFLAGS"
 AC_CACHE_CHECK(for __thread keyword for TLS variables,
                ac_cv_thread_keyword,
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -266,16 +266,17 @@ class GCRuntime
     // with a volatile flag that is set iff the counter is greater than
     // zero. (It will require some care to make sure the two variables stay in
     // sync.)
     bool isFJMinorCollecting() { return fjCollectionCounter > 0; }
     void incFJMinorCollecting() { fjCollectionCounter++; }
     void decFJMinorCollecting() { fjCollectionCounter--; }
 
     bool triggerGC(JS::gcreason::Reason reason);
+    void maybeAllocTriggerZoneGC(Zone *zone, const AutoLockGC &lock);
     bool triggerZoneGC(Zone *zone, JS::gcreason::Reason reason);
     bool maybeGC(Zone *zone);
     void maybePeriodicFullGC();
     void minorGC(JS::gcreason::Reason reason);
     void minorGC(JSContext *cx, JS::gcreason::Reason reason);
     void evictNursery(JS::gcreason::Reason reason = JS::gcreason::EVICT_NURSERY) { minorGC(reason); }
     bool gcIfNeeded(JSContext *cx = nullptr);
     void gc(JSGCInvocationKind gckind, JS::gcreason::Reason reason);
@@ -476,21 +477,25 @@ class GCRuntime
     template <AllowGC allowGC>
     static void *refillFreeListFromAnyThread(ThreadSafeContext *cx, AllocKind thingKind);
     static void *refillFreeListInGC(Zone *zone, AllocKind thingKind);
 
     // Free certain LifoAlloc blocks from the background sweep thread.
     void freeUnusedLifoBlocksAfterSweeping(LifoAlloc *lifo);
     void freeAllLifoBlocksAfterSweeping(LifoAlloc *lifo);
 
+    // Public here for ReleaseArenaLists and FinalizeTypedArenas.
+    void releaseArena(ArenaHeader *aheader, const AutoLockGC &lock);
+
   private:
     // For ArenaLists::allocateFromArena()
     friend class ArenaLists;
     Chunk *pickChunk(const AutoLockGC &lock,
                      AutoMaybeStartBackgroundAllocation &maybeStartBGAlloc);
+    ArenaHeader *allocateArena(Chunk *chunk, Zone *zone, AllocKind kind, const AutoLockGC &lock);
     inline void arenaAllocatedDuringGC(JS::Zone *zone, ArenaHeader *arena);
 
     template <AllowGC allowGC>
     static void *refillFreeListFromMainThread(JSContext *cx, AllocKind thingKind);
     static void *refillFreeListOffMainThread(ExclusiveContext *cx, AllocKind thingKind);
     static void *refillFreeListPJS(ForkJoinContext *cx, AllocKind thingKind);
 
     /*
@@ -538,18 +543,17 @@ class GCRuntime
     void getNextZoneGroup();
     void endMarkingZoneGroup();
     void beginSweepingZoneGroup();
     bool shouldReleaseObservedTypes();
     void endSweepingZoneGroup();
     bool sweepPhase(SliceBudget &sliceBudget);
     void endSweepPhase(bool lastGC);
     void sweepZones(FreeOp *fop, bool lastGC);
-    void decommitArenasFromAvailableList(Chunk **availableListHeadp);
-    void decommitArenas();
+    void decommitArenas(const AutoLockGC &lock);
     void expireChunksAndArenas(bool shouldShrink, const AutoLockGC &lock);
     void sweepBackgroundThings();
     void assertBackgroundSweepingFinished();
     bool shouldCompact();
 #ifdef JSGC_COMPACTING
     void sweepTypesAfterCompacting(Zone *zone);
     void sweepZoneAfterCompacting(Zone *zone);
     void compactPhase(bool lastGC);
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -32,16 +32,17 @@ struct JSRuntime;
 namespace JS {
 namespace shadow {
 struct Runtime;
 }
 }
 
 namespace js {
 
+class AutoLockGC;
 class FreeOp;
 
 #ifdef DEBUG
 extern bool
 RuntimeFromMainThreadIsHeapMajorCollecting(JS::shadow::Zone *shadowZone);
 
 // Barriers can't be triggered during backend Ion compilation, which may run on
 // a helper thread.
@@ -940,19 +941,20 @@ struct Chunk
     bool hasAvailableArenas() const {
         return info.numArenasFree != 0;
     }
 
     inline void addToAvailableList(JSRuntime *rt);
     inline void insertToAvailableList(Chunk **insertPoint);
     inline void removeFromAvailableList();
 
-    ArenaHeader *allocateArena(JS::Zone *zone, AllocKind kind);
+    ArenaHeader *allocateArena(JSRuntime *rt, JS::Zone *zone, AllocKind kind,
+                               const AutoLockGC &lock);
 
-    void releaseArena(ArenaHeader *aheader);
+    void releaseArena(JSRuntime *rt, ArenaHeader *aheader, const AutoLockGC &lock);
     void recycleArena(ArenaHeader *aheader, SortedArenaList &dest, AllocKind thingKind,
                       size_t thingsPerArena);
 
     static Chunk *allocate(JSRuntime *rt);
 
     void decommitAllArenas(JSRuntime *rt);
 
     /*
@@ -1142,27 +1144,16 @@ ArenaHeader::setNextAllocDuringSweep(Are
 inline void
 ArenaHeader::unsetAllocDuringSweep()
 {
     MOZ_ASSERT(allocatedDuringIncremental);
     allocatedDuringIncremental = 0;
     auxNextLink = 0;
 }
 
-inline void
-ReleaseArenaList(ArenaHeader *aheader)
-{
-    ArenaHeader *next;
-    for (; aheader; aheader = next) {
-        // Copy aheader->next before releasing.
-        next = aheader->next;
-        aheader->chunk()->releaseArena(aheader);
-    }
-}
-
 static void
 AssertValidColor(const TenuredCell *thing, uint32_t color)
 {
 #ifdef DEBUG
     ArenaHeader *aheader = thing->arenaHeader();
     MOZ_ASSERT(color < aheader->getThingSize() / CellSize);
 #endif
 }
--- a/js/src/irregexp/NativeRegExpMacroAssembler.cpp
+++ b/js/src/irregexp/NativeRegExpMacroAssembler.cpp
@@ -147,17 +147,17 @@ NativeRegExpMacroAssembler::GenerateCode
     frameSize = JS_ROUNDUP(frameSize + masm.framePushed(), ABIStackAlignment) - masm.framePushed();
 
     // Actually emit code to start a new stack frame.
     masm.reserveStack(frameSize);
     masm.checkStackAlignment();
 
     // Check if we have space on the stack.
     Label stack_ok;
-    void *stack_limit = &runtime->mainThread.jitStackLimit;
+    void *stack_limit = runtime->mainThread.addressOfJitStackLimit();
     masm.branchPtr(Assembler::Below, AbsoluteAddress(stack_limit), StackPointer, &stack_ok);
 
     // Exit with an exception. There is not enough space on the stack
     // for our working registers.
     masm.movePtr(ImmWord(RegExpRunStatus_Error), temp0);
     masm.jump(&return_temp0);
 
     masm.bind(&stack_ok);
@@ -497,17 +497,17 @@ NativeRegExpMacroAssembler::AdvanceRegis
 void
 NativeRegExpMacroAssembler::Backtrack()
 {
     JitSpew(SPEW_PREFIX "Backtrack");
 
     // Check for an interrupt.
     Label noInterrupt;
     masm.branch32(Assembler::Equal,
-                  AbsoluteAddress(&runtime->interrupt), Imm32(0),
+                  AbsoluteAddress(runtime->addressOfInterruptUint32()), Imm32(0),
                   &noInterrupt);
     masm.movePtr(ImmWord(RegExpRunStatus_Error), temp0);
     masm.jump(&exit_label_);
     masm.bind(&noInterrupt);
 
     // Pop code location from backtrack stack and jump to location.
     PopBacktrack(temp0);
     masm.jump(temp0);
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -491,17 +491,17 @@ BaselineCompiler::emitIC(ICStub *stub, I
 typedef bool (*CheckOverRecursedWithExtraFn)(JSContext *, BaselineFrame *, uint32_t, uint32_t);
 static const VMFunction CheckOverRecursedWithExtraInfo =
     FunctionInfo<CheckOverRecursedWithExtraFn>(CheckOverRecursedWithExtra);
 
 bool
 BaselineCompiler::emitStackCheck(bool earlyCheck)
 {
     Label skipCall;
-    uintptr_t *limitAddr = &cx->runtime()->mainThread.jitStackLimit;
+    void *limitAddr = cx->runtime()->mainThread.addressOfJitStackLimit();
     uint32_t slotsSize = script->nslots() * sizeof(Value);
     uint32_t tolerance = earlyCheck ? slotsSize : 0;
 
     masm.movePtr(BaselineStackReg, R1.scratchReg());
 
     // If this is the early stack check, locals haven't been pushed yet.  Adjust the
     // stack pointer to account for the locals that would be pushed before performing
     // the guard around the vmcall to the stack check.
@@ -641,17 +641,17 @@ typedef bool (*InterruptCheckFn)(JSConte
 static const VMFunction InterruptCheckInfo = FunctionInfo<InterruptCheckFn>(InterruptCheck);
 
 bool
 BaselineCompiler::emitInterruptCheck()
 {
     frame.syncStack(0);
 
     Label done;
-    void *interrupt = (void *)&cx->runtime()->interrupt;
+    void *interrupt = cx->runtimeAddressOfInterruptUint32();
     masm.branch32(Assembler::Equal, AbsoluteAddress(interrupt), Imm32(0), &done);
 
     prepareVMCall();
     if (!callVM(InterruptCheckInfo))
         return false;
 
     masm.bind(&done);
     return true;
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -3767,17 +3767,17 @@ CodeGenerator::visitCheckOverRecursedPar
     // on interrupt or abort, only the stack limit for the main thread
     // is reset, not the worker threads.  See comment in vm/ForkJoin.h
     // for more details.
 
     Register cxReg = ToRegister(lir->forkJoinContext());
     Register tempReg = ToRegister(lir->getTempReg());
 
     masm.loadPtr(Address(cxReg, offsetof(ForkJoinContext, perThreadData)), tempReg);
-    masm.loadPtr(Address(tempReg, offsetof(PerThreadData, jitStackLimit)), tempReg);
+    masm.loadPtr(Address(tempReg, PerThreadData::offsetOfJitStackLimit()), tempReg);
 
     // Conditional forward (unlikely) branch to failure.
     CheckOverRecursedFailure *ool = new(alloc()) CheckOverRecursedFailure(lir);
     if (!addOutOfLineCode(ool, lir->mir()))
         return false;
 
     masm.branchPtr(Assembler::BelowOrEqual, StackPointer, tempReg, ool->entry());
     masm.checkInterruptFlagPar(tempReg, ool->entry());
@@ -9945,28 +9945,28 @@ CodeGenerator::visitAssertRangeV(LAssert
 
 bool
 CodeGenerator::visitInterruptCheck(LInterruptCheck *lir)
 {
     OutOfLineCode *ool = oolCallVM(InterruptCheckInfo, lir, (ArgList()), StoreNothing());
     if (!ool)
         return false;
 
-    AbsoluteAddress interruptAddr(GetIonContext()->runtime->addressOfInterrupt());
+    AbsoluteAddress interruptAddr(GetIonContext()->runtime->addressOfInterruptUint32());
     masm.branch32(Assembler::NotEqual, interruptAddr, Imm32(0), ool->entry());
     masm.bind(ool->rejoin());
     return true;
 }
 
 bool
 CodeGenerator::visitAsmJSInterruptCheck(LAsmJSInterruptCheck *lir)
 {
     Register scratch = ToRegister(lir->scratch());
-    masm.movePtr(AsmJSImmPtr(AsmJSImm_RuntimeInterrupt), scratch);
-    masm.load8ZeroExtend(Address(scratch, 0), scratch);
+    masm.movePtr(AsmJSImmPtr(AsmJSImm_RuntimeInterruptUint32), scratch);
+    masm.load32(Address(scratch, 0), scratch);
     Label rejoin;
     masm.branch32(Assembler::Equal, scratch, Imm32(0), &rejoin);
     {
         uint32_t stackFixup = ComputeByteAlignment(masm.framePushed() + sizeof(AsmJSFrame),
                                                    ABIStackAlignment);
         masm.reserveStack(stackFixup);
         masm.call(lir->funcDesc(), lir->interruptExit());
         masm.freeStack(stackFixup);
--- a/js/src/jit/CompileWrappers.cpp
+++ b/js/src/jit/CompileWrappers.cpp
@@ -38,17 +38,17 @@ const void *
 CompileRuntime::addressOfJitTop()
 {
     return &runtime()->mainThread.jitTop;
 }
 
 const void *
 CompileRuntime::addressOfJitStackLimit()
 {
-    return &runtime()->mainThread.jitStackLimit;
+    return runtime()->mainThread.addressOfJitStackLimit();
 }
 
 const void *
 CompileRuntime::addressOfJSContext()
 {
     return &runtime()->mainThread.jitJSContext;
 }
 
@@ -68,25 +68,25 @@ CompileRuntime::addressOfLastCachedNativ
 const void *
 CompileRuntime::addressOfGCZeal()
 {
     return runtime()->gc.addressOfZealMode();
 }
 #endif
 
 const void *
-CompileRuntime::addressOfInterrupt()
+CompileRuntime::addressOfInterruptUint32()
 {
-    return &runtime()->interrupt;
+    return runtime()->addressOfInterruptUint32();
 }
 
 const void *
-CompileRuntime::addressOfInterruptPar()
+CompileRuntime::addressOfInterruptParUint32()
 {
-    return &runtime()->interruptPar;
+    return runtime()->addressOfInterruptParUint32();
 }
 
 const void *
 CompileRuntime::addressOfThreadPool()
 {
     return &runtime()->threadPool;
 }
 
--- a/js/src/jit/CompileWrappers.h
+++ b/js/src/jit/CompileWrappers.h
@@ -45,18 +45,18 @@ class CompileRuntime
 
     // &GetIonContext()->runtime->nativeIterCache.last
     const void *addressOfLastCachedNativeIterator();
 
 #ifdef JS_GC_ZEAL
     const void *addressOfGCZeal();
 #endif
 
-    const void *addressOfInterrupt();
-    const void *addressOfInterruptPar();
+    const void *addressOfInterruptUint32();
+    const void *addressOfInterruptParUint32();
 
     const void *addressOfThreadPool();
 
     const JitRuntime *jitRuntime();
 
     // Compilation does not occur off thread when the SPS profiler is enabled.
     SPSProfiler &spsProfiler();
 
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -423,17 +423,17 @@ JitRuntime::ensureIonCodeAccessible(JSRu
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
 #endif
 
     if (ionCodeProtected_) {
         ionAlloc_->toggleAllCodeAsAccessible(true);
         ionCodeProtected_ = false;
     }
 
-    if (rt->interrupt) {
+    if (rt->hasPendingInterrupt()) {
         // The interrupt handler needs to be invoked by this thread, but we may
         // be inside a signal handler and have no idea what is above us on the
         // stack (probably we are executing Ion code at an arbitrary point, but
         // we could be elsewhere, say repatching a jump for an IonCache).
         // Patch all backedges in the runtime so they will invoke the interrupt
         // handler the next time they execute.
         patchIonBackedges(rt, BackedgeInterruptCheck);
     }
@@ -1157,17 +1157,17 @@ IonScript::copyPatchableBackedges(JSCont
         CodeLocationLabel loopHeader(code, CodeOffsetLabel(loopHeaderOffset));
         CodeLocationLabel interruptCheck(code, CodeOffsetLabel(interruptCheckOffset));
         new(patchableBackedge) PatchableBackedge(backedge, loopHeader, interruptCheck);
 
         // Point the backedge to either of its possible targets, according to
         // whether an interrupt is currently desired, matching the targets
         // established by ensureIonCodeAccessible() above. We don't handle the
         // interrupt immediately as the interrupt lock is held here.
-        if (cx->runtime()->interrupt)
+        if (cx->runtime()->hasPendingInterrupt())
             PatchBackedge(backedge, interruptCheck, JitRuntime::BackedgeInterruptCheck);
         else
             PatchBackedge(backedge, loopHeader, JitRuntime::BackedgeLoopHeader);
 
         cx->runtime()->jitRuntime()->addPatchableBackedge(patchableBackedge);
     }
 }
 
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -1233,17 +1233,17 @@ MacroAssembler::loadStringChar(Register 
     load8ZeroExtend(BaseIndex(output, index, TimesOne), output);
 
     bind(&done);
 }
 
 void
 MacroAssembler::checkInterruptFlagPar(Register tempReg, Label *fail)
 {
-    movePtr(ImmPtr(GetIonContext()->runtime->addressOfInterruptPar()), tempReg);
+    movePtr(ImmPtr(GetIonContext()->runtime->addressOfInterruptParUint32()), tempReg);
     branch32(Assembler::NonZero, Address(tempReg, 0), Imm32(0), fail);
 }
 
 // Save an exit frame (which must be aligned to the stack pointer) to
 // PerThreadData::jitTop of the main thread.
 void
 MacroAssembler::linkExitFrame()
 {
--- a/js/src/jit/ParallelFunctions.cpp
+++ b/js/src/jit/ParallelFunctions.cpp
@@ -142,17 +142,17 @@ jit::CheckOverRecursedPar(ForkJoinContex
 
 #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
     if (Simulator::Current()->overRecursed()) {
         cx->bailoutRecord->joinCause(ParallelBailoutOverRecursed);
         return false;
     }
 #endif
 
-    if (!JS_CHECK_STACK_SIZE(cx->perThreadData->jitStackLimit, &stackDummy_)) {
+    if (!JS_CHECK_STACK_SIZE(cx->perThreadData->jitStackLimit(), &stackDummy_)) {
         cx->bailoutRecord->joinCause(ParallelBailoutOverRecursed);
         return false;
     }
 
     return InterruptCheckPar(cx);
 }
 
 bool
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -108,38 +108,27 @@ JSObject *
 NewGCObject(JSContext *cx, gc::AllocKind allocKind, gc::InitialHeap initialHeap)
 {
     return js::NewGCObject<CanGC>(cx, allocKind, 0, initialHeap);
 }
 
 bool
 CheckOverRecursed(JSContext *cx)
 {
-    // IonMonkey's stackLimit is equal to nativeStackLimit by default. When we
-    // request an interrupt, we set the jitStackLimit to nullptr, which causes
-    // the stack limit check to fail.
-    //
-    // There are two states we're concerned about here:
-    //   (1) The interrupt bit is set, and we need to fire the interrupt callback.
-    //   (2) The stack limit has been exceeded, and we need to throw an error.
-    //
-    // Note that we can reach here if jitStackLimit is MAXADDR, but interrupt
-    // has not yet been set to 1. That's okay; it will be set to 1 very shortly,
-    // and in the interim we might just fire a few useless calls to
-    // CheckOverRecursed.
+    // We just failed the jitStackLimit check. There are two possible reasons:
+    //  - jitStackLimit was the real stack limit and we're over-recursed
+    //  - jitStackLimit was set to UINTPTR_MAX by JSRuntime::requestInterrupt
+    //    and we need to call JSRuntime::handleInterrupt.
 #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
     JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, 0, return false);
 #else
     JS_CHECK_RECURSION(cx, return false);
 #endif
-
-    if (cx->runtime()->interrupt)
-        return InterruptCheck(cx);
-
-    return true;
+    gc::MaybeVerifyBarriers(cx);
+    return cx->runtime()->handleInterrupt(cx);
 }
 
 // This function can get called in two contexts.  In the usual context, it's
 // called with ealyCheck=false, after the scope chain has been initialized on
 // a baseline frame.  In this case, it's ok to throw an exception, so a failed
 // stack check returns false, and a successful stack check promps a check for
 // an interrupt from the runtime, which may also cause a false return.
 //
@@ -173,20 +162,18 @@ CheckOverRecursedWithExtra(JSContext *cx
         return false;
 
 #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
     JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, extra, return false);
 #else
     JS_CHECK_RECURSION_WITH_SP(cx, checkSp, return false);
 #endif
 
-    if (cx->runtime()->interrupt)
-        return InterruptCheck(cx);
-
-    return true;
+    gc::MaybeVerifyBarriers(cx);
+    return cx->runtime()->handleInterrupt(cx);
 }
 
 bool
 DefVarOrConst(JSContext *cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain)
 {
     // Given the ScopeChain, extract the VarObj.
     RootedObject obj(cx, scopeChain);
     while (!obj->isQualifiedVarObj())
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -778,17 +778,17 @@ enum AsmJSImmKind
     AsmJSImm_CeilF           = AsmJSExit::Builtin_CeilF,
     AsmJSImm_FloorD          = AsmJSExit::Builtin_FloorD,
     AsmJSImm_FloorF          = AsmJSExit::Builtin_FloorF,
     AsmJSImm_ExpD            = AsmJSExit::Builtin_ExpD,
     AsmJSImm_LogD            = AsmJSExit::Builtin_LogD,
     AsmJSImm_PowD            = AsmJSExit::Builtin_PowD,
     AsmJSImm_ATan2D          = AsmJSExit::Builtin_ATan2D,
     AsmJSImm_Runtime,
-    AsmJSImm_RuntimeInterrupt,
+    AsmJSImm_RuntimeInterruptUint32,
     AsmJSImm_StackLimit,
     AsmJSImm_ReportOverRecursed,
     AsmJSImm_OnDetached,
     AsmJSImm_HandleExecutionInterrupt,
     AsmJSImm_InvokeFromAsmJS_Ignore,
     AsmJSImm_InvokeFromAsmJS_ToInt32,
     AsmJSImm_InvokeFromAsmJS_ToNumber,
     AsmJSImm_CoerceInPlace_ToInt32,
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2026,78 +2026,58 @@ JS_IsExternalString(JSString *str)
 
 extern JS_PUBLIC_API(const JSStringFinalizer *)
 JS_GetExternalStringFinalizer(JSString *str)
 {
     return str->asExternal().externalFinalizer();
 }
 
 static void
-SetNativeStackQuota(JSRuntime *rt, StackKind kind, size_t stackSize)
+SetNativeStackQuotaAndLimit(JSRuntime *rt, StackKind kind, size_t stackSize)
 {
     rt->nativeStackQuota[kind] = stackSize;
-    if (rt->nativeStackBase)
-        RecomputeStackLimit(rt, kind);
-}
-
-void
-js::RecomputeStackLimit(JSRuntime *rt, StackKind kind)
-{
-    size_t stackSize = rt->nativeStackQuota[kind];
+
 #if JS_STACK_GROWTH_DIRECTION > 0
     if (stackSize == 0) {
         rt->mainThread.nativeStackLimit[kind] = UINTPTR_MAX;
     } else {
         MOZ_ASSERT(rt->nativeStackBase <= size_t(-1) - stackSize);
-        rt->mainThread.nativeStackLimit[kind] =
-          rt->nativeStackBase + stackSize - 1;
+        rt->mainThread.nativeStackLimit[kind] = rt->nativeStackBase + stackSize - 1;
     }
 #else
     if (stackSize == 0) {
         rt->mainThread.nativeStackLimit[kind] = 0;
     } else {
         MOZ_ASSERT(rt->nativeStackBase >= stackSize);
-        rt->mainThread.nativeStackLimit[kind] =
-          rt->nativeStackBase - (stackSize - 1);
+        rt->mainThread.nativeStackLimit[kind] = rt->nativeStackBase - (stackSize - 1);
     }
 #endif
-
-    // If there's no pending interrupt request set on the runtime's main thread's
-    // jitStackLimit, then update it so that it reflects the new nativeStacklimit.
-    //
-    // Note that, for now, we use the untrusted limit for ion. This is fine,
-    // because it's the most conservative limit, and if we hit it, we'll bail
-    // out of ion into the interpeter, which will do a proper recursion check.
-    if (kind == StackForUntrustedScript) {
-        JSRuntime::AutoLockForInterrupt lock(rt);
-        if (rt->mainThread.jitStackLimit != uintptr_t(-1)) {
-            rt->mainThread.jitStackLimit = rt->mainThread.nativeStackLimit[kind];
-#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
-            rt->mainThread.jitStackLimit = jit::Simulator::StackLimit();
-#endif
-        }
-    }
 }
 
 JS_PUBLIC_API(void)
-JS_SetNativeStackQuota(JSRuntime *rt, size_t systemCodeStackSize,
-                       size_t trustedScriptStackSize,
+JS_SetNativeStackQuota(JSRuntime *rt, size_t systemCodeStackSize, size_t trustedScriptStackSize,
                        size_t untrustedScriptStackSize)
 {
-    MOZ_ASSERT_IF(trustedScriptStackSize,
-                  trustedScriptStackSize < systemCodeStackSize);
+    MOZ_ASSERT(rt->requestDepth == 0);
+
     if (!trustedScriptStackSize)
         trustedScriptStackSize = systemCodeStackSize;
-    MOZ_ASSERT_IF(untrustedScriptStackSize,
-                  untrustedScriptStackSize < trustedScriptStackSize);
+    else
+        MOZ_ASSERT(trustedScriptStackSize < systemCodeStackSize);
+
     if (!untrustedScriptStackSize)
         untrustedScriptStackSize = trustedScriptStackSize;
-    SetNativeStackQuota(rt, StackForSystemCode, systemCodeStackSize);
-    SetNativeStackQuota(rt, StackForTrustedScript, trustedScriptStackSize);
-    SetNativeStackQuota(rt, StackForUntrustedScript, untrustedScriptStackSize);
+    else
+        MOZ_ASSERT(untrustedScriptStackSize < trustedScriptStackSize);
+
+    SetNativeStackQuotaAndLimit(rt, StackForSystemCode, systemCodeStackSize);
+    SetNativeStackQuotaAndLimit(rt, StackForTrustedScript, trustedScriptStackSize);
+    SetNativeStackQuotaAndLimit(rt, StackForUntrustedScript, untrustedScriptStackSize);
+
+    rt->mainThread.initJitStackLimit();
 }
 
 /************************************************************************/
 
 JS_PUBLIC_API(int)
 JS_IdArrayLength(JSContext *cx, JSIdArray *ida)
 {
     return ida->length;
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2267,16 +2267,19 @@ JS_GetExternalStringFinalizer(JSString *
  * behalf of such script), trusted script (as determined by JS_SetTrustedPrincipals),
  * and untrusted script. Each kind of code may have a different stack quota,
  * allowing embedders to keep higher-priority machinery running in the face of
  * scripted stack exhaustion by something else.
  *
  * The stack quotas for each kind of code should be monotonically descending,
  * and may be specified with this function. If 0 is passed for a given kind
  * of code, it defaults to the value of the next-highest-priority kind.
+ *
+ * This function may only be called immediately after the runtime is initialized
+ * and before any code is executed and/or interrupts requested.
  */
 extern JS_PUBLIC_API(void)
 JS_SetNativeStackQuota(JSRuntime *cx, size_t systemCodeStackSize,
                        size_t trustedScriptStackSize = 0,
                        size_t untrustedScriptStackSize = 0);
 
 /************************************************************************/
 
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -36,17 +36,16 @@
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jstypes.h"
 #include "jswatchpoint.h"
 
 #include "gc/Marking.h"
 #include "jit/Ion.h"
 #include "js/CharacterEncoding.h"
-#include "vm/Debugger.h"
 #include "vm/HelperThreads.h"
 #include "vm/Shape.h"
 
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/Stack-inl.h"
 
@@ -966,100 +965,16 @@ const JSErrorFormatString js_ErrorFormat
 JS_FRIEND_API(const JSErrorFormatString *)
 js_GetErrorMessage(void *userRef, const unsigned errorNumber)
 {
     if (errorNumber > 0 && errorNumber < JSErr_Limit)
         return &js_ErrorFormatString[errorNumber];
     return nullptr;
 }
 
-bool
-js::InvokeInterruptCallback(JSContext *cx)
-{
-    MOZ_ASSERT(cx->runtime()->requestDepth >= 1);
-
-    JSRuntime *rt = cx->runtime();
-    MOZ_ASSERT(rt->interrupt);
-
-    // Reset the callback counter first, then run GC and yield. If another
-    // thread is racing us here we will accumulate another callback request
-    // which will be serviced at the next opportunity.
-    rt->interrupt = false;
-
-    // IonMonkey sets its stack limit to UINTPTR_MAX to trigger interrupt
-    // callbacks.
-    rt->resetJitStackLimit();
-
-    cx->gcIfNeeded();
-
-    rt->interruptPar = false;
-
-    // A worker thread may have requested an interrupt after finishing an Ion
-    // compilation.
-    jit::AttachFinishedCompilations(cx);
-
-    // Important: Additional callbacks can occur inside the callback handler
-    // if it re-enters the JS engine. The embedding must ensure that the
-    // callback is disconnected before attempting such re-entry.
-    JSInterruptCallback cb = cx->runtime()->interruptCallback;
-    if (!cb)
-        return true;
-
-    if (cb(cx)) {
-        // Debugger treats invoking the interrupt callback as a "step", so
-        // invoke the onStep handler.
-        if (cx->compartment()->debugMode()) {
-            ScriptFrameIter iter(cx);
-            if (iter.script()->stepModeEnabled()) {
-                RootedValue rval(cx);
-                switch (Debugger::onSingleStep(cx, &rval)) {
-                  case JSTRAP_ERROR:
-                    return false;
-                  case JSTRAP_CONTINUE:
-                    return true;
-                  case JSTRAP_RETURN:
-                    // See note in Debugger::propagateForcedReturn.
-                    Debugger::propagateForcedReturn(cx, iter.abstractFramePtr(), rval);
-                    return false;
-                  case JSTRAP_THROW:
-                    cx->setPendingException(rval);
-                    return false;
-                  default:;
-                }
-            }
-        }
-
-        return true;
-    }
-
-    // No need to set aside any pending exception here: ComputeStackString
-    // already does that.
-    JSString *stack = ComputeStackString(cx);
-    JSFlatString *flat = stack ? stack->ensureFlat(cx) : nullptr;
-
-    const char16_t *chars;
-    AutoStableStringChars stableChars(cx);
-    if (flat && stableChars.initTwoByte(cx, flat))
-        chars = stableChars.twoByteRange().start().get();
-    else
-        chars = MOZ_UTF16("(stack not available)");
-    JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_WARNING, js_GetErrorMessage, nullptr,
-                                   JSMSG_TERMINATED, chars);
-
-    return false;
-}
-
-bool
-js::HandleExecutionInterrupt(JSContext *cx)
-{
-    if (cx->runtime()->interrupt)
-        return InvokeInterruptCallback(cx);
-    return true;
-}
-
 ThreadSafeContext::ThreadSafeContext(JSRuntime *rt, PerThreadData *pt, ContextKind kind)
   : ContextFriendFields(rt),
     contextKind_(kind),
     perThreadData(pt),
     allocator_(nullptr)
 {
 }
 
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -284,17 +284,17 @@ struct ThreadSafeContext : ContextFriend
     JSAtomState &names() { return *runtime_->commonNames; }
     StaticStrings &staticStrings() { return *runtime_->staticStrings; }
     AtomSet &permanentAtoms() { return *runtime_->permanentAtoms; }
     WellKnownSymbols &wellKnownSymbols() { return *runtime_->wellKnownSymbols; }
     const JS::AsmJSCacheOps &asmJSCacheOps() { return runtime_->asmJSCacheOps; }
     PropertyName *emptyString() { return runtime_->emptyString; }
     FreeOp *defaultFreeOp() { return runtime_->defaultFreeOp(); }
     void *runtimeAddressForJit() { return runtime_; }
-    void *runtimeAddressOfInterrupt() { return &runtime_->interrupt; }
+    void *runtimeAddressOfInterruptUint32() { return runtime_->addressOfInterruptUint32(); }
     void *stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; }
     void *stackLimitAddressForJitCode(StackKind kind);
     size_t gcSystemPageSize() { return gc::SystemPageSize(); }
     bool signalHandlersInstalled() const { return runtime_->signalHandlersInstalled(); }
     bool canUseSignalHandlers() const { return runtime_->canUseSignalHandlers(); }
     bool jitSupportsFloatingPoint() const { return runtime_->jitSupportsFloatingPoint; }
     bool jitSupportsSimd() const { return runtime_->jitSupportsSimd; }
 
@@ -777,43 +777,25 @@ js_ReportValueErrorFlags(JSContext *cx, 
 #define js_ReportValueError3(cx,errorNumber,spindex,v,fallback,arg1,arg2)     \
     ((void)js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber,          \
                                     spindex, v, fallback, arg1, arg2))
 
 extern const JSErrorFormatString js_ErrorFormatString[JSErr_Limit];
 
 namespace js {
 
-/*
- * Invoke the interrupt callback and return false if the current execution
- * is to be terminated.
- */
-bool
-InvokeInterruptCallback(JSContext *cx);
-
-bool
-HandleExecutionInterrupt(JSContext *cx);
-
-/*
- * Process any pending interrupt requests. Long-running inner loops in C++ must
- * call this periodically to make sure they are interruptible --- that is, to
- * make sure they do not prevent the slow script dialog from appearing.
- *
- * This can run a full GC or call the interrupt callback, which could do
- * anything. In the browser, it displays the slow script dialog.
- *
- * If this returns true, the caller can continue; if false, the caller must
- * break out of its loop. This happens if, for example, the user clicks "Stop
- * script" on the slow script dialog; treat it as an uncatchable error.
- */
 MOZ_ALWAYS_INLINE bool
 CheckForInterrupt(JSContext *cx)
 {
-    MOZ_ASSERT(cx->runtime()->requestDepth >= 1);
-    return !cx->runtime()->interrupt || InvokeInterruptCallback(cx);
+    // Add an inline fast-path since we have to check for interrupts in some hot
+    // C++ loops of library builtins.
+    JSRuntime *rt = cx->runtime();
+    if (rt->hasPendingInterrupt())
+        return rt->handleInterrupt(cx);
+    return true;
 }
 
 /************************************************************************/
 
 class AutoStringVector : public AutoVectorRooter<JSString *>
 {
   public:
     explicit AutoStringVector(JSContext *cx
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -540,52 +540,60 @@ Arena::finalize(FreeOp *fop, AllocKind t
     for (const FreeSpan *span = &newListHead; !span->isEmpty(); span = span->nextSpan())
         nfree += span->length(thingSize);
     MOZ_ASSERT(nfree + nmarked == thingsPerArena(thingSize));
 #endif
     aheader.setFirstFreeSpan(&newListHead);
     return nmarked;
 }
 
+// Finalize arenas from src list, releasing empty arenas if keepArenas wasn't
+// specified and inserting the others into the appropriate destination size
+// bins.
 template<typename T>
 static inline bool
 FinalizeTypedArenas(FreeOp *fop,
                     ArenaHeader **src,
                     SortedArenaList &dest,
                     AllocKind thingKind,
                     SliceBudget &budget,
                     ArenaLists::KeepArenasEnum keepArenas)
 {
-    /*
-     * Finalize arenas from src list, releasing empty arenas if keepArenas
-     * wasn't specified and inserting the others into the appropriate
-     * destination size bins.
-     */
+    // When operating in the foreground, take the lock at the top.
+    Maybe<AutoLockGC> maybeLock;
+    if (!fop->runtime()->gc.isBackgroundSweeping())
+        maybeLock.emplace(fop->runtime());
 
     /*
      * During parallel sections, we sometimes finalize the parallel arenas,
      * but in that case, we want to hold on to the memory in our arena
      * lists, not offer it up for reuse.
      */
     MOZ_ASSERT_IF(InParallelSection(), keepArenas);
 
     size_t thingSize = Arena::thingSize(thingKind);
     size_t thingsPerArena = Arena::thingsPerArena(thingSize);
 
     while (ArenaHeader *aheader = *src) {
         *src = aheader->next;
         size_t nmarked = aheader->getArena()->finalize<T>(fop, thingKind, thingSize);
         size_t nfree = thingsPerArena - nmarked;
 
-        if (nmarked)
+        if (nmarked) {
             dest.insertAt(aheader, nfree);
-        else if (keepArenas)
+        } else if (keepArenas) {
             aheader->chunk()->recycleArena(aheader, dest, thingKind, thingsPerArena);
-        else
-            aheader->chunk()->releaseArena(aheader);
+        } else if (fop->runtime()->gc.isBackgroundSweeping()) {
+            // When background sweeping, take the lock around each release so
+            // that we do not block the foreground for extended periods.
+            AutoLockGC lock(fop->runtime());
+            fop->runtime()->gc.releaseArena(aheader, lock);
+        } else {
+            fop->runtime()->gc.releaseArena(aheader, maybeLock.ref());
+        }
 
         budget.step(thingsPerArena);
         if (budget.isOverBudget())
             return false;
     }
 
     return true;
 }
@@ -933,77 +941,24 @@ Chunk::fetchNextFreeArena(JSRuntime *rt)
     --info.numArenasFreeCommitted;
     --info.numArenasFree;
     rt->gc.updateOnFreeArenaAlloc(info);
 
     return aheader;
 }
 
 ArenaHeader *
-Chunk::allocateArena(Zone *zone, AllocKind thingKind)
-{
-    MOZ_ASSERT(hasAvailableArenas());
-
-    JSRuntime *rt = zone->runtimeFromAnyThread();
-    if (!rt->isHeapMinorCollecting() &&
-        !rt->isHeapCompacting() &&
-        rt->gc.usage.gcBytes() >= rt->gc.tunables.gcMaxBytes())
-    {
-#ifdef JSGC_FJGENERATIONAL
-        // This is an approximation to the best test, which would check that
-        // this thread is currently promoting into the tenured area.  I doubt
-        // the better test would make much difference.
-        if (!rt->isFJMinorCollecting())
-            return nullptr;
-#else
-        return nullptr;
-#endif
-    }
-
-    ArenaHeader *aheader = MOZ_LIKELY(info.numArenasFreeCommitted > 0)
+Chunk::allocateArena(JSRuntime *rt, Zone *zone, AllocKind thingKind, const AutoLockGC &lock)
+{
+    ArenaHeader *aheader = info.numArenasFreeCommitted > 0
                            ? fetchNextFreeArena(rt)
                            : fetchNextDecommittedArena();
     aheader->init(zone, thingKind);
     if (MOZ_UNLIKELY(!hasAvailableArenas()))
         removeFromAvailableList();
-
-    zone->usage.addGCArena();
-
-    if (!rt->isHeapCompacting()) {
-        size_t usedBytes = zone->usage.gcBytes();
-        size_t thresholdBytes = zone->threshold.gcTriggerBytes();
-        size_t igcThresholdBytes = thresholdBytes * rt->gc.tunables.zoneAllocThresholdFactor();
-
-        if (usedBytes >= thresholdBytes) {
-            // The threshold has been surpassed, immediately trigger a GC,
-            // which will be done non-incrementally.
-            AutoUnlockGC unlock(rt);
-            rt->gc.triggerZoneGC(zone, JS::gcreason::ALLOC_TRIGGER);
-        } else if (usedBytes >= igcThresholdBytes) {
-            // Reduce the delay to the start of the next incremental slice.
-            if (zone->gcDelayBytes < ArenaSize)
-                zone->gcDelayBytes = 0;
-            else
-                zone->gcDelayBytes -= ArenaSize;
-
-            if (!zone->gcDelayBytes) {
-                // Start or continue an in progress incremental GC. We do this
-                // to try to avoid performing non-incremental GCs on zones
-                // which allocate a lot of data, even when incremental slices
-                // can't be triggered via scheduling in the event loop.
-                AutoUnlockGC unlock(rt);
-                rt->gc.triggerZoneGC(zone, JS::gcreason::ALLOC_TRIGGER);
-
-                // Delay the next slice until a certain amount of allocation
-                // has been performed.
-                zone->gcDelayBytes = rt->gc.tunables.zoneAllocDelayBytes();
-            }
-        }
-    }
-
     return aheader;
 }
 
 inline void
 GCRuntime::updateOnArenaFree(const ChunkInfo &info)
 {
     ++numArenasFreeCommitted;
 }
@@ -1023,47 +978,35 @@ void
 Chunk::recycleArena(ArenaHeader *aheader, SortedArenaList &dest, AllocKind thingKind,
                     size_t thingsPerArena)
 {
     aheader->getArena()->setAsFullyUnused(thingKind);
     dest.insertAt(aheader, thingsPerArena);
 }
 
 void
-Chunk::releaseArena(ArenaHeader *aheader)
+Chunk::releaseArena(JSRuntime *rt, ArenaHeader *aheader, const AutoLockGC &lock)
 {
     MOZ_ASSERT(aheader->allocated());
     MOZ_ASSERT(!aheader->hasDelayedMarking);
-    Zone *zone = aheader->zone;
-    JSRuntime *rt = zone->runtimeFromAnyThread();
-
-    Maybe<AutoLockGC> maybeLock;
-    if (rt->gc.isBackgroundSweeping())
-        maybeLock.emplace(rt);
-
-    if (rt->gc.isBackgroundSweeping())
-        zone->threshold.updateForRemovedArena(rt->gc.tunables);
-    zone->usage.removeGCArena();
 
     aheader->setAsNotAllocated();
     addArenaToFreeList(rt, aheader);
 
     if (info.numArenasFree == 1) {
         MOZ_ASSERT(!info.prevp);
         MOZ_ASSERT(!info.next);
         addToAvailableList(rt);
     } else if (!unused()) {
         MOZ_ASSERT(info.prevp);
     } else {
-        if (maybeLock.isNothing())
-            maybeLock.emplace(rt);
         MOZ_ASSERT(unused());
         removeFromAvailableList();
         decommitAllArenas(rt);
-        rt->gc.moveChunkToFreePool(this, maybeLock.ref());
+        rt->gc.moveChunkToFreePool(this, lock);
     }
 }
 
 void
 GCRuntime::moveChunkToFreePool(Chunk *chunk, const AutoLockGC &lock)
 {
     MOZ_ASSERT(chunk->unused());
     MOZ_ASSERT(chunkSet.has(chunk));
@@ -1156,16 +1099,56 @@ GCRuntime::pickChunk(const AutoLockGC &l
 
     chunk->info.prevp = nullptr;
     chunk->info.next = nullptr;
     chunk->addToAvailableList(rt);
 
     return chunk;
 }
 
+ArenaHeader *
+GCRuntime::allocateArena(Chunk *chunk, Zone *zone, AllocKind thingKind, const AutoLockGC &lock)
+{
+    MOZ_ASSERT(chunk->hasAvailableArenas());
+
+    // Fail the allocation if we are over our heap size limits.
+    if (!isHeapMinorCollecting() &&
+        !isHeapCompacting() &&
+        usage.gcBytes() >= tunables.gcMaxBytes())
+    {
+#ifdef JSGC_FJGENERATIONAL
+        // This is an approximation to the best test, which would check that
+        // this thread is currently promoting into the tenured area.  I doubt
+        // the better test would make much difference.
+        if (!isFJMinorCollecting())
+            return nullptr;
+#else
+        return nullptr;
+#endif
+    }
+
+    ArenaHeader *aheader = chunk->allocateArena(rt, zone, thingKind, lock);
+    zone->usage.addGCArena();
+
+    // Trigger an incremental slice if needed.
+    if (!isHeapMinorCollecting() && !isHeapCompacting())
+        maybeAllocTriggerZoneGC(zone, lock);
+
+    return aheader;
+}
+
+void
+GCRuntime::releaseArena(ArenaHeader *aheader, const AutoLockGC &lock)
+{
+    aheader->zone->usage.removeGCArena();
+    if (isBackgroundSweeping())
+        aheader->zone->threshold.updateForRemovedArena(tunables);
+    return aheader->chunk()->releaseArena(rt, aheader, lock);
+}
+
 GCRuntime::GCRuntime(JSRuntime *rt) :
     rt(rt),
     systemZone(nullptr),
 #ifdef JSGC_GENERATIONAL
     nursery(rt),
     storeBuffer(rt, nursery),
 #endif
     stats(rt),
@@ -1901,17 +1884,18 @@ ZoneHeapThreshold::updateForRemovedArena
 
     if (gcTriggerBytes_ - amount < tunables.gcZoneAllocThresholdBase() * gcHeapGrowthFactor_)
         return;
 
     gcTriggerBytes_ -= amount;
 }
 
 Allocator::Allocator(Zone *zone)
-  : zone_(zone)
+  : arenas(zone->runtimeFromMainThread()),
+    zone_(zone)
 {}
 
 inline void
 GCMarker::delayMarkingArena(ArenaHeader *aheader)
 {
     if (aheader->hasDelayedMarking) {
         /* Arena already scheduled to be marked later */
         return;
@@ -1987,17 +1971,17 @@ ArenaLists::allocateFromArena(JS::Zone *
         maybeLock.emplace(rt);
 
     Chunk *chunk = rt->gc.pickChunk(maybeLock.ref(), maybeStartBGAlloc);
     if (!chunk)
         return nullptr;
 
     // Although our chunk should definitely have enough space for another arena,
     // there are other valid reasons why Chunk::allocateArena() may fail.
-    aheader = chunk->allocateArena(zone, thingKind);
+    aheader = rt->gc.allocateArena(chunk, zone, thingKind, maybeLock.ref());
     if (!aheader)
         return nullptr;
 
     MOZ_ASSERT(al.isCursorAtEnd());
     al.insertAtCursor(aheader);
 
     return allocateFromArenaInner<IsEmpty>(zone, aheader, thingKind);
 }
@@ -2548,16 +2532,17 @@ GCRuntime::unprotectRelocatedArenas(Aren
     }
 }
 #endif
 
 void
 GCRuntime::releaseRelocatedArenas(ArenaHeader *relocatedList)
 {
     // Release the relocated arenas, now containing only forwarding pointers
+    AutoLockGC lock(rt);
 
     unsigned count = 0;
     while (relocatedList) {
         ArenaHeader *aheader = relocatedList;
         relocatedList = relocatedList->next;
 
         // Clear the mark bits
         aheader->unmarkAll();
@@ -2570,27 +2555,55 @@ GCRuntime::releaseRelocatedArenas(ArenaH
         fullSpan.initFinal(arena->thingsStart(thingKind), arena->thingsEnd() - thingSize, thingSize);
         aheader->setFirstFreeSpan(&fullSpan);
 
 #if defined(JS_CRASH_DIAGNOSTICS) || defined(JS_GC_ZEAL)
         JS_POISON(reinterpret_cast<void *>(arena->thingsStart(thingKind)),
                   JS_MOVED_TENURED_PATTERN, Arena::thingsSpan(thingSize));
 #endif
 
-        aheader->chunk()->releaseArena(aheader);
+        releaseArena(aheader, lock);
         ++count;
     }
 
-    AutoLockGC lock(rt);
     expireChunksAndArenas(true, lock);
 }
 
 #endif // JSGC_COMPACTING
 
 void
+ReleaseArenaList(JSRuntime *rt, ArenaHeader *aheader, const AutoLockGC &lock)
+{
+    ArenaHeader *next;
+    for (; aheader; aheader = next) {
+        next = aheader->next;
+        rt->gc.releaseArena(aheader, lock);
+    }
+}
+
+ArenaLists::~ArenaLists()
+{
+    AutoLockGC lock(runtime_);
+
+    for (size_t i = 0; i != FINALIZE_LIMIT; ++i) {
+        /*
+         * We can only call this during the shutdown after the last GC when
+         * the background finalization is disabled.
+         */
+        MOZ_ASSERT(backgroundFinalizeState[i] == BFS_DONE);
+        ReleaseArenaList(runtime_, arenaLists[i].head(), lock);
+    }
+    ReleaseArenaList(runtime_, incrementalSweptArenas.head(), lock);
+
+    for (size_t i = 0; i < FINALIZE_OBJECT_LIMIT; i++)
+        ReleaseArenaList(runtime_, savedObjectArenas[i].head(), lock);
+    ReleaseArenaList(runtime_, savedEmptyObjectArenas, lock);
+}
+
+void
 ArenaLists::finalizeNow(FreeOp *fop, const FinalizePhase& phase)
 {
     gcstats::AutoPhase ap(fop->runtime()->gc.stats, phase.statsPhase);
     for (unsigned i = 0; i < phase.length; ++i)
         finalizeNow(fop, phase.kinds[i], RELEASE_ARENAS, nullptr);
 }
 
 void
@@ -2746,17 +2759,18 @@ ArenaLists::queueForegroundObjectsForSwe
     savedObjectArenas[FINALIZE_OBJECT8] = arenaLists[FINALIZE_OBJECT8].copyAndClear();
     savedObjectArenas[FINALIZE_OBJECT12] = arenaLists[FINALIZE_OBJECT12].copyAndClear();
     savedObjectArenas[FINALIZE_OBJECT16] = arenaLists[FINALIZE_OBJECT16].copyAndClear();
 }
 
 void
 ArenaLists::mergeForegroundSweptObjectArenas()
 {
-    ReleaseArenaList(savedEmptyObjectArenas);
+    AutoLockGC lock(runtime_);
+    ReleaseArenaList(runtime_, savedEmptyObjectArenas, lock);
     savedEmptyObjectArenas = nullptr;
 
     mergeSweptArenas(FINALIZE_OBJECT0);
     mergeSweptArenas(FINALIZE_OBJECT2);
     mergeSweptArenas(FINALIZE_OBJECT4);
     mergeSweptArenas(FINALIZE_OBJECT8);
     mergeSweptArenas(FINALIZE_OBJECT12);
     mergeSweptArenas(FINALIZE_OBJECT16);
@@ -3018,16 +3032,50 @@ GCRuntime::triggerGC(JS::gcreason::Reaso
     if (rt->isHeapCollecting())
         return false;
 
     JS::PrepareForFullGC(rt);
     requestMajorGC(reason);
     return true;
 }
 
+void
+GCRuntime::maybeAllocTriggerZoneGC(Zone *zone, const AutoLockGC &lock)
+{
+    size_t usedBytes = zone->usage.gcBytes();
+    size_t thresholdBytes = zone->threshold.gcTriggerBytes();
+    size_t igcThresholdBytes = thresholdBytes * tunables.zoneAllocThresholdFactor();
+
+    if (usedBytes >= thresholdBytes) {
+        // The threshold has been surpassed, immediately trigger a GC,
+        // which will be done non-incrementally.
+        AutoUnlockGC unlock(rt);
+        triggerZoneGC(zone, JS::gcreason::ALLOC_TRIGGER);
+    } else if (usedBytes >= igcThresholdBytes) {
+        // Reduce the delay to the start of the next incremental slice.
+        if (zone->gcDelayBytes < ArenaSize)
+            zone->gcDelayBytes = 0;
+        else
+            zone->gcDelayBytes -= ArenaSize;
+
+        if (!zone->gcDelayBytes) {
+            // Start or continue an in progress incremental GC. We do this
+            // to try to avoid performing non-incremental GCs on zones
+            // which allocate a lot of data, even when incremental slices
+            // can't be triggered via scheduling in the event loop.
+            AutoUnlockGC unlock(rt);
+            triggerZoneGC(zone, JS::gcreason::ALLOC_TRIGGER);
+
+            // Delay the next slice until a certain amount of allocation
+            // has been performed.
+            zone->gcDelayBytes = tunables.zoneAllocDelayBytes();
+        }
+    }
+}
+
 bool
 GCRuntime::triggerZoneGC(Zone *zone, JS::gcreason::Reason reason)
 {
     /*
      * If parallel threads are running, wait till they
      * are stopped to trigger GC.
      */
     if (InParallelSection()) {
@@ -3118,18 +3166,19 @@ GCRuntime::maybePeriodicFullGC()
         } else {
             nextFullGCTime = now + GC_IDLE_FULL_SPAN;
         }
     }
 #endif
 }
 
 void
-GCRuntime::decommitArenasFromAvailableList(Chunk **availableListHeadp)
-{
+GCRuntime::decommitArenas(const AutoLockGC &lock)
+{
+    Chunk **availableListHeadp = &availableChunkListHead;
     Chunk *chunk = *availableListHeadp;
     if (!chunk)
         return;
 
     /*
      * Decommit is expensive so we avoid holding the GC lock while calling it.
      *
      * We decommit from the tail of the list to minimize interference with the
@@ -3227,35 +3276,29 @@ GCRuntime::decommitArenasFromAvailableLi
          * prevp exists and is not the list head. It must point to the next
          * field of the previous chunk.
          */
         chunk = chunk->getPrevious();
     }
 }
 
 void
-GCRuntime::decommitArenas()
-{
-    decommitArenasFromAvailableList(&availableChunkListHead);
-}
-
-void
 GCRuntime::expireChunksAndArenas(bool shouldShrink, const AutoLockGC &lock)
 {
 #ifdef JSGC_FJGENERATIONAL
     rt->threadPool.pruneChunkCache();
 #endif
 
     if (Chunk *toFree = expireEmptyChunkPool(shouldShrink, lock)) {
         AutoUnlockGC unlock(rt);
         freeChunkList(toFree);
     }
 
     if (shouldShrink)
-        decommitArenas();
+        decommitArenas(lock);
 }
 
 void
 GCRuntime::sweepBackgroundThings()
 {
     /*
      * We must finalize in the correct order, see comments in
      * finalizeObjects.
@@ -6615,17 +6658,17 @@ ArenaLists::adoptArenas(JSRuntime *rt, A
             // Copy fromHeader->next before releasing/reinserting.
             next = fromHeader->next;
 
             // During parallel execution, we sometimes keep empty arenas
             // on the lists rather than sending them back to the chunk.
             // Therefore, if fromHeader is empty, send it back to the
             // chunk now. Otherwise, attach to |toList|.
             if (fromHeader->isEmpty())
-                fromHeader->chunk()->releaseArena(fromHeader);
+                rt->gc.releaseArena(fromHeader, lock);
             else
                 toList->insertAtCursor(fromHeader);
         }
         fromList->clear();
         toList->check();
     }
 }
 
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -590,16 +590,18 @@ class SortedArenaList
         // Create an ArenaList with head and cursor set to the head and tail of
         // the first segment (if that segment is empty, only the head is used).
         return ArenaList(segments[0]);
     }
 };
 
 class ArenaLists
 {
+    JSRuntime *runtime_;
+
     /*
      * For each arena kind its free list is represented as the first span with
      * free things. Initially all the spans are initialized as empty. After we
      * find a new arena with available things we move its first free span into
      * the list and set the arena as fully allocated. way we do not need to
      * update the arena header after the initial allocation. When starting the
      * GC we only move the head of the of the list of spans back to the arena
      * only for the arena that was not fully allocated.
@@ -634,46 +636,32 @@ class ArenaLists
     // While sweeping type information, these lists save the arenas for the
     // objects which have already been finalized in the foreground (which must
     // happen at the beginning of the GC), so that type sweeping can determine
     // which of the object pointers are marked.
     ArenaList savedObjectArenas[FINALIZE_OBJECT_LIMIT];
     ArenaHeader *savedEmptyObjectArenas;
 
   public:
-    ArenaLists() {
+    ArenaLists(JSRuntime *rt) : runtime_(rt) {
         for (size_t i = 0; i != FINALIZE_LIMIT; ++i)
             freeLists[i].initAsEmpty();
         for (size_t i = 0; i != FINALIZE_LIMIT; ++i)
             backgroundFinalizeState[i] = BFS_DONE;
         for (size_t i = 0; i != FINALIZE_LIMIT; ++i)
             arenaListsToSweep[i] = nullptr;
         incrementalSweptArenaKind = FINALIZE_LIMIT;
         gcShapeArenasToUpdate = nullptr;
         gcAccessorShapeArenasToUpdate = nullptr;
         gcScriptArenasToUpdate = nullptr;
         gcTypeObjectArenasToUpdate = nullptr;
         savedEmptyObjectArenas = nullptr;
     }
 
-    ~ArenaLists() {
-        for (size_t i = 0; i != FINALIZE_LIMIT; ++i) {
-            /*
-             * We can only call this during the shutdown after the last GC when
-             * the background finalization is disabled.
-             */
-            MOZ_ASSERT(backgroundFinalizeState[i] == BFS_DONE);
-            ReleaseArenaList(arenaLists[i].head());
-        }
-        ReleaseArenaList(incrementalSweptArenas.head());
-
-        for (size_t i = 0; i < FINALIZE_OBJECT_LIMIT; i++)
-            ReleaseArenaList(savedObjectArenas[i].head());
-        ReleaseArenaList(savedEmptyObjectArenas);
-    }
+    ~ArenaLists();
 
     static uintptr_t getFreeListOffset(AllocKind thingKind) {
         uintptr_t offset = offsetof(ArenaLists, freeLists);
         return offset + thingKind * sizeof(FreeList);
     }
 
     const FreeList *getFreeList(AllocKind thingKind) const {
         return &freeLists[thingKind];
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -521,17 +521,17 @@ CheckAllocatorState(ThreadSafeContext *c
     }
 
     if (allowGC) {
 #ifdef JS_GC_ZEAL
         if (rt->gc.needZealousGC())
             rt->gc.runDebugGC();
 #endif
 
-        if (rt->interrupt) {
+        if (rt->hasPendingInterrupt()) {
             // Invoking the interrupt callback can fail and we can't usefully
             // handle that here. Just check in case we need to collect instead.
             ncx->gcIfNeeded();
         }
     }
 
     return true;
 }
--- a/js/src/jsnativestack.h
+++ b/js/src/jsnativestack.h
@@ -13,15 +13,16 @@ namespace js {
 
 extern void *
 GetNativeStackBaseImpl();
 
 inline uintptr_t
 GetNativeStackBase()
 {
     uintptr_t stackBase = reinterpret_cast<uintptr_t>(GetNativeStackBaseImpl());
+    MOZ_ASSERT(stackBase != 0);
     MOZ_ASSERT(stackBase % sizeof(void *) == 0);
     return stackBase;
 }
 
 } /* namespace js */
 
 #endif /* jsnativestack_h */
--- a/js/src/vm/ForkJoin.cpp
+++ b/js/src/vm/ForkJoin.cpp
@@ -1435,17 +1435,17 @@ ForkJoinShared::~ForkJoinShared()
 }
 
 ParallelResult
 ForkJoinShared::execute()
 {
     // Sometimes a GC request occurs *just before* we enter into the
     // parallel section.  Rather than enter into the parallel section
     // and then abort, we just check here and abort early.
-    if (cx_->runtime()->interruptPar)
+    if (cx_->runtime()->hasPendingInterruptPar())
         return TP_RETRY_SEQUENTIALLY;
 
     AutoLockMonitor lock(*this);
 
     ParallelResult jobResult = TP_SUCCESS;
     {
         AutoUnlockMonitor unlock(*this);
 
@@ -1513,17 +1513,17 @@ ForkJoinShared::executeFromWorker(Thread
     TlsPerThreadData.set(&thisThread);
 
 #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
     stackLimit = Simulator::StackLimit();
 #endif
 
     // Don't use setIonStackLimit() because that acquires the ionStackLimitLock, and the
     // lock has not been initialized in these cases.
-    thisThread.jitStackLimit = stackLimit;
+    thisThread.initJitStackLimitPar(stackLimit);
     executePortion(&thisThread, worker);
     TlsPerThreadData.set(nullptr);
 
     return !abort_;
 }
 
 bool
 ForkJoinShared::executeFromMainThread(ThreadPoolWorker *worker)
@@ -1546,17 +1546,17 @@ ForkJoinShared::executeFromMainThread(Th
     // In turn, the reason that it is okay for runtime->interrupt to be
     // set and for us to still continue PJS execution is because PJS, being
     // unable to use the signal-based interrupt handling like sequential JIT
     // code, keeps a separate flag, interruptPar, to filter out interrupts
     // which should not interrupt JIT code.
     //
     // Thus, use GetNativeStackLimit instead of just propagating the
     // main thread's.
-    thisThread.jitStackLimit = GetNativeStackLimit(cx_);
+    thisThread.initJitStackLimitPar(GetNativeStackLimit(cx_));
     executePortion(&thisThread, worker);
     TlsPerThreadData.set(oldData);
 
     return !abort_;
 }
 
 void
 ForkJoinShared::executePortion(PerThreadData *perThread, ThreadPoolWorker *worker)
@@ -1642,17 +1642,17 @@ ForkJoinShared::executePortion(PerThread
     }
 
     Spew(SpewOps, "Down");
 }
 
 void
 ForkJoinShared::setAbortFlagDueToInterrupt(ForkJoinContext &cx)
 {
-    MOZ_ASSERT(cx_->runtime()->interruptPar);
+    MOZ_ASSERT(cx_->runtime()->hasPendingInterruptPar());
     // The GC Needed flag should not be set during parallel
     // execution.  Instead, one of the requestGC() or
     // requestZoneGC() methods should be invoked.
     MOZ_ASSERT(!cx_->runtime()->gc.isGcNeeded());
 
     if (!abort_) {
         cx.bailoutRecord->joinCause(ParallelBailoutInterrupt);
         setAbortFlagAndRequestInterrupt(false);
@@ -1821,17 +1821,17 @@ bool
 ForkJoinContext::hasAcquiredJSContext() const
 {
     return acquiredJSContext_;
 }
 
 bool
 ForkJoinContext::check()
 {
-    if (runtime()->interruptPar) {
+    if (runtime()->hasPendingInterruptPar()) {
         shared_->setAbortFlagDueToInterrupt(*this);
         return false;
     }
     return true;
 }
 
 void
 ForkJoinContext::requestGC(JS::gcreason::Reason reason)
@@ -2268,23 +2268,16 @@ js::ParallelTestsShouldPass(JSContext *c
 {
     return IsIonEnabled(cx) &&
            IsBaselineEnabled(cx) &&
            !js_JitOptions.eagerCompilation &&
            js_JitOptions.baselineWarmUpThreshold != 0 &&
            cx->runtime()->gcZeal() == 0;
 }
 
-void
-js::RequestInterruptForForkJoin(JSRuntime *rt, JSRuntime::InterruptMode mode)
-{
-    if (mode != JSRuntime::RequestInterruptAnyThreadDontStopIon)
-        rt->interruptPar = true;
-}
-
 bool
 js::intrinsic_SetForkJoinTargetRegion(JSContext *cx, unsigned argc, Value *vp)
 {
     // This version of SetForkJoinTargetRegion is called during
     // sequential execution. It is a no-op. The parallel version
     // is intrinsic_SetForkJoinTargetRegionPar(), below.
     return true;
 }
--- a/js/src/vm/ForkJoin.h
+++ b/js/src/vm/ForkJoin.h
@@ -541,18 +541,16 @@ class LockedJSContext
     operator JSContext *() { return jscx_; }
     JSContext *operator->() { return jscx_; }
 };
 
 bool InExclusiveParallelSection();
 
 bool ParallelTestsShouldPass(JSContext *cx);
 
-void RequestInterruptForForkJoin(JSRuntime *rt, JSRuntime::InterruptMode mode);
-
 bool intrinsic_SetForkJoinTargetRegion(JSContext *cx, unsigned argc, Value *vp);
 extern const JSJitInfo intrinsic_SetForkJoinTargetRegionInfo;
 
 bool intrinsic_ClearThreadLocalArenas(JSContext *cx, unsigned argc, Value *vp);
 extern const JSJitInfo intrinsic_ClearThreadLocalArenasInfo;
 
 ///////////////////////////////////////////////////////////////////////////
 // Debug Spew
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -616,24 +616,18 @@ RegExpShared::execute(JSContext *cx, Han
         }
 
         if (result == RegExpRunStatus_Error) {
             // The RegExp engine might exit with an exception if an interrupt
             // was requested. If this happens, break out and retry the regexp
             // in the bytecode interpreter, which can execute while tolerating
             // future interrupts. Otherwise, if we keep getting interrupted we
             // will never finish executing the regexp.
-            bool interrupted;
-            {
-                JSRuntime::AutoLockForInterrupt lock(cx->runtime());
-                interrupted = cx->runtime()->interrupt;
-            }
-
-            if (interrupted) {
-                if (!InvokeInterruptCallback(cx))
+            if (cx->runtime()->hasPendingInterrupt()) {
+                if (!cx->runtime()->handleInterrupt(cx))
                     return RegExpRunStatus_Error;
                 break;
             }
 
             js_ReportOverRecursed(cx);
             return RegExpRunStatus_Error;
         }
 
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -31,16 +31,17 @@
 
 #include "asmjs/AsmJSSignalHandlers.h"
 #include "jit/arm/Simulator-arm.h"
 #include "jit/JitCompartment.h"
 #include "jit/mips/Simulator-mips.h"
 #include "jit/PcScriptCache.h"
 #include "js/MemoryMetrics.h"
 #include "js/SliceBudget.h"
+#include "vm/Debugger.h"
 
 #include "jscntxtinlines.h"
 #include "jsgcinlines.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::Atomic;
@@ -68,17 +69,17 @@ js::DisableExtraThreads()
 
 const JSSecurityCallbacks js::NullSecurityCallbacks = { };
 
 PerThreadData::PerThreadData(JSRuntime *runtime)
   : PerThreadDataFriendFields(),
     runtime_(runtime),
     jitTop(nullptr),
     jitJSContext(nullptr),
-    jitStackLimit(0),
+    jitStackLimit_(0xbad),
 #ifdef JS_TRACE_LOGGING
     traceLogger(nullptr),
 #endif
     activation_(nullptr),
     profilingActivation_(nullptr),
     asmJSActivationStack_(nullptr),
     autoFlushICache_(nullptr),
 #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
@@ -130,18 +131,18 @@ ReturnZeroSize(const void *p)
 JSRuntime::JSRuntime(JSRuntime *parentRuntime)
   : JS::shadow::Runtime(
 #ifdef JSGC_GENERATIONAL
         &gc.storeBuffer
 #endif
     ),
     mainThread(this),
     parentRuntime(parentRuntime),
-    interrupt(false),
-    interruptPar(false),
+    interrupt_(false),
+    interruptPar_(false),
     handlingSignal(false),
     interruptCallback(nullptr),
     interruptLock(nullptr),
     interruptLockOwner(nullptr),
     exclusiveAccessLock(nullptr),
     exclusiveAccessOwner(nullptr),
     mainThreadHasExclusiveAccess(false),
     numExclusiveThreads(0),
@@ -150,17 +151,17 @@ JSRuntime::JSRuntime(JSRuntime *parentRu
     defaultLocale(nullptr),
     defaultVersion_(JSVERSION_DEFAULT),
     futexAPI_(nullptr),
     ownerThread_(nullptr),
     tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     execAlloc_(nullptr),
     jitRuntime_(nullptr),
     selfHostingGlobal_(nullptr),
-    nativeStackBase(0),
+    nativeStackBase(GetNativeStackBase()),
     cxCallback(nullptr),
     destroyCompartmentCallback(nullptr),
     destroyZoneCallback(nullptr),
     sweepZoneCallback(nullptr),
     compartmentNameCallback(nullptr),
     activityCallback(nullptr),
     activityCallbackArg(nullptr),
     requestDepth(0),
@@ -316,18 +317,16 @@ JSRuntime::init(uint32_t maxbytes, uint3
     dateTimeInfo.updateTimeZoneAdjustment();
 
 #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
     simulatorRuntime_ = js::jit::CreateSimulatorRuntime();
     if (!simulatorRuntime_)
         return false;
 #endif
 
-    nativeStackBase = GetNativeStackBase();
-
     jitSupportsFloatingPoint = js::jit::JitSupportsFloatingPoint();
     jitSupportsSimd = js::jit::JitSupportsSimd();
 
     signalHandlersInstalled_ = EnsureAsmJSSignalHandlersInstalled(this);
     canUseSignalHandlers_ = signalHandlersInstalled_ && !SignalBasedTriggersDisabled();
 
     if (!spsProfiler.init())
         return false;
@@ -460,27 +459,16 @@ NewObjectCache::clearNurseryObjects(JSRu
         {
             PodZero(&e);
         }
     }
 #endif
 }
 
 void
-JSRuntime::resetJitStackLimit()
-{
-    AutoLockForInterrupt lock(this);
-    mainThread.setJitStackLimit(mainThread.nativeStackLimit[js::StackForUntrustedScript]);
-
-#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
-    mainThread.setJitStackLimit(js::jit::Simulator::StackLimit());
-#endif
-}
-
-void
 JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes *rtSizes)
 {
     // Several tables in the runtime enumerated below can be used off thread.
     AutoLockForExclusiveAccess lock(this);
 
     rtSizes->object += mallocSizeOf(this);
 
     rtSizes->atomsTable += atoms().sizeOfIncludingThis(mallocSizeOf);
@@ -524,43 +512,130 @@ JSRuntime::addSizeOfIncludingThis(mozill
 #ifdef JSGC_GENERATIONAL
     rtSizes->gc.nurseryCommitted += gc.nursery.sizeOfHeapCommitted();
     rtSizes->gc.nurseryDecommitted += gc.nursery.sizeOfHeapDecommitted();
     rtSizes->gc.nurseryHugeSlots += gc.nursery.sizeOfHugeSlots(mallocSizeOf);
     gc.storeBuffer.addSizeOfExcludingThis(mallocSizeOf, &rtSizes->gc);
 #endif
 }
 
+static bool
+InvokeInterruptCallback(JSContext *cx)
+{
+    MOZ_ASSERT(cx->runtime()->requestDepth >= 1);
+
+    cx->gcIfNeeded();
+
+    // A worker thread may have requested an interrupt after finishing an Ion
+    // compilation.
+    jit::AttachFinishedCompilations(cx);
+
+    // Important: Additional callbacks can occur inside the callback handler
+    // if it re-enters the JS engine. The embedding must ensure that the
+    // callback is disconnected before attempting such re-entry.
+    JSInterruptCallback cb = cx->runtime()->interruptCallback;
+    if (!cb)
+        return true;
+
+    if (cb(cx)) {
+        // Debugger treats invoking the interrupt callback as a "step", so
+        // invoke the onStep handler.
+        if (cx->compartment()->debugMode()) {
+            ScriptFrameIter iter(cx);
+            if (iter.script()->stepModeEnabled()) {
+                RootedValue rval(cx);
+                switch (Debugger::onSingleStep(cx, &rval)) {
+                  case JSTRAP_ERROR:
+                    return false;
+                  case JSTRAP_CONTINUE:
+                    return true;
+                  case JSTRAP_RETURN:
+                    // See note in Debugger::propagateForcedReturn.
+                    Debugger::propagateForcedReturn(cx, iter.abstractFramePtr(), rval);
+                    return false;
+                  case JSTRAP_THROW:
+                    cx->setPendingException(rval);
+                    return false;
+                  default:;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    // No need to set aside any pending exception here: ComputeStackString
+    // already does that.
+    JSString *stack = ComputeStackString(cx);
+    JSFlatString *flat = stack ? stack->ensureFlat(cx) : nullptr;
+
+    const char16_t *chars;
+    AutoStableStringChars stableChars(cx);
+    if (flat && stableChars.initTwoByte(cx, flat))
+        chars = stableChars.twoByteRange().start().get();
+    else
+        chars = MOZ_UTF16("(stack not available)");
+    JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_WARNING, js_GetErrorMessage, nullptr,
+                                   JSMSG_TERMINATED, chars);
+
+    return false;
+}
+
+void
+PerThreadData::resetJitStackLimit()
+{
+    // Note that, for now, we use the untrusted limit for ion. This is fine,
+    // because it's the most conservative limit, and if we hit it, we'll bail
+    // out of ion into the interpeter, which will do a proper recursion check.
+#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
+    jitStackLimit_ = jit::Simulator::StackLimit();
+#else
+    jitStackLimit_ = nativeStackLimit[StackForUntrustedScript];
+#endif
+}
+
+void
+PerThreadData::initJitStackLimit()
+{
+    resetJitStackLimit();
+}
+
+void
+PerThreadData::initJitStackLimitPar(uintptr_t limit)
+{
+    jitStackLimit_ = limit;
+}
+
 void
 JSRuntime::requestInterrupt(InterruptMode mode)
 {
-    AutoLockForInterrupt lock(this);
-
-    /*
-     * Invalidate ionTop to trigger its over-recursion check. Note this must be
-     * set before interrupt, to avoid racing with js::InvokeInterruptCallback,
-     * into a weird state where interrupt is stuck at 0 but jitStackLimit is
-     * MAXADDR.
-     */
-    mainThread.setJitStackLimit(-1);
+    interrupt_ = true;
+    interruptPar_ = true;
+    mainThread.jitStackLimit_ = UINTPTR_MAX;
 
-    interrupt = true;
-
-    RequestInterruptForForkJoin(this, mode);
-
-    /*
-     * asm.js and normal Ion code optionally use memory protection and signal
-     * handlers to halt running code.
-     */
     if (canUseSignalHandlers()) {
+        AutoLockForInterrupt lock(this);
         RequestInterruptForAsmJSCode(this, mode);
         jit::RequestInterruptForIonCode(this, mode);
     }
 }
 
+bool
+JSRuntime::handleInterrupt(JSContext *cx)
+{
+    MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
+    if (interrupt_ || mainThread.jitStackLimit_ == UINTPTR_MAX) {
+        interrupt_ = false;
+        interruptPar_ = false;
+        mainThread.resetJitStackLimit();
+        return InvokeInterruptCallback(cx);
+    }
+    return true;
+}
+
 jit::ExecutableAllocator *
 JSRuntime::createExecutableAllocator(JSContext *cx)
 {
     MOZ_ASSERT(!execAlloc_);
     MOZ_ASSERT(cx->runtime() == this);
 
     execAlloc_ = js_new<jit::ExecutableAllocator>();
     if (!execAlloc_)
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -514,23 +514,30 @@ class PerThreadData : public PerThreadDa
 
     /*
      * The current JSContext when entering JIT code. This field may only be used
      * from JIT code and C++ directly called by JIT code (otherwise it may refer
      * to the wrong JSContext).
      */
     JSContext           *jitJSContext;
 
-    /*
-     * The stack limit checked by JIT code. This stack limit may be temporarily
-     * set to null to force JIT code to exit (e.g., for the operation callback).
-     */
-    uintptr_t            jitStackLimit;
+    /* See comment for JSRuntime::interrupt_. */
+  private:
+    mozilla::Atomic<uintptr_t, mozilla::Relaxed> jitStackLimit_;
+    void resetJitStackLimit();
+    friend struct ::JSRuntime;
+  public:
+    void initJitStackLimit();
+    void initJitStackLimitPar(uintptr_t limit);
 
-    inline void setJitStackLimit(uintptr_t limit);
+    uintptr_t jitStackLimit() const { return jitStackLimit_; }
+
+    // For read-only JIT use:
+    void *addressOfJitStackLimit() { return &jitStackLimit_; }
+    static size_t offsetOfJitStackLimit() { return offsetof(PerThreadData, jitStackLimit_); }
 
     // Information about the heap allocated backtrack stack used by RegExp JIT code.
     irregexp::RegExpStack regexpStack;
 
 #ifdef JS_TRACE_LOGGING
     TraceLogger         *traceLogger;
 #endif
 
@@ -673,18 +680,16 @@ class PerThreadData : public PerThreadDa
     void setSimulator(js::jit::Simulator *sim);
     js::jit::SimulatorRuntime *simulatorRuntime() const;
     uintptr_t *addressOfSimulatorStackLimit();
 #endif
 };
 
 class AutoLockForExclusiveAccess;
 
-void RecomputeStackLimit(JSRuntime *rt, StackKind kind);
-
 } // namespace js
 
 struct JSRuntime : public JS::shadow::Runtime,
                    public js::MallocProvider<JSRuntime>
 {
     /*
      * Per-thread data for the main thread that is associated with
      * this JSRuntime, as opposed to any worker threads used in
@@ -698,28 +703,66 @@ struct JSRuntime : public JS::shadow::Ru
     js::PerThreadData mainThread;
 
     /*
      * If non-null, another runtime guaranteed to outlive this one and whose
      * permanent data may be used by this one where possible.
      */
     JSRuntime *parentRuntime;
 
-    /*
-     * If true, we've been asked to call the interrupt callback as soon as
-     * possible.
-     */
-    mozilla::Atomic<bool, mozilla::Relaxed> interrupt;
+  private:
+    mozilla::Atomic<uint32_t, mozilla::Relaxed> interrupt_;
+    mozilla::Atomic<uint32_t, mozilla::Relaxed> interruptPar_;
+  public:
+
+    enum InterruptMode {
+        RequestInterruptMainThread,
+        RequestInterruptAnyThread,
+        RequestInterruptAnyThreadDontStopIon,
+        RequestInterruptAnyThreadForkJoin
+    };
 
-    /*
-     * If non-zero, ForkJoin should service an interrupt. This is a separate
-     * flag from |interrupt| because we cannot use the mprotect trick with PJS
-     * code and ignore the TriggerCallbackAnyThreadDontStopIon trigger.
-     */
-    mozilla::Atomic<bool, mozilla::Relaxed> interruptPar;
+    // Any thread can call requestInterrupt() to request that the main JS thread
+    // stop running and call the interrupt callback (allowing the interrupt
+    // callback to halt execution). To stop the main JS thread, requestInterrupt
+    // sets two fields: interrupt_ (set to true) and jitStackLimit_ (set to
+    // UINTPTR_MAX). The JS engine must continually poll one of these fields
+    // and call handleInterrupt if either field has the interrupt value. (The
+    // point of setting jitStackLimit_ to UINTPTR_MAX is that JIT code already
+    // needs to guard on jitStackLimit_ in every function prologue to avoid
+    // stack overflow, so we avoid a second branch on interrupt_ by setting
+    // jitStackLimit_ to a value that is guaranteed to fail the guard.)
+    //
+    // Note that the writes to interrupt_ and jitStackLimit_ use a Relaxed
+    // Atomic so, while the writes are guaranteed to eventually be visible to
+    // the main thread, it can happen in any order. handleInterrupt calls the
+    // interrupt callback if either is set, so it really doesn't matter as long
+    // as the JS engine is continually polling at least one field. In corner
+    // cases, this relaxed ordering could lead to an interrupt handler being
+    // called twice in succession after a single requestInterrupt call, but
+    // that's fine.
+    void requestInterrupt(InterruptMode mode);
+    bool handleInterrupt(JSContext *cx);
+
+    MOZ_ALWAYS_INLINE bool hasPendingInterrupt() const {
+        return interrupt_;
+    }
+    MOZ_ALWAYS_INLINE bool hasPendingInterruptPar() const {
+        return interruptPar_;
+    }
+
+    // For read-only JIT use:
+    void *addressOfInterruptUint32() {
+        static_assert(sizeof(interrupt_) == sizeof(uint32_t), "Assumed by JIT callers");
+        return &interrupt_;
+    }
+    void *addressOfInterruptParUint32() {
+        static_assert(sizeof(interruptPar_) == sizeof(uint32_t), "Assumed by JIT callers");
+        return &interruptPar_;
+    }
 
     /* Set when handling a signal for a thread associated with this runtime. */
     bool handlingSignal;
 
     JSInterruptCallback interruptCallback;
 
 #ifdef DEBUG
     void assertCanLock(js::RuntimeLock which);
@@ -895,17 +938,17 @@ struct JSRuntime : public JS::shadow::Ru
 
     /* Gets current default locale. String remains owned by context. */
     const char *getDefaultLocale();
 
     JSVersion defaultVersion() { return defaultVersion_; }
     void setDefaultVersion(JSVersion v) { defaultVersion_ = v; }
 
     /* Base address of the native stack for the current thread. */
-    uintptr_t           nativeStackBase;
+    const uintptr_t     nativeStackBase;
 
     /* The native stack size limit that runtime should not exceed. */
     size_t              nativeStackQuota[js::StackKindCount];
 
     /* Context create/destroy callback. */
     JSContextCallback   cxCallback;
     void               *cxCallbackData;
 
@@ -1261,20 +1304,16 @@ struct JSRuntime : public JS::shadow::Ru
     js::ScriptDataTable &scriptDataTable() {
         MOZ_ASSERT(currentThreadHasExclusiveAccess());
         return scriptDataTable_;
     }
 
     bool                jitSupportsFloatingPoint;
     bool                jitSupportsSimd;
 
-    // Used to reset stack limit after a signaled interrupt (i.e. jitStackLimit_ = -1)
-    // has been noticed by Ion/Baseline.
-    void resetJitStackLimit();
-
     // Cache for jit::GetPcScript().
     js::jit::PcScriptCache *ionPcScriptCache;
 
     js::ThreadPool threadPool;
 
     js::DefaultJSContextCallback defaultJSContextCallback;
 
     js::CTypesActivityCallback  ctypesActivityCallback;
@@ -1325,27 +1364,16 @@ struct JSRuntime : public JS::shadow::Ru
      * The function must be called outside the GC lock.
      */
     JS_FRIEND_API(void *) onOutOfMemory(void *p, size_t nbytes);
     JS_FRIEND_API(void *) onOutOfMemory(void *p, size_t nbytes, JSContext *cx);
 
     /*  onOutOfMemory but can call the largeAllocationFailureCallback. */
     JS_FRIEND_API(void *) onOutOfMemoryCanGC(void *p, size_t bytes);
 
-    // Ways in which the interrupt callback on the runtime can be triggered,
-    // varying based on which thread is triggering the callback.
-    enum InterruptMode {
-        RequestInterruptMainThread,
-        RequestInterruptAnyThread,
-        RequestInterruptAnyThreadDontStopIon,
-        RequestInterruptAnyThreadForkJoin
-    };
-
-    void requestInterrupt(InterruptMode mode);
-
     void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes *runtime);
 
   private:
     JS::RuntimeOptions options_;
 
     // Settings for how helper threads can be used.
     bool offthreadIonCompilationEnabled_;
     bool parallelParsingEnabled_;
@@ -1559,23 +1587,16 @@ class MOZ_STACK_CLASS AutoKeepAtoms
     ~AutoKeepAtoms() {
         if (JSRuntime *rt = pt->runtimeIfOnOwnerThread()) {
             MOZ_ASSERT(rt->keepAtoms_);
             rt->keepAtoms_--;
         }
     }
 };
 
-inline void
-PerThreadData::setJitStackLimit(uintptr_t limit)
-{
-    MOZ_ASSERT(runtime_->currentThreadOwnsInterruptLock());
-    jitStackLimit = limit;
-}
-
 inline JSRuntime *
 PerThreadData::runtimeFromMainThread()
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
     return runtime_;
 }
 
 inline JSRuntime *
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -7,16 +7,17 @@
 
 #include "nsBulletFrame.h"
 
 #include "gfx2DGlue.h"
 #include "gfxUtils.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/PathHelpers.h"
 #include "mozilla/MathAlgorithms.h"
+#include "mozilla/Move.h"
 #include "nsCOMPtr.h"
 #include "nsFontMetrics.h"
 #include "nsGkAtoms.h"
 #include "nsGenericHTMLElement.h"
 #include "nsAttrValueInlines.h"
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 #include "nsIDocument.h"
@@ -46,30 +47,24 @@ NS_IMPL_FRAMEARENA_HELPERS(nsBulletFrame
 #ifdef DEBUG
 NS_QUERYFRAME_HEAD(nsBulletFrame)
   NS_QUERYFRAME_ENTRY(nsBulletFrame)
 NS_QUERYFRAME_TAIL_INHERITING(nsFrame)
 #endif
 
 nsBulletFrame::~nsBulletFrame()
 {
+  NS_ASSERTION(!mBlockingOnload, "Still blocking onload in destructor?");
 }
 
 void
 nsBulletFrame::DestroyFrom(nsIFrame* aDestructRoot)
 {
-  // Stop image loading first
-  if (mImageRequest) {
-    // Deregister our image request from the refresh driver
-    nsLayoutUtils::DeregisterImageRequest(PresContext(),
-                                          mImageRequest,
-                                          &mRequestRegistered);
-    mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
-    mImageRequest = nullptr;
-  }
+  // Stop image loading first.
+  DeregisterAndCancelImageRequest();
 
   if (mListener) {
     mListener->SetFrame(nullptr);
   }
 
   // Let base class do the rest
   nsFrame::DestroyFrom(aDestructRoot);
 }
@@ -127,45 +122,31 @@ nsBulletFrame::DidSetStyleContext(nsStyl
         newURI->Equals(oldURI, &same);
         if (same) {
           needNewRequest = false;
         }
       }
     }
 
     if (needNewRequest) {
-      nsRefPtr<imgRequestProxy> oldRequest = mImageRequest;
-      newRequest->Clone(mListener, getter_AddRefs(mImageRequest));
+      nsRefPtr<imgRequestProxy> newRequestClone;
+      newRequest->Clone(mListener, getter_AddRefs(newRequestClone));
 
       // Deregister the old request. We wait until after Clone is done in case
       // the old request and the new request are the same underlying image
       // accessed via different URLs.
-      if (oldRequest) {
-        nsLayoutUtils::DeregisterImageRequest(PresContext(), oldRequest,
-                                              &mRequestRegistered);
-        oldRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
-        oldRequest = nullptr;
-      }
+      DeregisterAndCancelImageRequest();
 
       // Register the new request.
-      if (mImageRequest) {
-        nsLayoutUtils::RegisterImageRequestIfAnimated(PresContext(),
-                                                      mImageRequest,
-                                                      &mRequestRegistered);
-      }
+      mImageRequest = Move(newRequestClone);
+      RegisterImageRequest(/* aKnownToBeAnimated = */ false);
     }
   } else {
-    // No image request on the new style context
-    if (mImageRequest) {
-      nsLayoutUtils::DeregisterImageRequest(PresContext(), mImageRequest,
-                                            &mRequestRegistered);
-
-      mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
-      mImageRequest = nullptr;
-    }
+    // No image request on the new style context.
+    DeregisterAndCancelImageRequest();
   }
 
 #ifdef ACCESSIBILITY
   // Update the list bullet accessible. If old style list isn't available then
   // no need to update the accessible tree because it's not created yet.
   if (aOldStyleContext) {
     nsAccessibilityService* accService = nsIPresShell::AccService();
     if (accService) {
@@ -687,24 +668,77 @@ nsBulletFrame::Notify(imgIRequest *aRequ
     // always correct, and I'll be a stunned mullet if it ever matters for performance
     InvalidateFrame();
   }
 
   if (aType == imgINotificationObserver::IS_ANIMATED) {
     // Register the image request with the refresh driver now that we know it's
     // animated.
     if (aRequest == mImageRequest) {
-      nsLayoutUtils::RegisterImageRequest(PresContext(), mImageRequest,
-                                          &mRequestRegistered);
+      RegisterImageRequest(/* aKnownToBeAnimated = */ true);
     }
   }
 
+  if (aType == imgINotificationObserver::LOAD_COMPLETE) {
+    // Unconditionally start decoding for now.
+    // XXX(seth): We eventually want to decide whether to do this based on
+    // visibility. We should get that for free from bug 1091236.
+    if (aRequest == mImageRequest) {
+      mImageRequest->RequestDecode();
+    }
+    InvalidateFrame();
+  }
+
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsBulletFrame::BlockOnload(imgIRequest* aRequest)
+{
+  if (aRequest != mImageRequest) {
+    return NS_OK;
+  }
+
+  NS_ASSERTION(!mBlockingOnload, "Double BlockOnload for an nsBulletFrame?");
+
+  nsIDocument* doc = GetOurCurrentDoc();
+  if (doc) {
+    mBlockingOnload = true;
+    doc->BlockOnload();
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBulletFrame::UnblockOnload(imgIRequest* aRequest)
+{
+  if (aRequest != mImageRequest) {
+    return NS_OK;
+  }
+
+  NS_ASSERTION(!mBlockingOnload, "Double UnblockOnload for an nsBulletFrame?");
+
+  nsIDocument* doc = GetOurCurrentDoc();
+  if (doc) {
+    doc->UnblockOnload(false);
+  }
+  mBlockingOnload = false;
+
+  return NS_OK;
+}
+
+nsIDocument*
+nsBulletFrame::GetOurCurrentDoc() const
+{
+  nsIContent* parentContent = GetParent()->GetContent();
+  return parentContent ? parentContent->GetComposedDoc()
+                       : nullptr;
+}
+
 nsresult nsBulletFrame::OnStartContainer(imgIRequest *aRequest,
                                          imgIContainer *aImage)
 {
   if (!aImage) return NS_ERROR_INVALID_ARG;
   if (!aRequest) return NS_ERROR_INVALID_ARG;
 
   uint32_t status;
   aRequest->GetImageStatus(&status);
@@ -868,17 +902,67 @@ nsBulletFrame::GetSpokenText(nsAString& 
   } else {
     nsAutoString prefix, suffix;
     style->GetPrefix(prefix);
     style->GetSuffix(suffix);
     aText = prefix + aText + suffix;
   }
 }
 
+void
+nsBulletFrame::RegisterImageRequest(bool aKnownToBeAnimated)
+{
+  if (mImageRequest) {
+    // mRequestRegistered is a bitfield; unpack it temporarily so we can take
+    // the address.
+    bool isRequestRegistered = mRequestRegistered;
 
+    if (aKnownToBeAnimated) {
+      nsLayoutUtils::RegisterImageRequest(PresContext(), mImageRequest,
+                                          &isRequestRegistered);
+    } else {
+      nsLayoutUtils::RegisterImageRequestIfAnimated(PresContext(),
+                                                    mImageRequest,
+                                                    &isRequestRegistered);
+    }
+
+    isRequestRegistered = mRequestRegistered;
+  }
+}
+
+
+void
+nsBulletFrame::DeregisterAndCancelImageRequest()
+{
+  if (mImageRequest) {
+    // mRequestRegistered is a bitfield; unpack it temporarily so we can take
+    // the address.
+    bool isRequestRegistered = mRequestRegistered;
+
+    // Deregister our image request from the refresh driver.
+    nsLayoutUtils::DeregisterImageRequest(PresContext(),
+                                          mImageRequest,
+                                          &isRequestRegistered);
+
+    isRequestRegistered = mRequestRegistered;
+
+    // Unblock onload if we blocked it.
+    if (mBlockingOnload) {
+      nsIDocument* doc = GetOurCurrentDoc();
+      if (doc) {
+        doc->UnblockOnload(false);
+      }
+      mBlockingOnload = false;
+    }
+
+    // Cancel the image request and forget about it.
+    mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
+    mImageRequest = nullptr;
+  }
+}
 
 
 
 
 
 
 NS_IMPL_ISUPPORTS(nsBulletListener, imgINotificationObserver)
 
@@ -889,12 +973,31 @@ nsBulletListener::nsBulletListener() :
 
 nsBulletListener::~nsBulletListener()
 {
 }
 
 NS_IMETHODIMP
 nsBulletListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
 {
-  if (!mFrame)
+  if (!mFrame) {
     return NS_ERROR_FAILURE;
+  }
   return mFrame->Notify(aRequest, aType, aData);
 }
+
+NS_IMETHODIMP
+nsBulletListener::BlockOnload(imgIRequest* aRequest)
+{
+  if (!mFrame) {
+    return NS_ERROR_FAILURE;
+  }
+  return mFrame->BlockOnload(aRequest);
+}
+
+NS_IMETHODIMP
+nsBulletListener::UnblockOnload(imgIRequest* aRequest)
+{
+  if (!mFrame) {
+    return NS_ERROR_FAILURE;
+  }
+  return mFrame->UnblockOnload(aRequest);
+}
--- a/layout/generic/nsBulletFrame.h
+++ b/layout/generic/nsBulletFrame.h
@@ -7,29 +7,32 @@
 
 #ifndef nsBulletFrame_h___
 #define nsBulletFrame_h___
 
 #include "mozilla/Attributes.h"
 #include "nsFrame.h"
 
 #include "imgINotificationObserver.h"
+#include "imgIOnloadBlocker.h"
 
 class imgIContainer;
 class imgRequestProxy;
 
 class nsBulletFrame;
 
-class nsBulletListener MOZ_FINAL : public imgINotificationObserver
+class nsBulletListener MOZ_FINAL : public imgINotificationObserver,
+                                   public imgIOnloadBlocker
 {
 public:
   nsBulletListener();
 
   NS_DECL_ISUPPORTS
   NS_DECL_IMGINOTIFICATIONOBSERVER
+  NS_DECL_IMGIONLOADBLOCKER
 
   void SetFrame(nsBulletFrame *frame) { mFrame = frame; }
 
 private:
   virtual ~nsBulletListener();
 
   nsBulletFrame *mFrame;
 };
@@ -45,21 +48,24 @@ public:
   NS_DECL_QUERYFRAME_TARGET(nsBulletFrame)
   NS_DECL_QUERYFRAME
 #endif
 
   explicit nsBulletFrame(nsStyleContext* aContext)
     : nsFrame(aContext)
     , mPadding(GetWritingMode())
     , mIntrinsicSize(GetWritingMode())
-  {
-  }
+    , mRequestRegistered(false)
+    , mBlockingOnload(false)
+  { }
   virtual ~nsBulletFrame();
 
-  NS_IMETHOD Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData);
+  NS_IMETHOD Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData);
+  NS_IMETHOD BlockOnload(imgIRequest* aRequest);
+  NS_IMETHOD UnblockOnload(imgIRequest* aRequest);
 
   // nsIFrame
   virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE;
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists) MOZ_OVERRIDE;
   virtual nsIAtom* GetType() const MOZ_OVERRIDE;
   virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) MOZ_OVERRIDE;
@@ -106,24 +112,30 @@ protected:
 
   void AppendSpacingToPadding(nsFontMetrics* aFontMetrics);
   void GetDesiredSize(nsPresContext* aPresContext,
                       nsRenderingContext *aRenderingContext,
                       nsHTMLReflowMetrics& aMetrics,
                       float aFontSizeInflation);
 
   void GetLoadGroup(nsPresContext *aPresContext, nsILoadGroup **aLoadGroup);
+  nsIDocument* GetOurCurrentDoc() const;
 
   mozilla::LogicalMargin mPadding;
   nsRefPtr<imgRequestProxy> mImageRequest;
   nsRefPtr<nsBulletListener> mListener;
 
   mozilla::LogicalSize mIntrinsicSize;
   int32_t mOrdinal;
 
 private:
+  void RegisterImageRequest(bool aKnownToBeAnimated);
+  void DeregisterAndCancelImageRequest();
 
   // This is a boolean flag indicating whether or not the current image request
   // has been registered with the refresh driver.
-  bool mRequestRegistered;
+  bool mRequestRegistered : 1;
+
+  // Whether we're currently blocking onload.
+  bool mBlockingOnload : 1;
 };
 
 #endif /* nsBulletFrame_h___ */
--- a/layout/reftests/svg/as-image/reftest.list
+++ b/layout/reftests/svg/as-image/reftest.list
@@ -122,17 +122,17 @@ fuzzy-if(d2d,16,10) == img-content-outsi
 # with with a fragmentIdentifier viewBox
 skip-if(B2G) == img-fragment-1a.html  img-fragment-1-ref.html # bug 773482
 skip-if(B2G) == img-fragment-1b.html  img-fragment-1-ref.html # bug 773482
 skip-if(B2G) == img-fragment-1c.html  img-fragment-1-ref.html # bug 773482
 skip-if(B2G) == img-fragment-2a.html  img-fragment-2-ref.html # bug 773482
 skip-if(B2G) == img-fragment-2b.html  img-fragment-2-ref.html # bug 773482
 skip-if(B2G) == img-fragment-2c.html  img-fragment-2-ref.html # bug 773482
 
-skip-if(B2G) == list-simple-1.html list-simple-1-ref.html # bug 773482
+fuzzy-if(B2G,68,4) == list-simple-1.html list-simple-1-ref.html
 
 == svg-image-simple-1.svg lime100x100.svg
 == svg-image-simple-2.svg lime100x100.svg
 == svg-image-simple-3.svg lime100x100.svg
 
 # tests for <svg> files that include themselves as an <image>
 == svg-image-recursive-1a.svg  svg-image-recursive-1-ref.svg
 == svg-image-recursive-1b.svg  svg-image-recursive-1-ref.svg
--- a/layout/style/FontFace.cpp
+++ b/layout/style/FontFace.cpp
@@ -221,17 +221,20 @@ FontFace::FontFace(nsISupports* aParent,
   , mInFontFaceSet(false)
   , mInitialized(false)
   , mLoadWhenInitialized(false)
 {
   MOZ_COUNT_CTOR(FontFace);
 
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aParent);
 
-  if (global) {
+  // If the pref is not set, don't create the Promise (which the page wouldn't
+  // be able to get to anyway) as it causes the window.FontFace constructor
+  // to be created.
+  if (global && FontFaceSet::PrefEnabled()) {
     ErrorResult rv;
     mLoaded = Promise::Create(global, rv);
   }
 }
 
 FontFace::~FontFace()
 {
   MOZ_COUNT_DTOR(FontFace);
--- a/layout/style/FontFaceSet.cpp
+++ b/layout/style/FontFaceSet.cpp
@@ -34,16 +34,18 @@
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 #define LOG(args) PR_LOG(gfxUserFontSet::GetUserFontsLog(), PR_LOG_DEBUG, args)
 #define LOG_ENABLED() PR_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), \
                                   PR_LOG_DEBUG)
 
+#define FONT_LOADING_API_ENABLED_PREF "layout.css.font-loading-api.enabled"
+
 NS_IMPL_CYCLE_COLLECTION_CLASS(FontFaceSet)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FontFaceSet, DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReady);
   for (size_t i = 0; i < tmp->mRuleFaces.Length(); i++) {
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRuleFaces[i].mFontFace);
   }
@@ -78,17 +80,20 @@ FontFaceSet::FontFaceSet(nsPIDOMWindow* 
   , mDispatchedLoadingEvent(false)
   , mHasLoadingFontFaces(false)
   , mHasLoadingFontFacesIsDirty(false)
 {
   MOZ_COUNT_CTOR(FontFaceSet);
 
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aWindow);
 
-  if (global) {
+  // If the pref is not set, don't create the Promise (which the page wouldn't
+  // be able to get to anyway) as it causes the window.FontFaceSet constructor
+  // to be created.
+  if (global && PrefEnabled()) {
     ErrorResult rv;
     mReady = Promise::Create(global, rv);
   }
 
   if (mReady) {
     mReady->MaybeResolve(this);
   }
 
@@ -1332,17 +1337,17 @@ FontFaceSet::CheckLoadingStarted()
 {
   if (HasLoadingFontFaces() && !mDispatchedLoadingEvent) {
     mStatus = FontFaceSetLoadStatus::Loading;
     mDispatchedLoadingEvent = true;
     (new AsyncEventDispatcher(this, NS_LITERAL_STRING("loading"),
                               false))->RunDOMEventWhenSafe();
   }
 
-  if (mReadyIsResolved) {
+  if (mReadyIsResolved && PrefEnabled()) {
     nsRefPtr<Promise> ready;
     if (GetParentObject()) {
       ErrorResult rv;
       ready = Promise::Create(GetParentObject(), rv);
     }
 
     if (ready) {
       mReady.swap(ready);
@@ -1491,16 +1496,28 @@ FontFaceSet::HandleEvent(nsIDOMEvent* aE
   }
 
   Disconnect();
   CheckLoadingFinished();
 
   return NS_OK;
 }
 
+/* static */ bool
+FontFaceSet::PrefEnabled()
+{
+  static bool initialized = false;
+  static bool enabled;
+  if (!initialized) {
+    initialized = true;
+    Preferences::AddBoolVarCache(&enabled, FONT_LOADING_API_ENABLED_PREF);
+  }
+  return enabled;
+}
+
 // nsICSSLoaderObserver
 
 NS_IMETHODIMP
 FontFaceSet::StyleSheetLoaded(mozilla::CSSStyleSheet* aSheet,
                               bool aWasAlternate,
                               nsresult aStatus)
 {
   CheckLoadingFinished();
--- a/layout/style/FontFaceSet.h
+++ b/layout/style/FontFaceSet.h
@@ -152,16 +152,21 @@ public:
 
   /**
    * Notification method called by the nsPresContext to indicate that the
    * refresh driver ticked and flushed style and layout.
    * were just flushed.
    */
   void DidRefresh();
 
+  /**
+   * Returns whether the "layout.css.font-loading-api.enabled" pref is true.
+   */
+  static bool PrefEnabled();
+
   // nsICSSLoaderObserver
   NS_IMETHOD StyleSheetLoaded(mozilla::CSSStyleSheet* aSheet,
                               bool aWasAlternate,
                               nsresult aStatus);
 
   // -- Web IDL --------------------------------------------------------------
 
   IMPL_EVENT_HANDLER(loading)
--- a/media/libnestegg/include/nestegg-stdint.h
+++ b/media/libnestegg/include/nestegg-stdint.h
@@ -1,7 +1,10 @@
 #ifdef _WIN32
 typedef __int64 int64_t;
 typedef unsigned __int64 uint64_t;
+#if !defined(INT64_MAX)
+#define INT64_MAX 9223372036854775807LL
+#endif
 #else
 #include <stdint.h>
 #endif
 
--- a/media/libnestegg/src/nestegg.c
+++ b/media/libnestegg/src/nestegg.c
@@ -2101,16 +2101,19 @@ nestegg_get_cue_point(nestegg * ctx, uns
   return 0;
 }
 
 int
 nestegg_offset_seek(nestegg * ctx, uint64_t offset)
 {
   int r;
 
+  if (offset > INT64_MAX)
+    return -1;
+
   /* Seek and set up parser state for segment-level element (Cluster). */
   r = ne_io_seek(ctx->io, offset, NESTEGG_SEEK_SET);
   if (r != 0)
     return -1;
   ctx->last_valid = 0;
 
   while (ctx->ancestor)
     ne_ctx_pop(ctx);
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -2477,18 +2477,19 @@ var NativeWindow = {
      */
     show: function(event) {
       // Android Long-press / contextmenu event provides clientX/Y data. This is not provided
       // by mochitest: test_browserElement_inproc_ContextmenuEvents.html.
       if (!event.clientX || !event.clientY) {
         return;
       }
 
-      // Use the highlighted element for the context menu target.
-      this._target = BrowserEventHandler._highlightElement;
+      // Use the highlighted element for the context menu target. When accessibility is
+      // enabled, elements may not be highlighted so use the event target instead.
+      this._target = BrowserEventHandler._highlightElement || event.target;
       if (!this._target) {
         return;
       }
 
       // Try to build a list of contextmenu items. If successful, actually show the
       // native context menu by passing the list to Java.
       this._buildMenu(event.clientX, event.clientY);
       if (this._shouldShow()) {
--- a/parser/html/nsHtml5RefPtr.h
+++ b/parser/html/nsHtml5RefPtr.h
@@ -445,35 +445,9 @@ template <class T>
 inline
 bool
 operator!=( NSCAP_Zero* lhs, const nsHtml5RefPtr<T>& rhs )
     // specifically to allow |0 != smartPtr|
   {
     return reinterpret_cast<const void*>(lhs) != static_cast<const void*>(rhs.get());
   }
 
-
-#ifdef HAVE_CPP_TROUBLE_COMPARING_TO_ZERO
-
-  // We need to explicitly define comparison operators for `int'
-  // because the compiler is lame.
-
-template <class T>
-inline
-bool
-operator==( const nsHtml5RefPtr<T>& lhs, int rhs )
-    // specifically to allow |smartPtr == 0|
-  {
-    return static_cast<const void*>(lhs.get()) == reinterpret_cast<const void*>(rhs);
-  }
-
-template <class T>
-inline
-bool
-operator==( int lhs, const nsHtml5RefPtr<T>& rhs )
-    // specifically to allow |0 == smartPtr|
-  {
-    return reinterpret_cast<const void*>(lhs) == static_cast<const void*>(rhs.get());
-  }
-
-#endif // !defined(HAVE_CPP_TROUBLE_COMPARING_TO_ZERO)
-
 #endif // !defined(nsHtml5RefPtr_h)
--- a/security/manager/locales/en-US/chrome/pippki/pippki.properties
+++ b/security/manager/locales/en-US/chrome/pippki/pippki.properties
@@ -75,17 +75,17 @@ pageInfo_Privacy_None2=Information sent 
 pageInfo_Privacy_None3=The page you are viewing is not encrypted.
 # LOCALIZATION NOTE (pageInfo_EncryptionWithBitsAndProtocol): %1$S is the name of the encryption standard,
 # %2$S is the key size of the cipher.
 # %3$S is protocol version like "SSL 3" or "TLS 1.2"
 pageInfo_EncryptionWithBitsAndProtocol=Connection Encrypted (%1$S, %2$S bit keys, %3$S)
 pageInfo_Privacy_Encrypted1=The page you are viewing was encrypted before being transmitted over the Internet.
 pageInfo_Privacy_Encrypted2=Encryption makes it difficult for unauthorized people to view information traveling between computers. It is therefore unlikely that anyone read this page as it traveled across the network.
 pageInfo_MixedContent=Connection Partially Encrypted
-pageInfo_Privacy_Mixed1=Parts of the page you are viewing were not encrypted before being transmitted over the Internet.
+pageInfo_Privacy_Broken1=Parts of the page you are viewing were not encrypted or the encryption is not strong enough before being transmitted over the Internet.
 
 #Cert Viewer
 certDetails=Certificate Viewer:
 notPresent=<Not Part Of Certificate>
 unknownIssuer=<Unknown Issuer>
 
 escrowFinalMessage=You should click OK only if you trust "%S" to protect your encryption private key.
 
--- a/security/manager/ssl/src/nsNSSCallbacks.cpp
+++ b/security/manager/ssl/src/nsNSSCallbacks.cpp
@@ -1167,26 +1167,113 @@ void HandshakeCallback(PRFileDesc* fd, v
           fd, static_cast<unsigned int>(versions.min),
               static_cast<unsigned int>(versions.max)));
 
   // If the handshake completed, then we know the site is TLS tolerant
   ioLayerHelpers.rememberTolerantAtVersion(infoObject->GetHostName(),
                                            infoObject->GetPort(),
                                            versions.max);
 
+  bool weakEncryption = false;
+  SSLChannelInfo channelInfo;
+  rv = SSL_GetChannelInfo(fd, &channelInfo, sizeof(channelInfo));
+  MOZ_ASSERT(rv == SECSuccess);
+  if (rv == SECSuccess) {
+    // Get the protocol version for telemetry
+    // 0=ssl3, 1=tls1, 2=tls1.1, 3=tls1.2
+    unsigned int versionEnum = channelInfo.protocolVersion & 0xFF;
+    Telemetry::Accumulate(Telemetry::SSL_HANDSHAKE_VERSION, versionEnum);
+    AccumulateCipherSuite(
+      infoObject->IsFullHandshake() ? Telemetry::SSL_CIPHER_SUITE_FULL
+                                    : Telemetry::SSL_CIPHER_SUITE_RESUMED,
+      channelInfo);
+
+    SSLCipherSuiteInfo cipherInfo;
+    rv = SSL_GetCipherSuiteInfo(channelInfo.cipherSuite, &cipherInfo,
+                                sizeof cipherInfo);
+    MOZ_ASSERT(rv == SECSuccess);
+    if (rv == SECSuccess) {
+      weakEncryption =
+        (channelInfo.protocolVersion <= SSL_LIBRARY_VERSION_3_0) ||
+        (cipherInfo.symCipher == ssl_calg_rc4);
+
+      // keyExchange null=0, rsa=1, dh=2, fortezza=3, ecdh=4
+      Telemetry::Accumulate(
+        infoObject->IsFullHandshake()
+          ? Telemetry::SSL_KEY_EXCHANGE_ALGORITHM_FULL
+          : Telemetry::SSL_KEY_EXCHANGE_ALGORITHM_RESUMED,
+        cipherInfo.keaType);
+
+      DebugOnly<int16_t> KEAUsed;
+      MOZ_ASSERT(NS_SUCCEEDED(infoObject->GetKEAUsed(&KEAUsed)) &&
+                 (KEAUsed == cipherInfo.keaType));
+
+      if (infoObject->IsFullHandshake()) {
+        switch (cipherInfo.keaType) {
+          case ssl_kea_rsa:
+            AccumulateNonECCKeySize(Telemetry::SSL_KEA_RSA_KEY_SIZE_FULL,
+                                    channelInfo.keaKeyBits);
+            break;
+          case ssl_kea_dh:
+            AccumulateNonECCKeySize(Telemetry::SSL_KEA_DHE_KEY_SIZE_FULL,
+                                    channelInfo.keaKeyBits);
+            break;
+          case ssl_kea_ecdh:
+            AccumulateECCCurve(Telemetry::SSL_KEA_ECDHE_CURVE_FULL,
+                               channelInfo.keaKeyBits);
+            break;
+          default:
+            MOZ_CRASH("impossible KEA");
+            break;
+        }
+
+        Telemetry::Accumulate(Telemetry::SSL_AUTH_ALGORITHM_FULL,
+                              cipherInfo.authAlgorithm);
+
+        // RSA key exchange doesn't use a signature for auth.
+        if (cipherInfo.keaType != ssl_kea_rsa) {
+          switch (cipherInfo.authAlgorithm) {
+            case ssl_auth_rsa:
+              AccumulateNonECCKeySize(Telemetry::SSL_AUTH_RSA_KEY_SIZE_FULL,
+                                      channelInfo.authKeyBits);
+              break;
+            case ssl_auth_dsa:
+              AccumulateNonECCKeySize(Telemetry::SSL_AUTH_DSA_KEY_SIZE_FULL,
+                                      channelInfo.authKeyBits);
+              break;
+            case ssl_auth_ecdsa:
+              AccumulateECCCurve(Telemetry::SSL_AUTH_ECDSA_CURVE_FULL,
+                                 channelInfo.authKeyBits);
+              break;
+            default:
+              MOZ_CRASH("impossible auth algorithm");
+              break;
+          }
+        }
+      }
+
+      Telemetry::Accumulate(
+          infoObject->IsFullHandshake()
+            ? Telemetry::SSL_SYMMETRIC_CIPHER_FULL
+            : Telemetry::SSL_SYMMETRIC_CIPHER_RESUMED,
+          cipherInfo.symCipher);
+    }
+  }
+
   PRBool siteSupportsSafeRenego;
   rv = SSL_HandshakeNegotiatedExtension(fd, ssl_renegotiation_info_xtn,
                                         &siteSupportsSafeRenego);
   MOZ_ASSERT(rv == SECSuccess);
   if (rv != SECSuccess) {
     siteSupportsSafeRenego = false;
   }
 
-  if (siteSupportsSafeRenego ||
-      !ioLayerHelpers.treatUnsafeNegotiationAsBroken()) {
+  if (!weakEncryption &&
+      (siteSupportsSafeRenego ||
+       !ioLayerHelpers.treatUnsafeNegotiationAsBroken())) {
     infoObject->SetSecurityState(nsIWebProgressListener::STATE_IS_SECURE |
                                  nsIWebProgressListener::STATE_SECURE_HIGH);
   } else {
     infoObject->SetSecurityState(nsIWebProgressListener::STATE_IS_BROKEN);
   }
 
   // XXX Bug 883674: We shouldn't be formatting messages here in PSM; instead,
   // we should set a flag on the channel that higher (UI) level code can check
@@ -1241,92 +1328,11 @@ void HandshakeCallback(PRFileDesc* fd, v
     }
     else {
       PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
               ("HandshakeCallback using NEW cert %p\n", nssc.get()));
       status->mServerCert = nssc;
     }
   }
 
-  SSLChannelInfo channelInfo;
-  rv = SSL_GetChannelInfo(fd, &channelInfo, sizeof(channelInfo));
-  MOZ_ASSERT(rv == SECSuccess);
-  if (rv == SECSuccess) {
-    // Get the protocol version for telemetry
-    // 0=ssl3, 1=tls1, 2=tls1.1, 3=tls1.2
-    unsigned int versionEnum = channelInfo.protocolVersion & 0xFF;
-    Telemetry::Accumulate(Telemetry::SSL_HANDSHAKE_VERSION, versionEnum);
-    AccumulateCipherSuite(
-      infoObject->IsFullHandshake() ? Telemetry::SSL_CIPHER_SUITE_FULL
-                                    : Telemetry::SSL_CIPHER_SUITE_RESUMED,
-      channelInfo);
-
-    SSLCipherSuiteInfo cipherInfo;
-    rv = SSL_GetCipherSuiteInfo(channelInfo.cipherSuite, &cipherInfo,
-                                sizeof cipherInfo);
-    MOZ_ASSERT(rv == SECSuccess);
-    if (rv == SECSuccess) {
-      // keyExchange null=0, rsa=1, dh=2, fortezza=3, ecdh=4
-      Telemetry::Accumulate(
-        infoObject->IsFullHandshake()
-          ? Telemetry::SSL_KEY_EXCHANGE_ALGORITHM_FULL
-          : Telemetry::SSL_KEY_EXCHANGE_ALGORITHM_RESUMED,
-        cipherInfo.keaType);
-
-      DebugOnly<int16_t> KEAUsed;
-      MOZ_ASSERT(NS_SUCCEEDED(infoObject->GetKEAUsed(&KEAUsed)) &&
-                 (KEAUsed == cipherInfo.keaType));
-
-      if (infoObject->IsFullHandshake()) {
-        switch (cipherInfo.keaType) {
-          case ssl_kea_rsa:
-            AccumulateNonECCKeySize(Telemetry::SSL_KEA_RSA_KEY_SIZE_FULL,
-                                    channelInfo.keaKeyBits);
-            break;
-          case ssl_kea_dh:
-            AccumulateNonECCKeySize(Telemetry::SSL_KEA_DHE_KEY_SIZE_FULL,
-                                    channelInfo.keaKeyBits);
-            break;
-          case ssl_kea_ecdh:
-            AccumulateECCCurve(Telemetry::SSL_KEA_ECDHE_CURVE_FULL,
-                               channelInfo.keaKeyBits);
-            break;
-          default:
-            MOZ_CRASH("impossible KEA");
-            break;
-        }
-
-        Telemetry::Accumulate(Telemetry::SSL_AUTH_ALGORITHM_FULL,
-                              cipherInfo.authAlgorithm);
-
-        // RSA key exchange doesn't use a signature for auth.
-        if (cipherInfo.keaType != ssl_kea_rsa) {
-          switch (cipherInfo.authAlgorithm) {
-            case ssl_auth_rsa:
-              AccumulateNonECCKeySize(Telemetry::SSL_AUTH_RSA_KEY_SIZE_FULL,
-                                      channelInfo.authKeyBits);
-              break;
-            case ssl_auth_dsa:
-              AccumulateNonECCKeySize(Telemetry::SSL_AUTH_DSA_KEY_SIZE_FULL,
-                                      channelInfo.authKeyBits);
-              break;
-            case ssl_auth_ecdsa:
-              AccumulateECCCurve(Telemetry::SSL_AUTH_ECDSA_CURVE_FULL,
-                                 channelInfo.authKeyBits);
-              break;
-            default:
-              MOZ_CRASH("impossible auth algorithm");
-              break;
-          }
-        }
-      }
-
-      Telemetry::Accumulate(
-          infoObject->IsFullHandshake()
-            ? Telemetry::SSL_SYMMETRIC_CIPHER_FULL
-            : Telemetry::SSL_SYMMETRIC_CIPHER_RESUMED,
-          cipherInfo.symCipher);
-    }
-  }
-
   infoObject->NoteTimeUntilReady();
   infoObject->SetHandshakeCompleted();
 }
--- a/xpcom/base/nsAutoPtr.h
+++ b/xpcom/base/nsAutoPtr.h
@@ -402,39 +402,16 @@ template <class T>
 inline bool
 operator!=(NSCAP_Zero* aLhs, const nsAutoPtr<T>& aRhs)
 // specifically to allow |0 != smartPtr|
 {
   return reinterpret_cast<const void*>(aLhs) != static_cast<const void*>(aRhs.get());
 }
 
 
-#ifdef HAVE_CPP_TROUBLE_COMPARING_TO_ZERO
-
-// We need to explicitly define comparison operators for `int'
-// because the compiler is lame.
-
-template <class T>
-inline bool
-operator==(const nsAutoPtr<T>& aLhs, int aRhs)
-// specifically to allow |smartPtr == 0|
-{
-  return static_cast<const void*>(aLhs.get()) == reinterpret_cast<const void*>(aRhs);
-}
-
-template <class T>
-inline bool
-operator==(int aLhs, const nsAutoPtr<T>& aRhs)
-// specifically to allow |0 == smartPtr|
-{
-  return reinterpret_cast<const void*>(aLhs) == static_cast<const void*>(aRhs.get());
-}
-
-#endif // !defined(HAVE_CPP_TROUBLE_COMPARING_TO_ZERO)
-
 /*****************************************************************************/
 
 // template <class T> class nsAutoArrayPtrGetterTransfers;
 
 template <class T>
 class nsAutoArrayPtr
 {
 private:
@@ -777,39 +754,16 @@ template <class T>
 inline bool
 operator!=(NSCAP_Zero* aLhs, const nsAutoArrayPtr<T>& aRhs)
 // specifically to allow |0 != smartPtr|
 {
   return reinterpret_cast<const void*>(aLhs) != static_cast<const void*>(aRhs.get());
 }
 
 
-#ifdef HAVE_CPP_TROUBLE_COMPARING_TO_ZERO
-
-// We need to explicitly define comparison operators for `int'
-// because the compiler is lame.
-
-template <class T>
-inline bool
-operator==(const nsAutoArrayPtr<T>& aLhs, int aRhs)
-// specifically to allow |smartPtr == 0|
-{
-  return static_cast<const void*>(aLhs.get()) == reinterpret_cast<const void*>(aRhs);
-}
-
-template <class T>
-inline bool
-operator==(int aLhs, const nsAutoArrayPtr<T>& aRhs)
-// specifically to allow |0 == smartPtr|
-{
-  return reinterpret_cast<const void*>(aLhs) == static_cast<const void*>(aRhs.get());
-}
-
-#endif // !defined(HAVE_CPP_TROUBLE_COMPARING_TO_ZERO)
-
 /*****************************************************************************/
 
 template<class T>
 class nsQueryObject : public nsCOMPtr_helper
 {
 public:
   explicit nsQueryObject(T* aRawPtr)
     : mRawPtr(aRawPtr)
--- a/xpcom/base/nsRefPtr.h
+++ b/xpcom/base/nsRefPtr.h
@@ -497,39 +497,16 @@ template <class T>
 inline bool
 operator!=(NSCAP_Zero* aLhs, const nsRefPtr<T>& aRhs)
 // specifically to allow |0 != smartPtr|
 {
   return reinterpret_cast<const void*>(aLhs) != static_cast<const void*>(aRhs.get());
 }
 
 
-#ifdef HAVE_CPP_TROUBLE_COMPARING_TO_ZERO
-
-// We need to explicitly define comparison operators for `int'
-// because the compiler is lame.
-
-template <class T>
-inline bool
-operator==(const nsRefPtr<T>& aLhs, int aRhs)
-// specifically to allow |smartPtr == 0|
-{
-  return static_cast<const void*>(aLhs.get()) == reinterpret_cast<const void*>(aRhs);
-}
-
-template <class T>
-inline bool
-operator==(int aLhs, const nsRefPtr<T>& aRhs)
-// specifically to allow |0 == smartPtr|
-{
-  return reinterpret_cast<const void*>(aLhs) == static_cast<const void*>(aRhs.get());
-}
-
-#endif // !defined(HAVE_CPP_TROUBLE_COMPARING_TO_ZERO)
-
 template <class SourceType, class DestinationType>
 inline nsresult
 CallQueryInterface(nsRefPtr<SourceType>& aSourcePtr, DestinationType** aDestPtr)
 {
   return CallQueryInterface(aSourcePtr.get(), aDestPtr);
 }
 
 /*****************************************************************************/
--- a/xpcom/glue/Monitor.h
+++ b/xpcom/glue/Monitor.h
@@ -91,17 +91,16 @@ public:
   nsresult Notify() { return mMonitor->Notify(); }
   nsresult NotifyAll() { return mMonitor->NotifyAll(); }
 
 private:
   MonitorAutoLock();
   MonitorAutoLock(const MonitorAutoLock&);
   MonitorAutoLock& operator=(const MonitorAutoLock&);
   static void* operator new(size_t) CPP_THROW_NEW;
-  static void operator delete(void*);
 
   Monitor* mMonitor;
 };
 
 /**
  * Unlock the monitor for the lexical scope instances of this class
  * are bound to (except for MonitorAutoLock in nested scopes).
  *
@@ -122,16 +121,15 @@ public:
     mMonitor->Lock();
   }
 
 private:
   MonitorAutoUnlock();
   MonitorAutoUnlock(const MonitorAutoUnlock&);
   MonitorAutoUnlock& operator=(const MonitorAutoUnlock&);
   static void* operator new(size_t) CPP_THROW_NEW;
-  static void operator delete(void*);
 
   Monitor* mMonitor;
 };
 
 } // namespace mozilla
 
 #endif // mozilla_Monitor_h
--- a/xpcom/glue/Mutex.h
+++ b/xpcom/glue/Mutex.h
@@ -169,17 +169,16 @@ public:
     mLock->Unlock();
   }
 
 private:
   BaseAutoLock();
   BaseAutoLock(BaseAutoLock&);
   BaseAutoLock& operator=(BaseAutoLock&);
   static void* operator new(size_t) CPP_THROW_NEW;
-  static void operator delete(void*);
 
   T* mLock;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 typedef BaseAutoLock<Mutex> MutexAutoLock;
 typedef BaseAutoLock<OffTheBooksMutex> OffTheBooksMutexAutoLock;
 
@@ -207,17 +206,16 @@ public:
     mLock->Lock();
   }
 
 private:
   BaseAutoUnlock();
   BaseAutoUnlock(BaseAutoUnlock&);
   BaseAutoUnlock& operator=(BaseAutoUnlock&);
   static void* operator new(size_t) CPP_THROW_NEW;
-  static void operator delete(void*);
 
   T* mLock;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 typedef BaseAutoUnlock<Mutex> MutexAutoUnlock;
 typedef BaseAutoUnlock<OffTheBooksMutex> OffTheBooksMutexAutoUnlock;
 
--- a/xpcom/glue/ReentrantMonitor.h
+++ b/xpcom/glue/ReentrantMonitor.h
@@ -193,17 +193,16 @@ public:
   nsresult Notify() { return mReentrantMonitor->Notify(); }
   nsresult NotifyAll() { return mReentrantMonitor->NotifyAll(); }
 
 private:
   ReentrantMonitorAutoEnter();
   ReentrantMonitorAutoEnter(const ReentrantMonitorAutoEnter&);
   ReentrantMonitorAutoEnter& operator=(const ReentrantMonitorAutoEnter&);
   static void* operator new(size_t) CPP_THROW_NEW;
-  static void operator delete(void*);
 
   mozilla::ReentrantMonitor* mReentrantMonitor;
 };
 
 /**
  * ReentrantMonitorAutoExit
  * Exit the ReentrantMonitor when it enters scope, and enters it when it leaves
  * scope.
@@ -235,17 +234,16 @@ public:
     mReentrantMonitor->Enter();
   }
 
 private:
   ReentrantMonitorAutoExit();
   ReentrantMonitorAutoExit(const ReentrantMonitorAutoExit&);
   ReentrantMonitorAutoExit& operator=(const ReentrantMonitorAutoExit&);
   static void* operator new(size_t) CPP_THROW_NEW;
-  static void operator delete(void*);
 
   ReentrantMonitor* mReentrantMonitor;
 };
 
 } // namespace mozilla
 
 
 #endif // ifndef mozilla_ReentrantMonitor_h
--- a/xpcom/glue/nsCOMPtr.h
+++ b/xpcom/glue/nsCOMPtr.h
@@ -1477,39 +1477,16 @@ operator!=(const nsCOMPtr<T>& aLhs, NSCA
 template<class T>
 inline bool
 operator!=(NSCAP_Zero* aLhs, const nsCOMPtr<T>& aRhs)
 {
   return reinterpret_cast<const void*>(aLhs) != static_cast<const void*>(aRhs.get());
 }
 
 
-#ifdef HAVE_CPP_TROUBLE_COMPARING_TO_ZERO
-
-// We need to explicitly define comparison operators for `int'
-// because the compiler is lame.
-
-// Specifically to allow |smartPtr == 0|.
-template<class T>
-inline bool
-operator==(const nsCOMPtr<T>& lhs, int rhs)
-{
-  return static_cast<const void*>(lhs.get()) == reinterpret_cast<const void*>(rhs);
-}
-
-// Specifically to allow |0 == smartPtr|.
-template<class T>
-inline bool
-operator==(int lhs, const nsCOMPtr<T>& rhs)
-{
-  return reinterpret_cast<const void*>(lhs) == static_cast<const void*>(rhs.get());
-}
-
-#endif // !defined(HAVE_CPP_TROUBLE_COMPARING_TO_ZERO)
-
 // Comparing any two [XP]COM objects for identity
 
 inline bool
 SameCOMIdentity(nsISupports* aLhs, nsISupports* aRhs)
 {
   return nsCOMPtr<nsISupports>(do_QueryInterface(aLhs)) ==
     nsCOMPtr<nsISupports>(do_QueryInterface(aRhs));
 }
--- a/xpcom/string/nsTSubstring.cpp
+++ b/xpcom/string/nsTSubstring.cpp
@@ -287,17 +287,18 @@ nsTSubstring_CharT::Assign(const char_ty
     NS_ABORT_OOM(char_traits::length(aData));
   }
 }
 
 void
 nsTSubstring_CharT::Assign(const char_type* aData, size_type aLength)
 {
   if (!Assign(aData, aLength, fallible_t())) {
-    NS_ABORT_OOM(aLength);
+    NS_ABORT_OOM(aLength == size_type(-1) ? char_traits::length(aData)
+                                          : aLength);
   }
 }
 
 bool
 nsTSubstring_CharT::Assign(const char_type* aData, size_type aLength,
                            const fallible_t&)
 {
   if (!aData || aLength == 0) {
--- a/xpcom/xpcom-config.h.in
+++ b/xpcom/xpcom-config.h.in
@@ -13,21 +13,16 @@
 #undef CPP_THROW_NEW
 
 /* Define if the c++ compiler can resolve ambiguity with |using| */
 #undef HAVE_CPP_AMBIGUITY_RESOLVING_USING
 
 /* Define if a dyanmic_cast to void* gives the most derived object */
 #undef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
 
-/* Define if the c++ compiler has trouble comparing a constant
- * reference to a templatized class to zero
- */
-#undef HAVE_CPP_TROUBLE_COMPARING_TO_ZERO
-
 /* Define if the c++ compiler requires implementations of 
  * unused virtual methods
  */
 #undef NEED_CPP_UNUSED_IMPLEMENTATIONS
 
 /* Define to either __attribute__((malloc)) or nothing */
 #undef NS_ATTR_MALLOC