merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 26 Oct 2015 10:57:35 +0100
changeset 304613 5ca03a00d26823ce91ee0eaa2937bed605bd53c1
parent 304518 474114740f0e95faf0b7a123ffbe9ea372247b1b (current diff)
parent 304612 3d02436df8427f61b2fddb0139ee6dd6512fb368 (diff)
child 304614 88dbb4b409401e8ad499ff5cdb72bb636dcd7707
child 304619 907afb78160656c333e889b71be3eb2ed22a3c5f
child 304630 be8c1d82d891a2492d392d11f13589a9c634a851
child 304662 f34a035657d6672025b3fc15d21cdbe329eb1826
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone44.0a1
first release with
nightly linux32
5ca03a00d268 / 44.0a1 / 20151026030422 / files
nightly linux64
5ca03a00d268 / 44.0a1 / 20151026030422 / files
nightly mac
5ca03a00d268 / 44.0a1 / 20151026030422 / files
nightly win32
5ca03a00d268 / 44.0a1 / 20151026030422 / files
nightly win64
5ca03a00d268 / 44.0a1 / 20151026030422 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
dom/events/EventListenerManager.cpp
layout/base/nsPresShell.cpp
layout/base/nsRefreshDriver.cpp
modules/libpref/init/all.js
security/nss/tests/ssl_gtests/ssl_gtests.sh
testing/web-platform/meta/dom/nodes/Document-createElement-namespace.html.ini
testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/div-align-ref.html
testing/web-platform/tests/html/rendering/non-replaced-elements/flow-content-0/div-align.html
testing/web-platform/tests/subresource-integrity/subresource-integrity.sub.html
testing/web-platform/tests/webstorage/storage_supported_property_names.html
toolkit/themes/linux/mozapps/jar.mn
toolkit/themes/osx/mozapps/jar.mn
toolkit/themes/windows/mozapps/jar.mn
new file mode 100644
--- /dev/null
+++ b/accessible/tests/crashtests/448064.xhtml
@@ -0,0 +1,73 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+</head>
+<body>
+<div id="mw_b">
+<div id="mw_f">
+<div id="mw_g" style="display: none;"/>
+</div>
+</div>
+
+<div id="mw_c" style="display: none;">
+<div id="mw_d">
+<div id="mw_e"></div>
+</div>
+</div>
+
+<input id="mw_a"/>
+
+
+<script>
+function dumpAccessibleNode(aNode, level) {
+	var msg = "";
+	
+	try {
+		msg += "name=\"" + aNode.name + "\" ";
+	} catch (e) {
+		msg += " noName ";
+	}
+	
+	dump(msg + '\n');
+}
+
+
+function dumpAccessibleTree(aNode, level) {
+netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+	level = level || 0;
+
+	dumpAccessibleNode(aNode, level);
+	try {	
+		var child = aNode.firstChild;
+		while (child) {
+			dumpAccessibleTree(child, level + 1);
+			child = child.nextSibling;
+		}
+	} catch (e) {
+		dump("Error visiting child nodes: " + e + '\n');
+	}
+}
+
+function A(o) { 
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+  var acc = Components.classes['@mozilla.org/accessibleRetrieval;1']
+                         .getService(Components.interfaces.nsIAccessibleRetrieval);
+  return acc.getAccessibleFor(o);
+}
+
+function beginAccessible() {
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+  dumpAccessibleTree(A(document),0);
+}
+setTimeout(beginAccessible, 100);
+
+
+setTimeout(doe, 200);
+function doe() {
+   document.getElementById('mw_a').appendChild(document.getElementById('mw_b'));
+   document.getElementById('mw_c').appendChild(document.getElementById('mw_d'));
+   document.getElementById('mw_e').appendChild(document.getElementById('mw_f')); 
+   document.getElementById('mw_g').appendChild(document.getElementById('mw_b')); 
+}
+</script>
+</body>
+</html>
\ No newline at end of file
--- a/accessible/tests/crashtests/crashtests.list
+++ b/accessible/tests/crashtests/crashtests.list
@@ -1,2 +1,3 @@
-# Disabled because it causes assertions/crashes in later crashtests, see bug 918246
+# Disabled because they cause assertions/crashes in later crashtests, see bug 918246
+skip load 448064.xhtml
 skip load 471493.xul
--- a/addon-sdk/source/test/addons/e10s-content/lib/test-content-worker.js
+++ b/addon-sdk/source/test/addons/e10s-content/lib/test-content-worker.js
@@ -480,17 +480,17 @@ exports["test:setInterval async Errors p
     let count = 0;
     let worker = Worker({
       window: browser.contentWindow,
       contentScript: "setInterval(() => { throw Error('ubik') }, 50)",
       contentScriptWhen: "ready",
       onError: function(err) {
         count++;
         assert.equal(err.message, "ubik",
-            "error (corectly) propagated  " + count + " time(s)");
+            "error (correctly) propagated  " + count + " time(s)");
         if (count >= 3) done();
       }
     });
   }
 );
 
 exports["test:setTimeout throws array, passed to .onError"] = WorkerTest(
   DEFAULT_CONTENT_URL,
--- a/addon-sdk/source/test/test-content-sync-worker.js
+++ b/addon-sdk/source/test/test-content-sync-worker.js
@@ -472,17 +472,17 @@ exports["test:setInterval async Errors p
     let count = 0;
     let worker = Worker({
       window: browser.contentWindow,
       contentScript: "setInterval(() => { throw Error('ubik') }, 50)",
       contentScriptWhen: "ready",
       onError: function(err) {
         count++;
         assert.equal(err.message, "ubik",
-            "error (corectly) propagated  " + count + " time(s)");
+            "error (correctly) propagated  " + count + " time(s)");
         if (count >= 3) done();
       }
     });
   }
 );
 
 exports["test:setTimeout throws array, passed to .onError"] = WorkerTest(
   DEFAULT_CONTENT_URL,
--- a/addon-sdk/source/test/test-content-worker.js
+++ b/addon-sdk/source/test/test-content-worker.js
@@ -480,17 +480,17 @@ exports["test:setInterval async Errors p
     let count = 0;
     let worker = Worker({
       window: browser.contentWindow,
       contentScript: "setInterval(() => { throw Error('ubik') }, 50)",
       contentScriptWhen: "ready",
       onError: function(err) {
         count++;
         assert.equal(err.message, "ubik",
-            "error (corectly) propagated  " + count + " time(s)");
+            "error (correctly) propagated  " + count + " time(s)");
         if (count >= 3) done();
       }
     });
   }
 );
 
 exports["test:setTimeout throws array, passed to .onError"] = WorkerTest(
   DEFAULT_CONTENT_URL,
--- a/b2g/components/UpdatePrompt.js
+++ b/b2g/components/UpdatePrompt.js
@@ -426,17 +426,17 @@ UpdatePrompt.prototype = {
   },
 
   _copyProperties: ["appVersion", "buildID", "detailsURL", "displayVersion",
                     "errorCode", "isOSUpdate", "platformVersion",
                     "previousAppVersion", "state", "statusText"],
 
   sendUpdateEvent: function UP_sendUpdateEvent(aType, aUpdate) {
     let detail = {};
-    for each (let property in this._copyProperties) {
+    for (let property of this._copyProperties) {
       detail[property] = aUpdate[property];
     }
 
     let patch = aUpdate.selectedPatch;
     if (!patch && aUpdate.patchCount > 0) {
       // For now we just check the first patch to get size information if a
       // patch hasn't been selected yet.
       patch = aUpdate.getPatchAt(0);
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1640,15 +1640,17 @@ pref("view_source.tab", true);
 // Enable ServiceWorkers for Push API consumers.
 // Interception is still disabled on beta and release.
 pref("dom.serviceWorkers.enabled", true);
 
 pref("dom.serviceWorkers.interception.enabled", true);
 
 // Enable Push API.
 pref("dom.push.enabled", true);
+
+pref("dom.serviceWorkers.openWindow.enabled", true);
 #endif
 
 // These are the thumbnail width/height set in about:newtab.
 // If you change this, ENSURE IT IS THE SAME SIZE SET
 // by about:newtab. These values are in CSS pixels.
 pref("toolkit.pageThumbs.minWidth", 280);
 pref("toolkit.pageThumbs.minHeight", 190);
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -314,36 +314,16 @@
            noautofocus="true"/>
 
     <panel id="loop-panel"
            class="loop-panel social-panel"
            type="arrow"
            orient="horizontal"
            hidden="true"/>
 
-    <menupopup id="processHangOptions"
-               onpopupshowing="ProcessHangMonitor.refreshMenu(window);">
-      <menuitem id="processHangTerminateScript"
-                oncommand="ProcessHangMonitor.terminateScript(window)"
-                accesskey="&processHang.terminateScript.accessKey;"
-                label="&processHang.terminateScript.label;"/>
-      <menuitem id="processHangDebugScript"
-                oncommand="ProcessHangMonitor.debugScript(window)"
-                accesskey="&processHang.debugScript.accessKey;"
-                label="&processHang.debugScript.label;"/>
-      <menuitem id="processHangTerminatePlugin"
-                oncommand="ProcessHangMonitor.terminatePlugin(window)"
-                accesskey="&processHang.terminatePlugin.accessKey;"
-                label="&processHang.terminatePlugin.label;"/>
-      <menuitem id="processHangTerminateProcess"
-                oncommand="ProcessHangMonitor.terminateProcess(window)"
-                accesskey="&processHang.terminateProcess.accessKey;"
-                label="&processHang.terminateProcess.label;"/>
-    </menupopup>
-
     <menupopup id="toolbar-context-menu"
                onpopupshowing="onViewToolbarsPopupShowing(event, document.getElementById('viewToolbarsMenuSeparator'));">
       <menuitem oncommand="gCustomizeMode.addToPanel(document.popupNode)"
                 accesskey="&customizeMenu.moveToPanel.accesskey;"
                 label="&customizeMenu.moveToPanel.label;"
                 contexttype="toolbaritem"
                 class="customize-context-moveToPanel"/>
       <menuitem oncommand="gCustomizeMode.removeFromArea(document.popupNode)"
--- a/browser/config/mozconfigs/linux32/beta
+++ b/browser/config/mozconfigs/linux32/beta
@@ -1,9 +1,13 @@
 MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1}
+if [ -n "$ENABLE_RELEASE_PROMOTION" ]; then
+  MOZ_AUTOMATION_UPLOAD_SYMBOLS=1
+  MOZ_AUTOMATION_UPDATE_PACKAGING=1
+fi
 
 . "$topsrcdir/browser/config/mozconfigs/linux32/common-opt"
 
 ac_add_options --enable-official-branding
 ac_add_options --enable-verify-mar
 
 mk_add_options MOZ_PGO=1
 
--- a/browser/config/mozconfigs/linux32/release
+++ b/browser/config/mozconfigs/linux32/release
@@ -1,12 +1,17 @@
 # This make file should be identical to the beta mozconfig, apart from the
 # safeguard below
 MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1}
 
+if [ -n "$ENABLE_RELEASE_PROMOTION" ]; then
+  MOZ_AUTOMATION_UPLOAD_SYMBOLS=1
+  MOZ_AUTOMATION_UPDATE_PACKAGING=1
+fi
+
 . "$topsrcdir/browser/config/mozconfigs/linux32/common-opt"
 
 ac_add_options --enable-official-branding
 ac_add_options --enable-verify-mar
 
 mk_add_options MOZ_PGO=1
 
 # safeguard against someone forgetting to re-set EARLY_BETA_OR_EARLIER in
--- a/browser/config/mozconfigs/linux64/beta
+++ b/browser/config/mozconfigs/linux64/beta
@@ -1,9 +1,13 @@
 MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1}
+if [ -n "$ENABLE_RELEASE_PROMOTION" ]; then
+  MOZ_AUTOMATION_UPLOAD_SYMBOLS=1
+  MOZ_AUTOMATION_UPDATE_PACKAGING=1
+fi
 
 . "$topsrcdir/browser/config/mozconfigs/linux64/common-opt"
 
 ac_add_options --enable-official-branding
 ac_add_options --enable-verify-mar
 
 mk_add_options MOZ_PGO=1
 
--- a/browser/config/mozconfigs/linux64/release
+++ b/browser/config/mozconfigs/linux64/release
@@ -1,12 +1,17 @@
 # This make file should be identical to the beta mozconfig, apart from the
 # safeguard below
 MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1}
 
+if [ -n "$ENABLE_RELEASE_PROMOTION" ]; then
+  MOZ_AUTOMATION_UPLOAD_SYMBOLS=1
+  MOZ_AUTOMATION_UPDATE_PACKAGING=1
+fi
+
 . "$topsrcdir/browser/config/mozconfigs/linux64/common-opt"
 
 ac_add_options --enable-official-branding
 ac_add_options --enable-verify-mar
 
 mk_add_options MOZ_PGO=1
 
 # safeguard against someone forgetting to re-set EARLY_BETA_OR_EARLIER in
--- a/browser/config/mozconfigs/macosx-universal/beta
+++ b/browser/config/mozconfigs/macosx-universal/beta
@@ -1,9 +1,14 @@
 MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1}
 
+if [ -n "$ENABLE_RELEASE_PROMOTION" ]; then
+  MOZ_AUTOMATION_UPLOAD_SYMBOLS=1
+  MOZ_AUTOMATION_UPDATE_PACKAGING=1
+fi
+
 . "$topsrcdir/browser/config/mozconfigs/macosx-universal/common-opt"
 
 ac_add_options --enable-official-branding
 ac_add_options --enable-verify-mar
 
 . "$topsrcdir/build/mozconfig.common.override"
 . "$topsrcdir/build/mozconfig.cache"
--- a/browser/config/mozconfigs/macosx-universal/release
+++ b/browser/config/mozconfigs/macosx-universal/release
@@ -1,12 +1,17 @@
 # This make file should be identical to the beta mozconfig, apart from the
 # safeguard below
 MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1}
 
+if [ -n "$ENABLE_RELEASE_PROMOTION" ]; then
+  MOZ_AUTOMATION_UPLOAD_SYMBOLS=1
+  MOZ_AUTOMATION_UPDATE_PACKAGING=1
+fi
+
 . "$topsrcdir/browser/config/mozconfigs/macosx-universal/common-opt"
 
 ac_add_options --enable-official-branding
 ac_add_options --enable-verify-mar
 
 # safeguard against someone forgetting to re-set EARLY_BETA_OR_EARLIER in
 # defines.sh during the beta cycle
 export BUILDING_RELEASE=1
--- a/browser/config/mozconfigs/whitelist
+++ b/browser/config/mozconfigs/whitelist
@@ -68,16 +68,20 @@ whitelist['nightly']['win64'] += [
 ]
 
 for platform in all_platforms:
     whitelist['release'][platform] = [
         'ac_add_options --enable-update-channel=release',
         'ac_add_options --enable-official-branding',
         'mk_add_options MOZ_MAKE_FLAGS="-j4"',
         'export BUILDING_RELEASE=1',
+        'if [ -n "$ENABLE_RELEASE_PROMOTION" ]; then',
+        'MOZ_AUTOMATION_UPLOAD_SYMBOLS=1',
+        'MOZ_AUTOMATION_UPDATE_PACKAGING=1',
+        'fi',
         'MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1}',
     ]
 whitelist['release']['win32'] += ['mk_add_options MOZ_PGO=1']
 whitelist['release']['win64'] += ['mk_add_options MOZ_PGO=1']
 
 whitelist['release']['linux32'] += [
     'export MOZILLA_OFFICIAL=1',
     'export MOZ_TELEMETRY_REPORTING=1',
--- a/browser/config/mozconfigs/win32/beta
+++ b/browser/config/mozconfigs/win32/beta
@@ -1,10 +1,15 @@
 MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1}
 
+if [ -n "$ENABLE_RELEASE_PROMOTION" ]; then
+  MOZ_AUTOMATION_UPLOAD_SYMBOLS=1
+  MOZ_AUTOMATION_UPDATE_PACKAGING=1
+fi
+
 . "$topsrcdir/build/mozconfig.win-common"
 . "$topsrcdir/browser/config/mozconfigs/win32/common-opt"
 
 mk_add_options MOZ_PGO=1
 
 ac_add_options --enable-official-branding
 ac_add_options --enable-verify-mar
 
--- a/browser/config/mozconfigs/win32/release
+++ b/browser/config/mozconfigs/win32/release
@@ -1,12 +1,17 @@
 # This make file should be identical to the beta mozconfig, apart from the
 # safeguard below
 MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1}
 
+if [ -n "$ENABLE_RELEASE_PROMOTION" ]; then
+  MOZ_AUTOMATION_UPLOAD_SYMBOLS=1
+  MOZ_AUTOMATION_UPDATE_PACKAGING=1
+fi
+
 . "$topsrcdir/build/mozconfig.win-common"
 . "$topsrcdir/browser/config/mozconfigs/win32/common-opt"
 
 mk_add_options MOZ_PGO=1
 
 ac_add_options --enable-official-branding
 ac_add_options --enable-verify-mar
 
--- a/browser/config/mozconfigs/win64/beta
+++ b/browser/config/mozconfigs/win64/beta
@@ -1,10 +1,15 @@
 MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1}
 
+if [ -n "$ENABLE_RELEASE_PROMOTION" ]; then
+  MOZ_AUTOMATION_UPLOAD_SYMBOLS=1
+  MOZ_AUTOMATION_UPDATE_PACKAGING=1
+fi
+
 . "$topsrcdir/build/mozconfig.win-common"
 . "$topsrcdir/browser/config/mozconfigs/win64/common-win64"
 . "$topsrcdir/browser/config/mozconfigs/win64/common-opt"
 
 mk_add_options MOZ_PGO=1
 
 ac_add_options --enable-official-branding
 ac_add_options --enable-verify-mar
--- a/browser/config/mozconfigs/win64/release
+++ b/browser/config/mozconfigs/win64/release
@@ -1,12 +1,17 @@
 # This make file should be identical to the beta mozconfig, apart from the
 # safeguard below
 MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1}
 
+if [ -n "$ENABLE_RELEASE_PROMOTION" ]; then
+  MOZ_AUTOMATION_UPLOAD_SYMBOLS=1
+  MOZ_AUTOMATION_UPDATE_PACKAGING=1
+fi
+
 . "$topsrcdir/build/mozconfig.win-common"
 . "$topsrcdir/browser/config/mozconfigs/win64/common-win64"
 . "$topsrcdir/browser/config/mozconfigs/win64/common-opt"
 
 mk_add_options MOZ_PGO=1
 
 ac_add_options --enable-official-branding
 ac_add_options --enable-verify-mar
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -920,25 +920,16 @@ you can use these alternative items. Oth
 
 <!ENTITY panicButton.view.undoWarning             "This action cannot be undone.">
 <!ENTITY panicButton.view.forgetButton            "Forget!">
 
 <!ENTITY panicButton.thankyou.msg1                "Your recent history is cleared.">
 <!ENTITY panicButton.thankyou.msg2                "Safe browsing!">
 <!ENTITY panicButton.thankyou.buttonlabel         "Thanks!">
 
-<!ENTITY processHang.terminateScript.label        "Stop Script">
-<!ENTITY processHang.terminateScript.accessKey    "S">
-<!ENTITY processHang.debugScript.label            "Debug Script">
-<!ENTITY processHang.debugScript.accessKey        "D">
-<!ENTITY processHang.terminatePlugin.label        "Kill Plugin">
-<!ENTITY processHang.terminatePlugin.accessKey    "P">
-<!ENTITY processHang.terminateProcess.label       "Kill Web Process">
-<!ENTITY processHang.terminateProcess.accessKey   "K">
-
 <!ENTITY emeLearnMoreContextMenu.label            "Learn more about DRM…">
 <!ENTITY emeLearnMoreContextMenu.accesskey        "D">
 <!ENTITY emeNotificationsNotNow.label             "Not now">
 <!ENTITY emeNotificationsNotNow.accesskey         "N">
 <!ENTITY emeNotificationsDontAskAgain.label       "Don't ask me again">
 <!ENTITY emeNotificationsDontAskAgain.accesskey   "D">
 
 <!-- LOCALIZATION NOTE (saveToPocketCmd.label, saveLinkToPocketCmd.label, pocketMenuitem.label): Pocket is a brand name -->
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -467,19 +467,23 @@ syncPromoNotification.addons.description
 syncPromoNotification.addons-sync-disabled.description=You can use your %S account to synchronize add-ons across multiple devices.\u0020
 
 # Mozilla data reporting notification (Telemetry, Firefox Health Report, etc)
 dataReportingNotification.message       = %1$S automatically sends some data to %2$S so that we can improve your experience.
 dataReportingNotification.button.label  = Choose What I Share
 dataReportingNotification.button.accessKey  = C
 
 # Process hang reporter
-processHang.message = A web page is causing %1$S to run slowly. What would you like to do?
-processHang.button.label = Options
-processHang.button.accessKey = O
+processHang.label = A web page is slowing down your browser. What would you like to do?
+processHang.button_stop.label = Stop It
+processHang.button_stop.accessKey = S
+processHang.button_wait.label = Wait
+processHang.button_wait.accessKey = W
+processHang.button_debug.label = Debug Script
+processHang.button_debug.accessKey = D
 
 # Webapps notification popup
 webapps.install = Install
 webapps.install.accesskey = I
 #LOCALIZATION NOTE (webapps.requestInstall2) %S is the web app name
 webapps.requestInstall2 = Do you want to install “%S” from this site?
 webapps.install.success = Application Installed
 webapps.install.inprogress = Installation in progress
--- a/browser/modules/ProcessHangMonitor.jsm
+++ b/browser/modules/ProcessHangMonitor.jsm
@@ -14,32 +14,56 @@ this.EXPORTED_SYMBOLS = ["ProcessHangMon
 Cu.import("resource://gre/modules/Services.jsm");
 
 /**
  * This JSM is responsible for observing content process hang reports
  * and asking the user what to do about them. See nsIHangReport for
  * the platform interface.
  */
 
-/**
- * If a hang hasn't been reported for more than 10 seconds, assume the
- * content process has gotten unstuck (and hide the hang notification).
- */
-const HANG_EXPIRATION_TIME = 10000;
+var ProcessHangMonitor = {
+  /**
+   * If a hang hasn't been reported for more than 10 seconds, assume the
+   * content process has gotten unstuck (and hide the hang notification).
+   */
+  get HANG_EXPIRATION_TIME() {
+    try {
+      return Services.prefs.getIntPref("browser.hangNotification.expiration");
+    } catch (ex) {
+      return 10000;
+    }
+  },
 
-var ProcessHangMonitor = {
+  /**
+   * This timeout is the wait period applied after a user selects "Wait" in
+   * an existing notification.
+   */
+  get WAIT_EXPIRATION_TIME() {
+    try {
+      return Services.prefs.getIntPref("browser.hangNotification.waitPeriod");
+    } catch (ex) {
+      return 10000;
+    }
+  },
+
   /**
    * Collection of hang reports that haven't expired or been dismissed
    * by the user. The keys are nsIHangReports and values keys are
    * timers. Each time the hang is reported, the timer is refreshed so
    * it expires after HANG_EXPIRATION_TIME.
    */
   _activeReports: new Map(),
 
   /**
+   * Collection of hang reports that have been suppressed for a
+   * short period of time.
+   */
+  _pausedReports: new Map(),
+
+  /**
    * Initialize hang reporting. Called once in the parent process.
    */
   init: function() {
     Services.obs.addObserver(this, "process-hang-report", false);
     Services.obs.addObserver(this, "xpcom-shutdown", false);
     Services.ww.registerNotification(this);
   },
 
@@ -65,69 +89,101 @@ var ProcessHangMonitor = {
 
       let svc = Cc["@mozilla.org/dom/slow-script-debug;1"].getService(Ci.nsISlowScriptDebug);
       let handler = svc.remoteActivationHandler;
       handler.handleSlowScriptDebug(report.scriptBrowser, callback);
     });
   },
 
   /**
-   * Kill the plugin process causing the hang being reported for the
-   * selected browser in |win|.
+   * Terminate the plugin process associated with a hang being reported
+   * for the selected browser in |win|. Will attempt to generate a combined
+   * crash report for all processes.
    */
   terminatePlugin: function(win) {
     this.handleUserInput(win, report => report.terminatePlugin());
   },
 
   /**
-   * Kill the content process causing the hang being reported for the selected
-   * browser in |win|.
+   * Dismiss the browser notification and invoke an appropriate action based on
+   * the hang type.
    */
-  terminateProcess: function(win) {
-    this.handleUserInput(win, report => report.terminateProcess());
-  },
-
-  /**
-   * Update the "Options" pop-up menu for the hang notification
-   * associated with the selected browser in |win|. The menu should
-   * display only options that are relevant to the given report.
-   */
-  refreshMenu: function(win) {
-    let report = this.findReport(win.gBrowser.selectedBrowser);
+  stopIt: function (win) {
+    let report = this.findActiveReport(win.gBrowser.selectedBrowser);
     if (!report) {
       return;
     }
 
-    function setVisible(id, visible) {
-      let item = win.document.getElementById(id);
-      item.hidden = !visible;
+    switch (report.hangType) {
+      case report.SLOW_SCRIPT:
+        this.terminateScript(win);
+        break;
+      case report.PLUGIN_HANG:
+        this.terminatePlugin(win);
+        break;
     }
+  },
+
+  /**
+   * Dismiss the notification, clear the report from the active list and set up
+   * a new timer to track a wait period during which we won't notify.
+   */
+  waitLonger: function(win) {
+    let report = this.findActiveReport(win.gBrowser.selectedBrowser);
+    if (!report) {
+      return;
+    }
+    // Remove the report from the active list and cancel its timer.
+    this.removeActiveReport(report);
+
+    // NOTE, we didn't call userCanceled on nsIHangReport here. This insures
+    // we don't repeatedly generate and cache crash report data for this hang
+    // in the process hang reporter. It already has one report for the browser
+    // process we want it hold onto.
 
-    if (report.hangType == report.SLOW_SCRIPT) {
-      setVisible("processHangTerminateScript", true);
-      setVisible("processHangDebugScript", true);
-      setVisible("processHangTerminatePlugin", false);
-    } else if (report.hangType == report.PLUGIN_HANG) {
-      setVisible("processHangTerminateScript", false);
-      setVisible("processHangDebugScript", false);
-      setVisible("processHangTerminatePlugin", true);
-    }
+    // Create a new wait timer with notify callback
+    let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+    timer.initWithCallback(() => {
+      for (let [stashedReport, otherTimer] of this._pausedReports) {
+        if (otherTimer === timer) {
+          this.removePausedReport(stashedReport);
+
+          // Create a new notification display timeout timer
+          let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+          timer.initWithCallback(this, this.HANG_EXPIRATION_TIME, timer.TYPE_ONE_SHOT);
+
+          // Store the timer in the active reports map. If we receive a new
+          // observer notification for this hang, we'll redisplay the browser
+          // notification in reportHang below. If we do not receive a new
+          // observer, timer will take care of cleaning up resources associated
+          // with this hang. The observer for active hangs fires about once
+          // a second.
+          this._activeReports.set(report, timer);
+          break;
+        }
+      }
+    }, this.WAIT_EXPIRATION_TIME, timer.TYPE_ONE_SHOT);
+
+    this._pausedReports.set(report, timer);
+
+    // remove the browser notification associated with this hang
+    this.updateWindows();
   },
 
   /**
    * If there is a hang report associated with the selected browser in
    * |win|, invoke |func| on that report and stop notifying the user
    * about it.
    */
   handleUserInput: function(win, func) {
-    let report = this.findReport(win.gBrowser.selectedBrowser);
+    let report = this.findActiveReport(win.gBrowser.selectedBrowser);
     if (!report) {
       return;
     }
-    this.removeReport(report);
+    this.removeActiveReport(report);
 
     return func(report);
   },
 
   observe: function(subject, topic, data) {
     switch (topic) {
       case "xpcom-shutdown":
         Services.obs.removeObserver(this, "xpcom-shutdown");
@@ -148,29 +204,67 @@ var ProcessHangMonitor = {
           this.updateWindows();
         };
         win.addEventListener("load", listener, true);
         break;
     }
   },
 
   /**
-   * Find any active hang reports for the given <browser> element.
+   * Find a active hang report for the given <browser> element.
    */
-  findReport: function(browser) {
+  findActiveReport: function(browser) {
     let frameLoader = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
     for (let [report, timer] of this._activeReports) {
       if (report.isReportForBrowser(frameLoader)) {
         return report;
       }
     }
     return null;
   },
 
   /**
+   * Find a paused hang report for the given <browser> element.
+   */
+  findPausedReport: function(browser) {
+    let frameLoader = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
+    for (let [report, timer] of this._pausedReports) {
+      if (report.isReportForBrowser(frameLoader)) {
+        return report;
+      }
+    }
+    return null;
+  },
+
+  /**
+   * Remove an active hang report from the active list and cancel the timer
+   * associated with it.
+   */
+  removeActiveReport: function(report) {
+    let timer = this._activeReports.get(report);
+    if (timer) {
+      timer.cancel();
+    }
+    this._activeReports.delete(report);
+    this.updateWindows();
+  },
+
+  /**
+   * Remove a paused hang report from the paused list and cancel the timer
+   * associated with it.
+   */
+  removePausedReport: function(report) {
+    let timer = this._pausedReports.get(report);
+    if (timer) {
+      timer.cancel();
+    }
+    this._pausedReports.delete(report);
+  },
+
+  /**
    * Iterate over all XUL windows and ensure that the proper hang
    * reports are shown for each one. Also install event handlers in
    * each window to watch for events that would cause a different hang
    * report to be displayed.
    */
   updateWindows: function() {
     let e = Services.wm.getEnumerator("navigator:browser");
     while (e.hasMoreElements()) {
@@ -186,17 +280,17 @@ var ProcessHangMonitor = {
       }
     }
   },
 
   /**
    * If there is a hang report for the current tab in |win|, display it.
    */
   updateWindow: function(win) {
-    let report = this.findReport(win.gBrowser.selectedBrowser);
+    let report = this.findActiveReport(win.gBrowser.selectedBrowser);
 
     if (report) {
       this.showNotification(win, report);
     } else {
       this.hideNotification(win);
     }
   },
 
@@ -207,29 +301,46 @@ var ProcessHangMonitor = {
     let nb = win.document.getElementById("high-priority-global-notificationbox");
     let notification = nb.getNotificationWithValue("process-hang");
     if (notification) {
       return;
     }
 
     let bundle = win.gNavigatorBundle;
     let brandBundle = win.document.getElementById("bundle_brand");
-    let appName = brandBundle.getString("brandShortName");
-    let message = bundle.getFormattedString(
-      "processHang.message",
-      [appName]);
 
     let buttons = [{
-      label: bundle.getString("processHang.button.label"),
-      accessKey: bundle.getString("processHang.button.accessKey"),
-      popup: "processHangOptions",
-      callback: null,
-    }];
+        label: bundle.getString("processHang.button_stop.label"),
+        accessKey: bundle.getString("processHang.button_stop.accessKey"),
+        callback: function() {
+          ProcessHangMonitor.stopIt(win);
+        }
+      },
+      {
+        label: bundle.getString("processHang.button_wait.label"),
+        accessKey: bundle.getString("processHang.button_wait.accessKey"),
+        callback: function() {
+          ProcessHangMonitor.waitLonger(win);
+        }
+      }];
 
-    nb.appendNotification(message, "process-hang",
+#ifdef MOZ_DEV_EDITION
+    if (report.hangType == report.SLOW_SCRIPT) {
+      buttons.push({
+        label: bundle.getString("processHang.button_debug.label"),
+        accessKey: bundle.getString("processHang.button_debug.accessKey"),
+        callback: function() {
+          ProcessHangMonitor.debugScript(win);
+        }
+      });
+    }
+#endif
+
+    nb.appendNotification(bundle.getString("processHang.label"),
+                          "process-hang",
                           "chrome://browser/content/aboutRobots-icon.png",
                           nb.PRIORITY_WARNING_HIGH, buttons);
   },
 
   /**
    * Ensure that no hang notifications are visible in |win|.
    */
   hideNotification: function(win) {
@@ -265,57 +376,56 @@ var ProcessHangMonitor = {
     }
   },
 
   /**
    * Handle a potentially new hang report. If it hasn't been seen
    * before, show a notification for it in all open XUL windows.
    */
   reportHang: function(report) {
-    // If this hang was already reported, then reset the timer for it.
+    // If this hang was already reported reset the timer for it.
     if (this._activeReports.has(report)) {
       let timer = this._activeReports.get(report);
       timer.cancel();
-      timer.initWithCallback(this, HANG_EXPIRATION_TIME, timer.TYPE_ONE_SHOT);
+      timer.initWithCallback(this, this.HANG_EXPIRATION_TIME, timer.TYPE_ONE_SHOT);
+      // if this report is in active but doesn't have a notification associated
+      // with it, display a notification.
+      this.updateWindows();
+      return;
+    }
+
+    // If this hang was already reported and paused by the user ignore it.
+    if (this._pausedReports.has(report)) {
       return;
     }
 
     // On e10s this counts slow-script/hanged-plugin notice only once.
     // This code is not reached on non-e10s.
     if (report.hangType == report.SLOW_SCRIPT) {
       // On non-e10s, SLOW_SCRIPT_NOTICE_COUNT is probed at nsGlobalWindow.cpp
       Services.telemetry.getHistogramById("SLOW_SCRIPT_NOTICE_COUNT").add();
     } else if (report.hangType == report.PLUGIN_HANG) {
       // On non-e10s we have sufficient plugin telemetry probes,
       // so PLUGIN_HANG_NOTICE_COUNT is only probed on e10s.
       Services.telemetry.getHistogramById("PLUGIN_HANG_NOTICE_COUNT").add();
     }
 
     // Otherwise create a new timer and display the report.
     let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-    timer.initWithCallback(this, HANG_EXPIRATION_TIME, timer.TYPE_ONE_SHOT);
+    timer.initWithCallback(this, this.HANG_EXPIRATION_TIME, timer.TYPE_ONE_SHOT);
 
     this._activeReports.set(report, timer);
     this.updateWindows();
   },
 
   /**
-   * Dismiss a hang report because the user closed the notification
-   * for it or the report expired.
-   */
-  removeReport: function(report) {
-    this._activeReports.delete(report);
-    this.updateWindows();
-  },
-
-  /**
    * Callback for when HANG_EXPIRATION_TIME has elapsed.
    */
   notify: function(timer) {
     for (let [otherReport, otherTimer] of this._activeReports) {
       if (otherTimer === timer) {
-        this.removeReport(otherReport);
+        this.removeActiveReport(otherReport);
         otherReport.userCanceled();
         break;
       }
     }
   },
 };
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -28,29 +28,32 @@ EXTRA_JS_MODULES += [
     'Feeds.jsm',
     'FormSubmitObserver.jsm',
     'FormValidationHandler.jsm',
     'HiddenFrame.jsm',
     'NetworkPrioritizer.jsm',
     'offlineAppCache.jsm',
     'PanelFrame.jsm',
     'PluginContent.jsm',
-    'ProcessHangMonitor.jsm',
     'ReaderParent.jsm',
     'RecentWindow.jsm',
     'RemotePrompt.jsm',
     'Sanitizer.jsm',
     'SelfSupportBackend.jsm',
     'SitePermissions.jsm',
     'Social.jsm',
     'TransientPrefs.jsm',
     'WebappManager.jsm',
     'webrtcUI.jsm',
 ]
 
+EXTRA_PP_JS_MODULES += [
+    'ProcessHangMonitor.jsm'
+]
+
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     EXTRA_JS_MODULES += [
         'Windows8WindowFrameColor.jsm',
         'WindowsJumpLists.jsm',
         'WindowsPreviewPerTab.jsm',
     ]
 
 if CONFIG['NIGHTLY_BUILD']:
--- a/browser/modules/test/browser.ini
+++ b/browser/modules/test/browser.ini
@@ -1,13 +1,15 @@
 [DEFAULT]
 support-files =
   head.js
 
 [browser_BrowserUITelemetry_buckets.js]
+[browser_ProcessHangNotifications.js]
+skip-if = !e10s
 [browser_ContentSearch.js]
 skip-if = e10s
 support-files =
   contentSearch.js
   contentSearchBadImage.xml
   contentSearchSuggestions.sjs
   contentSearchSuggestions.xml
 [browser_NetworkPrioritizer.js]
new file mode 100644
--- /dev/null
+++ b/browser/modules/test/browser_ProcessHangNotifications.js
@@ -0,0 +1,185 @@
+
+Cu.import("resource://gre/modules/UpdateUtils.jsm");
+
+function getNotificationBox(aWindow) {
+  return aWindow.document.getElementById("high-priority-global-notificationbox");
+}
+
+function promiseNotificationShown(aWindow, aName) {
+  return new Promise((resolve) => {
+    let notification = getNotificationBox(aWindow);
+    notification.addEventListener("AlertActive", function active() {
+      notification.removeEventListener("AlertActive", active, true);
+      is(notification.allNotifications.length, 1, "Notification Displayed.");
+      resolve(notification);
+    });
+  });
+}
+
+function promiseReportCallMade(aValue) {
+  return new Promise((resolve) => {
+    let old = gTestHangReport.testCallback;
+    gTestHangReport.testCallback = function (val) {
+      gTestHangReport.testCallback = old;
+      is(aValue, val, "was the correct method call made on the hang report object?");
+      resolve();
+    };
+  });
+}
+
+function pushPrefs(...aPrefs) {
+  return new Promise((resolve) => {
+    SpecialPowers.pushPrefEnv({"set": aPrefs}, resolve);
+    resolve();
+  });
+}
+
+function popPrefs() {
+  return new Promise((resolve) => {
+    SpecialPowers.popPrefEnv(resolve);
+    resolve();
+  });
+}
+
+let gTestHangReport = {
+  SLOW_SCRIPT: 1,
+  PLUGIN_HANG: 2,
+
+  TEST_CALLBACK_CANCELED: 1,
+  TEST_CALLBACK_TERMSCRIPT: 2,
+  TEST_CALLBACK_TERMPLUGIN: 3,
+
+  _hangType: 1,
+  _tcb: function (aCallbackType) {},
+
+  get hangType() {
+    return this._hangType;
+  },
+
+  set hangType(aValue) {
+    this._hangType = aValue;
+  },
+
+  set testCallback(aValue) {
+    this._tcb = aValue;
+  },
+
+  QueryInterface: function (aIID) {
+    if (aIID.equals(Components.interfaces.nsIHangReport) ||
+        aIID.equals(Components.interfaces.nsISupports))
+      return this;
+    throw Components.results.NS_NOINTERFACE;
+  },
+
+  userCanceled: function () {
+    this._tcb(this.TEST_CALLBACK_CANCELED);
+  },
+
+  terminateScript: function () {
+    this._tcb(this.TEST_CALLBACK_TERMSCRIPT);
+  },
+
+  terminatePlugin: function () {
+    this._tcb(this.TEST_CALLBACK_TERMPLUGIN);
+  },
+
+  isReportForBrowser: function(aFrameLoader) {
+    return true;
+  }
+};
+
+// on dev edition we add a button for js debugging of hung scripts.
+let buttonCount = (UpdateUtils.UpdateChannel == "aurora" ? 3 : 2);
+
+/**
+ * Test if hang reports receive a terminate script callback when the user selects
+ * stop in response to a script hang.
+ */
+
+add_task(function* terminateScriptTest() {
+  let promise = promiseNotificationShown(window, "process-hang");
+  Services.obs.notifyObservers(gTestHangReport, "process-hang-report", null);
+  let notification = yield promise;
+
+  let buttons = notification.currentNotification.getElementsByTagName("button");
+  is(buttons.length, buttonCount, "proper number of buttons");
+
+  // Click the "Stop It" button, we should get a terminate script callback
+  gTestHangReport.hangType = gTestHangReport.SLOW_SCRIPT;
+  promise = promiseReportCallMade(gTestHangReport.TEST_CALLBACK_TERMSCRIPT);
+  buttons[0].click();
+  yield promise;
+});
+
+/**
+ * Test if hang reports receive user canceled callbacks after a user selects wait
+ * and the browser frees up from a script hang on its own.
+ */
+
+add_task(function* waitForScriptTest() {
+  let promise = promiseNotificationShown(window, "process-hang");
+  Services.obs.notifyObservers(gTestHangReport, "process-hang-report", null);
+  let notification = yield promise;
+
+  let buttons = notification.currentNotification.getElementsByTagName("button");
+  is(buttons.length, buttonCount, "proper number of buttons");
+
+  yield pushPrefs(["browser.hangNotification.waitPeriod", 1000],
+                  ["browser.hangNotification.expiration", 2000]);
+
+  function nocbcheck() {
+    ok(false, "received a callback?");
+  }
+  let oldcb = gTestHangReport.testCallback;
+  gTestHangReport.testCallback = nocbcheck;
+  // Click the "Wait" button this time, we shouldn't get a callback at all.
+  buttons[1].click();
+  gTestHangReport.testCallback = oldcb;
+
+  // send another hang pulse, we should not get a notification here
+  Services.obs.notifyObservers(gTestHangReport, "process-hang-report", null);
+  is(notification.currentNotification, null, "no notification should be visible");
+
+  // After selecting Wait, we should get a userCanceled callback after
+  // HANG_EXPIRATION_TIME.
+  yield promiseReportCallMade(gTestHangReport.TEST_CALLBACK_CANCELED);
+
+  yield popPrefs();
+});
+
+/**
+ * Test if hang reports receive user canceled callbacks after the content
+ * process stops sending hang notifications.
+ */
+
+add_task(function* hangGoesAwayTest() {
+  yield pushPrefs(["browser.hangNotification.expiration", 1000]);
+
+  let promise = promiseNotificationShown(window, "process-hang");
+  Services.obs.notifyObservers(gTestHangReport, "process-hang-report", null);
+  yield promise;
+
+  yield promiseReportCallMade(gTestHangReport.TEST_CALLBACK_CANCELED);
+
+  yield popPrefs();
+});
+
+/**
+ * Tests if hang reports receive a terminate plugin callback when the user selects
+ * stop in response to a plugin hang.
+ */
+
+add_task(function* terminatePluginTest() {
+  let promise = promiseNotificationShown(window, "process-hang");
+  Services.obs.notifyObservers(gTestHangReport, "process-hang-report", null);
+  let notification = yield promise;
+
+  let buttons = notification.currentNotification.getElementsByTagName("button");
+  is(buttons.length, buttonCount, "proper number of buttons");
+
+  // Click the "Stop It" button, we should get a terminate script callback
+  gTestHangReport.hangType = gTestHangReport.PLUGIN_HANG;
+  promise = promiseReportCallMade(gTestHangReport.TEST_CALLBACK_TERMPLUGIN);
+  buttons[0].click();
+  yield promise;
+});
--- a/browser/themes/preprocess-tab-svgs.py
+++ b/browser/themes/preprocess-tab-svgs.py
@@ -13,19 +13,19 @@ from mozbuild.preprocessor import prepro
 # one marker needs to include a file with the other marker since the
 # pre-processor instructions in the included file will not be
 # processed. The following SVG files need to include a file which uses
 # "%" as the marker so we invoke the pre- processor ourselves here with
 # the marker specified. The resulting SVG files will get packaged by the
 # processing of the jar file in the appropriate directory.
 def _do_preprocessing(output_svg, input_svg_file, additional_defines):
     additional_defines.update(buildconfig.defines)
-    preprocess(output=output_svg,
-               includes=[input_svg_file],
-               marker='%',
-               defines=additional_defines)
+    return preprocess(output=output_svg,
+                      includes=[input_svg_file],
+                      marker='%',
+                      defines=additional_defines)
 
 def tab_side_start(output_svg, input_svg_file):
-    _do_preprocessing(output_svg, input_svg_file, {'TAB_SIDE': 'start'})
+    return _do_preprocessing(output_svg, input_svg_file, {'TAB_SIDE': 'start'})
 
 def tab_side_end(output_svg, input_svg_file):
-    _do_preprocessing(output_svg, input_svg_file, {'TAB_SIDE': 'end'})
+    return _do_preprocessing(output_svg, input_svg_file, {'TAB_SIDE': 'end'})
 
--- a/build/compare-mozconfig/compare-mozconfigs.py
+++ b/build/compare-mozconfig/compare-mozconfigs.py
@@ -80,21 +80,26 @@ def verify_mozconfigs(mozconfig_pair, ni
         clean_line = line[1:].strip()
         if (line[0] == '-' or line[0] == '+') and len(clean_line) > 1:
             # skip comment lines
             if clean_line.startswith('#'):
                 continue
             # compare to whitelist
             message = ""
             if line[0] == '-':
+                # handle lines that move around in diff
+                if '+' + line[1:] in diff_list:
+                    continue
                 if platform in mozconfigWhitelist.get('release', {}):
                     if clean_line in \
                             mozconfigWhitelist['release'][platform]:
                         continue
             elif line[0] == '+':
+                if '-' + line[1:] in diff_list:
+                    continue
                 if platform in mozconfigWhitelist.get('nightly', {}):
                     if clean_line in \
                             mozconfigWhitelist['nightly'][platform]:
                         continue
                     else:
                         log.warning("%s not in %s %s!" % (
                             clean_line, platform,
                             mozconfigWhitelist['nightly'][platform]))
--- a/config/external/nss/Makefile.in
+++ b/config/external/nss/Makefile.in
@@ -262,16 +262,20 @@ DEFAULT_GMAKE_FLAGS += MAKE_OBJDIR='$$(I
 # Work around NSS adding IMPORT_LIBRARY to TARGETS with no rule for
 # it, creating race conditions. See bug #836220
 DEFAULT_GMAKE_FLAGS += TARGETS='$$(LIBRARY) $$(SHARED_LIBRARY) $$(PROGRAM)'
 
 ifdef MOZ_FOLD_LIBS_FLAGS
 DEFAULT_GMAKE_FLAGS += XCFLAGS='$(MOZ_FOLD_LIBS_FLAGS)'
 endif
 
+ifeq (1,$(ALLOW_COMPILER_WARNINGS))
+DEFAULT_GMAKE_FLAGS += NSS_ENABLE_WERROR=0
+endif
+
 NSS_SRCDIR = $(topsrcdir)
 
 NSS_DIRS =
 ifndef MOZ_FOLD_LIBS
 NSS_DIRS += nss/lib
 else
 ifndef NSS_DISABLE_DBM
 NSS_DIRS += nss/lib/dbm
--- a/config/external/nss/nss.def
+++ b/config/external/nss/nss.def
@@ -677,8 +677,9 @@ VFY_CreateContext
 VFY_DestroyContext
 VFY_End
 VFY_EndWithSignature
 VFY_Update
 VFY_VerifyData
 VFY_VerifyDataWithAlgorithmID
 VFY_VerifyDigestDirect
 _SGN_VerifyPKCS1DigestInfo
+__PK11_SetCertificateNickname
--- a/devtools/client/debugger/test/mochitest/browser_dbg_search-symbols.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_search-symbols.js
@@ -62,31 +62,31 @@ function htmlSearch() {
       ];
 
       for (let [label, value, description, line, column] of expectedResults) {
         let target = gFilteredFunctions.selectedItem.target;
 
         if (label) {
           is(target.querySelector(".results-panel-item-label").getAttribute("value"),
             gDebugger.SourceUtils.trimUrlLength(label + "()"),
-            "The corect label (" + label + ") is currently selected.");
+            "The correct label (" + label + ") is currently selected.");
         } else {
           ok(!target.querySelector(".results-panel-item-label"),
             "Shouldn't create empty label nodes.");
         }
         if (value) {
           ok(target.querySelector(".results-panel-item-label-below").getAttribute("value").includes(value),
-            "The corect value (" + value + ") is attached.");
+            "The correct value (" + value + ") is attached.");
         } else {
           ok(!target.querySelector(".results-panel-item-label-below"),
             "Shouldn't create empty label nodes.");
         }
         if (description) {
           is(target.querySelector(".results-panel-item-label-before").getAttribute("value"), description,
-            "The corect description (" + description + ") is currently shown.");
+            "The correct description (" + description + ") is currently shown.");
         } else {
           ok(!target.querySelector(".results-panel-item-label-before"),
             "Shouldn't create empty label nodes.");
         }
 
         ok(isCaretPos(gPanel, line, column),
           "The editor didn't jump to the correct line.");
 
@@ -133,31 +133,31 @@ function firstJsSearch() {
       ];
 
       for (let [label, value, description, line, column] of expectedResults) {
         let target = gFilteredFunctions.selectedItem.target;
 
         if (label) {
           is(target.querySelector(".results-panel-item-label").getAttribute("value"),
             gDebugger.SourceUtils.trimUrlLength(label + "()"),
-            "The corect label (" + label + ") is currently selected.");
+            "The correct label (" + label + ") is currently selected.");
         } else {
           ok(!target.querySelector(".results-panel-item-label"),
             "Shouldn't create empty label nodes.");
         }
         if (value) {
           ok(target.querySelector(".results-panel-item-label-below").getAttribute("value").includes(value),
-            "The corect value (" + value + ") is attached.");
+            "The correct value (" + value + ") is attached.");
         } else {
           ok(!target.querySelector(".results-panel-item-label-below"),
             "Shouldn't create empty label nodes.");
         }
         if (description) {
           is(target.querySelector(".results-panel-item-label-before").getAttribute("value"), description,
-            "The corect description (" + description + ") is currently shown.");
+            "The correct description (" + description + ") is currently shown.");
         } else {
           ok(!target.querySelector(".results-panel-item-label-before"),
             "Shouldn't create empty label nodes.");
         }
 
         ok(isCaretPos(gPanel, line, column),
           "The editor didn't jump to the correct line.");
 
@@ -204,31 +204,31 @@ function secondJsSearch() {
       ];
 
       for (let [label, value, description, line, column] of expectedResults) {
         let target = gFilteredFunctions.selectedItem.target;
 
         if (label) {
           is(target.querySelector(".results-panel-item-label").getAttribute("value"),
             gDebugger.SourceUtils.trimUrlLength(label + "()"),
-            "The corect label (" + label + ") is currently selected.");
+            "The correct label (" + label + ") is currently selected.");
         } else {
           ok(!target.querySelector(".results-panel-item-label"),
             "Shouldn't create empty label nodes.");
         }
         if (value) {
           ok(target.querySelector(".results-panel-item-label-below").getAttribute("value").includes(value),
-            "The corect value (" + value + ") is attached.");
+            "The correct value (" + value + ") is attached.");
         } else {
           ok(!target.querySelector(".results-panel-item-label-below"),
             "Shouldn't create empty label nodes.");
         }
         if (description) {
           is(target.querySelector(".results-panel-item-label-before").getAttribute("value"), description,
-            "The corect description (" + description + ") is currently shown.");
+            "The correct description (" + description + ") is currently shown.");
         } else {
           ok(!target.querySelector(".results-panel-item-label-before"),
             "Shouldn't create empty label nodes.");
         }
 
         ok(isCaretPos(gPanel, line, column),
           "The editor didn't jump to the correct line.");
 
@@ -275,31 +275,31 @@ function thirdJsSearch() {
       ];
 
       for (let [label, value, description, line, column] of expectedResults) {
         let target = gFilteredFunctions.selectedItem.target;
 
         if (label) {
           is(target.querySelector(".results-panel-item-label").getAttribute("value"),
             gDebugger.SourceUtils.trimUrlLength(label + "()"),
-            "The corect label (" + label + ") is currently selected.");
+            "The correct label (" + label + ") is currently selected.");
         } else {
           ok(!target.querySelector(".results-panel-item-label"),
             "Shouldn't create empty label nodes.");
         }
         if (value) {
           ok(target.querySelector(".results-panel-item-label-below").getAttribute("value").includes(value),
-            "The corect value (" + value + ") is attached.");
+            "The correct value (" + value + ") is attached.");
         } else {
           ok(!target.querySelector(".results-panel-item-label-below"),
             "Shouldn't create empty label nodes.");
         }
         if (description) {
           is(target.querySelector(".results-panel-item-label-before").getAttribute("value"), description,
-            "The corect description (" + description + ") is currently shown.");
+            "The correct description (" + description + ") is currently shown.");
         } else {
           ok(!target.querySelector(".results-panel-item-label-before"),
             "Shouldn't create empty label nodes.");
         }
 
         ok(isCaretPos(gPanel, line, column),
           "The editor didn't jump to the correct line.");
 
@@ -343,31 +343,31 @@ function filterSearch() {
       ];
 
       for (let [label, value, description, line, column] of expectedResults) {
         let target = gFilteredFunctions.selectedItem.target;
 
         if (label) {
           is(target.querySelector(".results-panel-item-label").getAttribute("value"),
             gDebugger.SourceUtils.trimUrlLength(label + "()"),
-            "The corect label (" + label + ") is currently selected.");
+            "The correct label (" + label + ") is currently selected.");
         } else {
           ok(!target.querySelector(".results-panel-item-label"),
             "Shouldn't create empty label nodes.");
         }
         if (value) {
           ok(target.querySelector(".results-panel-item-label-below").getAttribute("value").includes(value),
-            "The corect value (" + value + ") is attached.");
+            "The correct value (" + value + ") is attached.");
         } else {
           ok(!target.querySelector(".results-panel-item-label-below"),
             "Shouldn't create empty label nodes.");
         }
         if (description) {
           is(target.querySelector(".results-panel-item-label-before").getAttribute("value"), description,
-            "The corect description (" + description + ") is currently shown.");
+            "The correct description (" + description + ") is currently shown.");
         } else {
           ok(!target.querySelector(".results-panel-item-label-before"),
             "Shouldn't create empty label nodes.");
         }
 
         ok(isCaretPos(gPanel, line, column),
           "The editor didn't jump to the correct line.");
 
--- a/devtools/client/tilt/test/browser_tilt_utils02.js
+++ b/devtools/client/tilt/test/browser_tilt_utils02.js
@@ -10,12 +10,12 @@ function test() {
   ok(l10.stringBundle,
     "The necessary string bundle wasn't found.");
   is(l10.get(), null,
     "The get() function shouldn't work if no params are passed.");
   is(l10.format(), null,
     "The format() function shouldn't work if no params are passed.");
 
   is(typeof l10.get("initWebGL.error"), "string",
-    "No valid string was returned from a corect name in the bundle.");
+    "No valid string was returned from a correct name in the bundle.");
   is(typeof l10.format("linkProgram.error", ["error"]), "string",
     "No valid formatted string was returned from a name in the bundle.");
 }
new file mode 100644
--- /dev/null
+++ b/dom/base/WindowOrientationObserver.cpp
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WindowOrientationObserver.h"
+
+#include "nsGlobalWindow.h"
+#include "mozilla/Hal.h"
+
+/**
+ * This class is used by nsGlobalWindow to implement window.orientation
+ * and window.onorientationchange. This class is defined in its own file
+ * because Hal.h pulls in windows.h and can't be included from
+ * nsGlobalWindow.cpp
+ */
+WindowOrientationObserver::WindowOrientationObserver(
+  nsGlobalWindow* aGlobalWindow)
+  : mWindow(aGlobalWindow)
+{
+  MOZ_ASSERT(aGlobalWindow && aGlobalWindow->IsInnerWindow());
+  hal::RegisterScreenConfigurationObserver(this);
+
+  hal::ScreenConfiguration config;
+  hal::GetCurrentScreenConfiguration(&config);
+  mAngle = config.angle();
+}
+
+WindowOrientationObserver::~WindowOrientationObserver()
+{
+  hal::UnregisterScreenConfigurationObserver(this);
+}
+
+void
+WindowOrientationObserver::Notify(
+  const mozilla::hal::ScreenConfiguration& aConfiguration)
+{
+  uint16_t currentAngle = aConfiguration.angle();
+  if (mAngle != currentAngle && mWindow->IsCurrentInnerWindow()) {
+    mAngle = currentAngle;
+    mWindow->GetOuterWindow()->DispatchCustomEvent(NS_LITERAL_STRING("orientationchange"));
+  }
+}
+
+/* static */ int16_t
+WindowOrientationObserver::OrientationAngle()
+{
+  hal::ScreenConfiguration config;
+  hal::GetCurrentScreenConfiguration(&config);
+  int16_t angle = static_cast<int16_t>(config.angle());
+  // config.angle() returns 0, 90, 180 or 270.
+  // window.orientation returns -90, 0, 90 or 180.
+  return angle <= 180 ? angle : angle - 360;
+}
new file mode 100644
--- /dev/null
+++ b/dom/base/WindowOrientationObserver.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_WindowOrientationObserver_h
+#define mozilla_dom_WindowOrientationObserver_h
+
+#include "mozilla/HalScreenConfiguration.h"
+
+class nsGlobalWindow;
+
+namespace mozilla {
+namespace dom {
+
+class WindowOrientationObserver final :
+  public mozilla::hal::ScreenConfigurationObserver
+{
+public:
+  explicit WindowOrientationObserver(nsGlobalWindow* aGlobalWindow);
+  ~WindowOrientationObserver();
+  void Notify(const mozilla::hal::ScreenConfiguration& aConfiguration) override;
+  static int16_t OrientationAngle();
+
+private:
+  // Weak pointer, instance is owned by mWindow.
+  nsGlobalWindow* MOZ_NON_OWNING_REF mWindow;
+  uint16_t mAngle;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_WindowOrientationObserver_h
new file mode 100644
--- /dev/null
+++ b/dom/base/crashtests/729431-1.xhtml
@@ -0,0 +1,36 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<script>
+<![CDATA[
+
+function boom()
+{
+  var d = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
+  d.setAttributeNS(null, "contenteditable", "true");
+  var s = document.createElementNS("http://www.w3.org/1999/xhtml", "span");
+  d.appendChild(s);
+  document.documentElement.appendChild(d);
+
+  var textarea = document.createElementNS("http://www.w3.org/1999/xhtml", "textarea");
+  var t1 = document.createTextNode("A");
+  textarea.appendChild(t1);
+  var t2 = document.createTextNode("B");
+  textarea.appendChild(t2);
+  document.documentElement.appendChild(textarea);
+
+  document.documentElement.offsetHeight;
+
+  d.removeChild(s);
+  textarea.removeChild(t2);
+  document.documentElement.appendChild(document.createTextNode(" C "));
+  document.documentElement.appendChild(t2);
+}
+
+window.addEventListener("load", boom, false);
+
+]]>
+</script>
+
+<!-- no body -->
+
+</html>
--- a/dom/base/crashtests/crashtests.list
+++ b/dom/base/crashtests/crashtests.list
@@ -144,16 +144,17 @@ load 700090-2.html
 load 700512.html
 load 706283-1.html
 load 708405-1.html
 load 709384.html
 load 709954.html
 load 713417-2.html
 load 713417.html
 load 715056.html
+load 729431-1.xhtml
 load 741163-1.html
 load 745495.html
 load 752226-1.html
 load 752226-2.html
 load 766426.html
 load 771639.html
 load 786854.html
 load 815043.html
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -204,16 +204,17 @@ EXPORTS.mozilla.dom += [
     'StructuredCloneTags.h',
     'StyleSheetList.h',
     'SubtleCrypto.h',
     'Text.h',
     'TreeWalker.h',
     'URL.h',
     'URLSearchParams.h',
     'WebSocket.h',
+    'WindowOrientationObserver.h',
 ]
 
 UNIFIED_SOURCES += [
     'AnonymousContent.cpp',
     'Attr.cpp',
     'BarProps.cpp',
     'ChildIterator.cpp',
     'ChromeUtils.cpp',
@@ -350,16 +351,17 @@ UNIFIED_SOURCES += [
     'Text.cpp',
     'TextInputProcessor.cpp',
     'ThirdPartyUtil.cpp',
     'TreeWalker.cpp',
     'URL.cpp',
     'URLSearchParams.cpp',
     'WebSocket.cpp',
     'WindowNamedPropertiesHandler.cpp',
+    'WindowOrientationObserver.cpp',
 ]
 
 if CONFIG['MOZ_WEBRTC']:
     UNIFIED_SOURCES += [
         'nsDOMDataChannel.cpp',
     ]
 
 # these files couldn't be in UNIFIED_SOURCES for now for reasons given below:
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -842,16 +842,17 @@ GK_ATOM(onmapsendmessagereq, "onmapsendm
 GK_ATOM(onmapmessageupdatereq, "onmapmessageupdatereq")
 GK_ATOM(onnewrdsgroup, "onnewrdsgroup")
 GK_ATOM(onnotificationclick, "onnotificationclick")
 GK_ATOM(onnoupdate, "onnoupdate")
 GK_ATOM(onobsolete, "onobsolete")
 GK_ATOM(ononline, "ononline")
 GK_ATOM(onoffline, "onoffline")
 GK_ATOM(onopen, "onopen")
+GK_ATOM(onorientationchange, "onorientationchange")
 GK_ATOM(onotastatuschange, "onotastatuschange")
 GK_ATOM(onoverflow, "onoverflow")
 GK_ATOM(onoverflowchanged, "onoverflowchanged")
 GK_ATOM(onpagehide, "onpagehide")
 GK_ATOM(onpageshow, "onpageshow")
 GK_ATOM(onpaint, "onpaint")
 GK_ATOM(onpairingaborted, "onpairingaborted")
 GK_ATOM(onpairingconfirmationreq, "onpairingconfirmationreq")
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -15,16 +15,19 @@
 #include "nsScreen.h"
 #include "nsHistory.h"
 #include "nsPerformance.h"
 #include "nsDOMNavigationTiming.h"
 #include "nsIDOMStorageManager.h"
 #include "mozilla/dom/DOMStorage.h"
 #include "mozilla/dom/StorageEvent.h"
 #include "mozilla/dom/StorageEventBinding.h"
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
+#include "mozilla/dom/WindowOrientationObserver.h"
+#endif
 #include "nsDOMOfflineResourceList.h"
 #include "nsError.h"
 #include "nsIIdleService.h"
 #include "nsISizeOfEventTarget.h"
 #include "nsDOMJSUtils.h"
 #include "nsArrayUtils.h"
 #include "nsIDOMWindowCollection.h"
 #include "nsDOMWindowList.h"
@@ -1522,16 +1525,20 @@ nsGlobalWindow::CleanUp()
   mMozSelfSupport = nullptr;
 
   mPerformance = nullptr;
 
 #ifdef MOZ_WEBSPEECH
   mSpeechSynthesis = nullptr;
 #endif
 
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
+  mOrientationChangeObserver = nullptr;
+#endif
+
   ClearControllers();
 
   mOpener = nullptr;             // Forces Release
   if (mContext) {
     mContext = nullptr;            // Forces Release
   }
   mChromeEventHandler = nullptr; // Forces Release
   mParentTarget = nullptr;
@@ -1636,16 +1643,20 @@ nsGlobalWindow::FreeInnerObjects()
     mNavigator->Invalidate();
     mNavigator = nullptr;
   }
 
   if (mScreen) {
     mScreen = nullptr;
   }
 
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
+  mOrientationChangeObserver = nullptr;
+#endif
+
   if (mDoc) {
     // Remember the document's principal and URI.
     mDocumentPrincipal = mDoc->NodePrincipal();
     mDocumentURI = mDoc->GetDocumentURI();
     mDocBaseURI = mDoc->GetDocBaseURI();
 
     while (mDoc->EventHandlingSuppressed()) {
       mDoc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eEvents, false);
@@ -13649,16 +13660,37 @@ nsGlobalWindow::DisableDeviceSensor(uint
   }
 
   nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
   if (ac) {
     ac->RemoveWindowListener(aType, this);
   }
 }
 
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
+void
+nsGlobalWindow::EnableOrientationChangeListener()
+{
+  MOZ_ASSERT(IsInnerWindow());
+
+  if (!mOrientationChangeObserver) {
+    mOrientationChangeObserver =
+      new WindowOrientationObserver(this);
+  }
+}
+
+void
+nsGlobalWindow::DisableOrientationChangeListener()
+{
+  MOZ_ASSERT(IsInnerWindow());
+
+  mOrientationChangeObserver = nullptr;
+}
+#endif
+
 void
 nsGlobalWindow::SetHasGamepadEventListener(bool aHasGamepad/* = true*/)
 {
   MOZ_ASSERT(IsInnerWindow());
   mHasGamepad = aHasGamepad;
   if (aHasGamepad) {
     EnableGamepadUpdates();
   }
@@ -14496,16 +14528,25 @@ nsGlobalModalWindow::SetReturnValue(nsIV
 
 /* static */
 bool
 nsGlobalWindow::IsModalContentWindow(JSContext* aCx, JSObject* aGlobal)
 {
   return xpc::WindowOrNull(aGlobal)->IsModalContentWindow();
 }
 
+
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
+int16_t
+nsGlobalWindow::Orientation() const
+{
+  return WindowOrientationObserver::OrientationAngle();
+}
+#endif
+
 NS_IMETHODIMP
 nsGlobalWindow::GetConsole(JSContext* aCx,
                            JS::MutableHandle<JS::Value> aConsole)
 {
   FORWARD_TO_INNER(GetConsole, (aCx, aConsole), NS_ERROR_FAILURE);
 
   ErrorResult rv;
   RefPtr<Console> console = GetConsole(rv);
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -113,16 +113,19 @@ class Navigator;
 class OwningExternalOrWindowProxy;
 class Promise;
 class PostMessageEvent;
 struct RequestInit;
 class RequestOrUSVString;
 class Selection;
 class SpeechSynthesis;
 class WakeLock;
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
+class WindowOrientationObserver;
+#endif
 namespace cache {
 class CacheStorage;
 } // namespace cache
 namespace indexedDB {
 class IDBFactory;
 } // namespace indexedDB
 } // namespace dom
 namespace gfx {
@@ -651,16 +654,21 @@ public:
   virtual void PageHidden() override;
   virtual nsresult DispatchAsyncHashchange(nsIURI *aOldURI, nsIURI *aNewURI) override;
   virtual nsresult DispatchSyncPopState() override;
 
   // Inner windows only.
   virtual void EnableDeviceSensor(uint32_t aType) override;
   virtual void DisableDeviceSensor(uint32_t aType) override;
 
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
+  virtual void EnableOrientationChangeListener() override;
+  virtual void DisableOrientationChangeListener() override;
+#endif
+
   virtual void EnableTimeChangeNotifications() override;
   virtual void DisableTimeChangeNotifications() override;
 
 #ifdef MOZ_B2G
   // Inner windows only.
   virtual void EnableNetworkEvent(mozilla::EventMessage aEventMessage) override;
   virtual void DisableNetworkEvent(
                  mozilla::EventMessage aEventMessage) override;
@@ -902,16 +910,20 @@ public:
                                            mozilla::ErrorResult& aError);
   already_AddRefed<nsIDOMWindow> Open(const nsAString& aUrl,
                                       const nsAString& aName,
                                       const nsAString& aOptions,
                                       mozilla::ErrorResult& aError);
   mozilla::dom::Navigator* GetNavigator(mozilla::ErrorResult& aError);
   nsIDOMOfflineResourceList* GetApplicationCache(mozilla::ErrorResult& aError);
 
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
+  int16_t Orientation() const;
+#endif
+
   mozilla::dom::Console* GetConsole(mozilla::ErrorResult& aRv);
 
   void GetSidebar(mozilla::dom::OwningExternalOrWindowProxy& aResult,
                   mozilla::ErrorResult& aRv);
   already_AddRefed<mozilla::dom::External> GetExternal(mozilla::ErrorResult& aRv);
 
   // Exposed only for testing
   static bool
@@ -1817,16 +1829,20 @@ protected:
   // This flag keeps track of whether dialogs are
   // currently enabled on this window.
   bool                          mAreDialogsEnabled;
 
   nsTHashtable<nsPtrHashKey<mozilla::DOMEventTargetHelper> > mEventTargetObjects;
 
   nsTArray<uint32_t> mEnabledSensors;
 
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
+  nsAutoPtr<mozilla::dom::WindowOrientationObserver> mOrientationChangeObserver;
+#endif
+
 #ifdef MOZ_WEBSPEECH
   // mSpeechSynthesis is only used on inner windows.
   RefPtr<mozilla::dom::SpeechSynthesis> mSpeechSynthesis;
 #endif
 
   // This is the CC generation the last time we called CanSkip.
   uint32_t mCanSkipCCGeneration;
 
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -630,16 +630,21 @@ public:
   /**
    * Tell this window that it should remove itself from sensor change
    * notifications.
    *
    * Inner windows only.
    */
   virtual void DisableDeviceSensor(uint32_t aType) = 0;
 
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
+  virtual void EnableOrientationChangeListener() = 0;
+  virtual void DisableOrientationChangeListener() = 0;
+#endif
+
   virtual void EnableTimeChangeNotifications() = 0;
   virtual void DisableTimeChangeNotifications() = 0;
 
 #ifdef MOZ_B2G
   /**
    * Tell the window that it should start to listen to the network event of the
    * given aType.
    *
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -354,16 +354,18 @@ skip-if = buildapp == 'mulet'
 skip-if = buildapp == 'mulet'
 [test_window_constructor.html]
 [test_window_cross_origin_props.html]
 [test_window_define_symbol.html]
 [test_window_enumeration.html]
 [test_window_extensible.html]
 [test_window_indexing.html]
 [test_window_named_frame_enumeration.html]
+[test_window_orientation.html]
+skip-if = toolkit != 'gonk'
 [test_writable-replaceable.html]
 [test_navigatorPrefOverride.html]
 [test_EventSource_redirects.html]
 [test_NodeIterator_basics_filters.xhtml]
 [test_NodeIterator_mutations_1.xhtml]
 [test_NodeIterator_mutations_2.html]
 [test_NodeIterator_mutations_3.html]
 [test_XHR.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_window_orientation.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test for window.orientation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="orientationcommon.js"></script>
+<div id="log"></div>
+<script>
+async_test(function(t) {
+  var originalOrientation = screen.orientation.type;
+  var alternateOrientation = originalOrientation == "portrait-primary" ?
+    "landscape-primary" : "portrait-primary";
+
+  var originalWindowOrientation = window.orientation;
+  window.onorientationchange = function() {
+    t.step(function() { assert_not_equals(window.orientation, originalWindowOrientation); });
+
+    var p2 = specialPowersUnlock();
+    p2.then(function() {
+      t.done();
+    }).catch(t.step_func(function(err) {
+      assert_unreached("Error unlocking orientation: " + err);
+      t.done();
+    }));
+  }
+
+  var p1 = specialPowersLock(alternateOrientation);
+  p1.catch(t.step_func(function(err) {
+    assert_unreached("Error locking orientation: " + err);
+    t.done();
+  }));
+}, "Test window.orientation and orientationchange.");
+</script>
--- a/dom/cache/test/mochitest/driver.js
+++ b/dom/cache/test/mochitest/driver.js
@@ -17,16 +17,17 @@
 // when the returned promise is resolved.
 
 function runTests(testFile, order) {
   function setupPrefs() {
     return new Promise(function(resolve, reject) {
       SpecialPowers.pushPrefEnv({
         "set": [["dom.caches.enabled", true],
                 ["dom.caches.testing.enabled", true],
+                ["dom.requestcache.enabled", true],
                 ["dom.serviceWorkers.enabled", true],
                 ["dom.serviceWorkers.testing.enabled", true],
                 ["dom.serviceWorkers.exemptFromPerDomainMax", true]]
       }, function() {
         resolve();
       });
     });
   }
--- a/dom/events/ClipboardEvent.cpp
+++ b/dom/events/ClipboardEvent.cpp
@@ -75,17 +75,18 @@ ClipboardEvent::Constructor(const Global
   RefPtr<DataTransfer> clipboardData;
   if (e->mEventIsInternal) {
     InternalClipboardEvent* event = e->mEvent->AsClipboardEvent();
     if (event) {
       // Always create a clipboardData for the copy event. If this is changed to
       // support other types of events, make sure that read/write privileges are
       // checked properly within DataTransfer.
       clipboardData = new DataTransfer(ToSupports(e), eCopy, false, -1);
-      clipboardData->SetData(aParam.mDataType, aParam.mData);
+      clipboardData->SetData(aParam.mDataType, aParam.mData, aRv);
+      NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
     }
   }
 
   e->InitClipboardEvent(aType, aParam.mBubbles, aParam.mCancelable,
                         clipboardData, aRv);
   e->SetTrusted(trusted);
   return e.forget();
 }
--- a/dom/events/DataTransfer.cpp
+++ b/dom/events/DataTransfer.cpp
@@ -276,30 +276,36 @@ DataTransfer::GetMozUserCancelled(bool* 
 {
   *aUserCancelled = MozUserCancelled();
   return NS_OK;
 }
 
 FileList*
 DataTransfer::GetFiles(ErrorResult& aRv)
 {
+  return GetFilesInternal(aRv, nsContentUtils::SubjectPrincipal());
+}
+
+FileList*
+DataTransfer::GetFilesInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal)
+{
   if (mEventMessage != eDrop &&
       mEventMessage != eLegacyDragDrop &&
       mEventMessage != ePaste) {
     return nullptr;
   }
 
   if (!mFiles) {
     mFiles = new FileList(static_cast<nsIDOMDataTransfer*>(this));
 
     uint32_t count = mItems.Length();
 
     for (uint32_t i = 0; i < count; i++) {
       nsCOMPtr<nsIVariant> variant;
-      aRv = MozGetDataAt(NS_ConvertUTF8toUTF16(kFileMime), i, getter_AddRefs(variant));
+      aRv = GetDataAtInternal(NS_ConvertUTF8toUTF16(kFileMime), i, aSubjectPrincipal, getter_AddRefs(variant));
       if (aRv.Failed()) {
         return nullptr;
       }
 
       if (!variant)
         continue;
 
       nsCOMPtr<nsISupports> supports;
@@ -341,17 +347,17 @@ DataTransfer::GetFiles(ErrorResult& aRv)
 
   return mFiles;
 }
 
 NS_IMETHODIMP
 DataTransfer::GetFiles(nsIDOMFileList** aFileList)
 {
   ErrorResult rv;
-  NS_IF_ADDREF(*aFileList = GetFiles(rv));
+  NS_IF_ADDREF(*aFileList = GetFilesInternal(rv, nsContentUtils::GetSystemPrincipal()));
   return rv.StealNSResult();
 }
 
 already_AddRefed<DOMStringList>
 DataTransfer::Types()
 {
   RefPtr<DOMStringList> types = new DOMStringList();
   if (mItems.Length()) {
@@ -386,17 +392,17 @@ DataTransfer::GetTypes(nsISupports** aTy
 void
 DataTransfer::GetData(const nsAString& aFormat, nsAString& aData,
                       ErrorResult& aRv)
 {
   // return an empty string if data for the format was not found
   aData.Truncate();
 
   nsCOMPtr<nsIVariant> data;
-  nsresult rv = MozGetDataAt(aFormat, 0, getter_AddRefs(data));
+  nsresult rv = GetDataAtInternal(aFormat, 0, nsContentUtils::SubjectPrincipal(), getter_AddRefs(data));
   if (NS_FAILED(rv)) {
     if (rv != NS_ERROR_DOM_INDEX_SIZE_ERR) {
       aRv.Throw(rv);
     }
     return;
   }
 
   if (data) {
@@ -445,25 +451,17 @@ DataTransfer::GetData(const nsAString& a
 
 void
 DataTransfer::SetData(const nsAString& aFormat, const nsAString& aData,
                       ErrorResult& aRv)
 {
   RefPtr<nsVariantCC> variant = new nsVariantCC();
   variant->SetAsAString(aData);
 
-  aRv = MozSetDataAt(aFormat, variant, 0);
-}
-
-NS_IMETHODIMP
-DataTransfer::SetData(const nsAString& aFormat, const nsAString& aData)
-{
-  ErrorResult rv;
-  SetData(aFormat, aData, rv);
-  return rv.StealNSResult();
+  aRv = SetDataAtInternal(aFormat, variant, 0, nsContentUtils::SubjectPrincipal());
 }
 
 void
 DataTransfer::ClearData(const Optional<nsAString>& aFormat, ErrorResult& aRv)
 {
   if (mReadOnly) {
     aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return;
@@ -572,19 +570,26 @@ NS_IMETHODIMP
 DataTransfer::MozTypesAt(uint32_t aIndex, nsISupports** aTypes)
 {
   ErrorResult rv;
   RefPtr<DOMStringList> types = MozTypesAt(aIndex, rv);
   types.forget(aTypes);
   return rv.StealNSResult();
 }
 
-NS_IMETHODIMP
-DataTransfer::MozGetDataAt(const nsAString& aFormat, uint32_t aIndex,
-                           nsIVariant** aData)
+nsresult
+DataTransfer::GetDataAtNoSecurityCheck(const nsAString& aFormat, uint32_t aIndex,
+                                       nsIVariant** aData)
+{
+  return GetDataAtInternal(aFormat, aIndex, nsContentUtils::GetSystemPrincipal(), aData);
+}
+
+nsresult
+DataTransfer::GetDataAtInternal(const nsAString& aFormat, uint32_t aIndex,
+                                nsIPrincipal* aSubjectPrincipal, nsIVariant** aData)
 {
   *aData = nullptr;
 
   if (aFormat.IsEmpty())
     return NS_OK;
 
   if (aIndex >= mItems.Length()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
@@ -604,32 +609,28 @@ DataTransfer::MozGetDataAt(const nsAStri
   nsTArray<TransferItem>& item = mItems[aIndex];
 
   // Check if the caller is allowed to access the drag data. Callers with
   // chrome privileges can always read the data. During the
   // drop event, allow retrieving the data except in the case where the
   // source of the drag is in a child frame of the caller. In that case,
   // we only allow access to data of the same principal. During other events,
   // only allow access to the data with the same principal.
-  nsIPrincipal* principal = nullptr;
-  if (mIsCrossDomainSubFrameDrop ||
+  bool checkFormatItemPrincipal = mIsCrossDomainSubFrameDrop ||
       (mEventMessage != eDrop && mEventMessage != eLegacyDragDrop &&
-       mEventMessage != ePaste &&
-       !nsContentUtils::IsCallerChrome())) {
-    principal = nsContentUtils::SubjectPrincipal();
-  }
+       mEventMessage != ePaste);
 
   uint32_t count = item.Length();
   for (uint32_t i = 0; i < count; i++) {
     TransferItem& formatitem = item[i];
     if (formatitem.mFormat.Equals(format)) {
-      bool subsumes;
-      if (formatitem.mPrincipal && principal &&
-          (NS_FAILED(principal->Subsumes(formatitem.mPrincipal, &subsumes)) || !subsumes))
+      if (formatitem.mPrincipal && checkFormatItemPrincipal &&
+          !aSubjectPrincipal->Subsumes(formatitem.mPrincipal)) {
         return NS_ERROR_DOM_SECURITY_ERR;
+      }
 
       if (!formatitem.mData) {
         FillInExternalData(formatitem, aIndex);
       } else {
         nsCOMPtr<nsISupports> data;
         formatitem.mData->GetAsISupports(getter_AddRefs(data));
         // Make sure the code that is calling us is same-origin with the data.
         nsCOMPtr<EventTarget> pt = do_QueryInterface(data);
@@ -638,22 +639,17 @@ DataTransfer::MozGetDataAt(const nsAStri
           nsIScriptContext* c = pt->GetContextForEventHandlers(&rv);
           NS_ENSURE_TRUE(c && NS_SUCCEEDED(rv), NS_ERROR_DOM_SECURITY_ERR);
           nsIGlobalObject* go = c->GetGlobalObject();
           NS_ENSURE_TRUE(go, NS_ERROR_DOM_SECURITY_ERR);
           nsCOMPtr<nsIScriptObjectPrincipal> sp = do_QueryInterface(go);
           MOZ_ASSERT(sp, "This cannot fail on the main thread.");
           nsIPrincipal* dataPrincipal = sp->GetPrincipal();
           NS_ENSURE_TRUE(dataPrincipal, NS_ERROR_DOM_SECURITY_ERR);
-          if (!principal) {
-            principal = nsContentUtils::SubjectPrincipal();
-          }
-          bool equals = false;
-          NS_ENSURE_TRUE(NS_SUCCEEDED(principal->Equals(dataPrincipal, &equals)) && equals,
-                         NS_ERROR_DOM_SECURITY_ERR);
+          NS_ENSURE_TRUE(aSubjectPrincipal->Subsumes(dataPrincipal), NS_ERROR_DOM_SECURITY_ERR);
         }
       }
       *aData = formatitem.mData;
       NS_IF_ADDREF(*aData);
       return NS_OK;
     }
   }
 
@@ -662,36 +658,36 @@ DataTransfer::MozGetDataAt(const nsAStri
 
 void
 DataTransfer::MozGetDataAt(JSContext* aCx, const nsAString& aFormat,
                            uint32_t aIndex,
                            JS::MutableHandle<JS::Value> aRetval,
                            mozilla::ErrorResult& aRv)
 {
   nsCOMPtr<nsIVariant> data;
-  aRv = MozGetDataAt(aFormat, aIndex, getter_AddRefs(data));
+  aRv = GetDataAtInternal(aFormat, aIndex, nsContentUtils::SubjectPrincipal(), getter_AddRefs(data));
   if (aRv.Failed()) {
     return;
   }
 
   if (!data) {
     aRetval.setNull();
     return;
   }
 
   JS::Rooted<JS::Value> result(aCx);
   if (!VariantToJsval(aCx, data, aRetval)) {
     aRv = NS_ERROR_FAILURE;
     return;
   }
 }
 
-NS_IMETHODIMP
-DataTransfer::MozSetDataAt(const nsAString& aFormat, nsIVariant* aData,
-                           uint32_t aIndex)
+nsresult
+DataTransfer::SetDataAtInternal(const nsAString& aFormat, nsIVariant* aData,
+                                uint32_t aIndex, nsIPrincipal* aSubjectPrincipal)
 {
   if (aFormat.IsEmpty()) {
     return NS_OK;
   }
 
   if (mReadOnly) {
     return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
   }
@@ -708,34 +704,33 @@ DataTransfer::MozSetDataAt(const nsAStri
        mEventMessage == ePaste)) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
   // don't allow non-chrome to add file data
   // XXX perhaps this should also limit any non-string type as well
   if ((aFormat.EqualsLiteral("application/x-moz-file-promise") ||
        aFormat.EqualsLiteral("application/x-moz-file")) &&
-       !nsContentUtils::IsCallerChrome()) {
+       !nsContentUtils::IsSystemPrincipal(aSubjectPrincipal)) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
-  return SetDataWithPrincipal(aFormat, aData, aIndex,
-                              nsContentUtils::SubjectPrincipal());
+  return SetDataWithPrincipal(aFormat, aData, aIndex, aSubjectPrincipal);
 }
 
 void
 DataTransfer::MozSetDataAt(JSContext* aCx, const nsAString& aFormat,
                            JS::Handle<JS::Value> aData,
                            uint32_t aIndex, ErrorResult& aRv)
 {
   nsCOMPtr<nsIVariant> data;
   aRv = nsContentUtils::XPConnect()->JSValToVariant(aCx, aData,
                                                     getter_AddRefs(data));
   if (!aRv.Failed()) {
-    aRv = MozSetDataAt(aFormat, data, aIndex);
+    aRv = SetDataAtInternal(aFormat, data, aIndex, nsContentUtils::SubjectPrincipal());
   }
 }
 
 void
 DataTransfer::MozClearDataAt(const nsAString& aFormat, uint32_t aIndex,
                              ErrorResult& aRv)
 {
   if (mReadOnly) {
--- a/dom/events/DataTransfer.h
+++ b/dom/events/DataTransfer.h
@@ -63,16 +63,18 @@ public:
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIDOMDATATRANSFER
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DataTransfer)
 
   friend class mozilla::EventStateManager;
 
+  static DataTransfer* Cast(nsIDOMDataTransfer* aArg) { return static_cast<DataTransfer*>(aArg); }
+
 protected:
 
   // hide the default constructor
   DataTransfer();
 
   // this constructor is used only by the Clone method to copy the fields as
   // needed to a new data transfer.
   DataTransfer(nsISupports* aParent,
@@ -178,16 +180,19 @@ public:
   }
   already_AddRefed<nsINode> GetMozSourceNode();
 
   mozilla::dom::Element* GetDragTarget()
   {
     return mDragTarget;
   }
 
+  nsresult GetDataAtNoSecurityCheck(const nsAString& aFormat, uint32_t aIndex,
+                                    nsIVariant** aData);
+
   // a readonly dataTransfer cannot have new data added or existing data removed.
   // Only the dropEffect and effectAllowed may be modified.
   void SetReadOnly() { mReadOnly = true; }
 
   // converts the data into an array of nsITransferable objects to be used for
   // drag and drop or clipboard operations.
   already_AddRefed<nsISupportsArray> GetTransferables(nsIDOMNode* aDragTarget);
   already_AddRefed<nsISupportsArray> GetTransferables(nsILoadContext* aLoadContext);
@@ -241,16 +246,23 @@ protected:
 
   // caches the formats that exist in the clipboard
   void CacheExternalClipboardFormats();
 
   // fills in the data field of aItem with the data from the drag service or
   // clipboard for a given index.
   void FillInExternalData(TransferItem& aItem, uint32_t aIndex);
 
+
+  FileList* GetFilesInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal);
+  nsresult GetDataAtInternal(const nsAString& aFormat, uint32_t aIndex,
+                             nsIPrincipal* aSubjectPrincipal, nsIVariant** aData);
+  nsresult SetDataAtInternal(const nsAString& aFormat, nsIVariant* aData, uint32_t aIndex,
+                             nsIPrincipal* aSubjectPrincipal);
+
   friend class ContentParent;
   void FillAllExternalData();
 
   void MozClearDataAtHelper(const nsAString& aFormat, uint32_t aIndex,
                             mozilla::ErrorResult& aRv);
 
   nsCOMPtr<nsISupports> mParent;
 
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -320,16 +320,20 @@ EventListenerManager::AddEventListenerIn
   } else if (aTypeAtom == nsGkAtoms::ondeviceorientation) {
     EnableDevice(eDeviceOrientation);
   } else if (aTypeAtom == nsGkAtoms::ondeviceproximity || aTypeAtom == nsGkAtoms::onuserproximity) {
     EnableDevice(eDeviceProximity);
   } else if (aTypeAtom == nsGkAtoms::ondevicelight) {
     EnableDevice(eDeviceLight);
   } else if (aTypeAtom == nsGkAtoms::ondevicemotion) {
     EnableDevice(eDeviceMotion);
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
+  } else if (aTypeAtom == nsGkAtoms::onorientationchange) {
+    EnableDevice(eOrientationChange);
+#endif
 #ifdef MOZ_B2G
   } else if (aTypeAtom == nsGkAtoms::onmoztimechange) {
     nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
     if (window) {
       window->EnableTimeChangeNotifications();
     }
   } else if (aTypeAtom == nsGkAtoms::onmoznetworkupload) {
     nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
@@ -426,16 +430,19 @@ bool
 EventListenerManager::IsDeviceType(EventMessage aEventMessage)
 {
   switch (aEventMessage) {
     case eDeviceOrientation:
     case eDeviceMotion:
     case eDeviceLight:
     case eDeviceProximity:
     case eUserProximity:
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
+    case eOrientationChange:
+#endif
       return true;
     default:
       break;
   }
   return false;
 }
 
 void
@@ -457,16 +464,21 @@ EventListenerManager::EnableDevice(Event
     case eDeviceLight:
       window->EnableDeviceSensor(SENSOR_LIGHT);
       break;
     case eDeviceMotion:
       window->EnableDeviceSensor(SENSOR_ACCELERATION);
       window->EnableDeviceSensor(SENSOR_LINEAR_ACCELERATION);
       window->EnableDeviceSensor(SENSOR_GYROSCOPE);
       break;
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
+    case eOrientationChange:
+      window->EnableOrientationChangeListener();
+      break;
+#endif
     default:
       NS_WARNING("Enabling an unknown device sensor.");
       break;
   }
 }
 
 void
 EventListenerManager::DisableDevice(EventMessage aEventMessage)
@@ -487,16 +499,21 @@ EventListenerManager::DisableDevice(Even
       break;
     case eDeviceProximity:
     case eUserProximity:
       window->DisableDeviceSensor(SENSOR_PROXIMITY);
       break;
     case eDeviceLight:
       window->DisableDeviceSensor(SENSOR_LIGHT);
       break;
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
+    case eOrientationChange:
+      window->DisableOrientationChangeListener();
+      break;
+#endif
     default:
       NS_WARNING("Disabling an unknown device sensor.");
       break;
   }
 }
 
 void
 EventListenerManager::RemoveEventListenerInternal(
--- a/dom/events/EventNameList.h
+++ b/dom/events/EventNameList.h
@@ -502,16 +502,22 @@ WINDOW_EVENT(message,
 WINDOW_EVENT(offline,
              eOffline,
              EventNameType_XUL | EventNameType_HTMLBodyOrFramesetOnly,
              eBasicEventClass)
 WINDOW_EVENT(online,
              eOnline,
              EventNameType_XUL | EventNameType_HTMLBodyOrFramesetOnly,
              eBasicEventClass)
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
+WINDOW_EVENT(orientationchange,
+             eOrientationChange,
+             EventNameType_HTMLBodyOrFramesetOnly,
+             eBasicEventClass)
+#endif
 WINDOW_EVENT(pagehide,
              ePageHide,
              EventNameType_HTMLBodyOrFramesetOnly,
              eBasicEventClass)
 WINDOW_EVENT(pageshow,
              ePageShow,
              EventNameType_HTMLBodyOrFramesetOnly,
              eBasicEventClass)
--- a/dom/fetch/Request.cpp
+++ b/dom/fetch/Request.cpp
@@ -59,16 +59,35 @@ Request::RequestContextEnabled(JSContext
   WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
   if (!workerPrivate) {
     return false;
   }
 
   return workerPrivate->RequestContextEnabled();
 }
 
+// static
+bool
+Request::RequestCacheEnabled(JSContext* aCx, JSObject* aObj)
+{
+  if (NS_IsMainThread()) {
+    return Preferences::GetBool("dom.requestcache.enabled", false);
+  }
+
+  using namespace workers;
+
+  // Otherwise, check the pref via the WorkerPrivate
+  WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
+  if (!workerPrivate) {
+    return false;
+  }
+
+  return workerPrivate->RequestCacheEnabled();
+}
+
 already_AddRefed<InternalRequest>
 Request::GetInternalRequest()
 {
   RefPtr<InternalRequest> r = mRequest;
   return r.forget();
 }
 
 namespace {
--- a/dom/fetch/Request.h
+++ b/dom/fetch/Request.h
@@ -31,16 +31,18 @@ class Request final : public nsISupports
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Request)
 
 public:
   Request(nsIGlobalObject* aOwner, InternalRequest* aRequest);
 
   static bool
   RequestContextEnabled(JSContext* aCx, JSObject* aObj);
+  static bool
+  RequestCacheEnabled(JSContext* aCx, JSObject* aObj);
 
   JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
   {
     return RequestBinding::Wrap(aCx, this, aGivenProto);
   }
 
   void
new file mode 100644
--- /dev/null
+++ b/dom/html/crashtests/495546-1.html
@@ -0,0 +1,19 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+  var video = document.createElement("video");
+  var source = document.createElement("source");
+  source.setAttributeNS(null, "src", "http://127.0.0.1/");
+  video.appendChild(source);
+  document.body.appendChild(video);
+  setTimeout(function() { document.body.removeChild(video); }, 20);
+}
+
+</script>
+</head>
+
+<body onload="boom();"></body>
+</html>
--- a/dom/html/crashtests/crashtests.list
+++ b/dom/html/crashtests/crashtests.list
@@ -18,16 +18,17 @@ load 448564.html
 load 451123-1.html
 load 453406-1.html
 load 464197-1.html
 load 465466-1.xhtml
 load 468562-1.html
 load 468562-2.html
 load 494225.html
 load 495543.svg
+load 495546-1.html
 load 504183-1.html
 load 515829-1.html
 load 515829-2.html
 load 564461.xhtml
 load 570566-1.html
 load 571428-1.html
 load 580507-1.xhtml
 load 590387.html
--- a/dom/interfaces/events/nsIDOMDataTransfer.idl
+++ b/dom/interfaces/events/nsIDOMDataTransfer.idl
@@ -3,17 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "domstubs.idl"
 
 interface nsIVariant;
 interface nsIDOMFileList;
 
-[builtinclass, uuid(c71180e3-298b-4fbb-9ccb-82c822474741)]
+[builtinclass, uuid(655078bf-1675-4aa0-a48d-a133e864ce57)]
 interface nsIDOMDataTransfer : nsISupports
 {
   /**
    * The actual effect that will be used, and should always be one of the
    * possible values of effectAllowed.
    *
    * For dragstart, drag and dragleave events, the dropEffect is initialized
    * to none. Any value assigned to the dropEffect will be set, but the value
@@ -77,27 +77,16 @@ interface nsIDOMDataTransfer : nsISuppor
    * Remove the data associated with a given format. If format is empty or not
    * specified, the data associated with all formats is removed. If data for
    * the specified format does not exist, or the data transfer contains no
    * data, this method will have no effect.
    */
   void clearData([optional] in DOMString format);
 
   /**
-   * Set the data for a given format. If data for the format does not exist,
-   * it is added at the end, such that the last item in the types list will be
-   * the new format. If data for the format already exists, the existing data
-   * is replaced in the same position. That is, the order of the types list is
-   * not changed.
-   *
-   * @throws NS_ERROR_NULL_POINTER if the data is null
-   */
-  void setData(in DOMString format, in DOMString data);
-
-  /**
    * Retrieves the data for a given format, or an empty string if data for
    * that format does not exist or the data transfer contains no data.
    */
   DOMString getData(in DOMString format);
 
   /**
    * Set the image to be used for dragging if a custom one is desired. Most of
    * the time, this would not be set, as a default image is created from the
@@ -166,50 +155,16 @@ interface nsIDOMDataTransfer : nsISuppor
    * If the format is not found, then this method has no effect.
    *
    * @param format the format to remove
    * @throws NS_ERROR_DOM_INDEX_SIZE_ERR if index is greater or equal than itemCount
    * @throws NO_MODIFICATION_ALLOWED_ERR if the item cannot be modified
    */
   void mozClearDataAt(in DOMString format, in unsigned long index);
 
-  /*
-   * A data transfer may store multiple items, each at a given zero-based
-   * index. setDataAt may only be called with an index argument less than
-   * itemCount in which case an existing item is modified, or equal to
-   * itemCount in which case a new item is added, and the itemCount is
-   * incremented by one.
-   *
-   * Data should be added in order of preference, with the most specific
-   * format added first and the least specific format added last. If data of
-   * the given format already exists, it is replaced in the same position as
-   * the old data.
-   *
-   * The data should be either a string, a primitive boolean or number type
-   * (which will be converted into a string) or an nsISupports.
-   *
-   * @param format the format to add
-   * @param data the data to add
-   * @throws NS_ERROR_NULL_POINTER if the data is null
-   * @throws NS_ERROR_DOM_INDEX_SIZE_ERR if index is greater than itemCount
-   * @throws NO_MODIFICATION_ALLOWED_ERR if the item cannot be modified
-   */
-  void mozSetDataAt(in DOMString format, in nsIVariant data, in unsigned long index);
-
-  /**
-   * Retrieve the data associated with the given format for an item at the
-   * specified index, or null if it does not exist. The index should be in the
-   * range from zero to itemCount - 1.
-   *
-   * @param format the format of the data to look up
-   * @returns the data of the given format, or null if it doesn't exist.
-   * @throws NS_ERROR_DOM_INDEX_SIZE_ERR if index is greater or equal than itemCount
-   */
-  nsIVariant mozGetDataAt(in DOMString format, in unsigned long index);
-
   /**
    * Will be true when the user has cancelled the drag (typically by pressing
    * Escape) and when the drag has been cancelled unexpectedly.  This will be
    * false otherwise, including when the drop has been rejected by its target.
    * This property is only relevant for the dragend event.
    */
   readonly attribute boolean mozUserCancelled;
 
--- a/dom/ipc/ProcessHangMonitor.cpp
+++ b/dom/ipc/ProcessHangMonitor.cpp
@@ -146,17 +146,16 @@ public:
   NS_IMETHOD GetScriptLineNo(uint32_t* aLineNo) override;
 
   NS_IMETHOD GetPluginName(nsACString& aPluginName) override;
 
   NS_IMETHOD TerminateScript() override;
   NS_IMETHOD BeginStartingDebugger() override;
   NS_IMETHOD EndStartingDebugger() override;
   NS_IMETHOD TerminatePlugin() override;
-  NS_IMETHOD TerminateProcess() override;
   NS_IMETHOD UserCanceled() override;
 
   NS_IMETHOD IsReportForBrowser(nsIFrameLoader* aFrameLoader, bool* aResult) override;
 
   // Called when a content process shuts down.
   void Clear() {
     mContentParent = nullptr;
     mActor = nullptr;
@@ -815,45 +814,29 @@ HangMonitoredProcess::EndStartingDebugge
 NS_IMETHODIMP
 HangMonitoredProcess::TerminatePlugin()
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   if (mHangData.type() != HangData::TPluginHangData) {
     return NS_ERROR_UNEXPECTED;
   }
 
+  // generates a crash report that includes a browser report taken here
+  // earlier, the content process, and any plugin process(es).
   uint32_t id = mHangData.get_PluginHangData().pluginId();
   plugins::TerminatePlugin(id, NS_LITERAL_CSTRING("HangMonitor"),
                            mBrowserDumpId);
 
   if (mActor) {
     mActor->CleanupPluginHang(id, false);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
-HangMonitoredProcess::TerminateProcess()
-{
-  MOZ_RELEASE_ASSERT(NS_IsMainThread());
-
-  if (!mContentParent) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  if (mActor && mHangData.type() == HangData::TPluginHangData) {
-    uint32_t id = mHangData.get_PluginHangData().pluginId();
-    mActor->CleanupPluginHang(id, true);
-  }
-
-  mContentParent->KillHard("HangMonitor");
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 HangMonitoredProcess::IsReportForBrowser(nsIFrameLoader* aFrameLoader, bool* aResult)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   if (!mActor) {
     *aResult = false;
     return NS_OK;
   }
--- a/dom/ipc/nsIHangReport.idl
+++ b/dom/ipc/nsIHangReport.idl
@@ -13,17 +13,17 @@ interface nsIFrameLoader;
  * When a content process hangs, Gecko notifies "process-hang-report" observers
  * and passes an nsIHangReport for the subject parameter. There is at most one
  * nsIHangReport associated with a given content process. As long as the content
  * process stays stuck, the "process-hang-report" observer will continue to be
  * notified at regular intervals (approximately once per second). The content
  * process will continue to run uninhibitedly during this time.
  */
 
-[scriptable, uuid(90cea731-dd3e-459e-b017-f9a14697b56e)]
+[scriptable, uuid(5fcffbb9-be62-49b1-b8a1-36e820787a74)]
 interface nsIHangReport : nsISupports
 {
   const unsigned long SLOW_SCRIPT = 1;
   const unsigned long PLUGIN_HANG = 2;
 
   // The type of hang being reported: SLOW_SCRIPT or PLUGIN_HANG.
   readonly attribute unsigned long hangType;
 
@@ -45,20 +45,16 @@ interface nsIHangReport : nsISupports
   // Terminate the slow script if it is still running.
   // Only valid for SLOW_SCRIPT reports.
   void terminateScript();
 
   // Terminate the plugin if it is still hung.
   // Only valid for PLUGIN_HANG reports.
   void terminatePlugin();
 
-  // Terminate the hung content process unconditionally.
-  // Valid for any type of hang.
-  void terminateProcess();
-
   // Ask the content process to start up the slow script debugger.
   // Only valid for SLOW_SCRIPT reports.
   void beginStartingDebugger();
 
   // Inform the content process that the slow script debugger has finished
   // spinning up. The content process will run a nested event loop until this
   // method is called.
   // Only valid for SLOW_SCRIPT reports.
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -496,21 +496,21 @@ StreamAndPromiseForOperation::StreamAndP
                                           dom::AudioContextOperation aOperation)
   : mStream(aStream)
   , mPromise(aPromise)
   , mOperation(aOperation)
 {
   // MOZ_ASSERT(aPromise);
 }
 
-AudioCallbackDriver::AudioCallbackDriver(MediaStreamGraphImpl* aGraphImpl, dom::AudioChannel aChannel)
+AudioCallbackDriver::AudioCallbackDriver(MediaStreamGraphImpl* aGraphImpl)
   : GraphDriver(aGraphImpl)
   , mIterationDurationMS(MEDIA_GRAPH_TARGET_PERIOD_MS)
   , mStarted(false)
-  , mAudioChannel(aChannel)
+  , mAudioChannel(aGraphImpl->AudioChannel())
   , mInCallback(false)
   , mPauseRequested(false)
 #ifdef XP_MACOSX
   , mCallbackReceivedWhileSwitching(0)
 #endif
 {
   STREAM_LOG(LogLevel::Debug, ("AudioCallbackDriver ctor for graph %p", aGraphImpl));
 }
--- a/dom/media/GraphDriver.h
+++ b/dom/media/GraphDriver.h
@@ -324,18 +324,17 @@ enum AsyncCubebOperation {
  *   the number of frames asked for by the callback. Since for the Web Audio
  *   API, we have to do block processing at 128 frames per block, we need to
  *   keep a little spill buffer to store the extra frames.
  */
 class AudioCallbackDriver : public GraphDriver,
                             public MixerCallbackReceiver
 {
 public:
-  explicit AudioCallbackDriver(MediaStreamGraphImpl* aGraphImpl,
-                               dom::AudioChannel aChannel = dom::AudioChannel::Normal);
+  explicit AudioCallbackDriver(MediaStreamGraphImpl* aGraphImpl);
   virtual ~AudioCallbackDriver();
 
   virtual void Destroy() override;
   virtual void Start() override;
   virtual void Stop() override;
   virtual void Resume() override;
   virtual void Revive() override;
   virtual void WaitForNextIteration() override;
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -1404,19 +1404,19 @@ MediaStreamGraphImpl::RunInStableState(b
         // to shut down, and we don't want to do that in a stable state handler.
         mLifecycleState = LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN;
         LIFECYCLE_LOG("Sending MediaStreamGraphShutDownRunnable %p", this);
         nsCOMPtr<nsIRunnable> event = new MediaStreamGraphShutDownRunnable(this );
         NS_DispatchToMainThread(event.forget());
 
         LIFECYCLE_LOG("Disconnecting MediaStreamGraph %p", this);
         MediaStreamGraphImpl* graph;
-        if (gGraphs.Get(mAudioChannel, &graph) && graph == this) {
+        if (gGraphs.Get(uint32_t(mAudioChannel), &graph) && graph == this) {
           // null out gGraph if that's the graph being shut down
-          gGraphs.Remove(mAudioChannel);
+          gGraphs.Remove(uint32_t(mAudioChannel));
         }
       }
     } else {
       if (mLifecycleState <= LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP) {
         MessageBlock* block = mBackMessageQueue.AppendElement();
         block->mMessages.SwapElements(mCurrentTaskMessageQueue);
         EnsureNextIterationLocked();
       }
@@ -1561,18 +1561,18 @@ MediaStreamGraphImpl::AppendMessage(Cont
 #ifdef DEBUG
     mCanRunMessagesSynchronously = true;
 #endif
     delete aMessage;
     if (IsEmpty() &&
         mLifecycleState >= LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION) {
 
       MediaStreamGraphImpl* graph;
-      if (gGraphs.Get(mAudioChannel, &graph) && graph == this) {
-        gGraphs.Remove(mAudioChannel);
+      if (gGraphs.Get(uint32_t(mAudioChannel), &graph) && graph == this) {
+        gGraphs.Remove(uint32_t(mAudioChannel));
       }
 
       Destroy();
     }
     return;
   }
 
   mCurrentTaskMessageQueue.AppendElement(aMessage);
@@ -1648,17 +1648,17 @@ MediaStream::Graph()
   return mGraph;
 }
 
 void
 MediaStream::SetGraphImpl(MediaStreamGraphImpl* aGraph)
 {
   MOZ_ASSERT(!mGraph, "Should only be called once");
   mGraph = aGraph;
-  mAudioChannelType = static_cast<AudioChannel>(aGraph->AudioChannel());
+  mAudioChannelType = aGraph->AudioChannel();
   mBuffer.InitGraphRate(aGraph->GraphRate());
 }
 
 void
 MediaStream::SetGraphImpl(MediaStreamGraph* aGraph)
 {
   MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(aGraph);
   SetGraphImpl(graph);
@@ -2562,25 +2562,25 @@ MediaStreamGraphImpl::MediaStreamGraphIm
 #endif
   , mMemoryReportMonitor("MSGIMemory")
   , mSelfRef(this)
   , mAudioStreamSizes()
   , mNeedsMemoryReport(false)
 #ifdef DEBUG
   , mCanRunMessagesSynchronously(false)
 #endif
-  , mAudioChannel(static_cast<uint32_t>(aChannel))
+  , mAudioChannel(aChannel)
 {
   if (!gMediaStreamGraphLog) {
     gMediaStreamGraphLog = PR_NewLogModule("MediaStreamGraph");
   }
 
   if (mRealtime) {
     if (aDriverRequested == AUDIO_THREAD_DRIVER) {
-      AudioCallbackDriver* driver = new AudioCallbackDriver(this, aChannel);
+      AudioCallbackDriver* driver = new AudioCallbackDriver(this);
       mDriver = driver;
       mMixer.AddCallback(driver);
     } else {
       mDriver = new SystemClockDriver(this);
     }
   } else {
     mDriver = new OfflineClockDriver(this, MEDIA_GRAPH_TARGET_PERIOD_MS);
   }
@@ -3077,17 +3077,17 @@ MediaStreamGraph::ApplyAudioContextOpera
 }
 
 bool
 MediaStreamGraph::IsNonRealtime() const
 {
   const MediaStreamGraphImpl* impl = static_cast<const MediaStreamGraphImpl*>(this);
   MediaStreamGraphImpl* graph;
 
-  return !gGraphs.Get(impl->AudioChannel(), &graph) || graph != impl;
+  return !gGraphs.Get(uint32_t(impl->AudioChannel()), &graph) || graph != impl;
 }
 
 void
 MediaStreamGraph::StartNonRealtimeProcessing(uint32_t aTicksToProcess)
 {
   NS_ASSERTION(NS_IsMainThread(), "main thread only");
 
   MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(this);
--- a/dom/media/MediaStreamGraphImpl.h
+++ b/dom/media/MediaStreamGraphImpl.h
@@ -712,17 +712,17 @@ public:
    * Hold a ref to the Latency logger
    */
   RefPtr<AsyncLatencyLogger> mLatencyLog;
   AudioMixer mMixer;
 #ifdef MOZ_WEBRTC
   RefPtr<AudioOutputObserver> mFarendObserverRef;
 #endif
 
-  uint32_t AudioChannel() const { return mAudioChannel; }
+  dom::AudioChannel AudioChannel() const { return mAudioChannel; }
 
 private:
   virtual ~MediaStreamGraphImpl();
 
   MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
 
   /**
    * Used to signal that a memory report has been requested.
@@ -757,16 +757,14 @@ private:
 
 #ifdef DEBUG
   /**
    * Used to assert when AppendMessage() runs ControlMessages synchronously.
    */
   bool mCanRunMessagesSynchronously;
 #endif
 
-  // We use uint32_t instead AudioChannel because this is just used as key for
-  // the hashtable gGraphs.
-  uint32_t mAudioChannel;
+  dom::AudioChannel mAudioChannel;
 };
 
 } // namespace mozilla
 
 #endif /* MEDIASTREAMGRAPHIMPL_H_ */
--- a/dom/media/webspeech/synth/nsSpeechTask.cpp
+++ b/dom/media/webspeech/synth/nsSpeechTask.cpp
@@ -24,18 +24,20 @@ extern PRLogModuleInfo* GetSpeechSynthLo
 #define AUDIO_TRACK 1
 
 namespace mozilla {
 namespace dom {
 
 class SynthStreamListener : public MediaStreamListener
 {
 public:
-  explicit SynthStreamListener(nsSpeechTask* aSpeechTask) :
+  explicit SynthStreamListener(nsSpeechTask* aSpeechTask,
+                               MediaStream* aStream) :
     mSpeechTask(aSpeechTask),
+    mStream(aStream),
     mStarted(false)
   {
   }
 
   void DoNotifyStarted()
   {
     if (mSpeechTask) {
       mSpeechTask->DispatchStartInner();
@@ -58,16 +60,18 @@ public:
         {
           nsCOMPtr<nsIRunnable> runnable =
             NS_NewRunnableMethod(this, &SynthStreamListener::DoNotifyFinished);
           aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
         }
         break;
       case EVENT_REMOVED:
         mSpeechTask = nullptr;
+        // Dereference MediaStream to destroy safety
+        mStream = nullptr;
         break;
       default:
         break;
     }
   }
 
   virtual void NotifyBlockingChanged(MediaStreamGraph* aGraph, Blocking aBlocked) override
   {
@@ -78,16 +82,18 @@ public:
       aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
     }
   }
 
 private:
   // Raw pointer; if we exist, the stream exists,
   // and 'mSpeechTask' exclusively owns it and therefor exists as well.
   nsSpeechTask* mSpeechTask;
+  // This is KungFuDeathGrip for MediaStream
+  RefPtr<MediaStream> mStream;
 
   bool mStarted;
 };
 
 // nsSpeechTask
 
 NS_IMPL_CYCLE_COLLECTION(nsSpeechTask, mSpeechSynthesis, mUtterance, mCallback);
 
@@ -127,16 +133,18 @@ nsSpeechTask::nsSpeechTask(float aVolume
 nsSpeechTask::~nsSpeechTask()
 {
   LOG(LogLevel::Debug, ("~nsSpeechTask"));
   if (mStream) {
     if (!mStream->IsDestroyed()) {
       mStream->Destroy();
     }
 
+    // This will finally destroyed by SynthStreamListener becasue
+    // MediaStream::Destroy() is async.
     mStream = nullptr;
   }
 
   if (mPort) {
     mPort->Destroy();
     mPort = nullptr;
   }
 }
@@ -180,17 +188,17 @@ nsSpeechTask::Setup(nsISpeechTaskCallbac
       NS_WARNING("Audio info arguments in Setup() are ignored for indirect audio services.");
     }
     return NS_OK;
   }
 
   // mStream is set up in Init() that should be called before this.
   MOZ_ASSERT(mStream);
 
-  mStream->AddListener(new SynthStreamListener(this));
+  mStream->AddListener(new SynthStreamListener(this, mStream));
 
   // XXX: Support more than one channel
   if(NS_WARN_IF(!(aChannels == 1))) {
     return NS_ERROR_FAILURE;
   }
 
   mChannels = aChannels;
 
@@ -706,17 +714,17 @@ nsSpeechTask::WindowAudioCaptureChanged(
 {
   // This is not supported yet.
   return NS_OK;
 }
 
 void
 nsSpeechTask::SetAudioOutputVolume(float aVolume)
 {
-  if (mStream) {
+  if (mStream && !mStream->IsDestroyed()) {
     mStream->SetAudioOutputVolume(this, aVolume);
   }
   if (mIndirectAudio) {
     mCallback->OnVolumeChanged(aVolume);
   }
 }
 
 } // namespace dom
--- a/dom/tests/mochitest/fetch/fetch_test_framework.js
+++ b/dom/tests/mochitest/fetch/fetch_test_framework.js
@@ -2,17 +2,18 @@ function testScript(script) {
   // reroute.html should have set this variable if a service worker is present!
   if (!("isSWPresent" in window)) {
     window.isSWPresent = false;
   }
 
   function setupPrefs() {
     return new Promise(function(resolve, reject) {
       SpecialPowers.pushPrefEnv({
-        "set": [["dom.requestcontext.enabled", true],
+        "set": [["dom.requestcache.enabled", true],
+                ["dom.requestcontext.enabled", true],
                 ["dom.serviceWorkers.enabled", true],
                 ["dom.serviceWorkers.testing.enabled", true],
                 ["dom.serviceWorkers.exemptFromPerDomainMax", true]]
       }, resolve);
     });
   }
 
   function workerTest() {
--- a/dom/tests/mochitest/fetch/mochitest.ini
+++ b/dom/tests/mochitest/fetch/mochitest.ini
@@ -31,14 +31,15 @@ skip-if = buildapp == 'b2g' # Bug 113768
 [test_fetch_cors.html]
 skip-if = buildapp == 'b2g' || (toolkit == 'android' && debug) # Bug 1210552 && 1210282
 [test_fetch_cors_sw_reroute.html]
 skip-if = buildapp == 'b2g' || (toolkit == 'android' && debug) # Bug 1137683 && 1210282
 [test_formdataparsing.html]
 [test_formdataparsing_sw_reroute.html]
 skip-if = buildapp == 'b2g' # Bug 1137683
 [test_request.html]
+[test_request_cache.html]
 [test_request_context.html]
 [test_request_sw_reroute.html]
 skip-if = buildapp == 'b2g' # Bug 1137683
 [test_response.html]
 [test_response_sw_reroute.html]
 skip-if = buildapp == 'b2g' # Bug 1137683
--- a/dom/tests/mochitest/fetch/sw_reroute.js
+++ b/dom/tests/mochitest/fetch/sw_reroute.js
@@ -5,17 +5,18 @@ function testScript(script) {
     gRegistration = registration;
 
     var iframe = document.createElement("iframe");
     iframe.src = "reroute.html?" + script.replace(".js", "");
     document.body.appendChild(iframe);
   }
 
   SpecialPowers.pushPrefEnv({
-    "set": [["dom.serviceWorkers.enabled", true],
+    "set": [["dom.requestcache.enabled", true],
+            ["dom.serviceWorkers.enabled", true],
             ["dom.serviceWorkers.interception.opaque.enabled", true],
             ["dom.serviceWorkers.testing.enabled", true],
             ["dom.serviceWorkers.exemptFromPerDomainMax", true]]
   }, function() {
     navigator.serviceWorker.ready.then(setupSW);
     navigator.serviceWorker.register("reroute.js", {scope: "/"});
   });
 }
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/fetch/test_request_cache.html
@@ -0,0 +1,19 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Make sure that Request.cache is not exposed by default</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script>
+var req = new Request("");
+ok(!("cache" in req), "Request.cache should not be exposed by default");
+</script>
+</body>
+</html>
+
--- a/dom/tests/mochitest/general/resource_timing_main_test.html
+++ b/dom/tests/mochitest/general/resource_timing_main_test.html
@@ -207,17 +207,17 @@ function checkEntries(anEntryList) {
   // Check that the iframe's image was NOT added as an entry to this window's performance entry.
   ok(!window.performance.getEntriesByName("http://mochi.test:8888/tests/image/test/mochitest/damon.jpg").length,
     "http://mochi.test:8888/tests/image/test/mochitest/damon.jpg should be a valid entry name");
 }
 
 function firstCheck() {
   is(window.performance.getEntriesByType("resource").length, 1, "The first xhr entry was not added.");
   is(window.performance.getEntriesByType("resource")[0].initiatorType, "xmlhttprequest",
-    "The initiatorType is incorect for this entry");
+    "The initiatorType is incorrect for this entry");
   makeXhr("test-data2.json", secondCheck);
 }
 
 function secondCheck() {
   // Since the buffer max size was set to '1', 'peformance.getEntriesByType()' should
   // return only  '1' entry (first xhr results).
   is(window.performance.getEntriesByType("resource").length, 1, "The second xhr entry should not be " +
     "returned since the buffer size was set to 1.");
--- a/dom/webidl/Clients.webidl
+++ b/dom/webidl/Clients.webidl
@@ -8,16 +8,17 @@
  *
  */
 
 [Exposed=ServiceWorker]
 interface Clients {
   // The objects returned will be new instances every time
   [Throws]
   Promise<sequence<Client>?> matchAll(optional ClientQueryOptions options);
+  [Throws, Func="mozilla::dom::workers::ServiceWorkerGlobalScope::OpenWindowEnabled"]
   Promise<WindowClient> openWindow(USVString url);
   [Throws]
   Promise<void> claim();
 };
 
 dictionary ClientQueryOptions {
   boolean includeUncontrolled = false;
   ClientType type = "window";
--- a/dom/webidl/Request.webidl
+++ b/dom/webidl/Request.webidl
@@ -17,16 +17,17 @@ interface Request {
   readonly attribute USVString url;
   [SameObject] readonly attribute Headers headers;
 
   [Func="mozilla::dom::Request::RequestContextEnabled"]
   readonly attribute RequestContext context;
   readonly attribute DOMString referrer;
   readonly attribute RequestMode mode;
   readonly attribute RequestCredentials credentials;
+  [Func="mozilla::dom::Request::RequestCacheEnabled"]
   readonly attribute RequestCache cache;
   readonly attribute RequestRedirect redirect;
 
   [Throws,
    NewObject] Request clone();
 
   // Bug 1124638 - Allow chrome callers to set the context.
   [ChromeOnly]
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -393,16 +393,24 @@ partial interface Window {
   [ChromeOnly, Throws]
   readonly attribute WindowRoot? windowRoot;
 };
 
 Window implements TouchEventHandlers;
 
 Window implements OnErrorEventHandlerForWindow;
 
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
+// https://compat.spec.whatwg.org/#windoworientation-interface
+partial interface Window {
+  readonly attribute short orientation;
+           attribute EventHandler onorientationchange;
+};
+#endif
+
 // ConsoleAPI
 partial interface Window {
   [Replaceable, GetterThrows]
   readonly attribute Console console;
 };
 
 #ifdef HAVE_SIDEBAR
 // Mozilla extension
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -163,17 +163,19 @@ static_assert(MAX_WORKERS_PER_DOMAIN >= 
 #define PREF_DOM_WORKERNOTIFICATION_ENABLED  "dom.webnotifications.enabled"
 #define PREF_DOM_SERVICEWORKERNOTIFICATION_ENABLED  "dom.webnotifications.serviceworker.enabled"
 #define PREF_WORKERS_LATEST_JS_VERSION "dom.workers.latestJSVersion"
 #define PREF_INTL_ACCEPT_LANGUAGES     "intl.accept_languages"
 #define PREF_SERVICEWORKERS_ENABLED    "dom.serviceWorkers.enabled"
 #define PREF_SERVICEWORKERS_TESTING_ENABLED "dom.serviceWorkers.testing.enabled"
 #define PREF_INTERCEPTION_ENABLED      "dom.serviceWorkers.interception.enabled"
 #define PREF_INTERCEPTION_OPAQUE_ENABLED "dom.serviceWorkers.interception.opaque.enabled"
+#define PREF_OPEN_WINDOW_ENABLED       "dom.serviceWorkers.openWindow.enabled"
 #define PREF_PUSH_ENABLED              "dom.push.enabled"
+#define PREF_REQUESTCACHE_ENABLED      "dom.requestcache.enabled"
 #define PREF_REQUESTCONTEXT_ENABLED    "dom.requestcontext.enabled"
 #define PREF_OFFSCREENCANVAS_ENABLED   "gfx.offscreencanvas.enabled"
 
 namespace {
 
 const uint32_t kNoIndex = uint32_t(-1);
 
 const JS::ContextOptions kRequiredContextOptions =
@@ -1937,16 +1939,20 @@ RuntimeService::Init()
                                   PREF_SERVICEWORKERS_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_SERVICEWORKERS))) ||
       NS_FAILED(Preferences::RegisterCallbackAndCall(
                                   WorkerPrefChanged,
                                   PREF_INTERCEPTION_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_INTERCEPTION_ENABLED))) ||
       NS_FAILED(Preferences::RegisterCallbackAndCall(
                                   WorkerPrefChanged,
+                                  PREF_OPEN_WINDOW_ENABLED,
+                                  reinterpret_cast<void *>(WORKERPREF_OPEN_WINDOW_ENABLED))) ||
+      NS_FAILED(Preferences::RegisterCallbackAndCall(
+                                  WorkerPrefChanged,
                                   PREF_INTERCEPTION_OPAQUE_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_INTERCEPTION_OPAQUE_ENABLED))) ||
       NS_FAILED(Preferences::RegisterCallbackAndCall(
                                   WorkerPrefChanged,
                                   PREF_DOM_CACHES_TESTING_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_DOM_CACHES_TESTING))) ||
       NS_FAILED(Preferences::RegisterCallbackAndCall(
                                   WorkerPrefChanged,
@@ -1957,16 +1963,20 @@ RuntimeService::Init()
                                   PREF_SERVICEWORKERS_TESTING_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_SERVICEWORKERS_TESTING))) ||
       NS_FAILED(Preferences::RegisterCallbackAndCall(
                                   WorkerPrefChanged,
                                   PREF_PUSH_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_PUSH))) ||
       NS_FAILED(Preferences::RegisterCallbackAndCall(
                                   WorkerPrefChanged,
+                                  PREF_REQUESTCACHE_ENABLED,
+                                  reinterpret_cast<void *>(WORKERPREF_REQUESTCACHE))) ||
+      NS_FAILED(Preferences::RegisterCallbackAndCall(
+                                  WorkerPrefChanged,
                                   PREF_REQUESTCONTEXT_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_REQUESTCONTEXT))) ||
       NS_FAILED(Preferences::RegisterCallbackAndCall(
                                   WorkerPrefChanged,
                                   PREF_OFFSCREENCANVAS_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_OFFSCREENCANVAS))) ||
       NS_FAILED(Preferences::RegisterCallback(LoadRuntimeOptions,
                                               PREF_JS_OPTIONS_PREFIX,
@@ -2180,16 +2190,20 @@ RuntimeService::Cleanup()
                                   PREF_INTERCEPTION_OPAQUE_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_INTERCEPTION_OPAQUE_ENABLED))) ||
         NS_FAILED(Preferences::UnregisterCallback(
                                   WorkerPrefChanged,
                                   PREF_INTERCEPTION_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_INTERCEPTION_ENABLED))) ||
         NS_FAILED(Preferences::UnregisterCallback(
                                   WorkerPrefChanged,
+                                  PREF_OPEN_WINDOW_ENABLED,
+                                  reinterpret_cast<void *>(WORKERPREF_OPEN_WINDOW_ENABLED))) ||
+        NS_FAILED(Preferences::UnregisterCallback(
+                                  WorkerPrefChanged,
                                   PREF_SERVICEWORKERS_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_SERVICEWORKERS))) ||
         NS_FAILED(Preferences::UnregisterCallback(
                                   WorkerPrefChanged,
                                   PREF_DOM_CACHES_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_DOM_CACHES))) ||
         NS_FAILED(Preferences::UnregisterCallback(
                                   WorkerPrefChanged,
@@ -2200,16 +2214,20 @@ RuntimeService::Cleanup()
                                   PREF_DOM_SERVICEWORKERNOTIFICATION_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_DOM_SERVICEWORKERNOTIFICATION))) ||
         NS_FAILED(Preferences::UnregisterCallback(
                                   WorkerPrefChanged,
                                   PREF_PUSH_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_PUSH))) ||
         NS_FAILED(Preferences::UnregisterCallback(
                                   WorkerPrefChanged,
+                                  PREF_REQUESTCACHE_ENABLED,
+                                  reinterpret_cast<void *>(WORKERPREF_REQUESTCACHE))) ||
+        NS_FAILED(Preferences::UnregisterCallback(
+                                  WorkerPrefChanged,
                                   PREF_REQUESTCONTEXT_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_REQUESTCONTEXT))) ||
         NS_FAILED(Preferences::UnregisterCallback(
                                   WorkerPrefChanged,
                                   PREF_OFFSCREENCANVAS_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_OFFSCREENCANVAS))) ||
 #if DUMP_CONTROLLED_BY_PREF
         NS_FAILED(Preferences::UnregisterCallback(
@@ -2780,19 +2798,21 @@ RuntimeService::WorkerPrefChanged(const 
     case WORKERPREF_DOM_WORKERNOTIFICATION:
     case WORKERPREF_DOM_SERVICEWORKERNOTIFICATION:
     case WORKERPREF_PERFORMANCE_LOGGING_ENABLED:
 #ifdef DUMP_CONTROLLED_BY_PREF
     case WORKERPREF_DUMP:
 #endif
     case WORKERPREF_INTERCEPTION_ENABLED:
     case WORKERPREF_INTERCEPTION_OPAQUE_ENABLED:
+    case WORKERPREF_OPEN_WINDOW_ENABLED:
     case WORKERPREF_SERVICEWORKERS:
     case WORKERPREF_SERVICEWORKERS_TESTING:
     case WORKERPREF_PUSH:
+    case WORKERPREF_REQUESTCACHE:
     case WORKERPREF_REQUESTCONTEXT:
     case WORKERPREF_OFFSCREENCANVAS:
       sDefaultPreferences[key] = Preferences::GetBool(aPrefName, false);
       break;
 
     default:
       MOZ_ASSERT_UNREACHABLE("Invalid pref key");
       break;
--- a/dom/workers/ServiceWorkerClients.cpp
+++ b/dom/workers/ServiceWorkerClients.cpp
@@ -11,16 +11,25 @@
 #include "ServiceWorkerClients.h"
 #include "ServiceWorkerManager.h"
 #include "ServiceWorkerWindowClient.h"
 
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "WorkerScope.h"
 
+#include "nsIDocShell.h"
+#include "nsIDOMChromeWindow.h"
+#include "nsIDOMWindow.h"
+#include "nsIWebNavigation.h"
+#include "nsIWindowMediator.h"
+#include "nsIWebProgress.h"
+#include "nsIWebProgressListener.h"
+#include "nsWeakReference.h"
+
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::workers;
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(ServiceWorkerClients)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(ServiceWorkerClients)
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ServiceWorkerClients, mWorkerScope)
 
@@ -194,16 +203,347 @@ public:
 
     AutoJSAPI jsapi;
     jsapi.Init();
     r->Dispatch(jsapi.cx());
     return NS_OK;
   }
 };
 
+class ResolveOpenWindowRunnable final : public WorkerRunnable
+{
+public:
+  ResolveOpenWindowRunnable(PromiseWorkerProxy* aPromiseProxy,
+                            UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
+                            const nsresult aStatus)
+  : WorkerRunnable(aPromiseProxy->GetWorkerPrivate(), WorkerThreadModifyBusyCount)
+  , mPromiseProxy(aPromiseProxy)
+  , mClientInfo(Move(aClientInfo))
+  , mStatus(aStatus)
+  {
+    AssertIsOnMainThread();
+    MOZ_ASSERT(aPromiseProxy);
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    aWorkerPrivate->AssertIsOnWorkerThread();
+
+    Promise* promise = mPromiseProxy->WorkerPromise();
+    if (NS_WARN_IF(NS_FAILED(mStatus))) {
+      promise->MaybeReject(mStatus);
+    } else if (mClientInfo) {
+      RefPtr<ServiceWorkerWindowClient> client =
+        new ServiceWorkerWindowClient(promise->GetParentObject(),
+                                      *mClientInfo);
+      promise->MaybeResolve(client);
+    } else {
+      promise->MaybeResolve(JS::NullHandleValue);
+    }
+
+    mPromiseProxy->CleanUp(aCx);
+    return true;
+  }
+
+private:
+  RefPtr<PromiseWorkerProxy> mPromiseProxy;
+  UniquePtr<ServiceWorkerClientInfo> mClientInfo;
+  const nsresult mStatus;
+};
+
+class WebProgressListener final : public nsIWebProgressListener,
+                                  public nsSupportsWeakReference
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(WebProgressListener, nsIWebProgressListener)
+
+  WebProgressListener(PromiseWorkerProxy* aPromiseProxy,
+                      ServiceWorkerPrivate* aServiceWorkerPrivate,
+                      nsPIDOMWindow* aWindow,
+                      nsIURI* aBaseURI)
+  : mPromiseProxy(aPromiseProxy)
+  , mServiceWorkerPrivate(aServiceWorkerPrivate)
+  , mWindow(aWindow)
+  , mBaseURI(aBaseURI)
+  {
+    MOZ_ASSERT(aPromiseProxy);
+    MOZ_ASSERT(aServiceWorkerPrivate);
+    MOZ_ASSERT(aWindow);
+    MOZ_ASSERT(aWindow->IsOuterWindow());
+    MOZ_ASSERT(aBaseURI);
+    AssertIsOnMainThread();
+
+    mServiceWorkerPrivate->StoreISupports(static_cast<nsIWebProgressListener*>(this));
+  }
+
+  NS_IMETHOD
+  OnStateChange(nsIWebProgress* aWebProgress,
+                nsIRequest* aRequest,
+                uint32_t aStateFlags, nsresult aStatus) override
+  {
+    if (!(aStateFlags & STATE_IS_DOCUMENT) ||
+         !(aStateFlags & (STATE_STOP | STATE_TRANSFERRING))) {
+      return NS_OK;
+    }
+
+    // Our caller keeps a strong reference, so it is safe to remove the listener
+    // from ServiceWorkerPrivate.
+    mServiceWorkerPrivate->RemoveISupports(static_cast<nsIWebProgressListener*>(this));
+    aWebProgress->RemoveProgressListener(this);
+
+    MutexAutoLock lock(mPromiseProxy->Lock());
+    if (mPromiseProxy->CleanedUp()) {
+      return NS_OK;
+    }
+
+    nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();
+    UniquePtr<ServiceWorkerClientInfo> clientInfo;
+    if (doc) {
+      // Check same origin.
+      nsCOMPtr<nsIScriptSecurityManager> securityManager =
+        nsContentUtils::GetSecurityManager();
+      nsresult rv = securityManager->CheckSameOriginURI(doc->GetOriginalURI(),
+                                                        mBaseURI, false);
+      if (NS_SUCCEEDED(rv)) {
+        clientInfo.reset(new ServiceWorkerClientInfo(doc));
+      }
+    }
+
+    RefPtr<ResolveOpenWindowRunnable> r =
+      new ResolveOpenWindowRunnable(mPromiseProxy,
+                                    Move(clientInfo),
+                                    NS_OK);
+    AutoJSAPI jsapi;
+    jsapi.Init();
+    JSContext* cx = jsapi.cx();
+    r->Dispatch(cx);
+
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  OnProgressChange(nsIWebProgress* aWebProgress,
+                   nsIRequest* aRequest,
+                   int32_t aCurSelfProgress,
+                   int32_t aMaxSelfProgress,
+                   int32_t aCurTotalProgress,
+                   int32_t aMaxTotalProgress) override
+  {
+    MOZ_ASSERT(false, "Unexpected notification.");
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  OnLocationChange(nsIWebProgress* aWebProgress,
+                   nsIRequest* aRequest,
+                   nsIURI* aLocation,
+                   uint32_t aFlags) override
+  {
+    MOZ_ASSERT(false, "Unexpected notification.");
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  OnStatusChange(nsIWebProgress* aWebProgress,
+                 nsIRequest* aRequest,
+                 nsresult aStatus, const char16_t* aMessage) override
+  {
+    MOZ_ASSERT(false, "Unexpected notification.");
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  OnSecurityChange(nsIWebProgress* aWebProgress,
+                   nsIRequest* aRequest,
+                   uint32_t aState) override
+  {
+    MOZ_ASSERT(false, "Unexpected notification.");
+    return NS_OK;
+  }
+
+private:
+  ~WebProgressListener()
+  { }
+
+  RefPtr<PromiseWorkerProxy> mPromiseProxy;
+  RefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate;
+  nsCOMPtr<nsPIDOMWindow> mWindow;
+  nsCOMPtr<nsIURI> mBaseURI;
+};
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(WebProgressListener)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(WebProgressListener)
+NS_IMPL_CYCLE_COLLECTION(WebProgressListener, mPromiseProxy,
+                         mServiceWorkerPrivate, mWindow)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebProgressListener)
+  NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
+  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+NS_INTERFACE_MAP_END
+
+class OpenWindowRunnable final : public nsRunnable
+{
+  RefPtr<PromiseWorkerProxy> mPromiseProxy;
+  nsString mUrl;
+  nsString mScope;
+
+public:
+  OpenWindowRunnable(PromiseWorkerProxy* aPromiseProxy,
+                     const nsAString& aUrl,
+                     const nsAString& aScope)
+    : mPromiseProxy(aPromiseProxy)
+    , mUrl(aUrl)
+    , mScope(aScope)
+  {
+    MOZ_ASSERT(aPromiseProxy);
+    MOZ_ASSERT(aPromiseProxy->GetWorkerPrivate());
+    aPromiseProxy->GetWorkerPrivate()->AssertIsOnWorkerThread();
+  }
+
+  NS_IMETHOD
+  Run() override
+  {
+    AssertIsOnMainThread();
+
+    MutexAutoLock lock(mPromiseProxy->Lock());
+    if (mPromiseProxy->CleanedUp()) {
+      return NS_OK;
+    }
+
+    nsCOMPtr<nsPIDOMWindow> window;
+    nsresult rv = OpenWindow(getter_AddRefs(window));
+    if (NS_SUCCEEDED(rv)) {
+      MOZ_ASSERT(window);
+
+      WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
+      MOZ_ASSERT(workerPrivate);
+
+      WorkerPrivate::LocationInfo& info = workerPrivate->GetLocationInfo();
+      nsCOMPtr<nsIURI> baseURI;
+      nsresult rv = NS_NewURI(getter_AddRefs(baseURI), info.mOrigin);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return NS_ERROR_FAILURE;
+      }
+
+      nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
+      nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(docShell);
+
+      if (!webProgress) {
+        return NS_ERROR_FAILURE;
+      }
+
+      RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+      MOZ_ASSERT(swm);
+
+      nsCOMPtr<nsIPrincipal> principal = workerPrivate->GetPrincipal();
+      MOZ_ASSERT(principal);
+      RefPtr<ServiceWorkerRegistrationInfo> registration =
+        swm->GetRegistration(principal, NS_ConvertUTF16toUTF8(mScope));
+      if (NS_WARN_IF(!registration)) {
+        return NS_ERROR_FAILURE;
+      }
+      RefPtr<ServiceWorkerInfo> serviceWorkerInfo =
+        registration->GetServiceWorkerInfoById(workerPrivate->ServiceWorkerID());
+      if (NS_WARN_IF(!serviceWorkerInfo)) {
+        return NS_ERROR_FAILURE;
+      }
+
+      nsCOMPtr<nsIWebProgressListener> listener =
+        new WebProgressListener(mPromiseProxy, serviceWorkerInfo->WorkerPrivate(),
+                                window, baseURI);
+
+      rv = webProgress->AddProgressListener(listener,
+                                            nsIWebProgress::NOTIFY_STATE_DOCUMENT);
+      MOZ_ASSERT(NS_SUCCEEDED(rv));
+      return NS_OK;
+    }
+
+    RefPtr<ResolveOpenWindowRunnable> resolveRunnable =
+      new ResolveOpenWindowRunnable(mPromiseProxy, nullptr, rv);
+
+    AutoJSAPI jsapi;
+    jsapi.Init();
+    NS_WARN_IF(!resolveRunnable->Dispatch(jsapi.cx()));
+
+    return NS_OK;
+  }
+
+private:
+  nsresult
+  OpenWindow(nsPIDOMWindow** aWindow)
+  {
+    WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
+
+    // [[1. Let url be the result of parsing url with entry settings object's API
+    //   base URL.]]
+    nsCOMPtr<nsIURI> uri;
+    WorkerPrivate::LocationInfo& info = workerPrivate->GetLocationInfo();
+
+    nsCOMPtr<nsIURI> baseURI;
+    nsresult rv = NS_NewURI(getter_AddRefs(baseURI), info.mOrigin);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return NS_ERROR_TYPE_ERR;
+    }
+
+    rv = NS_NewURI(getter_AddRefs(uri), mUrl, nullptr, baseURI);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return NS_ERROR_TYPE_ERR;
+    }
+
+    // [[6.1 Open Window]]
+    nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID,
+                                                   &rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    // Find the most recent browser window and open a new tab in it.
+    nsCOMPtr<nsIDOMWindow> browserWindow;
+    rv = wm->GetMostRecentWindow(MOZ_UTF16("navigator:browser"),
+                                 getter_AddRefs(browserWindow));
+    if (NS_WARN_IF(NS_FAILED(rv)) || !browserWindow) {
+      // It is possible to be running without a browser window on Mac OS, so
+      // we need to open a new chrome window.
+      // TODO(catalinb): open new chrome window. Bug 1218080
+      return NS_ERROR_FAILURE;
+    }
+
+    nsCOMPtr<nsIDOMChromeWindow> chromeWin = do_QueryInterface(browserWindow);
+    if (NS_WARN_IF(!chromeWin)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    nsCOMPtr<nsIBrowserDOMWindow> bwin;
+    chromeWin->GetBrowserDOMWindow(getter_AddRefs(bwin));
+
+    if (NS_WARN_IF(!bwin)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    nsCOMPtr<nsIDOMWindow> win;
+    rv = bwin->OpenURI(uri, nullptr,
+                       nsIBrowserDOMWindow::OPEN_DEFAULTWINDOW,
+                       nsIBrowserDOMWindow::OPEN_NEW,
+                       getter_AddRefs(win));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+    NS_ENSURE_STATE(win);
+
+    nsCOMPtr<nsPIDOMWindow> pWin = do_QueryInterface(win);
+    pWin = pWin->GetOuterWindow();
+    pWin.forget(aWindow);
+
+    return NS_OK;
+  }
+};
+
 } // namespace
 
 already_AddRefed<Promise>
 ServiceWorkerClients::MatchAll(const ClientQueryOptions& aOptions,
                                ErrorResult& aRv)
 {
   WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
   MOZ_ASSERT(workerPrivate);
@@ -232,25 +572,61 @@ ServiceWorkerClients::MatchAll(const Cli
   RefPtr<MatchAllRunnable> r =
     new MatchAllRunnable(promiseProxy,
                          NS_ConvertUTF16toUTF8(scope));
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
   return promise.forget();
 }
 
 already_AddRefed<Promise>
-ServiceWorkerClients::OpenWindow(const nsAString& aUrl)
+ServiceWorkerClients::OpenWindow(const nsAString& aUrl,
+                                 ErrorResult& aRv)
 {
-  ErrorResult result;
-  RefPtr<Promise> promise = Promise::Create(mWorkerScope, result);
-  if (NS_WARN_IF(result.Failed())) {
+  // XXXcatalinb: This works only on non-multiprocess for now, bail if we're
+  // running in a content process.
+  if (XRE_IsContentProcess()) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return nullptr;
+  }
+
+  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+  MOZ_ASSERT(workerPrivate);
+
+  RefPtr<Promise> promise = Promise::Create(mWorkerScope, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  promise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
+  if (aUrl.EqualsLiteral("about:blank")) {
+    promise->MaybeReject(NS_ERROR_TYPE_ERR);
+    return promise.forget();
+  }
+
+  // [[4. If this algorithm is not allowed to show a popup ..]]
+  // In Gecko the service worker is allowed to show a popup only if the user
+  // just clicked on a notification.
+  if (!workerPrivate->GlobalScope()->WindowInteractionAllowed()) {
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    return promise.forget();
+  }
+
+  RefPtr<PromiseWorkerProxy> promiseProxy =
+    PromiseWorkerProxy::Create(workerPrivate, promise);
+
+  if (!promiseProxy) {
+    return nullptr;
+  }
+
+  nsString scope;
+  mWorkerScope->GetScope(scope);
+
+  RefPtr<OpenWindowRunnable> r = new OpenWindowRunnable(promiseProxy,
+                                                          aUrl, scope);
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
+
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 ServiceWorkerClients::Claim(ErrorResult& aRv)
 {
   WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
   MOZ_ASSERT(workerPrivate);
--- a/dom/workers/ServiceWorkerClients.h
+++ b/dom/workers/ServiceWorkerClients.h
@@ -28,17 +28,17 @@ public:
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ServiceWorkerClients)
 
   explicit ServiceWorkerClients(ServiceWorkerGlobalScope* aWorkerScope);
 
   already_AddRefed<Promise>
   MatchAll(const ClientQueryOptions& aOptions, ErrorResult& aRv);
 
   already_AddRefed<Promise>
-  OpenWindow(const nsAString& aUrl);
+  OpenWindow(const nsAString& aUrl, ErrorResult& aRv);
 
   already_AddRefed<Promise>
   Claim(ErrorResult& aRv);
 
   JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   ServiceWorkerGlobalScope*
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -377,16 +377,31 @@ ServiceWorkerRegistrationInfo::ServiceWo
 
 ServiceWorkerRegistrationInfo::~ServiceWorkerRegistrationInfo()
 {
   if (IsControllingDocuments()) {
     NS_WARNING("ServiceWorkerRegistrationInfo is still controlling documents. This can be a bug or a leak in ServiceWorker API or in any other API that takes the document alive.");
   }
 }
 
+already_AddRefed<ServiceWorkerInfo>
+ServiceWorkerRegistrationInfo::GetServiceWorkerInfoById(uint64_t aId)
+{
+  RefPtr<ServiceWorkerInfo> serviceWorker;
+  if (mInstallingWorker && mInstallingWorker->ID() == aId) {
+    serviceWorker = mInstallingWorker;
+  } else if (mWaitingWorker && mWaitingWorker->ID() == aId) {
+    serviceWorker = mWaitingWorker;
+  } else if (mActiveWorker && mActiveWorker->ID() == aId) {
+    serviceWorker = mActiveWorker;
+  }
+
+  return serviceWorker.forget();
+}
+
 //////////////////////////
 // ServiceWorkerManager //
 //////////////////////////
 
 NS_IMPL_ADDREF(ServiceWorkerManager)
 NS_IMPL_RELEASE(ServiceWorkerManager)
 
 NS_INTERFACE_MAP_BEGIN(ServiceWorkerManager)
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -91,16 +91,19 @@ public:
       newest = mWaitingWorker;
     } else {
       newest = mActiveWorker;
     }
 
     return newest.forget();
   }
 
+  already_AddRefed<ServiceWorkerInfo>
+  GetServiceWorkerInfoById(uint64_t aId);
+
   void
   StartControllingADocument()
   {
     ++mControlledDocumentsCounter;
   }
 
   void
   StopControllingADocument()
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -8,17 +8,23 @@
 
 #include "ServiceWorkerManager.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 BEGIN_WORKERS_NAMESPACE
 
-NS_IMPL_ISUPPORTS0(ServiceWorkerPrivate)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(ServiceWorkerPrivate)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(ServiceWorkerPrivate)
+NS_IMPL_CYCLE_COLLECTION(ServiceWorkerPrivate, mSupportsArray)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerPrivate)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
 
 // Tracks the "dom.disable_open_click_delay" preference.  Modified on main
 // thread, read on worker threads.
 // It is updated every time a "notificationclick" event is dispatched. While
 // this is done without synchronization, at the worst, the thread will just get
 // an older value within which a popup is allowed to be displayed, which will
 // still be a valid value since it was set prior to dispatching the runnable.
 Atomic<uint32_t> gDOMDisableOpenClickDelay(0);
@@ -62,16 +68,17 @@ ServiceWorkerPrivate::ServiceWorkerPriva
   MOZ_ASSERT(mIdleWorkerTimer);
 }
 
 ServiceWorkerPrivate::~ServiceWorkerPrivate()
 {
   MOZ_ASSERT(!mWorkerPrivate);
   MOZ_ASSERT(!mTokenCount);
   MOZ_ASSERT(!mInfo);
+  MOZ_ASSERT(mSupportsArray.IsEmpty());
 
   mIdleWorkerTimer->Cancel();
 }
 
 nsresult
 ServiceWorkerPrivate::SendMessageEvent(JSContext* aCx,
                                        JS::Handle<JS::Value> aMessage,
                                        const Optional<Sequence<JS::Value>>& aTransferable,
@@ -1144,16 +1151,20 @@ ServiceWorkerPrivate::SpawnWorkerIfNeede
 
   if (mWorkerPrivate) {
     mWorkerPrivate->UpdateOverridenLoadGroup(aLoadGroup);
     ResetIdleTimeout(aWhy);
 
     return NS_OK;
   }
 
+  // Sanity check: mSupportsArray should be empty if we're about to
+  // spin up a new worker.
+  MOZ_ASSERT(mSupportsArray.IsEmpty());
+
   if (NS_WARN_IF(!mInfo)) {
     NS_WARNING("Trying to wake up a dead service worker.");
     return NS_ERROR_FAILURE;
   }
 
   // TODO(catalinb): Bug 1192138 - Add telemetry for service worker wake-ups.
 
   // Ensure that the IndexedDatabaseManager is initialized
@@ -1221,16 +1232,33 @@ ServiceWorkerPrivate::SpawnWorkerIfNeede
 
   mIsPushWorker = false;
   ResetIdleTimeout(aWhy);
 
   return NS_OK;
 }
 
 void
+ServiceWorkerPrivate::StoreISupports(nsISupports* aSupports)
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(mWorkerPrivate);
+  MOZ_ASSERT(!mSupportsArray.Contains(aSupports));
+
+  mSupportsArray.AppendElement(aSupports);
+}
+
+void
+ServiceWorkerPrivate::RemoveISupports(nsISupports* aSupports)
+{
+  AssertIsOnMainThread();
+  mSupportsArray.RemoveElement(aSupports);
+}
+
+void
 ServiceWorkerPrivate::TerminateWorker()
 {
   AssertIsOnMainThread();
 
   mIdleWorkerTimer->Cancel();
   mKeepAliveToken = nullptr;
   if (mWorkerPrivate) {
     if (Preferences::GetBool("dom.serviceWorkers.testing.enabled")) {
@@ -1239,16 +1267,17 @@ ServiceWorkerPrivate::TerminateWorker()
         os->NotifyObservers(this, "service-worker-shutdown", nullptr);
       }
     }
 
     AutoJSAPI jsapi;
     jsapi.Init();
     NS_WARN_IF(!mWorkerPrivate->Terminate(jsapi.cx()));
     mWorkerPrivate = nullptr;
+    mSupportsArray.Clear();
   }
 }
 
 void
 ServiceWorkerPrivate::NoteDeadServiceWorkerInfo()
 {
   AssertIsOnMainThread();
   mInfo = nullptr;
--- a/dom/workers/ServiceWorkerPrivate.h
+++ b/dom/workers/ServiceWorkerPrivate.h
@@ -56,17 +56,18 @@ public:
 // with an appropriate reason before any runnable is dispatched to the worker.
 // If the event is extendable then the runnable should inherit
 // ExtendableEventWorkerRunnable.
 class ServiceWorkerPrivate final : public nsISupports
 {
   friend class KeepAliveToken;
 
 public:
-  NS_DECL_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(ServiceWorkerPrivate)
 
   explicit ServiceWorkerPrivate(ServiceWorkerInfo* aInfo);
 
   nsresult
   SendMessageEvent(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                    const Optional<Sequence<JS::Value>>& aTransferable,
                    UniquePtr<ServiceWorkerClientInfo>&& aClientInfo);
 
@@ -101,16 +102,22 @@ public:
                              const nsAString& aScope);
 
   nsresult
   SendFetchEvent(nsIInterceptedChannel* aChannel,
                  nsILoadGroup* aLoadGroup,
                  UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
                  bool aIsReload);
 
+  void
+  StoreISupports(nsISupports* aSupports);
+
+  void
+  RemoveISupports(nsISupports* aSupports);
+
   // This will terminate the current running worker thread and drop the
   // workerPrivate reference.
   // Called by ServiceWorkerInfo when [[Clear Registration]] is invoked
   // or whenever the spec mandates that we terminate the worker.
   // This is a no-op if the worker has already been stopped.
   void
   TerminateWorker();
 
@@ -172,15 +179,21 @@ private:
   // is created.
   bool mIsPushWorker;
 
   // We keep a token for |dom.serviceWorkers.idle_timeout| seconds to give the
   // worker a grace period after each event.
   RefPtr<KeepAliveToken> mKeepAliveToken;
 
   uint64_t mTokenCount;
+
+  // Meant for keeping objects alive while handling requests from the worker
+  // on the main thread. Access to this array is provided through
+  // |StoreISupports| and |RemoveISupports|. Note that the array is also
+  // cleared whenever the worker is terminated.
+  nsTArray<nsCOMPtr<nsISupports>> mSupportsArray;
 };
 
 } // namespace workers
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_workers_serviceworkerprivate_h
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -1261,16 +1261,23 @@ public:
   bool
   InterceptionEnabled() const
   {
     AssertIsOnWorkerThread();
     return mPreferences[WORKERPREF_INTERCEPTION_ENABLED];
   }
 
   bool
+  OpenWindowEnabled() const
+  {
+    AssertIsOnWorkerThread();
+    return mPreferences[WORKERPREF_OPEN_WINDOW_ENABLED];
+  }
+
+  bool
   OpaqueInterceptionEnabled() const
   {
     AssertIsOnWorkerThread();
     return mPreferences[WORKERPREF_INTERCEPTION_OPAQUE_ENABLED];
   }
 
   bool
   DOMWorkerNotificationEnabled() const
@@ -1303,16 +1310,23 @@ public:
   bool
   PushEnabled() const
   {
     AssertIsOnWorkerThread();
     return mPreferences[WORKERPREF_PUSH];
   }
 
   bool
+  RequestCacheEnabled() const
+  {
+    AssertIsOnWorkerThread();
+    return mPreferences[WORKERPREF_REQUESTCACHE];
+  }
+
+  bool
   RequestContextEnabled() const
   {
     AssertIsOnWorkerThread();
     return mPreferences[WORKERPREF_REQUESTCONTEXT];
   }
 
   bool
   OffscreenCanvasEnabled() const
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -629,16 +629,25 @@ bool
 ServiceWorkerGlobalScope::InterceptionEnabled(JSContext* aCx, JSObject* aObj)
 {
   WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
   MOZ_ASSERT(worker);
   worker->AssertIsOnWorkerThread();
   return worker->InterceptionEnabled();
 }
 
+bool
+ServiceWorkerGlobalScope::OpenWindowEnabled(JSContext* aCx, JSObject* aObj)
+{
+  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+  MOZ_ASSERT(worker);
+  worker->AssertIsOnWorkerThread();
+  return worker->OpenWindowEnabled();
+}
+
 WorkerDebuggerGlobalScope::WorkerDebuggerGlobalScope(
                                                   WorkerPrivate* aWorkerPrivate)
 : mWorkerPrivate(aWorkerPrivate)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 }
 
 WorkerDebuggerGlobalScope::~WorkerDebuggerGlobalScope()
--- a/dom/workers/WorkerScope.h
+++ b/dom/workers/WorkerScope.h
@@ -244,16 +244,19 @@ public:
 
   virtual bool
   WrapGlobalObject(JSContext* aCx,
                    JS::MutableHandle<JSObject*> aReflector) override;
 
   static bool
   InterceptionEnabled(JSContext* aCx, JSObject* aObj);
 
+  static bool
+  OpenWindowEnabled(JSContext* aCx, JSObject* aObj);
+
   void
   GetScope(nsString& aScope) const
   {
     aScope = mScope;
   }
 
   ServiceWorkerClients*
   Clients();
--- a/dom/workers/Workers.h
+++ b/dom/workers/Workers.h
@@ -196,23 +196,25 @@ struct JSSettings
 };
 
 enum WorkerPreference
 {
   WORKERPREF_DUMP = 0, // browser.dom.window.dump.enabled
   WORKERPREF_DOM_CACHES, // dom.caches.enabled
   WORKERPREF_SERVICEWORKERS, // dom.serviceWorkers.enabled
   WORKERPREF_INTERCEPTION_ENABLED, // dom.serviceWorkers.interception.enabled
+  WORKERPREF_OPEN_WINDOW_ENABLED, // dom.serviceWorkers.openWindow.enabled
   WORKERPREF_DOM_WORKERNOTIFICATION, // dom.webnotifications.workers.enabled
   WORKERPREF_DOM_SERVICEWORKERNOTIFICATION, // dom.webnotifications.serviceworker.enabled
   WORKERPREF_DOM_CACHES_TESTING, // dom.caches.testing.enabled
   WORKERPREF_SERVICEWORKERS_TESTING, // dom.serviceWorkers.testing.enabled
   WORKERPREF_INTERCEPTION_OPAQUE_ENABLED, // dom.serviceWorkers.interception.opaque.enabled
   WORKERPREF_PERFORMANCE_LOGGING_ENABLED, // dom.performance.enable_user_timing_logging
   WORKERPREF_PUSH, // dom.push.enabled
+  WORKERPREF_REQUESTCACHE, // dom.requestcache.enabled
   WORKERPREF_REQUESTCONTEXT, // dom.requestcontext.enabled
   WORKERPREF_OFFSCREENCANVAS, // gfx.offscreencanvas.enabled
   WORKERPREF_COUNT
 };
 
 // Implemented in WorkerPrivate.cpp
 
 struct WorkerLoadInfo
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/779707.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+  var x = new XMLHttpRequest();
+  x.open('GET', "data:text/plain,2", false);
+  x.send();
+
+  new Worker("data:text/javascript,3");
+}
+
+</script>
+</head>
+
+<body onload="boom();"></body>
+</html>
--- a/dom/workers/test/crashtests.list
+++ b/dom/workers/test/crashtests.list
@@ -1,2 +1,3 @@
+load 779707.html
 load 943516.html
 load 1158031.html
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -179,16 +179,18 @@ support-files =
   fetch/plugin/plugins.html
   eventsource/*
   sw_clients/file_blob_upload_frame.html
   redirect_post.sjs
   xslt_worker.js
   xslt/*
   unresolved_fetch_worker.js
   header_checker.sjs
+  openWindow_worker.js
+  redirect.sjs
 
 [test_app_protocol.html]
 skip-if = release_build
 [test_bug1151916.html]
 [test_claim.html]
 [test_claim_fetch.html]
 [test_claim_oninstall.html]
 [test_close.html]
@@ -278,8 +280,10 @@ skip-if = toolkit == "android" || toolki
 [test_not_intercept_plugin.html]
 [test_file_blob_upload.html]
 [test_unresolved_fetch_interception.html]
 [test_hsts_upgrade_intercept.html]
 skip-if = e10s # Bug 1214305
 [test_csp_upgrade-insecure_intercept.html]
 skip-if = e10s # Bug 1214305
 [test_serviceworker_header.html]
+[test_openWindow.html]
+skip-if = toolkit == "android" || toolkit == "gonk" || e10s
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/openWindow_worker.js
@@ -0,0 +1,90 @@
+// the worker won't shut down between events because we increased
+// the timeout values.
+var client;
+
+function testForUrl(url, throwType, clientProperties, resultsArray) {
+  return clients.openWindow(url)
+    .then(function(e) {
+      if (throwType != null) {
+        resultsArray.push({
+          result: false,
+          message: "openWindow should throw " + throwType
+        });
+      } else if (clientProperties) {
+        resultsArray.push({
+          result: (e instanceof WindowClient),
+          message: "openWindow should resolve to a WindowClient"
+        });
+        resultsArray.push({
+          result: e.url == clientProperties.url,
+          message: "Client url should be " + clientProperties.url
+        });
+        // Add more properties
+      } else {
+        resultsArray.push({
+          result: e == null,
+          message: "Open window should resolve to null. Got: " + e
+        });
+      }
+    })
+    .catch(function(err) {
+      if (throwType == null) {
+        resultsArray.push({
+          result: false,
+          message: "Unexpected throw: " + err
+        });
+      } else {
+        resultsArray.push({
+          result: err.toString().indexOf(throwType) >= 0,
+          message: "openWindow should throw: " + err
+        });
+      }
+    })
+}
+
+onmessage = function(event) {
+  client = event.source;
+
+  var results = [];
+  var promises = [];
+  promises.push(testForUrl("about:blank", "TypeError", null, results));
+  promises.push(testForUrl("http://example.com", "InvalidAccessError", null, results));
+  promises.push(testForUrl("_._*`InvalidURL", "InvalidAccessError", null, results));
+  Promise.all(promises).then(function(e) {
+    client.postMessage(results);
+  });
+}
+
+onnotificationclick = function(e) {
+  var results = [];
+  var promises = [];
+
+  promises.push(testForUrl("about:blank", "TypeError", null, results));
+  promises.push(testForUrl("http://example.com", null, null, results));
+  promises.push(testForUrl("http://mochi.test:8888/same_origin.html", null,
+                           {url: "http://mochi.test:8888/same_origin.html"}, results));
+
+  // redirect tests
+  var redirect = "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/redirect.sjs?"
+  var baseURL = "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/"
+  promises.push(testForUrl(redirect + "same_origin_redirect.html", null,
+			   {url: baseURL + "same_origin_redirect.html"}, results));
+  promises.push(testForUrl(redirect + "http://example.com/redirect_to_other_origin.html", null,
+			   null, results));
+
+  var redirect_xorigin = "http://example.com/tests/dom/workers/test/serviceworkers/redirect.sjs?"
+  promises.push(testForUrl(redirect_xorigin + "xorigin_redirect.html", null,
+			   null, results));
+  promises.push(testForUrl(redirect_xorigin + "http://mochi.test:8888/xorigin_to_same_origin.html", null,
+			   {url: "http://mochi.test:8888/xorigin_to_same_origin.html"}, results));
+
+  Promise.all(promises).then(function(e) {
+    client.postMessage(results);
+  });
+}
+
+onfetch = function(e) {
+  if (e.request.url.indexOf(same_origin) >= 0) {
+    e.respondWith(new Response("same_origin_window"));
+  }
+}
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/redirect.sjs
@@ -0,0 +1,5 @@
+function handleRequest(request, response)
+{
+  response.setStatusLine(request.httpVersion, 301, "Moved Permanently");
+  response.setHeader("Location", request.queryString, false);
+}
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_openWindow.html
@@ -0,0 +1,111 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1172870
+-->
+<head>
+  <title>Bug 1172870 - Test clients.openWindow</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/MockServices.js"></script>
+  <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/NotificationTest.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=1172870">Bug 1172870</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+<script type="text/javascript">
+  SimpleTest.requestFlakyTimeout("Mock alert service dispatches show and click events.");
+
+  function setup(ctx) {
+    MockServices.register();
+
+    return navigator.serviceWorker.register("openWindow_worker.js", {scope: "./"})
+      .then(function(swr) {
+        ok(swr, "Registration successful");
+        ctx.registration = swr;
+        return ctx;
+      });
+  }
+
+  function waitForActiveServiceWorker(ctx) {
+    return navigator.serviceWorker.ready.then(function(result) {
+      ok(ctx.registration.active, "Service Worker is active");
+      return ctx;
+    });
+  }
+
+  function setupMessageHandler(ctx) {
+    return new Promise(function(res, rej) {
+      navigator.serviceWorker.onmessage = function(event) {
+        navigator.serviceWorker.onmessage = null;
+        for (i = 0; i < event.data.length; i++) {
+          ok(event.data[i].result, event.data[i].message);
+        }
+        res(ctx);
+      }
+    });
+  }
+
+  function testPopupNotAllowed(ctx) {
+    var p = setupMessageHandler(ctx);
+    ok(ctx.registration.active, "Worker is active.");
+    ctx.registration.active.postMessage("testNoPopup");
+
+    return p;
+  }
+
+  function testPopupAllowed(ctx) {
+    var p = setupMessageHandler(ctx);
+    ctx.registration.showNotification("testPopup");
+
+    return p;
+  }
+
+  function clear(ctx) {
+    MockServices.unregister();
+
+    var browser = SpecialPowers.Services.wm.getMostRecentWindow("navigator:browser").gBrowser;
+
+    ok(browser.tabs.length == 7, "Total number of tabs is correct.");
+    while (browser.tabs.length > 1) {
+      browser.removeTab(browser.tabs[1]);
+    }
+
+    return ctx.registration.unregister().then(function(result) {
+      ctx.registration = null;
+      ok(result, "Unregister was successful.");
+    });
+  }
+
+  function runTest() {
+    setup({})
+      .then(waitForActiveServiceWorker)
+      // Permission to allow popups persists for some time after a notification
+      // click event, so the order here is important.
+      .then(testPopupNotAllowed)
+      .then(testPopupAllowed)
+      .then(clear)
+      .catch(function(e) {
+        ok(false, "Some test failed with error " + e);
+      }).then(SimpleTest.finish);
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  SpecialPowers.pushPrefEnv({"set": [
+    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+    ["dom.serviceWorkers.enabled", true],
+    ["dom.serviceWorkers.testing.enabled", true],
+    ["dom.webnotifications.workers.enabled", true],
+    ["dom.webnotifications.serviceworker.enabled", true],
+    ["notification.prompt.testing", true],
+    ["dom.disable_open_click_delay", 1000],
+    ["dom.serviceWorkers.idle_timeout", 299999],
+    ["dom.serviceWorkers.idle_extended_timeout", 299999]
+  ]}, runTest);
+</script>
+</body>
+</html>
--- a/editor/composer/crashtests/crashtests.list
+++ b/editor/composer/crashtests/crashtests.list
@@ -1,6 +1,6 @@
 load 351236-1.html
 load 407062-1.html
 load 419563-1.xhtml
 load 428844-1.html
 load 461049-1.html
-asserts(0-1) asserts-if(winWidget||Android,0-2) load removing-editable-xslt.html # bug 500847
+load removing-editable-xslt.html
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/crashtests/667321-1.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+  document.body.style.cssFloat = "left";
+  document.createElement("div").appendChild(document.querySelector("legend"));
+}
+
+</script>
+</head>
+<body onload="boom();"><fieldset><legend></legend><textarea></textarea></fieldset></body>
+</html>
--- a/editor/libeditor/crashtests/crashtests.list
+++ b/editor/libeditor/crashtests/crashtests.list
@@ -31,16 +31,17 @@ load 580151-1.xhtml
 load 582138-1.xhtml
 load 612565-1.html
 asserts(0-6) load 615015-1.html # Bug 439258
 load 615450-1.html
 load 633709.xhtml
 load 636074-1.html
 load 639736-1.xhtml
 load 643786-1.html
+load 667321-1.html
 load 682650-1.html
 load 713427-1.html
 load 713427-2.xhtml
 asserts(0-1) load 716456-1.html
 load 759748.html
 load 761861.html
 load 762183.html
 load 769008-1.html
--- a/editor/libeditor/nsHTMLDataTransfer.cpp
+++ b/editor/libeditor/nsHTMLDataTransfer.cpp
@@ -1204,17 +1204,17 @@ nsHTMLEditor::InsertFromTransferable(nsI
   return rv;
 }
 
 static void
 GetStringFromDataTransfer(nsIDOMDataTransfer *aDataTransfer, const nsAString& aType,
                           int32_t aIndex, nsAString& aOutputString)
 {
   nsCOMPtr<nsIVariant> variant;
-  aDataTransfer->MozGetDataAt(aType, aIndex, getter_AddRefs(variant));
+  DataTransfer::Cast(aDataTransfer)->GetDataAtNoSecurityCheck(aType, aIndex, getter_AddRefs(variant));
   if (variant)
     variant->GetAsAString(aOutputString);
 }
 
 nsresult nsHTMLEditor::InsertFromDataTransfer(DataTransfer *aDataTransfer,
                                               int32_t aIndex,
                                               nsIDOMDocument *aSourceDoc,
                                               nsIDOMNode *aDestinationNode,
@@ -1239,17 +1239,17 @@ nsresult nsHTMLEditor::InsertFromDataTra
 
     if (!isText) {
       if (type.EqualsLiteral(kFileMime) ||
           type.EqualsLiteral(kJPEGImageMime) ||
           type.EqualsLiteral(kJPGImageMime) ||
           type.EqualsLiteral(kPNGImageMime) ||
           type.EqualsLiteral(kGIFImageMime)) {
         nsCOMPtr<nsIVariant> variant;
-        aDataTransfer->MozGetDataAt(type, aIndex, getter_AddRefs(variant));
+        DataTransfer::Cast(aDataTransfer)->GetDataAtNoSecurityCheck(type, aIndex, getter_AddRefs(variant));
         if (variant) {
           nsCOMPtr<nsISupports> object;
           variant->GetAsISupports(getter_AddRefs(object));
           return InsertObject(NS_ConvertUTF16toUTF8(type).get(), object, isSafe,
                               aSourceDoc, aDestinationNode, aDestOffset, aDoDeleteSelection);
         }
       }
       else if (!hasPrivateHTMLFlavor && type.EqualsLiteral(kNativeHTMLMime)) {
--- a/editor/libeditor/nsPlaintextDataTransfer.cpp
+++ b/editor/libeditor/nsPlaintextDataTransfer.cpp
@@ -140,18 +140,18 @@ NS_IMETHODIMP nsPlaintextEditor::InsertT
 nsresult nsPlaintextEditor::InsertFromDataTransfer(DataTransfer *aDataTransfer,
                                                    int32_t aIndex,
                                                    nsIDOMDocument *aSourceDoc,
                                                    nsIDOMNode *aDestinationNode,
                                                    int32_t aDestOffset,
                                                    bool aDoDeleteSelection)
 {
   nsCOMPtr<nsIVariant> data;
-  aDataTransfer->MozGetDataAt(NS_LITERAL_STRING("text/plain"), aIndex,
-                              getter_AddRefs(data));
+  DataTransfer::Cast(aDataTransfer)->GetDataAtNoSecurityCheck(NS_LITERAL_STRING("text/plain"), aIndex,
+                                                              getter_AddRefs(data));
   if (data) {
     nsAutoString insertText;
     data->GetAsAString(insertText);
     nsContentUtils::PlatformToDOMLineBreaks(insertText);
 
     nsAutoEditBatch beginBatching(this);
     return InsertTextAt(insertText, aDestinationNode, aDestOffset, aDoDeleteSelection);
   }
--- a/gfx/2d/ScaleFactors2D.h
+++ b/gfx/2d/ScaleFactors2D.h
@@ -2,17 +2,16 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_GFX_SCALEFACTORS2D_H_
 #define MOZILLA_GFX_SCALEFACTORS2D_H_
 
 #include <ostream>
-#include <iomanip>
 
 #include "mozilla/Attributes.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/gfx/ScaleFactor.h"
 
 #include "gfxPoint.h"
 
 namespace mozilla {
--- a/gfx/angle/moz.build
+++ b/gfx/angle/moz.build
@@ -107,21 +107,31 @@ SOURCES += [
     'src/compiler/translator/glslang_lex.cpp',
     'src/compiler/translator/glslang_tab.cpp',
 ]
 
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += [
         '-Wno-attributes',
+        '-Wno-shadow',
         '-Wno-sign-compare',
         '-Wno-unknown-pragmas',
+        '-Wno-unreachable-code',
     ]
     if CONFIG['CLANG_CXX']:
-        CXXFLAGS += ['-Wno-unused-private-field']
+        CXXFLAGS += [
+            '-Wno-inconsistent-missing-override',
+            '-Wno-unused-private-field',
+        ]
+    else:
+        CXXFLAGS += [
+            '-Wno-shadow-compatible-local',
+            '-Wno-shadow-local',
+        ]
 
 if CONFIG['MOZ_DIRECTX_SDK_PATH'] and not CONFIG['MOZ_HAS_WINSDK_WITH_D3D']:
     CXXFLAGS += ['-I\'%s/include/\'' % CONFIG['MOZ_DIRECTX_SDK_PATH']]
 
 DEFINES['_CRT_SECURE_NO_DEPRECATE'] = True
 DEFINES['_HAS_EXCEPTIONS'] = 0
 
 if not CONFIG['MOZ_DEBUG']:
--- a/gfx/angle/src/libANGLE/moz.build
+++ b/gfx/angle/src/libANGLE/moz.build
@@ -255,21 +255,31 @@ if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']:
     SOURCES += [
         'renderer/d3d/d3d11/SwapChain11.cpp',
     ]
 
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += [
         '-Wno-attributes',
+        '-Wno-shadow',
         '-Wno-sign-compare',
         '-Wno-unknown-pragmas',
+        '-Wno-unreachable-code',
     ]
     if CONFIG['CLANG_CXX']:
-        CXXFLAGS += ['-Wno-unused-private-field']
+        CXXFLAGS += [
+            '-Wno-inconsistent-missing-override',
+            '-Wno-unused-private-field',
+        ]
+    else:
+        CXXFLAGS += [
+            '-Wno-shadow-compatible-local',
+            '-Wno-shadow-local',
+        ]
 
 if CONFIG['MOZ_DIRECTX_SDK_PATH'] and not CONFIG['MOZ_HAS_WINSDK_WITH_D3D']:
     CXXFLAGS += ['-I\'%s/include/\'' % CONFIG['MOZ_DIRECTX_SDK_PATH']]
 
 DEFINES['_CRT_SECURE_NO_DEPRECATE'] = True
 DEFINES['_HAS_EXCEPTIONS'] = 0
 
 if not CONFIG['MOZ_DEBUG']:
--- a/gfx/angle/src/libEGL/moz.build
+++ b/gfx/angle/src/libEGL/moz.build
@@ -11,21 +11,31 @@
 UNIFIED_SOURCES += [
     'libEGL.cpp',
 ]
 
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += [
         '-Wno-attributes',
+        '-Wno-shadow',
         '-Wno-sign-compare',
         '-Wno-unknown-pragmas',
+        '-Wno-unreachable-code',
     ]
     if CONFIG['CLANG_CXX']:
-        CXXFLAGS += ['-Wno-unused-private-field']
+        CXXFLAGS += [
+            '-Wno-inconsistent-missing-override',
+            '-Wno-unused-private-field',
+        ]
+    else:
+        CXXFLAGS += [
+            '-Wno-shadow-compatible-local',
+            '-Wno-shadow-local',
+        ]
 
 if CONFIG['MOZ_DIRECTX_SDK_PATH'] and not CONFIG['MOZ_HAS_WINSDK_WITH_D3D']:
     CXXFLAGS += ['-I\'%s/include/\'' % CONFIG['MOZ_DIRECTX_SDK_PATH']]
 
 DEFINES['_CRT_SECURE_NO_DEPRECATE'] = True
 DEFINES['_HAS_EXCEPTIONS'] = 0
 
 if not CONFIG['MOZ_DEBUG']:
--- a/gfx/angle/src/libGLESv2/moz.build
+++ b/gfx/angle/src/libGLESv2/moz.build
@@ -17,21 +17,31 @@ UNIFIED_SOURCES += [
     'global_state.cpp',
     'libGLESv2.cpp',
 ]
 
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += [
         '-Wno-attributes',
+        '-Wno-shadow',
         '-Wno-sign-compare',
         '-Wno-unknown-pragmas',
+        '-Wno-unreachable-code',
     ]
     if CONFIG['CLANG_CXX']:
-        CXXFLAGS += ['-Wno-unused-private-field']
+        CXXFLAGS += [
+            '-Wno-inconsistent-missing-override',
+            '-Wno-unused-private-field',
+        ]
+    else:
+        CXXFLAGS += [
+            '-Wno-shadow-compatible-local',
+            '-Wno-shadow-local',
+        ]
 
 if CONFIG['MOZ_DIRECTX_SDK_PATH'] and not CONFIG['MOZ_HAS_WINSDK_WITH_D3D']:
     CXXFLAGS += ['-I\'%s/include/\'' % CONFIG['MOZ_DIRECTX_SDK_PATH']]
 
 DEFINES['_CRT_SECURE_NO_DEPRECATE'] = True
 DEFINES['_HAS_EXCEPTIONS'] = 0
 
 if not CONFIG['MOZ_DEBUG']:
--- a/gfx/cairo/cairo/src/cairo-pdf-operators.c
+++ b/gfx/cairo/cairo/src/cairo-pdf-operators.c
@@ -731,17 +731,17 @@ static cairo_int_status_t
      */
     if (fabs(ctm->xx) == 1.0 && fabs(ctm->yy) == 1.0 &&
 	fabs(ctm->xy) == 0.0 && fabs(ctm->yx) == 0.0)
     {
 	has_ctm = FALSE;
     }
 
     /* The PDF CTM is transformed to the user space CTM when stroking
-     * so the corect pen shape will be used. This also requires that
+     * so the correct pen shape will be used. This also requires that
      * the path be transformed to user space when emitted. The
      * conversion of path coordinates to user space may cause rounding
      * errors. For example the device space point (1.234, 3.142) when
      * transformed to a user space CTM of [100 0 0 100 0 0] will be
      * emitted as (0.012, 0.031).
      *
      * To avoid the rounding problem we scale the user space CTM
      * matrix so that all the non translation components of the matrix
--- a/gfx/layers/apz/src/TaskThrottler.cpp
+++ b/gfx/layers/apz/src/TaskThrottler.cpp
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- */
 /* vim: set sw=2 sts=2 ts=8 et 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 "TaskThrottler.h"
 
-#include "base/message_loop.h"
+#include "mozilla/layers/APZThreadUtils.h"
 
 #define TASK_LOG(...)
 // #define TASK_LOG(...) printf_stderr("TASK: " __VA_ARGS__)
 
 namespace mozilla {
 namespace layers {
 
 TaskThrottler::TaskThrottler(const TimeStamp& aTimeStamp, const TimeDuration& aMaxWait)
@@ -44,18 +44,17 @@ TaskThrottler::PostTask(const tracked_ob
     CancelPendingTask(lock);
     if (TimeSinceLastRequest(aTimeStamp, lock) < mMaxWait) {
       mQueuedTask = Move(aTask);
       TASK_LOG("%p queued task %p\n", this, mQueuedTask.get());
       // Make sure the queued task is sent after mMaxWait time elapses,
       // even if we don't get a TaskComplete() until then.
       TimeDuration timeout = mMaxWait - TimeSinceLastRequest(aTimeStamp, lock);
       mTimeoutTask = NewRunnableMethod(this, &TaskThrottler::OnTimeout);
-      MessageLoop::current()->PostDelayedTask(FROM_HERE, mTimeoutTask,
-          timeout.ToMilliseconds());
+      APZThreadUtils::RunDelayedTaskOnCurrentThread(mTimeoutTask, timeout);
       return;
     }
     // we've been waiting for more than the max-wait limit, so just fall through
     // and send the new task already.
   }
 
   mStartTime = aTimeStamp;
   aTask->Run();
--- a/gfx/layers/apz/util/APZThreadUtils.cpp
+++ b/gfx/layers/apz/util/APZThreadUtils.cpp
@@ -76,12 +76,29 @@ APZThreadUtils::RunOnControllerThread(Ta
     aTask->Run();
     delete aTask;
   } else {
     sControllerThread->PostTask(FROM_HERE, aTask);
   }
 #endif
 }
 
+/*static*/ void
+APZThreadUtils::RunDelayedTaskOnCurrentThread(Task* aTask,
+                                              const TimeDuration& aDelay)
+{
+  if (MessageLoop* messageLoop = MessageLoop::current()) {
+    messageLoop->PostDelayedTask(FROM_HERE, aTask, aDelay.ToMilliseconds());
+  } else {
+#ifdef MOZ_ANDROID_APZ
+    // Fennec does not have a MessageLoop::current() on the controller thread.
+    AndroidBridge::Bridge()->PostTaskToUiThread(aTask, aDelay.ToMilliseconds());
+#else
+    // Other platforms should.
+    MOZ_RELEASE_ASSERT(false, "This non-Fennec platform should have a MessageLoop::current()");
+#endif
+  }
+}
+
 NS_IMPL_ISUPPORTS(GenericTimerCallbackBase, nsITimerCallback)
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/apz/util/APZThreadUtils.h
+++ b/gfx/layers/apz/util/APZThreadUtils.h
@@ -2,16 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_layers_APZThreadUtils_h
 #define mozilla_layers_APZThreadUtils_h
 
 #include "base/message_loop.h"
+#include "mozilla/TimeStamp.h"  // for TimeDuration
 #include "nsITimer.h"
 
 class Task;
 
 namespace mozilla {
 namespace layers {
 
 class APZThreadUtils
@@ -45,16 +46,22 @@ public:
   static void AssertOnCompositorThread();
 
   /**
    * Run the given task on the APZ "controller thread" for this platform. If
    * this function is called from the controller thread itself then the task is
    * run immediately without getting queued.
    */
   static void RunOnControllerThread(Task* aTask);
+
+  /**
+   * Runs the given task on the current thread after a delay of |aDelay|.
+   */
+  static void RunDelayedTaskOnCurrentThread(Task* aTask,
+                                            const TimeDuration& aDelay);
 };
 
 // A base class for GenericTimerCallback<Function>.
 // This is necessary because NS_IMPL_ISUPPORTS doesn't work for a class
 // template.
 class GenericTimerCallbackBase : public nsITimerCallback
 {
 public:
--- a/gfx/layers/composite/ContentHost.cpp
+++ b/gfx/layers/composite/ContentHost.cpp
@@ -222,16 +222,21 @@ void
 ContentHostTexture::UseTextureHost(const nsTArray<TimedTexture>& aTextures)
 {
   ContentHostBase::UseTextureHost(aTextures);
   MOZ_ASSERT(aTextures.Length() == 1);
   const TimedTexture& t = aTextures[0];
   MOZ_ASSERT(t.mPictureRect.IsEqualInterior(
       nsIntRect(nsIntPoint(0, 0), nsIntSize(t.mTexture->GetSize()))),
       "Only default picture rect supported");
+
+  if (t.mTexture != mTextureHost) {
+    mReceivedNewHost = true;
+  }
+
   mTextureHost = t.mTexture;
   mTextureHostOnWhite = nullptr;
   mTextureSourceOnWhite = nullptr;
   if (mTextureHost) {
     mTextureHost->PrepareTextureSource(mTextureSource);
   }
 }
 
@@ -325,16 +330,21 @@ ContentHostSingleBuffered::UpdateThebes(
 
   if (!mTextureHost) {
     mInitialised = false;
     return true; // FIXME should we return false? Returning true for now
   }              // to preserve existing behavior of NOT causing IPC errors.
 
   // updated is in screen coordinates. Convert it to buffer coordinates.
   nsIntRegion destRegion(aUpdated);
+
+  if (mReceivedNewHost) {
+    destRegion.Or(destRegion, aOldValidRegionBack);
+    mReceivedNewHost = false;
+  }
   destRegion.MoveBy(-aData.rect().TopLeft());
 
   if (!aData.rect().Contains(aUpdated.GetBounds()) ||
       aData.rotation().x > aData.rect().width ||
       aData.rotation().y > aData.rect().height) {
     NS_ERROR("Invalid update data");
     return false;
   }
--- a/gfx/layers/composite/ContentHost.h
+++ b/gfx/layers/composite/ContentHost.h
@@ -107,16 +107,17 @@ protected:
  * use up to two TextureHosts.
  */
 class ContentHostTexture : public ContentHostBase
 {
 public:
   explicit ContentHostTexture(const TextureInfo& aTextureInfo)
     : ContentHostBase(aTextureInfo)
     , mLocked(false)
+    , mReceivedNewHost(false)
   { }
 
   virtual void Composite(LayerComposite* aLayer,
                          EffectChain& aEffectChain,
                          float aOpacity,
                          const gfx::Matrix4x4& aTransform,
                          const gfx::Filter& aFilter,
                          const gfx::Rect& aClipRect,
@@ -166,16 +167,17 @@ public:
   virtual already_AddRefed<TexturedEffect> GenEffect(const gfx::Filter& aFilter) override;
 
 protected:
   CompositableTextureHostRef mTextureHost;
   CompositableTextureHostRef mTextureHostOnWhite;
   CompositableTextureSourceRef mTextureSource;
   CompositableTextureSourceRef mTextureSourceOnWhite;
   bool mLocked;
+  bool mReceivedNewHost;
 };
 
 /**
  * Double buffering is implemented by swapping the front and back TextureHosts.
  * We assume that whenever we use double buffering, then we have
  * render-to-texture and thus no texture upload to do.
  */
 class ContentHostDoubleBuffered : public ContentHostTexture
new file mode 100644
--- /dev/null
+++ b/gfx/tests/crashtests/557348-1.html
@@ -0,0 +1,1 @@
+<html style="background: -moz-repeating-radial-gradient(left center , circle closest-side, red, white 100px, black); width: 300px; height: 1px;"></html>
new file mode 100644
--- /dev/null
+++ b/gfx/tests/crashtests/710149-1.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+
+<script>
+
+function boom()
+{
+  var d = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
+  d.style.setProperty("-moz-transform", "translate(0pt, 10px)", "");
+  d.style.setProperty("opacity", "0.8", "");
+  d.style.setProperty("background-color", "gray", "");
+  var c = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+  (d).appendChild(c);
+  (document.body).appendChild(d);
+  c.getContext("2d");
+}
+
+</script>
+
+<body onload="setTimeout(boom, 100);"></body>
--- a/gfx/tests/crashtests/crashtests.list
+++ b/gfx/tests/crashtests/crashtests.list
@@ -77,31 +77,32 @@ load 474410-1.html
 load 483120-1.xhtml
 load 483120-2.xhtml
 load 487549-1.html
 load 487724-1.html
 load 490777-1.html
 load 532726-1.html
 load 538065-1.html
 load 546870-1.html
-load balinese-letter-spacing.html
+load 557348-1.html
 load 580100-1.html
 load 580212-1.html
 load 580233-1.html
 load 580719-1.html
 load 593526.html
 load 593526.xul
 load 594654-1.xhtml
 load 595727-1.html
 load 624198.xhtml
 load 633453-1.html
 load 633322-1.html
 load 665218.html
 load 686190-1.html
 load 693143-1.html
+load 710149-1.html
 load 768079-1.html
 load 783041-1.html
 load 783041-2.html
 load 783041-3.html
 load 783041-4.html
 asserts-if(gtkWidget,0-1) load 798853.html # bug 868792
 asserts-if(winWidget,0-1) skip-if(B2G||Android) load 815489.html # bug 871763, 1216304
 load 836225-1.html
@@ -111,8 +112,9 @@ load 893572-1.html
 load 893572-2.html
 load 893572-3.html
 load 893572-4.html
 pref(layers.force-active,true) load 914457-1.html
 load 944579.svg
 load 944579.html
 pref(security.fileuri.strict_origin_policy,false) load 950000.html
 load 1034403-1.html
+load balinese-letter-spacing.html
--- a/image/Downscaler.cpp
+++ b/image/Downscaler.cpp
@@ -69,18 +69,18 @@ Downscaler::BeginFrame(const nsIntSize& 
              "Created a downscaler, but width is larger");
   MOZ_ASSERT(mTargetSize.height <= aOriginalSize.height,
              "Created a downscaler, but height is larger");
   MOZ_ASSERT(aOriginalSize.width > 0 && aOriginalSize.height > 0,
              "Invalid original size");
 
   mFrameRect = aFrameRect.valueOr(nsIntRect(nsIntPoint(), aOriginalSize));
   MOZ_ASSERT(mFrameRect.x >= 0 && mFrameRect.y >= 0 &&
-             mFrameRect.width > 0 && mFrameRect.height > 0,
-             "Frame rect must have positive components");
+             mFrameRect.width >= 0 && mFrameRect.height >= 0,
+             "Frame rect must have non-negative components");
   MOZ_ASSERT(nsIntRect(0, 0, aOriginalSize.width, aOriginalSize.height)
                .Contains(mFrameRect),
              "Frame rect must fit inside image");
   MOZ_ASSERT_IF(!nsIntRect(0, 0, aOriginalSize.width, aOriginalSize.height)
                   .IsEqualEdges(mFrameRect),
                 aHasAlpha);
 
   mOriginalSize = aOriginalSize;
@@ -154,18 +154,23 @@ Downscaler::SkipToRow(int32_t aRow)
 void
 Downscaler::ResetForNextProgressivePass()
 {
   mPrevInvalidatedLine = 0;
   mCurrentOutLine = 0;
   mCurrentInLine = 0;
   mLinesInBuffer = 0;
 
-  // If we have a vertical offset, commit rows to shift us past it.
-  SkipToRow(mFrameRect.y);
+  if (mFrameRect.IsEmpty()) {
+    // Our frame rect is zero size; commit rows until the end of the image.
+    SkipToRow(mOriginalSize.height - 1);
+  } else {
+    // If we have a vertical offset, commit rows to shift us past it.
+    SkipToRow(mFrameRect.y);
+  }
 }
 
 static void
 GetFilterOffsetAndLength(UniquePtr<skia::ConvolutionFilter1D>& aFilter,
                          int32_t aOutputImagePosition,
                          int32_t* aFilterOffsetOut,
                          int32_t* aFilterLengthOut)
 {
--- a/image/Downscaler.h
+++ b/image/Downscaler.h
@@ -78,16 +78,18 @@ public:
    *                        the way they are stored in some image formats.
    */
   nsresult BeginFrame(const nsIntSize& aOriginalSize,
                       const Maybe<nsIntRect>& aFrameRect,
                       uint8_t* aOutputBuffer,
                       bool aHasAlpha,
                       bool aFlipVertically = false);
 
+  bool IsFrameComplete() const { return mCurrentInLine >= mOriginalSize.height; }
+
   /// Retrieves the buffer into which the Decoder should write each row.
   uint8_t* RowBuffer()
   {
     return mRowBuffer.get() + mFrameRect.x * sizeof(uint32_t);
   }
 
   /// Clears the current row buffer (optionally starting at @aStartingAtCol).
   void ClearRow(uint32_t aStartingAtCol = 0);
@@ -156,16 +158,17 @@ public:
   const nsIntSize& TargetSize() const { return nsIntSize(); }
   const gfxSize& Scale() const { return gfxSize(1.0, 1.0); }
 
   nsresult BeginFrame(const nsIntSize&, const Maybe<nsIntRect>&, uint8_t*, bool, bool = false)
   {
     return NS_ERROR_FAILURE;
   }
 
+  bool IsFrameComplete() const { return false; }
   uint8_t* RowBuffer() { return nullptr; }
   void ClearRow(uint32_t = 0) { }
   void CommitRow() { }
   bool HasInvalidation() const { return false; }
   DownscalerInvalidRect TakeInvalidRect() { return DownscalerInvalidRect(); }
   void ResetForNextProgressivePass() { }
 };
 
--- a/image/decoders/nsGIFDecoder2.cpp
+++ b/image/decoders/nsGIFDecoder2.cpp
@@ -202,17 +202,17 @@ nsGIFDecoder2::BeginGIF()
   }
 
   mGIFOpen = true;
 
   PostSize(mGIFStruct.screen_width, mGIFStruct.screen_height);
 }
 
 bool
-nsGIFDecoder2::CheckForTransparency(IntRect aFrameRect)
+nsGIFDecoder2::CheckForTransparency(const IntRect& aFrameRect)
 {
   // Check if the image has a transparent color in its palette.
   if (mGIFStruct.is_transparent) {
     PostHasTransparency();
     return true;
   }
 
   if (mGIFStruct.images_decoded > 0) {
@@ -226,16 +226,32 @@ nsGIFDecoder2::CheckForTransparency(IntR
     PostHasTransparency();
     mSawTransparency = true;  // Make sure we don't optimize it away.
     return true;
   }
 
   return false;
 }
 
+IntRect
+nsGIFDecoder2::ClampToImageRect(const IntRect& aRect)
+{
+  IntRect imageRect(0, 0, mGIFStruct.screen_width, mGIFStruct.screen_height);
+  IntRect visibleFrameRect = aRect.Intersect(imageRect);
+
+  // If there's no intersection, |visibleFrameRect| will be an empty rect
+  // positioned at the maximum of |imageRect|'s and |aRect|'s coordinates, which
+  // is not what we want. Force it to (0, 0) in that case.
+  if (visibleFrameRect.IsEmpty()) {
+    visibleFrameRect.MoveTo(0, 0);
+  }
+
+  return visibleFrameRect;
+}
+
 //******************************************************************************
 nsresult
 nsGIFDecoder2::BeginImageFrame(uint16_t aDepth)
 {
   MOZ_ASSERT(HasSize());
 
   IntRect frameRect(mGIFStruct.x_offset, mGIFStruct.y_offset,
                     mGIFStruct.width, mGIFStruct.height);
@@ -270,18 +286,18 @@ nsGIFDecoder2::BeginImageFrame(uint16_t 
 
   mCurrentFrameIndex = mGIFStruct.images_decoded;
 
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (mDownscaler) {
-    rv = mDownscaler->BeginFrame(GetSize(), Some(frameRect), mImageData,
-                                 hasTransparency);
+    rv = mDownscaler->BeginFrame(GetSize(), Some(ClampToImageRect(frameRect)),
+                                 mImageData, hasTransparency);
   }
 
   return rv;
 }
 
 
 //******************************************************************************
 void
@@ -482,16 +498,19 @@ nsGIFDecoder2::OutputRow()
 //******************************************************************************
 // Perform Lempel-Ziv-Welch decoding
 bool
 nsGIFDecoder2::DoLzw(const uint8_t* q)
 {
   if (!mGIFStruct.rows_remaining) {
     return true;
   }
+  if (MOZ_UNLIKELY(mDownscaler && mDownscaler->IsFrameComplete())) {
+    return true;
+  }
 
   // Copy all the decoder state variables into locals so the compiler
   // won't worry about them being aliased.  The locals will be homed
   // back into the GIF decoder structure when we exit.
   int avail       = mGIFStruct.avail;
   int bits        = mGIFStruct.bits;
   int codesize    = mGIFStruct.codesize;
   int codemask    = mGIFStruct.codemask;
@@ -538,16 +557,20 @@ nsGIFDecoder2::DoLzw(const uint8_t* q)
       }
 
       // Check for explicit end-of-stream code
       if (code == (clear_code + 1)) {
         // end-of-stream should only appear after all image data
         return (mGIFStruct.rows_remaining == 0);
       }
 
+      if (MOZ_UNLIKELY(mDownscaler && mDownscaler->IsFrameComplete())) {
+        goto END;
+      }
+
       if (oldcode == -1) {
         if (code >= MAX_BITS) {
           return false;
         }
         *rowp++ = suffix[code] & mColorMask; // ensure index is within colormap
         if (rowp == rowend) {
           OUTPUT_ROW();
         }
--- a/image/decoders/nsGIFDecoder2.h
+++ b/image/decoders/nsGIFDecoder2.h
@@ -45,17 +45,18 @@ private:
   void      FlushImageData();
   void      FlushImageData(uint32_t fromRow, uint32_t rows);
 
   nsresult  GifWrite(const uint8_t* buf, uint32_t numbytes);
   uint32_t  OutputRow();
   bool      DoLzw(const uint8_t* q);
   bool      SetHold(const uint8_t* buf, uint32_t count,
                     const uint8_t* buf2 = nullptr, uint32_t count2 = 0);
-  bool      CheckForTransparency(gfx::IntRect aFrameRect);
+  bool      CheckForTransparency(const gfx::IntRect& aFrameRect);
+  gfx::IntRect ClampToImageRect(const gfx::IntRect& aFrameRect);
 
   inline int ClearCode() const { return 1 << mGIFStruct.datasize; }
 
   int32_t mCurrentRow;
   int32_t mLastFlushedRow;
 
   uint32_t mOldColor;        // The old value of the transparent pixel
 
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -100,17 +100,17 @@ BinaryLeft(ParseNode* pn)
     MOZ_ASSERT(pn->isArity(PN_BINARY));
     return pn->pn_left;
 }
 
 static inline ParseNode*
 ReturnExpr(ParseNode* pn)
 {
     MOZ_ASSERT(pn->isKind(PNK_RETURN));
-    return BinaryLeft(pn);
+    return UnaryKid(pn);
 }
 
 static inline ParseNode*
 TernaryKid1(ParseNode* pn)
 {
     MOZ_ASSERT(pn->isArity(PN_TERNARY));
     return pn->pn_kid1;
 }
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -2701,21 +2701,21 @@ ASTSerializer::statement(ParseNode* pn, 
         RootedValue arg(cx);
 
         return optExpression(pn->pn_kid, &arg) &&
                builder.throwStatement(arg, &pn->pn_pos, dst);
       }
 
       case PNK_RETURN:
       {
-        MOZ_ASSERT_IF(pn->pn_left, pn->pn_pos.encloses(pn->pn_left->pn_pos));
+        MOZ_ASSERT_IF(pn->pn_kid, pn->pn_pos.encloses(pn->pn_kid->pn_pos));
 
         RootedValue arg(cx);
 
-        return optExpression(pn->pn_left, &arg) &&
+        return optExpression(pn->pn_kid, &arg) &&
                builder.returnStatement(arg, &pn->pn_pos, dst);
       }
 
       case PNK_DEBUGGER:
         return builder.debuggerStatement(&pn->pn_pos, dst);
 
       case PNK_CLASS:
         return classDefinition(pn, false, dst);
@@ -3610,17 +3610,17 @@ ASTSerializer::functionArgsAndBody(Parse
         pnargs = nullptr;
         pnbody = pn;
     }
 
     /* Serialize the arguments and body. */
     switch (pnbody->getKind()) {
       case PNK_RETURN: /* expression closure, no destructured args */
         return functionArgs(pn, pnargs, pnbody, args, defaults, rest) &&
-               expression(pnbody->pn_left, body);
+               expression(pnbody->pn_kid, body);
 
       case PNK_STATEMENTLIST:     /* statement closure */
       {
         ParseNode* pnstart = pnbody->pn_head;
 
         // Skip over initial yield in generator.
         if (pnstart && pnstart->isKind(PNK_YIELD)) {
             MOZ_ASSERT(pnstart->getOp() == JSOP_INITIALYIELD);
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -1909,17 +1909,17 @@ TypedObject::obj_setProperty(JSContext* 
                 return false;
             }
             return result.failReadOnly();
         }
 
         uint32_t index;
         if (IdIsIndex(id, &index)) {
             if (!receiver.isObject() || obj != &receiver.toObject())
-                return SetPropertyByDefining(cx, obj, id, v, receiver, result);
+                return SetPropertyByDefining(cx, id, v, receiver, result);
 
             if (index >= uint32_t(typedObj->length())) {
                 JS_ReportErrorNumber(cx, GetErrorMessage,
                                      nullptr, JSMSG_TYPEDOBJECT_BINARYARRAY_BAD_INDEX);
                 return false;
             }
 
             Rooted<TypeDescr*> elementType(cx);
@@ -1935,17 +1935,17 @@ TypedObject::obj_setProperty(JSContext* 
       case type::Struct: {
         Rooted<StructTypeDescr*> descr(cx, &typedObj->typeDescr().as<StructTypeDescr>());
 
         size_t fieldIndex;
         if (!descr->fieldIndex(id, &fieldIndex))
             break;
 
         if (!receiver.isObject() || obj != &receiver.toObject())
-            return SetPropertyByDefining(cx, obj, id, v, receiver, result);
+            return SetPropertyByDefining(cx, id, v, receiver, result);
 
         size_t offset = descr->fieldOffset(fieldIndex);
         Rooted<TypeDescr*> fieldType(cx, &descr->fieldDescr(fieldIndex));
         RootedAtom fieldName(cx, &descr->fieldName(fieldIndex));
         if (!ConvertAndCopyTo(cx, fieldType, typedObj, offset, fieldName, v))
             return false;
         return result.succeed();
       }
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -636,20 +636,20 @@ NonLocalExitScope::prepareForNonLocalJum
             break;
 
           case StmtType::SPREAD:
             MOZ_ASSERT_UNREACHABLE("can't break/continue/return from inside a spread");
             break;
 
           case StmtType::SUBROUTINE:
             /*
-             * There's a [exception or hole, retsub pc-index] pair on the
-             * stack that we need to pop.
+             * There's a [exception or hole, retsub pc-index] pair and the
+             * possible return value on the stack that we need to pop.
              */
-            npops += 2;
+            npops += 3;
             break;
 
           default:;
         }
 
         if (stmt->isBlockScope) {
             StaticBlockObject& blockObj = stmt->staticBlock();
             if (blockObj.needsClone()) {
@@ -1032,18 +1032,18 @@ BytecodeEmitter::emitIndexOp(JSOp op, ui
 }
 
 bool
 BytecodeEmitter::emitAtomOp(JSAtom* atom, JSOp op)
 {
     MOZ_ASSERT(atom);
     MOZ_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
 
-    // .generator and .genrval lookups should be emitted as JSOP_GETALIASEDVAR
-    // instead of JSOP_GETNAME etc, to bypass |with| objects on the scope chain.
+    // .generator lookups should be emitted as JSOP_GETALIASEDVAR instead of
+    // JSOP_GETNAME etc, to bypass |with| objects on the scope chain.
     MOZ_ASSERT_IF(op == JSOP_GETNAME || op == JSOP_GETGNAME, !sc->isDotVariable(atom));
 
     if (op == JSOP_GETPROP && atom == cx->names().length) {
         /* Specialize length accesses for the interpreter. */
         op = JSOP_LENGTH;
     }
 
     jsatomid index;
@@ -5030,17 +5030,19 @@ BytecodeEmitter::emitTry(ParseNode* pn)
 
         finallyStart = offset();
 
         // Indicate that we're emitting a subroutine body.
         stmtInfo.type = StmtType::SUBROUTINE;
         if (!updateSourceCoordNotes(pn->pn_kid3->pn_pos.begin))
             return false;
         if (!emit1(JSOP_FINALLY) ||
+            !emit1(JSOP_GETRVAL) ||
             !emitTree(pn->pn_kid3) ||
+            !emit1(JSOP_SETRVAL) ||
             !emit1(JSOP_RETSUB))
         {
             return false;
         }
         hasTryFinally = true;
         MOZ_ASSERT(this->stackDepth == depth);
     }
     popStatement();
@@ -6063,38 +6065,28 @@ BytecodeEmitter::emitContinue(PropertyNa
         while (!stmt->isLoop())
             stmt = stmt->enclosing;
     }
 
     return emitGoto(stmt, &stmt->continues, SRC_CONTINUE);
 }
 
 bool
-BytecodeEmitter::inTryBlockWithFinally()
-{
-    for (StmtInfoBCE* stmt = innermostStmt(); stmt; stmt = stmt->enclosing) {
-        if (stmt->type == StmtType::FINALLY)
-            return true;
-    }
-    return false;
-}
-
-bool
 BytecodeEmitter::emitReturn(ParseNode* pn)
 {
     if (!updateSourceCoordNotes(pn->pn_pos.begin))
         return false;
 
     if (sc->isFunctionBox() && sc->asFunctionBox()->isStarGenerator()) {
         if (!emitPrepareIteratorResult())
             return false;
     }
 
     /* Push a return value */
-    if (ParseNode* pn2 = pn->pn_left) {
+    if (ParseNode* pn2 = pn->pn_kid) {
         if (!emitTree(pn2))
             return false;
     } else {
         /* No explicit return value provided */
         if (!emit1(JSOP_UNDEFINED))
             return false;
     }
 
@@ -6112,54 +6104,29 @@ BytecodeEmitter::emitReturn(ParseNode* p
      * for/in, etc., slots nested inside the finally's try).
      *
      * In this case we mutate JSOP_RETURN into JSOP_SETRVAL and add an
      * extra JSOP_RETRVAL after the fixups.
      */
     ptrdiff_t top = offset();
 
     bool isGenerator = sc->isFunctionBox() && sc->asFunctionBox()->isGenerator();
-    bool useGenRVal = false;
-    if (isGenerator) {
-        if (sc->asFunctionBox()->isStarGenerator() && inTryBlockWithFinally()) {
-            // Emit JSOP_SETALIASEDVAR .genrval to store the return value on the
-            // scope chain, so it's not lost when we yield in a finally block.
-            useGenRVal = true;
-            MOZ_ASSERT(pn->pn_right);
-            if (!emitTree(pn->pn_right))
-                return false;
-            if (!emit1(JSOP_POP))
-                return false;
-        } else {
-            if (!emit1(JSOP_SETRVAL))
-                return false;
-        }
-    } else {
-        if (!emit1(JSOP_RETURN))
-            return false;
-    }
+    if (!emit1(isGenerator ? JSOP_SETRVAL : JSOP_RETURN))
+        return false;
 
     NonLocalExitScope nle(this);
 
     if (!nle.prepareForNonLocalJump(nullptr))
         return false;
 
     if (isGenerator) {
         ScopeCoordinate sc;
-        // We know that .generator and .genrval are on the top scope chain node,
-        // as we just exited nested scopes.
+        // We know that .generator is on the top scope chain node, as we just
+        // exited nested scopes.
         sc.setHops(0);
-        if (useGenRVal) {
-            MOZ_ALWAYS_TRUE(lookupAliasedNameSlot(cx->names().dotGenRVal, &sc));
-            if (!emitAliasedVarOp(JSOP_GETALIASEDVAR, sc, DontCheckLexical))
-                return false;
-            if (!emit1(JSOP_SETRVAL))
-                return false;
-        }
-
         MOZ_ALWAYS_TRUE(lookupAliasedNameSlot(cx->names().dotGenerator, &sc));
         if (!emitAliasedVarOp(JSOP_GETALIASEDVAR, sc, DontCheckLexical))
             return false;
         if (!emitYieldOp(JSOP_FINALYIELDRVAL))
             return false;
     } else if (top + static_cast<ptrdiff_t>(JSOP_RETURN_LENGTH) != offset()) {
         code()[top] = JSOP_SETRVAL;
         if (!emit1(JSOP_RETRVAL))
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -299,18 +299,16 @@ struct BytecodeEmitter
     // The caller should initialize *answer to false and invoke this function on
     // an expression statement or similar subtree to decide whether the tree
     // could produce code that has any side effects.  For an expression
     // statement, we define useless code as code with no side effects, because
     // the main effect, the value left on the stack after the code executes,
     // will be discarded by a pop bytecode.
     bool checkSideEffects(ParseNode* pn, bool* answer);
 
-    bool inTryBlockWithFinally();
-
 #ifdef DEBUG
     bool checkStrictOrSloppy(JSOp op);
 #endif
 
     // Append a new source note of the given type (and therefore size) to the
     // notes dynamic array, updating noteCount. Return the new note's index
     // within the array pointed at by current->notes as outparam.
     bool newSrcNote(SrcNoteType type, unsigned* indexp = nullptr);
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -109,21 +109,21 @@ ContainsHoistedDeclaration(ExclusiveCont
       case PNK_DEBUGGER:
         MOZ_ASSERT(node->isArity(PN_NULLARY));
         *result = false;
         return true;
 
       // Statements containing only an expression have no declarations.
       case PNK_SEMI:
       case PNK_THROW:
+      case PNK_RETURN:
         MOZ_ASSERT(node->isArity(PN_UNARY));
         *result = false;
         return true;
 
-      case PNK_RETURN:
       // These two aren't statements in the spec, but we sometimes insert them
       // in statement lists anyway.
       case PNK_YIELD_STAR:
       case PNK_YIELD:
         MOZ_ASSERT(node->isArity(PN_BINARY));
         *result = false;
         return true;
 
@@ -1257,31 +1257,23 @@ FoldList(ExclusiveContext* cx, ParseNode
     return true;
 }
 
 static bool
 FoldReturn(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
            bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_RETURN));
-    MOZ_ASSERT(node->isArity(PN_BINARY));
+    MOZ_ASSERT(node->isArity(PN_UNARY));
 
-    if (ParseNode*& expr = node->pn_left) {
+    if (ParseNode*& expr = node->pn_kid) {
         if (!Fold(cx, &expr, parser, inGenexpLambda))
             return false;
     }
 
-#ifdef DEBUG
-    if (ParseNode* generatorSpecific = node->pn_right) {
-        MOZ_ASSERT(generatorSpecific->isKind(PNK_NAME));
-        MOZ_ASSERT(generatorSpecific->pn_atom->equals(".genrval"));
-        MOZ_ASSERT(generatorSpecific->isAssigned());
-    }
-#endif
-
     return true;
 }
 
 static bool
 FoldTry(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
         bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_TRY));
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -588,19 +588,19 @@ class FullParseHandler
     ParseNode* newContinueStatement(PropertyName* label, const TokenPos& pos) {
         return new_<ContinueStatement>(label, pos);
     }
 
     ParseNode* newBreakStatement(PropertyName* label, const TokenPos& pos) {
         return new_<BreakStatement>(label, pos);
     }
 
-    ParseNode* newReturnStatement(ParseNode* expr, ParseNode* genrval, const TokenPos& pos) {
+    ParseNode* newReturnStatement(ParseNode* expr, const TokenPos& pos) {
         MOZ_ASSERT_IF(expr, pos.encloses(expr->pn_pos));
-        return new_<BinaryNode>(PNK_RETURN, JSOP_RETURN, pos, expr, genrval);
+        return new_<UnaryNode>(PNK_RETURN, JSOP_RETURN, pos, expr);
     }
 
     ParseNode* newWithStatement(uint32_t begin, ParseNode* expr, ParseNode* body,
                                 ObjectBox* staticWith) {
         return new_<BinaryObjNode>(PNK_WITH, JSOP_NOP, TokenPos(begin, body->pn_pos.end),
                                    expr, body, staticWith);
     }
 
--- a/js/src/frontend/NameFunctions.cpp
+++ b/js/src/frontend/NameFunctions.cpp
@@ -496,28 +496,21 @@ class NameResolver
             }
             MOZ_ASSERT((cur->pn_right->isKind(PNK_NAME) && !cur->pn_right->isAssigned()) ||
                        (cur->pn_right->isKind(PNK_ASSIGN) &&
                         cur->pn_right->pn_left->isKind(PNK_NAME) &&
                         cur->pn_right->pn_right->isKind(PNK_GENERATOR)));
             break;
 
           case PNK_RETURN:
-            MOZ_ASSERT(cur->isArity(PN_BINARY));
-            if (ParseNode* returnValue = cur->pn_left) {
+            MOZ_ASSERT(cur->isArity(PN_UNARY));
+            if (ParseNode* returnValue = cur->pn_kid) {
                 if (!resolve(returnValue, prefix))
                     return false;
             }
-#ifdef DEBUG
-            if (ParseNode* internalAssignForGenerators = cur->pn_right) {
-                MOZ_ASSERT(internalAssignForGenerators->isKind(PNK_NAME));
-                MOZ_ASSERT(internalAssignForGenerators->pn_atom == cx->names().dotGenRVal);
-                MOZ_ASSERT(internalAssignForGenerators->isAssigned());
-            }
-#endif
             break;
 
           case PNK_IMPORT:
           case PNK_EXPORT_FROM:
           case PNK_EXPORT_DEFAULT:
             MOZ_ASSERT(cur->isArity(PN_BINARY));
             // The left halves of these nodes don't contain any unconstrained
             // expressions, but it's very hard to assert this to safely rely on
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -332,29 +332,22 @@ PushNodeChildren(ParseNode* pn, NodeStac
                     pn->pn_right->pn_left->isKind(PNK_NAME) &&
                     pn->pn_right->pn_right->isKind(PNK_GENERATOR)));
         if (pn->pn_left)
             stack->push(pn->pn_left);
         stack->push(pn->pn_right);
         return PushResult::Recyclable;
       }
 
-      // A return node's left half is what you'd expect: the return expression,
-      // if any.  The right half is non-null only for returns inside generator
-      // functions, with the structure described in the assertions.
+      // A return node's child is what you'd expect: the return expression,
+      // if any.
       case PNK_RETURN: {
-        MOZ_ASSERT(pn->isArity(PN_BINARY));
-        if (pn->pn_left)
-            stack->push(pn->pn_left);
-        if (pn->pn_right) {
-            MOZ_ASSERT(pn->pn_right->isKind(PNK_NAME));
-            MOZ_ASSERT(pn->pn_right->pn_atom->equals(".genrval"));
-            MOZ_ASSERT(pn->pn_right->isAssigned());
-            stack->push(pn->pn_right);
-        }
+        MOZ_ASSERT(pn->isArity(PN_UNARY));
+        if (pn->pn_kid)
+            stack->push(pn->pn_kid);
         return PushResult::Recyclable;
       }
 
       // Import and export-from nodes have a list of specifiers on the left
       // and a module string on the right.
       case PNK_IMPORT:
       case PNK_EXPORT_FROM: {
         MOZ_ASSERT(pn->isArity(PN_BINARY));
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -351,18 +351,17 @@ IsDeleteKind(ParseNodeKind kind)
  *                                   or
  *                                     pn_used: true
  *                                     pn_atom: variable name
  *                                     pn_lexdef: def node
  *                                   each assignment node has
  *                                     pn_left: PNK_NAME with pn_used true and
  *                                              pn_lexdef (NOT pn_expr) set
  *                                     pn_right: initializer
- * PNK_RETURN   binary      pn_left: return expr or null
- *                          pn_right: .genrval name or null
+ * PNK_RETURN   unary       pn_kid: return expr or null
  * PNK_SEMI     unary       pn_kid: expr or null statement
  *                          pn_prologue: true if Directive Prologue member
  *                              in original source, not introduced via
  *                              constant folding or other tree rewriting
  * PNK_LABEL    name        pn_atom: label, pn_expr: labeled statement
  * PNK_IMPORT   binary      pn_left: PNK_IMPORT_SPEC_LIST import specifiers
  *                          pn_right: PNK_STRING module specifier
  * PNK_EXPORT   unary       pn_kid: declaration expression
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1225,21 +1225,21 @@ Parser<ParseHandler>::functionBody(InHan
     Node pn;
     if (type == StatementListBody) {
         pn = statements(yieldHandling);
         if (!pn)
             return null();
     } else {
         MOZ_ASSERT(type == ExpressionBody);
 
-        Node kid = assignExpr(inHandling, yieldHandling);
+        Node kid = assignExpr(inHandling, yieldHandling, TripledotProhibited);
         if (!kid)
             return null();
 
-        pn = handler.newReturnStatement(kid, null(), handler.getPosition(kid));
+        pn = handler.newReturnStatement(kid, handler.getPosition(kid));
         if (!pn)
             return null();
     }
 
     switch (pc->generatorKind()) {
       case NotGenerator:
         MOZ_ASSERT(pc->lastYieldOffset == startYieldOffset);
         break;
@@ -1269,24 +1269,16 @@ Parser<ParseHandler>::functionBody(InHan
     if (pc->isGenerator()) {
         MOZ_ASSERT(type == StatementListBody);
         Node generator = newName(context->names().dotGenerator);
         if (!generator)
             return null();
         if (!pc->define(tokenStream, context->names().dotGenerator, generator, Definition::VAR))
             return null();
 
-        if (pc->isStarGenerator()) {
-            Node genrval = newName(context->names().dotGenRVal);
-            if (!genrval)
-                return null();
-            if (!pc->define(tokenStream, context->names().dotGenRVal, genrval, Definition::VAR))
-                return null();
-        }
-
         generator = newName(context->names().dotGenerator);
         if (!generator)
             return null();
         if (!noteNameUse(context->names().dotGenerator, generator))
             return null();
         if (!handler.prependInitialYield(pn, generator))
             return null();
     }
@@ -2425,17 +2417,17 @@ Parser<SyntaxParseHandler>::checkFunctio
     return true;
 }
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::addExprAndGetNextTemplStrToken(YieldHandling yieldHandling, Node nodeList,
                                                      TokenKind* ttp)
 {
-    Node pn = expr(InAllowed, yieldHandling);
+    Node pn = expr(InAllowed, yieldHandling, TripledotProhibited);
     if (!pn)
         return false;
     handler.addList(nodeList, pn);
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return false;
     if (tt != TOK_RC) {
@@ -3973,17 +3965,17 @@ Parser<SyntaxParseHandler>::checkDestruc
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::destructuringExpr(YieldHandling yieldHandling, BindData<ParseHandler>* data,
                                         TokenKind tt)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
 
     pc->inDeclDestructuring = true;
-    Node pn = primaryExpr(yieldHandling, tt);
+    Node pn = primaryExpr(yieldHandling, TripledotProhibited, tt);
     pc->inDeclDestructuring = false;
     if (!pn)
         return null();
     if (!checkDestructuringPattern(data, pn))
         return null();
     return pn;
 }
 
@@ -4224,17 +4216,17 @@ Parser<ParseHandler>::variables(YieldHan
             TokenKind tt;
             if (!tokenStream.getToken(&tt))
                 return null();
             if (tt == TOK_LB || tt == TOK_LC) {
                 if (psimple)
                     *psimple = false;
 
                 pc->inDeclDestructuring = true;
-                pn2 = primaryExpr(yieldHandling, tt);
+                pn2 = primaryExpr(yieldHandling, TripledotProhibited, tt);
                 pc->inDeclDestructuring = false;
                 if (!pn2)
                     return null();
 
                 bool parsingForInOrOfInit = false;
                 if (location == InForInit) {
                     bool isForIn, isForOf;
                     if (!matchInOrOf(&isForIn, &isForOf))
@@ -4253,17 +4245,17 @@ Parser<ParseHandler>::variables(YieldHan
                     tokenStream.ungetToken();
                     handler.addList(pn, pn2);
                     break;
                 }
 
                 MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
 
                 Node init = assignExpr(location == InForInit ? InProhibited : InAllowed,
-                                       yieldHandling);
+                                       yieldHandling, TripledotProhibited);
                 if (!init)
                     return null();
 
                 // Ban the nonsensical |for (var V = E1 in E2);| where V is a
                 // destructuring pattern.  See bug 1164741 for background.
                 if (location == InForInit && kind == PNK_VAR) {
                     TokenKind afterInit;
                     if (!tokenStream.peekToken(&afterInit))
@@ -4339,17 +4331,17 @@ Parser<ParseHandler>::variables(YieldHan
                 // If we are not parsing a let declaration, bind the name
                 // now. Otherwise we must wait until after parsing the initializing
                 // assignment.
                 bool bindBeforeInitializer = kind != PNK_LET && kind != PNK_CONST;
                 if (bindBeforeInitializer && !data.bind(name, this))
                     return null();
 
                 Node init = assignExpr(location == InForInit ? InProhibited : InAllowed,
-                                       yieldHandling);
+                                       yieldHandling, TripledotProhibited);
                 if (!init)
                     return null();
 
                 // Ignore an initializer if we have a for-in loop declaring a
                 // |var| with an initializer: |for (var v = ... in ...);|.
                 // Warn that this syntax is invalid so that developers looking
                 // in the console know to fix this.  ES<6 permitted the
                 // initializer while ES6 doesn't; ignoring it seems the best
@@ -5050,17 +5042,17 @@ Parser<FullParseHandler>::exportDeclarat
                 return null();
             break;
           default:
             tokenStream.ungetToken();
             RootedPropertyName name(context, context->names().starDefaultStar);
             binding = makeInitializedLexicalBinding(name, true, pos());
             if (!binding)
                 return null();
-            kid = assignExpr(InAllowed, YieldIsKeyword);
+            kid = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
             if (!kid)
                 return null();
             if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
                 return null();
             break;
         }
 
         return handler.newExportDefaultDeclaration(kid, binding, TokenPos(begin, pos().end));
@@ -5089,17 +5081,17 @@ Parser<SyntaxParseHandler>::exportDeclar
     return SyntaxParseHandler::NodeFailure;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::expressionStatement(YieldHandling yieldHandling, InvokedPrediction invoked)
 {
     tokenStream.ungetToken();
-    Node pnexpr = expr(InAllowed, yieldHandling, invoked);
+    Node pnexpr = expr(InAllowed, yieldHandling, TripledotProhibited, invoked);
     if (!pnexpr)
         return null();
     if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
         return null();
     return handler.newExprStatement(pnexpr, pos().end);
 }
 
 template <typename ParseHandler>
@@ -5333,17 +5325,17 @@ Parser<FullParseHandler>::forStatement(Y
                 blockObj->initEnclosingScopeFromParser(pc->innermostStaticScope());
                 pn1 = variables(yieldHandling, constDecl ? PNK_CONST : PNK_LET, InForInit,
                                 nullptr, blockObj, DontHoistVars);
             } else {
                 // Pass |InProhibited| when parsing an expression so that |in|
                 // isn't parsed in a RelationalExpression as a binary operator.
                 // In this context, |in| is part of a for-in loop -- *not* part
                 // of a binary expression.
-                pn1 = expr(InProhibited, yieldHandling);
+                pn1 = expr(InProhibited, yieldHandling, TripledotProhibited);
             }
             if (!pn1)
                 return null();
             modifier = TokenStream::None;
         }
     }
 
     MOZ_ASSERT_IF(isForDecl, pn1->isArity(PN_LIST));
@@ -5466,18 +5458,18 @@ Parser<FullParseHandler>::forStatement(Y
             pn2 = pn1;
             pn1 = nullptr;
 
             if (!checkAndMarkAsAssignmentLhs(pn2, PlainAssignment))
                 return null();
         }
 
         pn3 = (headKind == PNK_FOROF)
-              ? assignExpr(InAllowed, yieldHandling)
-              : expr(InAllowed, yieldHandling);
+              ? assignExpr(InAllowed, yieldHandling, TripledotProhibited)
+              : expr(InAllowed, yieldHandling, TripledotProhibited);
         if (!pn3)
             return null();
         modifier = TokenStream::None;
 
         if (blockObj) {
             /*
              * Now that the pn3 has been parsed, push the let scope. To hold
              * the blockObj for the emitter, wrap the PNK_LEXICALSCOPE node
@@ -5566,31 +5558,31 @@ Parser<FullParseHandler>::forStatement(Y
         MUST_MATCH_TOKEN_MOD(TOK_SEMI, modifier, JSMSG_SEMI_AFTER_FOR_INIT);
         TokenKind tt;
         if (!tokenStream.peekToken(&tt, TokenStream::Operand))
             return null();
         if (tt == TOK_SEMI) {
             pn2 = nullptr;
             modifier = TokenStream::Operand;
         } else {
-            pn2 = expr(InAllowed, yieldHandling);
+            pn2 = expr(InAllowed, yieldHandling, TripledotProhibited);
             if (!pn2)
                 return null();
             modifier = TokenStream::None;
         }
 
         /* Parse the update expression or null into pn3. */
         MUST_MATCH_TOKEN_MOD(TOK_SEMI, modifier, JSMSG_SEMI_AFTER_FOR_COND);
         if (!tokenStream.peekToken(&tt, TokenStream::Operand))
             return null();
         if (tt == TOK_RP) {
             pn3 = nullptr;
             modifier = TokenStream::Operand;
         } else {
-            pn3 = expr(InAllowed, yieldHandling);
+            pn3 = expr(InAllowed, yieldHandling, TripledotProhibited);
             if (!pn3)
                 return null();
             modifier = TokenStream::None;
         }
     }
 
     MUST_MATCH_TOKEN_MOD(TOK_RP, modifier, JSMSG_PAREN_AFTER_FOR_CTRL);
 
@@ -5666,17 +5658,17 @@ Parser<SyntaxParseHandler>::forStatement
                 tokenStream.consumeKnownToken(tt, TokenStream::Operand);
                 lhsNode = variables(yieldHandling, PNK_VAR, InForInit, &simpleForDecl);
             }
             else if (tt == TOK_CONST || tt == TOK_LET) {
                 JS_ALWAYS_FALSE(abortIfSyntaxParser());
                 return null();
             }
             else {
-                lhsNode = expr(InProhibited, yieldHandling);
+                lhsNode = expr(InProhibited, yieldHandling, TripledotProhibited);
             }
             if (!lhsNode)
                 return null();
             modifier = TokenStream::None;
         }
     }
 
     // If there's an |in| keyword here, it's a for-in loop, by dint of careful
@@ -5702,39 +5694,43 @@ Parser<SyntaxParseHandler>::forStatement
         if (!simpleForDecl) {
             JS_ALWAYS_FALSE(abortIfSyntaxParser());
             return null();
         }
 
         if (!isForDecl && !checkAndMarkAsAssignmentLhs(lhsNode, PlainAssignment))
             return null();
 
-        if (!(isForIn ? expr(InAllowed, yieldHandling) : assignExpr(InAllowed, yieldHandling)))
-            return null();
+        if (!(isForIn
+              ? expr(InAllowed, yieldHandling, TripledotProhibited)
+              : assignExpr(InAllowed, yieldHandling, TripledotProhibited)))
+        {
+            return null();
+        }
         modifier = TokenStream::None;
     } else {
         /* Parse the loop condition or null. */
         MUST_MATCH_TOKEN_MOD(TOK_SEMI, modifier, JSMSG_SEMI_AFTER_FOR_INIT);
         TokenKind tt;
         if (!tokenStream.peekToken(&tt, TokenStream::Operand))
             return null();
         modifier = TokenStream::Operand;
         if (tt != TOK_SEMI) {
-            if (!expr(InAllowed, yieldHandling))
+            if (!expr(InAllowed, yieldHandling, TripledotProhibited))
                 return null();
             modifier = TokenStream::None;
         }
 
         /* Parse the update expression or null. */
         MUST_MATCH_TOKEN_MOD(TOK_SEMI, modifier, JSMSG_SEMI_AFTER_FOR_COND);
         if (!tokenStream.peekToken(&tt, TokenStream::Operand))
             return null();
         modifier = TokenStream::Operand;
         if (tt != TOK_RP) {
-            if (!expr(InAllowed, yieldHandling))
+            if (!expr(InAllowed, yieldHandling, TripledotProhibited))
                 return null();
             modifier = TokenStream::None;
         }
     }
 
     MUST_MATCH_TOKEN_MOD(TOK_RP, modifier, JSMSG_PAREN_AFTER_FOR_CTRL);
 
     /* Parse the loop body. */
@@ -5787,17 +5783,17 @@ Parser<ParseHandler>::switchStatement(Yi
                 report(ParseError, false, null(), JSMSG_TOO_MANY_DEFAULTS);
                 return null();
             }
             seenDefault = true;
             caseExpr = null();  // The default case has pn_left == nullptr.
             break;
 
           case TOK_CASE:
-            caseExpr = expr(InAllowed, yieldHandling);
+            caseExpr = expr(InAllowed, yieldHandling, TripledotProhibited);
             if (!caseExpr)
                 return null();
             break;
 
           default:
             report(ParseError, false, null(), JSMSG_BAD_SWITCH);
             return null();
         }
@@ -5980,43 +5976,32 @@ Parser<ParseHandler>::returnStatement(Yi
       case TOK_EOL:
       case TOK_EOF:
       case TOK_SEMI:
       case TOK_RC:
         exprNode = null();
         pc->funHasReturnVoid = true;
         break;
       default: {
-        exprNode = expr(InAllowed, yieldHandling);
+        exprNode = expr(InAllowed, yieldHandling, TripledotProhibited);
         if (!exprNode)
             return null();
         pc->funHasReturnExpr = true;
       }
     }
 
     if (exprNode) {
         if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
             return null();
     } else {
         if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
             return null();
     }
 
-    Node genrval = null();
-    if (pc->isStarGenerator()) {
-        genrval = newName(context->names().dotGenRVal);
-        if (!genrval)
-            return null();
-        if (!noteNameUse(context->names().dotGenRVal, genrval))
-            return null();
-        if (!checkAndMarkAsAssignmentLhs(genrval, PlainAssignment))
-            return null();
-    }
-
-    Node pn = handler.newReturnStatement(exprNode, genrval, TokenPos(begin, pos().end));
+    Node pn = handler.newReturnStatement(exprNode, TokenPos(begin, pos().end));
     if (!pn)
         return null();
 
     if (pc->isLegacyGenerator() && exprNode) {
         /* Disallow "return v;" in legacy generators. */
         reportBadReturn(pn, ParseError, JSMSG_BAD_GENERATOR_RETURN,
                         JSMSG_BAD_ANON_GENERATOR_RETURN);
         return null();
@@ -6078,17 +6063,17 @@ Parser<ParseHandler>::yieldExpression(In
             exprNode = null();
             tokenStream.addModifierException(TokenStream::NoneIsOperand);
             break;
           case TOK_MUL:
             kind = PNK_YIELD_STAR;
             tokenStream.consumeKnownToken(TOK_MUL, TokenStream::Operand);
             // Fall through.
           default:
-            exprNode = assignExpr(inHandling, YieldIsKeyword);
+            exprNode = assignExpr(inHandling, YieldIsKeyword, TripledotProhibited);
             if (!exprNode)
                 return null();
         }
         return newYieldExpression(begin, exprNode, kind == PNK_YIELD_STAR);
       }
 
       case NotGenerator:
         // We are in code that has not seen a yield, but we are in JS 1.7 or
@@ -6137,17 +6122,17 @@ Parser<ParseHandler>::yieldExpression(In
           case TOK_RP:
           case TOK_COLON:
           case TOK_COMMA:
             // No value.
             exprNode = null();
             tokenStream.addModifierException(TokenStream::NoneIsOperand);
             break;
           default:
-            exprNode = assignExpr(inHandling, YieldIsKeyword);
+            exprNode = assignExpr(inHandling, YieldIsKeyword, TripledotProhibited);
             if (!exprNode)
                 return null();
         }
 
         return newYieldExpression(begin, exprNode);
       }
     }
 
@@ -6263,17 +6248,17 @@ Parser<ParseHandler>::throwStatement(Yie
         report(ParseError, false, null(), JSMSG_MISSING_EXPR_AFTER_THROW);
         return null();
     }
     if (tt == TOK_EOL) {
         report(ParseError, false, null(), JSMSG_LINE_BREAK_AFTER_THROW);
         return null();
     }
 
-    Node throwExpr = expr(InAllowed, yieldHandling);
+    Node throwExpr = expr(InAllowed, yieldHandling, TripledotProhibited);
     if (!throwExpr)
         return null();
 
     if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
         return null();
 
     return handler.newThrowStatement(throwExpr, TokenPos(begin, pos().end));
 }
@@ -6408,17 +6393,17 @@ Parser<ParseHandler>::tryStatement(Yield
              * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
              * to avoid conflicting with the JS2/ECMAv4 type annotation
              * catchguard syntax.
              */
             bool matched;
             if (!tokenStream.matchToken(&matched, TOK_IF))
                 return null();
             if (matched) {
-                catchGuard = expr(InAllowed, yieldHandling);
+                catchGuard = expr(InAllowed, yieldHandling, TripledotProhibited);
                 if (!catchGuard)
                     return null();
             }
 #endif
             MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
 
             MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
             Node catchBody = statements(yieldHandling);
@@ -6591,17 +6576,17 @@ Parser<FullParseHandler>::classDefinitio
 
     ParseNode* classHeritage = null();
     bool hasHeritage;
     if (!tokenStream.matchToken(&hasHeritage, TOK_EXTENDS))
         return null();
     if (hasHeritage) {
         if (!tokenStream.getToken(&tt))
             return null();
-        classHeritage = memberExpr(yieldHandling, tt, true);
+        classHeritage = memberExpr(yieldHandling, TripledotProhibited, tt, true);
         if (!classHeritage)
             return null();
     }
 
     MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CLASS);
 
     ParseNode* classMethods = handler.newClassMethodList(pos().begin);
 
@@ -6914,19 +6899,19 @@ Parser<ParseHandler>::statement(YieldHan
 
       // NOTE: default case handled in the ExpressionStatement section.
     }
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::expr(InHandling inHandling, YieldHandling yieldHandling,
-                           InvokedPrediction invoked)
-{
-    Node pn = assignExpr(inHandling, yieldHandling, invoked);
+                           TripledotHandling tripledotHandling, InvokedPrediction invoked)
+{
+    Node pn = assignExpr(inHandling, yieldHandling, tripledotHandling, invoked);
     if (!pn)
         return null();
 
     bool matched;
     if (!tokenStream.matchToken(&matched, TOK_COMMA))
         return null();
     if (!matched)
         return pn;
@@ -6935,17 +6920,17 @@ Parser<ParseHandler>::expr(InHandling in
     if (!seq)
         return null();
     while (true) {
         if (handler.isUnparenthesizedYieldExpression(pn)) {
             report(ParseError, false, pn, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
             return null();
         }
 
-        pn = assignExpr(inHandling, yieldHandling);
+        pn = assignExpr(inHandling, yieldHandling, tripledotHandling);
         if (!pn)
             return null();
         handler.addList(seq, pn);
 
         if (!tokenStream.matchToken(&matched, TOK_COMMA))
             return null();
         if (!matched)
             break;
@@ -7035,30 +7020,30 @@ Precedence(ParseNodeKind pnk) {
     MOZ_ASSERT(pnk >= PNK_BINOP_FIRST);
     MOZ_ASSERT(pnk <= PNK_BINOP_LAST);
     return PrecedenceTable[pnk - PNK_BINOP_FIRST];
 }
 
 template <typename ParseHandler>
 MOZ_ALWAYS_INLINE typename ParseHandler::Node
 Parser<ParseHandler>::orExpr1(InHandling inHandling, YieldHandling yieldHandling,
-                              InvokedPrediction invoked)
+                              TripledotHandling tripledotHandling, InvokedPrediction invoked)
 {
     // Shift-reduce parser for the binary operator part of the JS expression
     // syntax.
 
     // Conceptually there's just one stack, a stack of pairs (lhs, op).
     // It's implemented using two separate arrays, though.
     Node nodeStack[PRECEDENCE_CLASSES];
     ParseNodeKind kindStack[PRECEDENCE_CLASSES];
     int depth = 0;
 
     Node pn;
     for (;;) {
-        pn = unaryExpr(yieldHandling, invoked);
+        pn = unaryExpr(yieldHandling, tripledotHandling, invoked);
         if (!pn)
             return pn;
 
         // If a binary operator follows, consume it and compute the
         // corresponding operator.
         TokenKind tok;
         if (!tokenStream.getToken(&tok))
             return null();
@@ -7098,29 +7083,29 @@ Parser<ParseHandler>::orExpr1(InHandling
 
     MOZ_ASSERT(depth == 0);
     return pn;
 }
 
 template <typename ParseHandler>
 MOZ_ALWAYS_INLINE typename ParseHandler::Node
 Parser<ParseHandler>::condExpr1(InHandling inHandling, YieldHandling yieldHandling,
-                                InvokedPrediction invoked)
-{
-    Node condition = orExpr1(inHandling, yieldHandling, invoked);
+                                TripledotHandling tripledotHandling, InvokedPrediction invoked)
+{
+    Node condition = orExpr1(inHandling, yieldHandling, tripledotHandling, invoked);
     if (!condition || !tokenStream.isCurrentTokenType(TOK_HOOK))
         return condition;
 
-    Node thenExpr = assignExpr(InAllowed, yieldHandling);
+    Node thenExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
     if (!thenExpr)
         return null();
 
     MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
 
-    Node elseExpr = assignExpr(inHandling, yieldHandling);
+    Node elseExpr = assignExpr(inHandling, yieldHandling, TripledotProhibited);
     if (!elseExpr)
         return null();
 
     // Advance to the next token; the caller is responsible for interpreting it.
     TokenKind ignored;
     if (!tokenStream.getToken(&ignored))
         return null();
     return handler.newConditional(condition, thenExpr, elseExpr);
@@ -7163,17 +7148,17 @@ Parser<ParseHandler>::checkAndMarkAsAssi
 
     MOZ_ASSERT(handler.isFunctionCall(target));
     return makeSetCall(target, JSMSG_BAD_LEFTSIDE_OF_ASS);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandling,
-                                 InvokedPrediction invoked)
+                                 TripledotHandling tripledotHandling, InvokedPrediction invoked)
 {
     JS_CHECK_RECURSION(context, return null());
 
     // It's very common at this point to have a "detectably simple" expression,
     // i.e. a name/number/string token followed by one of the following tokens
     // that obviously isn't part of an expression: , ; : ) ] }
     //
     // (In Parsemark this happens 81.4% of the time;  in code with large
@@ -7215,17 +7200,17 @@ Parser<ParseHandler>::assignExpr(InHandl
 
     tokenStream.ungetToken();
 
     // Save the tokenizer state in case we find an arrow function and have to
     // rewind.
     TokenStream::Position start(keepAtoms);
     tokenStream.tell(&start);
 
-    Node lhs = condExpr1(inHandling, yieldHandling, invoked);
+    Node lhs = condExpr1(inHandling, yieldHandling, tripledotHandling, invoked);
     if (!lhs)
         return null();
 
     ParseNodeKind kind;
     JSOp op;
     switch (tokenStream.currentToken().type) {
       case TOK_ASSIGN:       kind = PNK_ASSIGN;       op = JSOP_NOP;    break;
       case TOK_ADDASSIGN:    kind = PNK_ADDASSIGN;    op = JSOP_ADD;    break;
@@ -7314,17 +7299,17 @@ Parser<ParseHandler>::assignExpr(InHandl
     }
 
     AssignmentFlavor flavor = kind == PNK_ASSIGN ? PlainAssignment : CompoundAssignment;
     if (!checkAndMarkAsAssignmentLhs(lhs, flavor))
         return null();
 
     bool saved = pc->inDeclDestructuring;
     pc->inDeclDestructuring = false;
-    Node rhs = assignExpr(inHandling, yieldHandling);
+    Node rhs = assignExpr(inHandling, yieldHandling, TripledotProhibited);
     pc->inDeclDestructuring = saved;
     if (!rhs)
         return null();
 
     return handler.newAssignment(kind, lhs, rhs, pc, op);
 }
 
 template <typename ParseHandler>
@@ -7447,25 +7432,26 @@ Parser<ParseHandler>::checkAndMarkAsIncO
     return true;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, JSOp op,
                                   uint32_t begin)
 {
-    Node kid = unaryExpr(yieldHandling);
+    Node kid = unaryExpr(yieldHandling, TripledotProhibited);
     if (!kid)
         return null();
     return handler.newUnary(kind, op, begin, kid);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, InvokedPrediction invoked)
+Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
+                                InvokedPrediction invoked)
 {
     JS_CHECK_RECURSION(context, return null());
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
     uint32_t begin = pos().begin;
     switch (tt) {
@@ -7487,59 +7473,60 @@ Parser<ParseHandler>::unaryExpr(YieldHan
         //
         //   // Looks up the name, doesn't find it and so evaluates to
         //   // "undefined".
         //   assertEq(typeof nonExistentName, "undefined");
         //
         //   // Evaluates expression, triggering a runtime ReferenceError for
         //   // the undefined name.
         //   typeof (1, nonExistentName);
-        Node kid = unaryExpr(yieldHandling);
+        Node kid = unaryExpr(yieldHandling, TripledotProhibited);
         if (!kid)
             return null();
 
         return handler.newTypeof(begin, kid);
       }
 
       case TOK_INC:
       case TOK_DEC:
       {
         TokenKind tt2;
         if (!tokenStream.getToken(&tt2, TokenStream::Operand))
             return null();
-        Node pn2 = memberExpr(yieldHandling, tt2, true);
+        Node pn2 = memberExpr(yieldHandling, TripledotProhibited, tt2, true);
         if (!pn2)
             return null();
         AssignmentFlavor flavor = (tt == TOK_INC) ? IncrementAssignment : DecrementAssignment;
         if (!checkAndMarkAsIncOperand(pn2, flavor))
             return null();
         return handler.newUnary((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT,
                                 JSOP_NOP,
                                 begin,
                                 pn2);
       }
 
       case TOK_DELETE: {
-        Node expr = unaryExpr(yieldHandling);
+        Node expr = unaryExpr(yieldHandling, TripledotProhibited);
         if (!expr)
             return null();
 
         // Per spec, deleting any unary expression is valid -- it simply
         // returns true -- except for one case that is illegal in strict mode.
         if (handler.maybeNameAnyParentheses(expr)) {
             if (!report(ParseStrictError, pc->sc->strict(), expr, JSMSG_DEPRECATED_DELETE_OPERAND))
                 return null();
             pc->sc->setBindingsAccessedDynamically();
         }
 
         return handler.newDelete(begin, expr);
       }
 
       default: {
-        Node pn = memberExpr(yieldHandling, tt, /* allowCallSyntax = */ true, invoked);
+        Node pn = memberExpr(yieldHandling, tripledotHandling, tt, /* allowCallSyntax = */ true,
+                             invoked);
         if (!pn)
             return null();
 
         /* Don't look across a newline boundary for a postfix incop. */
         if (!tokenStream.peekTokenSameLine(&tt))
             return null();
         if (tt == TOK_INC || tt == TOK_DEC) {
             tokenStream.consumeKnownToken(tt);
@@ -7887,17 +7874,17 @@ Parser<FullParseHandler>::legacyComprehe
 
         RootedPropertyName name(context);
         if (!tokenStream.getToken(&tt))
             return null();
         switch (tt) {
           case TOK_LB:
           case TOK_LC:
             pc->inDeclDestructuring = true;
-            pn3 = primaryExpr(YieldIsKeyword, tt);
+            pn3 = primaryExpr(YieldIsKeyword, TripledotProhibited, tt);
             pc->inDeclDestructuring = false;
             if (!pn3)
                 return null();
             break;
 
           case TOK_NAME:
             name = tokenStream.currentName();
 
@@ -7931,17 +7918,17 @@ Parser<FullParseHandler>::legacyComprehe
                 MOZ_ASSERT(pn2->pn_iflags == (JSITER_FOREACH | JSITER_ENUMERATE));
                 report(ParseError, false, null(), JSMSG_BAD_FOR_EACH_LOOP);
                 return null();
             }
             pn2->pn_iflags = 0;
             headKind = PNK_FOROF;
         }
 
-        ParseNode* pn4 = expr(InAllowed, YieldIsKeyword);
+        ParseNode* pn4 = expr(InAllowed, YieldIsKeyword, TripledotProhibited);
         if (!pn4)
             return null();
         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
 
         if (isGenexp && pc->lastYieldOffset != startYieldOffset) {
             reportWithOffset(ParseError, false, pc->lastYieldOffset,
                              JSMSG_BAD_GENEXP_BODY, js_yield_str);
             return null();
@@ -8265,17 +8252,17 @@ Parser<ParseHandler>::comprehensionFor(G
     bool matched;
     if (!tokenStream.matchContextualKeyword(&matched, context->names().of))
         return null();
     if (!matched) {
         report(ParseError, false, null(), JSMSG_OF_AFTER_FOR_NAME);
         return null();
     }
 
-    Node rhs = assignExpr(InAllowed, YieldIsKeyword);
+    Node rhs = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
     if (!rhs)
         return null();
 
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_OF_ITERABLE);
 
     TokenPos headPos(begin, pos().end);
 
     AutoPushStmtInfoPC stmtInfo(*this, StmtType::BLOCK);
@@ -8319,17 +8306,17 @@ template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::comprehensionIf(GeneratorKind comprehensionKind)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_IF));
 
     uint32_t begin = pos().begin;
 
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
-    Node cond = assignExpr(InAllowed, YieldIsKeyword);
+    Node cond = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
     if (!cond)
         return null();
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
 
     /* Check for (a = b) and warn about possible (a == b) mistype. */
     if (handler.isUnparenthesizedAssignment(cond)) {
         if (!report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN))
             return null();
@@ -8356,17 +8343,17 @@ Parser<ParseHandler>::comprehensionTail(
 
     if (!tokenStream.matchToken(&matched, TOK_IF, TokenStream::Operand))
         return null();
     if (matched)
         return comprehensionIf(comprehensionKind);
 
     uint32_t begin = pos().begin;
 
-    Node bodyExpr = assignExpr(InAllowed, YieldIsKeyword);
+    Node bodyExpr = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
     if (!bodyExpr)
         return null();
 
     if (comprehensionKind == NotGenerator)
         return handler.newUnary(PNK_ARRAYPUSH, JSOP_ARRAYPUSH, begin, bodyExpr);
 
     MOZ_ASSERT(comprehensionKind == StarGenerator);
     Node yieldExpr = newYieldExpression(begin, bodyExpr);
@@ -8446,17 +8433,17 @@ Parser<ParseHandler>::generatorComprehen
     return result;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::assignExprWithoutYield(YieldHandling yieldHandling, unsigned msg)
 {
     uint32_t startYieldOffset = pc->lastYieldOffset;
-    Node res = assignExpr(InAllowed, yieldHandling);
+    Node res = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
     if (res && pc->lastYieldOffset != startYieldOffset) {
         reportWithOffset(ParseError, false, pc->lastYieldOffset,
                          msg, js_yield_str);
         return null();
     }
     return res;
 }
 
@@ -8481,17 +8468,17 @@ Parser<ParseHandler>::argumentList(Yield
         if (!tokenStream.matchToken(&matched, TOK_TRIPLEDOT, TokenStream::Operand))
             return false;
         if (matched) {
             spread = true;
             begin = pos().begin;
             *isSpread = true;
         }
 
-        Node argNode = assignExpr(InAllowed, yieldHandling);
+        Node argNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
         if (!argNode)
             return false;
         if (spread) {
             argNode = handler.newUnary(PNK_SPREAD, JSOP_NOP, begin, argNode);
             if (!argNode)
                 return false;
         }
 
@@ -8560,18 +8547,18 @@ Parser<ParseHandler>::checkAndMarkSuperS
     if (!pc->sc->allowSuperProperty())
         return false;
     pc->sc->markSuperScopeNeedsHomeObject();
     return true;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TokenKind tt, bool allowCallSyntax,
-                                 InvokedPrediction invoked)
+Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
+                                 TokenKind tt, bool allowCallSyntax, InvokedPrediction invoked)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
 
     Node lhs;
 
     JS_CHECK_RECURSION(context, return null());
 
     /* Check for new expression first. */
@@ -8585,17 +8572,17 @@ Parser<ParseHandler>::memberExpr(YieldHa
             lhs = newTarget;
         } else {
             lhs = handler.newList(PNK_NEW, newBegin, JSOP_NEW);
             if (!lhs)
                 return null();
 
             // Gotten by tryNewTarget
             tt = tokenStream.currentToken().type;
-            Node ctorExpr = memberExpr(yieldHandling, tt, false, PredictInvoked);
+            Node ctorExpr = memberExpr(yieldHandling, TripledotProhibited, tt, false, PredictInvoked);
             if (!ctorExpr)
                 return null();
 
             handler.addList(lhs, ctorExpr);
 
             bool matched;
             if (!tokenStream.matchToken(&matched, TOK_LP))
                 return null();
@@ -8607,17 +8594,17 @@ Parser<ParseHandler>::memberExpr(YieldHa
                     handler.setOp(lhs, JSOP_SPREADNEW);
             }
         }
     } else if (tt == TOK_SUPER) {
         lhs = handler.newSuperBase(pos(), context);
         if (!lhs)
             return null();
     } else {
-        lhs = primaryExpr(yieldHandling, tt, invoked);
+        lhs = primaryExpr(yieldHandling, tripledotHandling, tt, invoked);
         if (!lhs)
             return null();
     }
 
     MOZ_ASSERT_IF(handler.isSuperBase(lhs, context), tokenStream.isCurrentTokenType(TOK_SUPER));
 
     while (true) {
         if (!tokenStream.getToken(&tt))
@@ -8638,17 +8625,17 @@ Parser<ParseHandler>::memberExpr(YieldHa
                 nextMember = handler.newPropertyAccess(lhs, field, pos().end);
                 if (!nextMember)
                     return null();
             } else {
                 report(ParseError, false, null(), JSMSG_NAME_AFTER_DOT);
                 return null();
             }
         } else if (tt == TOK_LB) {
-            Node propExpr = expr(InAllowed, yieldHandling);
+            Node propExpr = expr(InAllowed, yieldHandling, TripledotProhibited);
             if (!propExpr)
                 return null();
 
             MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
 
             if (handler.isSuperBase(lhs, context) && !checkAndMarkSuperScope()) {
                     report(ParseError, false, null(), JSMSG_BAD_SUPERPROP, "member");
                     return null();
@@ -8888,23 +8875,23 @@ Parser<ParseHandler>::arrayInitializer(Y
             if (tt == TOK_COMMA) {
                 tokenStream.consumeKnownToken(TOK_COMMA, TokenStream::Operand);
                 if (!handler.addElision(literal, pos()))
                     return null();
             } else if (tt == TOK_TRIPLEDOT) {
                 spread = true;
                 tokenStream.consumeKnownToken(TOK_TRIPLEDOT, TokenStream::Operand);
                 uint32_t begin = pos().begin;
-                Node inner = assignExpr(InAllowed, yieldHandling);
+                Node inner = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
                 if (!inner)
                     return null();
                 if (!handler.addSpreadElement(literal, begin, inner))
                     return null();
             } else {
-                Node element = assignExpr(InAllowed, yieldHandling);
+                Node element = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
                 if (!element)
                     return null();
                 if (foldConstants && !FoldConstants(context, &element, this))
                     return null();
                 handler.addArrayElement(literal, element);
             }
 
             if (tt != TOK_COMMA) {
@@ -9162,17 +9149,17 @@ Parser<ParseHandler>::computedPropertyNa
     uint32_t begin = pos().begin;
 
     // Turn off the inDeclDestructuring flag when parsing computed property
     // names. In short, when parsing 'let {[x + y]: z} = obj;', noteNameUse()
     // should be called on x and y, but not on z. See the comment on
     // Parser<>::checkDestructuringPattern() for details.
     bool saved = pc->inDeclDestructuring;
     pc->inDeclDestructuring = false;
-    Node assignNode = assignExpr(InAllowed, yieldHandling);
+    Node assignNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
     pc->inDeclDestructuring = saved;
     if (!assignNode)
         return null();
 
     MUST_MATCH_TOKEN(TOK_RB, JSMSG_COMP_PROP_UNTERM_EXPR);
     Node propname = handler.newComputedName(assignNode, begin, pos().end);
     if (!propname)
         return null();
@@ -9202,17 +9189,17 @@ Parser<ParseHandler>::objectLiteral(Yiel
         tokenStream.ungetToken();
 
         PropertyType propType;
         Node propName = propertyName(yieldHandling, literal, &propType, &propAtom);
         if (!propName)
             return null();
 
         if (propType == PropertyType::Normal) {
-            Node propExpr = assignExpr(InAllowed, yieldHandling);
+            Node propExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
             if (!propExpr)
                 return null();
 
             if (foldConstants && !FoldConstants(context, &propExpr, this))
                 return null();
 
             if (propAtom == context->names().proto) {
                 if (seenPrototypeMutation) {
@@ -9342,18 +9329,18 @@ Parser<ParseHandler>::tryNewTarget(Node 
         return false;
 
     newTarget = handler.newNewTarget(newHolder, targetHolder);
     return !!newTarget;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TokenKind tt,
-                                  InvokedPrediction invoked)
+Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
+                                  TokenKind tt, InvokedPrediction invoked)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
     JS_CHECK_RECURSION(context, return null());
 
     switch (tt) {
       case TOK_FUNCTION:
         return functionExpr(invoked);
 
@@ -9420,22 +9407,29 @@ Parser<ParseHandler>::primaryExpr(YieldH
       case TOK_THIS:
         if (pc->sc->isFunctionBox())
             pc->sc->asFunctionBox()->usesThis = true;
         return handler.newThisLiteral(pos());
       case TOK_NULL:
         return handler.newNullLiteral(pos());
 
       case TOK_TRIPLEDOT: {
-        TokenKind next;
-
         // This isn't valid expression syntax, but it's valid in an arrow
         // function as a trailing rest param: `(a, b, ...rest) => body`.  Check
-        // for a name, closing parenthesis, and arrow, and allow it only if all
-        // are present.
+        // if it's directly under
+        // CoverParenthesizedExpressionAndArrowParameterList, and check for a
+        // name, closing parenthesis, and arrow, and allow it only if all are
+        // present.
+        if (tripledotHandling != TripledotAllowed) {
+            report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
+                   "expression", TokenKindToDesc(tt));
+            return null();
+        }
+
+        TokenKind next;
         if (!tokenStream.getToken(&next))
             return null();
         // FIXME: This fails to handle a rest parameter named |yield| correctly
         //        outside of generators: |var f = (...yield) => 42;| should be
         //        valid code!  When this is fixed, make sure to consult both
         //        |yieldHandling| and |checkYieldNameValidity| for correctness
         //        until legacy generator syntax is removed.
         if (next != TOK_NAME) {
@@ -9482,17 +9476,17 @@ Parser<ParseHandler>::parenExprOrGenerat
     uint32_t startYieldOffset = pc->lastYieldOffset;
 
     bool matched;
     if (!tokenStream.matchToken(&matched, TOK_FOR, TokenStream::Operand))
         return null();
     if (matched)
         return generatorComprehension(begin);
 
-    Node pn = expr(InAllowed, yieldHandling, PredictInvoked);
+    Node pn = expr(InAllowed, yieldHandling, TripledotAllowed, PredictInvoked);
     if (!pn)
         return null();
 
 #if JS_HAS_GENERATOR_EXPRS
     if (!tokenStream.matchToken(&matched, TOK_FOR))
         return null();
     if (matched) {
         if (pc->lastYieldOffset != startYieldOffset) {
@@ -9550,17 +9544,17 @@ Parser<ParseHandler>::parenExprOrGenerat
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::exprInParens(InHandling inHandling, YieldHandling yieldHandling)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
     uint32_t begin = pos().begin;
     uint32_t startYieldOffset = pc->lastYieldOffset;
 
-    Node pn = expr(inHandling, yieldHandling, PredictInvoked);
+    Node pn = expr(inHandling, yieldHandling, TripledotProhibited, PredictInvoked);
     if (!pn)
         return null();
 
 #if JS_HAS_GENERATOR_EXPRS
     bool matched;
     if (!tokenStream.matchToken(&matched, TOK_FOR))
         return null();
     if (matched) {
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -377,16 +377,17 @@ enum class PropertyType {
 
 // Specify a value for an ES6 grammar parametrization.  We have no enum for
 // [Return] because its behavior is exactly equivalent to checking whether
 // we're in a function box -- easier and simpler than passing an extra
 // parameter everywhere.
 enum YieldHandling { YieldIsName, YieldIsKeyword };
 enum InHandling { InAllowed, InProhibited };
 enum DefaultHandling { NameRequired, AllowDefaultName };
+enum TripledotHandling { TripledotAllowed, TripledotProhibited };
 
 template <typename ParseHandler>
 class Parser : private JS::AutoGCRooter, public StrictModeGetter
 {
     class MOZ_STACK_CLASS AutoPushStmtInfoPC
     {
         Parser<ParseHandler>& parser_;
         StmtInfoPC stmt_;
@@ -687,29 +688,34 @@ class Parser : private JS::AutoGCRooter,
     Node expressionStatement(YieldHandling yieldHandling,
                              InvokedPrediction invoked = PredictUninvoked);
     Node variables(YieldHandling yieldHandling,
                    ParseNodeKind kind,
                    ForInitLocation location,
                    bool* psimple = nullptr, StaticBlockObject* blockObj = nullptr,
                    VarContext varContext = HoistVars);
     Node expr(InHandling inHandling, YieldHandling yieldHandling,
+              TripledotHandling tripledotHandling,
               InvokedPrediction invoked = PredictUninvoked);
     Node assignExpr(InHandling inHandling, YieldHandling yieldHandling,
+                    TripledotHandling tripledotHandling,
                     InvokedPrediction invoked = PredictUninvoked);
     Node assignExprWithoutYield(YieldHandling yieldHandling, unsigned err);
     Node yieldExpression(InHandling inHandling);
     Node condExpr1(InHandling inHandling, YieldHandling yieldHandling,
+                   TripledotHandling tripledotHandling,
                    InvokedPrediction invoked = PredictUninvoked);
     Node orExpr1(InHandling inHandling, YieldHandling yieldHandling,
-                 InvokedPrediction invoked = PredictUninvoked);
-    Node unaryExpr(YieldHandling yieldHandling, InvokedPrediction invoked = PredictUninvoked);
-    Node memberExpr(YieldHandling yieldHandling, TokenKind tt, bool allowCallSyntax,
-                    InvokedPrediction invoked = PredictUninvoked);
-    Node primaryExpr(YieldHandling yieldHandling, TokenKind tt,
+                 TripledotHandling tripledotHandling,
+                   InvokedPrediction invoked = PredictUninvoked);
+    Node unaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
+                   InvokedPrediction invoked = PredictUninvoked);
+    Node memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, TokenKind tt,
+                    bool allowCallSyntax, InvokedPrediction invoked = PredictUninvoked);
+    Node primaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, TokenKind tt,
                      InvokedPrediction invoked = PredictUninvoked);
     Node parenExprOrGeneratorComprehension(YieldHandling yieldHandling);
     Node exprInParens(InHandling inHandling, YieldHandling yieldHandling);
 
     bool tryNewTarget(Node& newTarget);
     bool checkAndMarkSuperScope();
 
     Node methodDefinition(YieldHandling yieldHandling, PropertyType propType,
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -250,17 +250,17 @@ class SharedContext
     }
 
     // JSOPTION_EXTRA_WARNINGS warnings or strict mode errors.
     bool needStrictChecks() const {
         return strict() || extraWarnings;
     }
 
     bool isDotVariable(JSAtom* atom) const {
-        return atom == context->names().dotGenerator || atom == context->names().dotGenRVal;
+        return atom == context->names().dotGenerator;
     }
 };
 
 class MOZ_STACK_CLASS GlobalSharedContext : public SharedContext
 {
     Rooted<ScopeObject*> staticScope_;
 
   public:
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -299,17 +299,17 @@ class SyntaxParseHandler
 
     Node newIfStatement(uint32_t begin, Node cond, Node then, Node else_) { return NodeGeneric; }
     Node newDoWhileStatement(Node body, Node cond, const TokenPos& pos) { return NodeGeneric; }
     Node newWhileStatement(uint32_t begin, Node cond, Node body) { return NodeGeneric; }
     Node newSwitchStatement(uint32_t begin, Node discriminant, Node caseList) { return NodeGeneric; }
     Node newCaseOrDefault(uint32_t begin, Node expr, Node body) { return NodeGeneric; }
     Node newContinueStatement(PropertyName* label, const TokenPos& pos) { return NodeGeneric; }
     Node newBreakStatement(PropertyName* label, const TokenPos& pos) { return NodeBreak; }
-    Node newReturnStatement(Node expr, Node genrval, const TokenPos& pos) { return NodeReturn; }
+    Node newReturnStatement(Node expr, const TokenPos& pos) { return NodeReturn; }
 
     Node newLabeledStatement(PropertyName* label, Node stmt, uint32_t begin) {
         return NodeGeneric;
     }
 
     Node newThrowStatement(Node expr, const TokenPos& pos) { return NodeThrow; }
     Node newTryStatement(uint32_t begin, Node body, Node catchList, Node finallyBlock) {
         return NodeGeneric;
--- a/js/src/jit-test/lib/prologue.js
+++ b/js/src/jit-test/lib/prologue.js
@@ -1,13 +1,15 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+// explicitly run on ECMA 5
+version(185);
 
 var appendToActual = function(s) {
     actual += s + ',';
 }
 
 if (!("gczeal" in this)) {
   gczeal = function() { }
 }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/property-enumeration-order.js
@@ -0,0 +1,37 @@
+function check(obj, expected, expectedAll = expected) {
+    assertEq(Reflect.ownKeys(obj).join(""), expectedAll);
+    assertEq(Object.getOwnPropertyNames(obj).join(""), expectedAll);
+    assertEq(Object.keys(obj).join(""), expected);
+    var s = "";
+    for (var x in obj)
+	s += x;
+    assertEq(s, expected);
+}
+
+var obj = {2: 0, 0: 0, "foo": "bar", 4: 0, "-5": 1};
+check(obj, "024foo-5");
+obj.x = 1;
+check(obj, "024foo-5x");
+obj[1] = 1;
+check(obj, "0124foo-5x");
+obj[1.2] = 1;
+check(obj, "0124foo-5x1.2");
+Object.defineProperty(obj, '3', {value:1,enumerable:false,configurable:false,writable:false});
+check(obj, "0124foo-5x1.2", "01234foo-5x1.2");
+delete obj[2];
+check(obj, "014foo-5x1.2", "0134foo-5x1.2");
+obj[2] = 1;
+check(obj, "0124foo-5x1.2", "01234foo-5x1.2");
+
+assertEq(JSON.stringify(obj), '{"0":0,"1":1,"2":1,"4":0,"foo":"bar","-5":1,"x":1,"1.2":1}');
+
+var arr = [1, 2, 3];
+Object.defineProperty(arr, '3', {value:1,enumerable:true,configurable:true,writable:true});
+check(arr, "0123", "0123length");
+Object.defineProperty(arr, '1000', {value:1,enumerable:true,configurable:true,writable:false});
+check(arr, "01231000", "01231000length");
+
+arr = [1, 2, , , 5];
+check(arr, "014", "014length");
+arr[0xfffffff] = 4;
+check(arr, "014268435455", "014268435455length");
--- a/js/src/jit-test/tests/ion/bug1060387.js
+++ b/js/src/jit-test/tests/ion/bug1060387.js
@@ -2,11 +2,11 @@ function foo() {
     var obj = new Object();
     var index = [-0, 2147483648, 1073741825];
     for (var j in index) {
         testProperty(index[j]);
     }
     function testProperty(i) {
         obj[i] = '' + i;
     }
-    assertEq(JSON.stringify(obj), '{"0":"0","2147483648":"2147483648","1073741825":"1073741825"}');
+    assertEq(JSON.stringify(obj), '{"0":"0","1073741825":"1073741825","2147483648":"2147483648"}');
 }
 foo();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/parser/arrow-rest.js
@@ -0,0 +1,234 @@
+// The parser should throw SyntaxError immediately if it finds "..." in a
+// context where it's not allowed.
+
+load(libdir + "class.js");
+
+function testThrow(code, column) {
+  var caught = false;
+  try {
+    eval(code);
+  } catch (e) {
+    caught = true;
+    assertEq(e.columnNumber, column);
+  }
+  assertEq(caught, true,
+           "failed to throw evaluating <" + code + ">");
+}
+
+// expression statement
+
+testThrow(`
+...a)=>
+`, 0);
+
+// default parameter
+
+testThrow(`
+function f(x=...a) =>
+`, 13);
+
+// rest parameter
+
+testThrow(`
+function f(... ...a) =>
+`, 15);
+
+// destructuring parameter
+
+testThrow(`
+([... ...a)=>
+`, 6);
+
+testThrow(`
+({...a)=>
+`, 2);
+
+testThrow(`
+function f([... ...a)=>
+`, 16);
+
+testThrow(`
+function f({...a)=>
+`, 12);
+
+// arrow
+
+testThrow(`
+x => ...a)=>
+`, 5);
+
+// template literal
+
+testThrow("`${ ...a)=>}`", 4);
+
+// destructing assignment
+
+testThrow(`
+var [... ...a)=>
+`, 9);
+
+testThrow(`
+var {...a)=>
+`, 5);
+
+// initializer
+
+testThrow(`
+var [a] = ...a)=>
+`, 10);
+
+testThrow(`
+var {a:a} = ...a)=>
+`, 12);
+
+testThrow(`
+var a = ...a)=>
+`, 8);
+
+// if statement
+
+testThrow(`
+if (...a) =>
+`, 4);
+
+// for statement
+
+testThrow(`
+for (...a)=>
+`, 5);
+
+testThrow(`
+for (let a in ...a)=>
+`, 14);
+
+testThrow(`
+for (let a of ...a)=>
+`, 14);
+
+testThrow(`
+for (; ...a)=>
+`, 7);
+
+testThrow(`
+for (;; ...a)=>
+`, 8);
+
+// case
+
+testThrow(`
+switch (x) { case ...a)=>
+`, 18);
+
+// return statement
+
+testThrow(`
+function f(x) { return ...a)=>
+`, 23);
+
+// yield expression
+
+testThrow(`
+function* f(x) { yield ...a)=>
+`, 23);
+
+// throw statement
+
+testThrow(`
+throw ...a) =>
+`, 6);
+
+testThrow(`
+try {} catch (x if ...a) =>
+`, 19);
+
+// class
+
+if (classesEnabled()) {
+testThrow(`
+class A extends ...a) =>
+`, 16);
+}
+
+// conditional expression
+
+testThrow(`
+1 ? ...a) =>
+`, 4);
+
+testThrow(`
+1 ? 2 : ...a) =>
+`, 8);
+
+// unary operators
+
+testThrow(`
+void ...a) =>
+`, 5);
+
+testThrow(`
+typeof ...a) =>
+`, 7);
+
+testThrow(`
+++ ...a) =>
+`, 3);
+
+testThrow(`
+delete ...a) =>
+`, 7);
+
+// array comprehension
+
+testThrow(`
+[x for (...a) =>
+`, 8);
+
+testThrow(`
+[x for (x of ...a) =>
+`, 13);
+
+testThrow(`
+[for (...a) =>
+`, 6);
+
+testThrow(`
+[for (x of y) if (...a) =>
+`, 18);
+
+testThrow(`
+[for (x of y) if (x) ...a) =>
+`, 21);
+
+// new
+
+testThrow(`
+new ...a) =>
+`, 4);
+
+// member expression
+
+testThrow(`
+x[...a) =>
+`, 2);
+
+// array literal
+
+testThrow(`
+[... ...a) =>
+`, 5);
+
+// object literal
+
+testThrow(`
+({[...a) =>
+`, 3);
+
+testThrow(`
+({x: ...a) =>
+`, 5);
+
+// assignment
+
+testThrow(`
+x = ...a) =>
+`, 4);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/parser/modifier-arrow-rest.js
@@ -0,0 +1,11 @@
+load(libdir + "syntax.js");
+
+var postfixes = [
+  "...foo) => 1 @",
+];
+
+function check_syntax_error(e, code) {
+  assertEq(e instanceof SyntaxError, true);
+}
+
+test_syntax(postfixes, check_syntax_error, true);
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -3575,16 +3575,39 @@ BaselineCompiler::emit_JSOP_ENDITER()
 {
     frame.popRegsAndSync(1);
 
     ICIteratorClose_Fallback::Compiler compiler(cx);
     return emitOpIC(compiler.getStub(&stubSpace_));
 }
 
 bool
+BaselineCompiler::emit_JSOP_GETRVAL()
+{
+    frame.syncStack(0);
+
+    Label norval, done;
+    Address flags = frame.addressOfFlags();
+    masm.branchTest32(Assembler::Zero, flags, Imm32(BaselineFrame::HAS_RVAL), &norval);
+    // Get the value from the return value slot, if any.
+    masm.loadValue(frame.addressOfReturnValue(), R0);
+    masm.jump(&done);
+
+    // Push undefined otherwise.  When we throw an exception in the try
+    // block, rval is not yet initialized when entering finally block.
+    masm.bind(&norval);
+    masm.moveValue(UndefinedValue(), R0);
+
+    masm.bind(&done);
+    frame.push(R0);
+
+    return true;
+}
+
+bool
 BaselineCompiler::emit_JSOP_SETRVAL()
 {
     // Store to the frame's return value slot.
     storeValue(frame.peek(-1), frame.addressOfReturnValue(), R2);
     masm.or32(Imm32(BaselineFrame::HAS_RVAL), frame.addressOfFlags());
     frame.pop();
     return true;
 }
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -193,16 +193,17 @@ namespace jit {
     _(JSOP_ENDITER)            \
     _(JSOP_GENERATOR)          \
     _(JSOP_INITIALYIELD)       \
     _(JSOP_YIELD)              \
     _(JSOP_DEBUGAFTERYIELD)    \
     _(JSOP_FINALYIELDRVAL)     \
     _(JSOP_RESUME)             \
     _(JSOP_CALLEE)             \
+    _(JSOP_GETRVAL)            \
     _(JSOP_SETRVAL)            \
     _(JSOP_RETRVAL)            \
     _(JSOP_RETURN)             \
     _(JSOP_NEWTARGET)          \
     _(JSOP_SUPERCALL)          \
     _(JSOP_SPREADSUPERCALL)    \
     _(JSOP_THROWSETCONST)      \
     _(JSOP_THROWSETALIASEDCONST)
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -642,43 +642,23 @@ class Imm8 : public Operand2
 {
   public:
     explicit Imm8(uint32_t imm)
       : Operand2(EncodeImm(imm))
     { }
 
   public:
     static datastore::Imm8mData EncodeImm(uint32_t imm) {
-        // mozilla::CountLeadingZeroes32(imm) requires imm != 0.
-        if (imm == 0)
-            return datastore::Imm8mData(0, 0);
-        int left = mozilla::CountLeadingZeroes32(imm) & 30;
-        // See if imm is a simple value that can be encoded with a rotate of 0.
-        // This is effectively imm <= 0xff, but I assume this can be optimized
-        // more.
-        if (left >= 24)
-            return datastore::Imm8mData(imm, 0);
-
-        // Mask out the 8 bits following the first bit that we found, see if we
-        // have 0 yet.
-        int no_imm = imm & ~(0xff << (24 - left));
-        if (no_imm == 0) {
-            return  datastore::Imm8mData(imm >> (24 - left), ((8 + left) >> 1));
+        // An encodable integer has a maximum of 8 contiguous set bits,
+        // with an optional wrapped left rotation to even bit positions.
+        for (int rot = 0; rot < 16; rot++) {
+            uint32_t rotimm = mozilla::RotateLeft(imm, rot*2);
+            if (rotimm <= 0xFF)
+                return datastore::Imm8mData(rotimm, rot);
         }
-        // Look for the most signifigant bit set, once again.
-        int right = 32 - (mozilla::CountLeadingZeroes32(no_imm) & 30);
-        // If it is in the bottom 8 bits, there is a chance that this is a
-        // wraparound case.
-        if (right >= 8)
-            return datastore::Imm8mData();
-        // Rather than masking out bits and checking for 0, just rotate the
-        // immediate that we were passed in, and see if it fits into 8 bits.
-        unsigned int mask = imm << (8 - right) | imm >> (24 + right);
-        if (mask <= 0xff)
-            return datastore::Imm8mData(mask, (8 - right) >> 1);
         return datastore::Imm8mData();
     }
 
     // Pair template?
     struct TwoImm8mData
     {
         datastore::Imm8mData fst, snd;
 
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -131,18 +131,19 @@ CodeGeneratorARM::bailoutIf(Assembler::C
 
     // Though the assembler doesn't track all frame pushes, at least make sure
     // the known value makes sense. We can't use bailout tables if the stack
     // isn't properly aligned to the static frame size.
     MOZ_ASSERT_IF(frameClass_ != FrameSizeClass::None(),
                   frameClass_.frameSize() == masm.framePushed());
 
     if (assignBailoutId(snapshot)) {
-        uint8_t* code = Assembler::BailoutTableStart(deoptTable_->raw()) + snapshot->bailoutId() * BAILOUT_TABLE_ENTRY_SIZE;
-        masm.ma_b(code, Relocation::HARDCODED, condition);
+        uint8_t* bailoutTable = Assembler::BailoutTableStart(deoptTable_->raw());
+        uint8_t* code = bailoutTable + snapshot->bailoutId() * BAILOUT_TABLE_ENTRY_SIZE;
+        masm.ma_b(code, condition);
         return;
     }
 
     // We could not use a jump table, either because all bailout IDs were
     // reserved, or a jump table is not optimal for this frame size or
     // platform. Whatever, we will generate a lazy bailout.
     InlineScriptTree* tree = snapshot->mir()->block()->trackedTree();
     OutOfLineBailout* ool = new(alloc()) OutOfLineBailout(snapshot, masm.framePushed());
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -46,18 +46,17 @@ MacroAssemblerARM::convertBoolToInt32(Re
     ma_and(Imm32(0xff), source, dest);
 }
 
 void
 MacroAssemblerARM::convertInt32ToDouble(Register src, FloatRegister dest_)
 {
     // Direct conversions aren't possible.
     VFPRegister dest = VFPRegister(dest_);
-    as_vxfer(src, InvalidReg, dest.sintOverlay(),
-             CoreToFloat);
+    as_vxfer(src, InvalidReg, dest.sintOverlay(), CoreToFloat);
     as_vcvt(dest, dest.sintOverlay());
 }
 
 void
 MacroAssemblerARM::convertInt32ToDouble(const Address& src, FloatRegister dest)
 {
     ScratchDoubleScope scratch(asMasm());
     ma_vldr(src, scratch);
@@ -311,34 +310,33 @@ MacroAssemblerARM::alu_dbl(Register src1
     // doesn't have a dest, such as check for overflow by doing first operation
     // don't do second operation if first operation overflowed. This preserves
     // the overflow condition code. Unfortunately, it is horribly brittle.
     as_alu(dest, src1, Operand2(both.fst), interop, LeaveCC, c);
     as_alu(dest, dest, Operand2(both.snd), op, s, c);
     return true;
 }
 
-
 void
 MacroAssemblerARM::ma_alu(Register src1, Imm32 imm, Register dest,
-                          ALUOp op,
-                          SBit s, Condition c)
+                          ALUOp op, SBit s, Condition c)
 {
     // As it turns out, if you ask for a compare-like instruction you *probably*
     // want it to set condition codes.
     if (dest == InvalidReg)
         MOZ_ASSERT(s == SetCC);
 
     // The operator gives us the ability to determine how this can be used.
     Imm8 imm8 = Imm8(imm.value);
     // One instruction: If we can encode it using an imm8m, then do so.
     if (!imm8.invalid) {
         as_alu(dest, src1, imm8, op, s, c);
         return;
     }
+
     // One instruction, negated:
     Imm32 negImm = imm;
     Register negDest;
     ALUOp negOp = ALUNeg(op, dest, &negImm, &negDest);
     Imm8 negImm8 = Imm8(negImm.value);
     // 'add r1, r2, -15' can be replaced with 'sub r1, r2, 15'. For bonus
     // points, dest can be replaced (nearly always invalid => ScratchRegister)
     // This is useful if we wish to negate tst. tst has an invalid (aka not
@@ -562,58 +560,66 @@ MacroAssemblerARM::ma_mov(ImmGCPtr ptr, 
 }
 
 // Shifts (just a move with a shifting op2)
 void
 MacroAssemblerARM::ma_lsl(Imm32 shift, Register src, Register dst)
 {
     as_mov(dst, lsl(src, shift.value));
 }
+
 void
 MacroAssemblerARM::ma_lsr(Imm32 shift, Register src, Register dst)
 {
     as_mov(dst, lsr(src, shift.value));
 }
+
 void
 MacroAssemblerARM::ma_asr(Imm32 shift, Register src, Register dst)
 {
     as_mov(dst, asr(src, shift.value));
 }
+
 void
 MacroAssemblerARM::ma_ror(Imm32 shift, Register src, Register dst)
 {
     as_mov(dst, ror(src, shift.value));
 }
+
 void
 MacroAssemblerARM::ma_rol(Imm32 shift, Register src, Register dst)
 {
     as_mov(dst, rol(src, shift.value));
 }
 
 // Shifts (just a move with a shifting op2)
 void
 MacroAssemblerARM::ma_lsl(Register shift, Register src, Register dst)
 {
     as_mov(dst, lsl(src, shift));
 }
+
 void
 MacroAssemblerARM::ma_lsr(Register shift, Register src, Register dst)
 {
     as_mov(dst, lsr(src, shift));
 }
+
 void
 MacroAssemblerARM::ma_asr(Register shift, Register src, Register dst)
 {
     as_mov(dst, asr(src, shift));
 }
+
 void
 MacroAssemblerARM::ma_ror(Register shift, Register src, Register dst)
 {
     as_mov(dst, ror(src, shift));
 }
+
 void
 MacroAssemblerARM::ma_rol(Register shift, Register src, Register dst)
 {
     ScratchRegisterScope scratch(asMasm());
     ma_rsb(shift, Imm32(32), scratch);
     as_mov(dst, ror(src, scratch));
 }
 
@@ -638,27 +644,30 @@ MacroAssemblerARM::ma_neg(Register src1,
 }
 
 // And.
 void
 MacroAssemblerARM::ma_and(Register src, Register dest, SBit s, Assembler::Condition c)
 {
     ma_and(dest, src, dest);
 }
+
 void
 MacroAssemblerARM::ma_and(Register src1, Register src2, Register dest,
                           SBit s, Assembler::Condition c)
 {
     as_and(dest, src1, O2Reg(src2), s, c);
 }
+
 void
 MacroAssemblerARM::ma_and(Imm32 imm, Register dest, SBit s, Assembler::Condition c)
 {
     ma_alu(dest, imm, dest, OpAnd, s, c);
 }
+
 void
 MacroAssemblerARM::ma_and(Imm32 imm, Register src1, Register dest,
                           SBit s, Assembler::Condition c)
 {
     ma_alu(src1, imm, dest, OpAnd, s, c);
 }
 
 // Bit clear (dest <- dest & ~imm) or (dest <- src1 & ~src2).
@@ -669,70 +678,78 @@ MacroAssemblerARM::ma_bic(Imm32 imm, Reg
 }
 
 // Exclusive or.
 void
 MacroAssemblerARM::ma_eor(Register src, Register dest, SBit s, Assembler::Condition c)
 {
     ma_eor(dest, src, dest, s, c);
 }
+
 void
 MacroAssemblerARM::ma_eor(Register src1, Register src2, Register dest,
                           SBit s, Assembler::Condition c)
 {
     as_eor(dest, src1, O2Reg(src2), s, c);
 }
+
 void
 MacroAssemblerARM::ma_eor(Imm32 imm, Register dest, SBit s, Assembler::Condition c)
 {
     ma_alu(dest, imm, dest, OpEor, s, c);
 }
+
 void
 MacroAssemblerARM::ma_eor(Imm32 imm, Register src1, Register dest,
        SBit s, Assembler::Condition c)
 {
     ma_alu(src1, imm, dest, OpEor, s, c);
 }
 
 // Or.
 void
 MacroAssemblerARM::ma_orr(Register src, Register dest, SBit s, Assembler::Condition c)
 {
     ma_orr(dest, src, dest, s, c);
 }
+
 void
 MacroAssemblerARM::ma_orr(Register src1, Register src2, Register dest,
                           SBit s, Assembler::Condition c)
 {
     as_orr(dest, src1, O2Reg(src2), s, c);
 }
+
 void
 MacroAssemblerARM::ma_orr(Imm32 imm, Register dest, SBit s, Assembler::Condition c)
 {
     ma_alu(dest, imm, dest, OpOrr, s, c);
 }
+
 void
 MacroAssemblerARM::ma_orr(Imm32 imm, Register src1, Register dest,
                           SBit s, Assembler::Condition c)
 {
     ma_alu(src1, imm, dest, OpOrr, s, c);
 }
 
 // Arithmetic-based ops.
 // Add with carry.
 void
 MacroAssemblerARM::ma_adc(Imm32 imm, Register dest, SBit s, Condition c)
 {
     ma_alu(dest, imm, dest, OpAdc, s, c);
 }
+
 void
 MacroAssemblerARM::ma_adc(Register src, Register dest, SBit s, Condition c)
 {
     as_alu(dest, dest, O2Reg(src), OpAdc, s, c);
 }
+
 void
 MacroAssemblerARM::ma_adc(Register src1, Register src2, Register dest, SBit s, Condition c)
 {
     as_alu(dest, src1, O2Reg(src2), OpAdc, s, c);
 }
 
 // Add.
 void
@@ -741,127 +758,143 @@ MacroAssemblerARM::ma_add(Imm32 imm, Reg
     ma_alu(dest, imm, dest, OpAdd, s, c);
 }
 
 void
 MacroAssemblerARM::ma_add(Register src1, Register dest, SBit s, Condition c)
 {
     ma_alu(dest, O2Reg(src1), dest, OpAdd, s, c);
 }
+
 void
 MacroAssemblerARM::ma_add(Register src1, Register src2, Register dest, SBit s, Condition c)
 {
     as_alu(dest, src1, O2Reg(src2), OpAdd, s, c);
 }
+
 void
 MacroAssemblerARM::ma_add(Register src1, Operand op, Register dest, SBit s, Condition c)
 {
     ma_alu(src1, op, dest, OpAdd, s, c);
 }
+
 void
 MacroAssemblerARM::ma_add(Register src1, Imm32 op, Register dest, SBit s, Condition c)
 {
     ma_alu(src1, op, dest, OpAdd, s, c);
 }
 
 // Subtract with carry.
 void
 MacroAssemblerARM::ma_sbc(Imm32 imm, Register dest, SBit s, Condition c)
 {
     ma_alu(dest, imm, dest, OpSbc, s, c);
 }
+
 void
 MacroAssemblerARM::ma_sbc(Register src1, Register dest, SBit s, Condition c)
 {
     as_alu(dest, dest, O2Reg(src1), OpSbc, s, c);
 }
+
 void
 MacroAssemblerARM::ma_sbc(Register src1, Register src2, Register dest, SBit s, Condition c)
 {
     as_alu(dest, src1, O2Reg(src2), OpSbc, s, c);
 }
 
 // Subtract.
 void
 MacroAssemblerARM::ma_sub(Imm32 imm, Register dest, SBit s, Condition c)
 {
     ma_alu(dest, imm, dest, OpSub, s, c);
 }
+
 void
 MacroAssemblerARM::ma_sub(Register src1, Register dest, SBit s, Condition c)
 {
     ma_alu(dest, Operand(src1), dest, OpSub, s, c);
 }
+
 void
 MacroAssemblerARM::ma_sub(Register src1, Register src2, Register dest, SBit s, Condition c)
 {
     ma_alu(src1, Operand(src2), dest, OpSub, s, c);
 }
+
 void
 MacroAssemblerARM::ma_sub(Register src1, Operand op, Register dest, SBit s, Condition c)
 {
     ma_alu(src1, op, dest, OpSub, s, c);
 }
+
 void
 MacroAssemblerARM::ma_sub(Register src1, Imm32 op, Register dest, SBit s, Condition c)
 {
     ma_alu(src1, op, dest, OpSub, s, c);
 }
 
-// Severse subtract.
+// Reverse subtract.
 void
 MacroAssemblerARM::ma_rsb(Imm32 imm, Register dest, SBit s, Condition c)
 {
     ma_alu(dest, imm, dest, OpRsb, s, c);
 }
+
 void
 MacroAssemblerARM::ma_rsb(Register src1, Register dest, SBit s, Condition c)
 {
     as_alu(dest, dest, O2Reg(src1), OpAdd, s, c);
 }
+
 void
 MacroAssemblerARM::ma_rsb(Register src1, Register src2, Register dest, SBit s, Condition c)
 {
     as_alu(dest, src1, O2Reg(src2), OpRsb, s, c);
 }
+
 void
 MacroAssemblerARM::ma_rsb(Register src1, Imm32 op2, Register dest, SBit s, Condition c)
 {
     ma_alu(src1, op2, dest, OpRsb, s, c);
 }
 
 // Reverse subtract with carry.
 void
 MacroAssemblerARM::ma_rsc(Imm32 imm, Register dest, SBit s, Condition c)
 {
     ma_alu(dest, imm, dest, OpRsc, s, c);
 }
+
 void
 MacroAssemblerARM::ma_rsc(Register src1, Register dest, SBit s, Condition c)
 {
     as_alu(dest, dest, O2Reg(src1), OpRsc, s, c);
 }
+
 void
 MacroAssemblerARM::ma_rsc(Register src1, Register src2, Register dest, SBit s, Condition c)
 {
     as_alu(dest, src1, O2Reg(src2), OpRsc, s, c);
 }
 
 // Compares/tests.
 // Compare negative (sets condition codes as src1 + src2 would).
 void
 MacroAssemblerARM::ma_cmn(Register src1, Imm32 imm, Condition c)
 {
     ma_alu(src1, imm, InvalidReg, OpCmn, SetCC, c);
 }
+
 void
 MacroAssemblerARM::ma_cmn(Register src1, Register src2, Condition c)
 {
     as_alu(InvalidReg, src2, O2Reg(src1), OpCmn, SetCC, c);
 }
+
 void
 MacroAssemblerARM::ma_cmn(Register src1, Operand op, Condition c)
 {
     MOZ_CRASH("Feature NYI");
 }
 
 // Compare (src - src2).
 void
@@ -878,16 +911,17 @@ MacroAssemblerARM::ma_cmp(Register src1,
 
 void
 MacroAssemblerARM::ma_cmp(Register src1, ImmGCPtr ptr, Condition c)
 {
     ScratchRegisterScope scratch(asMasm());
     ma_mov(ptr, scratch);
     ma_cmp(src1, scratch, c);
 }
+
 void
 MacroAssemblerARM::ma_cmp(Register src1, Operand op, Condition c)
 {
     switch (op.getTag()) {
       case Operand::OP2:
         as_cmp(src1, op.toOp2(), c);
         break;
       case Operand::MEM: {
@@ -895,62 +929,67 @@ MacroAssemblerARM::ma_cmp(Register src1,
         ma_ldr(op.toAddress(), scratch);
         as_cmp(src1, O2Reg(scratch), c);
         break;
       }
       default:
         MOZ_CRASH("trying to compare FP and integer registers");
     }
 }
+
 void
 MacroAssemblerARM::ma_cmp(Register src1, Register src2, Condition c)
 {
     as_cmp(src1, O2Reg(src2), c);
 }
 
 // Test for equality, (src1 ^ src2).
 void
 MacroAssemblerARM::ma_teq(Register src1, Imm32 imm, Condition c)
 {
     ma_alu(src1, imm, InvalidReg, OpTeq, SetCC, c);
 }
+
 void
 MacroAssemblerARM::ma_teq(Register src1, Register src2, Condition c)
 {
     as_tst(src1, O2Reg(src2), c);
 }
+
 void
 MacroAssemblerARM::ma_teq(Register src1, Operand op, Condition c)
 {
     as_teq(src1, op.toOp2(), c);
 }
 
-
 // Test (src1 & src2).
 void
 MacroAssemblerARM::ma_tst(Register src1, Imm32 imm, Condition c)
 {
     ma_alu(src1, imm, InvalidReg, OpTst, SetCC, c);
 }
+
 void
 MacroAssemblerARM::ma_tst(Register src1, Register src2, Condition c)
 {
     as_tst(src1, O2Reg(src2), c);
 }
+
 void
 MacroAssemblerARM::ma_tst(Register src1, Operand op, Condition c)
 {
     as_tst(src1, op.toOp2(), c);
 }
 
 void
 MacroAssemblerARM::ma_mul(Register src1, Register src2, Register dest)
 {
     as_mul(dest, src1, src2);
 }
+
 void
 MacroAssemblerARM::ma_mul(Register src1, Imm32 imm, Register dest)
 {
     ScratchRegisterScope scratch(asMasm());
     ma_mov(imm, scratch);
     as_mul(dest, src1, scratch);
 }
 
@@ -1091,22 +1130,24 @@ MacroAssemblerARM::ma_sdiv(Register num,
     as_sdiv(dest, num, div, cond);
 }
 
 void
 MacroAssemblerARM::ma_udiv(Register num, Register div, Register dest, Condition cond)
 {
     as_udiv(dest, num, div, cond);
 }
-// Miscelanous instructions
+
+// Miscellaneous instructions.
 void
 MacroAssemblerARM::ma_clz(Register src, Register dest, Condition cond)
 {
     as_clz(dest, src, cond);
 }
+
 // Memory.
 // Shortcut for when we know we're transferring 32 bits of data.
 void
 MacroAssemblerARM::ma_dtr(LoadStore ls, Register rn, Imm32 offset, Register rt,
                           Index mode, Assembler::Condition cc)
 {
     ma_dataTransferN(ls, 32, true, rn, offset, rt, mode, cc);
 }
@@ -1130,29 +1171,31 @@ MacroAssemblerARM::ma_dtr(LoadStore ls, 
     ma_dataTransferN(ls, 32, true, addr.base, Imm32(addr.offset), rt, mode, cc);
 }
 
 void
 MacroAssemblerARM::ma_str(Register rt, const Address& addr, Index mode, Condition cc)
 {
     ma_dtr(IsStore, rt, addr, mode, cc);
 }
+
 void
 MacroAssemblerARM::ma_strd(Register rt, DebugOnly<Register> rt2, EDtrAddr addr, Index mode, Condition cc)
 {
     MOZ_ASSERT((rt.code() & 1) == 0);
     MOZ_ASSERT(rt2.value.code() == rt.code() + 1);
     as_extdtr(IsStore, 64, true, mode, rt, addr, cc);
 }
 
 void
 MacroAssemblerARM::ma_ldr(DTRAddr addr, Register rt, Index mode, Condition cc)
 {
     as_dtr(IsLoad, 32, mode, rt, addr, cc);
 }
+
 void
 MacroAssemblerARM::ma_ldr(const Address& addr, Register rt, Index mode, Condition cc)
 {
     ma_dtr(IsLoad, rt, addr, mode, cc);
 }
 
 void
 MacroAssemblerARM::ma_ldrb(DTRAddr addr, Register rt, Index mode, Condition cc)
@@ -1166,46 +1209,49 @@ MacroAssemblerARM::ma_ldrsh(EDtrAddr add
     as_extdtr(IsLoad, 16, true, mode, rt, addr, cc);
 }
 
 void
 MacroAssemblerARM::ma_ldrh(EDtrAddr addr, Register rt, Index mode, Condition cc)
 {
     as_extdtr(IsLoad, 16, false, mode, rt, addr, cc);
 }
+
 void
 MacroAssemblerARM::ma_ldrsb(EDtrAddr addr, Register rt, Index mode, Condition cc)
 {
     as_extdtr(IsLoad, 8, true, mode, rt, addr, cc);
 }
+
 void
 MacroAssemblerARM::ma_ldrd(EDtrAddr addr, Register rt, DebugOnly<Register> rt2,
                            Index mode, Condition cc)
 {
     MOZ_ASSERT((rt.code() & 1) == 0);
     MOZ_ASSERT(rt2.value.code() == rt.code() + 1);
     as_extdtr(IsLoad, 64, true, mode, rt, addr, cc);
 }
+
 void
 MacroAssemblerARM::ma_strh(Register rt, EDtrAddr addr, Index mode, Condition cc)
 {
     as_extdtr(IsStore, 16, false, mode, rt, addr, cc);
 }
 
 void
 MacroAssemblerARM::ma_strb(Register rt, DTRAddr addr, Index mode, Condition cc)
 {
     as_dtr(IsStore, 8, mode, rt, addr, cc);
 }
 
 // Specialty for moving N bits of data, where n == 8,16,32,64.
 BufferOffset
 MacroAssemblerARM::ma_dataTransferN(LoadStore ls, int size, bool IsSigned,
                           Register rn, Register rm, Register rt,
-                                    Index mode, Assembler::Condition cc, unsigned shiftAmount)
+                          Index mode, Assembler::Condition cc, unsigned shiftAmount)
 {
     if (size == 32 || (size == 8 && !IsSigned))
         return as_dtr(ls, size, mode, rt, DTRAddr(rn, DtrRegImmShift(rm, LSL, shiftAmount)), cc);
 
     ScratchRegisterScope scratch(asMasm());
 
     if (shiftAmount != 0) {
         MOZ_ASSERT(rn != scratch);
@@ -1400,16 +1446,17 @@ MacroAssemblerARM::ma_dataTransferN(Load
     }
 }
 
 void
 MacroAssemblerARM::ma_pop(Register r)
 {
     ma_dtr(IsLoad, sp, Imm32(4), r, PostIndex);
 }
+
 void
 MacroAssemblerARM::ma_push(Register r)
 {
     // Pushing sp is not well defined: use two instructions.
     if (r == sp) {
         ScratchRegisterScope scratch(asMasm());
         ma_mov(sp, scratch);
         ma_dtr(IsStore, sp, Imm32(-4), scratch, PreIndex);
@@ -1421,16 +1468,17 @@ MacroAssemblerARM::ma_push(Register r)
 
 void
 MacroAssemblerARM::ma_vpop(VFPRegister r)
 {
     startFloatTransferM(IsLoad, sp, IA, WriteBack);
     transferFloatReg(r);
     finishFloatTransfer();
 }
+
 void
 MacroAssemblerARM::ma_vpush(VFPRegister r)
 {
     startFloatTransferM(IsStore, sp, DB, WriteBack);
     transferFloatReg(r);
     finishFloatTransfer();
 }
 
@@ -1438,16 +1486,17 @@ MacroAssemblerARM::ma_vpush(VFPRegister 
 void
 MacroAssemblerARM::ma_dmb(BarrierOption option)
 {
     if (HasDMBDSBISB())
         as_dmb(option);
     else
         as_dmb_trap();
 }
+
 void
 MacroAssemblerARM::ma_dsb(BarrierOption option)
 {
     if (HasDMBDSBISB())
         as_dsb(option);
     else
         as_dsb_trap();
 }
@@ -1460,47 +1509,21 @@ MacroAssemblerARM::ma_b(Label* dest, Ass
 }
 
 void
 MacroAssemblerARM::ma_bx(Register dest, Assembler::Condition c)
 {
     as_bx(dest, c);
 }
 
-static Assembler::RelocBranchStyle
-b_type()
-{
-    return Assembler::B_LDR;
-}
-void
-MacroAssemblerARM::ma_b(void* target, Relocation::Kind reloc, Assembler::Condition c)
-{
-    ScratchRegisterScope scratch(asMasm());
-
-    // We know the absolute address of the target, but not our final location
-    // (with relocating GC, we *can't* know our final location) for now, I'm
-    // going to be conservative, and load this with an absolute address
-    uint32_t trg = (uint32_t)target;
-    switch (b_type()) {
-      case Assembler::B_MOVWT:
-        as_movw(scratch, Imm16(trg & 0xffff), c);
-        as_movt(scratch, Imm16(trg >> 16), c);
-        // This is going to get the branch predictor pissed off.
-        as_bx(scratch, c);
-        break;
-      case Assembler::B_LDR_BX:
-        as_Imm32Pool(scratch, trg, c);
-        as_bx(scratch, c);
-        break;
-      case Assembler::B_LDR:
-        as_Imm32Pool(pc, trg, c);
-        break;
-      default:
-        MOZ_CRASH("Other methods of generating tracable jumps NYI");
-    }
+void
+MacroAssemblerARM::ma_b(void* target, Assembler::Condition c)
+{
+    // An immediate pool is used for easier patching.
+    as_Imm32Pool(pc, uint32_t(target), c);
 }
 
 // This is almost NEVER necessary: we'll basically never be calling a label,
 // except possibly in the crazy bailout-table case.
 void
 MacroAssemblerARM::ma_bl(Label* dest, Assembler::Condition c)
 {
     as_bl(dest, c);
@@ -1687,82 +1710,91 @@ MacroAssemblerARM::ma_vimm_f32(float val
     as_FImm32Pool(vd, value, cc);
 }
 
 void
 MacroAssemblerARM::ma_vcmp(FloatRegister src1, FloatRegister src2, Condition cc)
 {
     as_vcmp(VFPRegister(src1), VFPRegister(src2), cc);
 }
+
 void
 MacroAssemblerARM::ma_vcmp_f32(FloatRegister src1, FloatRegister src2, Condition cc)
 {
     as_vcmp(VFPRegister(src1).singleOverlay(), VFPRegister(src2).singleOverlay(), cc);
 }
+
 void
 MacroAssemblerARM::ma_vcmpz(FloatRegister src1, Condition cc)
 {
     as_vcmpz(VFPRegister(src1), cc);
 }
+
 void
 MacroAssemblerARM::ma_vcmpz_f32(FloatRegister src1, Condition cc)
 {
     as_vcmpz(VFPRegister(src1).singleOverlay(), cc);
 }
 
 void
 MacroAssemblerARM::ma_vcvt_F64_I32(FloatRegister src, FloatRegister dest, Condition cc)
 {
     MOZ_ASSERT(src.isDouble());
     MOZ_ASSERT(dest.isSInt());
     as_vcvt(dest, src, false, cc);
 }
+
 void
 MacroAssemblerARM::ma_vcvt_F64_U32(FloatRegister src, FloatRegister dest, Condition cc)
 {
     MOZ_ASSERT(src.isDouble());
     MOZ_ASSERT(dest.isUInt());
     as_vcvt(dest, src, false, cc);
 }
+
 void
 MacroAssemblerARM::ma_vcvt_I32_F64(FloatRegister src, FloatRegister dest, Condition cc)
 {
     MOZ_ASSERT(src.isSInt());
     MOZ_ASSERT(dest.isDouble());
     as_vcvt(dest, src, false, cc);
 }
+
 void
 MacroAssemblerARM::ma_vcvt_U32_F64(FloatRegister src, FloatRegister dest, Condition cc)
 {
     MOZ_ASSERT(src.isUInt());
     MOZ_ASSERT(dest.isDouble());
     as_vcvt(dest, src, false, cc);
 }
 
 void
 MacroAssemblerARM::ma_vcvt_F32_I32(FloatRegister src, FloatRegister dest, Condition cc)
 {
     MOZ_ASSERT(src.isSingle());
     MOZ_ASSERT(dest.isSInt());
     as_vcvt(VFPRegister(dest).sintOverlay(), VFPRegister(src).singleOverlay(), false, cc);
 }
+
 void
 MacroAssemblerARM::ma_vcvt_F32_U32(FloatRegister src, FloatRegister dest, Condition cc)
 {
     MOZ_ASSERT(src.isSingle());
     MOZ_ASSERT(dest.isUInt());
     as_vcvt(VFPRegister(dest).uintOverlay(), VFPRegister(src).singleOverlay(), false, cc);
 }
+
 void
 MacroAssemblerARM::ma_vcvt_I32_F32(FloatRegister src, FloatRegister dest, Condition cc)
 {
     MOZ_ASSERT(src.isSInt());
     MOZ_ASSERT(dest.isSingle());
     as_vcvt(VFPRegister(dest).singleOverlay(), VFPRegister(src).sintOverlay(), false, cc);
 }
+
 void
 MacroAssemblerARM::ma_vcvt_U32_F32(FloatRegister src, FloatRegister dest, Condition cc)
 {
     MOZ_ASSERT(src.isUInt());
     MOZ_ASSERT(dest.isSingle());
     as_vcvt(VFPRegister(dest).singleOverlay(), VFPRegister(src).uintOverlay(), false, cc);
 }
 
@@ -1850,21 +1882,23 @@ MacroAssemblerARM::ma_vdtr(LoadStore ls,
     return as_vdtr(ls, rt, VFPAddr(scratch, VFPOffImm(0)), cc);
 }
 
 BufferOffset
 MacroAssemblerARM::ma_vldr(VFPAddr addr, VFPRegister dest, Condition cc)
 {
     return as_vdtr(IsLoad, dest, addr, cc);
 }
+
 BufferOffset
 MacroAssemblerARM::ma_vldr(const Address& addr, VFPRegister dest, Condition cc)
 {
     return ma_vdtr(IsLoad, addr, dest, cc);
 }
+
 BufferOffset
 MacroAssemblerARM::ma_vldr(VFPRegister src, Register base, Register index, int32_t shift, Condition cc)
 {
     ScratchRegisterScope scratch(asMasm());
     as_add(scratch, base, lsl(index, shift), LeaveCC, cc);
     return ma_vldr(Address(scratch, 0), src, cc);
 }
 
@@ -1874,16 +1908,17 @@ MacroAssemblerARM::ma_vstr(VFPRegister s
     return as_vdtr(IsStore, src, addr, cc);
 }
 
 BufferOffset
 MacroAssemblerARM::ma_vstr(VFPRegister src, const Address& addr, Condition cc)
 {
     return ma_vdtr(IsStore, addr, src, cc);
 }
+
 BufferOffset
 MacroAssemblerARM::ma_vstr(VFPRegister src, Register base, Register index, int32_t shift,
                            int32_t offset, Condition cc)
 {
     ScratchRegisterScope scratch(asMasm());
     as_add(scratch, base, lsl(index, shift), LeaveCC, cc);
     return ma_vstr(src, Address(scratch, offset), cc);
 }
@@ -1961,52 +1996,58 @@ MacroAssemblerARMCompat::addPtr(const Ad
 
 void
 MacroAssemblerARMCompat::move32(Imm32 imm, Register dest)
 {
     ma_mov(imm, dest);
 }
 
 void
-MacroAssemblerARMCompat::move32(Register src, Register dest) {
+MacroAssemblerARMCompat::move32(Register src, Register dest)
+{
     ma_mov(src, dest);
 }
 
 void
 MacroAssemblerARMCompat::movePtr(Register src, Register dest)
 {
     ma_mov(src, dest);
 }
+
 void
 MacroAssemblerARMCompat::movePtr(ImmWord imm, Register dest)
 {
     ma_mov(Imm32(imm.value), dest);
 }
+
 void
 MacroAssemblerARMCompat::movePtr(ImmGCPtr imm, Register dest)
 {
     ma_mov(imm, dest);
 }
+
 void
 MacroAssemblerARMCompat::movePtr(ImmPtr imm, Register dest)
 {
     movePtr(ImmWord(uintptr_t(imm.value)), dest);
 }
+
 void
 MacroAssemblerARMCompat::movePtr(AsmJSImmPtr imm, Register dest)
 {
     RelocStyle rs;
     if (HasMOVWT())
         rs = L_MOVWT;
     else
         rs = L_LDR;
 
     append(AsmJSAbsoluteLink(CodeOffsetLabel(currentOffset()), imm.kind()));
     ma_movPatchable(Imm32(-1), dest, Always, rs);
 }
+
 void
 MacroAssemblerARMCompat::load8ZeroExtend(const Address& address, Register dest)
 {
     ma_dataTransferN(IsLoad, 8, false, address.base, Imm32(address.offset), dest);
 }
 
 void
 MacroAssemblerARMCompat::load8ZeroExtend(const BaseIndex& src, Register dest)
@@ -2122,16 +2163,17 @@ MacroAssemblerARMCompat::load32(const Ba
     loadPtr(address, dest);
 }
 
 void
 MacroAssemblerARMCompat::load32(AbsoluteAddress address, Register dest)
 {
     loadPtr(address, dest);
 }
+
 void
 MacroAssemblerARMCompat::loadPtr(const Address& address, Register dest)
 {
     ma_ldr(address, dest);
 }
 
 void
 MacroAssemblerARMCompat::loadPtr(const BaseIndex& src, Register dest)
@@ -2292,16 +2334,17 @@ MacroAssemblerARMCompat::store16(Registe
 
 void
 MacroAssemblerARMCompat::store16(Imm32 imm, const BaseIndex& dest)
 {
     AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
     ma_mov(imm, scratch2);
     store16(scratch2, dest);
 }
+
 void
 MacroAssemblerARMCompat::store16(Register src, const BaseIndex& address)
 {
     Register index = address.index;
 
     ScratchRegisterScope scratch(asMasm());
 
     // We don't have LSL on index register yet.
@@ -2311,16 +2354,17 @@ MacroAssemblerARMCompat::store16(Registe
     }
 
     if (address.offset != 0) {
         ma_add(index, Imm32(address.offset), scratch);
         index = scratch;
     }
     ma_strh(src, EDtrAddr(address.base, EDtrOffReg(index)));
 }
+
 void
 MacroAssemblerARMCompat::store32(Register src, AbsoluteAddress address)
 {
     storePtr(src, address);
 }
 
 void
 MacroAssemblerARMCompat::store32(Register src, const Address& address)
@@ -2722,16 +2766,17 @@ MacroAssemblerARMCompat::testInt32(Assem
 
 Assembler::Condition
 MacroAssemblerARMCompat::testBoolean(Assembler::Condition cond, const ValueOperand& value)
 {
     MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
     ma_cmp(value.typeReg(), ImmType(JSVAL_TYPE_BOOLEAN));
     return cond;
 }
+
 Assembler::Condition
 MacroAssemblerARMCompat::testDouble(Assembler::Condition cond, const ValueOperand& value)
 {
     MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
     Assembler::Condition actual = (cond == Equal) ? Below : AboveOrEqual;
     ma_cmp(value.typeReg(), ImmTag(JSVAL_TAG_CLEAR));
     return actual;
 }
@@ -3077,18 +3122,18 @@ MacroAssemblerARMCompat::testGCThing(Con
     MOZ_ASSERT(cond == Equal || cond == NotEqual);
     ScratchRegisterScope scratch(asMasm());
     extractTag(address, scratch);
     ma_cmp(scratch, ImmTag(JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET));
     return cond == Equal ? AboveOrEqual : Below;
 }
 
 void
-MacroAssemblerARMCompat::branchTestValue(Condition cond, const ValueOperand& value, const Value& v,
-                                         Label* label)
+MacroAssemblerARMCompat::branchTestValue(Condition cond, const ValueOperand& value,
+                                         const Value& v, Label* label)
 {
     // If cond == NotEqual, branch when a.payload != b.payload || a.tag !=
     // b.tag. If the payloads are equal, compare the tags. If the payloads are
     // not equal, short circuit true (NotEqual).
     //
     // If cand == Equal, branch when a.payload == b.payload && a.tag == b.tag.
     // If the payloads are equal, compare the tags. If the payloads are not
     // equal, short circuit false (NotEqual).
@@ -3267,17 +3312,18 @@ MacroAssemblerARMCompat::loadInt32OrDoub
 
     // Not an int, just load as double.
     bind(&notInt32);
     ma_vldr(src, dest);
     bind(&end);
 }
 
 void
-MacroAssemblerARMCompat::loadInt32OrDouble(Register base, Register index, FloatRegister dest, int32_t shift)
+MacroAssemblerARMCompat::loadInt32OrDouble(Register base, Register index,
+                                           FloatRegister dest, int32_t shift)
 {
     Label notInt32, end;
 
     JS_STATIC_ASSERT(NUNBOX32_PAYLOAD_OFFSET == 0);
 
     ScratchRegisterScope scratch(asMasm());
 
     // If it's an int, convert it to double.
@@ -3304,17 +3350,16 @@ MacroAssemblerARMCompat::loadInt32OrDoub
 
 void
 MacroAssemblerARMCompat::loadConstantDouble(double dp, FloatRegister dest)
 {
     as_FImm64Pool(dest, dp);
 }
 
 // Treat the value as a boolean, and set condition codes accordingly.
-
 Assembler::Condition
 MacroAssemblerARMCompat::testInt32Truthy(bool truthy, const ValueOperand& operand)
 {
     ma_tst(operand.payloadReg(), operand.payloadReg());
     return truthy ? NonZero : Zero;
 }
 
 Assembler::Condition
@@ -3351,18 +3396,18 @@ Register
 MacroAssemblerARMCompat::extractTag(const BaseIndex& address, Register scratch)
 {
     ma_alu(address.base, lsl(address.index, address.scale), scratch, OpAdd, LeaveCC);
     return extractTag(Address(scratch, address.offset), scratch);
 }
 
 template <typename T>
 void
-MacroAssemblerARMCompat::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest,
-                                           MIRType slotType)
+MacroAssemblerARMCompat::storeUnboxedValue(ConstantOrRegister value, MIRType valueType,
+                                           const T& dest, MIRType slotType)
 {
     if (valueType == MIRType_Double) {
         storeDouble(value.reg().typedReg().fpu(), dest);
         return;
     }
 
     // Store the type tag if needed.
     if (valueType != slotType)
@@ -3371,22 +3416,22 @@ MacroAssemblerARMCompat::storeUnboxedVal
     // Store the payload.
     if (value.constant())
         storePayload(value.value(), dest);
     else
         storePayload(value.reg().typedReg().gpr(), dest);
 }
 
 template void
-MacroAssemblerARMCompat::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const Address& dest,
-                                           MIRType slotType);
+MacroAssemblerARMCompat::storeUnboxedValue(ConstantOrRegister value, MIRType valueType,
+                                           const Address& dest, MIRType slotType);
 
 template void
-MacroAssemblerARMCompat::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const BaseIndex& dest,
-                                           MIRType slotType);
+MacroAssemblerARMCompat::storeUnboxedValue(ConstantOrRegister value, MIRType valueType,
+                                           const BaseIndex& dest, MIRType slotType);
 
 
 void
 MacroAssemblerARMCompat::branchTest64(Condition cond, Register64 lhs, Register64 rhs,
                                       Register temp, Label* label)
 {
     if (cond == Assembler::Zero) {
         MOZ_ASSERT(lhs.low == rhs.low);
@@ -3404,16 +3449,17 @@ MacroAssemblerARMCompat::moveValue(const
 {
     jsval_layout jv = JSVAL_TO_IMPL(val);
     ma_mov(Imm32(jv.s.tag), type);
     if (val.isMarkable())
         ma_mov(ImmGCPtr(reinterpret_cast<gc::Cell*>(val.toGCThing())), data);
     else
         ma_mov(Imm32(jv.s.payload.i32), data);
 }
+
 void
 MacroAssemblerARMCompat::moveValue(const Value& val, const ValueOperand& dest)
 {
     moveValue(val, dest.typeReg(), dest.payloadReg());
 }
 
 /////////////////////////////////////////////////////////////////
 // X86/X64-common (ARM too now) interface.
@@ -3498,30 +3544,21 @@ MacroAssemblerARMCompat::loadValue(Addre
     // instruction.
 
     if (val.payloadReg().code() < val.typeReg().code()) {
         if (src.offset <= 4 && src.offset >= -8 && (src.offset & 3) == 0) {
             // Turns out each of the 4 value -8, -4, 0, 4 corresponds exactly
             // with one of LDM{DB, DA, IA, IB}
             DTMMode mode;
             switch (src.offset) {
-              case -8:
-                mode = DB;
-                break;
-              case -4:
-                mode = DA;
-                break;
-              case 0:
-                mode = IA;
-                break;
-              case 4:
-                mode = IB;
-                break;
-              default:
-                MOZ_CRASH("Bogus Offset for LoadValue as DTM");
+              case -8: mode = DB; break;
+              case -4: mode = DA; break;
+              case  0: mode = IA; break;
+              case  4: mode = IB; break;
+              default: MOZ_CRASH("Bogus Offset for LoadValue as DTM");
             }
             startDataTransferM(IsLoad, src.base, mode);
             transferReg(val.payloadReg());
             transferReg(val.typeReg());
             finishDataTransfer();
             return;
         }
     }
@@ -3541,35 +3578,39 @@ MacroAssemblerARMCompat::tagValue(JSValu
 {
     MOZ_ASSERT(dest.typeReg() != dest.payloadReg());
     if (payload != dest.payloadReg())
         ma_mov(payload, dest.payloadReg());
     ma_mov(ImmType(type), dest.typeReg());
 }
 
 void
-MacroAssemblerARMCompat::pushValue(ValueOperand val) {
+MacroAssemblerARMCompat::pushValue(ValueOperand val)
+{
     ma_push(val.typeReg());
     ma_push(val.payloadReg());
 }
+
 void
 MacroAssemblerARMCompat::pushValue(const Address& addr)
 {
     ScratchRegisterScope scratch(asMasm());
     ma_ldr(ToType(addr), scratch);
     ma_push(scratch);
     ma_ldr(ToPayloadAfterStackPush(addr), scratch);
     ma_push(scratch);
 }
 
 void
-MacroAssemblerARMCompat::popValue(ValueOperand val) {
+MacroAssemblerARMCompat::popValue(ValueOperand val)
+{
     ma_pop(val.payloadReg());
     ma_pop(val.typeReg());
 }
+
 void
 MacroAssemblerARMCompat::storePayload(const Value& val, const Address& dest)
 {
     AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
 
     jsval_layout jv = JSVAL_TO_IMPL(val);
     if (val.isMarkable())
         ma_mov(ImmGCPtr((gc::Cell*)jv.s.payload.ptr), scratch2);
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -342,20 +342,19 @@ class MacroAssemblerARM : public Assembl
     void ma_vpush(VFPRegister r);
 
     // Barriers.
     void ma_dmb(BarrierOption option=BarrierSY);
     void ma_dsb(BarrierOption option=BarrierSY);
 
     // Branches when done from within arm-specific code.
     BufferOffset ma_b(Label* dest, Condition c = Always);
+    void ma_b(void* target, Condition c = Always);
     void ma_bx(Register dest, Condition c = Always);
 
-    void ma_b(void* target, Relocation::Kind reloc, Condition c = Always);
-
     // This is almost NEVER necessary, we'll basically never be calling a label
     // except, possibly in the crazy bailout-table case.
     void ma_bl(Label* dest, Condition c = Always);
 
     void ma_blx(Register dest, Condition c = Always);
 
     // VFP/ALU:
     void ma_vadd(FloatRegister src1, FloatRegister src2, FloatRegister dst);
--- a/js/src/jit/mips-shared/MoveEmitter-mips-shared.cpp
+++ b/js/src/jit/mips-shared/MoveEmitter-mips-shared.cpp
@@ -89,16 +89,52 @@ MoveEmitterMIPSShared::emitMove(const Mo
             MOZ_CRASH("Invalid emitMove arguments.");
         }
     } else {
         MOZ_CRASH("Invalid emitMove arguments.");
     }
 }
 
 void
+MoveEmitterMIPSShared::emitInt32Move(const MoveOperand &from, const MoveOperand &to)
+{
+    if (from.isGeneralReg()) {
+        // Second scratch register should not be moved by MoveEmitter.
+        MOZ_ASSERT(from.reg() != spilledReg_);
+
+        if (to.isGeneralReg())
+            masm.move32(from.reg(), to.reg());
+        else if (to.isMemory())
+            masm.store32(from.reg(), getAdjustedAddress(to));
+        else
+            MOZ_CRASH("Invalid emitInt32Move arguments.");
+    } else if (from.isMemory()) {
+        if (to.isGeneralReg()) {
+            masm.load32(getAdjustedAddress(from), to.reg());
+        } else if (to.isMemory()) {
+            masm.load32(getAdjustedAddress(from), tempReg());
+            masm.store32(tempReg(), getAdjustedAddress(to));
+        } else {
+            MOZ_CRASH("Invalid emitInt32Move arguments.");
+        }
+    } else if (from.isEffectiveAddress()) {
+        if (to.isGeneralReg()) {
+            masm.computeEffectiveAddress(getAdjustedAddress(from), to.reg());
+        } else if (to.isMemory()) {
+            masm.computeEffectiveAddress(getAdjustedAddress(from), tempReg());
+            masm.store32(tempReg(), getAdjustedAddress(to));
+        } else {
+            MOZ_CRASH("Invalid emitInt32Move arguments.");
+        }
+    } else {
+        MOZ_CRASH("Invalid emitInt32Move arguments.");
+    }
+}
+
+void
 MoveEmitterMIPSShared::emitFloat32Move(const MoveOperand& from, const MoveOperand& to)
 {
     // Ensure that we can use ScratchFloat32Reg in memory move.
     MOZ_ASSERT_IF(from.isFloatReg(), from.floatReg() != ScratchFloat32Reg);
     MOZ_ASSERT_IF(to.isFloatReg(), to.floatReg() != ScratchFloat32Reg);
 
     if (from.isFloatReg()) {
         if (to.isFloatReg()) {
@@ -157,17 +193,18 @@ MoveEmitterMIPSShared::emit(const MoveOp
     switch (move.type()) {
       case MoveOp::FLOAT32:
         emitFloat32Move(from, to);
         break;
       case MoveOp::DOUBLE:
         emitDoubleMove(from, to);
         break;
       case MoveOp::INT32:
-        MOZ_ASSERT(sizeof(uintptr_t) == sizeof(int32_t));
+        emitInt32Move(from, to);
+        break;
       case MoveOp::GENERAL:
         emitMove(from, to);
         break;
       default:
         MOZ_CRASH("Unexpected move type");
     }
 }
 
--- a/js/src/jit/mips-shared/MoveEmitter-mips-shared.h
+++ b/js/src/jit/mips-shared/MoveEmitter-mips-shared.h
@@ -37,16 +37,17 @@ class MoveEmitterMIPSShared
     void assertDone();
     Register tempReg();
     FloatRegister tempFloatReg();
     Address cycleSlot(uint32_t slot, uint32_t subslot) const;
     int32_t getAdjustedOffset(const MoveOperand& operand);
     Address getAdjustedAddress(const MoveOperand& operand);
 
     void emitMove(const MoveOperand& from, const MoveOperand& to);
+    void emitInt32Move(const MoveOperand& from, const MoveOperand& to);
     void emitFloat32Move(const MoveOperand& from, const MoveOperand& to);
     virtual void emitDoubleMove(const MoveOperand& from, const MoveOperand& to) = 0;
     virtual void breakCycle(const MoveOperand& from, const MoveOperand& to,
                     MoveOp::Type type, uint32_t slot) = 0;
     virtual void completeCycle(const MoveOperand& from, const MoveOperand& to,
                        MoveOp::Type type, uint32_t slot) = 0;
     void emit(const MoveOp& move);
 
--- a/js/src/jit/mips32/Architecture-mips32.h
+++ b/js/src/jit/mips32/Architecture-mips32.h
@@ -187,17 +187,17 @@ class FloatRegister : public FloatRegist
     FloatRegister asFloat32x4() const { MOZ_CRASH("NYI"); }
 
     Code code() const {
         MOZ_ASSERT(!isInvalid());
         return Code(code_  | (kind_ << 5));
     }
     Encoding encoding() const {
         MOZ_ASSERT(!isInvalid());
-        return Code(code_  | (kind_ << 5));
+        return Encoding(code_);
     }
     uint32_t id() const {
         return code_;
     }
     static FloatRegister FromCode(uint32_t i) {
         uint32_t code = i & 31;
         uint32_t kind = i >> 5;
         return FloatRegister(code, RegType(kind));
new file mode 100644
--- /dev/null
+++ b/js/src/jit/mips64/LIR-mips64.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef jit_mips64_LIR_mips64_h
+#define jit_mips64_LIR_mips64_h
+
+namespace js {
+namespace jit {
+
+class LUnbox : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(Unbox);
+
+    explicit LUnbox(const LAllocation& input) {
+        setOperand(0, input);
+    }
+
+    static const size_t Input = 0;
+
+    MUnbox* mir() const {
+        return mir_->toUnbox();
+    }
+    const char* extraName() const {
+        return StringFromMIRType(mir()->type());
+    }
+};
+
+class LUnboxFloatingPoint : public LUnbox
+{
+    MIRType type_;
+
+  public:
+    LIR_HEADER(UnboxFloatingPoint);
+
+    LUnboxFloatingPoint(const LAllocation& input, MIRType type)
+      : LUnbox(input),
+        type_(type)
+    { }
+
+    MIRType type() const {
+        return type_;
+    }
+};
+
+} // namespace jit
+} // namespace js
+
+#endif /* jit_mips64_LIR_mips64_h */
new file mode 100644
--- /dev/null
+++ b/js/src/jit/mips64/LOpcodes-mips64.h
@@ -0,0 +1,16 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef jit_mips64_LOpcodes_mips64_h__
+#define jit_mips64_LOpcodes_mips64_h__
+
+#include "jit/shared/LOpcodes-shared.h"
+
+#define LIR_CPU_OPCODE_LIST(_)  \
+    _(ModMaskI)                 \
+    _(UDivOrMod)
+
+#endif // jit_mips64_LOpcodes_mips64_h__
new file mode 100644
--- /dev/null
+++ b/js/src/jit/mips64/Lowering-mips64.cpp
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "jit/mips64/Lowering-mips64.h"
+
+#include "jit/mips64/Assembler-mips64.h"
+
+#include "jit/MIR.h"
+
+#include "jit/shared/Lowering-shared-inl.h"
+
+using namespace js;
+using namespace js::jit;
+
+void
+LIRGeneratorMIPS64::useBoxFixed(LInstruction* lir, size_t n, MDefinition* mir, Register reg1,
+                                Register reg2, bool useAtStart)
+{
+    MOZ_ASSERT(mir->type() == MIRType_Value);
+
+    ensureDefined(mir);
+    lir->setOperand(n, LUse(reg1, mir->virtualRegister(), useAtStart));
+}
+
+void
+LIRGeneratorMIPS64::visitBox(MBox* box)
+{
+    MDefinition* opd = box->getOperand(0);
+
+    // If the operand is a constant, emit near its uses.
+    if (opd->isConstant() && box->canEmitAtUses()) {
+        emitAtUses(box);
+        return;
+    }
+
+    if (opd->isConstant()) {
+        define(new(alloc()) LValue(opd->toConstant()->value()), box, LDefinition(LDefinition::BOX));
+    } else {
+        LBox* ins = new(alloc()) LBox(useRegister(opd), opd->type());
+        define(ins, box, LDefinition(LDefinition::BOX));
+    }
+}
+
+void
+LIRGeneratorMIPS64::visitUnbox(MUnbox* unbox)
+{
+    MDefinition* box = unbox->getOperand(0);
+
+    if (box->type() == MIRType_ObjectOrNull) {
+        LUnboxObjectOrNull* lir = new(alloc()) LUnboxObjectOrNull(useRegisterAtStart(box));
+        if (unbox->fallible())
+            assignSnapshot(lir, unbox->bailoutKind());
+        defineReuseInput(lir, unbox, 0);
+        return;
+    }
+
+    MOZ_ASSERT(box->type() == MIRType_Value);
+
+    LUnbox* lir;
+    if (IsFloatingPointType(unbox->type())) {
+        lir = new(alloc()) LUnboxFloatingPoint(useRegisterAtStart(box), unbox->type());
+    } else if (unbox->fallible()) {
+        // If the unbox is fallible, load the Value in a register first to
+        // avoid multiple loads.
+        lir = new(alloc()) LUnbox(useRegisterAtStart(box));
+    } else {
+        lir = new(alloc()) LUnbox(useAtStart(box));
+    }
+
+    if (unbox->fallible())
+        assignSnapshot(lir, unbox->bailoutKind());
+
+    define(lir, unbox);
+}
+
+void
+LIRGeneratorMIPS64::visitReturn(MReturn* ret)
+{
+    MDefinition* opd = ret->getOperand(0);
+    MOZ_ASSERT(opd->type() == MIRType_Value);
+
+    LReturn* ins = new(alloc()) LReturn;
+    ins->setOperand(0, useFixed(opd, JSReturnReg));
+    add(ins);
+}
+
+void
+LIRGeneratorMIPS64::defineUntypedPhi(MPhi* phi, size_t lirIndex)
+{
+    defineTypedPhi(phi, lirIndex);
+}
+
+void
+LIRGeneratorMIPS64::lowerUntypedPhiInput(MPhi* phi, uint32_t inputPosition,
+                                         LBlock* block, size_t lirIndex)
+{
+    lowerTypedPhiInput(phi, inputPosition, block, lirIndex);
+}
+
+void
+LIRGeneratorMIPS64::lowerTruncateDToInt32(MTruncateToInt32* ins)
+{
+    MDefinition* opd = ins->input();
+    MOZ_ASSERT(opd->type() == MIRType_Double);
+
+    define(new(alloc())
+           LTruncateDToInt32(useRegister(opd), tempDouble()), ins);
+}
+
+void
+LIRGeneratorMIPS64::lowerTruncateFToInt32(MTruncateToInt32* ins)
+{
+    MDefinition* opd = ins->input();
+    MOZ_ASSERT(opd->type() == MIRType_Float32);
+
+    define(new(alloc())
+           LTruncateFToInt32(useRegister(opd), tempFloat32()), ins);
+}
+
+void
+LIRGeneratorMIPS64::visitRandom(MRandom* ins)
+{
+    LRandom *lir = new(alloc()) LRandom(temp(), temp(), temp());
+    defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit/mips64/Lowering-mips64.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef jit_mips64_Lowering_mips64_h
+#define jit_mips64_Lowering_mips64_h
+
+#include "jit/mips-shared/Lowering-mips-shared.h"
+
+namespace js {
+namespace jit {
+
+class LIRGeneratorMIPS64 : public LIRGeneratorMIPSShared
+{
+  protected:
+    LIRGeneratorMIPS64(MIRGenerator* gen, MIRGraph& graph, LIRGraph& lirGraph)
+      : LIRGeneratorMIPSShared(gen, graph, lirGraph)
+    { }
+
+  protected:
+    // Adds a box input to an instruction, setting operand |n| to the type and
+    // |n+1| to the payload.
+    void useBoxFixed(LInstruction* lir, size_t n, MDefinition* mir, Register reg1, Register reg2,
+                     bool useAtStart = false);
+
+    inline LDefinition tempToUnbox() {
+        return temp();
+    }
+
+    void lowerUntypedPhiInput(MPhi* phi, uint32_t inputPosition, LBlock* block, size_t lirIndex);
+    void defineUntypedPhi(MPhi* phi, size_t lirIndex);
+
+    void lowerTruncateDToInt32(MTruncateToInt32* ins);
+    void lowerTruncateFToInt32(MTruncateToInt32* ins);
+
+  public:
+    void visitBox(MBox* box);
+    void visitUnbox(MUnbox* unbox);
+    void visitReturn(MReturn* ret);
+    void visitRandom(MRandom* ins);
+};
+
+typedef LIRGeneratorMIPS64 LIRGeneratorSpecific;
+
+} // namespace jit
+} // namespace js
+
+#endif /* jit_mips64_Lowering_mips64_h */
new file mode 100644
--- /dev/null
+++ b/js/src/jit/mips64/SharedIC-mips64.cpp
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "jsiter.h"
+
+#include "jit/BaselineCompiler.h"
+#include "jit/BaselineIC.h"
+#include "jit/BaselineJIT.h"
+#include "jit/Linker.h"
+#include "jit/SharedICHelpers.h"
+
+#include "jsboolinlines.h"
+
+using namespace js;
+using namespace js::jit;
+
+namespace js {
+namespace jit {
+
+// ICBinaryArith_Int32
+
+bool
+ICBinaryArith_Int32::Compiler::generateStubCode(MacroAssembler& masm)
+{
+    // Guard that R0 is an integer and R1 is an integer.
+    Label failure;
+    masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
+    masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
+
+    // Add R0 and R1. Don't need to explicitly unbox, just use R2's valueReg.
+    Register scratchReg = R2.valueReg();
+
+    Label goodMul, divTest1, divTest2;
+    switch(op_) {
+      case JSOP_ADD:
+        masm.unboxInt32(R0, ExtractTemp0);
+        masm.unboxInt32(R1, ExtractTemp1);
+        masm.ma_addTestOverflow(scratchReg, ExtractTemp0, ExtractTemp1, &failure);
+        masm.boxValue(JSVAL_TYPE_INT32, scratchReg, R0.valueReg());
+        break;
+      case JSOP_SUB:
+        masm.unboxInt32(R0, ExtractTemp0);
+        masm.unboxInt32(R1, ExtractTemp1);
+        masm.ma_subTestOverflow(scratchReg, ExtractTemp0, ExtractTemp1, &failure);
+        masm.boxValue(JSVAL_TYPE_INT32, scratchReg, R0.valueReg());
+        break;
+      case JSOP_MUL: {
+        masm.unboxInt32(R0, ExtractTemp0);
+        masm.unboxInt32(R1, ExtractTemp1);
+        masm.ma_mul_branch_overflow(scratchReg, ExtractTemp0, ExtractTemp1, &failure);
+
+        masm.ma_b(scratchReg, Imm32(0), &goodMul, Assembler::NotEqual, ShortJump);
+
+        // Result is -0 if operands have different signs.
+        masm.as_xor(t8, ExtractTemp0, ExtractTemp1);
+        masm.ma_b(t8, Imm32(0), &failure, Assembler::LessThan, ShortJump);
+
+        masm.bind(&goodMul);
+        masm.boxValue(JSVAL_TYPE_INT32, scratchReg, R0.valueReg());
+        break;
+      }
+      case JSOP_DIV:
+      case JSOP_MOD: {
+        masm.unboxInt32(R0, ExtractTemp0);
+        masm.unboxInt32(R1, ExtractTemp1);
+        // Check for INT_MIN / -1, it results in a double.
+        masm.ma_b(ExtractTemp0, Imm32(INT_MIN), &divTest1, Assembler::NotEqual, ShortJump);
+        masm.ma_b(ExtractTemp1, Imm32(-1), &failure, Assembler::Equal, ShortJump);
+        masm.bind(&divTest1);
+
+        // Check for division by zero
+        masm.ma_b(ExtractTemp1, Imm32(0), &failure, Assembler::Equal, ShortJump);
+
+        // Check for 0 / X with X < 0 (results in -0).
+        masm.ma_b(ExtractTemp0, Imm32(0), &divTest2, Assembler::NotEqual, ShortJump);
+        masm.ma_b(ExtractTemp1, Imm32(0), &failure, Assembler::LessThan, ShortJump);
+        masm.bind(&divTest2);
+
+        masm.as_div(ExtractTemp0, ExtractTemp1);
+
+        if (op_ == JSOP_DIV) {
+            // Result is a double if the remainder != 0.
+            masm.as_mfhi(scratchReg);
+            masm.ma_b(scratchReg, Imm32(0), &failure, Assembler::NotEqual, ShortJump);
+            masm.as_mflo(scratchReg);
+            masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
+        } else {
+            Label done;
+            // If X % Y == 0 and X < 0, the result is -0.
+            masm.as_mfhi(scratchReg);
+            masm.ma_b(scratchReg, Imm32(0), &done, Assembler::NotEqual, ShortJump);
+            masm.ma_b(ExtractTemp0, Imm32(0), &failure, Assembler::LessThan, ShortJump);
+            masm.bind(&done);
+            masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
+        }
+        break;
+      }
+      case JSOP_BITOR:
+        masm.as_or(R0.valueReg() , R0.valueReg(), R1.valueReg());
+        break;
+      case JSOP_BITXOR:
+        masm.as_xor(scratchReg, R0.valueReg(), R1.valueReg());
+        masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
+        break;
+      case JSOP_BITAND:
+        masm.as_and(R0.valueReg() , R0.valueReg(), R1.valueReg());
+        break;
+      case JSOP_LSH:
+        masm.unboxInt32(R0, ExtractTemp0);
+        masm.unboxInt32(R1, ExtractTemp1);
+        // MIPS will only use 5 lowest bits in R1 as shift offset.
+        masm.ma_sll(scratchReg, ExtractTemp0, ExtractTemp1);
+        masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
+        break;
+      case JSOP_RSH:
+        masm.unboxInt32(R0, ExtractTemp0);
+        masm.unboxInt32(R1, ExtractTemp1);
+        masm.ma_sra(scratchReg, ExtractTemp0, ExtractTemp1);
+        masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
+        break;
+      case JSOP_URSH:
+        masm.unboxInt32(R0, ExtractTemp0);
+        masm.unboxInt32(R1, ExtractTemp1);
+        masm.ma_srl(scratchReg, ExtractTemp0, ExtractTemp1);
+        if (allowDouble_) {
+            Label toUint;
+            masm.ma_b(scratchReg, Imm32(0), &toUint, Assembler::LessThan, ShortJump);
+
+            // Move result and box for return.
+            masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
+            EmitReturnFromIC(masm);
+
+            masm.bind(&toUint);
+            masm.convertUInt32ToDouble(scratchReg, FloatReg1);
+            masm.boxDouble(FloatReg1, R0);
+        } else {
+            masm.ma_b(scratchReg, Imm32(0), &failure, Assembler::LessThan, ShortJump);
+            // Move result for return.
+            masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
+        }
+        break;
+      default:
+        MOZ_CRASH("Unhandled op for BinaryArith_Int32.");
+    }
+
+    EmitReturnFromIC(masm);
+
+    // Failure case - jump to next stub
+    masm.bind(&failure);
+    EmitStubGuardFailure(masm);
+
+    return true;
+}
+
+bool
+ICUnaryArith_Int32::Compiler::generateStubCode(MacroAssembler& masm)
+{
+    Label failure;
+    masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
+
+    switch (op) {
+      case JSOP_BITNOT:
+        masm.not32(R0.valueReg());
+        masm.tagValue(JSVAL_TYPE_INT32, R0.valueReg(), R0);
+        break;
+      case JSOP_NEG:
+        masm.unboxInt32(R0, ExtractTemp0);
+        // Guard against 0 and MIN_INT, both result in a double.
+        masm.branchTest32(Assembler::Zero, ExtractTemp0, Imm32(INT32_MAX), &failure);
+
+        masm.neg32(ExtractTemp0);
+        masm.tagValue(JSVAL_TYPE_INT32, ExtractTemp0, R0);
+        break;
+      default:
+        MOZ_CRASH("Unexpected op");
+        return false;
+    }
+
+    EmitReturnFromIC(masm);
+
+    masm.bind(&failure);
+    EmitStubGuardFailure(masm);
+    return true;
+}
+
+
+} // namespace jit
+} // namespace js
new file mode 100644
--- /dev/null
+++ b/js/src/jit/mips64/SharedICHelpers-mips64.h
@@ -0,0 +1,350 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef jit_mips64_SharedICHelpers_mips64_h
+#define jit_mips64_SharedICHelpers_mips64_h
+
+#include "jit/BaselineFrame.h"
+#include "jit/BaselineIC.h"
+#include "jit/MacroAssembler.h"
+#include "jit/SharedICRegisters.h"
+
+namespace js {
+namespace jit {
+
+// Distance from sp to the top Value inside an IC stub (no return address on
+// the stack on MIPS).
+static const size_t ICStackValueOffset = 0;
+
+inline void
+EmitRestoreTailCallReg(MacroAssembler& masm)
+{
+    // No-op on MIPS because ra register is always holding the return address.
+}
+
+inline void
+EmitRepushTailCallReg(MacroAssembler& masm)
+{
+    // No-op on MIPS because ra register is always holding the return address.
+}
+
+inline void
+EmitCallIC(CodeOffsetLabel* patchOffset, MacroAssembler& masm)
+{
+    // Move ICEntry offset into ICStubReg.
+    CodeOffsetLabel offset = masm.movWithPatch(ImmWord(-1), ICStubReg);
+    *patchOffset = offset;
+
+    // Load stub pointer into ICStubReg.
+    masm.loadPtr(Address(ICStubReg, ICEntry::offsetOfFirstStub()), ICStubReg);
+
+    // Load stubcode pointer from BaselineStubEntry.
+    // R2 won't be active when we call ICs, so we can use it as scratch.
+    masm.loadPtr(Address(ICStubReg, ICStub::offsetOfStubCode()), R2.scratchReg());
+
+    // Call the stubcode via a direct jump-and-link
+    masm.call(R2.scratchReg());
+}
+
+inline void
+EmitEnterTypeMonitorIC(MacroAssembler& masm,
+                       size_t monitorStubOffset = ICMonitoredStub::offsetOfFirstMonitorStub())
+{
+    // This is expected to be called from within an IC, when ICStubReg
+    // is properly initialized to point to the stub.
+    masm.loadPtr(Address(ICStubReg, (uint32_t) monitorStubOffset), ICStubReg);
+
+    // Load stubcode pointer from BaselineStubEntry.
+    // R2 won't be active when we call ICs, so we can use it.
+    masm.loadPtr(Address(ICStubReg, ICStub::offsetOfStubCode()), R2.scratchReg());
+
+    // Jump to the stubcode.
+    masm.branch(R2.scratchReg());
+}
+
+inline void
+EmitReturnFromIC(MacroAssembler& masm)
+{
+    masm.branch(ra);
+}
+
+inline void
+EmitChangeICReturnAddress(MacroAssembler& masm, Register reg)
+{
+    masm.movePtr(reg, ra);
+}
+
+inline void
+EmitBaselineTailCallVM(JitCode* target, MacroAssembler& masm, uint32_t argSize)
+{
+    // We assume during this that R0 and R1 have been pushed, and that R2 is
+    // unused.
+    MOZ_ASSERT(R2 == ValueOperand(a6));
+
+    // Compute frame size.
+    masm.movePtr(BaselineFrameReg, a6);
+    masm.addPtr(Imm32(BaselineFrame::FramePointerOffset), a6);
+    masm.subPtr(BaselineStackReg, a6);
+
+    // Store frame size without VMFunction arguments for GC marking.
+    masm.ma_dsubu(a7, a6, Imm32(argSize));
+    masm.store32(a7, Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFrameSize()));
+
+    // Push frame descriptor and perform the tail call.
+    // ICTailCallReg (ra) already contains the return address (as we
+    // keep it there through the stub calls), but the VMWrapper code being
+    // called expects the return address to also be pushed on the stack.
+    MOZ_ASSERT(ICTailCallReg == ra);
+    masm.makeFrameDescriptor(a6, JitFrame_BaselineJS);
+    masm.subPtr(Imm32(sizeof(CommonFrameLayout)), StackPointer);
+    masm.storePtr(a6, Address(StackPointer, CommonFrameLayout::offsetOfDescriptor()));
+    masm.storePtr(ra, Address(StackPointer, CommonFrameLayout::offsetOfReturnAddress()));
+
+    masm.branch(target);
+}
+
+inline void
+EmitIonTailCallVM(JitCode* target, MacroAssembler& masm, uint32_t stackSize)
+{
+    MOZ_CRASH("Not implemented yet.");
+}
+
+inline void
+EmitBaselineCreateStubFrameDescriptor(MacroAssembler& masm, Register reg)
+{
+    // Compute stub frame size. We have to add two pointers: the stub reg and
+    // previous frame pointer pushed by EmitEnterStubFrame.
+    masm.movePtr(BaselineFrameReg, reg);
+    masm.addPtr(Imm32(sizeof(intptr_t) * 2), reg);
+    masm.subPtr(BaselineStackReg, reg);
+
+    masm.makeFrameDescriptor(reg, JitFrame_BaselineStub);
+}
+
+inline void
+EmitBaselineCallVM(JitCode* target, MacroAssembler& masm)
+{
+    EmitBaselineCreateStubFrameDescriptor(masm, a6);
+    masm.push(a6);
+    masm.call(target);
+}
+
+inline void
+EmitIonCallVM(JitCode* target, size_t stackSlots, MacroAssembler& masm)
+{
+    MOZ_CRASH("Not implemented yet.");
+}
+
+struct BaselineStubFrame {
+    uintptr_t savedFrame;
+    uintptr_t savedStub;
+    uintptr_t returnAddress;
+    uintptr_t descriptor;
+};
+
+static const uint32_t STUB_FRAME_SIZE = sizeof(BaselineStubFrame);
+static const uint32_t STUB_FRAME_SAVED_STUB_OFFSET = offsetof(BaselineStubFrame, savedStub);
+
+inline void
+EmitBaselineEnterStubFrame(MacroAssembler& masm, Register scratch)
+{
+    MOZ_ASSERT(scratch != ICTailCallReg);
+
+    // Compute frame size.
+    masm.movePtr(BaselineFrameReg, scratch);
+    masm.addPtr(Imm32(BaselineFrame::FramePointerOffset), scratch);
+    masm.subPtr(BaselineStackReg, scratch);
+
+    masm.store32(scratch, Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFrameSize()));
+
+    // Note: when making changes here, don't forget to update
+    // BaselineStubFrame if needed.
+
+    // Push frame descriptor and return address.
+    masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS);
+    masm.subPtr(Imm32(STUB_FRAME_SIZE), StackPointer);
+    masm.storePtr(scratch, Address(StackPointer, offsetof(BaselineStubFrame, descriptor)));
+    masm.storePtr(ICTailCallReg, Address(StackPointer,
+                                               offsetof(BaselineStubFrame, returnAddress)));
+
+    // Save old frame pointer, stack pointer and stub reg.
+    masm.storePtr(ICStubReg, Address(StackPointer,
+                                           offsetof(BaselineStubFrame, savedStub)));
+    masm.storePtr(BaselineFrameReg, Address(StackPointer,
+                                            offsetof(BaselineStubFrame, savedFrame)));
+    masm.movePtr(BaselineStackReg, BaselineFrameReg);
+}
+
+inline void
+EmitIonEnterStubFrame(MacroAssembler& masm, Register scratch)
+{
+    MOZ_CRASH("Not implemented yet.");
+}
+
+inline void
+EmitBaselineLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false)
+{
+    // Ion frames do not save and restore the frame pointer. If we called
+    // into Ion, we have to restore the stack pointer from the frame descriptor.
+    // If we performed a VM call, the descriptor has been popped already so
+    // in that case we use the frame pointer.
+    if (calledIntoIon) {
+        masm.pop(ScratchRegister);
+        masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), ScratchRegister);
+        masm.addPtr(ScratchRegister, BaselineStackReg);
+    } else {
+        masm.movePtr(BaselineFrameReg, BaselineStackReg);
+    }
+
+    masm.loadPtr(Address(StackPointer, offsetof(BaselineStubFrame, savedFrame)),
+                 BaselineFrameReg);
+    masm.loadPtr(Address(StackPointer, offsetof(BaselineStubFrame, savedStub)),
+                 ICStubReg);
+
+    // Load the return address.
+    masm.loadPtr(Address(StackPointer, offsetof(BaselineStubFrame, returnAddress)),
+                 ICTailCallReg);
+
+    // Discard the frame descriptor.
+    masm.loadPtr(Address(StackPointer, offsetof(BaselineStubFrame, descriptor)), ScratchRegister);
+    masm.addPtr(Imm32(STUB_FRAME_SIZE), StackPointer);
+}
+
+inline void
+EmitIonLeaveStubFrame(MacroAssembler& masm)
+{
+    MOZ_CRASH("Not implemented yet.");
+}
+
+inline void
+EmitStowICValues(MacroAssembler& masm, int values)
+{
+    MOZ_ASSERT(values >= 0 && values <= 2);
+    switch(values) {
+      case 1:
+        // Stow R0
+        masm.pushValue(R0);
+        break;
+      case 2:
+        // Stow R0 and R1
+        masm.pushValue(R0);
+        masm.pushValue(R1);
+        break;
+    }
+}
+
+inline void
+EmitUnstowICValues(MacroAssembler& masm, int values, bool discard = false)
+{
+    MOZ_ASSERT(values >= 0 && values <= 2);
+    switch(values) {
+      case 1:
+        // Unstow R0.
+        if (discard)
+            masm.addPtr(Imm32(sizeof(Value)), BaselineStackReg);
+        else
+            masm.popValue(R0);
+        break;
+      case 2:
+        // Unstow R0 and R1.
+        if (discard) {
+            masm.addPtr(Imm32(sizeof(Value) * 2), BaselineStackReg);
+        } else {
+            masm.popValue(R1);
+            masm.popValue(R0);
+        }
+        break;
+    }
+}
+
+inline void
+EmitCallTypeUpdateIC(MacroAssembler& masm, JitCode* code, uint32_t objectOffset)
+{
+    // R0 contains the value that needs to be typechecked.
+    // The object we're updating is a boxed Value on the stack, at offset
+    // objectOffset from $sp, excluding the return address.
+
+    // Save the current ICStubReg to stack, as well as the TailCallReg,
+    // since on mips, the $ra is live.
+    masm.subPtr(Imm32(2 * sizeof(intptr_t)), StackPointer);
+    masm.storePtr(ICStubReg, Address(StackPointer, sizeof(intptr_t)));
+    masm.storePtr(ICTailCallReg, Address(StackPointer, 0));
+
+    // This is expected to be called from within an IC, when ICStubReg
+    // is properly initialized to point to the stub.
+    masm.loadPtr(Address(ICStubReg, ICUpdatedStub::offsetOfFirstUpdateStub()),
+                 ICStubReg);
+
+    // Load stubcode pointer from ICStubReg into ICTailCallReg.
+    masm.loadPtr(Address(ICStubReg, ICStub::offsetOfStubCode()), R2.scratchReg());
+
+    // Call the stubcode.
+    masm.call(R2.scratchReg());
+
+    // Restore the old stub reg and tailcall reg.
+    masm.loadPtr(Address(StackPointer, 0), ICTailCallReg);
+    masm.loadPtr(Address(StackPointer, sizeof(intptr_t)), ICStubReg);
+    masm.addPtr(Imm32(2 * sizeof(intptr_t)), StackPointer);
+
+    // The update IC will store 0 or 1 in R1.scratchReg() reflecting if the
+    // value in R0 type-checked properly or not.
+    Label success;
+    masm.ma_b(R1.scratchReg(), Imm32(1), &success, Assembler::Equal, ShortJump);
+
+    // If the IC failed, then call the update fallback function.
+    EmitBaselineEnterStubFrame(masm, R1.scratchReg());
+
+    masm.loadValue(Address(BaselineStackReg, STUB_FRAME_SIZE + objectOffset), R1);
+
+    masm.pushValue(R0);
+    masm.pushValue(R1);
+    masm.push(ICStubReg);
+
+    // Load previous frame pointer, push BaselineFrame*.
+    masm.loadPtr(Address(BaselineFrameReg, 0), R0.scratchReg());
+    masm.pushBaselineFramePtr(R0.scratchReg(), R0.scratchReg());
+
+    EmitBaselineCallVM(code, masm);
+    EmitBaselineLeaveStubFrame(masm);
+
+    // Success at end.
+    masm.bind(&success);
+}
+
+template <typename AddrType>
+inline void
+EmitPreBarrier(MacroAssembler& masm, const AddrType& addr, MIRType type)
+{
+    // On MIPS, $ra is clobbered by patchableCallPreBarrier. Save it first.
+    masm.push(ra);
+    masm.patchableCallPreBarrier(addr, type);
+    masm.pop(ra);
+}
+
+inline void
+EmitStubGuardFailure(MacroAssembler& masm)
+{
+    // NOTE: This routine assumes that the stub guard code left the stack in
+    // the same state it was in when it was entered.
+
+    // BaselineStubEntry points to the current stub.
+
+    // Load next stub into ICStubReg
+    masm.loadPtr(Address(ICStubReg, ICStub::offsetOfNext()), ICStubReg);
+
+    // Load stubcode pointer from BaselineStubEntry into scratch register.
+    masm.loadPtr(Address(ICStubReg, ICStub::offsetOfStubCode()), R2.scratchReg());
+
+    // Return address is already loaded, just jump to the next stubcode.
+    MOZ_ASSERT(ICTailCallReg == ra);
+    masm.branch(R2.scratchReg());
+}
+
+
+} // namespace jit
+} // namespace js
+
+#endif /* jit_mips64_SharedICHelpers_mips64_h */
new file mode 100644
--- /dev/null
+++ b/js/src/jit/mips64/SharedICRegisters-mips64.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef jit_mips64_SharedICRegisters_mips64_h
+#define jit_mips64_SharedICRegisters_mips64_h
+
+#include "jit/MacroAssembler.h"
+
+namespace js {
+namespace jit {
+
+static MOZ_CONSTEXPR_VAR Register BaselineFrameReg = s5;
+static MOZ_CONSTEXPR_VAR Register BaselineStackReg = sp;
+
+// ValueOperands R0, R1, and R2.
+// R0 == JSReturnReg, and R2 uses registers not preserved across calls. R1 value
+// should be preserved across calls.
+static MOZ_CONSTEXPR_VAR ValueOperand R0(v1);
+static MOZ_CONSTEXPR_VAR ValueOperand R1(s4);
+static MOZ_CONSTEXPR_VAR ValueOperand R2(a6);
+
+// ICTailCallReg and ICStubReg
+// These use registers that are not preserved across calls.
+static MOZ_CONSTEXPR_VAR Register ICTailCallReg = ra;
+static MOZ_CONSTEXPR_VAR Register ICStubReg = a5;
+
+static MOZ_CONSTEXPR_VAR Register ExtractTemp0 = s6;
+static MOZ_CONSTEXPR_VAR Register ExtractTemp1 = s7;
+
+// Register used internally by MacroAssemblerMIPS.
+static MOZ_CONSTEXPR_VAR Register BaselineSecondScratchReg = SecondScratchReg;
+
+// Note that ICTailCallReg is actually just the link register.
+// In MIPS code emission, we do not clobber ICTailCallReg since we keep
+// the return address for calls there.
+
+// FloatReg0 must be equal to ReturnFloatReg.
+static MOZ_CONSTEXPR_VAR FloatRegister FloatReg0 = f0;
+static MOZ_CONSTEXPR_VAR FloatRegister FloatReg1 = f2;
+
+} // namespace jit
+} // namespace js
+
+#endif /* jit_mips64_SharedICRegisters_mips64_h */
new file mode 100644
--- /dev/null
+++ b/js/src/jit/mips64/Trampoline-mips64.cpp
@@ -0,0 +1,1319 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/DebugOnly.h"
+
+#include "jscompartment.h"
+
+#include "jit/Bailouts.h"
+#include "jit/JitCompartment.h"
+#include "jit/JitFrames.h"
+#include "jit/JitSpewer.h"
+#include "jit/Linker.h"
+#include "jit/mips-shared/Bailouts-mips-shared.h"
+#include "jit/mips64/SharedICHelpers-mips64.h"
+#ifdef JS_ION_PERF
+# include "jit/PerfSpewer.h"
+#endif
+#include "jit/VMFunctions.h"
+
+#include "jit/MacroAssembler-inl.h"
+
+using namespace js;
+using namespace js::jit;
+
+// All registers to save and restore. This includes the stack pointer, since we
+// use the ability to reference register values on the stack by index.
+static const LiveRegisterSet AllRegs =
+    LiveRegisterSet(GeneralRegisterSet(Registers::AllMask),
+                         FloatRegisterSet(FloatRegisters::AllMask));
+
+static_assert(sizeof(uintptr_t) == sizeof(uint64_t), "Not 32-bit clean.");
+
+struct EnterJITRegs
+{
+    double f31;
+    double f30;
+    double f29;
+    double f28;
+    double f27;
+    double f26;
+    double f25;
+    double f24;
+
+    // non-volatile registers.
+    uint64_t ra;
+    uint64_t s7;
+    uint64_t s6;
+    uint64_t s5;
+    uint64_t s4;
+    uint64_t s3;
+    uint64_t s2;
+    uint64_t s1;
+    uint64_t s0;
+    // Save reg_vp(a7) on stack, use it after call jit code.
+    uint64_t a7;
+};
+
+static void
+GenerateReturn(MacroAssembler& masm, int returnCode)
+{
+    MOZ_ASSERT(masm.framePushed() == sizeof(EnterJITRegs));
+
+    // Restore non-volatile registers
+    masm.as_ld(s0, StackPointer, offsetof(EnterJITRegs, s0));
+    masm.as_ld(s1, StackPointer, offsetof(EnterJITRegs, s1));
+    masm.as_ld(s2, StackPointer, offsetof(EnterJITRegs, s2));
+    masm.as_ld(s3, StackPointer, offsetof(EnterJITRegs, s3));
+    masm.as_ld(s4, StackPointer, offsetof(EnterJITRegs, s4));
+    masm.as_ld(s5, StackPointer, offsetof(EnterJITRegs, s5));
+    masm.as_ld(s6, StackPointer, offsetof(EnterJITRegs, s6));
+    masm.as_ld(s7, StackPointer, offsetof(EnterJITRegs, s7));
+    masm.as_ld(ra, StackPointer, offsetof(EnterJITRegs, ra));
+
+    // Restore non-volatile floating point registers
+    masm.as_ld(f24, StackPointer, offsetof(EnterJITRegs, f24));
+    masm.as_ld(f25, StackPointer, offsetof(EnterJITRegs, f25));
+    masm.as_ld(f26, StackPointer, offsetof(EnterJITRegs, f26));
+    masm.as_ld(f27, StackPointer, offsetof(EnterJITRegs, f27));
+    masm.as_ld(f28, StackPointer, offsetof(EnterJITRegs, f28));
+    masm.as_ld(f29, StackPointer, offsetof(EnterJITRegs, f29));
+    masm.as_ld(f30, StackPointer, offsetof(EnterJITRegs, f30));
+    masm.as_ld(f31, StackPointer, offsetof(EnterJITRegs, f31));
+
+    masm.freeStack(sizeof(EnterJITRegs));
+
+    masm.branch(ra);
+}
+
+static void
+GeneratePrologue(MacroAssembler& masm)
+{
+    masm.reserveStack(sizeof(EnterJITRegs));
+
+    masm.as_sd(s0, StackPointer, offsetof(EnterJITRegs, s0));
+    masm.as_sd(s1, StackPointer, offsetof(EnterJITRegs, s1));
+    masm.as_sd(s2, StackPointer, offsetof(EnterJITRegs, s2));
+    masm.as_sd(s3, StackPointer, offsetof(EnterJITRegs, s3));
+    masm.as_sd(s4, StackPointer, offsetof(EnterJITRegs, s4));
+    masm.as_sd(s5, StackPointer, offsetof(EnterJITRegs, s5));
+    masm.as_sd(s6, StackPointer, offsetof(EnterJITRegs, s6));
+    masm.as_sd(s7, StackPointer, offsetof(EnterJITRegs, s7));
+    masm.as_sd(ra, StackPointer, offsetof(EnterJITRegs, ra));
+    masm.as_sd(a7, StackPointer, offsetof(EnterJITRegs, a7));
+
+    masm.as_sd(f24, StackPointer, offsetof(EnterJITRegs, f24));
+    masm.as_sd(f25, StackPointer, offsetof(EnterJITRegs, f25));
+    masm.as_sd(f26, StackPointer, offsetof(EnterJITRegs, f26));
+    masm.as_sd(f27, StackPointer, offsetof(EnterJITRegs, f27));
+    masm.as_sd(f28, StackPointer, offsetof(EnterJITRegs, f28));
+    masm.as_sd(f29, StackPointer, offsetof(EnterJITRegs, f29));
+    masm.as_sd(f30, StackPointer, offsetof(EnterJITRegs, f30));
+    masm.as_sd(f31, StackPointer, offsetof(EnterJITRegs, f31));
+}
+
+
+// Generates a trampoline for calling Jit compiled code from a C++ function.
+// The trampoline use the EnterJitCode signature, with the standard x64 fastcall
+// calling convention.
+JitCode *
+JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type)
+{
+    const Register reg_code = IntArgReg0;
+    const Register reg_argc = IntArgReg1;
+    const Register reg_argv = IntArgReg2;
+    const mozilla::DebugOnly<Register> reg_frame = IntArgReg3;
+    const Register reg_token = IntArgReg4;
+    const Register reg_chain = IntArgReg5;
+    const Register reg_values = IntArgReg6;
+    const Register reg_vp = IntArgReg7;
+    MacroAssembler masm(cx);
+
+    MOZ_ASSERT(OsrFrameReg == reg_frame);
+
+    GeneratePrologue(masm);
+
+    // Save stack pointer into s4
+    masm.movePtr(StackPointer, s4);
+
+    // Save stack pointer as baseline frame.
+    if (type == EnterJitBaseline)
+        masm.movePtr(StackPointer, BaselineFrameReg);
+
+    // Load the number of actual arguments into s3.
+    masm.unboxInt32(Address(reg_vp, 0), s3);
+
+    /***************************************************************
+    Loop over argv vector, push arguments onto stack in reverse order
+    ***************************************************************/
+
+    // if we are constructing, that also needs to include newTarget
+    {
+        Label noNewTarget;
+        masm.branchTest32(Assembler::Zero, reg_token, Imm32(CalleeToken_FunctionConstructing),
+                          &noNewTarget);
+
+        masm.add32(Imm32(1), reg_argc);
+
+        masm.bind(&noNewTarget);
+    }
+
+    // Make stack algined
+    masm.ma_and(s0, reg_argc, Imm32(1));
+    masm.ma_dsubu(s1, StackPointer, Imm32(sizeof(Value)));
+    masm.as_movn(StackPointer, s1, s0);
+
+    masm.as_dsll(s0, reg_argc, 3); // Value* argv
+    masm.addPtr(reg_argv, s0); // s0 = &argv[argc]
+
+    // Loop over arguments, copying them from an unknown buffer onto the Ion
+    // stack so they can be accessed from JIT'ed code.
+    Label header, footer;
+    // If there aren't any arguments, don't do anything
+    masm.ma_b(s0, reg_argv, &footer, Assembler::BelowOrEqual, ShortJump);
+    {
+        masm.bind(&header);
+
+        masm.subPtr(Imm32(sizeof(Value)), s0);
+        masm.subPtr(Imm32(sizeof(Value)), StackPointer);
+
+        ValueOperand value = ValueOperand(s6);
+        masm.loadValue(Address(s0, 0), value);
+        masm.storeValue(value, Address(StackPointer, 0));
+
+        masm.ma_b(s0, reg_argv, &header, Assembler::Above, ShortJump);
+    }
+    masm.bind(&footer);
+
+    masm.subPtr(Imm32(2 * sizeof(uintptr_t)), StackPointer);
+    masm.storePtr(s3, Address(StackPointer, sizeof(uintptr_t))); // actual arguments
+    masm.storePtr(reg_token, Address(StackPointer, 0)); // callee token
+
+    masm.subPtr(StackPointer, s4);
+    masm.makeFrameDescriptor(s4, JitFrame_Entry);
+    masm.push(s4); // descriptor
+
+    CodeLabel returnLabel;
+    if (type == EnterJitBaseline) {
+        // Handle OSR.
+        AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
+        regs.take(OsrFrameReg);
+        regs.take(BaselineFrameReg);
+        regs.take(reg_code);
+        regs.take(ReturnReg);
+        regs.take(JSReturnOperand);
+
+        Label notOsr;
+        masm.ma_b(OsrFrameReg, OsrFrameReg, &notOsr, Assembler::Zero, ShortJump);
+
+        Register numStackValues = reg_values;
+        regs.take(numStackValues);
+        Register scratch = regs.takeAny();
+
+        // Push return address.
+        masm.subPtr(Imm32(sizeof(uintptr_t)), StackPointer);
+        masm.ma_li(scratch, returnLabel.dest());
+        masm.storePtr(scratch, Address(StackPointer, 0));
+
+        // Push previous frame pointer.
+        masm.subPtr(Imm32(sizeof(uintptr_t)), StackPointer);
+        masm.storePtr(BaselineFrameReg, Address(StackPointer, 0));
+
+        // Reserve frame.
+        Register framePtr = BaselineFrameReg;
+        masm.subPtr(Imm32(BaselineFrame::Size()), StackPointer);
+        masm.movePtr(StackPointer, framePtr);
+
+        // Reserve space for locals and stack values.
+        masm.ma_dsll(scratch, numStackValues, Imm32(3));
+        masm.subPtr(scratch, StackPointer);
+
+        // Enter exit frame.
+        masm.addPtr(Imm32(BaselineFrame::Size() + BaselineFrame::FramePointerOffset), scratch);
+        masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS);
+
+        // Push frame descriptor and fake return address.
+        masm.reserveStack(2 * sizeof(uintptr_t));
+        masm.storePtr(scratch, Address(StackPointer, sizeof(uintptr_t))); // Frame descriptor
+        masm.storePtr(zero, Address(StackPointer, 0)); // fake return address
+
+        // No GC things to mark, push a bare token.
+        masm.enterFakeExitFrame(ExitFrameLayoutBareToken);
+
+        masm.reserveStack(2 * sizeof(uintptr_t));
+        masm.storePtr(framePtr, Address(StackPointer, sizeof(uintptr_t))); // BaselineFrame
+        masm.storePtr(reg_code, Address(StackPointer, 0)); // jitcode
+
+        masm.setupUnalignedABICall(scratch);
+        masm.passABIArg(BaselineFrameReg); // BaselineFrame
+        masm.passABIArg(OsrFrameReg); // InterpreterFrame
+        masm.passABIArg(numStackValues);
+        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, jit::InitBaselineFrameForOsr));
+
+        regs.add(OsrFrameReg);
+        Register jitcode = regs.takeAny();
+        masm.loadPtr(Address(StackPointer, 0), jitcode);
+        masm.loadPtr(Address(StackPointer, sizeof(uintptr_t)), framePtr);
+        masm.freeStack(2 * sizeof(uintptr_t));
+
+        Label error;
+        masm.freeStack(ExitFrameLayout::SizeWithFooter());
+        masm.addPtr(Imm32(BaselineFrame::Size()), framePtr);
+        masm.branchIfFalseBool(ReturnReg, &error);
+
+        // If OSR-ing, then emit instrumentation for setting lastProfilerFrame
+        // if profiler instrumentation is enabled.
+        {
+            Label skipProfilingInstrumentation;
+            Register realFramePtr = numStackValues;
+            AbsoluteAddress addressOfEnabled(cx->runtime()->spsProfiler.addressOfEnabled());
+            masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0),
+                          &skipProfilingInstrumentation);
+            masm.ma_daddu(realFramePtr, framePtr, Imm32(sizeof(void*)));
+            masm.profilerEnterFrame(realFramePtr, scratch);
+            masm.bind(&skipProfilingInstrumentation);
+        }
+
+        masm.jump(jitcode);
+
+        // OOM: load error value, discard return address and previous frame
+        // pointer and return.
+        masm.bind(&error);
+        masm.movePtr(framePtr, StackPointer);
+        masm.addPtr(Imm32(2 * sizeof(uintptr_t)), StackPointer);
+        masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
+        masm.ma_li(scratch, returnLabel.dest());
+        masm.jump(scratch);
+
+        masm.bind(&notOsr);
+        // Load the scope chain in R1.
+        MOZ_ASSERT(R1.scratchReg() != reg_code);
+        masm.ma_move(R1.scratchReg(), reg_chain);
+    }
+
+    // The call will push the return address on the stack, thus we check that
+    // the stack would be aligned once the call is complete.
+    masm.assertStackAlignment(JitStackAlignment, sizeof(uintptr_t));
+
+    // Call the function with pushing return address to stack.
+    masm.callJitNoProfiler(reg_code);
+
+    if (type == EnterJitBaseline) {
+        // Baseline OSR will return here.
+        masm.bind(returnLabel.src());
+        masm.addCodeLabel(returnLabel);
+    }
+
+    // Pop arguments off the stack.
+    // s0 <- 8*argc (size of all arguments we pushed on the stack)
+    masm.pop(s0);
+    masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), s0);
+    masm.addPtr(s0, StackPointer);
+
+    // Store the returned value into the vp
+    masm.as_ld(reg_vp, StackPointer, offsetof(EnterJITRegs, a7));
+    masm.storeValue(JSReturnOperand, Address(reg_vp, 0));
+
+    // Restore non-volatile registers and return.
+    GenerateReturn(masm, ShortJump);
+
+    Linker linker(masm);
+    AutoFlushICache afc("GenerateEnterJIT");
+    JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerJitCodeProfile(code, "EnterJIT");
+#endif
+
+    return code;
+}
+
+JitCode*
+JitRuntime::generateInvalidator(JSContext* cx)
+{
+    MacroAssembler masm(cx);
+
+    // Stack has to be alligned here. If not, we will have to fix it.
+    masm.checkStackAlignment();
+
+    // Push registers such that we can access them from [base + code].
+    masm.PushRegsInMask(AllRegs);
+
+    // Pass pointer to InvalidationBailoutStack structure.
+    masm.movePtr(StackPointer, a0);
+
+    // Reserve place for return value and BailoutInfo pointer
+    masm.subPtr(Imm32(2 * sizeof(uintptr_t)), StackPointer);
+    // Pass pointer to return value.
+    masm.ma_daddu(a1, StackPointer, Imm32(sizeof(uintptr_t)));
+    // Pass pointer to BailoutInfo
+    masm.movePtr(StackPointer, a2);
+
+    masm.setupAlignedABICall();
+    masm.passABIArg(a0);
+    masm.passABIArg(a1);
+    masm.passABIArg(a2);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, InvalidationBailout));
+
+    masm.loadPtr(Address(StackPointer, 0), a2);
+    masm.loadPtr(Address(StackPointer, sizeof(uintptr_t)), a1);
+    // Remove the return address, the IonScript, the register state
+    // (InvaliationBailoutStack) and the space that was allocated for the
+    // return value.
+    masm.addPtr(Imm32(sizeof(InvalidationBailoutStack) + 2 * sizeof(uintptr_t)), StackPointer);
+    // remove the space that this frame was using before the bailout
+    // (computed by InvalidationBailout)
+    masm.addPtr(a1, StackPointer);
+
+    // Jump to shared bailout tail. The BailoutInfo pointer has to be in r2.
+    JitCode* bailoutTail = cx->runtime()->jitRuntime()->getBailoutTail();
+    masm.branch(bailoutTail);
+
+    Linker linker(masm);
+    AutoFlushICache afc("Invalidator");
+    JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
+    JitSpew(JitSpew_IonInvalidate, "   invalidation thunk created at %p", (void*) code->raw());
+
+#ifdef JS_ION_PERF
+    writePerfSpewerJitCodeProfile(code, "Invalidator");
+#endif
+
+    return code;
+}
+
+JitCode*
+JitRuntime::generateArgumentsRectifier(JSContext* cx, void** returnAddrOut)
+{
+    // Do not erase the frame pointer in this function.
+
+    MacroAssembler masm(cx);
+    // Caller:
+    // [arg2] [arg1] [this] [[argc] [callee] [descr] [raddr]] <- sp
+    // '--- s3 ---'
+
+    // ArgumentsRectifierReg contains the |nargs| pushed onto the current
+    // frame. Including |this|, there are (|nargs| + 1) arguments to copy.
+    MOZ_ASSERT(ArgumentsRectifierReg == s3);
+
+    // Add |this|, in the counter of known arguments.
+    masm.addPtr(Imm32(1), ArgumentsRectifierReg);
+
+    Register numActArgsReg = a6;
+    Register calleeTokenReg = a7;
+    Register numArgsReg = a5;
+
+    // Load |nformals| into numArgsReg.
+    masm.loadPtr(Address(StackPointer, RectifierFrameLayout::offsetOfCalleeToken()),
+                 calleeTokenReg);
+    masm.mov(calleeTokenReg, numArgsReg);
+    masm.andPtr(Imm32(uint32_t(CalleeTokenMask)), numArgsReg);
+    masm.load16ZeroExtend(Address(numArgsReg, JSFunction::offsetOfNargs()), numArgsReg);
+
+    // Stash another copy in t3, since we are going to do destructive operations
+    // on numArgsReg
+    masm.mov(numArgsReg, t3);
+
+    static_assert(CalleeToken_FunctionConstructing == 1,
+      "Ensure that we can use the constructing bit to count the value");
+    masm.mov(calleeTokenReg, t2);
+    masm.ma_and(t2, Imm32(uint32_t(CalleeToken_FunctionConstructing)));
+
+    // Including |this|, and |new.target|, there are (|nformals| + 1 + isConstructing)
+    // arguments to push to the stack.  Then we push a JitFrameLayout.  We
+    // compute the padding expressed in the number of extra |undefined| values
+    // to push on the stack.
+    static_assert(sizeof(JitFrameLayout) % JitStackAlignment == 0,
+      "No need to consider the JitFrameLayout for aligning the stack");
+    static_assert(JitStackAlignment % sizeof(Value) == 0,
+      "Ensure that we can pad the stack by pushing extra UndefinedValue");
+
+    MOZ_ASSERT(IsPowerOfTwo(JitStackValueAlignment));
+    masm.add32(Imm32(JitStackValueAlignment - 1 /* for padding */ + 1 /* for |this| */), numArgsReg);
+    masm.add32(t2, numArgsReg);
+    masm.and32(Imm32(~(JitStackValueAlignment - 1)), numArgsReg);
+
+    // Load the number of |undefined|s to push into t1.
+    masm.as_dsubu(t1, numArgsReg, s3);
+
+    // Caller:
+    // [arg2] [arg1] [this] [[argc] [callee] [descr] [raddr]] <- sp <- t2
+    // '------ s3 -------'
+    //
+    // Rectifier frame:
+    // [undef] [undef] [undef] [arg2] [arg1] [this] [[argc] [callee] [descr] [raddr]]
+    // '-------- t1 ---------' '------- s3 -------'
+
+    // Copy number of actual arguments into numActArgsReg
+    masm.loadPtr(Address(StackPointer, RectifierFrameLayout::offsetOfNumActualArgs()),
+                 numActArgsReg);
+
+
+    masm.moveValue(UndefinedValue(), ValueOperand(t0));
+
+    masm.movePtr(StackPointer, t2); // Save %sp.
+
+    // Push undefined. (including the padding)
+    {
+        Label undefLoopTop;
+
+        masm.bind(&undefLoopTop);
+        masm.sub32(Imm32(1), t1);
+        masm.subPtr(Imm32(sizeof(Value)), StackPointer);
+        masm.storeValue(ValueOperand(t0), Address(StackPointer, 0));
+
+        masm.ma_b(t1, t1, &undefLoopTop, Assembler::NonZero, ShortJump);
+    }
+
+    // Get the topmost argument.
+    static_assert(sizeof(Value) == 8, "TimesEight is used to skip arguments");
+
+    // | - sizeof(Value)| is used to put rcx such that we can read the last
+    // argument, and not the value which is after.
+    masm.ma_dsll(t0, s3, Imm32(3)); // t0 <- nargs * 8
+    masm.as_daddu(t1, t2, t0); // t1 <- t2(saved sp) + nargs * 8
+    masm.addPtr(Imm32(sizeof(RectifierFrameLayout) - sizeof(Value)), t1);
+
+    // Copy & Push arguments, |nargs| + 1 times (to include |this|).
+    {
+        Label copyLoopTop;
+
+        masm.bind(&copyLoopTop);
+        masm.sub32(Imm32(1), s3);
+        masm.subPtr(Imm32(sizeof(Value)), StackPointer);
+        masm.loadValue(Address(t1, 0), ValueOperand(t0));
+        masm.storeValue(ValueOperand(t0), Address(StackPointer, 0));
+        masm.subPtr(Imm32(sizeof(Value)), t1);
+
+        masm.ma_b(s3, s3, &copyLoopTop, Assembler::NonZero, ShortJump);
+    }
+
+    // if constructing, copy newTarget
+    {
+        Label notConstructing;
+
+        masm.branchTest32(Assembler::Zero, calleeTokenReg, Imm32(CalleeToken_FunctionConstructing),
+                          &notConstructing);
+
+        // thisFrame[numFormals] = prevFrame[argc]
+        ValueOperand newTarget(t0);
+
+        // +1 for |this|. We want vp[argc], so don't subtract 1
+        BaseIndex newTargetSrc(t2, numActArgsReg, TimesEight, sizeof(RectifierFrameLayout) + sizeof(Value));
+        masm.loadValue(newTargetSrc, newTarget);
+
+        // Again, 1 for |this|
+        BaseIndex newTargetDest(StackPointer, t3, TimesEight, sizeof(Value));
+        masm.storeValue(newTarget, newTargetDest);
+
+        masm.bind(&notConstructing);
+    }
+
+    // Caller:
+    // [arg2] [arg1] [this] [[argc] [callee] [descr] [raddr]] <- t2
+    //
+    //
+    // Rectifier frame:
+    // [undef] [undef] [undef] [arg2] [arg1] [this] <- sp [[argc] [callee] [descr] [raddr]]
+
+    // Construct sizeDescriptor.
+    masm.subPtr(StackPointer, t2);
+    masm.makeFrameDescriptor(t2, JitFrame_Rectifier);
+
+    // Construct JitFrameLayout.
+    masm.subPtr(Imm32(3 * sizeof(uintptr_t)), StackPointer);
+    // Push actual arguments.
+    masm.storePtr(numActArgsReg, Address(StackPointer, 2 * sizeof(uintptr_t)));
+    // Push callee token.
+    masm.storePtr(calleeTokenReg, Address(StackPointer, sizeof(uintptr_t)));
+    // Push frame descriptor.
+    masm.storePtr(t2, Address(StackPointer, 0));
+
+    // Call the target function.
+    // Note that this code assumes the function is JITted.
+    masm.andPtr(Imm32(uint32_t(CalleeTokenMask)), calleeTokenReg);
+    masm.loadPtr(Address(calleeTokenReg, JSFunction::offsetOfNativeOrScript()), t1);
+    masm.loadBaselineOrIonRaw(t1, t1, nullptr);
+    uint32_t returnOffset = masm.callJitNoProfiler(t1);
+
+    // Remove the rectifier frame.
+    // t2 <- descriptor with FrameType.
+    masm.loadPtr(Address(StackPointer, 0), t2);
+    masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), t2); // t2 <- descriptor.
+
+    // Discard descriptor, calleeToken and number of actual arguments.
+    masm.addPtr(Imm32(3 * sizeof(uintptr_t)), StackPointer);
+
+    // Discard pushed arguments.
+    masm.addPtr(t2, StackPointer);
+
+    masm.ret();
+    Linker linker(masm);
+    AutoFlushICache afc("ArgumentsRectifier");
+    JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
+
+    CodeOffsetLabel returnLabel(returnOffset);
+    returnLabel.fixup(&masm);
+    if (returnAddrOut)
+        *returnAddrOut = (void*) (code->raw() + returnLabel.offset());
+
+#ifdef JS_ION_PERF
+    writePerfSpewerJitCodeProfile(code, "ArgumentsRectifier");
+#endif
+
+    return code;
+}
+
+/* - When bailout is done via out of line code (lazy bailout).
+ * Frame size is stored in $ra (look at
+ * CodeGeneratorMIPS64::generateOutOfLineCode()) and thunk code should save it
+ * on stack. Other difference is that members snapshotOffset_ and padding_ are
+ * pushed to the stack by CodeGeneratorMIPS64::visitOutOfLineBailout(). Field
+ * frameClassId_ is forced to be NO_FRAME_SIZE_CLASS_ID
+ * (See: JitRuntime::generateBailoutHandler).
+ */
+static void
+PushBailoutFrame(MacroAssembler& masm, uint32_t frameClass, Register spArg)
+{
+    // Make sure that alignment is proper.
+    masm.checkStackAlignment();
+
+    // Push registers such that we can access them from [base + code].
+    masm.PushRegsInMask(AllRegs);
+
+    // Push the frameSize_ or tableOffset_ stored in ra
+    // See: CodeGeneratorMIPS64::generateOutOfLineCode()
+    masm.push(ra);
+
+    // Push frame class to stack
+    masm.push(ImmWord(frameClass));
+
+    // Put pointer to BailoutStack as first argument to the Bailout()
+    masm.movePtr(StackPointer, spArg);
+}
+
+static void
+GenerateBailoutThunk(JSContext* cx, MacroAssembler& masm, uint32_t frameClass)
+{
+    PushBailoutFrame(masm, frameClass, a0);
+
+    // Put pointer to BailoutInfo
+    static const uint32_t sizeOfBailoutInfo = sizeof(uintptr_t) * 2;
+    masm.subPtr(Imm32(sizeOfBailoutInfo), StackPointer);
+    masm.storePtr(ImmPtr(nullptr), Address(StackPointer, 0));
+    masm.movePtr(StackPointer, a1);
+
+    masm.setupAlignedABICall();
+    masm.passABIArg(a0);
+    masm.passABIArg(a1);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, Bailout));
+
+    // Get BailoutInfo pointer
+    masm.loadPtr(Address(StackPointer, 0), a2);
+
+    // Remove both the bailout frame and the topmost Ion frame's stack.
+    // Load frameSize from stack
+    masm.loadPtr(Address(StackPointer,
+                         sizeOfBailoutInfo + BailoutStack::offsetOfFrameSize()), a1);
+    // Remove complete BailoutStack class and data after it
+    masm.addPtr(Imm32(sizeof(BailoutStack) + sizeOfBailoutInfo), StackPointer);
+    // Remove frame size srom stack
+    masm.addPtr(a1, StackPointer);
+
+    // Jump to shared bailout tail. The BailoutInfo pointer has to be in a2.
+    JitCode* bailoutTail = cx->runtime()->jitRuntime()->getBailoutTail();
+    masm.branch(bailoutTail);
+}
+
+JitCode*
+JitRuntime::generateBailoutTable(JSContext* cx, uint32_t frameClass)
+{
+    MOZ_CRASH("MIPS64 does not use bailout tables");
+}
+
+JitCode*
+JitRuntime::generateBailoutHandler(JSContext* cx)
+{
+    MacroAssembler masm(cx);
+    GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
+
+    Linker linker(masm);
+    AutoFlushICache afc("BailoutHandler");
+    JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerJitCodeProfile(code, "BailoutHandler");
+#endif
+
+    return code;
+}
+
+JitCode*
+JitRuntime::generateVMWrapper(JSContext* cx, const VMFunction& f)
+{
+    MOZ_ASSERT(functionWrappers_);
+    MOZ_ASSERT(functionWrappers_->initialized());
+    VMWrapperMap::AddPtr p = functionWrappers_->lookupForAdd(&f);
+    if (p)
+        return p->value();
+
+    MacroAssembler masm(cx);
+
+    AllocatableGeneralRegisterSet regs(Register::Codes::WrapperMask);
+
+    static_assert((Register::Codes::VolatileMask & ~Register::Codes::WrapperMask) == 0,
+                  "Wrapper register set should be a superset of Volatile register set.");
+
+    // The context is the first argument; a0 is the first argument register.
+    Register cxreg = a0;
+    regs.take(cxreg);
+
+    // We're aligned to an exit frame, so link it up.
+    masm.enterExitFrame(&f);
+    masm.loadJSContext(cxreg);
+
+    // Save the base of the argument set stored on the stack.
+    Register argsBase = InvalidReg;
+    if (f.explicitArgs) {
+        argsBase = t1; // Use temporary register.
+        regs.take(argsBase);
+        masm.ma_daddu(argsBase, StackPointer, Imm32(ExitFrameLayout::SizeWithFooter()));
+    }
+
+    // Reserve space for the outparameter.
+    Register outReg = InvalidReg;
+    switch (f.outParam) {
+      case Type_Value:
+        outReg = regs.takeAny();
+        masm.reserveStack(sizeof(Value));
+        masm.movePtr(StackPointer, outReg);
+        break;
+
+      case Type_Handle:
+        outReg = regs.takeAny();
+        masm.PushEmptyRooted(f.outParamRootType);
+        masm.movePtr(StackPointer, outReg);
+        break;
+
+      case Type_Bool:
+      case Type_Int32:
+        outReg = regs.takeAny();
+        // Reserve 4-byte space to make stack aligned to 8-byte.
+        masm.reserveStack(2 * sizeof(int32_t));
+        masm.movePtr(StackPointer, outReg);
+        break;
+
+      case Type_Pointer:
+        outReg = regs.takeAny();
+        masm.reserveStack(sizeof(uintptr_t));
+        masm.movePtr(StackPointer, outReg);
+        break;
+
+      case Type_Double:
+        outReg = regs.takeAny();
+        masm.reserveStack(sizeof(double));
+        masm.movePtr(StackPointer, outReg);
+        break;
+
+      default:
+        MOZ_ASSERT(f.outParam == Type_Void);
+        break;
+    }
+
+    masm.setupUnalignedABICall(regs.getAny());
+    masm.passABIArg(cxreg);
+
+    size_t argDisp = 0;
+
+    // Copy any arguments.
+    for (uint32_t explicitArg = 0; explicitArg < f.explicitArgs; explicitArg++) {
+        MoveOperand from;
+        switch (f.argProperties(explicitArg)) {
+          case VMFunction::WordByValue:
+            if (f.argPassedInFloatReg(explicitArg))
+                masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::DOUBLE);
+            else
+                masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::GENERAL);
+            argDisp += sizeof(void*);
+            break;
+          case VMFunction::WordByRef:
+            masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE_ADDRESS),
+                            MoveOp::GENERAL);
+            argDisp += sizeof(void*);
+            break;
+          case VMFunction::DoubleByValue:
+          case VMFunction::DoubleByRef:
+            MOZ_CRASH("NYI: MIPS64 callVM should not be used with 128bits values.");
+            break;
+        }
+    }
+
+    // Copy the implicit outparam, if any.
+    if (InvalidReg != outReg)
+        masm.passABIArg(outReg);
+
+    masm.callWithABI(f.wrapped);
+
+    // Test for failure.
+    switch (f.failType()) {
+      case Type_Object:
+        masm.branchTestPtr(Assembler::Zero, v0, v0, masm.failureLabel());
+        break;
+      case Type_Bool:
+        // Called functions return bools, which are 0/false and non-zero/true
+        masm.branchIfFalseBool(v0, masm.failureLabel());
+        break;
+      default:
+        MOZ_CRASH("unknown failure kind");
+    }
+
+    // Load the outparam and free any allocated stack.
+    switch (f.outParam) {
+      case Type_Handle:
+        masm.popRooted(f.outParamRootType, ReturnReg, JSReturnOperand);
+        break;
+
+      case Type_Value:
+        masm.loadValue(Address(StackPointer, 0), JSReturnOperand);
+        masm.freeStack(sizeof(Value));
+        break;
+
+      case Type_Int32:
+        masm.load32(Address(StackPointer, 0), ReturnReg);
+        masm.freeStack(2 * sizeof(int32_t));
+        break;
+
+      case Type_Pointer:
+        masm.loadPtr(Address(StackPointer, 0), ReturnReg);
+        masm.freeStack(sizeof(uintptr_t));
+        break;
+
+      case Type_Bool:
+        masm.load8ZeroExtend(Address(StackPointer, 0), ReturnReg);
+        masm.freeStack(2 * sizeof(int32_t));
+        break;
+
+      case Type_Double:
+        if (cx->runtime()->jitSupportsFloatingPoint) {
+            masm.as_ld(ReturnDoubleReg, StackPointer, 0);
+        } else {
+            masm.assumeUnreachable("Unable to load into float reg, with no FP support.");
+        }
+        masm.freeStack(sizeof(double));
+        break;
+
+      default:
+        MOZ_ASSERT(f.outParam == Type_Void);
+        break;
+    }
+
+    masm.leaveExitFrame();
+    masm.retn(Imm32(sizeof(ExitFrameLayout) +
+                    f.explicitStackSlots() * sizeof(void*) +
+                    f.extraValuesToPop * sizeof(Value)));
+
+    Linker linker(masm);
+    AutoFlushICache afc("VMWrapper");
+    JitCode* wrapper = linker.newCode<NoGC>(cx, OTHER_CODE);
+    if (!wrapper)
+        return nullptr;
+
+    // linker.newCode may trigger a GC and sweep functionWrappers_ so we have
+    // to use relookupOrAdd instead of add.
+    if (!functionWrappers_->relookupOrAdd(p, &f, wrapper))
+        return nullptr;
+
+#ifdef JS_ION_PERF
+    writePerfSpewerJitCodeProfile(wrapper, "VMWrapper");
+#endif
+
+    return wrapper;
+}
+
+JitCode*
+JitRuntime::generatePreBarrier(JSContext* cx, MIRType type)
+{
+    MacroAssembler masm(cx);
+
+    LiveRegisterSet save;
+    if (cx->runtime()->jitSupportsFloatingPoint) {
+        save.set() = RegisterSet(GeneralRegisterSet(Registers::VolatileMask),
+                           FloatRegisterSet(FloatRegisters::VolatileMask));
+    } else {
+        save.set() = RegisterSet(GeneralRegisterSet(Registers::VolatileMask),
+                           FloatRegisterSet());
+    }
+    masm.PushRegsInMask(save);
+
+    MOZ_ASSERT(PreBarrierReg == a1);
+    masm.movePtr(ImmPtr(cx->runtime()), a0);
+
+    masm.setupUnalignedABICall(a2);
+    masm.passABIArg(a0);
+    masm.passABIArg(a1);
+    masm.callWithABI(IonMarkFunction(type));
+
+    masm.PopRegsInMask(save);
+    masm.ret();
+
+    Linker linker(masm);
+    AutoFlushICache afc("PreBarrier");
+    JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerJitCodeProfile(code, "PreBarrier");
+#endif
+
+    return code;
+}
+
+typedef bool (*HandleDebugTrapFn)(JSContext*, BaselineFrame*, uint8_t*, bool*);
+static const VMFunction HandleDebugTrapInfo = FunctionInfo<HandleDebugTrapFn>(HandleDebugTrap);
+
+JitCode*
+JitRuntime::generateDebugTrapHandler(JSContext* cx)
+{
+    MacroAssembler masm(cx);
+
+    Register scratch1 = t0;
+    Register scratch2 = t1;
+
+    // Load BaselineFrame pointer in scratch1.
+    masm.movePtr(s5, scratch1);
+    masm.subPtr(Imm32(BaselineFrame::Size()), scratch1);
+
+    // Enter a stub frame and call the HandleDebugTrap VM function. Ensure
+    // the stub frame has a nullptr ICStub pointer, since this pointer is
+    // marked during GC.
+    masm.movePtr(ImmPtr(nullptr), ICStubReg);
+    EmitBaselineEnterStubFrame(masm, scratch2);
+
+    JitCode* code = cx->runtime()->jitRuntime()->getVMWrapper(HandleDebugTrapInfo);
+    if (!code)
+        return nullptr;
+
+    masm.subPtr(Imm32(2 * sizeof(uintptr_t)), StackPointer);
+    masm.storePtr(ra, Address(StackPointer, sizeof(uintptr_t)));
+    masm.storePtr(scratch1, Address(StackPointer, 0));
+
+    EmitBaselineCallVM(code, masm);
+
+    EmitBaselineLeaveStubFrame(masm);
+
+    // If the stub returns |true|, we have to perform a forced return
+    // (return from the JS frame). If the stub returns |false|, just return
+    // from the trap stub so that execution continues at the current pc.
+    Label forcedReturn;
+    masm.branchTest32(Assembler::NonZero, ReturnReg, ReturnReg, &forcedReturn);
+
+    // ra was restored by EmitLeaveStubFrame
+    masm.branch(ra);
+
+    masm.bind(&forcedReturn);
+    masm.loadValue(Address(s5, BaselineFrame::reverseOffsetOfReturnValue()),
+                   JSReturnOperand);
+    masm.movePtr(s5, StackPointer);
+    masm.pop(s5);
+
+    // Before returning, if profiling is turned on, make sure that lastProfilingFrame
+    // is set to the correct caller frame.
+    {
+        Label skipProfilingInstrumentation;
+        AbsoluteAddress addressOfEnabled(cx->runtime()->spsProfiler.addressOfEnabled());
+        masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0), &skipProfilingInstrumentation);
+        masm.profilerExitFrame();
+        masm.bind(&skipProfilingInstrumentation);
+    }
+
+    masm.ret();
+
+    Linker linker(masm);
+    AutoFlushICache afc("DebugTrapHandler");
+    JitCode* codeDbg = linker.newCode<NoGC>(cx, OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerJitCodeProfile(codeDbg, "DebugTrapHandler");
+#endif
+
+    return codeDbg;
+}
+
+
+JitCode*
+JitRuntime::generateExceptionTailStub(JSContext* cx, void* handler)
+{
+    MacroAssembler masm;
+
+    masm.handleFailureWithHandlerTail(handler);
+
+    Linker linker(masm);
+    AutoFlushICache afc("ExceptionTailStub");
+    JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerJitCodeProfile(code, "ExceptionTailStub");
+#endif
+
+    return code;
+}
+
+JitCode*
+JitRuntime::generateBailoutTailStub(JSContext* cx)
+{
+    MacroAssembler masm;
+
+    masm.generateBailoutTail(a1, a2);
+
+    Linker linker(masm);
+    AutoFlushICache afc("BailoutTailStub");
+    JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerJitCodeProfile(code, "BailoutTailStub");
+#endif
+
+    return code;
+}
+
+JitCode*
+JitRuntime::generateProfilerExitFrameTailStub(JSContext* cx)
+{
+    MacroAssembler masm;
+
+    Register scratch1 = t0;
+    Register scratch2 = t1;
+    Register scratch3 = t2;
+    Register scratch4 = t3;
+
+    //
+    // The code generated below expects that the current stack pointer points
+    // to an Ion or Baseline frame, at the state it would be immediately
+    // before a ret().  Thus, after this stub's business is done, it executes
+    // a ret() and returns directly to the caller script, on behalf of the
+    // callee script that jumped to this code.
+    //
+    // Thus the expected stack is:
+    //
+    //                                   StackPointer ----+
+    //                                                    v
+    // ..., ActualArgc, CalleeToken, Descriptor, ReturnAddr
+    // MEM-HI                                       MEM-LOW
+    //
+    //
+    // The generated jitcode is responsible for overwriting the
+    // jitActivation->lastProfilingFrame field with a pointer to the previous
+    // Ion or Baseline jit-frame that was pushed before this one. It is also
+    // responsible for overwriting jitActivation->lastProfilingCallSite with
+    // the return address into that frame.  The frame could either be an
+    // immediate "caller" frame, or it could be a frame in a previous
+    // JitActivation (if the current frame was entered from C++, and the C++
+    // was entered by some caller jit-frame further down the stack).
+    //
+    // So this jitcode is responsible for "walking up" the jit stack, finding
+    // the previous Ion or Baseline JS frame, and storing its address and the
+    // return address into the appropriate fields on the current jitActivation.
+    //
+    // There are a fixed number of different path types that can lead to the
+    // current frame, which is either a baseline or ion frame:
+    //
+    // <Baseline-Or-Ion>
+    // ^
+    // |
+    // ^--- Ion
+    // |
+    // ^--- Baseline Stub <---- Baseline
+    // |
+    // ^--- Argument Rectifier
+    // |    ^
+    // |    |
+    // |    ^--- Ion
+    // |    |
+    // |    ^--- Baseline Stub <---- Baseline
+    // |
+    // ^--- Entry Frame (From C++)
+    //
+    Register actReg = scratch4;
+    AbsoluteAddress activationAddr(GetJitContext()->runtime->addressOfProfilingActivation());
+    masm.loadPtr(activationAddr, actReg);
+
+    Address lastProfilingFrame(actReg, JitActivation::offsetOfLastProfilingFrame());
+    Address lastProfilingCallSite(actReg, JitActivation::offsetOfLastProfilingCallSite());
+
+#ifdef DEBUG
+    // Ensure that frame we are exiting is current lastProfilingFrame
+    {
+        masm.loadPtr(lastProfilingFrame, scratch1);
+        Label checkOk;
+        masm.branchPtr(Assembler::Equal, scratch1, ImmWord(0), &checkOk);
+        masm.branchPtr(Assembler::Equal, StackPointer, scratch1, &checkOk);
+        masm.assumeUnreachable(
+            "Mismatch between stored lastProfilingFrame and current stack pointer.");
+        masm.bind(&checkOk);
+    }
+#endif
+
+    // Load the frame descriptor into |scratch1|, figure out what to do depending on its type.
+    masm.loadPtr(Address(StackPointer, JitFrameLayout::offsetOfDescriptor()), scratch1);
+
+    // Going into the conditionals, we will have:
+    //      FrameDescriptor.size in scratch1
+    //      FrameDescriptor.type in scratch2
+    masm.ma_and(scratch2, scratch1, Imm32((1 << FRAMETYPE_BITS) - 1));
+    masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch1);
+
+    // Handling of each case is dependent on FrameDescriptor.type
+    Label handle_IonJS;
+    Label handle_BaselineStub;
+    Label handle_Rectifier;
+    Label handle_IonAccessorIC;
+    Label handle_Entry;
+    Label end;
+
+    masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_IonJS), &handle_IonJS);
+    masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineJS), &handle_IonJS);
+    masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineStub), &handle_BaselineStub);
+    masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Rectifier), &handle_Rectifier);
+    masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_IonAccessorIC), &handle_IonAccessorIC);
+    masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Entry), &handle_Entry);
+
+    masm.assumeUnreachable("Invalid caller frame type when exiting from Ion frame.");
+
+    //
+    // JitFrame_IonJS
+    //
+    // Stack layout:
+    //                  ...
+    //                  Ion-Descriptor
+    //     Prev-FP ---> Ion-ReturnAddr
+    //                  ... previous frame data ... |- Descriptor.Size
+    //                  ... arguments ...           |
+    //                  ActualArgc          |
+    //                  CalleeToken         |- JitFrameLayout::Size()
+    //                  Descriptor          |
+    //        FP -----> ReturnAddr          |
+    //
+    masm.bind(&handle_IonJS);
+    {
+