Merge m-c to b2g-inbound.
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 24 Apr 2014 13:43:09 -0400
changeset 180493 f31baef145d4b5d0369bb59d6b284605c05032f9
parent 180492 f93d04f0ba993b8897d1e9427b37fe6e843b6e28 (current diff)
parent 180428 3b166b8add936dd501593c6177d67bdeb15c46e8 (diff)
child 180494 2a544d65dac80281e839b9fa2c4fcd8a3dedc876
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
milestone31.0a1
Merge m-c to b2g-inbound.
dom/apps/tests/moz.build
dom/apps/tests/signed/moz.build
intl/uconv/tests/unit/test_decode_utf-7.js
intl/uconv/tests/unit/test_decode_utf-7_internal.js
intl/uconv/tests/unit/test_encode_utf-7.js
intl/uconv/tests/unit/test_encode_utf-7_internal.js
intl/uconv/ucvlatin/nsMUTF7ToUnicode.cpp
intl/uconv/ucvlatin/nsMUTF7ToUnicode.h
intl/uconv/ucvlatin/nsUTF7ToUnicode.cpp
intl/uconv/ucvlatin/nsUTF7ToUnicode.h
intl/uconv/ucvlatin/nsUnicodeToMUTF7.cpp
intl/uconv/ucvlatin/nsUnicodeToMUTF7.h
intl/uconv/ucvlatin/nsUnicodeToUTF7.cpp
intl/uconv/ucvlatin/nsUnicodeToUTF7.h
intl/uconv/util/nsUCSupport.h
--- a/browser/components/customizableui/src/CustomizeMode.jsm
+++ b/browser/components/customizableui/src/CustomizeMode.jsm
@@ -156,18 +156,17 @@ CustomizeMode.prototype = {
         }.bind(this);
         Services.obs.addObserver(delayedStartupObserver, "browser-delayed-startup-finished", false);
         yield delayedStartupDeferred.promise;
       }
 
       let toolbarVisibilityBtn = document.getElementById(kToolbarVisibilityBtn);
       let togglableToolbars = window.getTogglableToolbars();
       let bookmarksToolbar = document.getElementById("PersonalToolbar");
-      if (togglableToolbars.length == 0 ||
-          (togglableToolbars.length == 1 && togglableToolbars[0] == bookmarksToolbar)) {
+      if (togglableToolbars.length == 0) {
         toolbarVisibilityBtn.setAttribute("hidden", "true");
       } else {
         toolbarVisibilityBtn.removeAttribute("hidden");
       }
 
       // Disable lightweight themes while in customization mode since
       // they don't have large enough images to pad the full browser window.
       if (this.document.documentElement._lightweightTheme)
--- a/browser/devtools/layoutview/test/browser_layoutview.js
+++ b/browser/devtools/layoutview/test/browser_layoutview.js
@@ -92,8 +92,24 @@ function*() {
 
   yield waitForUpdate();
 
   for (let i = 0; i < res2.length; i++) {
     let elt = viewdoc.querySelector(res2[i].selector);
     is(elt.textContent, res2[i].value, res2[i].selector + " has the right value after style update.");
   }
 });
+
+addTest("Test that long labels on left/right are rotated 90 degrees",
+function*() {
+  let viewdoc = view.document;
+  const LONG_TEXT_ROTATE_LIMIT = 3;
+
+  for (let i = 0; i < res1.length; i++) {
+    let elt = viewdoc.querySelector(res1[i].selector);
+    let isLong = elt.textContent.length > LONG_TEXT_ROTATE_LIMIT;
+    let classList = elt.parentNode.classList
+    let canBeRotated = classList.contains("left") || classList.contains("right");
+    let isRotated = classList.contains("rotate");
+
+    is(canBeRotated && isLong, isRotated, res1[i].selector + " correctly rotated.");
+  }
+});
--- a/browser/devtools/layoutview/view.css
+++ b/browser/devtools/layoutview/view.css
@@ -169,16 +169,24 @@ body {
 .margin.right {
   right: 0;
 }
 
 .margin.left {
   left: 0;
 }
 
+.rotate.left:not(.editing) {
+  transform: rotate(-90deg);
+}
+
+.rotate.right:not(.editing) {
+  transform: rotate(90deg);
+}
+
 .tooltip {
   position: absolute;
   bottom: 0;
   right: 2px;
   pointer-events: none;
 }
 
 body.dim > #header > #element-position,
--- a/browser/devtools/layoutview/view.js
+++ b/browser/devtools/layoutview/view.js
@@ -15,16 +15,17 @@ Cu.import("resource://gre/modules/Task.j
 Cu.import("resource://gre/modules/devtools/Loader.jsm");
 Cu.import("resource://gre/modules/devtools/Console.jsm");
 
 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 const {InplaceEditor, editableItem} = devtools.require("devtools/shared/inplace-editor");
 const {parseDeclarations} = devtools.require("devtools/styleinspector/css-parsing-utils");
 
 const NUMERIC = /^-?[\d\.]+$/;
+const LONG_TEXT_ROTATE_LIMIT = 3;
 
 /**
  * An instance of EditingSession tracks changes that have been made during the
  * modification of box model values. All of these changes can be reverted by
  * calling revert.
  *
  * @param doc    A DOM document that can be used to test style rules.
  * @param rules  An array of the style rules defined for the node being edited.
@@ -218,20 +219,24 @@ LayoutView.prototype = {
    */
   initEditor: function LV_initEditor(element, event, dimension) {
     let { property, realProperty } = dimension;
     if (!realProperty)
       realProperty = property;
     let session = new EditingSession(document, this.elementRules);
     let initialValue = session.getProperty(realProperty);
 
-    new InplaceEditor({
+    let editor = new InplaceEditor({
       element: element,
       initial: initialValue,
 
+      start: (editor) => {
+        editor.elt.parentNode.classList.add("editing");
+      },
+
       change: (value) => {
         if (NUMERIC.test(value))
           value += "px";
         let properties = [
           { name: property, value: value }
         ]
 
         if (property.substring(0, 7) == "border-") {
@@ -240,16 +245,17 @@ LayoutView.prototype = {
           if (!style || style == "none" || style == "hidden")
             properties.push({ name: bprop, value: "solid" });
         }
 
         session.setProperties(properties);
       },
 
       done: (value, commit) => {
+        editor.elt.parentNode.classList.remove("editing");
         if (!commit)
           session.revert();
       }
     }, event);
   },
 
   /**
    * Is the layoutview visible in the sidebar?
@@ -382,16 +388,17 @@ LayoutView.prototype = {
       for (let i in this.map) {
         let selector = this.map[i].selector;
         let span = this.doc.querySelector(selector);
         if (span.textContent.length > 0 &&
             span.textContent == this.map[i].value) {
           continue;
         }
         span.textContent = this.map[i].value;
+        this.manageOverflowingText(span);
       }
 
       width -= this.map.borderLeft.value + this.map.borderRight.value +
                this.map.paddingLeft.value + this.map.paddingRight.value;
 
       height -= this.map.borderTop.value + this.map.borderBottom.value +
                 this.map.paddingTop.value + this.map.paddingBottom.value;
 
@@ -415,16 +422,25 @@ LayoutView.prototype = {
     toolbox.highlighterUtils.highlightNodeFront(nodeFront, options);
   },
 
   hideBoxModel: function() {
     let toolbox = this.inspector.toolbox;
 
     toolbox.highlighterUtils.unhighlight();
   },
+
+  manageOverflowingText: function(span) {
+    let classList = span.parentNode.classList;
+
+    if (classList.contains("left") || classList.contains("right")) {
+      let force = span.textContent.length > LONG_TEXT_ROTATE_LIMIT;
+      classList.toggle("rotate", force);
+    }
+  }
 };
 
 let elts;
 let tooltip;
 
 let onmouseover = function(e) {
   let region = e.target.getAttribute("data-box");
 
--- a/browser/devtools/scratchpad/test/browser_scratchpad_recent_files.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_recent_files.js
@@ -188,23 +188,23 @@ function testClearedAll()
 
   finishTest();
 }
 
 function createAndLoadTemporaryFile(aFile, aFileName, aFileContent)
 {
   // Create a temporary file.
   aFile = FileUtils.getFile("TmpD", [aFileName]);
-  aFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
+  aFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
 
   // Write the temporary file.
   let fout = Cc["@mozilla.org/network/file-output-stream;1"].
              createInstance(Ci.nsIFileOutputStream);
   fout.init(aFile.QueryInterface(Ci.nsILocalFile), 0x02 | 0x08 | 0x20,
-            0644, fout.DEFER_OPEN);
+            0o644, fout.DEFER_OPEN);
 
   gScratchpad.setFilename(aFile.path);
   gScratchpad.importFromFile(aFile.QueryInterface(Ci.nsILocalFile),  true,
                             fileImported);
   gScratchpad.saveFile(fileSaved);
 
   return aFile;
 }
--- a/browser/devtools/scratchpad/test/browser_scratchpad_reset_undo.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_reset_undo.js
@@ -40,31 +40,31 @@ function test()
 }
 
 function runTests()
 {
   gScratchpad = gScratchpadWindow.Scratchpad;
 
   // Create a temporary file.
   gFileA = FileUtils.getFile("TmpD", ["fileAForBug684546.tmp"]);
-  gFileA.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
+  gFileA.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
 
   gFileB = FileUtils.getFile("TmpD", ["fileBForBug684546.tmp"]);
-  gFileB.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
+  gFileB.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
 
   // Write the temporary file.
   let foutA = Cc["@mozilla.org/network/file-output-stream;1"].
              createInstance(Ci.nsIFileOutputStream);
   foutA.init(gFileA.QueryInterface(Ci.nsILocalFile), 0x02 | 0x08 | 0x20,
-            0644, foutA.DEFER_OPEN);
+            0o644, foutA.DEFER_OPEN);
 
   let foutB = Cc["@mozilla.org/network/file-output-stream;1"].
              createInstance(Ci.nsIFileOutputStream);
   foutB.init(gFileB.QueryInterface(Ci.nsILocalFile), 0x02 | 0x08 | 0x20,
-            0644, foutB.DEFER_OPEN);
+            0o644, foutB.DEFER_OPEN);
 
   let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
                   createInstance(Ci.nsIScriptableUnicodeConverter);
   converter.charset = "UTF-8";
   let fileContentStreamA = converter.convertToInputStream(gFileAContent);
   let fileContentStreamB = converter.convertToInputStream(gFileBContent);
 
   NetUtil.asyncCopy(fileContentStreamA, foutA, tempFileSaved);
--- a/browser/devtools/scratchpad/test/browser_scratchpad_revert_to_saved.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_revert_to_saved.js
@@ -93,23 +93,23 @@ function testAfterSecondRevert() {
   gFile = gScratchpad = menu = null;
   finish();
 }
 
 function createAndLoadTemporaryFile()
 {
   // Create a temporary file.
   gFile = FileUtils.getFile("TmpD", [gFileName]);
-  gFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
+  gFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
 
   // Write the temporary file.
   let fout = Cc["@mozilla.org/network/file-output-stream;1"].
              createInstance(Ci.nsIFileOutputStream);
   fout.init(gFile.QueryInterface(Ci.nsILocalFile), 0x02 | 0x08 | 0x20,
-            0644, fout.DEFER_OPEN);
+            0o644, fout.DEFER_OPEN);
 
   let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
                   createInstance(Ci.nsIScriptableUnicodeConverter);
   converter.charset = "UTF-8";
   let fileContentStream = converter.convertToInputStream(gFileContent);
 
   NetUtil.asyncCopy(fileContentStream, fout, tempFileSaved);
 }
--- a/build/gyp.mozbuild
+++ b/build/gyp.mozbuild
@@ -30,17 +30,17 @@ gyp_vars = {
     # saves 4MB when webrtc_trace is off
     'enable_lazy_trace_alloc': 1,
 
      # turn off mandatory use of NEON and instead use NEON detection
     'arm_neon': 0,
     'arm_neon_optional': 1,
 
     'moz_widget_toolkit_gonk': 0,
-    'moz_omx_encoder': 0,
+    'moz_webrtc_omx': 0,
 
     # (for vp8) chromium sets to 0 also
     'use_temporal_layers': 0,
     # Creates AEC internal sample dump files in current directory
     # 'aec_debug_dump': 1,
 
     # codec enable/disables:
     'include_g711': 1,
@@ -57,18 +57,18 @@ if os == 'WINNT':
     gyp_vars.update(
         MSVS_VERSION=CONFIG['_MSVS_VERSION'],
         MSVS_OS_BITS=64 if CONFIG['HAVE_64BIT_OS'] else 32,
     )
 elif os == 'Android':
     if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
         gyp_vars['build_with_gonk'] = 1
         gyp_vars['moz_widget_toolkit_gonk'] = 1
-        if CONFIG['MOZ_OMX_ENCODER']:
-          gyp_vars['moz_omx_encoder'] = 1
+        if int(CONFIG['ANDROID_VERSION']) >= 18:
+          gyp_vars['moz_webrtc_omx'] = 1
     else:
         gyp_vars.update(
             gtest_target_type='executable',
             android_toolchain=CONFIG['ANDROID_TOOLCHAIN'],
         )
 
 flavors = {
     'WINNT': 'win',
--- a/configure.in
+++ b/configure.in
@@ -7431,16 +7431,28 @@ MOZ_ARG_ENABLE_BOOL(visual-event-tracer,
 [  --enable-visual-event-tracer   Enable visual event tracer instrumentation],
     MOZ_VISUAL_EVENT_TRACER=1,
     MOZ_VISUAL_EVENT_TRACER=)
 if test -n "$MOZ_VISUAL_EVENT_TRACER"; then
     AC_DEFINE(MOZ_VISUAL_EVENT_TRACER)
 fi
 
 dnl ========================================================
+dnl = Enable TaskTracer
+dnl ========================================================
+MOZ_ARG_ENABLE_BOOL(tasktracer,
+[  --enable-tasktracer       Set compile flags necessary for using TaskTracer],
+    MOZ_TASK_TRACER=1,
+    MOZ_TASK_TRACER= )
+if test "$MOZ_WIDGET_TOOLKIT" = "gonk" -a -n "$MOZ_TASK_TRACER"; then
+    AC_DEFINE(MOZ_TASK_TRACER)
+    AC_SUBST(MOZ_TASK_TRACER)
+fi
+
+dnl ========================================================
 dnl Turn on reflow counting
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(reflow-perf,
 [  --enable-reflow-perf    Enable reflow performance tracing],
     MOZ_REFLOW_PERF=1,
     MOZ_REFLOW_PERF= )
 if test -n "$MOZ_REFLOW_PERF"; then
     AC_DEFINE(MOZ_REFLOW_PERF)
@@ -8892,19 +8904,17 @@ if test "$MOZ_X11"; then
 
 fi # MOZ_X11
 
 fi # COMPILE_ENVIRONMENT
 
 dnl Set various defines and substitutions
 dnl ========================================================
 
-if test "$OS_ARCH" = "Darwin"; then
-  AC_DEFINE(XP_UNIX)
-elif test "$OS_ARCH" != "WINNT"; then
+if test "$OS_ARCH" != "WINNT"; then
   AC_DEFINE(XP_UNIX)
 fi
 
 if test "$MOZ_DEBUG"; then
     AC_DEFINE(MOZ_REFLOW_PERF)
     AC_DEFINE(MOZ_REFLOW_PERF_DSP)
 fi
 
--- a/content/html/content/src/nsTextEditorState.cpp
+++ b/content/html/content/src/nsTextEditorState.cpp
@@ -1229,16 +1229,26 @@ nsTextEditorState::PrepareEditor(const n
       // Set the correct value in the root node
       rv = mBoundFrame->UpdateValueDisplay(true, !mEditorInitialized, aValue);
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     newEditor = mEditor; // just pretend that we have a new editor!
   }
 
+  // Get the current value of the textfield from the content.
+  // Note that if we've created a new editor, mEditor is null at this stage,
+  // so we will get the real value from the content.
+  nsAutoString defaultValue;
+  if (aValue) {
+    defaultValue = *aValue;
+  } else {
+    GetValue(defaultValue, true);
+  }
+
   if (!mEditorInitialized) {
     // Now initialize the editor.
     //
     // NOTE: Conversion of '\n' to <BR> happens inside the
     //       editor's Init() call.
 
     // Get the DOM document
     nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(shell->GetDocument());
@@ -1249,17 +1259,18 @@ nsTextEditorState::PrepareEditor(const n
     // for its content manipulations, and it causes it to fail some security
     // checks deep inside when initializing. So we explictly make it clear that
     // we're native code.
     // Note that any script that's directly trying to access our value
     // has to be going through some scriptable object to do that and that
     // already does the relevant security checks.
     AutoNoJSAPI nojsapi;
 
-    rv = newEditor->Init(domdoc, GetRootNode(), mSelCon, editorFlags);
+    rv = newEditor->Init(domdoc, GetRootNode(), mSelCon, editorFlags,
+                         defaultValue);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Initialize the controller for the editor
 
   if (!SuppressEventHandlers(presContext)) {
     nsCOMPtr<nsIControllers> controllers;
     nsCOMPtr<nsIDOMHTMLInputElement> inputElement =
@@ -1330,26 +1341,16 @@ nsTextEditorState::PrepareEditor(const n
 
     // Disable the selection if necessary.
     if (editorFlags & nsIPlaintextEditor::eEditorDisabledMask)
       mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_OFF);
 
     newEditor->SetFlags(editorFlags);
   }
 
-  // Get the current value of the textfield from the content.
-  // Note that if we've created a new editor, mEditor is null at this stage,
-  // so we will get the real value from the content.
-  nsAutoString defaultValue;
-  if (aValue) {
-    defaultValue = *aValue;
-  } else {
-    GetValue(defaultValue, true);
-  }
-
   if (shouldInitializeEditor) {
     // Hold on to the newly created editor
     preDestroyer.Swap(mEditor);
   }
 
   // If we have a default value, insert it under the div we created
   // above, but be sure to use the editor so that '*' characters get
   // displayed for password fields, etc. SetValue() will call the
@@ -1775,28 +1776,17 @@ nsTextEditorState::SetValue(const nsAStr
 #ifdef DEBUG
     if (IsSingleLineTextControl()) {
       NS_ASSERTION(mEditorInitialized || mInitializing,
                    "We should never try to use the editor if we're not initialized unless we're being initialized");
     }
 #endif
 
     nsAutoString currentValue;
-    if (!mEditorInitialized && IsSingleLineTextControl()) {
-      // Grab the current value directly from the text node to make sure that we
-      // deal with stale data correctly.
-      NS_ASSERTION(mRootNode, "We should have a root node here");
-      nsIContent *textContent = mRootNode->GetFirstChild();
-      nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(textContent);
-      if (textNode) {
-        textNode->GetData(currentValue);
-      }
-    } else {
-      mBoundFrame->GetText(currentValue);
-    }
+    mBoundFrame->GetText(currentValue);
 
     nsWeakFrame weakFrame(mBoundFrame);
 
     // this is necessary to avoid infinite recursion
     if (!currentValue.Equals(aValue))
     {
       ValueSetter valueSetter(mEditor);
 
--- a/content/html/content/test/mochitest.ini
+++ b/content/html/content/test/mochitest.ini
@@ -388,16 +388,17 @@ skip-if = (buildapp == 'b2g' && toolkit 
 [test_bug742030.html]
 [test_bug742549.html]
 [test_bug745685.html]
 [test_bug763626.html]
 [test_bug780993.html]
 [test_bug787134.html]
 [test_bug797113.html]
 [test_bug803677.html]
+[test_bug821307.html]
 [test_bug827126.html]
 [test_bug827426.html]
 [test_bug838582.html]
 [test_bug839371.html]
 [test_bug839913.html]
 [test_bug840877.html]
 [test_bug841466.html]
 skip-if = (toolkit == 'gonk' && debug) || e10s #debug-only failure
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_bug821307.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=821307
+-->
+<head>
+  <title>Test for Bug 821307</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=821307">Mozilla Bug 821307</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+
+<input id='dummy'></input>
+<input type="password" id='input' value='11111111111111111' style="width:40em; font-size:40px;"></input>
+
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+  var dummy = document.getElementById('dummy');
+  dummy.focus();
+  is(document.activeElement, dummy, "Check dummy element is now focused");
+
+  var input = document.getElementById('input');
+  var rect = input.getBoundingClientRect();
+  synthesizeMouse(input, 100, rect.height/2, {});
+  is(document.activeElement, input, "Check input element is now focused");
+
+  SimpleTest.finish();
+});
+
+</script>
+</pre>
+</body>
+</html>
--- a/content/media/AudioMixer.h
+++ b/content/media/AudioMixer.h
@@ -9,17 +9,18 @@
 #include "AudioSampleFormat.h"
 #include "nsTArray.h"
 #include "mozilla/PodOperations.h"
 
 namespace mozilla {
 typedef void(*MixerFunc)(AudioDataValue* aMixedBuffer,
                          AudioSampleFormat aFormat,
                          uint32_t aChannels,
-                         uint32_t aFrames);
+                         uint32_t aFrames,
+                         uint32_t aSampleRate);
 
 /**
  * This class mixes multiple streams of audio together to output a single audio
  * stream.
  *
  * AudioMixer::Mix is to be called repeatedly with buffers that have the same
  * length, sample rate, sample format and channel count.
  *
@@ -29,40 +30,47 @@ typedef void(*MixerFunc)(AudioDataValue*
  * This class is not thread safe.
  */
 class AudioMixer
 {
 public:
   AudioMixer(MixerFunc aCallback)
     : mCallback(aCallback),
       mFrames(0),
-      mChannels(0)
+      mChannels(0),
+      mSampleRate(0)
   { }
 
   /* Get the data from the mixer. This is supposed to be called when all the
    * tracks have been mixed in. The caller should not hold onto the data. */
   void FinishMixing() {
     mCallback(mMixedAudio.Elements(),
               AudioSampleTypeToFormat<AudioDataValue>::Format,
               mChannels,
-              mFrames);
+              mFrames,
+              mSampleRate);
     PodZero(mMixedAudio.Elements(), mMixedAudio.Length());
-    mChannels = mFrames = 0;
+    mSampleRate = mChannels = mFrames = 0;
   }
 
   /* Add a buffer to the mix. aSamples is interleaved. */
-  void Mix(AudioDataValue* aSamples, uint32_t aChannels, uint32_t aFrames) {
+  void Mix(AudioDataValue* aSamples,
+           uint32_t aChannels,
+           uint32_t aFrames,
+           uint32_t aSampleRate) {
     if (!mFrames && !mChannels) {
       mFrames = aFrames;
       mChannels = aChannels;
+      mSampleRate = aSampleRate;
       EnsureCapacityAndSilence();
     }
 
     MOZ_ASSERT(aFrames == mFrames);
     MOZ_ASSERT(aChannels == mChannels);
+    MOZ_ASSERT(aSampleRate == mSampleRate);
 
     for (uint32_t i = 0; i < aFrames * aChannels; i++) {
       mMixedAudio[i] += aSamples[i];
     }
   }
 private:
   void EnsureCapacityAndSilence() {
     if (mFrames * mChannels > mMixedAudio.Length()) {
@@ -72,14 +80,16 @@ private:
   }
 
   /* Function that is called when the mixing is done. */
   MixerFunc mCallback;
   /* Number of frames for this mixing block. */
   uint32_t mFrames;
   /* Number of channels for this mixing block. */
   uint32_t mChannels;
+  /* Sample rate the of the mixed data. */
+  uint32_t mSampleRate;
   /* Buffer containing the mixed audio data. */
   nsTArray<AudioDataValue> mMixedAudio;
 };
 }
 
 #endif // MOZILLA_AUDIOMIXER_H_
--- a/content/media/AudioSegment.cpp
+++ b/content/media/AudioSegment.cpp
@@ -211,14 +211,14 @@ AudioSegment::WriteTo(uint64_t aID, Audi
       LogTime(AsyncLatencyLogger::AudioMediaStreamTrack, aID,
               (now - c.mTimeStamp).ToMilliseconds(), c.mTimeStamp);
     }
   }
 
   aOutput->Write(buf.Elements(), GetDuration(), &(mChunks[mChunks.Length() - 1].mTimeStamp));
 
   if (aMixer) {
-    aMixer->Mix(buf.Elements(), outputChannels, GetDuration());
+    aMixer->Mix(buf.Elements(), outputChannels, GetDuration(), aOutput->GetRate());
   }
   aOutput->Start();
 }
 
 }
--- a/content/media/MediaDecoderStateMachine.cpp
+++ b/content/media/MediaDecoderStateMachine.cpp
@@ -2461,17 +2461,20 @@ void MediaDecoderStateMachine::AdvanceFr
       HasLowDecodedData(remainingTime + EXHAUSTED_DATA_MARGIN_USECS) &&
       !mDecoder->IsDataCachedToEndOfResource() &&
       !resource->IsSuspended()) {
     if (JustExitedQuickBuffering() || HasLowUndecodedData()) {
       if (currentFrame) {
         mReader->VideoQueue().PushFront(currentFrame.forget());
       }
       StartBuffering();
-      ScheduleStateMachine();
+      // Don't go straight back to the state machine loop since that might
+      // cause us to start decoding again and we could flip-flop between
+      // decoding and quick-buffering.
+      ScheduleStateMachine(USECS_PER_S);
       return;
     }
   }
 
   // We've got enough data to keep playing until at least the next frame.
   // Start playing now if need be.
   if (!IsPlaying() && ((mFragmentEndTime >= 0 && clock_time < mFragmentEndTime) || mFragmentEndTime < 0)) {
     StartPlayback();
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -335,17 +335,17 @@ MediaStreamGraphImpl::GetAudioPosition(M
   if (aStream->mAudioOutputStreams.IsEmpty()) {
     return mCurrentTime;
   }
   int64_t positionInFrames = aStream->mAudioOutputStreams[0].mStream->GetPositionInFrames();
   if (positionInFrames < 0) {
     return mCurrentTime;
   }
   return aStream->mAudioOutputStreams[0].mAudioPlaybackStartTime +
-      TicksToTimeRoundDown(IdealAudioRate(),
+      TicksToTimeRoundDown(mSampleRate,
                            positionInFrames);
 }
 
 void
 MediaStreamGraphImpl::UpdateCurrentTime()
 {
   GraphTime prevCurrentTime, nextCurrentTime;
   if (mRealtime) {
@@ -581,25 +581,26 @@ MediaStreamGraphImpl::UpdateStreamOrderF
 
   stream->mHasBeenOrdered = true;
   *mStreams.AppendElement() = stream.forget();
 }
 
 static void AudioMixerCallback(AudioDataValue* aMixedBuffer,
                                AudioSampleFormat aFormat,
                                uint32_t aChannels,
-                               uint32_t aFrames)
+                               uint32_t aFrames,
+                               uint32_t aSampleRate)
 {
   // Need an api to register mixer callbacks, bug 989921
 #ifdef MOZ_WEBRTC
   if (aFrames > 0 && aChannels > 0) {
     // XXX need Observer base class and registration API
     if (gFarendObserver) {
       gFarendObserver->InsertFarEnd(aMixedBuffer, aFrames, false,
-                                    IdealAudioRate(), aChannels, aFormat);
+                                    aSampleRate, aChannels, aFormat);
     }
   }
 #endif
 }
 
 void
 MediaStreamGraphImpl::UpdateStreamOrder()
 {
@@ -842,17 +843,17 @@ MediaStreamGraphImpl::CreateOrDestroyAud
           aStream->mAudioOutputStreams.AppendElement();
         audioOutputStream->mAudioPlaybackStartTime = aAudioOutputStartTime;
         audioOutputStream->mBlockedAudioTime = 0;
         audioOutputStream->mLastTickWritten = 0;
         audioOutputStream->mStream = new AudioStream();
         // XXX for now, allocate stereo output. But we need to fix this to
         // match the system's ideal channel configuration.
         // NOTE: we presume this is either fast or async-under-the-covers
-        audioOutputStream->mStream->Init(2, IdealAudioRate(),
+        audioOutputStream->mStream->Init(2, mSampleRate,
                                          AudioChannel::Normal,
                                          AudioStream::LowLatency);
         audioOutputStream->mTrackID = tracks->GetID();
 
         LogLatency(AsyncLatencyLogger::AudioStreamCreate,
                    reinterpret_cast<uint64_t>(aStream),
                    reinterpret_cast<int64_t>(audioOutputStream->mStream.get()));
       }
@@ -874,17 +875,17 @@ MediaStreamGraphImpl::PlayAudio(MediaStr
   MOZ_ASSERT(mRealtime, "Should only attempt to play audio in realtime mode");
 
   TrackTicks ticksWritten = 0;
   // We compute the number of needed ticks by converting a difference of graph
   // time rather than by substracting two converted stream time to ensure that
   // the rounding between {Graph,Stream}Time and track ticks is not dependant
   // on the absolute value of the {Graph,Stream}Time, and so that number of
   // ticks to play is the same for each cycle.
-  TrackTicks ticksNeeded = TimeToTicksRoundDown(IdealAudioRate(), aTo) - TimeToTicksRoundDown(IdealAudioRate(), aFrom);
+  TrackTicks ticksNeeded = TimeToTicksRoundDown(mSampleRate, aTo) - TimeToTicksRoundDown(mSampleRate, aFrom);
 
   if (aStream->mAudioOutputStreams.IsEmpty()) {
     return 0;
   }
 
   // When we're playing multiple copies of this stream at the same time, they're
   // perfectly correlated so adding volumes is the right thing to do.
   float volume = 0.0f;
@@ -892,17 +893,17 @@ MediaStreamGraphImpl::PlayAudio(MediaStr
     volume += aStream->mAudioOutputs[i].mVolume;
   }
 
   for (uint32_t i = 0; i < aStream->mAudioOutputStreams.Length(); ++i) {
     MediaStream::AudioOutputStream& audioOutput = aStream->mAudioOutputStreams[i];
     StreamBuffer::Track* track = aStream->mBuffer.FindTrack(audioOutput.mTrackID);
     AudioSegment* audio = track->Get<AudioSegment>();
     AudioSegment output;
-    MOZ_ASSERT(track->GetRate() == IdealAudioRate());
+    MOZ_ASSERT(track->GetRate() == mSampleRate);
 
     // offset and audioOutput.mLastTickWritten can differ by at most one sample,
     // because of the rounding issue. We track that to ensure we don't skip a
     // sample, or play a sample twice.
     TrackTicks offset = track->TimeToTicksRoundDown(GraphTimeToStreamTime(aStream, aFrom));
     if (!audioOutput.mLastTickWritten) {
         audioOutput.mLastTickWritten = offset;
     }
@@ -928,17 +929,17 @@ MediaStreamGraphImpl::PlayAudio(MediaStr
       end = std::min(end, aTo);
 
       // Check how many ticks of sound we can provide if we are blocked some
       // time in the middle of this cycle.
       TrackTicks toWrite = 0;
       if (end >= aTo) {
         toWrite = ticksNeeded;
       } else {
-        toWrite = TimeToTicksRoundDown(IdealAudioRate(), end - aFrom);
+        toWrite = TimeToTicksRoundDown(mSampleRate, end - aFrom);
       }
 
       if (blocked) {
         output.InsertNullDataAtStart(toWrite);
         STREAM_LOG(PR_LOG_DEBUG+1, ("MediaStream %p writing %ld blocking-silence samples for %f to %f (%ld to %ld)\n",
                                     aStream, toWrite, MediaTimeToSeconds(t), MediaTimeToSeconds(end),
                                     offset, offset + toWrite));
         ticksNeeded -= toWrite;
@@ -1279,33 +1280,18 @@ MediaStreamGraphImpl::RunThread()
       }
     }
     messageQueue.Clear();
 
     if (mStreamOrderDirty) {
       UpdateStreamOrder();
     }
 
-    TrackRate sampleRate;
-    // Find the sampling rate that we need to use for non-realtime graphs.
-    if (!mRealtime) {
-      for (uint32_t i = 0; i < mStreams.Length(); ++i) {
-        AudioNodeStream* n = mStreams[i]->AsAudioNodeStream();
-        if (n) {
-          // We know that the rest of the streams will run at the same rate.
-          sampleRate = n->SampleRate();
-          break;
-        }
-      }
-    } else {
-      sampleRate = IdealAudioRate();
-    }
-
     GraphTime endBlockingDecisions =
-      RoundUpToNextAudioBlock(sampleRate, mCurrentTime + MillisecondsToMediaTime(AUDIO_TARGET_MS));
+      RoundUpToNextAudioBlock(mSampleRate, mCurrentTime + MillisecondsToMediaTime(AUDIO_TARGET_MS));
     bool ensureNextIteration = false;
 
     // Grab pending stream input.
     for (uint32_t i = 0; i < mStreams.Length(); ++i) {
       SourceMediaStream* is = mStreams[i]->AsSourceStream();
       if (is) {
         UpdateConsumptionState(is);
         ExtractPendingInput(is, endBlockingDecisions, &ensureNextIteration);
@@ -1914,17 +1900,18 @@ MediaStream::GetProcessingGraphUpdateInd
 StreamBuffer::Track*
 MediaStream::EnsureTrack(TrackID aTrackId, TrackRate aSampleRate)
 {
   StreamBuffer::Track* track = mBuffer.FindTrack(aTrackId);
   if (!track) {
     nsAutoPtr<MediaSegment> segment(new AudioSegment());
     for (uint32_t j = 0; j < mListeners.Length(); ++j) {
       MediaStreamListener* l = mListeners[j];
-      l->NotifyQueuedTrackChanges(Graph(), aTrackId, IdealAudioRate(), 0,
+      l->NotifyQueuedTrackChanges(Graph(), aTrackId,
+                                  GraphImpl()->AudioSampleRate(), 0,
                                   MediaStreamListener::TRACK_EVENT_CREATED,
                                   *segment);
     }
     track = &mBuffer.AddTrack(aTrackId, aSampleRate, 0, segment.forget());
   }
   return track;
 }
 
@@ -2264,39 +2251,39 @@ SourceMediaStream::AddTrack(TrackID aID,
                             MediaSegment* aSegment)
 {
   MutexAutoLock lock(mMutex);
   TrackData* data = mUpdateTracks.AppendElement();
   data->mID = aID;
   data->mInputRate = aRate;
   // We resample all audio input tracks to the sample rate of the audio mixer.
   data->mOutputRate = aSegment->GetType() == MediaSegment::AUDIO ?
-                      IdealAudioRate() : aRate;
+                      GraphImpl()->AudioSampleRate() : aRate;
   data->mStart = aStart;
   data->mCommands = TRACK_CREATE;
   data->mData = aSegment;
   data->mHaveEnough = false;
   if (!mDestroyed) {
     GraphImpl()->EnsureNextIteration();
   }
 }
 
 void
 SourceMediaStream::ResampleAudioToGraphSampleRate(TrackData* aTrackData, MediaSegment* aSegment)
 {
   if (aSegment->GetType() != MediaSegment::AUDIO ||
-      aTrackData->mInputRate == IdealAudioRate()) {
+      aTrackData->mInputRate == GraphImpl()->AudioSampleRate()) {
     return;
   }
   AudioSegment* segment = static_cast<AudioSegment*>(aSegment);
   if (!aTrackData->mResampler) {
     int channels = segment->ChannelCount();
     SpeexResamplerState* state = speex_resampler_init(channels,
                                                       aTrackData->mInputRate,
-                                                      IdealAudioRate(),
+                                                      GraphImpl()->AudioSampleRate(),
                                                       SPEEX_RESAMPLER_QUALITY_DEFAULT,
                                                       nullptr);
     if (state) {
       aTrackData->mResampler.own(state);
     }
   }
   segment->ResampleChunks(aTrackData->mResampler);
 }
@@ -2634,25 +2621,26 @@ ProcessedMediaStream::DestroyImpl()
 }
 
 /**
  * We make the initial mCurrentTime nonzero so that zero times can have
  * special meaning if necessary.
  */
 static const int32_t INITIAL_CURRENT_TIME = 1;
 
-MediaStreamGraphImpl::MediaStreamGraphImpl(bool aRealtime)
+MediaStreamGraphImpl::MediaStreamGraphImpl(bool aRealtime, TrackRate aSampleRate)
   : mCurrentTime(INITIAL_CURRENT_TIME)
   , mStateComputedTime(INITIAL_CURRENT_TIME)
   , mProcessingGraphUpdateIndex(0)
   , mPortCount(0)
   , mMonitor("MediaStreamGraphImpl")
   , mLifecycleState(LIFECYCLE_THREAD_NOT_STARTED)
   , mWaitState(WAITSTATE_RUNNING)
   , mEndTime(GRAPH_TIME_MAX)
+  , mSampleRate(aSampleRate)
   , mNeedAnotherIteration(false)
   , mForceShutDown(false)
   , mPostedRunInStableStateEvent(false)
   , mDetectedNotRunning(false)
   , mPostedRunInStableState(false)
   , mRealtime(aRealtime)
   , mNonRealtimeProcessing(false)
   , mStreamOrderDirty(false)
@@ -2709,32 +2697,32 @@ MediaStreamGraph::GetInstance()
   NS_ASSERTION(NS_IsMainThread(), "Main thread only");
 
   if (!gGraph) {
     if (!gShutdownObserverRegistered) {
       gShutdownObserverRegistered = true;
       nsContentUtils::RegisterShutdownObserver(new MediaStreamGraphShutdownObserver());
     }
 
-    gGraph = new MediaStreamGraphImpl(true);
+    AudioStream::InitPreferredSampleRate();
+
+    gGraph = new MediaStreamGraphImpl(true, AudioStream::PreferredSampleRate());
 
     STREAM_LOG(PR_LOG_DEBUG, ("Starting up MediaStreamGraph %p", gGraph));
-
-    AudioStream::InitPreferredSampleRate();
   }
 
   return gGraph;
 }
 
 MediaStreamGraph*
-MediaStreamGraph::CreateNonRealtimeInstance()
+MediaStreamGraph::CreateNonRealtimeInstance(TrackRate aSampleRate)
 {
   NS_ASSERTION(NS_IsMainThread(), "Main thread only");
 
-  MediaStreamGraphImpl* graph = new MediaStreamGraphImpl(false);
+  MediaStreamGraphImpl* graph = new MediaStreamGraphImpl(false, aSampleRate);
 
   return graph;
 }
 
 void
 MediaStreamGraph::DestroyNonRealtimeInstance(MediaStreamGraph* aGraph)
 {
   NS_ASSERTION(NS_IsMainThread(), "Main thread only");
--- a/content/media/MediaStreamGraph.h
+++ b/content/media/MediaStreamGraph.h
@@ -785,17 +785,18 @@ public:
   };
   /**
    * Data for each track that hasn't ended.
    */
   struct TrackData {
     TrackID mID;
     // Sample rate of the input data.
     TrackRate mInputRate;
-    // Sample rate of the output data, always equal to IdealAudioRate()
+    // Sample rate of the output data, always equal to the sample rate of the
+    // graph.
     TrackRate mOutputRate;
     // Resampler if the rate of the input track does not match the
     // MediaStreamGraph's.
     nsAutoRef<SpeexResamplerState> mResampler;
     TrackTicks mStart;
     // Each time the track updates are flushed to the media graph thread,
     // this is cleared.
     uint32_t mCommands;
@@ -1073,34 +1074,31 @@ protected:
   // The list of all inputs that are currently enabled or waiting to be enabled.
   nsTArray<MediaInputPort*> mInputs;
   bool mAutofinish;
   // True if and only if this stream is in a cycle.
   // Updated by MediaStreamGraphImpl::UpdateStreamOrder.
   bool mInCycle;
 };
 
-// Returns ideal audio rate for processing.
-inline TrackRate IdealAudioRate() { return AudioStream::PreferredSampleRate(); }
-
 /**
  * Initially, at least, we will have a singleton MediaStreamGraph per
  * process.  Each OfflineAudioContext object creates its own MediaStreamGraph
  * object too.
  */
 class MediaStreamGraph {
 public:
   // We ensure that the graph current time advances in multiples of
-  // IdealAudioBlockSize()/IdealAudioRate(). A stream that never blocks
-  // and has a track with the ideal audio rate will produce audio in
-  // multiples of the block size.
+  // IdealAudioBlockSize()/AudioStream::PreferredSampleRate(). A stream that
+  // never blocks and has a track with the ideal audio rate will produce audio
+  // in multiples of the block size.
 
   // Main thread only
   static MediaStreamGraph* GetInstance();
-  static MediaStreamGraph* CreateNonRealtimeInstance();
+  static MediaStreamGraph* CreateNonRealtimeInstance(TrackRate aSampleRate);
   // Idempotent
   static void DestroyNonRealtimeInstance(MediaStreamGraph* aGraph);
 
   // Control API.
   /**
    * Create a stream that a media decoder (or some other source of
    * media data, such as a camera) can write to.
    */
--- a/content/media/MediaStreamGraphImpl.h
+++ b/content/media/MediaStreamGraphImpl.h
@@ -115,17 +115,17 @@ public:
 
   /**
    * Set aRealtime to true in order to create a MediaStreamGraph which provides
    * support for real-time audio and video.  Set it to false in order to create
    * a non-realtime instance which just churns through its inputs and produces
    * output.  Those objects currently only support audio, and are used to
    * implement OfflineAudioContext.  They do not support MediaStream inputs.
    */
-  explicit MediaStreamGraphImpl(bool aRealtime);
+  explicit MediaStreamGraphImpl(bool aRealtime, TrackRate aSampleRate);
 
   /**
    * Unregisters memory reporting and deletes this instance. This should be
    * called instead of calling the destructor directly.
    */
   void Destroy();
 
   // Main thread only.
@@ -387,16 +387,18 @@ public:
    * Pause all AudioStreams being written to by MediaStreams
    */
   void PauseAllAudioOutputs();
   /**
    * Resume all AudioStreams being written to by MediaStreams
    */
   void ResumeAllAudioOutputs();
 
+  TrackRate AudioSampleRate() { return mSampleRate; }
+
   // Data members
 
   /**
    * Media graph thread.
    * Readonly after initialization on the main thread.
    */
   nsCOMPtr<nsIThread> mThread;
 
@@ -526,16 +528,23 @@ public:
     // but it hasn't done so yet
     WAITSTATE_WAKING_UP
   };
   WaitState mWaitState;
   /**
    * The graph should stop processing at or after this time.
    */
   GraphTime mEndTime;
+
+  /**
+   * Sample rate at which this graph runs. For real time graphs, this is
+   * the rate of the audio mixer. For offline graphs, this is the rate specified
+   * at construction.
+   */
+  TrackRate mSampleRate;
   /**
    * True when another iteration of the control loop is required.
    */
   bool mNeedAnotherIteration;
   /**
    * True when we need to do a forced shutdown during application shutdown.
    */
   bool mForceShutDown;
--- a/content/media/compiledtest/TestAudioMixer.cpp
+++ b/content/media/compiledtest/TestAudioMixer.cpp
@@ -6,17 +6,17 @@
 #include "AudioMixer.h"
 #include <assert.h>
 
 using mozilla::AudioDataValue;
 using mozilla::AudioSampleFormat;
 
 /* In this test, the different audio stream and channels are always created to
  * cancel each other. */
-void MixingDone(AudioDataValue* aData, AudioSampleFormat aFormat, uint32_t aChannels, uint32_t aFrames)
+void MixingDone(AudioDataValue* aData, AudioSampleFormat aFormat, uint32_t aChannels, uint32_t aFrames, uint32_t aSampleRate)
 {
   bool silent = true;
   for (uint32_t i = 0; i < aChannels * aFrames; i++) {
     if (aData[i] != 0.0) {
       if (aFormat == mozilla::AUDIO_FORMAT_S16) {
         fprintf(stderr, "Sample at %d is not silent: %d\n", i, (short)aData[i]);
       } else {
         fprintf(stderr, "Sample at %d is not silent: %f\n", i, (float)aData[i]);
@@ -62,94 +62,95 @@ void FillBuffer(AudioDataValue* aBuffer,
   AudioDataValue* end = aBuffer + aLength;
   while (aBuffer != end) {
     *aBuffer++ = aValue;
   }
 }
 
 int main(int argc, char* argv[]) {
   const uint32_t CHANNEL_LENGTH = 256;
+  const uint32_t AUDIO_RATE = 44100;
   AudioDataValue a[CHANNEL_LENGTH * 2];
   AudioDataValue b[CHANNEL_LENGTH * 2];
   FillBuffer(a, CHANNEL_LENGTH, GetLowValue<AudioDataValue>());
   FillBuffer(a + CHANNEL_LENGTH, CHANNEL_LENGTH, GetHighValue<AudioDataValue>());
   FillBuffer(b, CHANNEL_LENGTH, GetHighValue<AudioDataValue>());
   FillBuffer(b + CHANNEL_LENGTH, CHANNEL_LENGTH, GetLowValue<AudioDataValue>());
 
   {
     int iterations = 2;
     mozilla::AudioMixer mixer(MixingDone);
 
     fprintf(stderr, "Test AudioMixer constant buffer length.\n");
 
     while (iterations--) {
-      mixer.Mix(a, 2, CHANNEL_LENGTH);
-      mixer.Mix(b, 2, CHANNEL_LENGTH);
+      mixer.Mix(a, 2, CHANNEL_LENGTH, AUDIO_RATE);
+      mixer.Mix(b, 2, CHANNEL_LENGTH, AUDIO_RATE);
       mixer.FinishMixing();
     }
   }
 
   {
     mozilla::AudioMixer mixer(MixingDone);
 
     fprintf(stderr, "Test AudioMixer variable buffer length.\n");
 
     FillBuffer(a, CHANNEL_LENGTH / 2, GetLowValue<AudioDataValue>());
     FillBuffer(a + CHANNEL_LENGTH / 2, CHANNEL_LENGTH / 2, GetLowValue<AudioDataValue>());
     FillBuffer(b, CHANNEL_LENGTH / 2, GetHighValue<AudioDataValue>());
     FillBuffer(b + CHANNEL_LENGTH / 2, CHANNEL_LENGTH / 2, GetHighValue<AudioDataValue>());
-    mixer.Mix(a, 2, CHANNEL_LENGTH / 2);
-    mixer.Mix(b, 2, CHANNEL_LENGTH / 2);
+    mixer.Mix(a, 2, CHANNEL_LENGTH / 2, AUDIO_RATE);
+    mixer.Mix(b, 2, CHANNEL_LENGTH / 2, AUDIO_RATE);
     mixer.FinishMixing();
     FillBuffer(a, CHANNEL_LENGTH, GetLowValue<AudioDataValue>());
     FillBuffer(a + CHANNEL_LENGTH, CHANNEL_LENGTH, GetHighValue<AudioDataValue>());
     FillBuffer(b, CHANNEL_LENGTH, GetHighValue<AudioDataValue>());
     FillBuffer(b + CHANNEL_LENGTH, CHANNEL_LENGTH, GetLowValue<AudioDataValue>());
-    mixer.Mix(a, 2, CHANNEL_LENGTH);
-    mixer.Mix(b, 2, CHANNEL_LENGTH);
+    mixer.Mix(a, 2, CHANNEL_LENGTH, AUDIO_RATE);
+    mixer.Mix(b, 2, CHANNEL_LENGTH, AUDIO_RATE);
     mixer.FinishMixing();
     FillBuffer(a, CHANNEL_LENGTH / 2, GetLowValue<AudioDataValue>());
     FillBuffer(a + CHANNEL_LENGTH / 2, CHANNEL_LENGTH / 2, GetLowValue<AudioDataValue>());
     FillBuffer(b, CHANNEL_LENGTH / 2, GetHighValue<AudioDataValue>());
     FillBuffer(b + CHANNEL_LENGTH / 2, CHANNEL_LENGTH / 2, GetHighValue<AudioDataValue>());
-    mixer.Mix(a, 2, CHANNEL_LENGTH / 2);
-    mixer.Mix(b, 2, CHANNEL_LENGTH / 2);
+    mixer.Mix(a, 2, CHANNEL_LENGTH / 2, AUDIO_RATE);
+    mixer.Mix(b, 2, CHANNEL_LENGTH / 2, AUDIO_RATE);
     mixer.FinishMixing();
   }
 
   FillBuffer(a, CHANNEL_LENGTH, GetLowValue<AudioDataValue>());
   FillBuffer(b, CHANNEL_LENGTH, GetHighValue<AudioDataValue>());
 
   {
     mozilla::AudioMixer mixer(MixingDone);
     fprintf(stderr, "Test AudioMixer variable channel count.\n");
 
-    mixer.Mix(a, 1, CHANNEL_LENGTH);
-    mixer.Mix(b, 1, CHANNEL_LENGTH);
+    mixer.Mix(a, 1, CHANNEL_LENGTH, AUDIO_RATE);
+    mixer.Mix(b, 1, CHANNEL_LENGTH, AUDIO_RATE);
     mixer.FinishMixing();
-    mixer.Mix(a, 1, CHANNEL_LENGTH);
-    mixer.Mix(b, 1, CHANNEL_LENGTH);
+    mixer.Mix(a, 1, CHANNEL_LENGTH, AUDIO_RATE);
+    mixer.Mix(b, 1, CHANNEL_LENGTH, AUDIO_RATE);
     mixer.FinishMixing();
-    mixer.Mix(a, 1, CHANNEL_LENGTH);
-    mixer.Mix(b, 1, CHANNEL_LENGTH);
+    mixer.Mix(a, 1, CHANNEL_LENGTH, AUDIO_RATE);
+    mixer.Mix(b, 1, CHANNEL_LENGTH, AUDIO_RATE);
     mixer.FinishMixing();
   }
 
   {
     mozilla::AudioMixer mixer(MixingDone);
     fprintf(stderr, "Test AudioMixer variable stream count.\n");
 
-    mixer.Mix(a, 2, CHANNEL_LENGTH);
-    mixer.Mix(b, 2, CHANNEL_LENGTH);
+    mixer.Mix(a, 2, CHANNEL_LENGTH, AUDIO_RATE);
+    mixer.Mix(b, 2, CHANNEL_LENGTH, AUDIO_RATE);
     mixer.FinishMixing();
-    mixer.Mix(a, 2, CHANNEL_LENGTH);
-    mixer.Mix(b, 2, CHANNEL_LENGTH);
-    mixer.Mix(a, 2, CHANNEL_LENGTH);
-    mixer.Mix(b, 2, CHANNEL_LENGTH);
+    mixer.Mix(a, 2, CHANNEL_LENGTH, AUDIO_RATE);
+    mixer.Mix(b, 2, CHANNEL_LENGTH, AUDIO_RATE);
+    mixer.Mix(a, 2, CHANNEL_LENGTH, AUDIO_RATE);
+    mixer.Mix(b, 2, CHANNEL_LENGTH, AUDIO_RATE);
     mixer.FinishMixing();
-    mixer.Mix(a, 2, CHANNEL_LENGTH);
-    mixer.Mix(b, 2, CHANNEL_LENGTH);
+    mixer.Mix(a, 2, CHANNEL_LENGTH, AUDIO_RATE);
+    mixer.Mix(b, 2, CHANNEL_LENGTH, AUDIO_RATE);
     mixer.FinishMixing();
   }
 
   return 0;
 }
 
--- a/content/media/omx/OMXCodecWrapper.cpp
+++ b/content/media/omx/OMXCodecWrapper.cpp
@@ -413,25 +413,28 @@ OMXVideoEncoder::AppendFrame(nsTArray<ui
 nsresult
 OMXVideoEncoder::GetCodecConfig(nsTArray<uint8_t>* aOutputBuf)
 {
   MOZ_ASSERT(mHasConfigBlob, "Haven't received codec config yet.");
 
   return AppendDecoderConfig(aOutputBuf, nullptr) == OK ? NS_OK : NS_ERROR_FAILURE;
 }
 
+// MediaCodec::setParameters() is available only after API level 18.
+#if ANDROID_VERSION >= 18
 nsresult
 OMXVideoEncoder::SetBitrate(int32_t aKbps)
 {
   sp<AMessage> msg = new AMessage();
   msg->setInt32("videoBitrate", aKbps * 1000 /* kbps -> bps */);
   status_t result = mCodec->setParameters(msg);
   MOZ_ASSERT(result == OK);
   return result == OK ? NS_OK : NS_ERROR_FAILURE;
 }
+#endif
 
 nsresult
 OMXAudioEncoder::Configure(int aChannels, int aInputSampleRate,
                            int aEncodedSampleRate)
 {
   MOZ_ASSERT(!mStarted);
 
   NS_ENSURE_TRUE(aChannels > 0 && aInputSampleRate > 0 && aEncodedSampleRate >= 0,
--- a/content/media/omx/OMXCodecWrapper.h
+++ b/content/media/omx/OMXCodecWrapper.h
@@ -262,18 +262,20 @@ public:
    * Encode a aWidth pixels wide and aHeight pixels tall video frame of
    * semi-planar YUV420 format stored in the buffer of aImage. aTimestamp gives
    * the frame timestamp/presentation time (in microseconds). To notify end of
    * stream, set aInputFlags to BUFFER_EOS.
    */
   nsresult Encode(const mozilla::layers::Image* aImage, int aWidth, int aHeight,
                   int64_t aTimestamp, int aInputFlags = 0);
 
+#if ANDROID_VERSION >= 18
   /** Set encoding bitrate (in kbps). */
   nsresult SetBitrate(int32_t aKbps);
+#endif
 
   /**
    * Get current AVC codec config blob. The output format depends on the
    * aBlobFormat argument given when Configure() was called.
    */
   nsresult GetCodecConfig(nsTArray<uint8_t>* aOutputBuf);
 
 protected:
--- a/content/media/test/cancellable_request.sjs
+++ b/content/media/test/cancellable_request.sjs
@@ -78,17 +78,17 @@ function handleRequest(request, response
   if (cancel) {
     setState(cancel[1], "cancelled");
     response.setStatusLine(request.httpVersion, 200, "OK");
     response.write("Cancel approved!");
     return;
   }
 
   var samples = [];
-  for (var i = 0; i < 100000; ++i) {
+  for (var i = 0; i < 1000000; ++i) {
     samples.push(0);
   }
   var bytes = buildWave(samples, 44100).join("");
 
   var key = parseQuery(request, "key");
   response.setHeader("Content-Type", "audio/x-wav");
   response.setHeader("Content-Length", ""+bytes.length, false);
 
@@ -132,20 +132,20 @@ function handleRequest(request, response
   }
   
   if (start > 0) {
     // Send all requested data
     out.write(bytes.slice(start, end + 1), end + 1 - start);
     return;
   }
 
-  // Write the first 120K of the Wave file. We know the cache size is set to
+  // Write the first 1.2M of the Wave file. We know the cache size is set to
   // 100K so this will fill the cache and and cause a "suspend" event on
   // the loading element.
-  out.write(bytes, 120000);
+  out.write(bytes, 1200000);
 
   response.processAsync();
   // Now wait for the message to cancel this response
   poll(function() {
     if (getState(key[1]) != "cancelled") {
       return false;
     }
     response.finish();
--- a/content/media/webaudio/AudioDestinationNode.cpp
+++ b/content/media/webaudio/AudioDestinationNode.cpp
@@ -239,17 +239,17 @@ AudioDestinationNode::AudioDestinationNo
   , mAudioChannel(AudioChannel::Normal)
   , mIsOffline(aIsOffline)
   , mHasFinished(false)
   , mExtraCurrentTime(0)
   , mExtraCurrentTimeSinceLastStartedBlocking(0)
   , mExtraCurrentTimeUpdatedSinceLastStableState(false)
 {
   MediaStreamGraph* graph = aIsOffline ?
-                            MediaStreamGraph::CreateNonRealtimeInstance() :
+                            MediaStreamGraph::CreateNonRealtimeInstance(aSampleRate) :
                             MediaStreamGraph::GetInstance();
   AudioNodeEngine* engine = aIsOffline ?
                             new OfflineDestinationNodeEngine(this, aNumberOfChannels,
                                                              aLength, aSampleRate) :
                             static_cast<AudioNodeEngine*>(new DestinationNodeEngine(this));
 
   mStream = graph->CreateAudioNodeStream(engine, MediaStreamGraph::EXTERNAL_STREAM);
   mStream->AddMainThreadListener(this);
--- a/dom/apps/moz.build
+++ b/dom/apps/moz.build
@@ -1,8 +1,13 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 PARALLEL_DIRS += ['src']
-TEST_DIRS += ['tests']
+
+XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
+
+MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
+
+MOCHITEST_CHROME_MANIFESTS += ['tests/chrome.ini']
deleted file mode 100644
--- a/dom/apps/tests/moz.build
+++ /dev/null
@@ -1,13 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
-
-MOCHITEST_MANIFESTS += ['mochitest.ini']
-
-MOCHITEST_CHROME_MANIFESTS += ['chrome.ini']
-
-DIRS += ['signed']
deleted file mode 100644
--- a/dom/apps/tests/signed/moz.build
+++ /dev/null
@@ -1,5 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
--- a/dom/base/URL.cpp
+++ b/dom/base/URL.cpp
@@ -342,16 +342,18 @@ void
 URL::GetHostname(nsString& aHostname) const
 {
   URL_GETTER(aHostname, GetHost);
 }
 
 void
 URL::SetHostname(const nsAString& aHostname)
 {
+  // nsStandardURL returns NS_ERROR_UNEXPECTED for an empty hostname
+  // The return code is silently ignored
   mURI->SetHost(NS_ConvertUTF16toUTF8(aHostname));
 }
 
 void
 URL::GetPort(nsString& aPort) const
 {
   aPort.Truncate();
 
--- a/dom/base/test/test_url.html
+++ b/dom/base/test/test_url.html
@@ -1,22 +1,21 @@
 
 <!DOCTYPE HTML>
 <html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=887364
--->
 <head>
   <meta charset="utf-8">
-  <title>Test for Bug 887364</title>
+  <title>Test URL API</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=887364">Mozilla Bug 887364</a>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=991471">Mozilla Bug 991471</a>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=996055">Mozilla Bug 996055</a>
 <p id="display"></p>
 <div id="content" style="display: none">
   <iframe name="x" id="x"></iframe>
   <iframe name="y" id="y"></iframe>
 </div>
 <pre id="test">
 </pre>
   <script type="application/javascript">
@@ -270,10 +269,28 @@ https://bugzilla.mozilla.org/show_bug.cg
     if ('pathname' in test) is(test.pathname, url.pathname, "pathname");
     if ('search' in test) is(test.search, url.search, "search");
     if ('hash' in test) is(test.hash, url.hash, "hash");
 
     if ('href' in test) is (test.href, url + '', 'stringify works');
   }
 
   </script>
+
+  <script>
+    /** Test for Bug 991471 **/
+    var url = new URL("http://localhost/");
+    url.hostname = "";
+    url.username = "tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt";
+    url.hostname = "www.mozilla.org";
+    url.username = "";
+    url.hostname = "www.mozilla.org";
+    is(url.href, "http://www.mozilla.org/", "No parsing error with empty host");
+  </script>
+
+  <script>
+    /** Test for Bug 996055 **/
+    var url = new URL("http://localhost/");
+    url.hostname = "";
+    is(url.href, "http://localhost/", "Empty hostname is ignored");
+  </script>
 </body>
 </html>
--- a/dom/events/test/mochitest.ini
+++ b/dom/events/test/mochitest.ini
@@ -109,16 +109,17 @@ skip-if = toolkit == 'android' #CRASH_DU
 [test_bug667612.html]
 skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
 [test_bug667919-1.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' #CRASH_DUMP, RANDOM # b2g(bug 900969, 5 tests) b2g-debug(bug 900969, 5 tests) b2g-desktop(bug 900969, 5 tests)
 [test_bug689564.html]
 skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
 [test_bug698929.html]
 skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
+[test_bug704423.html]
 [test_bug741666.html]
 skip-if = toolkit == 'android'
 [test_bug742376.html]
 [test_bug812744.html]
 [test_bug855741.html]
 [test_bug864040.html]
 skip-if = buildapp == 'b2g' # b2g(failing when the test gets moved around, and on debug) b2g-debug(failing when the test gets moved around, and on debug) b2g-desktop(failing when the test gets moved around, and on debug)
 [test_bug930374-content.html]
new file mode 100644
--- /dev/null
+++ b/dom/events/test/test_bug704423.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=704423
+-->
+<head>
+  <title>Test for Bug 704423</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=704423">Mozilla Bug 704423</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 704423 **/
+
+function doTest()
+{
+  function handler(aEvent) {
+    aEvent.preventDefault();
+    ok(aEvent.defaultPrevented,
+       "mousemove event should be cancelable");
+  }
+  window.addEventListener("mousemove", handler, true);
+  synthesizeMouseAtCenter(document.body, { type: "mousemove" });
+  window.removeEventListener("mousemove", handler, true);
+
+  SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(doTest);
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/tests/mochitest/webapps/head.js
+++ b/dom/tests/mochitest/webapps/head.js
@@ -1,40 +1,26 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-/**
- * DOMApplicationRegistry._isLaunchable() sometimes returns false right after
- * installation on Mac, perhaps because of a race condition between
- * WebappsInstaller and nsIMacWebAppUtils::pathForAppWithIdentifier().
- * That causes methods like mgmt.getAll() to exclude the app from their results,
- * even though the app is registered and installed.
- *
- * To work around this problem, set DOMApplicationRegistry.allAppsLaunchable
- * to true, which makes _isLaunchable() return true for all registered apps.
- */
-function makeAllAppsLaunchable() {
-  var Webapps = {};
-  Components.utils.import("resource://gre/modules/Webapps.jsm", Webapps);
-  var originalValue = Webapps.DOMApplicationRegistry.allAppsLaunchable;
-  Webapps.DOMApplicationRegistry.allAppsLaunchable = true;
-
-  // Clean up after ourselves once tests are done so the test page is unloaded.
-  window.addEventListener("unload", function restoreAllAppsLaunchable(event) {
-    if (event.target == window.document) {
-      window.removeEventListener("unload", restoreAllAppsLaunchable, false);
-      Webapps.DOMApplicationRegistry.allAppsLaunchable = originalValue;
-    }
-  }, false);
-}
-
 function runAll(steps) {
   SimpleTest.waitForExplicitFinish();
 
-  makeAllAppsLaunchable();
+  /**
+   * On Mac, apps aren't considered launchable right after they've been
+   * installed because the OS takes some time to detect them (so
+   * nsIMacWebAppUtils::pathForAppWithIdentifier() returns null).
+   * That causes methods like mgmt.getAll() to exclude the app from their
+   * results, even though the app is installed and is in the registry.
+   * See the tests under toolkit/webapps for a viable solution.
+   *
+   * To work around this problem, set allAppsLaunchable to true, which makes
+   * all apps considered as launchable.
+   */
+  SpecialPowers.setAllAppsLaunchable(true);
 
   // Clone the array so we don't modify the original.
   steps = steps.concat();
   function next() {
     if (steps.length) {
       steps.shift()(next);
     }
     else {
--- a/dom/tests/mochitest/webapps/test_bug_765063.xul
+++ b/dom/tests/mochitest/webapps/test_bug_765063.xul
@@ -15,17 +15,17 @@
   <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=741549"
      target="_blank">Mozilla Bug 765063</a>
   </body>
 
 <script> 
 
 SimpleTest.waitForExplicitFinish();
 
-makeAllAppsLaunchable();
+SpecialPowers.setAllAppsLaunchable(true);
 
 var mmListener = {
   receiveMessage: function(aMessage) {
     ppmm.removeMessageListener("Webapps:Install", mmListener);
 
     var msg = aMessage.json;
     var ioService = Components.classes["@mozilla.org/network/io-service;1"]
                                       .getService(Components.interfaces.nsIIOService);
--- a/dom/tests/mochitest/webapps/test_bug_771294.xul
+++ b/dom/tests/mochitest/webapps/test_bug_771294.xul
@@ -17,17 +17,17 @@
   </body>
 
 <script>
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 SimpleTest.waitForExplicitFinish();
 
-makeAllAppsLaunchable();
+SpecialPowers.setAllAppsLaunchable(true);
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/PopupNotifications.jsm");
 
 let blocked = true;
 
 function blockedListener() {
   blocked = false;
--- a/dom/wifi/WifiProxyService.cpp
+++ b/dom/wifi/WifiProxyService.cpp
@@ -5,16 +5,21 @@
 #include "WifiProxyService.h"
 #include "nsServiceManagerUtils.h"
 #include "mozilla/ModuleUtils.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "nsXULAppAPI.h"
 #include "WifiUtils.h"
 #include "nsCxPusher.h"
 
+#ifdef MOZ_TASK_TRACER
+#include "GeckoTaskTracer.h"
+using namespace mozilla::tasktracer;
+#endif
+
 #define NS_WIFIPROXYSERVICE_CID \
   { 0xc6c9be7e, 0x744f, 0x4222, {0xb2, 0x03, 0xcd, 0x55, 0xdf, 0xc8, 0xbc, 0x12} }
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 namespace mozilla {
 
@@ -58,16 +63,22 @@ public:
   }
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(!NS_IsMainThread());
     nsAutoString event;
     gWpaSupplicant->WaitForEvent(event, mInterface);
     if (!event.IsEmpty()) {
+#ifdef MOZ_TASK_TRACER
+      // Make wifi initialization events to be the source events of TaskTracer,
+      // and originate the rest correlation tasks from here.
+      AutoSourceEvent taskTracerEvent(SourceEventType::WIFI);
+      AddLabel("%s %s", mInterface.get(), NS_ConvertUTF16toUTF8(event).get());
+#endif
       nsCOMPtr<nsIRunnable> runnable = new WifiEventDispatcher(event, mInterface);
       NS_DispatchToMainThread(runnable);
     }
     return NS_OK;
   }
 
 private:
   nsCString mInterface;
--- a/editor/composer/src/nsEditingSession.cpp
+++ b/editor/composer/src/nsEditingSession.cpp
@@ -443,17 +443,17 @@ nsEditingSession::SetupEditorOnWindow(ns
   NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE);
 
   // Set up as a doc state listener
   // Important! We must have this to broadcast the "obs_documentCreated" message
   rv = editor->AddDocumentStateListener(mStateMaintainer);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = editor->Init(domDoc, nullptr /* root content */,
-                    nullptr, mEditorFlags);
+                    nullptr, mEditorFlags, EmptyString());
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsISelection> selection;
   editor->GetSelection(getter_AddRefs(selection));
   nsCOMPtr<nsISelectionPrivate> selPriv = do_QueryInterface(selection);
   NS_ENSURE_TRUE(selPriv, NS_ERROR_FAILURE);
 
   rv = selPriv->AddSelectionListener(mStateMaintainer);
--- a/editor/idl/nsIEditor.idl
+++ b/editor/idl/nsIEditor.idl
@@ -16,17 +16,17 @@ interface nsIDocumentStateListener;
 interface nsIOutputStream;
 interface nsITransactionManager;
 interface nsITransaction;
 interface nsIEditorObserver;
 interface nsIEditActionListener;
 interface nsIInlineSpellChecker;
 interface nsITransferable;
 
-[scriptable, uuid(753b38d1-ee03-4e58-a650-1076ccccdb7f)]
+[scriptable, uuid(65523eab-db1f-44aa-893e-dfe57ad306f0)]
 
 interface nsIEditor  : nsISupports
 {
 %{C++
   typedef short EDirection;
   typedef short EStripWrappers;
 %}
   const short eNone = 0;
@@ -51,17 +51,18 @@ interface nsIEditor  : nsISupports
    * @param aSelCon       this should be used to get the selection location
    *                      (will be null for HTML editors)
    * @param aFlags        A bitmask of flags for specifying the behavior
    *                      of the editor.
    */
   [noscript] void init(in nsIDOMDocument doc,
                        in nsIContent aRoot,
                        in nsISelectionController aSelCon,
-                       in unsigned long aFlags);
+                       in unsigned long aFlags,
+                       in AString initialValue);
 
   void setAttributeOrEquivalent(in nsIDOMElement element,
                                 in AString sourceAttrName,
                                 in AString sourceAttrValue,
                                 in boolean aSuppressTransaction);
   void removeAttributeOrEquivalent(in nsIDOMElement element,
                                    in DOMString sourceAttrName,
                                    in boolean aSuppressTransaction);
--- a/editor/libeditor/base/nsEditRules.h
+++ b/editor/libeditor/base/nsEditRules.h
@@ -1,20 +1,19 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsEditRules_h__
 #define nsEditRules_h__
 
-// FB45AC36-E8F1-44ae-8FB7-466E1BE119B0
 #define NS_IEDITRULES_IID \
-{ 0x2cc50d11, 0x9909, 0x433f, \
-  { 0xb6, 0xfb, 0x4c, 0xf2, 0x56, 0xe5, 0xe5, 0x71 } }
+{ 0x3836386d, 0x806a, 0x488d, \
+  { 0x8b, 0xab, 0xaf, 0x42, 0xbb, 0x4c, 0x90, 0x66 } }
 
 #include "nsEditor.h"
 
 class nsPlaintextEditor;
 class nsISelection;
 
 /***************************************************************************
  * base for an object to encapsulate any additional info needed to be passed
@@ -38,16 +37,17 @@ class nsIEditRules : public nsISupports
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IEDITRULES_IID)
   
 //Interfaces for addref and release and queryinterface
 //NOTE: Use   NS_DECL_ISUPPORTS_INHERITED in any class inherited from nsIEditRules
 
   NS_IMETHOD Init(nsPlaintextEditor *aEditor)=0;
+  NS_IMETHOD SetInitialValue(const nsAString& aValue) = 0;
   NS_IMETHOD DetachEditor()=0;
   NS_IMETHOD BeforeEdit(EditAction action,
                         nsIEditor::EDirection aDirection) = 0;
   NS_IMETHOD AfterEdit(EditAction action,
                        nsIEditor::EDirection aDirection) = 0;
   NS_IMETHOD WillDoAction(mozilla::Selection* aSelection, nsRulesInfo* aInfo,
                           bool* aCancel, bool* aHandled) = 0;
   NS_IMETHOD DidDoAction(nsISelection *aSelection, nsRulesInfo *aInfo, nsresult aResult)=0;
--- a/editor/libeditor/base/nsEditor.cpp
+++ b/editor/libeditor/base/nsEditor.cpp
@@ -199,17 +199,19 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditor)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsEditor)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsEditor)
 
 
 NS_IMETHODIMP
-nsEditor::Init(nsIDOMDocument *aDoc, nsIContent *aRoot, nsISelectionController *aSelCon, uint32_t aFlags)
+nsEditor::Init(nsIDOMDocument *aDoc, nsIContent *aRoot,
+               nsISelectionController *aSelCon, uint32_t aFlags,
+               const nsAString& aValue)
 {
   NS_PRECONDITION(aDoc, "bad arg");
   if (!aDoc)
     return NS_ERROR_NULL_POINTER;
 
   // First only set flags, but other stuff shouldn't be initialized now.
   // Don't move this call after initializing mDocWeak.
   // SetFlags() can check whether it's called during initialization or not by
--- a/editor/libeditor/html/nsHTMLEditor.cpp
+++ b/editor/libeditor/html/nsHTMLEditor.cpp
@@ -220,30 +220,32 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_
   NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
 NS_INTERFACE_MAP_END_INHERITING(nsPlaintextEditor)
 
 
 NS_IMETHODIMP
 nsHTMLEditor::Init(nsIDOMDocument *aDoc,
                    nsIContent *aRoot,
                    nsISelectionController *aSelCon,
-                   uint32_t aFlags)
+                   uint32_t aFlags,
+                   const nsAString& aInitialValue)
 {
   NS_PRECONDITION(aDoc && !aSelCon, "bad arg");
   NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER);
+  MOZ_ASSERT(aInitialValue.IsEmpty(), "Non-empty initial values not supported");
 
   nsresult result = NS_OK, rulesRes = NS_OK;
    
   if (1)
   {
     // block to scope nsAutoEditInitRulesTrigger
     nsAutoEditInitRulesTrigger rulesTrigger(static_cast<nsPlaintextEditor*>(this), rulesRes);
 
     // Init the plaintext editor
-    result = nsPlaintextEditor::Init(aDoc, aRoot, nullptr, aFlags);
+    result = nsPlaintextEditor::Init(aDoc, aRoot, nullptr, aFlags, aInitialValue);
     if (NS_FAILED(result)) { return result; }
 
     // Init mutation observer
     nsCOMPtr<nsINode> document = do_QueryInterface(aDoc);
     document->AddMutationObserverUnlessExists(this);
 
     // disable Composer-only features
     if (IsMailEditor())
@@ -3222,23 +3224,33 @@ NS_IMETHODIMP nsHTMLEditor::InsertTextIm
   return nsEditor::InsertTextImpl(aStringToInsert, aInOutNode, aInOutOffset, aDoc);
 }
 
 void
 nsHTMLEditor::ContentAppended(nsIDocument *aDocument, nsIContent* aContainer,
                               nsIContent* aFirstNewContent,
                               int32_t aIndexInContainer)
 {
-  ContentInserted(aDocument, aContainer, aFirstNewContent, aIndexInContainer);
+  DoContentInserted(aDocument, aContainer, aFirstNewContent, aIndexInContainer,
+                    eAppended);
 }
 
 void
 nsHTMLEditor::ContentInserted(nsIDocument *aDocument, nsIContent* aContainer,
                               nsIContent* aChild, int32_t aIndexInContainer)
 {
+  DoContentInserted(aDocument, aContainer, aChild, aIndexInContainer,
+                    eInserted);
+}
+
+void
+nsHTMLEditor::DoContentInserted(nsIDocument* aDocument, nsIContent* aContainer,
+                                nsIContent* aChild, int32_t aIndexInContainer,
+                                InsertedOrAppended aInsertedOrAppended)
+{
   if (!aChild) {
     return;
   }
 
   nsCOMPtr<nsIHTMLEditor> kungFuDeathGrip(this);
 
   if (ShouldReplaceRootElement()) {
     nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(
@@ -3252,18 +3264,27 @@ nsHTMLEditor::ContentInserted(nsIDocumen
     }
     // Protect the edit rules object from dying
     nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
     mRules->DocumentModified();
 
     // Update spellcheck for only the newly-inserted node (bug 743819)
     if (mInlineSpellChecker) {
       nsRefPtr<nsRange> range = new nsRange(aChild);
+      int32_t endIndex = aIndexInContainer + 1;
+      if (aInsertedOrAppended == eAppended) {
+        // Count all the appended nodes
+        nsIContent* sibling = aChild->GetNextSibling();
+        while (sibling) {
+          endIndex++;
+          sibling = sibling->GetNextSibling();
+        }
+      }
       nsresult res = range->Set(aContainer, aIndexInContainer,
-                                aContainer, aIndexInContainer + 1);
+                                aContainer, endIndex);
       if (NS_SUCCEEDED(res)) {
         mInlineSpellChecker->SpellCheckRange(range);
       }
     }
   }
 }
 
 void
--- a/editor/libeditor/html/nsHTMLEditor.h
+++ b/editor/libeditor/html/nsHTMLEditor.h
@@ -244,17 +244,19 @@ public:
                                   nsIContent** outNode = nullptr,
                                   int32_t* outOffset = 0);
 
   /* ------------ Overrides of nsEditor interface methods -------------- */
 
   nsresult EndUpdateViewBatch();
 
   /** prepare the editor for use */
-  NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIContent *aRoot, nsISelectionController *aSelCon, uint32_t aFlags);
+  NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIContent *aRoot,
+                  nsISelectionController *aSelCon, uint32_t aFlags,
+                  const nsAString& aValue);
   NS_IMETHOD PreDestroy(bool aDestroyingFrames);
 
   /** Internal, static version */
   // aElement must not be null.
   static bool NodeIsBlockStatic(const mozilla::dom::Element* aElement);
   static nsresult NodeIsBlockStatic(nsIDOMNode *aNode, bool *aIsBlock);
 protected:
   using nsEditor::IsBlockNode;
@@ -945,12 +947,15 @@ private:
   bool IsSimpleModifiableNode(nsIContent* aContent,
                               nsIAtom* aProperty,
                               const nsAString* aAttribute,
                               const nsAString* aValue);
   nsresult SetInlinePropertyOnNodeImpl(nsIContent* aNode,
                                        nsIAtom* aProperty,
                                        const nsAString* aAttribute,
                                        const nsAString* aValue);
-
+  typedef enum { eInserted, eAppended } InsertedOrAppended;
+  void DoContentInserted(nsIDocument* aDocument, nsIContent* aContainer,
+                         nsIContent* aChild, int32_t aIndexInContainer,
+                         InsertedOrAppended aInsertedOrAppended);
 };
 #endif //nsHTMLEditor_h__
 
--- a/editor/libeditor/text/nsPlaintextEditor.cpp
+++ b/editor/libeditor/text/nsPlaintextEditor.cpp
@@ -110,41 +110,48 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_
   NS_INTERFACE_MAP_ENTRY(nsIPlaintextEditor)
   NS_INTERFACE_MAP_ENTRY(nsIEditorMailSupport)
 NS_INTERFACE_MAP_END_INHERITING(nsEditor)
 
 
 NS_IMETHODIMP nsPlaintextEditor::Init(nsIDOMDocument *aDoc, 
                                       nsIContent *aRoot,
                                       nsISelectionController *aSelCon,
-                                      uint32_t aFlags)
+                                      uint32_t aFlags,
+                                      const nsAString& aInitialValue)
 {
   NS_PRECONDITION(aDoc, "bad arg");
   NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER);
   
   nsresult res = NS_OK, rulesRes = NS_OK;
   if (mRules) {
     mRules->DetachEditor();
     mRules = nullptr;
   }
   
-  if (1)
   {
     // block to scope nsAutoEditInitRulesTrigger
     nsAutoEditInitRulesTrigger rulesTrigger(this, rulesRes);
   
     // Init the base editor
-    res = nsEditor::Init(aDoc, aRoot, aSelCon, aFlags);
+    res = nsEditor::Init(aDoc, aRoot, aSelCon, aFlags, aInitialValue);
   }
 
   // check the "single line editor newline handling"
   // and "caret behaviour in selection" prefs
   GetDefaultEditorPrefs(mNewlineHandling, mCaretStyle);
 
   NS_ENSURE_SUCCESS(rulesRes, rulesRes);
+
+  // mRules may not have been initialized yet, when this is called via
+  // nsHTMLEditor::Init.
+  if (mRules) {
+    mRules->SetInitialValue(aInitialValue);
+  }
+
   return res;
 }
 
 static int32_t sNewlineHandlingPref = -1,
                sCaretStylePref = -1;
 
 static void
 EditorPrefsChangedCallback(const char *aPrefName, void *)
--- a/editor/libeditor/text/nsPlaintextEditor.h
+++ b/editor/libeditor/text/nsPlaintextEditor.h
@@ -68,17 +68,19 @@ public:
                                       const nsAString & aAttribute,
                                       const nsAString & aValue,
                                       bool aSuppressTransaction);
   NS_IMETHOD RemoveAttributeOrEquivalent(nsIDOMElement * aElement,
                                          const nsAString & aAttribute,
                                          bool aSuppressTransaction);
 
   /** prepare the editor for use */
-  NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIContent *aRoot, nsISelectionController *aSelCon, uint32_t aFlags);
+  NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIContent *aRoot,
+                  nsISelectionController *aSelCon, uint32_t aFlags,
+                  const nsAString& aValue);
   
   NS_IMETHOD GetDocumentIsEmpty(bool *aDocumentIsEmpty);
   NS_IMETHOD GetIsDocumentEditable(bool *aIsDocumentEditable);
 
   NS_IMETHOD DeleteSelection(EDirection aAction,
                              EStripWrappers aStripWrappers);
 
   NS_IMETHOD SetDocumentCharacterSet(const nsACString & characterSet);
--- a/editor/libeditor/text/nsTextEditRules.cpp
+++ b/editor/libeditor/text/nsTextEditRules.cpp
@@ -131,16 +131,25 @@ nsTextEditRules::Init(nsPlaintextEditor 
 
   mDeleteBidiImmediately =
     Preferences::GetBool("bidi.edit.delete_immediately", false);
 
   return res;
 }
 
 NS_IMETHODIMP
+nsTextEditRules::SetInitialValue(const nsAString& aValue)
+{
+  if (IsPasswordEditor()) {
+    mPasswordText = aValue;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsTextEditRules::DetachEditor()
 {
   if (mTimer)
     mTimer->Cancel();
 
   mEditor = nullptr;
   return NS_OK;
 }
--- a/editor/libeditor/text/nsTextEditRules.h
+++ b/editor/libeditor/text/nsTextEditRules.h
@@ -42,16 +42,17 @@ public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsTextEditRules, nsIEditRules)
   
               nsTextEditRules();
   virtual     ~nsTextEditRules();
 
   // nsIEditRules methods
   NS_IMETHOD Init(nsPlaintextEditor *aEditor);
+  NS_IMETHOD SetInitialValue(const nsAString& aValue);
   NS_IMETHOD DetachEditor();
   NS_IMETHOD BeforeEdit(EditAction action,
                         nsIEditor::EDirection aDirection);
   NS_IMETHOD AfterEdit(EditAction action,
                        nsIEditor::EDirection aDirection);
   NS_IMETHOD WillDoAction(mozilla::Selection* aSelection, nsRulesInfo* aInfo,
                           bool* aCancel, bool* aHandled);
   NS_IMETHOD DidDoAction(nsISelection *aSelection, nsRulesInfo *aInfo, nsresult aResult);
--- a/gfx/layers/basic/BasicLayersImpl.cpp
+++ b/gfx/layers/basic/BasicLayersImpl.cpp
@@ -106,17 +106,17 @@ FillRectWithMask(DrawTarget* aDT,
 {
   if (aMaskSource && aMaskTransform) {
     aDT->PushClipRect(aRect);
     Matrix oldTransform = aDT->GetTransform();
 
     Matrix inverseMask = *aMaskTransform;
     inverseMask.Invert();
 
-    Matrix transform = inverseMask * oldTransform;
+    Matrix transform = oldTransform * inverseMask;
     if (aSurfaceTransform) {
       transform = transform * (*aSurfaceTransform);
     }
 
     SurfacePattern source(aSurface, aExtendMode, transform, aFilter);
 
     aDT->SetTransform(*aMaskTransform);
     aDT->MaskSurface(source, aMaskSource, Point(0, 0), aOptions);
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -96,16 +96,18 @@ WalkTheTree(Layer* aLayer,
 }
 
 void
 AsyncCompositionManager::ResolveRefLayers()
 {
   if (!mLayerManager->GetRoot()) {
     return;
   }
+
+  mReadyForCompose = true;
   WalkTheTree<Resolve>(mLayerManager->GetRoot(),
                        mReadyForCompose,
                        mTargetConfig);
 }
 
 void
 AsyncCompositionManager::DetachRefLayers()
 {
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -795,21 +795,27 @@ void
 gfxFontEntry::DisconnectSVG()
 {
     if (mSVGInitialized && mSVGGlyphs) {
         mSVGGlyphs = nullptr;
         mSVGInitialized = false;
     }
 }
 
+bool
+gfxFontEntry::HasFontTable(uint32_t aTableTag)
+{
+    AutoTable table(this, aTableTag);
+    return table && hb_blob_get_length(table) > 0;
+}
+
 void
 gfxFontEntry::CheckForGraphiteTables()
 {
-    AutoTable silfTable(this, TRUETYPE_TAG('S','i','l','f'));
-    mHasGraphiteTables = silfTable && hb_blob_get_length(silfTable) > 0;
+    mHasGraphiteTables = HasFontTable(TRUETYPE_TAG('S','i','l','f'));
 }
 
 /* static */ size_t
 gfxFontEntry::FontTableHashEntry::SizeOfEntryExcludingThis
     (FontTableHashEntry *aEntry,
      MallocSizeOf aMallocSizeOf,
      void* aUserArg)
 {
@@ -4160,23 +4166,35 @@ gfxFont::InitMetricsFromSfntTables(Metri
             reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
         if (len >= offsetof(OS2Table, sxHeight) + sizeof(int16_t) &&
             uint16_t(os2->version) >= 2) {
             // version 2 and later includes the x-height field
             SET_SIGNED(xHeight, os2->sxHeight);
             // Abs because of negative xHeight seen in Kokonor (Tibetan) font
             aMetrics.xHeight = Abs(aMetrics.xHeight);
         }
-        // this should always be present
-        if (len >= offsetof(OS2Table, yStrikeoutPosition) + sizeof(int16_t)) {
+        // this should always be present in any valid OS/2 of any version
+        if (len >= offsetof(OS2Table, sTypoLineGap) + sizeof(int16_t)) {
             SET_SIGNED(aveCharWidth, os2->xAvgCharWidth);
             SET_SIGNED(subscriptOffset, os2->ySubscriptYOffset);
             SET_SIGNED(superscriptOffset, os2->ySuperscriptYOffset);
             SET_SIGNED(strikeoutSize, os2->yStrikeoutSize);
             SET_SIGNED(strikeoutOffset, os2->yStrikeoutPosition);
+
+            // for fonts with USE_TYPO_METRICS set in the fsSelection field,
+            // and for all OpenType math fonts (having a 'MATH' table),
+            // let the OS/2 sTypo* metrics override those from the hhea table
+            // (see http://www.microsoft.com/typography/otspec/os2.htm#fss)
+            const uint16_t kUseTypoMetricsMask = 1 << 7;
+            if ((uint16_t(os2->fsSelection) & kUseTypoMetricsMask) ||
+                mFontEntry->HasFontTable(TRUETYPE_TAG('M','A','T','H'))) {
+                SET_SIGNED(maxAscent, os2->sTypoAscender);
+                SET_SIGNED(maxDescent, - int16_t(os2->sTypoDescender));
+                SET_SIGNED(externalLeading, os2->sTypoLineGap);
+            }
         }
     }
 
     mIsValid = true;
 
     return true;
 }
 
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -263,16 +263,18 @@ public:
     bool IsFixedPitch() const { return mFixedPitch; }
     bool IsItalic() const { return mItalic; }
     bool IsBold() const { return mWeight >= 600; } // bold == weights 600 and above
     bool IgnoreGDEF() const { return mIgnoreGDEF; }
     bool IgnoreGSUB() const { return mIgnoreGSUB; }
 
     virtual bool IsSymbolFont();
 
+    virtual bool HasFontTable(uint32_t aTableTag);
+
     inline bool HasGraphiteTables() {
         if (!mCheckedForGraphiteTables) {
             CheckForGraphiteTables();
             mCheckedForGraphiteTables = true;
         }
         return mHasGraphiteTables;
     }
 
--- a/intl/uconv/public/moz.build
+++ b/intl/uconv/public/moz.build
@@ -4,11 +4,12 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS += [
     'nsEncoderDecoderUtils.h',
     'nsIUnicodeDecoder.h',
     'nsIUnicodeEncoder.h',
     'nsUConvCID.h',
+    'nsUCSupport.h',
     'uconvutil.h',
 ]
 
rename from intl/uconv/util/nsUCSupport.h
rename to intl/uconv/public/nsUCSupport.h
--- a/intl/uconv/src/moz.build
+++ b/intl/uconv/src/moz.build
@@ -106,17 +106,16 @@ UNIFIED_SOURCES += [
     '../ucvlatin/nsMacFarsiToUnicode.cpp',
     '../ucvlatin/nsMacGreekToUnicode.cpp',
     '../ucvlatin/nsMacGujaratiToUnicode.cpp',
     '../ucvlatin/nsMacGurmukhiToUnicode.cpp',
     '../ucvlatin/nsMacHebrewToUnicode.cpp',
     '../ucvlatin/nsMacIcelandicToUnicode.cpp',
     '../ucvlatin/nsMacRomanianToUnicode.cpp',
     '../ucvlatin/nsMacTurkishToUnicode.cpp',
-    '../ucvlatin/nsMUTF7ToUnicode.cpp',
     '../ucvlatin/nsT61ToUnicode.cpp',
     '../ucvlatin/nsTCVN5712ToUnicode.cpp',
     '../ucvlatin/nsTIS620ToUnicode.cpp',
     '../ucvlatin/nsUnicodeToARMSCII8.cpp',
     '../ucvlatin/nsUnicodeToAscii.cpp',
     '../ucvlatin/nsUnicodeToCP1250.cpp',
     '../ucvlatin/nsUnicodeToCP1251.cpp',
     '../ucvlatin/nsUnicodeToCP1253.cpp',
@@ -156,28 +155,25 @@ UNIFIED_SOURCES += [
     '../ucvlatin/nsUnicodeToMacFarsi.cpp',
     '../ucvlatin/nsUnicodeToMacGreek.cpp',
     '../ucvlatin/nsUnicodeToMacGujarati.cpp',
     '../ucvlatin/nsUnicodeToMacGurmukhi.cpp',
     '../ucvlatin/nsUnicodeToMacHebrew.cpp',
     '../ucvlatin/nsUnicodeToMacIcelandic.cpp',
     '../ucvlatin/nsUnicodeToMacRomanian.cpp',
     '../ucvlatin/nsUnicodeToMacTurkish.cpp',
-    '../ucvlatin/nsUnicodeToMUTF7.cpp',
     '../ucvlatin/nsUnicodeToT61.cpp',
     '../ucvlatin/nsUnicodeToTCVN5712.cpp',
     '../ucvlatin/nsUnicodeToTIS620.cpp',
     '../ucvlatin/nsUnicodeToUserDefined.cpp',
     '../ucvlatin/nsUnicodeToUTF16.cpp',
-    '../ucvlatin/nsUnicodeToUTF7.cpp',
     '../ucvlatin/nsUnicodeToVISCII.cpp',
     '../ucvlatin/nsUnicodeToVPS.cpp',
     '../ucvlatin/nsUserDefinedToUnicode.cpp',
     '../ucvlatin/nsUTF16ToUnicode.cpp',
-    '../ucvlatin/nsUTF7ToUnicode.cpp',
     '../ucvlatin/nsVISCIIToUnicode.cpp',
     '../ucvlatin/nsVPSToUnicode.cpp',
 ]
 
 UNIFIED_SOURCES += [
     '../ucvtw/nsBIG5HKSCSToUnicode.cpp',
     '../ucvtw/nsBIG5ToUnicode.cpp',
     '../ucvtw/nsUnicodeToBIG5.cpp',
--- a/intl/uconv/src/nsUConvModule.cpp
+++ b/intl/uconv/src/nsUConvModule.cpp
@@ -69,18 +69,16 @@
 #include "nsMacCroatianToUnicode.h"
 #include "nsMacRomanianToUnicode.h"
 #include "nsMacCyrillicToUnicode.h"
 #include "nsMacIcelandicToUnicode.h"
 #include "nsARMSCII8ToUnicode.h"
 #include "nsTCVN5712ToUnicode.h"
 #include "nsVISCIIToUnicode.h"
 #include "nsVPSToUnicode.h"
-#include "nsUTF7ToUnicode.h"
-#include "nsMUTF7ToUnicode.h"
 #include "nsUTF16ToUnicode.h"
 #include "nsT61ToUnicode.h"
 #include "nsUserDefinedToUnicode.h"
 #include "nsUnicodeToAscii.h"
 #include "nsUnicodeToISO88592.h"
 #include "nsUnicodeToISO88593.h"
 #include "nsUnicodeToISO88594.h"
 #include "nsUnicodeToISO88595.h"
@@ -118,18 +116,16 @@
 #include "nsUnicodeToMacCroatian.h"
 #include "nsUnicodeToMacRomanian.h"
 #include "nsUnicodeToMacCyrillic.h"
 #include "nsUnicodeToMacIcelandic.h"
 #include "nsUnicodeToARMSCII8.h"
 #include "nsUnicodeToTCVN5712.h"
 #include "nsUnicodeToVISCII.h"
 #include "nsUnicodeToVPS.h"
-#include "nsUnicodeToUTF7.h"
-#include "nsUnicodeToMUTF7.h"
 #include "nsUnicodeToUTF16.h"
 #include "nsUnicodeToT61.h"
 #include "nsUnicodeToUserDefined.h"
 #include "nsMacArabicToUnicode.h"
 #include "nsMacDevanagariToUnicode.h"
 #include "nsMacFarsiToUnicode.h"
 #include "nsMacGujaratiToUnicode.h"
 #include "nsMacGurmukhiToUnicode.h"
@@ -248,18 +244,16 @@ NS_UCONV_REG_UNREG("x-mac-turkish", NS_M
 NS_UCONV_REG_UNREG("x-mac-croatian", NS_MACCROATIANTOUNICODE_CID, NS_UNICODETOMACCROATIAN_CID)
 NS_UCONV_REG_UNREG("x-mac-romanian", NS_MACROMANIANTOUNICODE_CID, NS_UNICODETOMACROMANIAN_CID)
 NS_UCONV_REG_UNREG("x-mac-cyrillic", NS_MACCYRILLICTOUNICODE_CID, NS_UNICODETOMACCYRILLIC_CID)
 NS_UCONV_REG_UNREG("x-mac-icelandic", NS_MACICELANDICTOUNICODE_CID, NS_UNICODETOMACICELANDIC_CID)
 NS_UCONV_REG_UNREG("armscii-8", NS_ARMSCII8TOUNICODE_CID, NS_UNICODETOARMSCII8_CID)
 NS_UCONV_REG_UNREG("x-viet-tcvn5712", NS_TCVN5712TOUNICODE_CID, NS_UNICODETOTCVN5712_CID)
 NS_UCONV_REG_UNREG("VISCII", NS_VISCIITOUNICODE_CID, NS_UNICODETOVISCII_CID)
 NS_UCONV_REG_UNREG("x-viet-vps", NS_VPSTOUNICODE_CID, NS_UNICODETOVPS_CID)
-NS_UCONV_REG_UNREG("UTF-7", NS_UTF7TOUNICODE_CID, NS_UNICODETOUTF7_CID)
-NS_UCONV_REG_UNREG("x-imap4-modified-utf7", NS_MUTF7TOUNICODE_CID, NS_UNICODETOMUTF7_CID)
 NS_UCONV_REG_UNREG("UTF-16", NS_UTF16TOUNICODE_CID, NS_UNICODETOUTF16_CID)
 NS_UCONV_REG_UNREG("UTF-16BE", NS_UTF16BETOUNICODE_CID, NS_UNICODETOUTF16BE_CID)
 NS_UCONV_REG_UNREG("UTF-16LE", NS_UTF16LETOUNICODE_CID, NS_UNICODETOUTF16LE_CID)
 NS_UCONV_REG_UNREG("T.61-8bit", NS_T61TOUNICODE_CID, NS_UNICODETOT61_CID)
 NS_UCONV_REG_UNREG("x-user-defined", NS_USERDEFINEDTOUNICODE_CID, NS_UNICODETOUSERDEFINED_CID)
 NS_UCONV_REG_UNREG("x-mac-arabic" , NS_MACARABICTOUNICODE_CID, NS_UNICODETOMACARABIC_CID)
 NS_UCONV_REG_UNREG("x-mac-devanagari" , NS_MACDEVANAGARITOUNICODE_CID, NS_UNICODETOMACDEVANAGARI_CID)
 NS_UCONV_REG_UNREG("x-mac-farsi" , NS_MACFARSITOUNICODE_CID, NS_UNICODETOMACFARSI_CID)
@@ -308,23 +302,19 @@ NS_UCONV_REG_UNREG_DECODER("ISO-2022-CN"
   
 NS_CONVERTER_REGISTRY_END
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsUnicodeToUTF8)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsUTF8ToUnicode)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsReplacementToUnicode)
 
 // ucvlatin
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsUTF7ToUnicode)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsMUTF7ToUnicode)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsUTF16ToUnicode)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsUTF16BEToUnicode)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsUTF16LEToUnicode)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsUnicodeToUTF7)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsUnicodeToMUTF7)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsUnicodeToUTF16BE)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsUnicodeToUTF16LE)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsUnicodeToUTF16)
 
 // ucvibm
 
 // ucvja
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsShiftJISToUnicode)
@@ -525,18 +515,16 @@ NS_DEFINE_NAMED_CID(NS_MACTURKISHTOUNICO
 NS_DEFINE_NAMED_CID(NS_MACCROATIANTOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_MACROMANIANTOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_MACCYRILLICTOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_MACICELANDICTOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_ARMSCII8TOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_TCVN5712TOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_VISCIITOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_VPSTOUNICODE_CID);
-NS_DEFINE_NAMED_CID(NS_UTF7TOUNICODE_CID);
-NS_DEFINE_NAMED_CID(NS_MUTF7TOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_UTF16TOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_UTF16BETOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_UTF16LETOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_T61TOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_USERDEFINEDTOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_MACARABICTOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_MACDEVANAGARITOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_MACFARSITOUNICODE_CID);
@@ -582,18 +570,16 @@ NS_DEFINE_NAMED_CID(NS_UNICODETOMACTURKI
 NS_DEFINE_NAMED_CID(NS_UNICODETOMACCROATIAN_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOMACROMANIAN_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOMACCYRILLIC_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOMACICELANDIC_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOARMSCII8_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOTCVN5712_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOVISCII_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOVPS_CID);
-NS_DEFINE_NAMED_CID(NS_UNICODETOUTF7_CID);
-NS_DEFINE_NAMED_CID(NS_UNICODETOMUTF7_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOUTF16BE_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOUTF16LE_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOUTF16_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOT61_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOUSERDEFINED_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOMACARABIC_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOMACDEVANAGARI_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOMACFARSI_CID);
@@ -696,18 +682,16 @@ static const mozilla::Module::CIDEntry k
   { &kNS_MACCROATIANTOUNICODE_CID, false, nullptr, nsMacCroatianToUnicodeConstructor },
   { &kNS_MACROMANIANTOUNICODE_CID, false, nullptr, nsMacRomanianToUnicodeConstructor },
   { &kNS_MACCYRILLICTOUNICODE_CID, false, nullptr, nsMacCyrillicToUnicodeConstructor },
   { &kNS_MACICELANDICTOUNICODE_CID, false, nullptr, nsMacIcelandicToUnicodeConstructor },
   { &kNS_ARMSCII8TOUNICODE_CID, false, nullptr, nsARMSCII8ToUnicodeConstructor },
   { &kNS_TCVN5712TOUNICODE_CID, false, nullptr, nsTCVN5712ToUnicodeConstructor },
   { &kNS_VISCIITOUNICODE_CID, false, nullptr, nsVISCIIToUnicodeConstructor },
   { &kNS_VPSTOUNICODE_CID, false, nullptr, nsVPSToUnicodeConstructor },
-  { &kNS_UTF7TOUNICODE_CID, false, nullptr, nsUTF7ToUnicodeConstructor },
-  { &kNS_MUTF7TOUNICODE_CID, false, nullptr, nsMUTF7ToUnicodeConstructor },
   { &kNS_UTF16TOUNICODE_CID, false, nullptr, nsUTF16ToUnicodeConstructor },
   { &kNS_UTF16BETOUNICODE_CID, false, nullptr, nsUTF16BEToUnicodeConstructor },
   { &kNS_UTF16LETOUNICODE_CID, false, nullptr, nsUTF16LEToUnicodeConstructor },
   { &kNS_T61TOUNICODE_CID, false, nullptr, nsT61ToUnicodeConstructor },
   { &kNS_USERDEFINEDTOUNICODE_CID, false, nullptr, nsUserDefinedToUnicodeConstructor },
   { &kNS_MACARABICTOUNICODE_CID, false, nullptr, nsMacArabicToUnicodeConstructor },
   { &kNS_MACDEVANAGARITOUNICODE_CID, false, nullptr, nsMacDevanagariToUnicodeConstructor },
   { &kNS_MACFARSITOUNICODE_CID, false, nullptr, nsMacFarsiToUnicodeConstructor },
@@ -753,18 +737,16 @@ static const mozilla::Module::CIDEntry k
   { &kNS_UNICODETOMACCROATIAN_CID, false, nullptr, nsUnicodeToMacCroatianConstructor },
   { &kNS_UNICODETOMACROMANIAN_CID, false, nullptr, nsUnicodeToMacRomanianConstructor },
   { &kNS_UNICODETOMACCYRILLIC_CID, false, nullptr, nsUnicodeToMacCyrillicConstructor },
   { &kNS_UNICODETOMACICELANDIC_CID, false, nullptr, nsUnicodeToMacIcelandicConstructor },
   { &kNS_UNICODETOARMSCII8_CID, false, nullptr, nsUnicodeToARMSCII8Constructor },
   { &kNS_UNICODETOTCVN5712_CID, false, nullptr, nsUnicodeToTCVN5712Constructor },
   { &kNS_UNICODETOVISCII_CID, false, nullptr, nsUnicodeToVISCIIConstructor },
   { &kNS_UNICODETOVPS_CID, false, nullptr, nsUnicodeToVPSConstructor },
-  { &kNS_UNICODETOUTF7_CID, false, nullptr, nsUnicodeToUTF7Constructor },
-  { &kNS_UNICODETOMUTF7_CID, false, nullptr, nsUnicodeToMUTF7Constructor },
   { &kNS_UNICODETOUTF16BE_CID, false, nullptr, nsUnicodeToUTF16BEConstructor },
   { &kNS_UNICODETOUTF16LE_CID, false, nullptr, nsUnicodeToUTF16LEConstructor },
   { &kNS_UNICODETOUTF16_CID, false, nullptr, nsUnicodeToUTF16Constructor },
   { &kNS_UNICODETOT61_CID, false, nullptr, nsUnicodeToT61Constructor },
   { &kNS_UNICODETOUSERDEFINED_CID, false, nullptr, nsUnicodeToUserDefinedConstructor },
   { &kNS_UNICODETOMACARABIC_CID, false, nullptr, nsUnicodeToMacArabicConstructor },
   { &kNS_UNICODETOMACDEVANAGARI_CID, false, nullptr, nsUnicodeToMacDevanagariConstructor },
   { &kNS_UNICODETOMACFARSI_CID, false, nullptr, nsUnicodeToMacFarsiConstructor },
@@ -869,18 +851,16 @@ static const mozilla::Module::ContractID
   { NS_UNICODEDECODER_CONTRACTID_BASE "x-mac-croatian", &kNS_MACCROATIANTOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "x-mac-romanian", &kNS_MACROMANIANTOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "x-mac-cyrillic", &kNS_MACCYRILLICTOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "x-mac-icelandic", &kNS_MACICELANDICTOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "armscii-8", &kNS_ARMSCII8TOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "x-viet-tcvn5712", &kNS_TCVN5712TOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "VISCII", &kNS_VISCIITOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "x-viet-vps", &kNS_VPSTOUNICODE_CID },
-  { NS_UNICODEDECODER_CONTRACTID_BASE "UTF-7", &kNS_UTF7TOUNICODE_CID },
-  { NS_UNICODEDECODER_CONTRACTID_BASE "x-imap4-modified-utf7", &kNS_MUTF7TOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "UTF-16", &kNS_UTF16TOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "UTF-16BE", &kNS_UTF16BETOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "UTF-16LE", &kNS_UTF16LETOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "T.61-8bit", &kNS_T61TOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "x-user-defined", &kNS_USERDEFINEDTOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "x-mac-arabic", &kNS_MACARABICTOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "x-mac-devanagari", &kNS_MACDEVANAGARITOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "x-mac-farsi", &kNS_MACFARSITOUNICODE_CID },
@@ -926,18 +906,16 @@ static const mozilla::Module::ContractID
   { NS_UNICODEENCODER_CONTRACTID_BASE "x-mac-croatian", &kNS_UNICODETOMACCROATIAN_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "x-mac-romanian", &kNS_UNICODETOMACROMANIAN_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "x-mac-cyrillic", &kNS_UNICODETOMACCYRILLIC_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "x-mac-icelandic", &kNS_UNICODETOMACICELANDIC_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "armscii-8", &kNS_UNICODETOARMSCII8_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "x-viet-tcvn5712", &kNS_UNICODETOTCVN5712_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "VISCII", &kNS_UNICODETOVISCII_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "x-viet-vps", &kNS_UNICODETOVPS_CID },
-  { NS_UNICODEENCODER_CONTRACTID_BASE "UTF-7", &kNS_UNICODETOUTF7_CID },
-  { NS_UNICODEENCODER_CONTRACTID_BASE "x-imap4-modified-utf7", &kNS_UNICODETOMUTF7_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "UTF-16BE", &kNS_UNICODETOUTF16BE_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "UTF-16LE", &kNS_UNICODETOUTF16LE_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "UTF-16", &kNS_UNICODETOUTF16_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "T.61-8bit", &kNS_UNICODETOT61_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "x-user-defined", &kNS_UNICODETOUSERDEFINED_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "x-mac-arabic", &kNS_UNICODETOMACARABIC_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "x-mac-devanagari", &kNS_UNICODETOMACDEVANAGARI_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "x-mac-farsi", &kNS_UNICODETOMACFARSI_CID },
--- a/intl/uconv/tests/nsTestUConv.cpp
+++ b/intl/uconv/tests/nsTestUConv.cpp
@@ -702,88 +702,16 @@ nsresult testUTF8Decoder()
   if (NS_FAILED(res)) {
     return res;
   } else {
     printf("Test Passed.\n");
     return NS_OK;
   }
 }
 
-/**
- * Test the M-UTF-7 decoder.
- */
-nsresult testMUTF7Decoder()
-{
-  char * testName = "T107";
-  printf("\n[%s] Unicode <- MUTF7\n", testName);
-
-  // create converter
-  CREATE_DECODER("x-imap4-modified-utf7");
-
-  // test data
-  char src[] = {"\x50\x51\x52\x53&AAAAAAAA-&-&AAA-"};
-  char16_t exp[] = {0x0050,0x0051,0x0052,0x0053,0x0000,0x0000,0x0000,'&',0x0000};
-
-  // test converter - normal operation
-  res = testDecoder(dec, src, ARRAY_SIZE(src)-1, exp, ARRAY_SIZE(exp), testName);
-
-  // reset converter
-  if (NS_SUCCEEDED(res)) res = resetDecoder(dec, testName);
-
-  // test converter - stress test
-  if (NS_SUCCEEDED(res)) 
-    res = testStressDecoder(dec, src, ARRAY_SIZE(src)-1, exp, ARRAY_SIZE(exp), testName);
-
-  // release converter
-  NS_RELEASE(dec);
-
-  if (NS_FAILED(res)) {
-    return res;
-  } else {
-    printf("Test Passed.\n");
-    return NS_OK;
-  }
-}
-
-/**
- * Test the UTF-7 decoder.
- */
-nsresult testUTF7Decoder()
-{
-  char * testName = "T108";
-  printf("\n[%s] Unicode <- UTF7\n", testName);
-
-  // create converter
-  CREATE_DECODER("utf-7");
-
-  // test data
-  char src[] = {"+ADwAIQ-DOC"};
-  char16_t exp[] = {'<','!','D','O','C'};
-
-  // test converter - normal operation
-  res = testDecoder(dec, src, ARRAY_SIZE(src)-1, exp, ARRAY_SIZE(exp), testName);
-
-  // reset converter
-  if (NS_SUCCEEDED(res)) res = resetDecoder(dec, testName);
-
-  // test converter - stress test
-  if (NS_SUCCEEDED(res)) 
-    res = testStressDecoder(dec, src, ARRAY_SIZE(src)-1, exp, ARRAY_SIZE(exp), testName);
-
-  // release converter
-  NS_RELEASE(dec);
-
-  if (NS_FAILED(res)) {
-    return res;
-  } else {
-    printf("Test Passed.\n");
-    return NS_OK;
-  }
-}
-
 //----------------------------------------------------------------------
 // Encoders testing functions
 
 /**
  * Test the Latin1 encoder.
  */
 nsresult testLatin1Encoder()
 {
@@ -937,90 +865,16 @@ nsresult testISO2022JPEncoder()
   if (NS_FAILED(res)) {
     return res;
   } else {
     printf("Test Passed.\n");
     return NS_OK;
   }
 }
 
-/**
- * Test the M-UTF-7 encoder.
- */
-nsresult testMUTF7Encoder()
-{
-  char * testName = "T205";
-  printf("\n[%s] Unicode -> MUTF-7\n", testName);
-
-  // create converter
-  CREATE_ENCODER("x-imap4-modified-utf7");
-  enc->SetOutputErrorBehavior(enc->kOnError_Replace, nullptr, 0x00cc);
-
-  // test data
-  char16_t src[] = {0x0050,0x0051,0x0052,0x0053,0x0000,0x0000,0x0000,'&',0x0000};
-  char exp[] = {"\x50\x51\x52\x53&AAAAAAAA-&-&AAA-"};
-
-  // test converter - easy test
-  res = testEncoder(enc, src, ARRAY_SIZE(src), exp, ARRAY_SIZE(exp)-1, testName);
-
-  // reset converter
-  if (NS_SUCCEEDED(res)) res = resetEncoder(enc, testName);
-
-  // test converter - stress test
-  if (NS_SUCCEEDED(res)) 
-    res = testStressEncoder(enc, src, ARRAY_SIZE(src), exp, ARRAY_SIZE(exp)-1, testName);
-
-  // release converter
-  NS_RELEASE(enc);
-
-  if (NS_FAILED(res)) {
-    return res;
-  } else {
-    printf("Test Passed.\n");
-    return NS_OK;
-  }
-}
-
-/**
- * Test the UTF-7 encoder.
- */
-nsresult testUTF7Encoder()
-{
-  char * testName = "T206";
-  printf("\n[%s] Unicode -> UTF-7\n", testName);
-
-  // create converter
-  CREATE_ENCODER("utf-7");
-  enc->SetOutputErrorBehavior(enc->kOnError_Replace, nullptr, 0x00cc);
-
-  // test data
-  char16_t src[] = {'e','t','i','r','a',0x0a};
-  char exp[] = {"etira\x0a"};
-
-  // test converter - easy test
-  res = testEncoder(enc, src, ARRAY_SIZE(src), exp, ARRAY_SIZE(exp)-1, testName);
-
-  // reset converter
-  if (NS_SUCCEEDED(res)) res = resetEncoder(enc, testName);
-
-  // test converter - stress test
-  if (NS_SUCCEEDED(res)) 
-    res = testStressEncoder(enc, src, ARRAY_SIZE(src), exp, ARRAY_SIZE(exp)-1, testName);
-
-  // release converter
-  NS_RELEASE(enc);
-
-  if (NS_FAILED(res)) {
-    return res;
-  } else {
-    printf("Test Passed.\n");
-    return NS_OK;
-  }
-}
-
 nsresult  testPlatformCharset()
 {
   nsIPlatformCharset *cinfo;
   nsresult res = CallGetService(kPlatformCharsetCID, &cinfo);
   if (NS_FAILED(res)) {
     printf("ERROR at GetService() code=0x%x.\n",res);
     return res;
   }
--- a/intl/uconv/tests/unit/test_bug718500.js
+++ b/intl/uconv/tests/unit/test_bug718500.js
@@ -54,18 +54,16 @@ var encoderList = [
   "x-mac-croatian",
   "x-mac-romanian",
   "x-mac-cyrillic",
   "x-mac-icelandic",
   "armscii-8",
   "x-viet-tcvn5712",
   "VISCII",
   "x-viet-vps",
-  "UTF-7",
-  "x-imap4-modified-utf7",
   "UTF-16",
   "UTF-16BE",
   "UTF-16LE",
   "T.61-8bit",
   "x-user-defined",
   "x-mac-arabic",
   "x-mac-devanagari",
   "x-mac-farsi",
@@ -140,18 +138,16 @@ var decoderList = [
   "x-mac-croatian",
   "x-mac-romanian",
   "x-mac-cyrillic",
   "x-mac-icelandic",
   "armscii-8",
   "x-viet-tcvn5712",
   "VISCII",
   "x-viet-vps",
-  "UTF-7",
-  "x-imap4-modified-utf7",
   "UTF-16",
   "UTF-16BE",
   "UTF-16LE",
   "T.61-8bit",
   "x-user-defined",
   "x-mac-arabic",
   "x-mac-devanagari",
   "x-mac-farsi",
deleted file mode 100644
--- a/intl/uconv/tests/unit/test_decode_utf-7.js
+++ /dev/null
@@ -1,14 +0,0 @@
-// Tests conversion from UTF-7 to Unicode. The conversion should fail!
-
-load('CharsetConversionTests.js');
-	
-const inString = "+LGI--+ITIipSIp- +AocCeQ-oddns +Ad0CjQ- s+ATECZQKH- p+AlAB3QJ5- u+AlACVA- no+Ao4- +Al8-I";
-    
-const expectedString = "+LGI--+ITIipSIp- +AocCeQ-oddns +Ad0CjQ- s+ATECZQKH- p+AlAB3QJ5- u+AlACVA- no+Ao4- +Al8-I";
-
-const aliases = [ "UTF-7", "utf-7", "x-unicode-2-0-utf-7", "unicode-2-0-utf-7",
-		  "unicode-1-1-utf-7", "csunicode11utf7" ];
-
-function run_test() {
-  testDecodeAliases();
-}
deleted file mode 100644
--- a/intl/uconv/tests/unit/test_decode_utf-7_internal.js
+++ /dev/null
@@ -1,14 +0,0 @@
-// Tests conversion from UTF-7 to Unicode.
-
-load('CharsetConversionTests.js');
-	
-const inString = "+LGI--+ITIipSIp- +AocCeQ-oddns +Ad0CjQ- s+ATECZQKH- p+AlAB3QJ5- u+AlACVA- no+Ao4- +Al8-I";
-    
-const expectedString = "\u2C62-\u2132\u22A5\u2229 \u0287\u0279oddns \u01DD\u028D s\u0131\u0265\u0287 p\u0250\u01DD\u0279 u\u0250\u0254 no\u028E \u025FI";
-
-const aliases = [ "UTF-7", "utf-7", "x-unicode-2-0-utf-7", "unicode-2-0-utf-7",
-		  "unicode-1-1-utf-7", "csunicode11utf7" ];
-
-function run_test() {
-  testDecodeAliasesInternal();
-}
deleted file mode 100644
--- a/intl/uconv/tests/unit/test_encode_utf-7.js
+++ /dev/null
@@ -1,14 +0,0 @@
-// Tests conversion from Unicode to UTF-7. The conversion should fail!
-
-load('CharsetConversionTests.js');
-	
-const inString = "\u2C62-\u2132\u22A5\u2229 \u0287\u0279oddns \u01DD\u028D s\u0131\u0265\u0287 p\u0250\u01DD\u0279 u\u0250\u0254 no\u028E \u025FI";
-    
-const expectedString = "?-??? ??oddns ?? s??? p??? u?? no? ?I";
-
-const aliases = [ "UTF-7", "utf-7", "x-unicode-2-0-utf-7", "unicode-2-0-utf-7",
-		  "unicode-1-1-utf-7", "csunicode11utf7" ];
-
-function run_test() {
-  testEncodeAliases();
-}
deleted file mode 100644
--- a/intl/uconv/tests/unit/test_encode_utf-7_internal.js
+++ /dev/null
@@ -1,14 +0,0 @@
-// Tests conversion from Unicode to UTF-7.
-
-load('CharsetConversionTests.js');
-	
-const inString = "\u2C62-\u2132\u22A5\u2229 \u0287\u0279oddns \u01DD\u028D s\u0131\u0265\u0287 p\u0250\u01DD\u0279 u\u0250\u0254 no\u028E \u025FI";
-    
-const expectedString = "+LGI--+ITIipSIp- +AocCeQ-oddns +Ad0CjQ- s+ATECZQKH- p+AlAB3QJ5- u+AlACVA- no+Ao4- +Al8-I";
-
-const aliases = [ "UTF-7", "utf-7", "x-unicode-2-0-utf-7", "unicode-2-0-utf-7",
-		  "unicode-1-1-utf-7", "csunicode11utf7" ];
-
-function run_test() {
-  testEncodeAliasesInternal();
-}
--- a/intl/uconv/tests/unit/xpcshell.ini
+++ b/intl/uconv/tests/unit/xpcshell.ini
@@ -71,18 +71,16 @@ support-files =
 [test_decode_CP862.js]
 [test_decode_CP864.js]
 [test_decode_CP874.js]
 [test_decode_EUCKR_Hangul.js]
 [test_decode_armscii.js]
 [test_decode_gb18030.js]
 [test_decode_gbk.js]
 [test_decode_tcvn5712.js]
-[test_decode_utf-7_internal.js]
-[test_decode_utf-7.js]
 [test_decode_viscii.js]
 [test_decode_vps.js]
 [test_decode_x_mac_arabic.js]
 [test_decode_x_mac_arabic_internal.js]
 [test_decode_x_mac_ce.js]
 [test_decode_x_mac_croatian.js]
 [test_decode_x_mac_cyrillic.js]
 [test_decode_x_mac_devanagari.js]
@@ -126,18 +124,16 @@ support-files =
 [test_encode_CP855.js]
 [test_encode_CP857.js]
 [test_encode_CP862.js]
 [test_encode_CP864.js]
 [test_encode_CP874.js]
 [test_encode_armscii.js]
 [test_encode_gbk.js]
 [test_encode_tcvn5712.js]
-[test_encode_utf-7_internal.js]
-[test_encode_utf-7.js]
 [test_encode_viscii.js]
 [test_encode_vps.js]
 [test_encode_x_mac_arabic.js]
 [test_encode_x_mac_arabic_internal.js]
 [test_encode_x_mac_ce.js]
 [test_encode_x_mac_croatian.js]
 [test_encode_x_mac_cyrillic.js]
 [test_encode_x_mac_devanagari.js]
deleted file mode 100644
--- a/intl/uconv/ucvlatin/nsMUTF7ToUnicode.cpp
+++ /dev/null
@@ -1,14 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsMUTF7ToUnicode.h"
-
-//----------------------------------------------------------------------
-// Class nsMUTF7ToUnicode [implementation]
-
-nsMUTF7ToUnicode::nsMUTF7ToUnicode() 
-: nsBasicUTF7Decoder(',', '&')
-{
-}
deleted file mode 100644
--- a/intl/uconv/ucvlatin/nsMUTF7ToUnicode.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef nsMUTF7ToUnicode_h___
-#define nsMUTF7ToUnicode_h___
-
-#include "nsUTF7ToUnicode.h"
-
-//----------------------------------------------------------------------
-// Class nsMUTF7ToUnicode [declaration]
-
-/**
- * A character set converter from Modified UTF7 to Unicode.
- *
- * @created         18/May/1999
- * @author  Catalin Rotaru [CATA]
- */
-class nsMUTF7ToUnicode : public nsBasicUTF7Decoder 
-{
-public:
-
-  /**
-   * Class constructor.
-   */
-  nsMUTF7ToUnicode();
-
-};
-
-#endif /* nsMUTF7ToUnicode_h___ */
--- a/intl/uconv/ucvlatin/nsUCvLatinCID.h
+++ b/intl/uconv/ucvlatin/nsUCvLatinCID.h
@@ -184,26 +184,16 @@
 #define NS_VISCIITOUNICODE_CID \
   { 0x6394eeae, 0xfc3d, 0x11d2, {0xb3, 0xb8, 0x0, 0x80, 0x5f, 0x8a, 0x66, 0x70}}
 
 // Class ID for our VPSToUnicode charset converter
 // {6394EEB0-FC3D-11d2-B3B8-00805F8A6670}
 #define NS_VPSTOUNICODE_CID \
   { 0x6394eeb0, 0xfc3d, 0x11d2, {0xb3, 0xb8, 0x0, 0x80, 0x5f, 0x8a, 0x66, 0x70}}
 
-// Class ID for our UTF7ToUnicode charset converter
-// {77CFAAF1-1CF4-11d3-8AAF-00600811A836}
-#define NS_UTF7TOUNICODE_CID \
-  { 0x77cfaaf1, 0x1cf4, 0x11d3, {0x8a, 0xaf, 0x0, 0x60, 0x8, 0x11, 0xa8, 0x36}}
-
-// Class ID for our MUTF7ToUnicode charset converter
-// {B57F97C1-0D70-11d3-8AAE-00600811A836}
-#define NS_MUTF7TOUNICODE_CID \
-  { 0xb57f97c1, 0xd70, 0x11d3, {0x8a, 0xae, 0x0, 0x60, 0x8, 0x11, 0xa8, 0x36}}
-
 // Class ID for our UnicodeToISO88592 charset converter
 // {7B8556A6-EC79-11d2-8AAC-00600811A836}
 #define NS_UNICODETOISO88592_CID \
   { 0x7b8556a6, 0xec79, 0x11d2, {0x8a, 0xac, 0x0, 0x60, 0x8, 0x11, 0xa8, 0x36}}
 
 // Class ID for our UnicodeToISO88593 charset converter
 // {660D8CA5-F763-11d2-8AAD-00600811A836}
 #define NS_UNICODETOISO88593_CID \
@@ -375,26 +365,16 @@
 #define NS_UNICODETOVISCII_CID \
   { 0x6394eebf, 0xfc3d, 0x11d2, {0xb3, 0xb8, 0x0, 0x80, 0x5f, 0x8a, 0x66, 0x70}}
 
 // Class ID for our UnicodeToVPS charset converter
 // {6394EEC0-FC3D-11d2-B3B8-00805F8A6670}
 #define NS_UNICODETOVPS_CID \
   { 0x6394eec0, 0xfc3d, 0x11d2, {0xb3, 0xb8, 0x0, 0x80, 0x5f, 0x8a, 0x66, 0x70}}
 
-// Class ID for our UnicodeToUTF7 charset converter
-// {77CFAAF2-1CF4-11d3-8AAF-00600811A836}
-#define NS_UNICODETOUTF7_CID \
-  { 0x77cfaaf2, 0x1cf4, 0x11d3, {0x8a, 0xaf, 0x0, 0x60, 0x8, 0x11, 0xa8, 0x36}}
-
-// Class ID for our UnicodeToMUTF7 charset converter
-// {B57F97C2-0D70-11d3-8AAE-00600811A836}
-#define NS_UNICODETOMUTF7_CID \
-  { 0xb57f97c2, 0xd70, 0x11d3, {0x8a, 0xae, 0x0, 0x60, 0x8, 0x11, 0xa8, 0x36}}
-
 // Class ID for our CP1255ToUnicode charset converter
 // {BA6151A1-1DFA-11d3-B3BF-00805F8A6670}
 #define NS_CP1255TOUNICODE_CID \
   { 0xba6151a1, 0x1dfa, 0x11d3, {0xb3, 0xbf, 0x0, 0x80, 0x5f, 0x8a, 0x66, 0x70}}
 
 // Class ID for our CP1256ToUnicode charset converter
 // {BA6151A2-1DFA-11d3-B3BF-00805F8A6670}
 #define NS_CP1256TOUNICODE_CID \
deleted file mode 100644
--- a/intl/uconv/ucvlatin/nsUTF7ToUnicode.cpp
+++ /dev/null
@@ -1,228 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsUTF7ToUnicode.h"
-
-#define ENC_DIRECT      0
-#define ENC_BASE64      1
-
-//----------------------------------------------------------------------
-// Class nsBasicUTF7Decoder [implementation]
-
-nsBasicUTF7Decoder::nsBasicUTF7Decoder(char aLastChar, char aEscChar) 
-: nsBufferDecoderSupport(1)
-{
-  mLastChar = aLastChar;
-  mEscChar = aEscChar;
-  Reset();
-}
-
-nsresult nsBasicUTF7Decoder::DecodeDirect(
-                             const char * aSrc, 
-                             int32_t * aSrcLength, 
-                             char16_t * aDest, 
-                             int32_t * aDestLength)
-{
-  const char * srcEnd = aSrc + *aSrcLength;
-  const char * src = aSrc;
-  char16_t * destEnd = aDest + *aDestLength;
-  char16_t * dest = aDest;
-  nsresult res = NS_OK;
-  char ch;
-
-  while (src < srcEnd) {
-    ch = *src;
-
-    // stop when we meet other chars or end of direct encoded seq.
-    // if (!(DirectEncodable(ch)) || (ch == mEscChar)) {
-    // but we are decoding; so we should be lax; pass everything until escchar
-    if (ch == mEscChar) {
-      res = NS_ERROR_UDEC_ILLEGALINPUT;
-      break;
-    }
-
-    if (dest >= destEnd) {
-      res = NS_OK_UDEC_MOREOUTPUT;
-      break;
-    } else {
-      *dest++ = ch;
-      src++;
-    }
-  }
-
-  *aSrcLength = src - aSrc;
-  *aDestLength = dest - aDest;
-  return res;
-}
-
-nsresult nsBasicUTF7Decoder::DecodeBase64(
-                             const char * aSrc, 
-                             int32_t * aSrcLength, 
-                             char16_t * aDest, 
-                             int32_t * aDestLength)
-{
-  const char * srcEnd = aSrc + *aSrcLength;
-  const char * src = aSrc;
-  char16_t * destEnd = aDest + *aDestLength;
-  char16_t * dest = aDest;
-  nsresult res = NS_OK;
-  char ch;
-  uint32_t value;
-
-  while (src < srcEnd) {
-    ch = *src;
-
-    // stop when we meet other chars or end of direct encoded seq.
-    value = CharToValue(ch);
-    if (value > 0xff) {
-      res = NS_ERROR_UDEC_ILLEGALINPUT;
-      break;
-    }
-
-    switch (mEncStep) {
-      case 0:
-        mEncBits = value << 10;
-        break;
-      case 1:
-        mEncBits += value << 4;
-        break;
-      case 2:
-        if (dest >= destEnd) {
-          res = NS_OK_UDEC_MOREOUTPUT;
-          break;
-        }
-        mEncBits += value >> 2;
-        *(dest++) = (char16_t) mEncBits;
-        mEncBits = (value & 0x03) << 14;
-        break;
-      case 3:
-        mEncBits += value << 8;
-        break;
-      case 4:
-        mEncBits += value << 2;
-        break;
-      case 5:
-        if (dest >= destEnd) {
-          res = NS_OK_UDEC_MOREOUTPUT;
-          break;
-        }
-        mEncBits += value >> 4;
-        *(dest++) = (char16_t) mEncBits;
-        mEncBits = (value & 0x0f) << 12;
-        break;
-      case 6:
-        mEncBits += value << 6;
-        break;
-      case 7:
-        if (dest >= destEnd) {
-          res = NS_OK_UDEC_MOREOUTPUT;
-          break;
-        }
-        mEncBits += value;
-        *(dest++) = (char16_t) mEncBits;
-        mEncBits = 0;
-        break;
-    }
-
-    if (res != NS_OK) break;
-
-    src++;
-    (++mEncStep)%=8;
-  }
-
-  *aSrcLength = src - aSrc;
-  *aDestLength = dest - aDest;
-  return res;
-}
-
-uint32_t nsBasicUTF7Decoder::CharToValue(char aChar) {
-  if ((aChar>='A')&&(aChar<='Z'))
-    return (uint8_t)(aChar-'A');
-  else if ((aChar>='a')&&(aChar<='z'))
-    return (uint8_t)(26+aChar-'a');
-  else if ((aChar>='0')&&(aChar<='9'))
-    return (uint8_t)(26+26+aChar-'0');
-  else if (aChar=='+')
-    return (uint8_t)(26+26+10);
-  else if (aChar==mLastChar)
-    return (uint8_t)(26+26+10+1);
-  else
-    return 0xffff;
-}
-
-//----------------------------------------------------------------------
-// Subclassing of nsBufferDecoderSupport class [implementation]
-
-NS_IMETHODIMP nsBasicUTF7Decoder::ConvertNoBuff(const char * aSrc, 
-                                                int32_t * aSrcLength, 
-                                                char16_t * aDest, 
-                                                int32_t * aDestLength)
-{
-  const char * srcEnd = aSrc + *aSrcLength;
-  const char * src = aSrc;
-  char16_t * destEnd = aDest + *aDestLength;
-  char16_t * dest = aDest;
-  int32_t bcr,bcw;
-  nsresult res = NS_OK;
-
-  while (src < srcEnd) {
-
-    // fist, attept to decode in the current mode
-    bcr = srcEnd - src;
-    bcw = destEnd - dest;
-    if (mEncoding == ENC_DIRECT) 
-      res = DecodeDirect(src, &bcr, dest, &bcw);
-    else if ((mFreshBase64) && (*src == '-')) {
-      *dest = mEscChar;
-      bcr = 0;
-      bcw = 1;
-      res = NS_ERROR_UDEC_ILLEGALINPUT;
-    } else {
-      mFreshBase64 = false;
-      res = DecodeBase64(src, &bcr, dest, &bcw);
-    }
-    src += bcr;
-    dest += bcw;
-
-    // if an illegal char was encountered, test if it is an escape seq.
-    if (res == NS_ERROR_UDEC_ILLEGALINPUT) {
-      if (mEncoding == ENC_DIRECT) {
-        if (*src == mEscChar) {
-          mEncoding = ENC_BASE64;
-          mFreshBase64 = true;
-          mEncBits = 0;
-          mEncStep = 0;
-          src++;
-          res = NS_OK;
-        } else break;
-      } else {
-        mEncoding = ENC_DIRECT;
-        res = NS_OK;
-        // absorbe end of escape sequence
-        if (*src == '-') src++;
-      }
-    } else if (res != NS_OK) break;
-  }
-
-  *aSrcLength = src - aSrc;
-  *aDestLength = dest - aDest;
-  return res;
-}
-
-NS_IMETHODIMP nsBasicUTF7Decoder::Reset()
-{
-  mEncoding = ENC_DIRECT;
-  mEncBits = 0;
-  mEncStep = 0;
-  return nsBufferDecoderSupport::Reset();
-}
-
-//----------------------------------------------------------------------
-// Class nsUTF7ToUnicode [implementation]
-
-nsUTF7ToUnicode::nsUTF7ToUnicode() 
-: nsBasicUTF7Decoder('/', '+')
-{
-}
deleted file mode 100644
--- a/intl/uconv/ucvlatin/nsUTF7ToUnicode.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef nsUTF7ToUnicode_h___
-#define nsUTF7ToUnicode_h___
-
-#include "nsUCSupport.h"
-
-//----------------------------------------------------------------------
-// Class nsBasicUTF7Decoder [declaration]
-
-/**
- * Basic class for a character set converter from UTF-7 to Unicode.
- *
- * @created         03/Jun/1999
- * @author  Catalin Rotaru [CATA]
- */
-class nsBasicUTF7Decoder : public nsBufferDecoderSupport
-{
-public:
-
-  /**
-   * Class constructor.
-   */
-  nsBasicUTF7Decoder(char aLastChar, char aEscChar);
-
-protected:
-
-  int32_t                   mEncoding;      // current encoding
-  uint32_t                  mEncBits;
-  int32_t                   mEncStep;
-  char                      mLastChar;
-  char                      mEscChar;
-  bool                      mFreshBase64;
-
-  nsresult DecodeDirect(const char * aSrc, int32_t * aSrcLength, 
-      char16_t * aDest, int32_t * aDestLength);
-  nsresult DecodeBase64(const char * aSrc, int32_t * aSrcLength, 
-      char16_t * aDest, int32_t * aDestLength);
-  uint32_t CharToValue(char aChar);
-
-  //--------------------------------------------------------------------
-  // Subclassing of nsBufferDecoderSupport class [declaration]
-
-  NS_IMETHOD ConvertNoBuff(const char * aSrc, int32_t * aSrcLength, 
-      char16_t * aDest, int32_t * aDestLength);
-  NS_IMETHOD Reset();
-};
-
-//----------------------------------------------------------------------
-// Class nsUTF7ToUnicode [declaration]
-
-/**
- * A character set converter from Modified UTF7 to Unicode.
- *
- * @created         18/May/1999
- * @author  Catalin Rotaru [CATA]
- */
-class nsUTF7ToUnicode : public nsBasicUTF7Decoder 
-{
-public:
-
-  /**
-   * Class constructor.
-   */
-  nsUTF7ToUnicode();
-
-};
-
-#endif /* nsUTF7ToUnicode_h___ */
deleted file mode 100644
--- a/intl/uconv/ucvlatin/nsUnicodeToMUTF7.cpp
+++ /dev/null
@@ -1,14 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsUnicodeToMUTF7.h"
-
-//----------------------------------------------------------------------
-// Class nsUnicodeToMUTF7 [implementation]
-
-nsUnicodeToMUTF7::nsUnicodeToMUTF7() 
-: nsBasicUTF7Encoder(',', '&')
-{
-}
deleted file mode 100644
--- a/intl/uconv/ucvlatin/nsUnicodeToMUTF7.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef nsUnicodeToMUTF7_h___
-#define nsUnicodeToMUTF7_h___
-
-#include "nsUnicodeToUTF7.h"
-
-//----------------------------------------------------------------------
-// Class nsUnicodeToMUTF7 [declaration]
-
-/**
- * A character set converter from Unicode to Modified UTF-7.
- *
- * @created         18/May/1999
- * @author  Catalin Rotaru [CATA]
- */
-class nsUnicodeToMUTF7 : public nsBasicUTF7Encoder
-{
-public:
-
-  /**
-   * Class constructor.
-   */
-  nsUnicodeToMUTF7();
-
-};
-
-#endif /* nsUnicodeToMUTF7_h___ */
deleted file mode 100644
--- a/intl/uconv/ucvlatin/nsUnicodeToUTF7.cpp
+++ /dev/null
@@ -1,298 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsUnicodeToUTF7.h"
-#include <string.h>
-
-//----------------------------------------------------------------------
-// Global functions and data [declaration]
-
-#define ENC_DIRECT      0
-#define ENC_BASE64      1
-
-//----------------------------------------------------------------------
-// Class nsBasicUTF7Encoder [implementation]
-
-nsBasicUTF7Encoder::nsBasicUTF7Encoder(char aLastChar, char aEscChar) 
-: nsEncoderSupport(5)
-{
-  mLastChar = aLastChar;
-  mEscChar = aEscChar;
-  Reset();
-}
-
-nsresult nsBasicUTF7Encoder::ShiftEncoding(int32_t aEncoding,
-                                          char * aDest, 
-                                          int32_t * aDestLength)
-{
-  if (aEncoding == mEncoding) {
-    *aDestLength = 0;
-    return NS_OK;
-  } 
-
-  nsresult res = NS_OK;
-  char * dest = aDest;
-  char * destEnd = aDest + *aDestLength;
-
-  if (mEncStep != 0) {
-    if (dest >= destEnd) return NS_OK_UENC_MOREOUTPUT;
-    *(dest++)=ValueToChar(mEncBits);
-    mEncStep = 0;
-    mEncBits = 0;
-  }
-
-  if (dest >= destEnd) {
-    res = NS_OK_UENC_MOREOUTPUT;
-  } else {
-    switch (aEncoding) {
-      case 0:
-        *(dest++) = '-';
-        mEncStep = 0;
-        mEncBits = 0;
-        break;
-      case 1:
-        *(dest++) = mEscChar;
-        break;
-    }
-    mEncoding = aEncoding;
-  }
-
-  *aDestLength  = dest - aDest;
-  return res;
-}
-
-nsresult nsBasicUTF7Encoder::EncodeDirect(
-                            const char16_t * aSrc, 
-                            int32_t * aSrcLength, 
-                            char * aDest, 
-                            int32_t * aDestLength)
-{
-  nsresult res = NS_OK;
-  const char16_t * src = aSrc;
-  const char16_t * srcEnd = aSrc + *aSrcLength;
-  char * dest = aDest;
-  char * destEnd = aDest + *aDestLength;
-  char16_t ch;
-
-  while (src < srcEnd) {
-    ch = *src;
-
-    // stop when we reach Unicode chars
-    if (!DirectEncodable(ch)) break;
-
-    if (ch == mEscChar) {
-      // special case for the escape char
-      if (destEnd - dest < 1) {
-        res = NS_OK_UENC_MOREOUTPUT;
-        break;
-      } else {
-        *dest++ = (char)ch;
-        *dest++ = (char)'-';
-        src++;
-      }
-    } else {
-      //classic direct encoding
-      if (dest >= destEnd) {
-        res = NS_OK_UENC_MOREOUTPUT;
-        break;
-      } else {
-        *dest++ = (char)ch;
-        src++;
-      }
-    }
-  }
-
-  *aSrcLength = src - aSrc;
-  *aDestLength  = dest - aDest;
-  return res;
-}
-
-nsresult nsBasicUTF7Encoder::EncodeBase64(
-                             const char16_t * aSrc, 
-                             int32_t * aSrcLength, 
-                             char * aDest, 
-                             int32_t * aDestLength)
-{
-  nsresult res = NS_OK;
-  const char16_t * src = aSrc;
-  const char16_t * srcEnd = aSrc + *aSrcLength;
-  char * dest = aDest;
-  char * destEnd = aDest + *aDestLength;
-  char16_t ch;
-  uint32_t value;
-
-  while (src < srcEnd) {
-    ch = *src;
-
-    // stop when we reach printable US-ASCII chars
-    if (DirectEncodable(ch)) break;
-
-    switch (mEncStep) {
-      case 0:
-        if (destEnd - dest < 2) {
-          res = NS_OK_UENC_MOREOUTPUT;
-          break;
-        }
-        value=ch>>10;
-        *(dest++)=ValueToChar(value);
-        value=(ch>>4)&0x3f;
-        *(dest++)=ValueToChar(value);
-        mEncBits=(ch&0x0f)<<2;
-        break;
-      case 1:
-        if (destEnd - dest < 3) {
-          res = NS_OK_UENC_MOREOUTPUT;
-          break;
-        }
-        value=mEncBits+(ch>>14);
-        *(dest++)=ValueToChar(value);
-        value=(ch>>8)&0x3f;
-        *(dest++)=ValueToChar(value);
-        value=(ch>>2)&0x3f;
-        *(dest++)=ValueToChar(value);
-        mEncBits=(ch&0x03)<<4;
-        break;
-      case 2:
-        if (destEnd - dest < 3) {
-          res = NS_OK_UENC_MOREOUTPUT;
-          break;
-        }
-        value=mEncBits+(ch>>12);
-        *(dest++)=ValueToChar(value);
-        value=(ch>>6)&0x3f;
-        *(dest++)=ValueToChar(value);
-        value=ch&0x3f;
-        *(dest++)=ValueToChar(value);
-        mEncBits=0;
-        break;
-    }
-
-    if (res != NS_OK) break;
-
-    src++;
-    (++mEncStep)%=3;
-  }
-
-  *aSrcLength = src - aSrc;
-  *aDestLength  = dest - aDest;
-  return res;
-}
-
-char nsBasicUTF7Encoder::ValueToChar(uint32_t aValue) { 
-  if (aValue < 26) 
-    return (char)('A'+aValue);
-  else if (aValue < 26 + 26) 
-    return (char)('a' + aValue - 26);
-  else if (aValue < 26 + 26 + 10)
-    return (char)('0' + aValue - 26 - 26);
-  else if (aValue == 26 + 26 + 10)
-    return '+';
-  else if (aValue == 26 + 26 + 10 + 1)
-    return mLastChar;
-  else
-    return -1;
-}
-
-bool nsBasicUTF7Encoder::DirectEncodable(char16_t aChar) {
-  // spec says: printable US-ASCII chars
-  if ((aChar >= 0x20) && (aChar <= 0x7e)) return true;
-  else return false;
-}
-
-//----------------------------------------------------------------------
-// Subclassing of nsEncoderSupport class [implementation]
-
-NS_IMETHODIMP nsBasicUTF7Encoder::ConvertNoBuffNoErr(
-                                  const char16_t * aSrc, 
-                                  int32_t * aSrcLength, 
-                                  char * aDest, 
-                                  int32_t * aDestLength)
-{
-  nsresult res = NS_OK;
-  const char16_t * src = aSrc;
-  const char16_t * srcEnd = aSrc + *aSrcLength;
-  char * dest = aDest;
-  char * destEnd = aDest + *aDestLength;
-  int32_t bcr,bcw;
-  char16_t ch;
-  int32_t enc;
-
-  while (src < srcEnd) {
-    // find the encoding for the next char
-    ch = *src;
-    if (DirectEncodable(ch)) 
-      enc = ENC_DIRECT;
-    else
-      enc = ENC_BASE64;
-
-    // if necessary, shift into the required encoding
-    bcw = destEnd - dest;
-    res = ShiftEncoding(enc, dest, &bcw);
-    dest += bcw;
-    if (res != NS_OK) break;
-
-    // now encode (as much as you can)
-    bcr = srcEnd - src;
-    bcw = destEnd - dest;
-    if (enc == ENC_DIRECT) 
-      res = EncodeDirect(src, &bcr, dest, &bcw);
-    else 
-      res = EncodeBase64(src, &bcr, dest, &bcw);
-    src += bcr;
-    dest += bcw;
-
-    if (res != NS_OK) break;
-  }
-
-  *aSrcLength = src - aSrc;
-  *aDestLength  = dest - aDest;
-  return res;
-}
-
-NS_IMETHODIMP nsBasicUTF7Encoder::FinishNoBuff(char * aDest, 
-                                               int32_t * aDestLength)
-{
-  return ShiftEncoding(ENC_DIRECT, aDest, aDestLength);
-}
-
-NS_IMETHODIMP nsBasicUTF7Encoder::Reset()
-{
-  mEncoding = ENC_DIRECT;
-  mEncBits = 0;
-  mEncStep = 0;
-  return nsEncoderSupport::Reset();
-}
-
-//----------------------------------------------------------------------
-// Class nsUnicodeToUTF7 [implementation]
-
-nsUnicodeToUTF7::nsUnicodeToUTF7() 
-: nsBasicUTF7Encoder('/', '+')
-{
-}
-
-
-bool nsUnicodeToUTF7::DirectEncodable(char16_t aChar) {
-  if ((aChar >= 'A') && (aChar <= 'Z')) return true;
-  else if ((aChar >= 'a') && (aChar <= 'z')) return true;
-  else if ((aChar >= '0') && (aChar <= '9')) return true;
-  else if ((aChar >= 39) && (aChar <= 41)) return true;
-  else if ((aChar >= 44) && (aChar <= 47)) return true;
-  else if (aChar == 58) return true;
-  else if (aChar == 63) return true;
-  else if (aChar == ' ') return true;
-  else if (aChar == 9) return true;
-  else if (aChar == 13) return true;
-  else if (aChar == 10) return true;
-  else if (aChar == 60) return true;  // '<'
-  else if (aChar == 33) return true;  // '!'
-  else if (aChar == 34) return true;  // '"'
-  else if (aChar == 62) return true;  // '>'
-  else if (aChar == 61) return true;  // '='
-  else if (aChar == 59) return true;  // ';'
-  else if (aChar == 91) return true;  // '['
-  else if (aChar == 93) return true;  // ']'
-  else return false;
-}
deleted file mode 100644
--- a/intl/uconv/ucvlatin/nsUnicodeToUTF7.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef nsUnicodeToUTF7_h___
-#define nsUnicodeToUTF7_h___
-
-#include "nsUCSupport.h"
-
-//----------------------------------------------------------------------
-// Class nsBasicUTF7Encoder [declaration]
-
-/**
- * Basic class for a character set converter from Unicode to UTF-7.
- *
- * @created         03/Jun/1999
- * @author  Catalin Rotaru [CATA]
- */
-class nsBasicUTF7Encoder : public nsEncoderSupport
-{
-public:
-
-  /**
-   * Class constructor.
-   */
-  nsBasicUTF7Encoder(char aLastChar, char aEscChar);
-
-protected:
-
-  int32_t                   mEncoding;      // current encoding
-  uint32_t                  mEncBits;
-  int32_t                   mEncStep;
-  char                      mLastChar;
-  char                      mEscChar;
-
-  nsresult ShiftEncoding(int32_t aEncoding, char * aDest, 
-      int32_t * aDestLength);
-  nsresult EncodeDirect(const char16_t * aSrc, int32_t * aSrcLength, 
-      char * aDest, int32_t * aDestLength);
-  nsresult EncodeBase64(const char16_t * aSrc, int32_t * aSrcLength, 
-      char * aDest, int32_t * aDestLength);
-  char ValueToChar(uint32_t aValue);
-  virtual bool DirectEncodable(char16_t aChar);
-
-  //--------------------------------------------------------------------
-  // Subclassing of nsEncoderSupport class [declaration]
-
-  NS_IMETHOD ConvertNoBuffNoErr(const char16_t * aSrc, int32_t * aSrcLength, 
-      char * aDest, int32_t * aDestLength);
-  NS_IMETHOD FinishNoBuff(char * aDest, int32_t * aDestLength);
-  NS_IMETHOD Reset();
-};
-
-//----------------------------------------------------------------------
-// Class nsUnicodeToUTF7 [declaration]
-
-/**
- * A character set converter from Unicode to UTF-7.
- *
- * @created         03/Jun/1999
- * @author  Catalin Rotaru [CATA]
- */
-class nsUnicodeToUTF7 : public nsBasicUTF7Encoder
-{
-public:
-
-  /**
-   * Class constructor.
-   */
-  nsUnicodeToUTF7();
-
-protected:
-
-  virtual bool DirectEncodable(char16_t aChar);
-};
-
-#endif /* nsUnicodeToUTF7_h___ */
--- a/ipc/chromium/src/base/message_loop.cc
+++ b/ipc/chromium/src/base/message_loop.cc
@@ -26,16 +26,19 @@
 #endif
 #ifdef MOZ_WIDGET_QT
 #include "base/message_pump_qt.h"
 #endif
 #endif
 #ifdef ANDROID
 #include "base/message_pump_android.h"
 #endif
+#ifdef MOZ_TASK_TRACER
+#include "GeckoTaskTracer.h"
+#endif
 
 #include "MessagePump.h"
 
 using base::Time;
 using base::TimeDelta;
 using base::TimeTicks;
 
 // A lazily created thread local storage for quick access to a thread's message
@@ -272,25 +275,35 @@ void MessageLoop::PostNonNestableTask(
 void MessageLoop::PostNonNestableDelayedTask(
     const tracked_objects::Location& from_here, Task* task, int delay_ms) {
   PostTask_Helper(from_here, task, delay_ms, false);
 }
 
 void MessageLoop::PostIdleTask(
     const tracked_objects::Location& from_here, Task* task) {
   DCHECK(current() == this);
+
+#ifdef MOZ_TASK_TRACER
+  task = mozilla::tasktracer::CreateTracedTask(task);
+#endif
+
   task->SetBirthPlace(from_here);
   PendingTask pending_task(task, false);
   deferred_non_nestable_work_queue_.push(pending_task);
 }
 
 // Possibly called on a background thread!
 void MessageLoop::PostTask_Helper(
     const tracked_objects::Location& from_here, Task* task, int delay_ms,
     bool nestable) {
+
+#ifdef MOZ_TASK_TRACER
+  task = mozilla::tasktracer::CreateTracedTask(task);
+#endif
+
   task->SetBirthPlace(from_here);
 
   PendingTask pending_task(task, nestable);
 
   if (delay_ms > 0) {
     pending_task.delayed_run_time =
         TimeTicks::Now() + TimeDelta::FromMilliseconds(delay_ms);
   } else {
--- a/ipc/chromium/src/base/thread.cc
+++ b/ipc/chromium/src/base/thread.cc
@@ -6,16 +6,20 @@
 
 #include "base/lazy_instance.h"
 #include "base/string_util.h"
 #include "base/thread_local.h"
 #include "base/waitable_event.h"
 #include "GeckoProfiler.h"
 #include "mozilla/IOInterposer.h"
 
+#ifdef MOZ_TASK_TRACER
+#include "GeckoTaskTracer.h"
+#endif
+
 namespace base {
 
 // This task is used to trigger the message loop to exit.
 class ThreadQuitTask : public Task {
  public:
   virtual void Run() {
     MessageLoop::current()->Quit();
     Thread::SetThreadWasQuitProperly(true);
@@ -167,14 +171,18 @@ void Thread::ThreadMain() {
   CleanUp();
 
   // Assert that MessageLoop::Quit was called by ThreadQuitTask.
   DCHECK(GetThreadWasQuitProperly());
 
   mozilla::IOInterposer::UnregisterCurrentThread();
   profiler_unregister_thread();
 
+#ifdef MOZ_TASK_TRACER
+  mozilla::tasktracer::FreeTraceInfo();
+#endif
+
   // We can't receive messages anymore.
   message_loop_ = NULL;
   thread_id_ = 0;
 }
 
 }  // namespace base
--- a/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
+++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
@@ -27,16 +27,21 @@
 #include "base/singleton.h"
 #include "base/stats_counters.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/file_descriptor_set_posix.h"
 #include "chrome/common/ipc_logging.h"
 #include "chrome/common/ipc_message_utils.h"
 #include "mozilla/ipc/ProtocolUtils.h"
 
+#ifdef MOZ_TASK_TRACER
+#include "GeckoTaskTracerImpl.h"
+using namespace mozilla::tasktracer;
+#endif
+
 namespace IPC {
 
 // IPC channels on Windows use named pipes (CreateNamedPipe()) with
 // channel ids as the pipe names.  Channels on POSIX use anonymous
 // Unix domain sockets created via socketpair() as pipes.  These don't
 // quite line up.
 //
 // When creating a child subprocess, the parent side of the fork
@@ -589,16 +594,24 @@ bool Channel::ChannelImpl::ProcessIncomi
           m.file_descriptor_set()->SetDescriptors(
               &fds[fds_i], m.header()->num_fds);
           fds_i += m.header()->num_fds;
         }
 #ifdef IPC_MESSAGE_DEBUG_EXTRA
         DLOG(INFO) << "received message on channel @" << this <<
                       " with type " << m.type();
 #endif
+
+#ifdef MOZ_TASK_TRACER
+        AutoSaveCurTraceInfo saveCurTraceInfo;
+        SetCurTraceInfo(m.header()->source_event_id,
+                        m.header()->parent_task_id,
+                        m.header()->source_event_type);
+#endif
+
         if (m.routing_id() == MSG_ROUTING_NONE &&
             m.type() == HELLO_MESSAGE_TYPE) {
           // The Hello message contains only the process id.
           listener_->OnChannelConnected(MessageIterator(m).NextInt());
 #if defined(OS_MACOSX)
         } else if (m.routing_id() == MSG_ROUTING_NONE &&
                    m.type() == RECEIVED_FDS_MESSAGE_TYPE) {
           DCHECK(m.fd_cookie() != 0);
@@ -682,16 +695,21 @@ bool Channel::ChannelImpl::ProcessOutgoi
           reinterpret_cast<int*>(CMSG_DATA(cmsg)));
       msgh.msg_controllen = cmsg->cmsg_len;
 
       msg->header()->num_fds = num_fds;
 #if defined(OS_MACOSX)
       msg->set_fd_cookie(++last_pending_fd_id_);
 #endif
     }
+#ifdef MOZ_TASK_TRACER
+    GetCurTraceInfo(&msg->header()->source_event_id,
+                    &msg->header()->parent_task_id,
+                    &msg->header()->source_event_type);
+#endif
 
     size_t amt_to_write = msg->size() - message_send_bytes_written_;
     DCHECK(amt_to_write != 0);
     const char *out_bytes = reinterpret_cast<const char*>(msg->data()) +
         message_send_bytes_written_;
 
     struct iovec iov = {const_cast<char*>(out_bytes), amt_to_write};
     msgh.msg_iov = &iov;
--- a/ipc/chromium/src/chrome/common/ipc_message.cc
+++ b/ipc/chromium/src/chrome/common/ipc_message.cc
@@ -5,30 +5,42 @@
 #include "chrome/common/ipc_message.h"
 
 #include "base/logging.h"
 #include "build/build_config.h"
 
 #if defined(OS_POSIX)
 #include "chrome/common/file_descriptor_set_posix.h"
 #endif
+#ifdef MOZ_TASK_TRACER
+#include "GeckoTaskTracer.h"
+#endif
+
+#ifdef MOZ_TASK_TRACER
+using namespace mozilla::tasktracer;
+#endif
 
 namespace IPC {
 
 //------------------------------------------------------------------------------
 
 Message::~Message() {
 }
 
 Message::Message()
     : Pickle(sizeof(Header)) {
   header()->routing = header()->type = header()->flags = 0;
 #if defined(OS_POSIX)
   header()->num_fds = 0;
 #endif
+#ifdef MOZ_TASK_TRACER
+  header()->source_event_id = 0;
+  header()->parent_task_id = 0;
+  header()->source_event_type = SourceEventType::UNKNOWN;
+#endif
   InitLoggingVariables();
 }
 
 Message::Message(int32_t routing_id, msgid_t type, PriorityValue priority,
                  MessageCompression compression, const char* const name)
     : Pickle(sizeof(Header)) {
   header()->routing = routing_id;
   header()->type = type;
@@ -39,28 +51,38 @@ Message::Message(int32_t routing_id, msg
   header()->num_fds = 0;
 #endif
   header()->interrupt_remote_stack_depth_guess = static_cast<uint32_t>(-1);
   header()->interrupt_local_stack_depth = static_cast<uint32_t>(-1);
   header()->seqno = 0;
 #if defined(OS_MACOSX)
   header()->cookie = 0;
 #endif
+#ifdef MOZ_TASK_TRACER
+  header()->source_event_id = 0;
+  header()->parent_task_id = 0;
+  header()->source_event_type = SourceEventType::UNKNOWN;
+#endif
   InitLoggingVariables(name);
 }
 
 Message::Message(const char* data, int data_len) : Pickle(data, data_len) {
   InitLoggingVariables();
 }
 
 Message::Message(const Message& other) : Pickle(other) {
   InitLoggingVariables(other.name_);
 #if defined(OS_POSIX)
   file_descriptor_set_ = other.file_descriptor_set_;
 #endif
+#ifdef MOZ_TASK_TRACER
+  header()->source_event_id = other.header()->source_event_id;
+  header()->parent_task_id = other.header()->parent_task_id;
+  header()->source_event_type = other.header()->source_event_type;
+#endif
 }
 
 void Message::InitLoggingVariables(const char* const name) {
   name_ = name;
 #ifdef IPC_MESSAGE_LOG_ENABLED
   received_time_ = 0;
   dont_log_ = false;
   log_data_ = NULL;
@@ -68,16 +90,21 @@ void Message::InitLoggingVariables(const
 }
 
 Message& Message::operator=(const Message& other) {
   *static_cast<Pickle*>(this) = other;
   InitLoggingVariables(other.name_);
 #if defined(OS_POSIX)
   file_descriptor_set_ = other.file_descriptor_set_;
 #endif
+#ifdef MOZ_TASK_TRACER
+  header()->source_event_id = other.header()->source_event_id;
+  header()->parent_task_id = other.header()->parent_task_id;
+  header()->source_event_type = other.header()->source_event_type;
+#endif
   return *this;
 }
 
 #ifdef IPC_MESSAGE_LOG_ENABLED
 void Message::set_sent_time(int64_t time) {
   DCHECK((header()->flags & HAS_SENT_TIME_BIT) == 0);
   header()->flags |= HAS_SENT_TIME_BIT;
   WriteInt64(time);
--- a/ipc/chromium/src/chrome/common/ipc_message.h
+++ b/ipc/chromium/src/chrome/common/ipc_message.h
@@ -5,16 +5,20 @@
 #ifndef CHROME_COMMON_IPC_MESSAGE_H__
 #define CHROME_COMMON_IPC_MESSAGE_H__
 
 #include <string>
 
 #include "base/basictypes.h"
 #include "base/pickle.h"
 
+#ifdef MOZ_TASK_TRACER
+#include "GeckoTaskTracer.h"
+#endif
+
 #ifndef NDEBUG
 #define IPC_MESSAGE_LOG_ENABLED
 #endif
 
 #if defined(OS_POSIX)
 #include "base/ref_counted.h"
 #endif
 
@@ -331,16 +335,21 @@ class Message : public Pickle {
 
       // For RPC and Urgent messages, a transaction ID for message ordering.
       int32_t txid;
     };
     // The actual local stack depth.
     uint32_t interrupt_local_stack_depth;
     // Sequence number
     int32_t seqno;
+#ifdef MOZ_TASK_TRACER
+    uint64_t source_event_id;
+    uint64_t parent_task_id;
+    mozilla::tasktracer::SourceEventType source_event_type;
+#endif
   };
 
   Header* header() {
     return headerT<Header>();
   }
   const Header* header() const {
     return headerT<Header>();
   }
--- a/ipc/unixsocket/UnixSocket.cpp
+++ b/ipc/unixsocket/UnixSocket.cpp
@@ -4,16 +4,21 @@
  * 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 "UnixSocket.h"
 #include "nsTArray.h"
 #include "nsXULAppAPI.h"
 #include <fcntl.h>
 
+#ifdef MOZ_TASK_TRACER
+#include "GeckoTaskTracer.h"
+using namespace mozilla::tasktracer;
+#endif
+
 static const size_t MAX_READ_SIZE = 1 << 16;
 
 namespace mozilla {
 namespace ipc {
 
 class UnixSocketImpl : public UnixSocketWatcher
 {
 public:
@@ -668,16 +673,22 @@ UnixSocketImpl::OnSocketCanReceiveWithou
       // cause us to end up back here.
       RemoveWatchers(READ_WATCHER|WRITE_WATCHER);
       nsRefPtr<RequestClosingSocketRunnable> r =
         new RequestClosingSocketRunnable(this);
       NS_DispatchToMainThread(r);
       return;
     }
 
+#ifdef MOZ_TASK_TRACER
+    // Make unix socket creation events to be the source events of TaskTracer,
+    // and originate the rest correlation tasks from here.
+    AutoSourceEvent taskTracerEvent(SourceEventType::UNIXSOCKET);
+#endif
+
     incoming->mSize = ret;
     nsRefPtr<SocketReceiveRunnable> r =
       new SocketReceiveRunnable(this, incoming.forget());
     NS_DispatchToMainThread(r);
 
     // If ret is less than MAX_READ_SIZE, there's no
     // more data in the socket for us to read now.
     if (ret < ssize_t(MAX_READ_SIZE)) {
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -232,16 +232,17 @@ typedef enum JSWhyMagic
     JS_LAZY_ARGUMENTS,           /* lazy arguments value on the stack */
     JS_OPTIMIZED_ARGUMENTS,      /* optimized-away 'arguments' value */
     JS_IS_CONSTRUCTING,          /* magic value passed to natives to indicate construction */
     JS_OVERWRITTEN_CALLEE,       /* arguments.callee has been overwritten */
     JS_BLOCK_NEEDS_CLONE,        /* value of static block object slot */
     JS_HASH_KEY_EMPTY,           /* see class js::HashableValue */
     JS_ION_ERROR,                /* error while running Ion code */
     JS_ION_BAILOUT,              /* status code to signal EnterIon will OSR into Interpret */
+    JS_OPTIMIZED_OUT,            /* optimized out slot */
     JS_GENERIC_MAGIC             /* for local use */
 } JSWhyMagic;
 
 #if defined(IS_LITTLE_ENDIAN)
 # if JS_BITS_PER_WORD == 32
 typedef union jsval_layout
 {
     uint64_t asBits;
@@ -1273,16 +1274,26 @@ IsPoisonedValue(const Value &v)
 {
     if (v.isString())
         return IsPoisonedPtr(v.toString());
     if (v.isObject())
         return IsPoisonedPtr(&v.toObject());
     return false;
 }
 
+inline bool
+IsOptimizedPlaceholderMagicValue(const Value &v)
+{
+    if (v.isMagic()) {
+        MOZ_ASSERT(v.whyMagic() == JS_OPTIMIZED_ARGUMENTS || v.whyMagic() == JS_OPTIMIZED_OUT);
+        return true;
+    }
+    return false;
+}
+
 /************************************************************************/
 
 static inline Value
 NullValue()
 {
     Value v;
     v.setNull();
     return v;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/lib/jitopts.js
@@ -0,0 +1,69 @@
+// These predicates are for tests that require a particular set of JIT options.
+
+// Check if toggles match. Useful for tests that shouldn't be run if a
+// different set of JIT toggles are set, since TBPL runs each jit-test
+// multiple times with a variety of flags.
+function jitTogglesMatch(opts) {
+  var currentOpts = getJitCompilerOptions();
+  for (var k in opts) {
+    if (k.indexOf(".enable") > 0 && opts[k] != currentOpts[k])
+      return false;
+  }
+  return true;
+}
+
+// Run fn under a particular set of JIT options.
+function withJitOptions(opts, fn) {
+  var oldOpts = getJitCompilerOptions();
+  for (var k in opts)
+    setJitCompilerOption(k, opts[k]);
+  try {
+    fn();
+  } finally {
+    for (var k in oldOpts)
+      setJitCompilerOption(k, oldOpts[k]);
+  }
+}
+
+// N.B. Ion opts *must come before* baseline opts because there's some kind of
+// "undo eager compilation" logic. If we don't set the baseline usecount
+// *after* the Ion usecount we end up setting the baseline usecount to be the
+// default if we hit the "undo eager compilation" logic.
+var Opts_BaselineEager =
+    {
+      'ion.enable': 1,
+      'baseline.enable': 1,
+      'baseline.usecount.trigger': 0,
+      'parallel-compilation.enable': 1
+    };
+
+// Checking for parallel compilation being off is often helpful if the test
+// requires a function be Ion compiled. Each individual test will usually
+// finish before the Ion compilation thread has a chance to attach the
+// compiled code.
+var Opts_IonEagerNoParallelCompilation =
+    {
+      'ion.enable': 1,
+      'ion.usecount.trigger': 0,
+      'baseline.enable': 1,
+      'baseline.usecount.trigger': 0,
+      'parallel-compilation.enable': 0,
+    };
+
+var Opts_Ion2NoParallelCompilation =
+    {
+      'ion.enable': 1,
+      'ion.usecount.trigger': 2,
+      'baseline.enable': 1,
+      'baseline.usecount.trigger': 1,
+      'parallel-compilation.enable': 0
+    };
+
+var Opts_NoJits =
+    {
+      'ion.enable': 0,
+      'ion.usecount.trigger': 0,
+      'baseline.usecount.trigger': 0,
+      'baseline.enable': 0,
+      'parallel-compilation.enable': 0
+    };
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Debugger-debuggees-22.js
@@ -0,0 +1,24 @@
+// Adding a debuggee allowed with scripts on stack.
+
+var g = newGlobal();
+g.dbg = new Debugger;
+
+g.eval("" + function f(d) {
+  g(d);
+  if (d)
+    assertEq(dbg.hasDebuggee(this), true);
+});
+
+g.eval("" + function g(d) {
+  if (!d)
+    return;
+
+  dbg.addDebuggee(this);
+});
+
+g.eval("(" + function test() {
+  f(false);
+  f(false);
+  f(true);
+  f(true);
+} + ")();");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Debugger-debuggees-23.js
@@ -0,0 +1,107 @@
+// Adding a debuggee allowed with scripts on stack from stranger places.
+
+// Test CCW.
+(function testCCW() {
+  var g = newGlobal();
+  var dbg = new Debugger;
+  g.dbg = dbg;
+  g.GLOBAL = g;
+
+  g.turnOnDebugger = function () {
+    dbg.addDebuggee(g);
+  };
+
+  g.eval("" + function f(d) {
+    turnOnDebugger();
+    assertEq(dbg.hasDebuggee(GLOBAL), true);
+  });
+
+  g.eval("(" + function test() {
+    f(false);
+    f(false);
+    f(true);
+    f(true);
+  } + ")();");
+})();
+
+// Test getter.
+(function testGetter() {
+  var g = newGlobal();
+  g.dbg = new Debugger;
+  g.GLOBAL = g;
+
+  g.eval("" + function f(obj) {
+    obj.foo;
+    assertEq(dbg.hasDebuggee(GLOBAL), true);
+  });
+
+  g.eval("(" + function test() {
+    f({ get foo() { dbg.addDebuggee(GLOBAL); } });
+  } + ")();");
+})();
+
+// Test setter.
+(function testSetter() {
+  var g = newGlobal();
+  g.dbg = new Debugger;
+  g.GLOBAL = g;
+
+  g.eval("" + function f(obj) {
+    obj.foo = 42;
+    assertEq(dbg.hasDebuggee(GLOBAL), true);
+  });
+
+  g.eval("(" + function test() {
+    f({ set foo(v) { dbg.addDebuggee(GLOBAL); } });
+  } + ")();");
+})();
+
+// Test toString.
+(function testToString() {
+  var g = newGlobal();
+  g.dbg = new Debugger;
+  g.GLOBAL = g;
+
+  g.eval("" + function f(obj) {
+    obj + "";
+    assertEq(dbg.hasDebuggee(GLOBAL), true);
+  });
+
+  g.eval("(" + function test() {
+    f({ toString: function () { dbg.addDebuggee(GLOBAL); }});
+  } + ")();");
+})();
+
+// Test valueOf.
+(function testValueOf() {
+  var g = newGlobal();
+  g.dbg = new Debugger;
+  g.GLOBAL = g;
+
+  g.eval("" + function f(obj) {
+    obj + "";
+    assertEq(dbg.hasDebuggee(GLOBAL), true);
+  });
+
+  g.eval("(" + function test() {
+    f({ valueOf: function () { dbg.addDebuggee(GLOBAL); }});
+  } + ")();");
+})();
+
+// Test proxy trap.
+(function testProxyTrap() {
+  var g = newGlobal();
+  g.dbg = new Debugger;
+  g.GLOBAL = g;
+
+  g.eval("" + function f(proxy) {
+    proxy["foo"];
+    assertEq(dbg.hasDebuggee(GLOBAL), true);
+  });
+
+  g.eval("(" + function test() {
+    var handler = { get: function () { dbg.addDebuggee(GLOBAL); } };
+    var proxy = new Proxy({}, handler);
+    f(proxy);
+  } + ")();");
+})();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Debugger-debuggees-24.js
@@ -0,0 +1,55 @@
+// Turning debugger on for a particular global with on-stack scripts shouldn't
+// make other globals' scripts observable.
+
+var g1 = newGlobal();
+var g2 = newGlobal();
+var g3 = newGlobal();
+
+g1.eval("" + function f() {
+  var name = "f";
+  g();
+  return name;
+});
+g2.eval("" + function g() {
+  var name = "g";
+  h();
+  return name;
+});
+g3.eval("" + function h() {
+  var name = "h";
+  toggle();
+  return name;
+});
+
+g1.g = g2.g;
+g2.h = g3.h;
+
+function name(f) {
+  return f.environment.getVariable("name");
+}
+
+var dbg = new Debugger;
+g3.toggle = function () {
+  var frame;
+
+  // Only f should be visible.
+  dbg.addDebuggee(g1);
+  frame = dbg.getNewestFrame();
+  assertEq(name(frame), "f");
+
+  // Now h should also be visible.
+  dbg.addDebuggee(g3);
+  frame = dbg.getNewestFrame();
+  assertEq(name(frame), "h");
+  assertEq(name(frame.older), "f");
+
+  // Finally everything should be visible.
+  dbg.addDebuggee(g2);
+  frame = dbg.getNewestFrame();
+  assertEq(name(frame), "h");
+  assertEq(name(frame.older), "g");
+  assertEq(name(frame.older.older), "f");
+};
+
+g1.eval("(" + function () { f(); } + ")();");
+
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Debugger-debuggees-25.js
@@ -0,0 +1,48 @@
+// Turning debugger off global at a time.
+
+var g1 = newGlobal();
+var g2 = newGlobal();
+var g3 = newGlobal();
+
+g1.eval("" + function f() {
+  var name = "f";
+  g();
+  return name;
+});
+g2.eval("" + function g() {
+  var name = "g";
+  h();
+  return name;
+});
+g3.eval("" + function h() {
+  var name = "h";
+  toggle();
+  return name;
+});
+
+g1.g = g2.g;
+g2.h = g3.h;
+
+function name(f) {
+  return f.environment.getVariable("name");
+}
+
+var dbg = new Debugger;
+g3.toggle = function () {
+  var frame;
+
+  // Add all globals.
+  dbg.addDebuggee(g1);
+  dbg.addDebuggee(g3);
+  dbg.addDebuggee(g2);
+
+  // Remove one at a time.
+  dbg.removeDebuggee(g3);
+  assertEq(name(dbg.getNewestFrame()), "g");
+  dbg.removeDebuggee(g2);
+  assertEq(name(dbg.getNewestFrame()), "f");
+  dbg.removeDebuggee(g1);
+};
+
+g1.eval("(" + function () { f(); } + ")();");
+
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Debugger-debuggees-26.js
@@ -0,0 +1,34 @@
+// Ion can bail in-place when throwing exceptions with debug mode toggled on.
+
+load(libdir + "jitopts.js");
+
+if (!jitTogglesMatch(Opts_Ion2NoParallelCompilation))
+  quit();
+
+withJitOptions(Opts_Ion2NoParallelCompilation, function () {
+  var g = newGlobal();
+  var dbg = new Debugger;
+
+  g.toggle = function toggle(x, d) {
+    if (d) {
+      dbg.addDebuggee(g);
+      var frame = dbg.getNewestFrame().older;
+      assertEq(frame.callee.name, "f");
+      assertEq(frame.implementation, "ion");
+      throw 42;
+    }
+  };
+
+  g.eval("" + function f(x, d) { g(x, d); });
+  g.eval("" + function g(x, d) { toggle(x, d); });
+
+  try {
+    g.eval("(" + function test() {
+      for (var i = 0; i < 5; i++)
+        f(42, false);
+      f(42, true);
+    } + ")();");
+  } catch (exc) {
+    assertEq(exc, 42);
+  }
+});
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Environment-getVariable-13.js
@@ -0,0 +1,47 @@
+// Tests that we can use debug scopes with Ion frames.
+//
+// Unfortunately these tests are brittle. They depend on opaque JIT heuristics
+// kicking in.
+
+load(libdir + "jitopts.js");
+
+if (!jitTogglesMatch(Opts_Ion2NoParallelCompilation))
+  quit(0);
+
+withJitOptions(Opts_Ion2NoParallelCompilation, function () {
+  var g = newGlobal();
+  var dbg = new Debugger;
+
+  // Note that this *depends* on CCW scripted functions being opaque to Ion
+  // optimization and not deoptimizing the frames below the call to toggle.
+  g.toggle = function toggle(d) {
+    if (d) {
+      dbg.addDebuggee(g);
+      var frame = dbg.getNewestFrame();
+      assertEq(frame.implementation, "ion");
+      // g is heavyweight but its call object is optimized out, because its
+      // arguments and locals are unaliased.
+      //
+      // Calling frame.environment here should make a fake debug scope that
+      // gets things directly from the frame. Calling frame.arguments doesn't
+      // go through the scope object and reads directly off the frame. Assert
+      // that the two are equal.
+      assertEq(frame.environment.getVariable("x"), frame.arguments[1]);
+    }
+  };
+
+  g.eval("" + function f(d, x) { g(d, x); });
+  g.eval("" + function g(d, x) {
+    for (var i = 0; i < 200; i++);
+    function inner() { i = 42; };
+    toggle(d);
+    // Use x so it doesn't get optimized out.
+    x++;
+  });
+
+  g.eval("(" + function test() {
+    for (i = 0; i < 5; i++)
+      f(false, 42);
+    f(true, 42);
+  } + ")();");
+});
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Frame-eval-19.js
@@ -0,0 +1,34 @@
+// Eval-in-frame of optimized frames to break out of an infinite loop.
+
+load(libdir + "jitopts.js");
+
+if (!jitTogglesMatch(Opts_IonEagerNoParallelCompilation))
+  quit(0);
+
+withJitOptions(Opts_IonEagerNoParallelCompilation, function () {
+  var g = newGlobal();
+  var dbg = new Debugger;
+
+  g.eval("" + function f(d) { g(d); });
+  g.eval("" + function g(d) { h(d); });
+  g.eval("" + function h(d) {
+    var i = 0;
+    while (d)
+      interruptIf(d && i++ == 4000);
+  });
+
+  setInterruptCallback(function () {
+    dbg.addDebuggee(g);
+    var frame = dbg.getNewestFrame();
+    if (frame.callee.name != "h" || frame.implementation != "ion")
+      return true;
+    frame.eval("d = false;");
+    return true;
+  });
+
+  g.eval("(" + function () {
+    for (i = 0; i < 5; i++)
+      f(false);
+    f(true);
+  } + ")();");
+});
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Frame-eval-20.js
@@ -0,0 +1,46 @@
+// Eval-in-frame with different type on non-youngest Ion frame.
+
+load(libdir + "jitopts.js");
+
+if (!jitTogglesMatch(Opts_Ion2NoParallelCompilation))
+  quit(0);
+
+withJitOptions(Opts_Ion2NoParallelCompilation, function () {
+  function test(shadow) {
+    var g = newGlobal();
+    var dbg = new Debugger;
+
+    // Note that we depend on CCW scripted functions being opaque to Ion
+    // optimization for this test.
+    g.h = function h(d) {
+      if (d) {
+        dbg.addDebuggee(g);
+        var f = dbg.getNewestFrame().older;
+        assertEq(f.implementation, "ion");
+        assertEq(f.environment.getVariable("foo"), 42);
+
+        // EIF of a different type too.
+        f.eval((shadow ? "var " : "") + "foo = 'string of 42'");
+        g.expected = shadow ? 42 : "string of 42";
+      }
+    }
+
+    g.eval("" + function f(d) {
+      var foo = 42;
+      g(d);
+      return foo;
+    });
+    g.eval("" + function g(d) {
+      h(d);
+    });
+
+    g.eval("(" + function () {
+      for (i = 0; i < 5; i++)
+        f(false);
+      assertEq(f(true), "string of 42");
+    } + ")();");
+  }
+
+  test(false);
+  test(true);
+});
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Frame-eval-21.js
@@ -0,0 +1,33 @@
+// Eval-in-frame with different type on baseline frame with let-scoping
+
+load(libdir + "jitopts.js");
+
+if (!jitTogglesMatch(Opts_BaselineEager))
+  quit(0);
+
+withJitOptions(Opts_BaselineEager, function () {
+  var g = newGlobal();
+  var dbg = new Debugger;
+
+  g.h = function h(d) {
+    if (d) {
+      dbg.addDebuggee(g);
+      var f = dbg.getNewestFrame().older;
+      assertEq(f.implementation, "baseline");
+      assertEq(f.environment.getVariable("foo"), 42);
+      f.eval("foo = 'string of 42'");
+    }
+  }
+
+  g.eval("" + function f(d) {
+    if (d) {
+      let foo = 42;
+      g(d);
+      return foo;
+    }
+  });
+
+  g.eval("" + function g(d) { h(d); });
+
+  g.eval("(" + function () { assertEq(f(true), "string of 42"); } + ")();");
+});
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Frame-eval-22.js
@@ -0,0 +1,32 @@
+// Debugger.Frame preserves Ion frame identity
+
+load(libdir + "jitopts.js");
+
+if (!jitTogglesMatch(Opts_Ion2NoParallelCompilation))
+  quit();
+
+withJitOptions(Opts_Ion2NoParallelCompilation, function () {
+  var g = newGlobal();
+  var dbg1 = new Debugger;
+  var dbg2 = new Debugger;
+
+  g.toggle = function toggle(x, d) {
+    if (d) {
+      dbg1.addDebuggee(g);
+      dbg2.addDebuggee(g);
+      var frame1 = dbg1.getNewestFrame();
+      assertEq(frame1.environment.getVariable("x"), x);
+      assertEq(frame1.implementation, "ion");
+      frame1.environment.setVariable("x", "not 42");
+      assertEq(dbg2.getNewestFrame().environment.getVariable("x"), "not 42");
+    }
+  };
+
+  g.eval("" + function f(x, d) { toggle(x, d); });
+
+  g.eval("(" + function test() {
+    for (var i = 0; i < 5; i++)
+      f(42, false);
+    f(42, true);
+  } + ")();");
+});
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Frame-eval-23.js
@@ -0,0 +1,37 @@
+// Debugger.Frame preserves Ion frame mutations after removing debuggee.
+
+load(libdir + "jitopts.js");
+
+if (!jitTogglesMatch(Opts_Ion2NoParallelCompilation))
+  quit();
+
+withJitOptions(Opts_Ion2NoParallelCompilation, function () {
+  var g = newGlobal();
+  var dbg = new Debugger;
+
+  g.toggle = function toggle(x, d) {
+    if (d) {
+      dbg.addDebuggee(g);
+      var frame = dbg.getNewestFrame().older;
+      assertEq(frame.callee.name, "f");
+      assertEq(frame.environment.getVariable("x"), x);
+      assertEq(frame.implementation, "ion");
+      frame.environment.setVariable("x", "not 42");
+      dbg.removeDebuggee(g);
+    }
+  };
+
+  g.eval("" + function f(x, d) {
+    g(x, d);
+    if (d)
+      assertEq(x, "not 42");
+  });
+
+  g.eval("" + function g(x, d) { toggle(x, d); });
+
+  g.eval("(" + function test() {
+    for (var i = 0; i < 5; i++)
+      f(42, false);
+    f(42, true);
+  } + ")();");
+});
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Frame-implementation-01.js
@@ -0,0 +1,45 @@
+// Debugger.Frames of all implementations.
+
+load(libdir + "jitopts.js");
+
+function testFrameImpl(jitopts, assertFrameImpl) {
+  if (!jitTogglesMatch(jitopts))
+    return;
+
+  withJitOptions(jitopts, function () {
+    var g = newGlobal();
+    var dbg = new Debugger;
+
+    g.toggle = function toggle(d) {
+      if (d) {
+        dbg.addDebuggee(g);
+        var frame = dbg.getNewestFrame();
+        // We only care about the f and g frames.
+        for (var i = 0; i < 2; i++) {
+          assertFrameImpl(frame);
+          frame = frame.older;
+        }
+      }
+    };
+
+    g.eval("" + function f(d) { g(d); });
+    g.eval("" + function g(d) { toggle(d); });
+
+    g.eval("(" + function test() {
+      for (var i = 0; i < 5; i++)
+        f(false);
+      f(true);
+    } + ")();");
+  });
+}
+
+[[Opts_BaselineEager,
+  function (f) { assertEq(f.implementation, "baseline"); }],
+ // Note that the Ion case *depends* on CCW scripted functions being opaque to
+ // Ion optimization and not deoptimizing the frames below the call to toggle.
+ [Opts_Ion2NoParallelCompilation,
+  function (f) { assertEq(f.implementation, "ion"); }],
+ [Opts_NoJits,
+  function (f) { assertEq(f.implementation, "interpreter"); }]].forEach(function ([opts, fn]) {
+    testFrameImpl(opts, fn);
+  });
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Frame-implementation-02.js
@@ -0,0 +1,51 @@
+// Test that Ion frames are invalidated by turning on Debugger. Invalidation
+// is unobservable, but we know that Ion scripts cannot handle Debugger
+// handlers, so we test for the handlers being called.
+
+load(libdir + "jitopts.js");
+
+if (!jitTogglesMatch(Opts_Ion2NoParallelCompilation))
+  quit();
+
+withJitOptions(Opts_Ion2NoParallelCompilation, function () {
+  var g = newGlobal();
+  var dbg = new Debugger;
+  var onPopExecuted = false;
+  var breakpointHit = false;
+
+  g.toggle = function toggle(d) {
+    if (d) {
+      dbg.addDebuggee(g);
+
+      var frame1 = dbg.getNewestFrame();
+      assertEq(frame1.implementation, "ion");
+      frame1.onPop = function () {
+        onPopExecuted = true;
+      };
+
+      var frame2 = frame1.older;
+      assertEq(frame2.implementation, "ion");
+      // Offset of |print(42 + 42)|
+      var offset = frame2.script.getLineOffsets(3)[0];
+      frame2.script.setBreakpoint(offset, { hit: function (fr) {
+        assertEq(fr.implementation != "ion", true);
+        breakpointHit = true;
+      }});
+    }
+  };
+
+  g.eval("" + function f(d) {
+    g(d);
+    print(42 + 42);
+  });
+  g.eval("" + function g(d) { toggle(d); });
+
+  g.eval("(" + function test() {
+    for (var i = 0; i < 5; i++)
+      f(false);
+    f(true);
+  } + ")();");
+
+  assertEq(onPopExecuted, true);
+  assertEq(breakpointHit, true);
+});
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/optimized-out-01.js
@@ -0,0 +1,42 @@
+// Tests that we can reflect optimized out values.
+//
+// Unfortunately these tests are brittle. They depend on opaque JIT heuristics
+// kicking in.
+
+load(libdir + "jitopts.js");
+
+if (!jitTogglesMatch(Opts_Ion2NoParallelCompilation))
+  quit(0);
+
+withJitOptions(Opts_Ion2NoParallelCompilation, function () {
+  var g = newGlobal();
+  var dbg = new Debugger;
+
+  // Note that this *depends* on CCW scripted functions being opaque to Ion
+  // optimization and not deoptimizing the frames below the call to toggle.
+  g.toggle = function toggle(d) {
+    if (d) {
+      dbg.addDebuggee(g);
+      var frame = dbg.getNewestFrame();
+      assertEq(frame.implementation, "ion");
+      // x is unused and should be elided.
+      assertEq(frame.environment.getVariable("x").optimizedOut, true);
+      assertEq(frame.arguments[1].optimizedOut, true);
+    }
+  };
+
+  g.eval("" + function f(d, x) { g(d, x); });
+
+  g.eval("" + function g(d, x) {
+    for (var i = 0; i < 200; i++);
+    // Hack to prevent inlining.
+    function inner() { i = 42; };
+    toggle(d);
+  });
+
+  g.eval("(" + function test() {
+    for (i = 0; i < 5; i++)
+      f(false, 42);
+    f(true, 42);
+  } + ")();");
+});
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/resumption-08.js
@@ -0,0 +1,93 @@
+// Check whether we respect resumption values when toggling debug mode on->off
+// from various points with live scripts on the stack.
+
+var g = newGlobal();
+var dbg = new Debugger;
+
+function reset() {
+  dbg.onEnterFrame = undefined;
+  dbg.onDebuggerStatement = undefined;
+  dbg.addDebuggee(g);
+  g.eval("(" + function test() {
+    for (i = 0; i < 5; i++)
+      f(42);
+  } + ")();");
+}
+
+g.eval("" + function f(d) {
+  return g(d);
+});
+
+g.eval("" + function g(d) {
+  debugger;
+  return d;
+});
+
+function testResumptionValues(handlerSetter) {
+  // Test normal return.
+  reset();
+  handlerSetter(undefined);
+  assertEq(g.eval("(" + function test() { return f(42); } + ")();"), 42);
+
+  // Test forced return.
+  reset();
+  handlerSetter({ return: "not 42" });
+  assertEq(g.eval("(" + function test() { return f(42); } + ")();"), "not 42");
+
+  // Test throw.
+  reset();
+  handlerSetter({ throw: "thrown 42" });
+  try {
+    g.eval("(" + function test() { return f(42); } + ")();");;
+  } catch (e) {
+    assertEq(e, "thrown 42");
+  }
+}
+
+// Turn off from within the prologue.
+testResumptionValues(function (resumptionVal) {
+  dbg.onEnterFrame = function (frame) {
+    if (frame.older) {
+      if (frame.older.older) {
+        dbg.removeDebuggee(g);
+        return resumptionVal;
+      }
+    }
+  };
+});
+
+// Turn off from within the epilogue.
+testResumptionValues(function (resumptionVal) {
+  dbg.onEnterFrame = function (frame) {
+    if (frame.older) {
+      if (frame.older.older) {
+        frame.onPop = function () {
+          dbg.removeDebuggee(g);
+          return resumptionVal;
+        };
+      }
+    }
+  };
+});
+
+// Turn off from within debugger statement handler.
+testResumptionValues(function (resumptionVal) {
+  dbg.onDebuggerStatement = function (frame) {
+    dbg.removeDebuggee(g);
+    return resumptionVal;
+  };
+});
+
+// Turn off from within debug trap handler.
+testResumptionValues(function (resumptionVal) {
+  dbg.onEnterFrame = function (frame) {
+    if (frame.older) {
+      if (frame.older.older) {
+        frame.onStep = function () {
+          dbg.removeDebuggee(g);
+          return resumptionVal;
+        }
+      }
+    }
+  };
+});
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/array-splice.js
@@ -0,0 +1,27 @@
+function test1() {
+    // splice GetElement calls are observable and should be executed even if
+    // the return value of splice is unused.
+    Object.defineProperty(Object.prototype, "0", {get: function() {
+	c++;
+    }, set: function() {}});
+    var arr = [,,,];
+    var c = 0;
+    for (var i=0; i<100; i++) {
+	arr.splice(0, 1);
+	arr.length = 1;
+    }
+
+    assertEq(c, 100);
+}
+test1();
+
+function test2() {
+    var arr = [];
+    for (var i=0; i<100; i++)
+	arr.push(i);
+    for (var i=0; i<40; i++)
+	arr.splice(0, 2);
+    assertEq(arr.length, 20);
+    assertEq(arr[0], 80);
+}
+test2();
--- a/js/src/jit/Bailouts.cpp
+++ b/js/src/jit/Bailouts.cpp
@@ -162,33 +162,59 @@ IonBailoutIterator::IonBailoutIterator(c
     current_ = (uint8_t *) frame.fp();
     type_ = JitFrame_IonJS;
     topFrameSize_ = frame.frameSize();
     snapshotOffset_ = osiIndex->snapshotOffset();
 }
 
 uint32_t
 jit::ExceptionHandlerBailout(JSContext *cx, const InlineFrameIterator &frame,
+                             ResumeFromException *rfe,
                              const ExceptionBailoutInfo &excInfo,
-                             BaselineBailoutInfo **bailoutInfo)
+                             bool *overrecursed)
 {
-    JS_ASSERT(cx->isExceptionPending());
+    // We can be propagating debug mode exceptions without there being an
+    // actual exception pending. For instance, when we return false from an
+    // operation callback like a timeout handler.
+    MOZ_ASSERT_IF(!excInfo.propagatingIonExceptionForDebugMode(), cx->isExceptionPending());
 
     cx->mainThread().ionTop = nullptr;
     JitActivationIterator jitActivations(cx->runtime());
     IonBailoutIterator iter(jitActivations, frame.frame());
     JitActivation *activation = jitActivations.activation()->asJit();
 
-    *bailoutInfo = nullptr;
-    uint32_t retval = BailoutIonToBaseline(cx, activation, iter, true, bailoutInfo, &excInfo);
-    JS_ASSERT(retval == BAILOUT_RETURN_OK ||
-              retval == BAILOUT_RETURN_FATAL_ERROR ||
-              retval == BAILOUT_RETURN_OVERRECURSED);
+    BaselineBailoutInfo *bailoutInfo = nullptr;
+    uint32_t retval = BailoutIonToBaseline(cx, activation, iter, true, &bailoutInfo, &excInfo);
+
+    if (retval == BAILOUT_RETURN_OK) {
+        MOZ_ASSERT(bailoutInfo);
+
+        // Overwrite the kind so HandleException after the bailout returns
+        // false, jumping directly to the exception tail.
+        if (excInfo.propagatingIonExceptionForDebugMode())
+            bailoutInfo->bailoutKind = Bailout_IonExceptionDebugMode;
 
-    JS_ASSERT((retval == BAILOUT_RETURN_OK) == (*bailoutInfo != nullptr));
+        rfe->kind = ResumeFromException::RESUME_BAILOUT;
+        rfe->target = cx->runtime()->jitRuntime()->getBailoutTail()->raw();
+        rfe->bailoutInfo = bailoutInfo;
+    } else {
+        // Bailout failed. If there was a fatal error, clear the
+        // exception to turn this into an uncatchable error. If the
+        // overrecursion check failed, continue popping all inline
+        // frames and have the caller report an overrecursion error.
+        MOZ_ASSERT(!bailoutInfo);
+
+        if (!excInfo.propagatingIonExceptionForDebugMode())
+            cx->clearPendingException();
+
+        if (retval == BAILOUT_RETURN_OVERRECURSED)
+            *overrecursed = true;
+        else
+            MOZ_ASSERT(retval == BAILOUT_RETURN_FATAL_ERROR);
+    }
 
     return retval;
 }
 
 // Initialize the decl env Object, call object, and any arguments obj of the current frame.
 bool
 jit::EnsureHasScopeObjects(JSContext *cx, AbstractFramePtr fp)
 {
--- a/js/src/jit/Bailouts.h
+++ b/js/src/jit/Bailouts.h
@@ -150,28 +150,62 @@ struct BaselineBailoutInfo;
 
 // Called from a bailout thunk. Returns a BAILOUT_* error code.
 uint32_t Bailout(BailoutStack *sp, BaselineBailoutInfo **info);
 
 // Called from the invalidation thunk. Returns a BAILOUT_* error code.
 uint32_t InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut,
                              BaselineBailoutInfo **info);
 
-struct ExceptionBailoutInfo
+class ExceptionBailoutInfo
 {
-    size_t frameNo;
-    jsbytecode *resumePC;
-    size_t numExprSlots;
+    size_t frameNo_;
+    jsbytecode *resumePC_;
+    size_t numExprSlots_;
+
+  public:
+    ExceptionBailoutInfo(size_t frameNo, jsbytecode *resumePC, size_t numExprSlots)
+      : frameNo_(frameNo),
+        resumePC_(resumePC),
+        numExprSlots_(numExprSlots)
+    { }
+
+    ExceptionBailoutInfo()
+      : frameNo_(0),
+        resumePC_(nullptr),
+        numExprSlots_(0)
+    { }
+
+    bool catchingException() const {
+        return !!resumePC_;
+    }
+    bool propagatingIonExceptionForDebugMode() const {
+        return !resumePC_;
+    }
+
+    size_t frameNo() const {
+        MOZ_ASSERT(catchingException());
+        return frameNo_;
+    }
+    jsbytecode *resumePC() const {
+        MOZ_ASSERT(catchingException());
+        return resumePC_;
+    }
+    size_t numExprSlots() const {
+        MOZ_ASSERT(catchingException());
+        return numExprSlots_;
+    }
 };
 
 // Called from the exception handler to enter a catch or finally block.
 // Returns a BAILOUT_* error code.
 uint32_t ExceptionHandlerBailout(JSContext *cx, const InlineFrameIterator &frame,
+                                 ResumeFromException *rfe,
                                  const ExceptionBailoutInfo &excInfo,
-                                 BaselineBailoutInfo **bailoutInfo);
+                                 bool *overrecursed);
 
 uint32_t FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo);
 
 bool CheckFrequentBailouts(JSContext *cx, JSScript *script);
 
 } // namespace jit
 } // namespace js
 
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -6,17 +6,20 @@
 
 #include "jsprf.h"
 #include "jit/arm/Simulator-arm.h"
 #include "jit/BaselineIC.h"
 #include "jit/BaselineJIT.h"
 #include "jit/CompileInfo.h"
 #include "jit/IonSpewer.h"
 #include "jit/Recover.h"
+#include "jit/RematerializedFrame.h"
+
 #include "vm/ArgumentsObject.h"
+#include "vm/Debugger.h"
 #include "vm/TraceLogging.h"
 
 #include "jsscriptinlines.h"
 
 #include "jit/IonFrames-inl.h"
 
 using namespace js;
 using namespace js::jit;
@@ -341,19 +344,19 @@ struct BaselineStackBuilder
         // so we can calculate it directly.  For other archs, the previous frame pointer
         // is stored on the stack in the frame that precedes the rectifier frame.
         size_t priorOffset = IonJSFrameLayout::Size() + topFrame->prevFrameLocalSize();
 #if defined(JS_CODEGEN_X86)
         // On X86, the FramePointer is pushed as the first value in the Rectifier frame.
         JS_ASSERT(BaselineFrameReg == FramePointer);
         priorOffset -= sizeof(void *);
         return virtualPointerAtStackOffset(priorOffset);
-#elif defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM)
-        // On X64 and ARM, the frame pointer save location depends on the caller of the
-        // the rectifier frame.
+#elif defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
+        // On X64, ARM and MIPS, the frame pointer save location depends on
+        // the caller of the rectifier frame.
         BufferPointer<IonRectifierFrameLayout> priorFrame =
             pointerAtStackOffset<IonRectifierFrameLayout>(priorOffset);
         FrameType priorType = priorFrame->prevType();
         JS_ASSERT(priorType == JitFrame_IonJS || priorType == JitFrame_BaselineStub);
 
         // If the frame preceding the rectifier is an IonJS frame, then once again
         // the frame pointer does not matter.
         if (priorType == JitFrame_IonJS)
@@ -379,22 +382,22 @@ IsInlinableFallback(ICFallbackStub *icEn
     return icEntry->isCall_Fallback() || icEntry->isGetProp_Fallback() ||
            icEntry->isSetProp_Fallback();
 }
 
 static inline void*
 GetStubReturnAddress(JSContext *cx, jsbytecode *pc)
 {
     if (IsGetPropPC(pc))
-        return cx->compartment()->jitCompartment()->baselineGetPropReturnAddr();
+        return cx->compartment()->jitCompartment()->baselineGetPropReturnFromIonAddr();
     if (IsSetPropPC(pc))
-        return cx->compartment()->jitCompartment()->baselineSetPropReturnAddr();
+        return cx->compartment()->jitCompartment()->baselineSetPropReturnFromIonAddr();
     // This should be a call op of some kind, now.
     JS_ASSERT(IsCallPC(pc));
-    return cx->compartment()->jitCompartment()->baselineCallReturnAddr();
+    return cx->compartment()->jitCompartment()->baselineCallReturnFromIonAddr();
 }
 
 static inline jsbytecode *
 GetNextNonLoopEntryPc(jsbytecode *pc)
 {
     JSOp op = JSOp(*pc);
     if (op == JSOP_GOTO)
         return pc + GET_JUMP_OFFSET(pc);
@@ -479,22 +482,26 @@ static bool
 InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
                 HandleFunction fun, HandleScript script, IonScript *ionScript,
                 SnapshotIterator &iter, bool invalidate, BaselineStackBuilder &builder,
                 AutoValueVector &startFrameFormals, MutableHandleFunction nextCallee,
                 jsbytecode **callPC, const ExceptionBailoutInfo *excInfo)
 {
     MOZ_ASSERT(script->hasBaselineScript());
 
-    // If excInfo is non-nullptr, we are bailing out to a catch or finally block
-    // and this is the frame where we will resume. Usually the expression stack
-    // should be empty in this case but there can be iterators on the stack.
+    // Are we catching an exception?
+    bool catchingException = excInfo && excInfo->catchingException();
+
+    // If we are catching an exception, we are bailing out to a catch or
+    // finally block and this is the frame where we will resume. Usually the
+    // expression stack should be empty in this case but there can be
+    // iterators on the stack.
     uint32_t exprStackSlots;
-    if (excInfo)
-        exprStackSlots = excInfo->numExprSlots;
+    if (catchingException)
+        exprStackSlots = excInfo->numExprSlots();
     else
         exprStackSlots = iter.numAllocations() - (script->nfixed() + CountArgSlots(script, fun));
 
     builder.resetFramePushed();
 
     // Build first baseline frame:
     // +===============+
     // | PrevFramePtr  |
@@ -583,17 +590,17 @@ InitFromBailout(JSContext *cx, HandleScr
         }
     } else {
         Value v = iter.read();
         if (v.isObject()) {
             scopeChain = &v.toObject();
             if (fun && fun->isHeavyweight())
                 flags |= BaselineFrame::HAS_CALL_OBJ;
         } else {
-            JS_ASSERT(v.isUndefined());
+            JS_ASSERT(v.isUndefined() || v.isMagic(JS_OPTIMIZED_OUT));
 
             // Get scope chain from function or script.
             if (fun) {
                 // If pcOffset == 0, we may have to push a new call object, so
                 // we leave scopeChain nullptr and enter baseline code before
                 // the prologue.
                 if (iter.pcOffset() != 0 || iter.resumeAfter())
                     scopeChain = fun->environment();
@@ -612,17 +619,17 @@ InitFromBailout(JSContext *cx, HandleScr
         // Make sure to add HAS_RVAL to flags here because setFlags() below
         // will clobber it.
         returnValue = iter.read();
         flags |= BaselineFrame::HAS_RVAL;
 
         // If script maybe has an arguments object, the third slot will hold it.
         if (script->argumentsHasVarBinding()) {
             v = iter.read();
-            JS_ASSERT(v.isObject() || v.isUndefined());
+            JS_ASSERT(v.isObject() || v.isUndefined() || v.isMagic(JS_OPTIMIZED_OUT));
             if (v.isObject())
                 argsObj = &v.toObject().as<ArgumentsObject>();
         }
     }
     IonSpew(IonSpew_BaselineBailouts, "      ScopeChain=%p", scopeChain);
     blFrame->setScopeChain(scopeChain);
     IonSpew(IonSpew_BaselineBailouts, "      ReturnValue=%016llx", *((uint64_t *) &returnValue));
     blFrame->setReturnValue(returnValue);
@@ -675,18 +682,18 @@ InitFromBailout(JSContext *cx, HandleScr
     for (uint32_t i = 0; i < script->nfixed(); i++) {
         Value slot = iter.read();
         if (!builder.writeValue(slot, "FixedValue"))
             return false;
     }
 
     // Get the pc. If we are handling an exception, resume at the pc of the
     // catch or finally block.
-    jsbytecode *pc = excInfo ? excInfo->resumePC : script->offsetToPC(iter.pcOffset());
-    bool resumeAfter = excInfo ? false : iter.resumeAfter();
+    jsbytecode *pc = catchingException ? excInfo->resumePC() : script->offsetToPC(iter.pcOffset());
+    bool resumeAfter = catchingException ? false : iter.resumeAfter();
 
     JSOp op = JSOp(*pc);
 
     // Fixup inlined JSOP_FUNCALL, JSOP_FUNAPPLY, and accessors on the caller side.
     // On the caller side this must represent like the function wasn't inlined.
     uint32_t pushedSlots = 0;
     AutoValueVector savedCallerArgs(cx);
     bool needToSaveArgs = op == JSOP_FUNAPPLY || IsGetPropPC(pc) || IsSetPropPC(pc);
@@ -769,26 +776,40 @@ InitFromBailout(JSContext *cx, HandleScr
         }
     }
 
     IonSpew(IonSpew_BaselineBailouts, "      pushing %u expression stack slots",
                                       exprStackSlots - pushedSlots);
     for (uint32_t i = pushedSlots; i < exprStackSlots; i++) {
         Value v;
 
-        // If coming from an invalidation bailout, and this is the topmost
-        // value, and a value override has been specified, don't read from the
-        // iterator. Otherwise, we risk using a garbage value.
         if (!iter.moreFrames() && i == exprStackSlots - 1 &&
             cx->runtime()->hasIonReturnOverride())
         {
+            // If coming from an invalidation bailout, and this is the topmost
+            // value, and a value override has been specified, don't read from the
+            // iterator. Otherwise, we risk using a garbage value.
             JS_ASSERT(invalidate);
             iter.skip();
             IonSpew(IonSpew_BaselineBailouts, "      [Return Override]");
             v = cx->runtime()->takeIonReturnOverride();
+        } else if (excInfo && excInfo->propagatingIonExceptionForDebugMode()) {
+            // If we are in the middle of propagating an exception from Ion by
+            // bailing to baseline due to debug mode, we might not have all
+            // the stack if we are at the newest frame.
+            //
+            // For instance, if calling |f()| pushed an Ion frame which threw,
+            // the snapshot expects the return value to be pushed, but it's
+            // possible nothing was pushed before we threw. Iterators might
+            // still be on the stack, so we can't just drop the stack.
+            MOZ_ASSERT(cx->compartment()->debugMode());
+            if (iter.moreFrames())
+                v = iter.read();
+            else
+                v = MagicValue(JS_OPTIMIZED_OUT);
         } else {
             v = iter.read();
         }
         if (!builder.writeValue(v, "StackValue"))
             return false;
     }
 
     size_t endOfBaselineJSFrameStack = builder.framePushed();
@@ -851,17 +872,17 @@ InitFromBailout(JSContext *cx, HandleScr
                 resumeAfter ? "after" : "at", (int) pcOff, js_CodeName[op],
                 PCToLineNumber(script, pc), script->filename(), (int) script->lineno());
     IonSpew(IonSpew_BaselineBailouts, "      Bailout kind: %s",
             BailoutKindString(bailoutKind));
 #endif
 
     // If this was the last inline frame, or we are bailing out to a catch or
     // finally block in this frame, then unpacking is almost done.
-    if (!iter.moreFrames() || excInfo) {
+    if (!iter.moreFrames() || catchingException) {
         // Last frame, so PC for call to next frame is set to nullptr.
         *callPC = nullptr;
 
         // If the bailout was a resumeAfter, and the opcode is monitored,
         // then the bailed out state should be in a position to enter
         // into the ICTypeMonitor chain for the op.
         bool enterMonitorChain = false;
         if (resumeAfter && (js_CodeSpec[op].format & JOF_TYPESET)) {
@@ -1315,18 +1336,31 @@ jit::BailoutIonToBaseline(JSContext *cx,
     //      |    |||||      |
     //      |    |||||      |
     //      +---------------+
 
     IonSpew(IonSpew_BaselineBailouts, "Bailing to baseline %s:%u (IonScript=%p) (FrameType=%d)",
             iter.script()->filename(), iter.script()->lineno(), (void *) iter.ionScript(),
             (int) prevFrameType);
 
-    if (excInfo)
-        IonSpew(IonSpew_BaselineBailouts, "Resuming in catch or finally block");
+    bool catchingException;
+    bool propagatingExceptionForDebugMode;
+    if (excInfo) {
+        catchingException = excInfo->catchingException();
+        propagatingExceptionForDebugMode = excInfo->propagatingIonExceptionForDebugMode();
+
+        if (catchingException)
+            IonSpew(IonSpew_BaselineBailouts, "Resuming in catch or finally block");
+
+        if (propagatingExceptionForDebugMode)
+            IonSpew(IonSpew_BaselineBailouts, "Resuming in-place for debug mode");
+    } else {
+        catchingException = false;
+        propagatingExceptionForDebugMode = false;
+    }
 
     IonSpew(IonSpew_BaselineBailouts, "  Reading from snapshot offset %u size %u",
             iter.snapshotOffset(), iter.ionScript()->snapshotsListSize());
 
     if (!excInfo)
         iter.ionScript()->incNumBailouts();
     iter.script()->updateBaselineOrIonRaw();
 
@@ -1371,23 +1405,27 @@ jit::BailoutIonToBaseline(JSContext *cx,
             TraceLogStartEvent(logger, TraceLogCreateTextId(logger, scr));
             TraceLogStartEvent(logger, TraceLogger::Baseline);
         }
 
         IonSpew(IonSpew_BaselineBailouts, "    FrameNo %d", frameNo);
 
         // If we are bailing out to a catch or finally block in this frame,
         // pass excInfo to InitFromBailout and don't unpack any other frames.
-        bool handleException = (excInfo && excInfo->frameNo == frameNo);
+        bool handleException = (catchingException && excInfo->frameNo() == frameNo);
+
+        // We also need to pass excInfo if we're bailing out in place for
+        // debug mode.
+        bool passExcInfo = handleException || propagatingExceptionForDebugMode;
 
         jsbytecode *callPC = nullptr;
         RootedFunction nextCallee(cx, nullptr);
         if (!InitFromBailout(cx, caller, callerPC, fun, scr, iter.ionScript(),
                              snapIter, invalidate, builder, startFrameFormals,
-                             &nextCallee, &callPC, handleException ? excInfo : nullptr))
+                             &nextCallee, &callPC, passExcInfo ? excInfo : nullptr))
         {
             return BAILOUT_RETURN_FATAL_ERROR;
         }
 
         if (!snapIter.moreFrames()) {
             JS_ASSERT(!callPC);
             break;
         }
@@ -1495,16 +1533,49 @@ HandleBaselineInfoBailout(JSContext *cx,
             outerScript->filename(), outerScript->lineno());
 
     JS_ASSERT(!outerScript->ionScript()->invalidated());
 
     IonSpew(IonSpew_BaselineBailouts, "Invalidating due to invalid baseline info");
     return Invalidate(cx, outerScript);
 }
 
+static bool
+CopyFromRematerializedFrame(JSContext *cx, JitActivation *act, uint8_t *fp, size_t inlineDepth,
+                            BaselineFrame *frame)
+{
+    RematerializedFrame *rematFrame = act->lookupRematerializedFrame(fp, inlineDepth);
+
+    // We might not have rematerialized a frame if the user never requested a
+    // Debugger.Frame for it.
+    if (!rematFrame)
+        return true;
+
+    MOZ_ASSERT(rematFrame->script() == frame->script());
+    MOZ_ASSERT(rematFrame->numActualArgs() == frame->numActualArgs());
+
+    frame->setScopeChain(rematFrame->scopeChain());
+    frame->thisValue() = rematFrame->thisValue();
+
+    for (unsigned i = 0; i < frame->numActualArgs(); i++)
+        frame->argv()[i] = rematFrame->argv()[i];
+
+    for (size_t i = 0; i < frame->script()->nfixed(); i++)
+        *frame->valueSlot(i) = rematFrame->locals()[i];
+
+    IonSpew(IonSpew_BaselineBailouts,
+            "  Copied from rematerialized frame at (%p,%u)",
+            fp, inlineDepth);
+
+    if (cx->compartment()->debugMode())
+        return Debugger::handleIonBailout(cx, rematFrame, frame);
+
+    return true;
+}
+
 uint32_t
 jit::FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo)
 {
     // The caller pushes R0 and R1 on the stack without rooting them.
     // Since GC here is very unlikely just suppress it.
     JSContext *cx = GetJSContextFromJitCode();
     js::gc::AutoSuppressGC suppressGC(cx);
 
@@ -1534,16 +1605,17 @@ jit::FinishBailoutToBaseline(BaselineBai
 
     // Create arguments objects for bailed out frames, to maintain the invariant
     // that script->needsArgsObj() implies frame->hasArgsObj().
     RootedScript innerScript(cx, nullptr);
     RootedScript outerScript(cx, nullptr);
 
     JS_ASSERT(cx->currentlyRunningInJit());
     JitFrameIterator iter(cx);
+    uint8_t *outerFp = nullptr;
 
     uint32_t frameno = 0;
     while (frameno < numFrames) {
         JS_ASSERT(!iter.isIonJS());
 
         if (iter.isBaselineJS()) {
             BaselineFrame *frame = iter.baselineFrame();
             MOZ_ASSERT(frame->script()->hasBaselineScript());
@@ -1567,27 +1639,54 @@ jit::FinishBailoutToBaseline(BaselineBai
                 // to the slot.
                 RootedScript script(cx, frame->script());
                 SetFrameArgumentsObject(cx, frame, script, argsObj);
             }
 
             if (frameno == 0)
                 innerScript = frame->script();
 
-            if (frameno == numFrames - 1)
+            if (frameno == numFrames - 1) {
                 outerScript = frame->script();
+                outerFp = iter.fp();
+            }
 
             frameno++;
         }
 
         ++iter;
     }
 
-    JS_ASSERT(innerScript);
-    JS_ASSERT(outerScript);
+    MOZ_ASSERT(innerScript);
+    MOZ_ASSERT(outerScript);
+    MOZ_ASSERT(outerFp);
+
+    // If we rematerialized Ion frames due to debug mode toggling, copy their
+    // values into the baseline frame. We need to do this even when debug mode
+    // is off, as we should respect the mutations made while debug mode was
+    // on.
+    JitActivation *act = cx->mainThread().activation()->asJit();
+    if (act->hasRematerializedFrame(outerFp)) {
+        JitFrameIterator iter(cx);
+        size_t inlineDepth = numFrames;
+        while (inlineDepth > 0) {
+            if (iter.isBaselineJS() &&
+                !CopyFromRematerializedFrame(cx, act, outerFp, --inlineDepth,
+                                             iter.baselineFrame()))
+            {
+                return false;
+            }
+            ++iter;
+        }
+
+        // After copying from all the rematerialized frames, remove them from
+        // the table to keep the table up to date.
+        act->removeRematerializedFrame(outerFp);
+    }
+
     IonSpew(IonSpew_BaselineBailouts,
             "  Restored outerScript=(%s:%u,%u) innerScript=(%s:%u,%u) (bailoutKind=%u)",
             outerScript->filename(), outerScript->lineno(), outerScript->getUseCount(),
             innerScript->filename(), innerScript->lineno(), innerScript->getUseCount(),
             (unsigned) bailoutKind);
 
     switch (bailoutKind) {
       case Bailout_Normal:
@@ -1603,16 +1702,20 @@ jit::FinishBailoutToBaseline(BaselineBai
       case Bailout_ShapeGuard:
         if (!HandleShapeGuardFailure(cx, outerScript, innerScript))
             return false;
         break;
       case Bailout_BaselineInfo:
         if (!HandleBaselineInfoBailout(cx, outerScript, innerScript))
             return false;
         break;
+      case Bailout_IonExceptionDebugMode:
+        // Return false to resume in HandleException with reconstructed
+        // baseline frame.
+        return false;
       default:
         MOZ_ASSUME_UNREACHABLE("Unknown bailout kind!");
     }
 
     if (!CheckFrequentBailouts(cx, outerScript))
         return false;
 
     return true;
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -20,17 +20,17 @@
 
 #include "jsscriptinlines.h"
 
 #include "vm/Interpreter-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
-BaselineCompiler::BaselineCompiler(JSContext *cx, TempAllocator &alloc, HandleScript script)
+BaselineCompiler::BaselineCompiler(JSContext *cx, TempAllocator &alloc, JSScript *script)
   : BaselineCompilerSpecific(cx, alloc, script),
     modifiesArguments_(false)
 {
 }
 
 bool
 BaselineCompiler::init()
 {
@@ -65,17 +65,17 @@ BaselineCompiler::addPCMappingEntry(bool
 
     return pcMappingEntries_.append(entry);
 }
 
 MethodStatus
 BaselineCompiler::compile()
 {
     IonSpew(IonSpew_BaselineScripts, "Baseline compiling script %s:%d (%p)",
-            script->filename(), script->lineno(), script.get());
+            script->filename(), script->lineno(), script);
 
     IonSpew(IonSpew_Codegen, "# Emitting baseline code for script %s:%d",
             script->filename(), script->lineno());
 
     TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
     AutoTraceLog logScript(logger, TraceLogCreateTextId(logger, script));
     AutoTraceLog logCompile(logger, TraceLogger::BaselineCompilation);
 
@@ -116,17 +116,18 @@ BaselineCompiler::compile()
     JitCode *code = linker.newCode<CanGC>(cx, JSC::BASELINE_CODE);
     if (!code)
         return Method_Error;
 
     JSObject *templateScope = nullptr;
     if (script->functionNonDelazifying()) {
         RootedFunction fun(cx, script->functionNonDelazifying());
         if (fun->isHeavyweight()) {
-            templateScope = CallObject::createTemplateObject(cx, script, gc::TenuredHeap);
+            RootedScript scriptRoot(cx, script);
+            templateScope = CallObject::createTemplateObject(cx, scriptRoot, gc::TenuredHeap);
             if (!templateScope)
                 return Method_Error;
 
             if (fun->isNamedLambda()) {
                 RootedObject declEnvObject(cx, DeclEnvObject::createTemplateObject(cx, fun, gc::TenuredHeap));
                 if (!declEnvObject)
                     return Method_Error;
                 templateScope->as<ScopeObject>().setEnclosingScope(declEnvObject);
@@ -169,23 +170,27 @@ BaselineCompiler::compile()
 
         previousOffset = entry.nativeOffset;
     }
 
     if (pcEntries.oom())
         return Method_Error;
 
     prologueOffset_.fixup(&masm);
+    epilogueOffset_.fixup(&masm);
     spsPushToggleOffset_.fixup(&masm);
+    postDebugPrologueOffset_.fixup(&masm);
 
     // Note: There is an extra entry in the bytecode type map for the search hint, see below.
     size_t bytecodeTypeMapEntries = script->nTypeSets() + 1;
 
     BaselineScript *baselineScript = BaselineScript::New(cx, prologueOffset_.offset(),
+                                                         epilogueOffset_.offset(),
                                                          spsPushToggleOffset_.offset(),
+                                                         postDebugPrologueOffset_.offset(),
                                                          icEntries_.length(),
                                                          pcMappingIndexEntries.length(),
                                                          pcEntries.length(),
                                                          bytecodeTypeMapEntries);
     if (!baselineScript)
         return Method_Error;
 
     baselineScript->setMethod(code);
@@ -384,16 +389,20 @@ BaselineCompiler::emitPrologue()
         return false;
 
     return true;
 }
 
 bool
 BaselineCompiler::emitEpilogue()
 {
+    // Record the offset of the epilogue, so we can do early return from
+    // Debugger handlers during on-stack recompile.
+    epilogueOffset_ = masm.currentOffset();
+
     masm.bind(&return_);
 
 #ifdef JS_TRACE_LOGGING
     TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
     Register loggerReg = RegisterSet::Volatile().takeGeneral();
     masm.Push(loggerReg);
     masm.movePtr(ImmPtr(logger), loggerReg);
     masm.tracelogStop(loggerReg, TraceLogger::Baseline);
@@ -428,33 +437,35 @@ BaselineCompiler::emitOutOfLinePostBarri
     GeneralRegisterSet regs(GeneralRegisterSet::All());
     regs.take(objReg);
     regs.take(BaselineFrameReg);
     Register scratch = regs.takeAny();
 #if defined(JS_CODEGEN_ARM)
     // On ARM, save the link register before calling.  It contains the return
     // address.  The |masm.ret()| later will pop this into |pc| to return.
     masm.push(lr);
+#elif defined(JS_CODEGEN_MIPS)
+    masm.push(ra);
 #endif
 
     masm.setupUnalignedABICall(2, scratch);
     masm.movePtr(ImmPtr(cx->runtime()), scratch);
     masm.passABIArg(scratch);
     masm.passABIArg(objReg);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, PostWriteBarrier));
 
     masm.ret();
     return true;
 }
 #endif // JSGC_GENERATIONAL
 
 bool
-BaselineCompiler::emitIC(ICStub *stub, bool isForOp)
+BaselineCompiler::emitIC(ICStub *stub, ICEntry::Kind kind)
 {
-    ICEntry *entry = allocateICEntry(stub, isForOp);
+    ICEntry *entry = allocateICEntry(stub, kind);
     if (!entry)
         return false;
 
     CodeOffsetLabel patchOffset;
     EmitCallIC(&patchOffset, masm);
     entry->setReturnOffset(masm.currentOffset());
     if (!addICLoadLabel(patchOffset))
         return false;
@@ -522,37 +533,42 @@ BaselineCompiler::emitStackCheck(bool ea
 }
 
 typedef bool (*DebugPrologueFn)(JSContext *, BaselineFrame *, jsbytecode *, bool *);
 static const VMFunction DebugPrologueInfo = FunctionInfo<DebugPrologueFn>(jit::DebugPrologue);
 
 bool
 BaselineCompiler::emitDebugPrologue()
 {
-    if (!debugMode_)
-        return true;
-
-    // Load pointer to BaselineFrame in R0.
-    masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
-
-    prepareVMCall();
-    pushArg(ImmPtr(pc));
-    pushArg(R0.scratchReg());
-    if (!callVM(DebugPrologueInfo))
-        return false;
-
-    // If the stub returns |true|, we have to return the value stored in the
-    // frame's return value slot.
-    Label done;
-    masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &done);
-    {
-        masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
-        masm.jump(&return_);
+    if (debugMode_) {
+        // Load pointer to BaselineFrame in R0.
+        masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
+
+        prepareVMCall();
+        pushArg(ImmPtr(pc));
+        pushArg(R0.scratchReg());
+        if (!callVM(DebugPrologueInfo))
+            return false;
+
+        // Fix up the fake ICEntry appended by callVM for on-stack recompilation.
+        icEntries_.back().setForDebugPrologue();
+
+        // If the stub returns |true|, we have to return the value stored in the
+        // frame's return value slot.
+        Label done;
+        masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &done);
+        {
+            masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
+            masm.jump(&return_);
+        }
+        masm.bind(&done);
     }
-    masm.bind(&done);
+
+    postDebugPrologueOffset_ = masm.currentOffset();
+
     return true;
 }
 
 typedef bool (*StrictEvalPrologueFn)(JSContext *, BaselineFrame *);
 static const VMFunction StrictEvalPrologueInfo =
     FunctionInfo<StrictEvalPrologueFn>(jit::StrictEvalPrologue);
 
 typedef bool (*HeavyweightFunPrologueFn)(JSContext *, BaselineFrame *);
@@ -716,17 +732,17 @@ BaselineCompiler::emitDebugTrap()
 
 #ifdef DEBUG
     // Patchable call offset has to match the pc mapping offset.
     PCMappingEntry &entry = pcMappingEntries_.back();
     JS_ASSERT((&offset)->offset() == entry.nativeOffset);
 #endif
 
     // Add an IC entry for the return offset -> pc mapping.
-    ICEntry icEntry(script->pcToOffset(pc), false);
+    ICEntry icEntry(script->pcToOffset(pc), ICEntry::Kind_DebugTrap);
     icEntry.setReturnOffset(masm.currentOffset());
     if (!icEntries_.append(icEntry))
         return false;
 
     return true;
 }
 
 bool
@@ -2097,17 +2113,16 @@ BaselineCompiler::emit_JSOP_SETALIASEDVA
     frame.push(R0);
 
 #ifdef JSGC_GENERATIONAL
     // Fully sync the stack if post-barrier is needed.
     // Scope coordinate object is already in R2.scratchReg().
     frame.syncStack(0);
     Register temp = R1.scratchReg();
 
-    Nursery &nursery = cx->runtime()->gcNursery;
     Label skipBarrier;
     masm.branchTestObject(Assembler::NotEqual, R0, &skipBarrier);
     masm.branchPtrInNurseryRange(objReg, temp, &skipBarrier);
 
     masm.call(&postBarrierSlot_);
 
     masm.bind(&skipBarrier);
 #endif
@@ -2417,17 +2432,16 @@ BaselineCompiler::emitFormalArgAccess(ui
         // Fully sync the stack if post-barrier is needed.
         frame.syncStack(0);
         Register temp = R1.scratchReg();
 
         // Reload the arguments object
         Register reg = R2.scratchReg();
         masm.loadPtr(Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfArgsObj()), reg);
 
-        Nursery &nursery = cx->runtime()->gcNursery;
         Label skipBarrier;
         masm.branchPtrInNurseryRange(reg, temp, &skipBarrier);
 
         masm.call(&postBarrierSlot_);
 
         masm.bind(&skipBarrier);
 #endif
     }
@@ -2738,16 +2752,19 @@ BaselineCompiler::emit_JSOP_EXCEPTION()
 
 typedef bool (*OnDebuggerStatementFn)(JSContext *, BaselineFrame *, jsbytecode *pc, bool *);
 static const VMFunction OnDebuggerStatementInfo =
     FunctionInfo<OnDebuggerStatementFn>(jit::OnDebuggerStatement);
 
 bool
 BaselineCompiler::emit_JSOP_DEBUGGER()
 {
+    if (!debugMode_)
+        return true;
+
     prepareVMCall();
     pushArg(ImmPtr(pc));
 
     masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
     pushArg(R0.scratchReg());
 
     if (!callVM(OnDebuggerStatementInfo))
         return false;
@@ -2780,16 +2797,19 @@ BaselineCompiler::emitReturn()
 
         prepareVMCall();
         pushArg(Imm32(1));
         pushArg(ImmPtr(pc));
         pushArg(R0.scratchReg());
         if (!callVM(DebugEpilogueInfo))
             return false;
 
+        // Fix up the fake ICEntry appended by callVM for on-stack recompilation.
+        icEntries_.back().setForDebugEpilogue();
+
         masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
     }
 
     // Only emit the jump if this JSOP_RETRVAL is not the last instruction.
     // Not needed for last instruction, because last instruction flows
     // into return label.
     if (pc + GetBytecodeLength(pc) < script->codeEnd())
         masm.jump(&return_);
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -9,18 +9,22 @@
 
 #ifdef JS_ION
 
 #include "jit/FixedList.h"
 #if defined(JS_CODEGEN_X86)
 # include "jit/x86/BaselineCompiler-x86.h"
 #elif defined(JS_CODEGEN_X64)
 # include "jit/x64/BaselineCompiler-x64.h"
+#elif defined(JS_CODEGEN_ARM)
+# include "jit/arm/BaselineCompiler-arm.h"
+#elif defined(JS_CODEGEN_MIPS)
+# include "jit/mips/BaselineCompiler-mips.h"
 #else
-# include "jit/arm/BaselineCompiler-arm.h"
+# error "Unknown architecture!"
 #endif
 
 namespace js {
 namespace jit {
 
 #define OPCODE_LIST(_)         \
     _(JSOP_NOP)                \
     _(JSOP_LABEL)              \
@@ -168,50 +172,58 @@ class BaselineCompiler : public Baseline
     NonAssertingLabel           return_;
 #ifdef JSGC_GENERATIONAL
     NonAssertingLabel           postBarrierSlot_;
 #endif
 
     // Native code offset right before the scope chain is initialized.
     CodeOffsetLabel prologueOffset_;
 
+    // Native code offset right before the frame is popped and the method
+    // returned from.
+    CodeOffsetLabel epilogueOffset_;
+
+    // Native code offset right after debug prologue and epilogue, or
+    // equivalent positions when debug mode is off.
+    CodeOffsetLabel postDebugPrologueOffset_;
+
     // Whether any on stack arguments are modified.
     bool modifiesArguments_;
 
     Label *labelOf(jsbytecode *pc) {
         return &labels_[script->pcToOffset(pc)];
     }
 
     // If a script has more |nslots| than this, then emit code to do an
     // early stack check.
     static const unsigned EARLY_STACK_CHECK_SLOT_COUNT = 128;
     bool needsEarlyStackCheck() const {
         return script->nslots() > EARLY_STACK_CHECK_SLOT_COUNT;
     }
 
   public:
-    BaselineCompiler(JSContext *cx, TempAllocator &alloc, HandleScript script);
+    BaselineCompiler(JSContext *cx, TempAllocator &alloc, JSScript *script);
     bool init();
 
     MethodStatus compile();
 
   private:
     MethodStatus emitBody();
 
     bool emitPrologue();
     bool emitEpilogue();
 #ifdef JSGC_GENERATIONAL
     bool emitOutOfLinePostBarrierSlot();
 #endif
-    bool emitIC(ICStub *stub, bool isForOp);
+    bool emitIC(ICStub *stub, ICEntry::Kind kind);
     bool emitOpIC(ICStub *stub) {
-        return emitIC(stub, true);
+        return emitIC(stub, ICEntry::Kind_Op);
     }
     bool emitNonOpIC(ICStub *stub) {
-        return emitIC(stub, false);
+        return emitIC(stub, ICEntry::Kind_NonOp);
     }
 
     bool emitStackCheck(bool earlyCheck=false);
     bool emitInterruptCheck();
     bool emitUseCountIncrement(bool allowOsr=true);
     bool emitArgumentTypeChecks();
     bool emitDebugPrologue();
     bool emitDebugTrap();
new file mode 100644
--- /dev/null
+++ b/js/src/jit/BaselineDebugModeOSR.cpp
@@ -0,0 +1,709 @@
+/* -*- 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/BaselineDebugModeOSR.h"
+
+#include "mozilla/DebugOnly.h"
+
+#include "jit/IonLinker.h"
+
+#include "jit/IonFrames-inl.h"
+#include "vm/Stack-inl.h"
+
+using namespace mozilla;
+using namespace js;
+using namespace js::jit;
+
+struct DebugModeOSREntry
+{
+    JSScript *script;
+    BaselineScript *oldBaselineScript;
+    BaselineDebugModeOSRInfo *recompInfo;
+    uint32_t pcOffset;
+    ICEntry::Kind frameKind;
+
+    // Used for sanity asserts in debug builds.
+    DebugOnly<ICStub *> stub;
+
+    DebugModeOSREntry(JSScript *script)
+      : script(script),
+        oldBaselineScript(script->baselineScript()),
+        recompInfo(nullptr),
+        pcOffset(uint32_t(-1)),
+        frameKind(ICEntry::Kind_NonOp),
+        stub(nullptr)
+    { }
+
+    DebugModeOSREntry(JSScript *script, const ICEntry &icEntry)
+      : script(script),
+        oldBaselineScript(script->baselineScript()),
+        recompInfo(nullptr),
+        pcOffset(icEntry.pcOffset()),
+        frameKind(icEntry.kind()),
+        stub(nullptr)
+    {
+#ifdef DEBUG
+        MOZ_ASSERT(pcOffset == icEntry.pcOffset());
+        MOZ_ASSERT(frameKind == icEntry.kind());
+
+        // Assert that if we have a NonOp ICEntry, that there are no unsynced
+        // slots, since such a recompile could have only been triggered from
+        // either an interrupt check or a debug trap handler.
+        //
+        // If triggered from an interrupt check, the stack should be fully
+        // synced.
+        //
+        // If triggered from a debug trap handler, we must be recompiling for
+        // toggling debug mode on->off, in which case the old baseline script
+        // should have fully synced stack at every bytecode.
+        if (frameKind == ICEntry::Kind_NonOp) {
+            PCMappingSlotInfo slotInfo;
+            jsbytecode *pc = script->offsetToPC(pcOffset);
+            oldBaselineScript->nativeCodeForPC(script, pc, &slotInfo);
+            MOZ_ASSERT(slotInfo.numUnsynced() == 0);
+        }
+#endif
+    }
+
+    DebugModeOSREntry(DebugModeOSREntry &&other)
+      : script(other.script),
+        oldBaselineScript(other.oldBaselineScript),
+        recompInfo(other.recompInfo ? other.takeRecompInfo() : nullptr),
+        pcOffset(other.pcOffset),
+        frameKind(other.frameKind),
+        stub(other.stub)
+    { }
+
+    ~DebugModeOSREntry() {
+        // Note that this is nulled out when the recompInfo is taken by the
+        // frame. The frame then has the responsibility of freeing the
+        // recompInfo.
+        js_delete(recompInfo);
+    }
+
+    bool needsRecompileInfo() const {
+        return (frameKind == ICEntry::Kind_CallVM ||
+                frameKind == ICEntry::Kind_DebugTrap ||
+                frameKind == ICEntry::Kind_DebugPrologue ||
+                frameKind == ICEntry::Kind_DebugEpilogue);
+    }
+
+    BaselineDebugModeOSRInfo *takeRecompInfo() {
+        MOZ_ASSERT(recompInfo);
+        BaselineDebugModeOSRInfo *tmp = recompInfo;
+        recompInfo = nullptr;
+        return tmp;
+    }
+
+    bool allocateRecompileInfo(JSContext *cx) {
+        MOZ_ASSERT(needsRecompileInfo());
+
+        // If we are returning to a frame which needs a continuation fixer,
+        // allocate the recompile info up front so that the patching function
+        // is infallible.
+        jsbytecode *pc = script->offsetToPC(pcOffset);
+
+        // XXX: Work around compiler error disallowing using bitfields
+        // with the template magic of new_.
+        ICEntry::Kind kind = frameKind;
+        recompInfo = cx->new_<BaselineDebugModeOSRInfo>(pc, kind);
+        return !!recompInfo;
+    }
+};
+
+typedef js::Vector<DebugModeOSREntry> DebugModeOSREntryVector;
+
+static bool
+CollectOnStackScripts(JSContext *cx, const JitActivationIterator &activation,
+                      DebugModeOSREntryVector &entries)
+{
+    DebugOnly<ICStub *> prevFrameStubPtr = nullptr;
+    bool needsRecompileHandler = false;
+    for (JitFrameIterator iter(activation); !iter.done(); ++iter) {
+        switch (iter.type()) {
+          case JitFrame_BaselineJS: {
+            JSScript *script = iter.script();
+            uint8_t *retAddr = iter.returnAddressToFp();
+            ICEntry &entry = script->baselineScript()->icEntryFromReturnAddress(retAddr);
+
+            if (!entries.append(DebugModeOSREntry(script, entry)))
+                return false;
+
+            if (entries.back().needsRecompileInfo()) {
+                if (!entries.back().allocateRecompileInfo(cx))
+                    return false;
+
+                needsRecompileHandler |= true;
+            }
+
+            entries.back().stub = prevFrameStubPtr;
+            prevFrameStubPtr = nullptr;
+            break;
+          }
+
+          case JitFrame_BaselineStub:
+            prevFrameStubPtr =
+                reinterpret_cast<IonBaselineStubFrameLayout *>(iter.fp())->maybeStubPtr();
+            break;
+
+          case JitFrame_IonJS: {
+            JSScript *script = iter.script();
+            if (!entries.append(DebugModeOSREntry(script)))
+                return false;
+            for (InlineFrameIterator inlineIter(cx, &iter); inlineIter.more(); ++inlineIter) {
+                if (!entries.append(DebugModeOSREntry(inlineIter.script())))
+                    return false;
+            }
+            break;
+          }
+
+          default:;
+        }
+    }
+
+    // Initialize the on-stack recompile handler, which may fail, so that
+    // patching the stack is infallible.
+    if (needsRecompileHandler) {
+        JitRuntime *rt = cx->runtime()->jitRuntime();
+        if (!rt->getBaselineDebugModeOSRHandlerAddress(cx, true))
+            return false;
+    }
+
+    return true;
+}
+
+static inline uint8_t *
+GetStubReturnFromStubAddress(JSContext *cx, jsbytecode *pc)
+{
+    JitCompartment *comp = cx->compartment()->jitCompartment();
+    void *addr;
+    if (IsGetPropPC(pc)) {
+        addr = comp->baselineGetPropReturnFromStubAddr();
+    } else if (IsSetPropPC(pc)) {
+        addr = comp->baselineSetPropReturnFromStubAddr();
+    } else {
+        JS_ASSERT(IsCallPC(pc));
+        addr = comp->baselineCallReturnFromStubAddr();
+    }
+    return reinterpret_cast<uint8_t *>(addr);
+}
+
+static const char *
+ICEntryKindToString(ICEntry::Kind kind)
+{
+    switch (kind) {
+      case ICEntry::Kind_Op:
+        return "IC";
+      case ICEntry::Kind_NonOp:
+        return "non-op IC";
+      case ICEntry::Kind_CallVM:
+        return "callVM";
+      case ICEntry::Kind_DebugTrap:
+        return "debug trap";
+      case ICEntry::Kind_DebugPrologue:
+        return "debug prologue";
+      case ICEntry::Kind_DebugEpilogue:
+        return "debug epilogue";
+      default:
+        MOZ_ASSUME_UNREACHABLE("bad ICEntry kind");
+    }
+}
+
+static void
+SpewPatchBaselineFrame(uint8_t *oldReturnAddress, uint8_t *newReturnAddress,
+                       JSScript *script, ICEntry::Kind frameKind, jsbytecode *pc)
+{
+    IonSpew(IonSpew_BaselineDebugModeOSR,
+            "Patch return %#016llx -> %#016llx to BaselineJS (%s:%d) from %s at %s",
+            uintptr_t(oldReturnAddress), uintptr_t(newReturnAddress),
+            script->filename(), script->lineno(),
+            ICEntryKindToString(frameKind), js_CodeName[(JSOp)*pc]);
+}
+
+static void
+SpewPatchStubFrame(uint8_t *oldReturnAddress, uint8_t *newReturnAddress,
+                   ICStub *oldStub, ICStub *newStub)
+{
+    IonSpew(IonSpew_BaselineDebugModeOSR,
+            "Patch return %#016llx -> %#016llx",
+            uintptr_t(oldReturnAddress), uintptr_t(newReturnAddress));
+    IonSpew(IonSpew_BaselineDebugModeOSR,
+            "Patch   stub %#016llx -> %#016llx to BaselineStub",
+            uintptr_t(oldStub), uintptr_t(newStub));
+}
+
+static void
+PatchBaselineFramesForDebugMode(JSContext *cx, const JitActivationIterator &activation,
+                                DebugModeOSREntryVector &entries, size_t *start)
+{
+    //
+    // Recompile Patching Overview
+    //
+    // When toggling debug mode with live baseline scripts on the stack, we
+    // could have entered the VM via the following ways from the baseline
+    // script.
+    //
+    // Off to On:
+    //  A. From a "can call" stub.
+    //  B. From a VM call (interrupt handler, debugger statement handler).
+    //
+    // On to Off:
+    //  - All the ways above.
+    //  C. From the debug trap handler.
+    //  D. From the debug prologue.
+    //  E. From the debug epilogue.
+    //
+    // In general, we patch the return address from the VM call to return to a
+    // "continuation fixer" to fix up machine state (registers and stack
+    // state). Specifics on what need to be done are documented below.
+    //
+
+    IonCommonFrameLayout *prev = nullptr;
+    size_t entryIndex = *start;
+    DebugOnly<bool> expectedDebugMode = cx->compartment()->debugMode();
+
+    for (JitFrameIterator iter(activation); !iter.done(); ++iter) {
+        switch (iter.type()) {
+          case JitFrame_BaselineJS: {
+            JSScript *script = entries[entryIndex].script;
+            uint32_t pcOffset = entries[entryIndex].pcOffset;
+            jsbytecode *pc = script->offsetToPC(pcOffset);
+
+            MOZ_ASSERT(script == iter.script());
+            MOZ_ASSERT(pcOffset < script->length());
+            MOZ_ASSERT(script->baselineScript()->debugMode() == expectedDebugMode);
+
+            BaselineScript *bl = script->baselineScript();
+            ICEntry::Kind kind = entries[entryIndex].frameKind;
+
+            if (kind == ICEntry::Kind_Op) {
+                // Case A above.
+                //
+                // Patching this case needs to patch both the stub frame and
+                // the baseline frame. The stub frame is patched below. For
+                // the baseline frame here, we resume right after the IC
+                // returns.
+                //
+                // Since we're using the IC-specific k-fixer, we can resume
+                // directly to the IC resume address.
+                uint8_t *retAddr = bl->returnAddressForIC(bl->icEntryFromPCOffset(pcOffset));
+                SpewPatchBaselineFrame(prev->returnAddress(), retAddr, script, kind, pc);
+                prev->setReturnAddress(retAddr);
+                entryIndex++;
+                break;
+            }
+
+            bool popFrameReg;
+
+            // The RecompileInfo must already be allocated so that this
+            // function may be infallible.
+            BaselineDebugModeOSRInfo *recompInfo = entries[entryIndex].takeRecompInfo();
+
+            switch (kind) {
+              case ICEntry::Kind_CallVM:
+                // Case B above.
+                //
+                // Patching returns from an interrupt handler or the debugger
+                // statement handler is similar in that we can resume at the
+                // next op.
+                pc += GetBytecodeLength(pc);
+                recompInfo->resumeAddr = bl->nativeCodeForPC(script, pc, &recompInfo->slotInfo);
+                popFrameReg = true;
+                break;
+
+              case ICEntry::Kind_DebugTrap:
+                // Case C above.
+                //
+                // Debug traps are emitted before each op, so we resume at the
+                // same op. Calling debug trap handlers is done via a toggled
+                // call to a thunk (DebugTrapHandler) that takes care tearing
+                // down its own stub frame so we don't need to worry about
+                // popping the frame reg.
+                recompInfo->resumeAddr = bl->nativeCodeForPC(script, pc, &recompInfo->slotInfo);
+                popFrameReg = false;
+                break;
+
+              case ICEntry::Kind_DebugPrologue:
+                // Case D above.
+                //
+                // We patch a jump directly to the right place in the prologue
+                // after popping the frame reg and checking for forced return.
+                recompInfo->resumeAddr = bl->postDebugPrologueAddr();
+                popFrameReg = true;
+                break;
+
+              default:
+                // Case E above.
+                //
+                // We patch a jump directly to the epilogue after popping the
+                // frame reg and checking for forced return.
+                MOZ_ASSERT(kind == ICEntry::Kind_DebugEpilogue);
+                recompInfo->resumeAddr = bl->epilogueEntryAddr();
+                popFrameReg = true;
+                break;
+            }
+
+            SpewPatchBaselineFrame(prev->returnAddress(), recompInfo->resumeAddr,
+                                   script, kind, recompInfo->pc);
+
+            // The recompile handler must already be created so that this
+            // function may be infallible.
+            JitRuntime *rt = cx->runtime()->jitRuntime();
+            void *handlerAddr = rt->getBaselineDebugModeOSRHandlerAddress(cx, popFrameReg);
+            MOZ_ASSERT(handlerAddr);
+
+            prev->setReturnAddress(reinterpret_cast<uint8_t *>(handlerAddr));
+            iter.baselineFrame()->setDebugModeOSRInfo(recompInfo);
+
+            entryIndex++;
+            break;
+          }
+
+          case JitFrame_BaselineStub: {
+            JSScript *script = entries[entryIndex].script;
+            IonBaselineStubFrameLayout *layout =
+                reinterpret_cast<IonBaselineStubFrameLayout *>(iter.fp());
+            MOZ_ASSERT(script->baselineScript()->debugMode() == expectedDebugMode);
+            MOZ_ASSERT(layout->maybeStubPtr() == entries[entryIndex].stub);
+
+            // Patch baseline stub frames for case A above.
+            //
+            // We need to patch the stub frame return address to go to the
+            // k-fixer that is at the end of fallback stubs of all such
+            // can-call ICs. These k-fixers share code with bailout-from-Ion
+            // fixers, but in this case we are returning from VM and not
+            // Ion. See e.g., JitCompartment::baselineCallReturnFromStubAddr()
+            //
+            // Subtlety here: the debug trap handler of case C above pushes a
+            // stub frame with a null stub pointer. This handler will exist
+            // across recompiling the script, so we don't patch anything for
+            // such stub frames. We will return to that handler, which takes
+            // care of cleaning up the stub frame.
+            //
+            // Note that for stub pointers that are already on the C stack
+            // (i.e. fallback calls), we need to check for recompilation using
+            // DebugModeOSRVolatileStub.
+            if (layout->maybeStubPtr()) {
+                MOZ_ASSERT(layout->maybeStubPtr() == entries[entryIndex].stub);
+                uint32_t pcOffset = entries[entryIndex].pcOffset;
+                uint8_t *retAddr = GetStubReturnFromStubAddress(cx, script->offsetToPC(pcOffset));
+
+                // Get the fallback stub for the IC in the recompiled
+                // script. The fallback stub is guaranteed to exist.
+                ICEntry &entry = script->baselineScript()->icEntryFromPCOffset(pcOffset);
+                ICStub *newStub = entry.fallbackStub();
+                SpewPatchStubFrame(prev->returnAddress(), retAddr, layout->maybeStubPtr(), newStub);
+                prev->setReturnAddress(retAddr);
+                layout->setStubPtr(newStub);
+            }
+
+            break;
+          }
+
+          case JitFrame_IonJS:
+            // Nothing to patch.
+            entryIndex++;
+            for (InlineFrameIterator inlineIter(cx, &iter); inlineIter.more(); ++inlineIter)
+                entryIndex++;
+            break;
+
+          default:;
+        }
+
+        prev = iter.current();
+    }
+
+    *start = entryIndex;
+}
+
+static bool
+RecompileBaselineScriptForDebugMode(JSContext *cx, JSScript *script)
+{
+    BaselineScript *oldBaselineScript = script->baselineScript();
+
+    // If a script is on the stack multiple times, it may have already
+    // been recompiled.
+    bool expectedDebugMode = cx->compartment()->debugMode();
+    if (oldBaselineScript->debugMode() == expectedDebugMode)
+        return true;
+
+    IonSpew(IonSpew_BaselineDebugModeOSR, "Recompiling (%s:%d) for debug mode %s",
+            script->filename(), script->lineno(), expectedDebugMode ? "ON" : "OFF");
+
+    if (script->hasIonScript())
+        Invalidate(cx, script, /* resetUses = */ false);
+
+    script->setBaselineScript(cx, nullptr);
+
+    MethodStatus status = BaselineCompile(cx, script);
+    if (status != Method_Compiled) {
+        // We will only fail to recompile for debug mode due to OOM. Restore
+        // the old baseline script in case something doesn't properly
+        // propagate OOM.
+        MOZ_ASSERT(status == Method_Error);
+        script->setBaselineScript(cx, oldBaselineScript);
+        return false;
+    }
+
+    // Don't destroy the old baseline script yet, since if we fail any of the
+    // recompiles we need to rollback all the old baseline scripts.
+    MOZ_ASSERT(script->baselineScript()->debugMode() == expectedDebugMode);
+    return true;
+}
+
+static void
+UndoRecompileBaselineScriptsForDebugMode(JSContext *cx,
+                                         const DebugModeOSREntryVector &entries)
+{
+    // In case of failure, roll back the entire set of active scripts so that
+    // we don't have to patch return addresses on the stack.
+    for (size_t i = 0; i < entries.length(); i++) {
+        JSScript *script = entries[i].script;
+        BaselineScript *baselineScript = script->baselineScript();
+        if (baselineScript != entries[i].oldBaselineScript) {
+            script->setBaselineScript(cx, entries[i].oldBaselineScript);
+            BaselineScript::Destroy(cx->runtime()->defaultFreeOp(), baselineScript);
+        }
+    }
+}
+
+bool
+jit::RecompileOnStackBaselineScriptsForDebugMode(JSContext *cx, JSCompartment *comp)
+{
+    AutoCompartment ac(cx, comp);
+
+    // First recompile the active scripts on the stack and patch the live
+    // frames.
+    Vector<DebugModeOSREntry> entries(cx);
+
+    for (JitActivationIterator iter(cx->runtime()); !iter.done(); ++iter) {
+        if (iter.activation()->compartment() == comp) {
+            if (!CollectOnStackScripts(cx, iter, entries))
+                return false;
+        }
+    }
+
+#ifdef JSGC_GENERATIONAL
+    // Scripts can entrain nursery things. See note in js::ReleaseAllJITCode.
+    if (!entries.empty())
+        MinorGC(cx->runtime(), JS::gcreason::EVICT_NURSERY);
+#endif
+
+    // Try to recompile all the scripts. If we encounter an error, we need to
+    // roll back as if none of the compilations happened, so that we don't
+    // crash.
+    for (size_t i = 0; i < entries.length(); i++) {
+        JSScript *script = entries[i].script;
+        if (!RecompileBaselineScriptForDebugMode(cx, script)) {
+            UndoRecompileBaselineScriptsForDebugMode(cx, entries);
+            return false;
+        }
+    }
+
+    // If all recompiles succeeded, destroy the old baseline scripts and patch
+    // the live frames.
+    //
+    // After this point the function must be infallible.
+
+    for (size_t i = 0; i < entries.length(); i++)
+        BaselineScript::Destroy(cx->runtime()->defaultFreeOp(), entries[i].oldBaselineScript);
+
+    size_t processed = 0;
+    for (JitActivationIterator iter(cx->runtime()); !iter.done(); ++iter) {
+        if (iter.activation()->compartment() == comp)
+            PatchBaselineFramesForDebugMode(cx, iter, entries, &processed);
+    }
+    MOZ_ASSERT(processed == entries.length());
+
+    return true;
+}
+
+void
+BaselineDebugModeOSRInfo::popValueInto(PCMappingSlotInfo::SlotLocation loc, Value *vp)
+{
+    switch (loc) {
+      case PCMappingSlotInfo::SlotInR0:
+        valueR0 = vp[stackAdjust];
+        break;
+      case PCMappingSlotInfo::SlotInR1:
+        valueR1 = vp[stackAdjust];
+        break;
+      case PCMappingSlotInfo::SlotIgnore:
+        break;
+      default:
+        MOZ_ASSUME_UNREACHABLE("Bad slot location");
+    }
+
+    stackAdjust++;
+}
+
+static inline bool
+HasForcedReturn(BaselineDebugModeOSRInfo *info, bool rv)
+{
+    ICEntry::Kind kind = info->frameKind;
+
+    // The debug epilogue always checks its resumption value, so we don't need
+    // to check rv.
+    if (kind == ICEntry::Kind_DebugEpilogue)
+        return true;
+
+    // |rv| is the value in ReturnReg. If true, in the case of the prologue,
+    // debug trap, and debugger statement handler, it means a forced return.
+    if (kind == ICEntry::Kind_DebugPrologue ||
+        (kind == ICEntry::Kind_CallVM && JSOp(*info->pc) == JSOP_DEBUGGER))
+    {
+        return rv;
+    }
+
+    // N.B. The debug trap handler handles its own forced return, so no
+    // need to deal with it here.
+    return false;
+}
+
+static void
+SyncBaselineDebugModeOSRInfo(BaselineFrame *frame, Value *vp, bool rv)
+{
+    BaselineDebugModeOSRInfo *info = frame->debugModeOSRInfo();
+    MOZ_ASSERT(info);
+    MOZ_ASSERT(frame->script()->baselineScript()->containsCodeAddress(info->resumeAddr));
+
+    if (HasForcedReturn(info, rv)) {
+        // Load the frame's rval and overwrite the resume address to go to the
+        // epilogue.
+        MOZ_ASSERT(R0 == JSReturnOperand);
+        info->valueR0 = frame->returnValue();
+        info->resumeAddr = frame->script()->baselineScript()->epilogueEntryAddr();
+        return;
+    }
+
+    // Read stack values and make sure R0 and R1 have the right values.
+    unsigned numUnsynced = info->slotInfo.numUnsynced();
+    MOZ_ASSERT(numUnsynced <= 2);
+    if (numUnsynced > 0)
+        info->popValueInto(info->slotInfo.topSlotLocation(), vp);
+    if (numUnsynced > 1)
+        info->popValueInto(info->slotInfo.nextSlotLocation(), vp);
+
+    // Scale stackAdjust.
+    info->stackAdjust *= sizeof(Value);
+}
+
+static void
+FinishBaselineDebugModeOSR(BaselineFrame *frame)
+{
+    frame->deleteDebugModeOSRInfo();
+}
+
+void
+BaselineFrame::deleteDebugModeOSRInfo()
+{
+    js_delete(getDebugModeOSRInfo());
+    flags_ &= ~HAS_DEBUG_MODE_OSR_INFO;
+}
+
+JitCode *
+JitRuntime::getBaselineDebugModeOSRHandler(JSContext *cx)
+{
+    if (!baselineDebugModeOSRHandler_) {
+        AutoLockForExclusiveAccess lock(cx);
+        AutoCompartment ac(cx, cx->runtime()->atomsCompartment());
+        uint32_t offset;
+        if (JitCode *code = generateBaselineDebugModeOSRHandler(cx, &offset)) {
+            baselineDebugModeOSRHandler_ = code;
+            baselineDebugModeOSRHandlerNoFrameRegPopAddr_ = code->raw() + offset;
+        }
+    }
+
+    return baselineDebugModeOSRHandler_;
+}
+
+void *
+JitRuntime::getBaselineDebugModeOSRHandlerAddress(JSContext *cx, bool popFrameReg)
+{
+    if (!getBaselineDebugModeOSRHandler(cx))
+        return nullptr;
+    return (popFrameReg
+            ? baselineDebugModeOSRHandler_->raw()
+            : baselineDebugModeOSRHandlerNoFrameRegPopAddr_);
+}
+
+JitCode *
+JitRuntime::generateBaselineDebugModeOSRHandler(JSContext *cx, uint32_t *noFrameRegPopOffsetOut)
+{
+    MacroAssembler masm(cx);
+
+    GeneralRegisterSet regs(GeneralRegisterSet::All());
+    regs.take(BaselineFrameReg);
+    regs.take(ReturnReg);
+    Register temp = regs.takeAny();
+    Register syncedStackStart = regs.takeAny();
+
+    // Pop the frame reg.
+    masm.pop(BaselineFrameReg);
+
+    // Not all patched baseline frames are returning from a situation where
+    // the frame reg is already fixed up.
+    CodeOffsetLabel noFrameRegPopOffset = masm.currentOffset();
+
+    // Record the stack pointer for syncing.
+    masm.movePtr(StackPointer, syncedStackStart);
+    masm.push(BaselineFrameReg);
+
+    // Call a stub to fully initialize the info.
+    masm.setupUnalignedABICall(3, temp);
+    masm.loadBaselineFramePtr(BaselineFrameReg, temp);
+    masm.passABIArg(temp);
+    masm.passABIArg(syncedStackStart);
+    masm.passABIArg(ReturnReg);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, SyncBaselineDebugModeOSRInfo));
+
+    // Discard stack values depending on how many were unsynced, as we always
+    // have a fully synced stack in the recompile handler. See assert in
+    // DebugModeOSREntry constructor.
+    masm.pop(BaselineFrameReg);
+    masm.loadPtr(Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfScratchValue()), temp);
+    masm.addPtr(Address(temp, offsetof(BaselineDebugModeOSRInfo, stackAdjust)), StackPointer);
+
+    // Save real return address on the stack temporarily.
+    masm.pushValue(Address(temp, offsetof(BaselineDebugModeOSRInfo, valueR0)));
+    masm.pushValue(Address(temp, offsetof(BaselineDebugModeOSRInfo, valueR1)));
+    masm.push(BaselineFrameReg);
+    masm.push(Address(temp, offsetof(BaselineDebugModeOSRInfo, resumeAddr)));
+
+    // Call a stub to free the allocated info.
+    masm.setupUnalignedABICall(1, temp);
+    masm.loadBaselineFramePtr(BaselineFrameReg, temp);
+    masm.passABIArg(temp);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, FinishBaselineDebugModeOSR));
+
+    // Restore saved values.
+    GeneralRegisterSet jumpRegs(GeneralRegisterSet::All());
+    jumpRegs.take(R0);
+    jumpRegs.take(R1);
+    jumpRegs.take(BaselineFrameReg);
+    Register target = jumpRegs.takeAny();
+
+    masm.pop(target);
+    masm.pop(BaselineFrameReg);
+    masm.popValue(R1);
+    masm.popValue(R0);
+
+    masm.jump(target);
+
+    Linker linker(masm);
+    JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
+    if (!code)
+        return nullptr;
+
+    noFrameRegPopOffset.fixup(&masm);
+    *noFrameRegPopOffsetOut = noFrameRegPopOffset.offset();
+
+#ifdef JS_ION_PERF
+    writePerfSpewerJitCodeProfile(code, "BaselineDebugModeOSRHandler");
+#endif
+
+    return code;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit/BaselineDebugModeOSR.h
@@ -0,0 +1,106 @@
+/* -*- 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_BaselineDebugModeOSR_h
+#define jit_BaselineDebugModeOSR_h
+
+#ifdef JS_ION
+
+#include "jit/BaselineFrame.h"
+#include "jit/BaselineIC.h"
+#include "jit/BaselineJIT.h"
+
+namespace js {
+namespace jit {
+
+// Note that this file and the corresponding .cpp implement debug mode
+// on-stack recompilation. This is to be distinguished from ordinary
+// Baseline->Ion OSR, which is used to jump into compiled loops.
+
+//
+// A volatile location due to recompilation of an on-stack baseline script
+// (e.g., for debug mode toggling).
+//
+// It is usually used in fallback stubs which may trigger on-stack
+// recompilation by calling out into the VM. Example use:
+//
+//     DebugModeOSRVolatileStub<FallbackStubT *> stub(frame, stub_)
+//
+//     // Call out to the VM
+//     // Other effectful operations like TypeScript::Monitor
+//
+//     if (stub.invalid())
+//         return true;
+//
+//     // First use of stub after VM call.
+//
+template <typename T>
+class DebugModeOSRVolatileStub
+{
+    T stub_;
+    BaselineFrame *frame_;
+    uint32_t pcOffset_;
+
+  public:
+    DebugModeOSRVolatileStub(BaselineFrame *frame, ICFallbackStub *stub)
+      : stub_(static_cast<T>(stub)),
+        frame_(frame),
+        pcOffset_(stub->icEntry()->pcOffset())
+    { }
+
+    bool invalid() const {
+        ICEntry &entry = frame_->script()->baselineScript()->icEntryFromPCOffset(pcOffset_);
+        return stub_ != entry.fallbackStub();
+    }
+
+    operator const T&() const { MOZ_ASSERT(!invalid()); return stub_; }
+    T operator->() const { MOZ_ASSERT(!invalid()); return stub_; }
+    T *address() { MOZ_ASSERT(!invalid()); return &stub_; }
+    const T *address() const { MOZ_ASSERT(!invalid()); return &stub_; }
+    T &get() { MOZ_ASSERT(!invalid()); return stub_; }
+    const T &get() const { MOZ_ASSERT(!invalid()); return stub_; }
+
+    bool operator!=(const T &other) const { MOZ_ASSERT(!invalid()); return stub_ != other; }
+    bool operator==(const T &other) const { MOZ_ASSERT(!invalid()); return stub_ == other; }
+};
+
+//
+// Auxiliary info to help the DebugModeOSRHandler fix up state.
+//
+struct BaselineDebugModeOSRInfo
+{
+    uint8_t *resumeAddr;
+    jsbytecode *pc;
+    PCMappingSlotInfo slotInfo;
+    ICEntry::Kind frameKind;
+
+    // Filled in by SyncBaselineDebugModeOSRInfo.
+    uintptr_t stackAdjust;
+    Value valueR0;
+    Value valueR1;
+
+    BaselineDebugModeOSRInfo(jsbytecode *pc, ICEntry::Kind kind)
+      : resumeAddr(nullptr),
+        pc(pc),
+        slotInfo(0),
+        frameKind(kind),
+        stackAdjust(0),
+        valueR0(UndefinedValue()),
+        valueR1(UndefinedValue())
+    { }
+
+    void popValueInto(PCMappingSlotInfo::SlotLocation loc, Value *vp);
+};
+
+bool
+RecompileOnStackBaselineScriptsForDebugMode(JSContext *cx, JSCompartment *comp);
+
+} // namespace jit
+} // namespace js
+
+#endif // JS_ION
+
+#endif // jit_BaselineDebugModeOSR_h
--- a/js/src/jit/BaselineFrame.h
+++ b/js/src/jit/BaselineFrame.h
@@ -10,16 +10,18 @@
 #ifdef JS_ION
 
 #include "jit/IonFrames.h"
 #include "vm/Stack.h"
 
 namespace js {
 namespace jit {
 
+struct BaselineDebugModeOSRInfo;
+
 // The stack looks like this, fp is the frame pointer:
 //
 // fp+y   arguments
 // fp+x   IonJSFrameLayout (frame header)
 // fp  => saved frame pointer
 // fp-x   BaselineFrame
 //        locals
 //        stack values
@@ -52,17 +54,21 @@ class BaselineFrame
 
         // Frame has hookData_ set.
         HAS_HOOK_DATA    = 1 << 7,
 
         // Frame has profiler entry pushed.
         HAS_PUSHED_SPS_FRAME = 1 << 8,
 
         // Frame has over-recursed on an early check.
-        OVER_RECURSED    = 1 << 9
+        OVER_RECURSED    = 1 << 9,
+
+        // Frame has a BaselineRecompileInfo stashed in the scratch value
+        // slot. See PatchBaselineFramesForDebugMOde.
+        HAS_DEBUG_MODE_OSR_INFO = 1 << 10
     };
 
   protected: // Silence Clang warning about unused private fields.
     // We need to split the Value into 2 fields of 32 bits, otherwise the C++
     // compiler may add some padding between the fields.
     uint32_t loScratchValue_;
     uint32_t hiScratchValue_;
     uint32_t loReturnValue_;        // If HAS_RVAL, the frame's return value.
@@ -291,16 +297,34 @@ class BaselineFrame
     bool overRecursed() const {
         return flags_ & OVER_RECURSED;
     }
 
     void setOverRecursed() {
         flags_ |= OVER_RECURSED;
     }
 
+    BaselineDebugModeOSRInfo *debugModeOSRInfo() {
+        MOZ_ASSERT(flags_ & HAS_DEBUG_MODE_OSR_INFO);
+        return *reinterpret_cast<BaselineDebugModeOSRInfo **>(&loScratchValue_);
+    }
+
+    BaselineDebugModeOSRInfo *getDebugModeOSRInfo() {
+        if (flags_ & HAS_DEBUG_MODE_OSR_INFO)
+            return debugModeOSRInfo();
+        return nullptr;
+    }
+
+    void setDebugModeOSRInfo(BaselineDebugModeOSRInfo *info) {
+        flags_ |= HAS_DEBUG_MODE_OSR_INFO;
+        *reinterpret_cast<BaselineDebugModeOSRInfo **>(&loScratchValue_) = info;
+    }
+
+    void deleteDebugModeOSRInfo();
+
     void trace(JSTracer *trc, JitFrameIterator &frame);
 
     bool isFunctionFrame() const {
         return CalleeTokenIsFunction(calleeToken());
     }
     bool isGlobalFrame() const {
         return !CalleeTokenIsFunction(calleeToken());
     }
--- a/js/src/jit/BaselineFrameInfo.h
+++ b/js/src/jit/BaselineFrameInfo.h
@@ -157,25 +157,25 @@ class StackValue
         knownType_ = JSVAL_TYPE_UNKNOWN;
     }
 };
 
 enum StackAdjustment { AdjustStack, DontAdjustStack };
 
 class FrameInfo
 {
-    RootedScript script;
+    JSScript *script;
     MacroAssembler &masm;
 
     FixedList<StackValue> stack;
     size_t spIndex;
 
   public:
-    FrameInfo(JSContext *cx, HandleScript script, MacroAssembler &masm)
-      : script(cx, script),
+    FrameInfo(JSScript *script, MacroAssembler &masm)
+      : script(script),
         masm(masm),
         stack(),
         spIndex(0)
     { }
 
     bool init(TempAllocator &alloc);
 
     uint32_t nlocals() const {
--- a/js/src/jit/BaselineHelpers.h
+++ b/js/src/jit/BaselineHelpers.h
@@ -10,16 +10,18 @@
 #ifdef JS_ION
 
 #if defined(JS_CODEGEN_X86)
 # include "jit/x86/BaselineHelpers-x86.h"
 #elif defined(JS_CODEGEN_X64)
 # include "jit/x64/BaselineHelpers-x64.h"
 #elif defined(JS_CODEGEN_ARM)
 # include "jit/arm/BaselineHelpers-arm.h"
+#elif defined(JS_CODEGEN_MIPS)
+# include "jit/mips/BaselineHelpers-mips.h"
 #else
 # error "Unknown architecture!"
 #endif
 
 namespace js {
 namespace jit {
 
 } // namespace jit
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -8,16 +8,17 @@
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/TemplateLib.h"
 
 #include "jslibmath.h"
 #include "jstypes.h"
 
 #include "builtin/Eval.h"
+#include "jit/BaselineDebugModeOSR.h"
 #include "jit/BaselineHelpers.h"
 #include "jit/BaselineJIT.h"
 #include "jit/IonLinker.h"
 #include "jit/IonSpewer.h"
 #include "jit/Lowering.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
@@ -665,16 +666,30 @@ ICStubCompiler::enterStubFrame(MacroAsse
 void
 ICStubCompiler::leaveStubFrame(MacroAssembler &masm, bool calledIntoIon)
 {
     JS_ASSERT(entersStubFrame_);
     EmitLeaveStubFrame(masm, calledIntoIon);
 }
 
 void
+ICStubCompiler::leaveStubFrameHead(MacroAssembler &masm, bool calledIntoIon)
+{
+    JS_ASSERT(entersStubFrame_);
+    EmitLeaveStubFrameHead(masm, calledIntoIon);
+}
+
+void
+ICStubCompiler::leaveStubFrameCommonTail(MacroAssembler &masm)
+{
+    JS_ASSERT(entersStubFrame_);
+    EmitLeaveStubFrameCommonTail(masm);
+}
+
+void
 ICStubCompiler::guardProfilingEnabled(MacroAssembler &masm, Register scratch, Label *skip)
 {
     // This should only be called from the following stubs.
     JS_ASSERT(kind == ICStub::Call_Scripted                             ||
               kind == ICStub::Call_AnyScripted                          ||
               kind == ICStub::Call_Native                               ||
               kind == ICStub::Call_ScriptedApplyArray                   ||
               kind == ICStub::Call_ScriptedApplyArguments               ||
@@ -740,17 +755,17 @@ ICStubCompiler::emitPostWriteBarrierSlot
 
     masm.branchPtrInNurseryRange(obj, scratch, &skipBarrier);
 
     Register valReg = masm.extractObject(val, scratch);
     masm.branchPtr(Assembler::Below, valReg, ImmWord(nursery.start()), &skipBarrier);
     masm.branchPtr(Assembler::AboveOrEqual, valReg, ImmWord(nursery.heapEnd()), &skipBarrier);
 
     // void PostWriteBarrier(JSRuntime *rt, JSObject *obj);
-#ifdef JS_CODEGEN_ARM
+#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
     saveRegs.add(BaselineTailCallReg);
 #endif
     saveRegs = GeneralRegisterSet::Intersect(saveRegs, GeneralRegisterSet::Volatile());
     masm.PushRegsInMask(saveRegs);
     masm.setupUnalignedABICall(2, scratch);
     masm.movePtr(ImmPtr(cx->runtime()), scratch);
     masm.passABIArg(scratch);
     masm.passABIArg(obj);
@@ -1752,19 +1767,22 @@ ICNewObject_Fallback::Compiler::generate
     return tailCallVM(DoNewObjectInfo, masm);
 }
 
 //
 // Compare_Fallback
 //
 
 static bool
-DoCompareFallback(JSContext *cx, BaselineFrame *frame, ICCompare_Fallback *stub, HandleValue lhs,
+DoCompareFallback(JSContext *cx, BaselineFrame *frame, ICCompare_Fallback *stub_, HandleValue lhs,
                   HandleValue rhs, MutableHandleValue ret)
 {
+    // This fallback stub may trigger debug mode toggling.
+    DebugModeOSRVolatileStub<ICCompare_Fallback *> stub(frame, stub_);
+
     jsbytecode *pc = stub->icEntry()->pc(frame->script());
     JSOp op = JSOp(*pc);
 
     FallbackICSpew(cx, stub, "Compare(%s)", js_CodeName[op]);
 
     // Case operations in a CONDSWITCH are performing strict equality.
     if (op == JSOP_CASE)
         op = JSOP_STRICTEQ;
@@ -1811,16 +1829,20 @@ DoCompareFallback(JSContext *cx, Baselin
         break;
       default:
         JS_ASSERT(!"Unhandled baseline compare op");
         return false;
     }
 
     ret.setBoolean(out);
 
+    // Check if debug mode toggling made the stub invalid.
+    if (stub.invalid())
+        return true;
+
     // Check to see if a new stub should be generated.
     if (stub->numOptimizedStubs() >= ICCompare_Fallback::MAX_OPTIMIZED_STUBS) {
         // TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
         // But for now we just bail.
         return true;
     }
 
     JSScript *script = frame->script();
@@ -2484,19 +2506,22 @@ ICToNumber_Fallback::Compiler::generateS
 // BinaryArith_Fallback
 //
 
 // Disable PGO (see bug 851490).
 #if defined(_MSC_VER)
 # pragma optimize("g", off)
 #endif
 static bool
-DoBinaryArithFallback(JSContext *cx, BaselineFrame *frame, ICBinaryArith_Fallback *stub,
+DoBinaryArithFallback(JSContext *cx, BaselineFrame *frame, ICBinaryArith_Fallback *stub_,
                       HandleValue lhs, HandleValue rhs, MutableHandleValue ret)
 {
+    // This fallback stub may trigger debug mode toggling.
+    DebugModeOSRVolatileStub<ICBinaryArith_Fallback *> stub(frame, stub_);
+
     RootedScript script(cx, frame->script());
     jsbytecode *pc = stub->icEntry()->pc(script);
     JSOp op = JSOp(*pc);
     FallbackICSpew(cx, stub, "BinaryArith(%s,%d,%d)", js_CodeName[op],
             int(lhs.isDouble() ? JSVAL_TYPE_DOUBLE : lhs.extractNonDoubleType()),
             int(rhs.isDouble() ? JSVAL_TYPE_DOUBLE : rhs.extractNonDoubleType()));
 
     // Don't pass lhs/rhs directly, we need the original values when
@@ -2566,16 +2591,20 @@ DoBinaryArithFallback(JSContext *cx, Bas
         if (!UrshOperation(cx, lhs, rhs, ret))
             return false;
         break;
       }
       default:
         MOZ_ASSUME_UNREACHABLE("Unhandled baseline arith op");
     }
 
+    // Check if debug mode toggling made the stub invalid.
+    if (stub.invalid())
+        return true;
+
     if (ret.isDouble())
         stub->setSawDoubleResult();
 
     // Check to see if a new stub should be generated.
     if (stub->numOptimizedStubs() >= ICBinaryArith_Fallback::MAX_OPTIMIZED_STUBS) {
         stub->noteUnoptimizableOperands();
         return true;
     }
@@ -3043,19 +3072,22 @@ ICBinaryArith_DoubleWithInt32::Compiler:
 // UnaryArith_Fallback
 //
 
 // Disable PGO (see bug 851490).
 #if defined(_MSC_VER)
 # pragma optimize("g", off)
 #endif
 static bool
-DoUnaryArithFallback(JSContext *cx, BaselineFrame *frame, ICUnaryArith_Fallback *stub,
+DoUnaryArithFallback(JSContext *cx, BaselineFrame *frame, ICUnaryArith_Fallback *stub_,
                      HandleValue val, MutableHandleValue res)
 {
+    // This fallback stub may trigger debug mode toggling.
+    DebugModeOSRVolatileStub<ICUnaryArith_Fallback *> stub(frame, stub_);
+
     RootedScript script(cx, frame->script());
     jsbytecode *pc = stub->icEntry()->pc(script);
     JSOp op = JSOp(*pc);
     FallbackICSpew(cx, stub, "UnaryArith(%s)", js_CodeName[op]);
 
     switch (op) {
       case JSOP_BITNOT: {
         int32_t result;
@@ -3067,16 +3099,20 @@ DoUnaryArithFallback(JSContext *cx, Base
       case JSOP_NEG:
         if (!NegOperation(cx, script, pc, val, res))
             return false;
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Unexpected op");
     }
 
+    // Check if debug mode toggling made the stub invalid.
+    if (stub.invalid())
+        return true;
+
     if (res.isDouble())
         stub->setSawDoubleResult();
 
     if (stub->numOptimizedStubs() >= ICUnaryArith_Fallback::MAX_OPTIMIZED_STUBS) {
         // TODO: Discard/replace stubs.
         return true;
     }
 
@@ -3972,19 +4008,23 @@ TryAttachGetElemStub(JSContext *cx, JSSc
     // determine that an object has no properties on such indexes.
     if (rhs.isNumber() && rhs.toNumber() < 0)
         stub->noteNegativeIndex();
 
     return true;
 }
 
 static bool
-DoGetElemFallback(JSContext *cx, BaselineFrame *frame, ICGetElem_Fallback *stub, HandleValue lhs,
+DoGetElemFallback(JSContext *cx, BaselineFrame *frame, ICGetElem_Fallback *stub_, HandleValue lhs,
                   HandleValue rhs, MutableHandleValue res)
 {
+    // This fallback stub may trigger debug mode toggling.
+    DebugModeOSRVolatileStub<ICGetElem_Fallback *> stub(frame, stub_);
+
+    RootedScript script(cx, frame->script());
     jsbytecode *pc = stub->icEntry()->pc(frame->script());
     JSOp op = JSOp(*pc);
     FallbackICSpew(cx, stub, "GetElem(%s)", js_CodeName[op]);
 
     JS_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM);
 
     // Don't pass lhs directly, we need it when generating stubs.
     RootedValue lhsCopy(cx, lhs);
@@ -3999,16 +4039,20 @@ DoGetElemFallback(JSContext *cx, Baselin
     }
 
     if (!isOptimizedArgs) {
         if (!GetElementOperation(cx, op, &lhsCopy, rhs, res))
             return false;
         types::TypeScript::Monitor(cx, frame->script(), pc, res);
     }
 
+    // Check if debug mode toggling made the stub invalid.
+    if (stub.invalid())
+        return true;
+
     // Add a type monitor stub for the resulting value.
     if (!stub->addMonitorStubForValue(cx, frame->script(), res))
         return false;
 
     if (stub->numOptimizedStubs() >= ICGetElem_Fallback::MAX_OPTIMIZED_STUBS) {
         // TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
         // But for now we just bail.
         return true;
@@ -4940,19 +4984,22 @@ CanOptimizeDenseSetElem(JSContext *cx, H
         return false;
 
     *isAddingCaseOut = true;
 
     return true;
 }
 
 static bool
-DoSetElemFallback(JSContext *cx, BaselineFrame *frame, ICSetElem_Fallback *stub, Value *stack,
+DoSetElemFallback(JSContext *cx, BaselineFrame *frame, ICSetElem_Fallback *stub_, Value *stack,
                   HandleValue objv, HandleValue index, HandleValue rhs)
 {
+    // This fallback stub may trigger debug mode toggling.
+    DebugModeOSRVolatileStub<ICSetElem_Fallback *> stub(frame, stub_);
+
     RootedScript script(cx, frame->script());
     jsbytecode *pc = stub->icEntry()->pc(script);
     JSOp op = JSOp(*pc);
     FallbackICSpew(cx, stub, "SetElem(%s)", js_CodeName[JSOp(*pc)]);
 
     JS_ASSERT(op == JSOP_SETELEM ||
               op == JSOP_INITELEM ||
               op == JSOP_INITELEM_ARRAY);
@@ -4982,16 +5029,20 @@ DoSetElemFallback(JSContext *cx, Baselin
         if (!SetObjectElement(cx, obj, index, rhs, script->strict(), script, pc))
             return false;
     }
 
     // Overwrite the object on the stack (pushed for the decompiler) with the rhs.
     JS_ASSERT(stack[2] == objv);
     stack[2] = rhs;
 
+    // Check if debug mode toggling made the stub invalid.
+    if (stub.invalid())
+        return true;
+
     if (stub->numOptimizedStubs() >= ICSetElem_Fallback::MAX_OPTIMIZED_STUBS) {
         // TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
         // But for now we just bail.
         return true;
     }
 
     // Try to generate new stubs.
     if (obj->isNative() &&
@@ -5752,19 +5803,22 @@ TryAttachScopeNameStub(JSContext *cx, Ha
     if (!newStub)
         return false;
 
     stub->addNewStub(newStub);
     return true;
 }
 
 static bool
-DoGetNameFallback(JSContext *cx, BaselineFrame *frame, ICGetName_Fallback *stub,
+DoGetNameFallback(JSContext *cx, BaselineFrame *frame, ICGetName_Fallback *stub_,
                   HandleObject scopeChain, MutableHandleValue res)
 {
+    // This fallback stub may trigger debug mode toggling.
+    DebugModeOSRVolatileStub<ICGetName_Fallback *> stub(frame, stub_);
+
     RootedScript script(cx, frame->script());
     jsbytecode *pc = stub->icEntry()->pc(script);
     mozilla::DebugOnly<JSOp> op = JSOp(*pc);
     FallbackICSpew(cx, stub, "GetName(%s)", js_CodeName[JSOp(*pc)]);
 
     JS_ASSERT(op == JSOP_NAME || op == JSOP_GETGNAME);
 
     RootedPropertyName name(cx, script->getName(pc));
@@ -5774,16 +5828,20 @@ DoGetNameFallback(JSContext *cx, Baselin
             return false;
     } else {
         if (!GetScopeName(cx, scopeChain, name, res))
             return false;
     }
 
     types::TypeScript::Monitor(cx, script, pc, res);
 
+    // Check if debug mode toggling made the stub invalid.
+    if (stub.invalid())
+        return true;
+
     // Add a type monitor stub for the resulting value.
     if (!stub->addMonitorStubForValue(cx, script, res))
         return false;
 
     // Attach new stub.
     if (stub->numOptimizedStubs() >= ICGetName_Fallback::MAX_OPTIMIZED_STUBS) {
         // TODO: Discard all stubs in this IC and replace with generic stub.
         return true;
@@ -5929,35 +5987,42 @@ ICBindName_Fallback::Compiler::generateS
     return tailCallVM(DoBindNameFallbackInfo, masm);
 }
 
 //
 // GetIntrinsic_Fallback
 //
 
 static bool
-DoGetIntrinsicFallback(JSContext *cx, BaselineFrame *frame, ICGetIntrinsic_Fallback *stub,
+DoGetIntrinsicFallback(JSContext *cx, BaselineFrame *frame, ICGetIntrinsic_Fallback *stub_,
                        MutableHandleValue res)
 {
+    // This fallback stub may trigger debug mode toggling.
+    DebugModeOSRVolatileStub<ICGetIntrinsic_Fallback *> stub(frame, stub_);
+
     RootedScript script(cx, frame->script());
     jsbytecode *pc = stub->icEntry()->pc(script);
     mozilla::DebugOnly<JSOp> op = JSOp(*pc);
     FallbackICSpew(cx, stub, "GetIntrinsic(%s)", js_CodeName[JSOp(*pc)]);
 
     JS_ASSERT(op == JSOP_GETINTRINSIC);
 
     if (!GetIntrinsicOperation(cx, pc, res))
         return false;
 
     // An intrinsic operation will always produce the same result, so only
     // needs to be monitored once. Attach a stub to load the resulting constant
     // directly.
 
     types::TypeScript::Monitor(cx, script, pc, res);
 
+    // Check if debug mode toggling made the stub invalid.
+    if (stub.invalid())
+        return true;
+
     IonSpew(IonSpew_BaselineIC, "  Generating GetIntrinsic optimized stub");
     ICGetIntrinsic_Constant::Compiler compiler(cx, res);
     ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
     if (!newStub)
         return false;
 
     stub->addNewStub(newStub);
     return true;
@@ -6309,19 +6374,22 @@ TryAttachPrimitiveGetPropStub(JSContext 
         return false;
 
     stub->addNewStub(newStub);
     *attached = true;
     return true;
 }
 
 static bool
-DoGetPropFallback(JSContext *cx, BaselineFrame *frame, ICGetProp_Fallback *stub,
+DoGetPropFallback(JSContext *cx, BaselineFrame *frame, ICGetProp_Fallback *stub_,
                   MutableHandleValue val, MutableHandleValue res)
 {
+    // This fallback stub may trigger debug mode toggling.
+    DebugModeOSRVolatileStub<ICGetProp_Fallback *> stub(frame, stub_);
+
     jsbytecode *pc = stub->icEntry()->pc(frame->script());
     JSOp op = JSOp(*pc);
     FallbackICSpew(cx, stub, "GetProp(%s)", js_CodeName[op]);
 
     JS_ASSERT(op == JSOP_GETPROP || op == JSOP_CALLPROP || op == JSOP_LENGTH || op == JSOP_GETXPROP);
 
     RootedPropertyName name(cx, frame->script()->getName(pc));
 
@@ -6357,16 +6425,20 @@ DoGetPropFallback(JSContext *cx, Baselin
     if (op == JSOP_CALLPROP && MOZ_UNLIKELY(res.isUndefined()) && val.isObject()) {
         if (!OnUnknownMethod(cx, obj, IdToValue(id), res))
             return false;
     }
 #endif
 
     types::TypeScript::Monitor(cx, frame->script(), pc, res);
 
+    // Check if debug mode toggling made the stub invalid.
+    if (stub.invalid())
+        return true;
+
     // Add a type monitor stub for the resulting value.
     if (!stub->addMonitorStubForValue(cx, frame->script(), res))
         return false;
 
     if (stub->numOptimizedStubs() >= ICGetProp_Fallback::MAX_OPTIMIZED_STUBS) {
         // TODO: Discard all stubs in this IC and replace with generic getprop stub.
         return true;
     }
@@ -6418,44 +6490,61 @@ ICGetProp_Fallback::Compiler::generateSt
     // Push arguments.
     masm.pushValue(R0);
     masm.push(BaselineStubReg);
     masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
 
     if (!tailCallVM(DoGetPropFallbackInfo, masm))
         return false;
 
-    // What follows is bailout-only code for inlined scripted getters
-    // The return address pointed to by the baseline stack points here.
-    returnOffset_ = masm.currentOffset();
-
+    // What follows is bailout for inlined scripted getters or for on-stack
+    // debug mode recompile. The return address pointed to by the baseline
+    // stack points here.
+    //
     // Even though the fallback frame doesn't enter a stub frame, the CallScripted
     // frame that we are emulating does. Again, we lie.
 #ifdef DEBUG
     entersStubFrame_ = true;
 #endif
 
-    leaveStubFrame(masm, true);
+    Label leaveStubCommon;
+
+    returnFromStubOffset_ = masm.currentOffset();
+    leaveStubFrameHead(masm, false);
+    masm.jump(&leaveStubCommon);
+
+    returnFromIonOffset_ = masm.currentOffset();
+    leaveStubFrameHead(masm, true);
+
+    masm.bind(&leaveStubCommon);
+    leaveStubFrameCommonTail(masm);
 
     // When we get here, BaselineStubReg contains the ICGetProp_Fallback stub,
     // which we can't use to enter the TypeMonitor IC, because it's a MonitoredFallbackStub
     // instead of a MonitoredStub. So, we cheat.
     masm.loadPtr(Address(BaselineStubReg, ICMonitoredFallbackStub::offsetOfFallbackMonitorStub()),
                  BaselineStubReg);
     EmitEnterTypeMonitorIC(masm, ICTypeMonitor_Fallback::offsetOfFirstMonitorStub());
 
     return true;
 }
 
 bool
 ICGetProp_Fallback::Compiler::postGenerateStubCode(MacroAssembler &masm, Handle<JitCode *> code)
 {
-    CodeOffsetLabel offset(returnOffset_);
-    offset.fixup(&masm);
-    cx->compartment()->jitCompartment()->initBaselineGetPropReturnAddr(code->raw() + offset.offset());
+    JitCompartment *comp = cx->compartment()->jitCompartment();
+
+    CodeOffsetLabel fromIon(returnFromIonOffset_);
+    fromIon.fixup(&masm);
+    comp->initBaselineGetPropReturnFromIonAddr(code->raw() + fromIon.offset());
+
+    CodeOffsetLabel fromVM(returnFromStubOffset_);
+    fromVM.fixup(&masm);
+    comp->initBaselineGetPropReturnFromStubAddr(code->raw() + fromVM.offset());
+
     return true;
 }
 
 bool
 ICGetProp_ArrayLength::Compiler::generateStubCode(MacroAssembler &masm)
 {
     Label failure;
     masm.branchTestObject(Assembler::NotEqual, R0, &failure);
@@ -7243,19 +7332,22 @@ TryAttachSetPropStub(JSContext *cx, Hand
         *attached = true;
         return true;
     }
 
     return true;
 }
 
 static bool
-DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub, HandleValue lhs,
-                  HandleValue rhs, MutableHandleValue res)
-{
+DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub_,
+                  HandleValue lhs, HandleValue rhs, MutableHandleValue res)
+{
+    // This fallback stub may trigger debug mode toggling.
+    DebugModeOSRVolatileStub<ICSetProp_Fallback *> stub(frame, stub_);
+
     RootedScript script(cx, frame->script());
     jsbytecode *pc = stub->icEntry()->pc(script);
     JSOp op = JSOp(*pc);
     FallbackICSpew(cx, stub, "SetProp(%s)", js_CodeName[op]);
 
     JS_ASSERT(op == JSOP_SETPROP ||
               op == JSOP_SETNAME ||
               op == JSOP_SETGNAME ||
@@ -7294,16 +7386,20 @@ DoSetPropFallback(JSContext *cx, Baselin
             if (!js::SetProperty<false>(cx, obj, id, rhs))
                 return false;
         }
     }
 
     // Leave the RHS on the stack.
     res.set(rhs);
 
+    // Check if debug mode toggling made the stub invalid.
+    if (stub.invalid())
+        return true;
+
     if (stub->numOptimizedStubs() >= ICSetProp_Fallback::MAX_OPTIMIZED_STUBS) {
         // TODO: Discard all stubs in this IC and replace with generic setprop stub.
         return true;
     }
 
     bool attached = false;
     if (!TryAttachSetPropStub(cx, script, pc, stub, obj, oldShape, oldSlots, name, id, rhs,
          &attached))
@@ -7339,41 +7435,58 @@ ICSetProp_Fallback::Compiler::generateSt
     masm.pushValue(R1);
     masm.pushValue(R0);
     masm.push(BaselineStubReg);
     masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
 
     if (!tailCallVM(DoSetPropFallbackInfo, masm))
         return false;
 
-    // What follows is bailout-only code for inlined scripted getters
-    // The return address pointed to by the baseline stack points here.
-    returnOffset_ = masm.currentOffset();
-
+    // What follows is bailout debug mode recompile code for inlined scripted
+    // getters The return address pointed to by the baseline stack points
+    // here.
+    //
     // Even though the fallback frame doesn't enter a stub frame, the CallScripted
     // frame that we are emulating does. Again, we lie.
 #ifdef DEBUG
     entersStubFrame_ = true;
 #endif
 
-    leaveStubFrame(masm, true);
+    Label leaveStubCommon;
+
+    returnFromStubOffset_ = masm.currentOffset();
+    leaveStubFrameHead(masm, false);
+    masm.jump(&leaveStubCommon);
+
+    returnFromIonOffset_ = masm.currentOffset();
+    leaveStubFrameHead(masm, true);
+
+    masm.bind(&leaveStubCommon);
+    leaveStubFrameCommonTail(masm);
 
     // Retrieve the stashed initial argument from the caller's frame before returning
     EmitUnstowICValues(masm, 1);
     EmitReturnFromIC(masm);
 
     return true;
 }
 
 bool
 ICSetProp_Fallback::Compiler::postGenerateStubCode(MacroAssembler &masm, Handle<JitCode *> code)
 {
-    CodeOffsetLabel offset(returnOffset_);
-    offset.fixup(&masm);
-    cx->compartment()->jitCompartment()->initBaselineSetPropReturnAddr(code->raw() + offset.offset());
+    JitCompartment *comp = cx->compartment()->jitCompartment();
+
+    CodeOffsetLabel fromIon(returnFromIonOffset_);
+    fromIon.fixup(&masm);
+    comp->initBaselineSetPropReturnFromIonAddr(code->raw() + fromIon.offset());
+
+    CodeOffsetLabel fromVM(returnFromStubOffset_);
+    fromVM.fixup(&masm);
+    comp->initBaselineSetPropReturnFromStubAddr(code->raw() + fromVM.offset());
+
     return true;
 }
 
 bool
 ICSetProp_Native::Compiler::generateStubCode(MacroAssembler &masm)
 {
     Label failure;
 
@@ -8079,19 +8192,22 @@ MaybeCloneFunctionAtCallsite(JSContext *
     if (!fun)
         return false;
 
     callee.setObject(*fun);
     return true;
 }
 
 static bool
-DoCallFallback(JSContext *cx, BaselineFrame *frame, ICCall_Fallback *stub, uint32_t argc,
+DoCallFallback(JSContext *cx, BaselineFrame *frame, ICCall_Fallback *stub_, uint32_t argc,
                Value *vp, MutableHandleValue res)
 {
+    // This fallback stub may trigger debug mode toggling.
+    DebugModeOSRVolatileStub<ICCall_Fallback *> stub(frame, stub_);
+
     // Ensure vp array is rooted - we may GC in here.
     AutoArrayRooter vpRoot(cx, argc + 2, vp);
 
     RootedScript script(cx, frame->script());
     jsbytecode *pc = stub->icEntry()->pc(script);
     JSOp op = JSOp(*pc);
     FallbackICSpew(cx, stub, "Call(%s)", js_CodeName[op]);
 
@@ -8133,16 +8249,20 @@ DoCallFallback(JSContext *cx, BaselineFr
     } else {
         JS_ASSERT(op == JSOP_CALL || op == JSOP_FUNCALL || op == JSOP_FUNAPPLY || op == JSOP_EVAL);
         if (!Invoke(cx, thisv, callee, argc, args, res))
             return false;
     }
 
     types::TypeScript::Monitor(cx, script, pc, res);
 
+    // Check if debug mode toggling made the stub invalid.
+    if (stub.invalid())
+        return true;
+
     // Attach a new TypeMonitor stub for this value.
     ICTypeMonitor_Fallback *typeMonFbStub = stub->fallbackMonitorStub();
     if (!typeMonFbStub->addMonitorStubForValue(cx, script, res))
         return false;
     // Add a type monitor stub for the resulting value.
     if (!stub->addMonitorStubForValue(cx, script, res))
         return false;
 
@@ -8377,27 +8497,45 @@ ICCall_Fallback::Compiler::generateStubC
     masm.pushBaselineFramePtr(R0.scratchReg(), R0.scratchReg());
 
     if (!callVM(DoCallFallbackInfo, masm))
         return false;
 
     leaveStubFrame(masm);
     EmitReturnFromIC(masm);
 
-    // The following asmcode is only used when an Ion inlined frame bails out into
-    // baseline jitcode.  The return address pushed onto the reconstructed baseline stack
-    // points here.
-    returnOffset_ = masm.currentOffset();
+    // The following asmcode is only used either when an Ion inlined frame
+    // bails out into baseline jitcode or we need to do on-stack script
+    // replacement for debug mode recompile.
+    Label leaveStubCommon;
+    returnFromStubOffset_ = masm.currentOffset();
 
     // Load passed-in ThisV into R1 just in case it's needed.  Need to do this before
     // we leave the stub frame since that info will be lost.
     // Current stack:  [...., ThisV, ActualArgc, CalleeToken, Descriptor ]
     masm.loadValue(Address(BaselineStackReg, 3 * sizeof(size_t)), R1);
 
-    leaveStubFrame(masm, true);
+    // Emit the coming-from-VM specific part of the stub-leaving code.
+    leaveStubFrameHead(masm, /* calledIntoIon = */ false);
+
+    // Jump to the common leave stub tail.
+    masm.jump(&leaveStubCommon);
+
+    // For Ion bailouts, the return address pushed onto the reconstructed
+    // baseline stack points here.
+    returnFromIonOffset_ = masm.currentOffset();
+
+    masm.loadValue(Address(BaselineStackReg, 3 * sizeof(size_t)), R1);
+
+    // Emit the coming-from-Ion specific part of the stub-leaving code.
+    leaveStubFrameHead(masm, /* calledIntoIon = */ true);
+
+    // Emit the common stub-leaving tail.
+    masm.bind(&leaveStubCommon);
+    leaveStubFrameCommonTail(masm);
 
     // R1 and R0 are taken.
     regs = availableGeneralRegs(2);
     Register scratch = regs.takeAny();
 
     // If this is a |constructing| call, if the callee returns a non-object, we replace it with
     // the |this| object passed in.
     JS_ASSERT(JSReturnOperand == R0);
@@ -8422,19 +8560,26 @@ ICCall_Fallback::Compiler::generateStubC
     EmitEnterTypeMonitorIC(masm, ICTypeMonitor_Fallback::offsetOfFirstMonitorStub());
 
     return true;
 }
 
 bool
 ICCall_Fallback::Compiler::postGenerateStubCode(MacroAssembler &masm, Handle<JitCode *> code)
 {
-    CodeOffsetLabel offset(returnOffset_);
-    offset.fixup(&masm);
-    cx->compartment()->jitCompartment()->initBaselineCallReturnAddr(code->raw() + offset.offset());
+    JitCompartment *comp = cx->compartment()->jitCompartment();
+
+    CodeOffsetLabel fromIon(returnFromIonOffset_);
+    fromIon.fixup(&masm);
+    comp->initBaselineCallReturnFromIonAddr(code->raw() + fromIon.offset());
+
+    CodeOffsetLabel fromVM(returnFromStubOffset_);
+    fromVM.fixup(&masm);
+    comp->initBaselineCallReturnFromStubAddr(code->raw() + fromVM.offset());
+
     return true;
 }
 
 typedef bool (*CreateThisFn)(JSContext *cx, HandleObject callee, MutableHandleValue rval);
 static const VMFunction CreateThisInfoBaseline = FunctionInfo<CreateThisFn>(CreateThis);
 
 bool
 ICCallScriptedCompiler::generateStubCode(MacroAssembler &masm)
@@ -9197,17 +9342,17 @@ ICTableSwitch::Compiler::getStub(ICStubS
             table[i] = defaultpc;
         pc += JUMP_OFFSET_LEN;
     }
 
     return ICTableSwitch::New(space, code, table, low, length, defaultpc);
 }
 
 void
-ICTableSwitch::fixupJumpTable(HandleScript script, BaselineScript *baseline)
+ICTableSwitch::fixupJumpTable(JSScript *script, BaselineScript *baseline)
 {
     defaultTarget_ = baseline->nativeCodeForPC(script, (jsbytecode *) defaultTarget_);
 
     for (int32_t i = 0; i < length_; i++)
         table_[i] = baseline->nativeCodeForPC(script, (jsbytecode *) table_[i]);
 }
 
 //
@@ -9246,26 +9391,33 @@ ICIteratorNew_Fallback::Compiler::genera
     return tailCallVM(DoIteratorNewFallbackInfo, masm);
 }
 
 //
 // IteratorMore_Fallback
 //
 
 static bool
-DoIteratorMoreFallback(JSContext *cx, BaselineFrame *frame, ICIteratorMore_Fallback *stub,
+DoIteratorMoreFallback(JSContext *cx, BaselineFrame *frame, ICIteratorMore_Fallback *stub_,
                        HandleValue iterValue, MutableHandleValue res)
 {
+    // This fallback stub may trigger debug mode toggling.
+    DebugModeOSRVolatileStub<ICIteratorMore_Fallback *> stub(frame, stub_);
+
     FallbackICSpew(cx, stub, "IteratorMore");
 
     bool cond;
     if (!IteratorMore(cx, &iterValue.toObject(), &cond, res))
         return false;
     res.setBoolean(cond);
 
+    // Check if debug mode toggling made the stub invalid.
+    if (stub.invalid())
+        return true;
+
     if (iterValue.toObject().is<PropertyIteratorObject>() &&
         !stub->hasStub(ICStub::IteratorMore_Native))
     {
         ICIteratorMore_Native::Compiler compiler(cx);
         ICStub *newStub = compiler.getStub(compiler.getStubSpace(frame->script()));
         if (!newStub)
             return false;
         stub->addNewStub(newStub);
@@ -9327,25 +9479,32 @@ ICIteratorMore_Native::Compiler::generat
     return true;
 }
 
 //
 // IteratorNext_Fallback
 //
 
 static bool
-DoIteratorNextFallback(JSContext *cx, BaselineFrame *frame, ICIteratorNext_Fallback *stub,
+DoIteratorNextFallback(JSContext *cx, BaselineFrame *frame, ICIteratorNext_Fallback *stub_,
                        HandleValue iterValue, MutableHandleValue res)
 {
+    // This fallback stub may trigger debug mode toggling.
+    DebugModeOSRVolatileStub<ICIteratorNext_Fallback *> stub(frame, stub_);
+
     FallbackICSpew(cx, stub, "IteratorNext");
 
     RootedObject iteratorObject(cx, &iterValue.toObject());
     if (!IteratorNext(cx, iteratorObject, res))
         return false;
 
+    // Check if debug mode toggling made the stub invalid.
+    if (stub.invalid())
+        return true;
+
     if (!res.isString() && !stub->hasNonStringResult())
         stub->setHasNonStringResult();
 
     if (iteratorObject->is<PropertyIteratorObject>() &&
         !stub->hasStub(ICStub::IteratorNext_Native))
     {
         ICIteratorNext_Native::Compiler compiler(cx);
         ICStub *newStub = compiler.getStub(compiler.getStubSpace(frame->script()));
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -4,16 +4,18 @@
  * 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_BaselineIC_h
 #define jit_BaselineIC_h
 
 #ifdef JS_ION
 
+#include "mozilla/Assertions.h"
+
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jsgc.h"
 #include "jsopcode.h"
 
 #include "jit/BaselineJIT.h"
 #include "jit/BaselineRegisters.h"
 
@@ -201,25 +203,59 @@ class ICEntry
     // A pointer to the baseline IC stub for this instruction.
     ICStub *firstStub_;
 
     // Offset from the start of the JIT code where the IC
     // load and call instructions are.
     uint32_t returnOffset_;
 
     // The PC of this IC's bytecode op within the JSScript.
-    uint32_t pcOffset_ : 31;
-
-    // Whether this IC is for a bytecode op.
-    uint32_t isForOp_ : 1;
+    uint32_t pcOffset_ : 29;
 
   public:
-    ICEntry(uint32_t pcOffset, bool isForOp)
-      : firstStub_(nullptr), returnOffset_(), pcOffset_(pcOffset), isForOp_(isForOp)
-    {}
+    enum Kind {
+        // A for-op IC entry.
+        Kind_Op = 0,
+
+        // A non-op IC entry.
+        Kind_NonOp,
+
+        // A fake IC entry for returning from a callVM.
+        Kind_CallVM,
+
+        // A fake IC entry for returning from DebugTrapHandler.
+        Kind_DebugTrap,
+
+        // A fake IC entry for returning from a callVM to
+        // Debug{Prologue,Epilogue}.
+        Kind_DebugPrologue,
+        Kind_DebugEpilogue
+    };
+
+  private:
+    // What this IC is for.
+    Kind kind_ : 3;
+
+    // Set the kind and asserts that it's sane.
+    void setKind(Kind kind) {
+        kind_ = kind;
+        MOZ_ASSERT(this->kind() == kind);
+    }
+
+  public:
+    ICEntry(uint32_t pcOffset, Kind kind)
+      : firstStub_(nullptr), returnOffset_(), pcOffset_(pcOffset)
+    {
+        // The offset must fit in at least 29 bits, since we shave off 3 for
+        // the Kind enum.
+        MOZ_ASSERT(pcOffset_ == pcOffset);
+        JS_STATIC_ASSERT(BaselineScript::MAX_JSSCRIPT_LENGTH < 0x1fffffffu);
+        MOZ_ASSERT(pcOffset <= BaselineScript::MAX_JSSCRIPT_LENGTH);
+        setKind(kind);
+    }
 
     CodeOffsetLabel returnOffset() const {
         return CodeOffsetLabel(returnOffset_);
     }
 
     void setReturnOffset(CodeOffsetLabel offset) {
         JS_ASSERT(offset.offset() <= (size_t) UINT32_MAX);
         returnOffset_ = (uint32_t) offset.offset();
@@ -235,18 +271,31 @@ class ICEntry
     uint32_t pcOffset() const {
         return pcOffset_;
     }
 
     jsbytecode *pc(JSScript *script) const {
         return script->offsetToPC(pcOffset_);
     }
 
+    Kind kind() const {
+        // MSVC compiles enums as signed.
+        return (Kind)(kind_ & 0x7);
+    }
     bool isForOp() const {
-        return isForOp_;
+        return kind() == Kind_Op;
+    }
+
+    void setForDebugPrologue() {
+        MOZ_ASSERT(kind() == Kind_CallVM);
+        setKind(Kind_DebugPrologue);
+    }
+    void setForDebugEpilogue() {
+        MOZ_ASSERT(kind() == Kind_CallVM);
+        setKind(Kind_DebugEpilogue);
     }
 
     bool hasStub() const {
         return firstStub_ != nullptr;
     }
     ICStub *firstStub() const {
         JS_ASSERT(hasStub());
         return firstStub_;
@@ -1038,33 +1087,38 @@ class ICStubCompiler
     // checked is already in R0.
     bool callTypeUpdateIC(MacroAssembler &masm, uint32_t objectOffset);
 
     // A stub frame is used when a stub wants to call into the VM without
     // performing a tail call. This is required for the return address
     // to pc mapping to work.
     void enterStubFrame(MacroAssembler &masm, Register scratch);
     void leaveStubFrame(MacroAssembler &masm, bool calledIntoIon = false);
+    void leaveStubFrameHead(MacroAssembler &masm, bool calledIntoIon = false);
+    void leaveStubFrameCommonTail(MacroAssembler &masm);
 
     // Some stubs need to emit SPS profiler updates.  This emits the guarding
     // jitcode for those stubs.  If profiling is not enabled, jumps to the
     // given label.
     void guardProfilingEnabled(MacroAssembler &masm, Register scratch, Label *skip);
 
     // Higher-level helper to emit an update to the profiler pseudo-stack.
     void emitProfilingUpdate(MacroAssembler &masm, Register pcIdx, Register scratch,
                              uint32_t stubPcOffset);
     void emitProfilingUpdate(MacroAssembler &masm, GeneralRegisterSet regs, uint32_t stubPcOffset);
 
     inline GeneralRegisterSet availableGeneralRegs(size_t numInputs) const {
         GeneralRegisterSet regs(GeneralRegisterSet::All());
         JS_ASSERT(!regs.has(BaselineStackReg));
-#ifdef JS_CODEGEN_ARM
+#if defined(JS_CODEGEN_ARM)
         JS_ASSERT(!regs.has(BaselineTailCallReg));
         regs.take(BaselineSecondScratchReg);
+#elif defined(JS_CODEGEN_MIPS)
+        JS_ASSERT(!regs.has(BaselineTailCallReg));
+        JS_ASSERT(!regs.has(BaselineSecondScratchReg));
 #endif
         regs.take(BaselineFrameReg);
         regs.take(BaselineStubReg);
 #ifdef JS_CODEGEN_X64
         regs.take(ExtractTemp0);
         regs.take(ExtractTemp1);
 #endif
 
@@ -4036,17 +4090,18 @@ class ICGetProp_Fallback : public ICMoni
         extra_ |= (1u << ACCESSED_GETTER_BIT);
     }
     bool hasAccessedGetter() const {
         return extra_ & (1u << ACCESSED_GETTER_BIT);
     }
 
     class Compiler : public ICStubCompiler {
       protected:
-        uint32_t returnOffset_;
+        uint32_t returnFromIonOffset_;
+        uint32_t returnFromStubOffset_;
         bool generateStubCode(MacroAssembler &masm);
         bool postGenerateStubCode(MacroAssembler &masm, Handle<JitCode *> code);
 
       public:
         Compiler(JSContext *cx)
           : ICStubCompiler(cx, ICStub::GetProp_Fallback)
         { }
 
@@ -4929,17 +4984,18 @@ class ICSetProp_Fallback : public ICFall
         extra_ |= (1u << UNOPTIMIZABLE_ACCESS_BIT);
     }
     bool hadUnoptimizableAccess() const {
         return extra_ & (1u << UNOPTIMIZABLE_ACCESS_BIT);
     }
 
     class Compiler : public ICStubCompiler {
       protected:
-        uint32_t returnOffset_;
+        uint32_t returnFromIonOffset_;
+        uint32_t returnFromStubOffset_;
         bool generateStubCode(MacroAssembler &masm);
         bool postGenerateStubCode(MacroAssembler &masm, Handle<JitCode *> code);
 
       public:
         Compiler(JSContext *cx)
           : ICStubCompiler(cx, ICStub::SetProp_Fallback)
         { }
 
@@ -5353,17 +5409,18 @@ class ICCall_Fallback : public ICMonitor
         // Return hasStub(Call_AnyNative) after Call_AnyNative stub is added.
         return false;
     }
 
     // Compiler for this stub kind.
     class Compiler : public ICCallStubCompiler {
       protected:
         bool isConstructing_;
-        uint32_t returnOffset_;
+        uint32_t returnFromIonOffset_;
+        uint32_t returnFromStubOffset_;
         bool generateStubCode(MacroAssembler &masm);
         bool postGenerateStubCode(MacroAssembler &masm, Handle<JitCode *> code);
 
       public:
         Compiler(JSContext *cx, bool isConstructing)
           : ICCallStubCompiler(cx, ICStub::Call_Fallback),
             isConstructing_(isConstructing)
         { }
@@ -5747,17 +5804,17 @@ class ICTableSwitch : public ICStub
   public:
     static inline ICTableSwitch *New(ICStubSpace *space, JitCode *code, void **table,
                                      int32_t min, int32_t length, void *defaultTarget) {
         if (!code)
             return nullptr;
         return space->allocate<ICTableSwitch>(code, table, min, length, defaultTarget);
     }
 
-    void fixupJumpTable(HandleScript script, BaselineScript *baseline);
+    void fixupJumpTable(JSScript *script, BaselineScript *baseline);
 
     class Compiler : public ICStubCompiler {
         bool generateStubCode(MacroAssembler &masm);
 
         jsbytecode *pc_;
 
       public:
         Compiler(JSContext *cx, jsbytecode *pc)
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -35,25 +35,28 @@ PCMappingSlotInfo::ToSlotLocation(const 
             return SlotInR0;
         JS_ASSERT(stackVal->reg() == R1);
         return SlotInR1;
     }
     JS_ASSERT(stackVal->kind() != StackValue::Stack);
     return SlotIgnore;
 }
 
-BaselineScript::BaselineScript(uint32_t prologueOffset, uint32_t spsPushToggleOffset)
+BaselineScript::BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset,
+                               uint32_t spsPushToggleOffset, uint32_t postDebugPrologueOffset)
   : method_(nullptr),
     templateScope_(nullptr),
     fallbackStubSpace_(),
     prologueOffset_(prologueOffset),
+    epilogueOffset_(epilogueOffset),
 #ifdef DEBUG
     spsOn_(false),
 #endif
     spsPushToggleOffset_(spsPushToggleOffset),
+    postDebugPrologueOffset_(postDebugPrologueOffset),
     flags_(0)
 { }
 
 static const size_t BASELINE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 4096;
 static const unsigned BASELINE_MAX_ARGS_LENGTH = 20000;
 
 static bool
 CheckFrame(InterpreterFrame *fp)
@@ -209,17 +212,17 @@ jit::EnterBaselineAtBranch(JSContext *cx
     if (status != IonExec_Ok)
         return status;
 
     fp->setReturnValue(data.result);
     return IonExec_Ok;
 }
 
 MethodStatus
-jit::BaselineCompile(JSContext *cx, HandleScript script)
+jit::BaselineCompile(JSContext *cx, JSScript *script)
 {
     JS_ASSERT(!script->hasBaselineScript());
     JS_ASSERT(script->canBaselineCompile());
     JS_ASSERT(IsBaselineEnabled(cx));
     LifoAlloc alloc(BASELINE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
 
     script->ensureNonLazyCanonicalFunction(cx);
 
@@ -354,19 +357,19 @@ jit::CanEnterBaselineMethod(JSContext *c
         return Method_CantCompile;
     }
 
     RootedScript script(cx, state.script());
     return CanEnterBaselineJIT(cx, script, /* osr = */false);
 };
 
 BaselineScript *
-BaselineScript::New(JSContext *cx, uint32_t prologueOffset,
-                    uint32_t spsPushToggleOffset, size_t icEntries,
-                    size_t pcMappingIndexEntries, size_t pcMappingSize,
+BaselineScript::New(JSContext *cx, uint32_t prologueOffset, uint32_t epilogueOffset,
+                    uint32_t spsPushToggleOffset, uint32_t postDebugPrologueOffset,
+                    size_t icEntries, size_t pcMappingIndexEntries, size_t pcMappingSize,
                     size_t bytecodeTypeMapEntries)
 {
     static const unsigned DataAlignment = sizeof(uintptr_t);
 
     size_t paddedBaselineScriptSize = AlignBytes(sizeof(BaselineScript), DataAlignment);
 
     size_t icEntriesSize = icEntries * sizeof(ICEntry);
     size_t pcMappingIndexEntriesSize = pcMappingIndexEntries * sizeof(PCMappingIndexEntry);
@@ -383,17 +386,18 @@ BaselineScript::New(JSContext *cx, uint3
         paddedPCMappingSize +
         paddedBytecodeTypesMapSize;
 
     uint8_t *buffer = (uint8_t *)cx->malloc_(allocBytes);
     if (!buffer)
         return nullptr;
 
     BaselineScript *script = reinterpret_cast<BaselineScript *>(buffer);
-    new (script) BaselineScript(prologueOffset, spsPushToggleOffset);
+    new (script) BaselineScript(prologueOffset, epilogueOffset,
+                                spsPushToggleOffset, postDebugPrologueOffset);
 
     size_t offsetCursor = paddedBaselineScriptSize;
 
     script->icEntriesOffset_ = offsetCursor;
     script->icEntries_ = icEntries;
     offsetCursor += paddedICEntriesSize;
 
     script->pcMappingIndexOffset_ = offsetCursor;
@@ -591,17 +595,17 @@ BaselineScript::icEntryFromReturnAddress
 {
     JS_ASSERT(returnAddr > method_->raw());
     JS_ASSERT(returnAddr < method_->raw() + method_->instructionsSize());
     CodeOffsetLabel offset(returnAddr - method_->raw());
     return icEntryFromReturnOffset(offset);
 }
 
 void
-BaselineScript::copyICEntries(HandleScript script, const ICEntry *entries, MacroAssembler &masm)
+BaselineScript::copyICEntries(JSScript *script, const ICEntry *entries, MacroAssembler &masm)
 {
     // Fix up the return offset in the IC entries and copy them in.
     // Also write out the IC entry ptrs in any fallback stubs that were added.
     for (uint32_t i = 0; i < numICEntries(); i++) {
         ICEntry &realEntry = icEntry(i);
         realEntry = entries[i];
         realEntry.fixupReturnOffset(masm);
 
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -114,22 +114,34 @@ struct BaselineScript
     HeapPtrObject templateScope_;
 
     // Allocated space for fallback stubs.
     FallbackICStubSpace fallbackStubSpace_;
 
     // Native code offset right before the scope chain is initialized.
     uint32_t prologueOffset_;
 
+    // Native code offset right before the frame is popped and the method
+    // returned from.
+    uint32_t epilogueOffset_;
+
     // The offsets for the toggledJump instructions for SPS update ICs.
 #ifdef DEBUG
     mozilla::DebugOnly<bool> spsOn_;
 #endif
     uint32_t spsPushToggleOffset_;
 
+    // Native code offsets right after the debug prologue VM call returns, or
+    // would have returned. This offset is recorded even when debug mode is
+    // off to aid on-stack debug mode recompilation.
+    //
+    // We don't need one for the debug epilogue because that always happens
+    // right before the epilogue, so we just use the epilogue offset.
+    uint32_t postDebugPrologueOffset_;
+
   public:
     enum Flag {
         // Flag set by JSScript::argumentsOptimizationFailed. Similar to
         // JSScript::needsArgsObj_, but can be read from JIT code.
         NEEDS_ARGS_OBJ = 1 << 0,
 
         // Flag set when discarding JIT code, to indicate this script is
         // on the stack and should not be discarded.
@@ -160,19 +172,21 @@ struct BaselineScript
     uint32_t pcMappingSize_;
 
     // List mapping indexes of bytecode type sets to the offset of the opcode
     // they correspond to, for use by TypeScript::BytecodeTypes.
     uint32_t bytecodeTypeMapOffset_;
 
   public:
     // Do not call directly, use BaselineScript::New. This is public for cx->new_.
-    BaselineScript(uint32_t prologueOffset, uint32_t spsPushToggleOffset);
+    BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset,
+                   uint32_t spsPushToggleOffset, uint32_t postDebugPrologueOffset);
 
     static BaselineScript *New(JSContext *cx, uint32_t prologueOffset,
+                               uint32_t epilogueOffset, uint32_t postDebugPrologueOffset,
                                uint32_t spsPushToggleOffset, size_t icEntries,
                                size_t pcMappingIndexEntries, size_t pcMappingSize,
                                size_t bytecodeTypeMapEntries);
     static void Trace(JSTracer *trc, BaselineScript *script);
     static void Destroy(FreeOp *fop, BaselineScript *script);
 
     void purgeOptimizedStubs(Zone *zone);
 
@@ -219,16 +233,30 @@ struct BaselineScript
 
     uint32_t prologueOffset() const {
         return prologueOffset_;
     }
     uint8_t *prologueEntryAddr() const {
         return method_->raw() + prologueOffset_;
     }
 
+    uint32_t epilogueOffset() const {
+        return epilogueOffset_;
+    }
+    uint8_t *epilogueEntryAddr() const {
+        return method_->raw() + epilogueOffset_;
+    }
+
+    uint32_t postDebugPrologueOffset() const {
+        return postDebugPrologueOffset_;
+    }
+    uint8_t *postDebugPrologueAddr() const {
+        return method_->raw() + postDebugPrologueOffset_;
+    }
+
     ICEntry *icEntryList() {
         return (ICEntry *)(reinterpret_cast<uint8_t *>(this) + icEntriesOffset_);
     }
     PCMappingIndexEntry *pcMappingIndexEntryList() {
         return (PCMappingIndexEntry *)(reinterpret_cast<uint8_t *>(this) + pcMappingIndexOffset_);
     }
     uint8_t *pcMappingData() {
         return reinterpret_cast<uint8_t *>(this) + pcMappingOffset_;
@@ -252,30 +280,35 @@ struct BaselineScript
         JS_ASSERT(!templateScope_);
         templateScope_ = templateScope;
     }
 
     void toggleBarriers(bool enabled) {
         method()->togglePreBarriers(enabled);
     }
 
+    bool containsCodeAddress(uint8_t *addr) const {
+        return method()->raw() <= addr && addr <= method()->raw() + method()->instructionsSize();
+    }
+
     ICEntry &icEntry(size_t index);
     ICEntry *maybeICEntryFromReturnOffset(CodeOffsetLabel returnOffset);
     ICEntry &icEntryFromReturnOffset(CodeOffsetLabel returnOffset);
     ICEntry &icEntryFromPCOffset(uint32_t pcOffset);
+    ICEntry &icEntryForDebugModeRecompileFromPCOffset(uint32_t pcOffset);
     ICEntry &icEntryFromPCOffset(uint32_t pcOffset, ICEntry *prevLookedUpEntry);
     ICEntry *maybeICEntryFromReturnAddress(uint8_t *returnAddr);
     ICEntry &icEntryFromReturnAddress(uint8_t *returnAddr);
     uint8_t *returnAddressForIC(const ICEntry &ent);
 
     size_t numICEntries() const {
         return icEntries_;
     }
 
-    void copyICEntries(HandleScript script, const ICEntry *entries, MacroAssembler &masm);
+    void copyICEntries(JSScript *script, const ICEntry *entries, MacroAssembler &masm);
     void adoptFallbackStubs(FallbackICStubSpace *stubSpace);
 
     PCMappingIndexEntry &pcMappingIndexEntry(size_t index);
     CompactBufferReader pcMappingReader(size_t indexEntry);
 
     size_t numPCMappingIndexEntries() const {
         return pcMappingIndexEntries_;
     }
@@ -382,16 +415,16 @@ BailoutIonToBaseline(JSContext *cx, JitA
                      const ExceptionBailoutInfo *exceptionInfo = nullptr);
 
 // Mark baseline scripts on the stack as active, so that they are not discarded
 // during GC.
 void
 MarkActiveBaselineScripts(Zone *zone);
 
 MethodStatus
-BaselineCompile(JSContext *cx, HandleScript script);
+BaselineCompile(JSContext *cx, JSScript *script);
 
 } // namespace jit
 } // namespace js
 
 #endif // JS_ION
 
 #endif /* jit_BaselineJIT_h */
--- a/js/src/jit/BaselineRegisters.h
+++ b/js/src/jit/BaselineRegisters.h
@@ -8,18 +8,22 @@
 #define jit_BaselineRegisters_h
 
 #ifdef JS_ION
 
 #if defined(JS_CODEGEN_X86)
 # include "jit/x86/BaselineRegisters-x86.h"
 #elif defined(JS_CODEGEN_X64)
 # include "jit/x64/BaselineRegisters-x64.h"
+#elif defined(JS_CODEGEN_ARM)
+# include "jit/arm/BaselineRegisters-arm.h"
+#elif defined(JS_CODEGEN_MIPS)
+# include "jit/mips/BaselineRegisters-mips.h"
 #else
-# include "jit/arm/BaselineRegisters-arm.h"
+# error "Unknown architecture!"
 #endif
 
 namespace js {
 namespace jit {
 
 } // namespace jit
 } // namespace js
 
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -2535,16 +2535,29 @@ CodeGenerator::visitApplyArgsGeneric(LAp
 
     // Pop arguments and continue.
     masm.bind(&end);
     emitPopArguments(apply, copyreg);
 
     return true;
 }
 
+typedef bool (*ArraySpliceDenseFn)(JSContext *, HandleObject, uint32_t, uint32_t);
+static const VMFunction ArraySpliceDenseInfo = FunctionInfo<ArraySpliceDenseFn>(ArraySpliceDense);
+
+bool
+CodeGenerator::visitArraySplice(LArraySplice *lir)
+{
+    pushArg(ToRegister(lir->getDeleteCount()));
+    pushArg(ToRegister(lir->getStart()));
+    pushArg(ToRegister(lir->getObject()));
+    return callVM(ArraySpliceDenseInfo, lir);
+}
+
+
 bool
 CodeGenerator::visitBail(LBail *lir)
 {
     return bailout(lir->snapshot());
 }
 
 bool
 CodeGenerator::visitGetDynamicName(LGetDynamicName *lir)
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -13,18 +13,20 @@
 #endif
 
 #if defined(JS_CODEGEN_X86)
 # include "jit/x86/CodeGenerator-x86.h"
 #elif defined(JS_CODEGEN_X64)
 # include "jit/x64/CodeGenerator-x64.h"
 #elif defined(JS_CODEGEN_ARM)
 # include "jit/arm/CodeGenerator-arm.h"
+#elif defined(JS_CODEGEN_MIPS)
+# include "jit/mips/CodeGenerator-mips.h"
 #else
-#error "CPU Not Supported"
+#error "Unknown architecture!"
 #endif
 
 namespace js {
 namespace jit {
 
 class OutOfLineTestObject;
 class OutOfLineNewArray;
 class OutOfLineNewObject;
@@ -183,16 +185,17 @@ class CodeGenerator : public CodeGenerat
     bool visitStoreFixedSlotT(LStoreFixedSlotT *ins);
     bool emitGetPropertyPolymorphic(LInstruction *lir, Register obj,
                                     Register scratch, const TypedOrValueRegister &output);
     bool visitGetPropertyPolymorphicV(LGetPropertyPolymorphicV *ins);
     bool visitGetPropertyPolymorphicT(LGetPropertyPolymorphicT *ins);
     bool emitSetPropertyPolymorphic(LInstruction *lir, Register obj,
                                     Register scratch, const ConstantOrRegister &value);
     bool visitSetPropertyPolymorphicV(LSetPropertyPolymorphicV *ins);
+    bool visitArraySplice(LArraySplice *splice);
     bool visitSetPropertyPolymorphicT(LSetPropertyPolymorphicT *ins);
     bool visitAbsI(LAbsI *lir);
     bool visitAtan2D(LAtan2D *lir);
     bool visitHypot(LHypot *lir);
     bool visitPowI(LPowI *lir);
     bool visitPowD(LPowD *lir);
     bool visitRandom(LRandom *lir);
     bool visitMathFunctionD(LMathFunctionD *ins);
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -12,16 +12,17 @@
 #include "jscompartment.h"
 #include "jsprf.h"
 #include "jsworkers.h"
 
 #include "gc/Marking.h"
 #include "jit/AliasAnalysis.h"
 #include "jit/AsmJSModule.h"
 #include "jit/BacktrackingAllocator.h"
+#include "jit/BaselineDebugModeOSR.h"
 #include "jit/BaselineFrame.h"
 #include "jit/BaselineInspector.h"
 #include "jit/BaselineJIT.h"
 #include "jit/CodeGenerator.h"
 #include "jit/EdgeCaseAnalysis.h"
 #include "jit/EffectiveAddressAnalysis.h"
 #include "jit/IonAnalysis.h"
 #include "jit/IonBuilder.h"
@@ -153,16 +154,17 @@ JitRuntime::JitRuntime()
     enterJIT_(nullptr),
     bailoutHandler_(nullptr),
     argumentsRectifier_(nullptr),
     argumentsRectifierReturnAddr_(nullptr),
     parallelArgumentsRectifier_(nullptr),
     invalidator_(nullptr),
     debugTrapHandler_(nullptr),
     forkJoinGetSliceStub_(nullptr),
+    baselineDebugModeOSRHandler_(nullptr),
     functionWrappers_(nullptr),
     osrTempData_(nullptr),
     flusher_(nullptr),
     ionCodeProtected_(false)
 {
 }
 
 JitRuntime::~JitRuntime()
@@ -451,19 +453,22 @@ jit::RequestInterruptForIonCode(JSRuntim
 
       default:
         MOZ_ASSUME_UNREACHABLE("Bad interrupt mode");
     }
 }
 
 JitCompartment::JitCompartment()
   : stubCodes_(nullptr),
-    baselineCallReturnAddr_(nullptr),
-    baselineGetPropReturnAddr_(nullptr),
-    baselineSetPropReturnAddr_(nullptr),
+    baselineCallReturnFromIonAddr_(nullptr),
+    baselineGetPropReturnFromIonAddr_(nullptr),
+    baselineSetPropReturnFromIonAddr_(nullptr),
+    baselineCallReturnFromStubAddr_(nullptr),
+    baselineGetPropReturnFromStubAddr_(nullptr),
+    baselineSetPropReturnFromStubAddr_(nullptr),
     stringConcatStub_(nullptr),
     parallelStringConcatStub_(nullptr),
     activeParallelEntryScripts_(nullptr)
 {
 }
 
 JitCompartment::~JitCompartment()
 {
@@ -616,23 +621,29 @@ JitCompartment::mark(JSTracer *trc, JSCo
 }
 
 void
 JitCompartment::sweep(FreeOp *fop)
 {
     stubCodes_->sweep(fop);
 
     // If the sweep removed the ICCall_Fallback stub, nullptr the baselineCallReturnAddr_ field.
-    if (!stubCodes_->lookup(static_cast<uint32_t>(ICStub::Call_Fallback)))
-        baselineCallReturnAddr_ = nullptr;
+    if (!stubCodes_->lookup(static_cast<uint32_t>(ICStub::Call_Fallback))) {
+        baselineCallReturnFromIonAddr_ = nullptr;
+        baselineCallReturnFromStubAddr_ = nullptr;
+    }
     // Similarly for the ICGetProp_Fallback stub.
-    if (!stubCodes_->lookup(static_cast<uint32_t>(ICStub::GetProp_Fallback)))
-        baselineGetPropReturnAddr_ = nullptr;
-    if (!stubCodes_->lookup(static_cast<uint32_t>(ICStub::SetProp_Fallback)))
-        baselineSetPropReturnAddr_ = nullptr;
+    if (!stubCodes_->lookup(static_cast<uint32_t>(ICStub::GetProp_Fallback))) {
+        baselineGetPropReturnFromIonAddr_ = nullptr;
+        baselineGetPropReturnFromStubAddr_ = nullptr;
+    }
+    if (!stubCodes_->lookup(static_cast<uint32_t>(ICStub::SetProp_Fallback))) {
+        baselineSetPropReturnFromIonAddr_ = nullptr;
+        baselineSetPropReturnFromStubAddr_ = nullptr;
+    }
 
     if (stringConcatStub_ && !IsJitCodeMarked(stringConcatStub_.unsafeGet()))
         stringConcatStub_ = nullptr;
 
     if (parallelStringConcatStub_ && !IsJitCodeMarked(parallelStringConcatStub_.unsafeGet()))
         parallelStringConcatStub_ = nullptr;
 
     if (activeParallelEntryScripts_) {
@@ -2003,17 +2014,17 @@ CheckScriptSize(JSContext *cx, JSScript*
             return Method_CantCompile;
         }
     }
 
     return Method_Compiled;
 }
 
 bool
-CanIonCompileScript(JSContext *cx, HandleScript script, bool osr)
+CanIonCompileScript(JSContext *cx, JSScript *script, bool osr)
 {
     if (!script->canIonCompile() || !CheckScript(cx, script, osr))
         return false;
 
     return CheckScriptSize(cx, script) == Method_Compiled;
 }
 
 static OptimizationLevel
@@ -3004,16 +3015,54 @@ jit::TraceIonScripts(JSTracer* trc, JSSc
 
     if (script->hasParallelIonScript())
         jit::IonScript::Trace(trc, script->parallelIonScript());
 
     if (script->hasBaselineScript())
         jit::BaselineScript::Trace(trc, script->baselineScript());
 }
 
+bool
+jit::RematerializeAllFrames(JSContext *cx, JSCompartment *comp)
+{
+    for (JitActivationIterator iter(comp->runtimeFromMainThread()); !iter.done(); ++iter) {
+        if (iter.activation()->compartment() == comp) {
+            for (JitFrameIterator frameIter(iter); !frameIter.done(); ++frameIter) {
+                if (!frameIter.isIonJS())
+                    continue;
+                if (!iter.activation()->asJit()->getRematerializedFrame(cx, frameIter))
+                    return false;
+            }
+        }
+    }
+    return true;
+}
+
+bool
+jit::UpdateForDebugMode(JSContext *maybecx, JSCompartment *comp,
+                     AutoDebugModeInvalidation &invalidate)
+{
+    MOZ_ASSERT(invalidate.isFor(comp));
+
+    // Schedule invalidation of all optimized JIT code since debug mode
+    // invalidates assumptions.
+    invalidate.scheduleInvalidation(comp->debugMode());
+
+    // Recompile on-stack baseline scripts if we have a cx.
+    if (maybecx) {
+        IonContext ictx(maybecx, nullptr);
+        if (!RecompileOnStackBaselineScriptsForDebugMode(maybecx, comp)) {
+            js_ReportOutOfMemory(maybecx);
+            return false;
+        }
+    }
+
+    return true;
+}
+
 AutoDebugModeInvalidation::~AutoDebugModeInvalidation()
 {
     MOZ_ASSERT(!!comp_ != !!zone_);
 
     if (needInvalidation_ == NoNeed)
         return;
 
     Zone *zone = zone_ ? zone_ : comp_->zone();
@@ -3022,31 +3071,33 @@ AutoDebugModeInvalidation::~AutoDebugMod
 
     if (comp_) {
         StopAllOffThreadCompilations(comp_);
     } else {
         for (CompartmentsInZoneIter comp(zone_); !comp.done(); comp.next())
             StopAllOffThreadCompilations(comp);
     }
 
+    // Don't discard active baseline scripts. They are recompiled for debug
+    // mode.
     jit::MarkActiveBaselineScripts(zone);
 
     for (JitActivationIterator iter(rt); !iter.done(); ++iter) {
         JSCompartment *comp = iter.activation()->compartment();
-        if ((comp_ && comp_ == comp) || (zone_ && zone_ == comp->zone())) {
+        if (comp_ == comp || zone_ == comp->zone()) {
             IonContext ictx(CompileRuntime::get(rt));
             AutoFlushCache afc("AutoDebugModeInvalidation", rt->jitRuntime());
             IonSpew(IonSpew_Invalidate, "Invalidating frames for debug mode toggle");
             InvalidateActivation(fop, iter.jitTop(), true);
         }
     }
 
     for (gc::CellIter i(zone, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
         JSScript *script = i.get<JSScript>();
-        if ((comp_ && script->compartment() == comp_) || zone_) {
+        if (script->compartment() == comp_ || zone_) {
             FinishInvalidation<SequentialExecution>(fop, script);
             FinishInvalidation<ParallelExecution>(fop, script);
             FinishDiscardBaselineScript(fop, script);
             script->resetUseCount();
         } else if (script->hasBaselineScript()) {
             script->baselineScript()->resetActive();
         }
     }
--- a/js/src/jit/Ion.h
+++ b/js/src/jit/Ion.h
@@ -77,17 +77,17 @@ class IonContext
 bool InitializeIon();
 
 // Get and set the current Ion context.
 IonContext *GetIonContext();
 IonContext *MaybeGetIonContext();
 
 void SetIonContext(IonContext *ctx);
 
-bool CanIonCompileScript(JSContext *cx, HandleScript script, bool osr);
+bool CanIonCompileScript(JSContext *cx, JSScript *script, bool osr);
 
 MethodStatus CanEnterAtBranch(JSContext *cx, JSScript *script,
                               BaselineFrame *frame, jsbytecode *pc, bool isConstructing);
 MethodStatus CanEnter(JSContext *cx, RunState &state);
 MethodStatus CompileFunctionForBaseline(JSContext *cx, HandleScript script, BaselineFrame *frame,
                                         bool isConstructing);
 MethodStatus CanEnterUsingFastInvoke(JSContext *cx, HandleScript script, uint32_t numActualArgs);
 
@@ -183,14 +183,18 @@ void ForbidCompilation(JSContext *cx, JS
 
 void PurgeCaches(JSScript *script, JS::Zone *zone);
 size_t SizeOfIonData(JSScript *script, mozilla::MallocSizeOf mallocSizeOf);
 void DestroyIonScripts(FreeOp *fop, JSScript *script);
 void TraceIonScripts(JSTracer* trc, JSScript *script);
 
 void RequestInterruptForIonCode(JSRuntime *rt, JSRuntime::InterruptMode mode);
 
+bool RematerializeAllFrames(JSContext *cx, JSCompartment *comp);
+bool UpdateForDebugMode(JSContext *maybecx, JSCompartment *comp,
+                        AutoDebugModeInvalidation &invalidate);
+
 } // namespace jit
 } // namespace js
 
 #endif // JS_ION
 
 #endif /* jit_Ion_h */
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -132,25 +132,25 @@ jit::EliminateDeadResumePointOperands(MI
                     !mrp->instruction() ||
                     mrp->instruction() == *ins ||
                     mrp->instruction()->id() <= maxDefinition)
                 {
                     uses++;
                     continue;
                 }
 
-                // Store an undefined value in place of all dead resume point
-                // operands. Making any such substitution can in general alter
-                // the interpreter's behavior, even though the code is dead, as
-                // the interpreter will still execute opcodes whose effects
-                // cannot be observed. If the undefined value were to flow to,
-                // say, a dead property access the interpreter could throw an
-                // exception; we avoid this problem by removing dead operands
-                // before removing dead code.
-                MConstant *constant = MConstant::New(graph.alloc(), UndefinedValue());
+                // Store an optimized out magic value in place of all dead
+                // resume point operands. Making any such substitution can in
+                // general alter the interpreter's behavior, even though the
+                // code is dead, as the interpreter will still execute opcodes
+                // whose effects cannot be observed. If the undefined value
+                // were to flow to, say, a dead property access the
+                // interpreter could throw an exception; we avoid this problem
+                // by removing dead operands before removing dead code.
+                MConstant *constant = MConstant::New(graph.alloc(), MagicValue(JS_OPTIMIZED_OUT));
                 block->insertBefore(*(block->begin()), constant);
                 uses = mrp->replaceOperand(uses, constant);
             }
         }
     }
 
     return true;
 }
@@ -438,17 +438,19 @@ class TypeAnalyzer
 
 } /* anonymous namespace */
 
 // Try to specialize this phi based on its non-cyclic inputs.
 static MIRType
 GuessPhiType(MPhi *phi, bool *hasInputsWithEmptyTypes)
 {
 #ifdef DEBUG
-    // Check that different magic constants aren't flowing together.
+    // Check that different magic constants aren't flowing together. Ignore
+    // JS_OPTIMIZED_OUT, since an operand could be legitimately optimized
+    // away.
     MIRType magicType = MIRType_None;
     for (size_t i = 0; i < phi->numOperands(); i++) {
         MDefinition *in = phi->getOperand(i);
         if (in->type() == MIRType_MagicOptimizedArguments ||
             in->type() == MIRType_MagicHole ||
             in->type() == MIRType_MagicIsConstructing)
         {
             if (magicType == MIRType_None)
@@ -728,16 +730,19 @@ TypeAnalyzer::replaceRedundantPhi(MPhi *
         v = UndefinedValue();
         break;
       case MIRType_Null:
         v = NullValue();
         break;
       case MIRType_MagicOptimizedArguments:
         v = MagicValue(JS_OPTIMIZED_ARGUMENTS);
         break;
+      case MIRType_MagicOptimizedOut:
+        v = MagicValue(JS_OPTIMIZED_OUT);
+        break;
       default:
         MOZ_ASSUME_UNREACHABLE("unexpected type");
     }
     MConstant *c = MConstant::New(alloc(), v);
     // The instruction pass will insert the box
     block->insertBefore(*(block->begin()), c);
     phi->replaceAllUsesWith(c);
 }
@@ -750,17 +755,18 @@ TypeAnalyzer::insertConversions()
     // inputs of uses) does not conflict with input adjustment.
     for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) {
         if (mir->shouldCancel("Insert Conversions"))
             return false;
 
         for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd();) {
             if (phi->type() == MIRType_Undefined ||
                 phi->type() == MIRType_Null ||
-                phi->type() == MIRType_MagicOptimizedArguments)
+                phi->type() == MIRType_MagicOptimizedArguments ||
+                phi->type() == MIRType_MagicOptimizedOut)
             {
                 replaceRedundantPhi(*phi);
                 phi = block->discardPhiAt(phi);
             } else {
                 adjustPhiInputs(*phi);
                 phi++;
             }
         }
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -61,45 +61,50 @@ jit::NewBaselineFrameInspector(TempAlloc
     BaselineFrameInspector *inspector = temp->lifoAlloc()->new_<BaselineFrameInspector>(temp);
     if (!inspector)
         return nullptr;
 
     // Note: copying the actual values into a temporary structure for use
     // during compilation could capture nursery pointers, so the values' types
     // are recorded instead.
 
-    inspector->thisType = types::GetValueType(frame->thisValue());
+    inspector->thisType = types::GetMaybeOptimizedOutValueType(frame->thisValue());
 
     if (frame->scopeChain()->hasSingletonType())
         inspector->singletonScopeChain = frame->scopeChain();
 
     JSScript *script = frame->script();
 
     if (script->functionNonDelazifying()) {
         if (!inspector->argTypes.reserve(frame->numFormalArgs()))
             return nullptr;
         for (size_t i = 0; i < frame->numFormalArgs(); i++) {
-            if (script->formalIsAliased(i))
+            if (script->formalIsAliased(i)) {
                 inspector->argTypes.infallibleAppend(types::Type::UndefinedType());
-            else if (!script->argsObjAliasesFormals())
-                inspector->argTypes.infallibleAppend(types::GetValueType(frame->unaliasedFormal(i)));
-            else if (frame->hasArgsObj())
-                inspector->argTypes.infallibleAppend(types::GetValueType(frame->argsObj().arg(i)));
-            else
+            } else if (!script->argsObjAliasesFormals()) {
+                types::Type type = types::GetMaybeOptimizedOutValueType(frame->unaliasedFormal(i));
+                inspector->argTypes.infallibleAppend(type);
+            } else if (frame->hasArgsObj()) {
+                types::Type type = types::GetMaybeOptimizedOutValueType(frame->argsObj().arg(i));
+                inspector->argTypes.infallibleAppend(type);
+            } else {
                 inspector->argTypes.infallibleAppend(types::Type::UndefinedType());
+            }
         }
     }
 
     if (!inspector->varTypes.reserve(frame->script()->nfixed()))
         return nullptr;
     for (size_t i = 0; i < frame->script()->nfixed(); i++) {
-        if (info->isSlotAliasedAtOsr(i + info->firstLocalSlot()))
+        if (info->isSlotAliasedAtOsr(i + info->firstLocalSlot())) {
             inspector->varTypes.infallibleAppend(types::Type::UndefinedType());
-        else
-            inspector->varTypes.infallibleAppend(types::GetValueType(frame->unaliasedLocal(i)));
+        } else {
+            types::Type type = types::GetMaybeOptimizedOutValueType(frame->unaliasedLocal(i));
+            inspector->varTypes.infallibleAppend(type);
+        }
     }
 
     return inspector;
 }
 
 IonBuilder::IonBuilder(JSContext *analysisContext, CompileCompartment *comp,
                        const JitCompileOptions &options, TempAllocator *temp,
                        MIRGraph *graph, types::CompilerConstraintList *constraints,
@@ -3676,44 +3681,49 @@ IonBuilder::processReturn(JSOp op)
     return processControlEnd();
 }
 
 IonBuilder::ControlStatus
 IonBuilder::processThrow()
 {
     MDefinition *def = current->pop();
 
-    if (graph().hasTryBlock()) {
-        // MThrow is not marked as effectful. This means when it throws and we
-        // are inside a try block, we could use an earlier resume point and this
-        // resume point may not be up-to-date, for example:
-        //
-        // (function() {
-        //     try {
-        //         var x = 1;
-        //         foo(); // resume point
-        //         x = 2;
-        //         throw foo;
-        //     } catch(e) {
-        //         print(x);
-        //     }
-        // ])();
-        //
-        // If we use the resume point after the call, this will print 1 instead
-        // of 2. To fix this, we create a resume point right before the MThrow.
-        //
-        // Note that this is not a problem for instructions other than MThrow
-        // because they are either marked as effectful (have their own resume
-        // point) or cannot throw a catchable exception.
-        MNop *ins = MNop::New(alloc());
-        current->add(ins);
-
-        if (!resumeAfter(ins))
-            return ControlStatus_Error;
-    }
+    // MThrow is not marked as effectful. This means when it throws and we
+    // are inside a try block, we could use an earlier resume point and this
+    // resume point may not be up-to-date, for example:
+    //
+    // (function() {
+    //     try {
+    //         var x = 1;
+    //         foo(); // resume point
+    //         x = 2;
+    //         throw foo;
+    //     } catch(e) {
+    //         print(x);
+    //     }
+    // ])();
+    //
+    // If we use the resume point after the call, this will print 1 instead
+    // of 2. To fix this, we create a resume point right before the MThrow.
+    //
+    // Note that this is not a problem for instructions other than MThrow
+    // because they are either marked as effectful (have their own resume
+    // point) or cannot throw a catchable exception.
+    //
+    // We always install this resume point (instead of only when the function
+    // has a try block) in order to handle the Debugger onExceptionUnwind
+    // hook. When we need to handle the hook, we bail out to baseline right
+    // after the throw and propagate the exception when debug mode is on. This
+    // is opposed to the normal behavior of resuming directly in the
+    // associated catch block.
+    MNop *nop = MNop::New(alloc());
+    current->add(nop);
+
+    if (!resumeAfter(nop))
+        return ControlStatus_Error;
 
     MThrow *ins = MThrow::New(alloc(), def);
     current->end(ins);
 
     // Make sure no one tries to use this block now.
     setCurrent(nullptr);
     return processControlEnd();
 }
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -644,16 +644,17 @@ class IonBuilder : public MIRGenerator
     types::TemporaryTypeSet *getInlineReturnTypeSet();
     MIRType getInlineReturnType();
 
     // Array natives.
     InliningStatus inlineArray(CallInfo &callInfo);
     InliningStatus inlineArrayPopShift(CallInfo &callInfo, MArrayPopShift::Mode mode);
     InliningStatus inlineArrayPush(CallInfo &callInfo);
     InliningStatus inlineArrayConcat(CallInfo &callInfo);
+    InliningStatus inlineArraySplice(CallInfo &callInfo);
 
     // Math natives.
     InliningStatus inlineMathAbs(CallInfo &callInfo);
     InliningStatus inlineMathFloor(CallInfo &callInfo);
     InliningStatus inlineMathCeil(CallInfo &callInfo);
     InliningStatus inlineMathRound(CallInfo &callInfo);
     InliningStatus inlineMathSqrt(CallInfo &callInfo);
     InliningStatus inlineMathAtan2(CallInfo &callInfo);
--- a/js/src/jit/IonCaches.h
+++ b/js/src/jit/IonCaches.h
@@ -2,18 +2,20 @@
  * 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_IonCaches_h
 #define jit_IonCaches_h
 
-#ifdef JS_CODEGEN_ARM
+#if defined(JS_CODEGEN_ARM)
 # include "jit/arm/Assembler-arm.h"
+#elif defined(JS_CODEGEN_MIPS)
+# include "jit/mips/Assembler-mips.h"
 #endif
 #include "jit/Registers.h"
 #include "jit/shared/Assembler-shared.h"
 
 namespace js {
 
 class LockedJSContext;
 class TypedArrayObject;
@@ -344,23 +346,26 @@ class RepatchIonCache : public IonCache
     class RepatchStubAppender;
 
     CodeLocationJump initialJump_;
     CodeLocationJump lastJump_;
 
     // Offset from the initial jump to the rejoin label.
 #ifdef JS_CODEGEN_ARM
     static const size_t REJOIN_LABEL_OFFSET = 4;
+#elif defined(JS_CODEGEN_MIPS)
+    // The size of jump created by MacroAssemblerMIPSCompat::jumpWithPatch.
+    static const size_t REJOIN_LABEL_OFFSET = 4 * sizeof(void *);
 #else
     static const size_t REJOIN_LABEL_OFFSET = 0;
 #endif
 
     CodeLocationLabel rejoinLabel() const {
         uint8_t *ptr = initialJump_.raw();
-#ifdef JS_CODEGEN_ARM
+#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
         uint32_t i = 0;
         while (i < REJOIN_LABEL_OFFSET)
             ptr = Assembler::nextInstruction(ptr, &i);
 #endif
         return CodeLocationLabel(ptr);
     }
 
   public:
--- a/js/src/jit/IonFrames.cpp
+++ b/js/src/jit/IonFrames.cpp
@@ -6,32 +6,35 @@
 
 #include "jit/IonFrames-inl.h"
 
 #include "jsfun.h"
 #include "jsobj.h"
 #include "jsscript.h"
 
 #include "gc/Marking.h"
+#include "jit/BaselineDebugModeOSR.h"
 #include "jit/BaselineFrame.h"
 #include "jit/BaselineIC.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/IonMacroAssembler.h"
 #include "jit/IonSpewer.h"
 #include "jit/JitCompartment.h"
 #include "jit/ParallelFunctions.h"
 #include "jit/PcScriptCache.h"
 #include "jit/Recover.h"
 #include "jit/Safepoints.h"
 #include "jit/Snapshots.h"
 #include "jit/VMFunctions.h"
+#include "vm/ArgumentsObject.h"
 #include "vm/ForkJoin.h"
 #include "vm/Interpreter.h"
 
+#include "jsscriptinlines.h"
 #include "jit/JitFrameIterator-inl.h"
 #include "vm/Probes-inl.h"
 
 namespace js {
 namespace jit {
 
 // Given a slot index, returns the offset, in bytes, of that slot from an
 // IonJSFrameLayout. Slot distances are uniform across architectures, however,
@@ -218,20 +221,28 @@ JitFrameIterator::script() const
 void
 JitFrameIterator::baselineScriptAndPc(JSScript **scriptRes, jsbytecode **pcRes) const
 {
     JS_ASSERT(isBaselineJS());
     JSScript *script = this->script();
     if (scriptRes)
         *scriptRes = script;
     uint8_t *retAddr = returnAddressToFp();
+
+    // If we are in the middle of a recompile handler, get the real return
+    // address as stashed in the RecompileInfo.
+    if (BaselineDebugModeOSRInfo *info = baselineFrame()->getDebugModeOSRInfo())
+        retAddr = info->resumeAddr;
+
     if (pcRes) {
-        // If the return address is into the prologue entry address, then assume start
-        // of script.
-        if (retAddr == script->baselineScript()->prologueEntryAddr()) {
+        // If the return address is into the prologue entry address or just
+        // after the debug prologue, then assume start of script.
+        if (retAddr == script->baselineScript()->prologueEntryAddr() ||
+            retAddr == script->baselineScript()->postDebugPrologueAddr())
+        {
             *pcRes = script->code();
             return;
         }
 
         // The return address _may_ be a return from a callVM or IC chain call done for
         // some op.
         ICEntry *icEntry = script->baselineScript()->maybeICEntryFromReturnAddress(retAddr);
         if (icEntry) {
@@ -368,16 +379,36 @@ CloseLiveIterator(JSContext *cx, const I
 
 static void
 HandleExceptionIon(JSContext *cx, const InlineFrameIterator &frame, ResumeFromException *rfe,
                    bool *overrecursed)
 {
     RootedScript script(cx, frame.script());
     jsbytecode *pc = frame.pc();
 
+    bool bailedOutForDebugMode = false;
+    if (cx->compartment()->debugMode()) {
+        // If we have an exception from within Ion and the debugger is active,
+        // we do the following:
+        //
+        //   1. Bailout to baseline to reconstruct a baseline frame.
+        //   2. Resume immediately into the exception tail afterwards, and
+        //   handle the exception again with the top frame now a baseline
+        //   frame.
+        //
+        // An empty exception info denotes that we're propagating an Ion
+        // exception due to debug mode, which BailoutIonToBaseline needs to
+        // know. This is because we might not be able to fully reconstruct up
+        // to the stack depth at the snapshot, as we could've thrown in the
+        // middle of a call.
+        ExceptionBailoutInfo propagateInfo;
+        uint32_t retval = ExceptionHandlerBailout(cx, frame, rfe, propagateInfo, overrecursed);
+        bailedOutForDebugMode = retval == BAILOUT_RETURN_OK;
+    }
+
     if (!script->hasTrynotes())
         return;
 
     JSTryNote *tn = script->trynotes()->vector;
     JSTryNote *tnEnd = tn + script->trynotes()->length;
 
     uint32_t pcOffset = uint32_t(pc - script->main());
     for (; tn != tnEnd; ++tn) {
@@ -395,52 +426,31 @@ HandleExceptionIon(JSContext *cx, const 
             CloseLiveIterator(cx, frame, localSlot);
             break;
           }
 
           case JSTRY_LOOP:
             break;
 
           case JSTRY_CATCH:
-            if (cx->isExceptionPending()) {
+            if (cx->isExceptionPending() && !bailedOutForDebugMode) {
                 // Ion can compile try-catch, but bailing out to catch
                 // exceptions is slow. Reset the use count so that if we
                 // catch many exceptions we won't Ion-compile the script.
                 script->resetUseCount();
 
                 // Bailout at the start of the catch block.
                 jsbytecode *catchPC = script->main() + tn->start + tn->length;
-
-                ExceptionBailoutInfo excInfo;
-                excInfo.frameNo = frame.frameNo();
-                excInfo.resumePC = catchPC;
-                excInfo.numExprSlots = tn->stackDepth;
-
-                BaselineBailoutInfo *info = nullptr;
-                uint32_t retval = ExceptionHandlerBailout(cx, frame, excInfo, &info);
+                ExceptionBailoutInfo excInfo(frame.frameNo(), catchPC, tn->stackDepth);
+                uint32_t retval = ExceptionHandlerBailout(cx, frame, rfe, excInfo, overrecursed);
+                if (retval == BAILOUT_RETURN_OK)
+                    return;
 
-                if (retval == BAILOUT_RETURN_OK) {
-                    JS_ASSERT(info);
-                    rfe->kind = ResumeFromException::RESUME_BAILOUT;
-                    rfe->target = cx->runtime()->jitRuntime()->getBailoutTail()->raw();
-                    rfe->bailoutInfo = info;
-                    return;
-                }
-
-                // Bailout failed. If there was a fatal error, clear the
-                // exception to turn this into an uncatchable error. If the
-                // overrecursion check failed, continue popping all inline
-                // frames and have the caller report an overrecursion error.
-                JS_ASSERT(!info);
-                cx->clearPendingException();
-
-                if (retval == BAILOUT_RETURN_OVERRECURSED)
-                    *overrecursed = true;
-                else
-                    JS_ASSERT(retval == BAILOUT_RETURN_FATAL_ERROR);
+                // Error on bailout clears pending exception.
+                MOZ_ASSERT(!cx->isExceptionPending());
             }
             break;
 
           default:
             MOZ_ASSUME_UNREACHABLE("Unexpected try note");
         }
     }
 }
@@ -562,16 +572,23 @@ HandleExceptionBaseline(JSContext *cx, c
 
           default:
             MOZ_ASSUME_UNREACHABLE("Invalid try note");
         }
     }
 
 }
 
+struct AutoDeleteDebugModeOSRInfo
+{
+    BaselineFrame *frame;
+    AutoDeleteDebugModeOSRInfo(BaselineFrame *frame) : frame(frame) { MOZ_ASSERT(frame); }
+    ~AutoDeleteDebugModeOSRInfo() { frame->deleteDebugModeOSRInfo(); }
+};
+
 void
 HandleException(ResumeFromException *rfe)
 {
     JSContext *cx = GetJSContextFromJitCode();
 
     rfe->kind = ResumeFromException::RESUME_ENTRY_FRAME;
 
     IonSpew(IonSpew_Invalidate, "handling exception");
@@ -632,16 +649,26 @@ HandleException(ResumeFromException *rfe
             if (invalidated)
                 ionScript->decref(cx->runtime()->defaultFreeOp());
 
         } else if (iter.isBaselineJS()) {
             // It's invalid to call DebugEpilogue twice for the same frame.
             bool calledDebugEpilogue = false;
 
             HandleExceptionBaseline(cx, iter, rfe, &calledDebugEpilogue);
+
+            // If we are propagating an exception through a frame with
+            // on-stack recompile info, we should free the allocated
+            // RecompileInfo struct before we leave this block, as we will not
+            // be returning to the recompile handler.
+            //
+            // We cannot delete it immediately because of the call to
+            // iter.baselineScriptAndPc below.
+            AutoDeleteDebugModeOSRInfo deleteDebugModeOSRInfo(iter.baselineFrame());
+
             if (rfe->kind != ResumeFromException::RESUME_ENTRY_FRAME)
                 return;
 
             // Unwind profiler pseudo-stack
             JSScript *script = iter.script();
             probes::ExitScript(cx, script, script->functionNonDelazifying(),
                                iter.baselineFrame()->hasPushedSPSFrame());
             // After this point, any pushed SPS frame would have been popped if it needed
@@ -1142,26 +1169,29 @@ MarkRectifierFrame(JSTracer *trc, const 
     // it if we're calling a constructor that returns a primitive value.
     IonRectifierFrameLayout *layout = (IonRectifierFrameLayout *)frame.fp();
     gc::MarkValueRoot(trc, &layout->argv()[0], "ion-thisv");
 }
 
 static void
 MarkJitActivation(JSTracer *trc, const JitActivationIterator &activations)
 {
+    JitActivation *activation = activations.activation()->asJit();
+
 #ifdef CHECK_OSIPOINT_REGISTERS
     if (js_JitOptions.checkOsiPointRegisters) {
         // GC can modify spilled registers, breaking our register checks.
         // To handle this, we disable these checks for the current VM call
         // when a GC happens.
-        JitActivation *activation = activations.activation()->asJit();
         activation->setCheckRegs(false);
     }
 #endif
 
+    activation->markRematerializedFrames(trc);
+
     for (JitFrameIterator frames(activations); !frames.done(); ++frames) {
         switch (frames.type()) {
           case JitFrame_Exit:
             MarkJitExitFrame(trc, frames);
             break;
           case JitFrame_BaselineJS:
             frames.baselineFrame()->trace(trc, frames);
             break;
--- a/js/src/jit/IonFrames.h
+++ b/js/src/jit/IonFrames.h
@@ -797,16 +797,20 @@ class IonBaselineStubFrameLayout : publi
     static inline int reverseOffsetOfSavedFramePtr() {
         return -int(2 * sizeof(void *));
     }
 
     inline ICStub *maybeStubPtr() {
         uint8_t *fp = reinterpret_cast<uint8_t *>(this);
         return *reinterpret_cast<ICStub **>(fp + reverseOffsetOfStubPtr());
     }
+    inline void setStubPtr(ICStub *stub) {
+        uint8_t *fp = reinterpret_cast<uint8_t *>(this);
+        *reinterpret_cast<ICStub **>(fp + reverseOffsetOfStubPtr()) = stub;
+    }
 };
 
 // An invalidation bailout stack is at the stack pointer for the callee frame.
 class InvalidationBailoutStack
 {
     mozilla::Array<double, FloatRegisters::Total> fpregs_;
     mozilla::Array<uintptr_t, Registers::Total> regs_;
     IonScript   *ionScript_;
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -12,16 +12,20 @@
 #include "jscompartment.h"
 
 #if defined(JS_CODEGEN_X86)
 # include "jit/x86/MacroAssembler-x86.h"
 #elif defined(JS_CODEGEN_X64)
 # include "jit/x64/MacroAssembler-x64.h"
 #elif defined(JS_CODEGEN_ARM)
 # include "jit/arm/MacroAssembler-arm.h"
+#elif defined(JS_CODEGEN_MIPS)
+# include "jit/mips/MacroAssembler-mips.h"
+#else
+# error "Unknown architecture!"
 #endif
 #include "jit/IonInstrumentation.h"
 #include "jit/JitCompartment.h"
 #include "jit/VMFunctions.h"
 #include "vm/ProxyObject.h"
 #include "vm/Shape.h"
 
 namespace js {
--- a/js/src/jit/IonSpewer.cpp
+++ b/js/src/jit/IonSpewer.cpp
@@ -256,16 +256,17 @@ jit::CheckLogging()
             "\n"
             "  bl-aborts  Baseline compiler abort messages\n"
             "  bl-scripts Baseline script-compilation\n"
             "  bl-op      Baseline compiler detailed op-specific messages\n"
             "  bl-ic      Baseline inline-cache messages\n"
             "  bl-ic-fb   Baseline IC fallback stub messages\n"
             "  bl-osr     Baseline IC OSR messages\n"
             "  bl-bails   Baseline bailouts\n"
+            "  bl-dbg-osr Baseline debug mode on stack recompile messages\n"
             "  bl-all     All baseline spew\n"
             "\n"
         );
         exit(0);
         /*NOTREACHED*/
     }
     if (ContainsFlag(env, "aborts"))
         EnableChannel(IonSpew_Abort);
@@ -317,24 +318,27 @@ jit::CheckLogging()
     if (ContainsFlag(env, "bl-ic"))
         EnableChannel(IonSpew_BaselineIC);
     if (ContainsFlag(env, "bl-ic-fb"))
         EnableChannel(IonSpew_BaselineICFallback);
     if (ContainsFlag(env, "bl-osr"))
         EnableChannel(IonSpew_BaselineOSR);
     if (ContainsFlag(env, "bl-bails"))
         EnableChannel(IonSpew_BaselineBailouts);
+    if (ContainsFlag(env, "bl-dbg-osr"))
+        EnableChannel(IonSpew_BaselineDebugModeOSR);
     if (ContainsFlag(env, "bl-all")) {
         EnableChannel(IonSpew_BaselineAbort);
         EnableChannel(IonSpew_BaselineScripts);
         EnableChannel(IonSpew_BaselineOp);
         EnableChannel(IonSpew_BaselineIC);
         EnableChannel(IonSpew_BaselineICFallback);
         EnableChannel(IonSpew_BaselineOSR);
         EnableChannel(IonSpew_BaselineBailouts);
+        EnableChannel(IonSpew_BaselineDebugModeOSR);
     }
 
     IonSpewFile = stderr;
 }
 
 void
 jit::IonSpewStartVA(IonSpewChannel channel, const char *fmt, va_list ap)
 {
--- a/js/src/jit/IonSpewer.h
+++ b/js/src/jit/IonSpewer.h
@@ -69,17 +69,19 @@ namespace jit {
     _(BaselineOp)                           \
     /* Inline caches. */                    \
     _(BaselineIC)                           \
     /* Inline cache fallbacks. */           \
     _(BaselineICFallback)                   \
     /* OSR from Baseline => Ion. */         \
     _(BaselineOSR)                          \
     /* Bailouts. */                         \
-    _(BaselineBailouts)
+    _(BaselineBailouts)                     \
+    /* Debug Mode On Stack Recompile . */   \
+    _(BaselineDebugModeOSR)
 
 
 enum IonSpewChannel {
 #define IONSPEW_CHANNEL(name) IonSpew_##name,
     IONSPEW_CHANNEL_LIST(IONSPEW_CHANNEL)
 #undef IONSPEW_CHANNEL
     IonSpew_Terminator
 };
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -43,33 +43,38 @@ enum BailoutKind
 
     // A bailout triggered by a bounds-check failure.
     Bailout_BoundsCheck,
 
     // A shape guard based on TI information failed.
     Bailout_ShapeGuard,
 
     // A bailout caused by invalid assumptions based on Baseline code.
-    Bailout_BaselineInfo
+    Bailout_BaselineInfo,
+
+    // A bailout to baseline from Ion on exception to handle Debugger hooks.
+    Bailout_IonExceptionDebugMode,
 };
 
 inline const char *
 BailoutKindString(BailoutKind kind)
 {
     switch (kind) {
       case Bailout_Normal:
         return "Bailout_Normal";
       case Bailout_ArgumentCheck:
         return "Bailout_ArgumentCheck";
       case Bailout_BoundsCheck:
         return "Bailout_BoundsCheck";
       case Bailout_ShapeGuard:
         return "Bailout_ShapeGuard";
       case Bailout_BaselineInfo:
         return "Bailout_BaselineInfo";
+      case Bailout_IonExceptionDebugMode:
+        return "Bailout_IonExceptionDebugMode";
       default:
         MOZ_ASSUME_UNREACHABLE("Invalid BailoutKind");
     }
 }
 
 static const uint32_t ELEMENT_TYPE_BITS = 5;
 static const uint32_t ELEMENT_TYPE_SHIFT = 0;
 static const uint32_t ELEMENT_TYPE_MASK = (1 << ELEMENT_TYPE_BITS) - 1;
@@ -86,16 +91,17 @@ enum MIRType
     MIRType_Null,
     MIRType_Boolean,
     MIRType_Int32,
     MIRType_Double,
     MIRType_Float32,
     MIRType_String,
     MIRType_Object,
     MIRType_MagicOptimizedArguments, // JS_OPTIMIZED_ARGUMENTS magic value.
+    MIRType_MagicOptimizedOut,       // JS_OPTIMIZED_OUT magic value.
     MIRType_MagicHole,               // JS_ELEMENTS_HOLE magic value.
     MIRType_MagicIsConstructing,     // JS_IS_CONSTRUCTING magic value.
     MIRType_Value,
     MIRType_None,                    // Invalid, used as a placeholder.
     MIRType_Slots,                   // A slots vector
     MIRType_Elements,                // An elements vector
     MIRType_Pointer,                 // An opaque pointer that receives no special treatment
     MIRType_Shape,                   // A Shape pointer.
@@ -159,16 +165,17 @@ ValueTypeFromMIRType(MIRType type)
     case MIRType_Int32:
       return JSVAL_TYPE_INT32;
     case MIRType_Float32: // Fall through, there's no JSVAL for Float32
     case MIRType_Double:
       return JSVAL_TYPE_DOUBLE;
     case MIRType_String:
       return JSVAL_TYPE_STRING;
     case MIRType_MagicOptimizedArguments:
+    case MIRType_MagicOptimizedOut:
     case MIRType_MagicHole:
     case MIRType_MagicIsConstructing:
       return JSVAL_TYPE_MAGIC;
     default:
       JS_ASSERT(type == MIRType_Object);
       return JSVAL_TYPE_OBJECT;
   }
 }
@@ -196,16 +203,18 @@ StringFromMIRType(MIRType type)
     case MIRType_Float32:
       return "Float32";
     case MIRType_String:
       return "String";
     case MIRType_Object:
       return "Object";
     case MIRType_MagicOptimizedArguments:
       return "MagicOptimizedArguments";
+    case MIRType_MagicOptimizedOut:
+      return "MagicOptimizedOut";
     case MIRType_MagicHole:
       return "MagicHole";
     case MIRType_MagicIsConstructing:
       return "MagicIsConstructing";
     case MIRType_Value:
       return "Value";
     case MIRType_None:
       return "None";
--- a/js/src/jit/JitCompartment.h
+++ b/js/src/jit/JitCompartment.h
@@ -187,16 +187,20 @@ class JitRuntime
     JitCode *shapePreBarrier_;
 
     // Thunk used by the debugger for breakpoint and step mode.
     JitCode *debugTrapHandler_;
 
     // Stub used to inline the ForkJoinGetSlice intrinsic.
     JitCode *forkJoinGetSliceStub_;
 
+    // Thunk used to fix up on-stack recompile of baseline scripts.
+    JitCode *baselineDebugModeOSRHandler_;
+    void *baselineDebugModeOSRHandlerNoFrameRegPopAddr_;
+
     // Map VMFunction addresses to the JitCode of the wrapper.
     typedef WeakCache<const VMFunction *, JitCode *> VMWrapperMap;
     VMWrapperMap *functionWrappers_;
 
     // Buffer for OSR from baseline to Ion. To avoid holding on to this for
     // too long, it's also freed in JitCompartment::mark and in EnterBaseline
     // (after returning from JIT code).
     uint8_t *osrTempData_;
@@ -218,16 +222,17 @@ class JitRuntime
     JitCode *generateEnterJIT(JSContext *cx, EnterJitType type);
     JitCode *generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void **returnAddrOut);
     JitCode *generateBailoutTable(JSContext *cx, uint32_t frameClass);
     JitCode *generateBailoutHandler(JSContext *cx);
     JitCode *generateInvalidator(JSContext *cx);
     JitCode *generatePreBarrier(JSContext *cx, MIRType type);
     JitCode *generateDebugTrapHandler(JSContext *cx);
     JitCode *generateForkJoinGetSliceStub(JSContext *cx);
+    JitCode *generateBaselineDebugModeOSRHandler(JSContext *cx, uint32_t *noFrameRegPopOffsetOut);
     JitCode *generateVMWrapper(JSContext *cx, const VMFunction &f);
 
     JSC::ExecutableAllocator *createIonAlloc(JSContext *cx);
 
   public:
     JitRuntime();
     ~JitRuntime();
     bool initialize(JSContext *cx);
@@ -278,16 +283,18 @@ class JitRuntime
     void ensureIonCodeProtected(JSRuntime *rt);
     void ensureIonCodeAccessible(JSRuntime *rt);
     void patchIonBackedges(JSRuntime *rt, BackedgeTarget target);
 
     bool handleAccessViolation(JSRuntime *rt, void *faultingAddress);
 
     JitCode *getVMWrapper(const VMFunction &f) const;
     JitCode *debugTrapHandler(JSContext *cx);
+    JitCode *getBaselineDebugModeOSRHandler(JSContext *cx);
+    void *getBaselineDebugModeOSRHandlerAddress(JSContext *cx, bool popFrameReg);
 
     JitCode *getGenericBailoutHandler() const {
         return bailoutHandler_;
     }
 
     JitCode *getExceptionTail() const {
         return exceptionTail_;
     }
@@ -352,19 +359,26 @@ class JitCompartment
     friend class JitActivation;
 
     // Map ICStub keys to ICStub shared code objects.
     typedef WeakValueCache<uint32_t, ReadBarriered<JitCode> > ICStubCodeMap;
     ICStubCodeMap *stubCodes_;
 
     // Keep track of offset into various baseline stubs' code at return
     // point from called script.
-    void *baselineCallReturnAddr_;
-    void *baselineGetPropReturnAddr_;
-    void *baselineSetPropReturnAddr_;
+    void *baselineCallReturnFromIonAddr_;
+    void *baselineGetPropReturnFromIonAddr_;
+    void *baselineSetPropReturnFromIonAddr_;
+
+    // Same as above, but is used for return from a baseline stub. This is
+    // used for recompiles of on-stack baseline scripts (e.g., for debug
+    // mode).
+    void *baselineCallReturnFromStubAddr_;
+    void *baselineGetPropReturnFromStubAddr_;
+    void *baselineSetPropReturnFromStubAddr_;
 
     // Stub to concatenate two strings inline. Note that it can't be
     // stored in JitRuntime because masm.newGCString bakes in zone-specific
     // pointers. This has to be a weak pointer to avoid keeping the whole
     // compartment alive.
     ReadBarriered<JitCode> stringConcatStub_;
     ReadBarriered<JitCode> parallelStringConcatStub_;
 
@@ -386,39 +400,64 @@ class JitCompartment
     bool putStubCode(uint32_t key, Handle<JitCode *> stubCode) {
         // Make sure to do a lookupForAdd(key) and then insert into that slot, because
         // that way if stubCode gets moved due to a GC caused by lookupForAdd, then
         // we still write the correct pointer.
         JS_ASSERT(!stubCodes_->has(key));
         ICStubCodeMap::AddPtr p = stubCodes_->lookupForAdd(key);
         return stubCodes_->add(p, key, stubCode.get());
     }
-    void initBaselineCallReturnAddr(void *addr) {
-        JS_ASSERT(baselineCallReturnAddr_ == nullptr);
-        baselineCallReturnAddr_ = addr;
+    void initBaselineCallReturnFromIonAddr(void *addr) {
+        JS_ASSERT(baselineCallReturnFromIonAddr_ == nullptr);
+        baselineCallReturnFromIonAddr_ = addr;
+    }
+    void *baselineCallReturnFromIonAddr() {
+        JS_ASSERT(baselineCallReturnFromIonAddr_ != nullptr);
+        return baselineCallReturnFromIonAddr_;
+    }
+    void initBaselineGetPropReturnFromIonAddr(void *addr) {
+        JS_ASSERT(baselineGetPropReturnFromIonAddr_ == nullptr);
+        baselineGetPropReturnFromIonAddr_ = addr;
     }
-    void *baselineCallReturnAddr() {
-        JS_ASSERT(baselineCallReturnAddr_ != nullptr);
-        return baselineCallReturnAddr_;
+    void *baselineGetPropReturnFromIonAddr() {
+        JS_ASSERT(baselineGetPropReturnFromIonAddr_ != nullptr);
+        return baselineGetPropReturnFromIonAddr_;
     }
-    void initBaselineGetPropReturnAddr(void *addr) {
-        JS_ASSERT(baselineGetPropReturnAddr_ == nullptr);
-        baselineGetPropReturnAddr_ = addr;
+    void initBaselineSetPropReturnFromIonAddr(void *addr) {
+        JS_ASSERT(baselineSetPropReturnFromIonAddr_ == nullptr);
+        baselineSetPropReturnFromIonAddr_ = addr;
+    }
+    void *baselineSetPropReturnFromIonAddr() {
+        JS_ASSERT(baselineSetPropReturnFromIonAddr_ != nullptr);
+        return baselineSetPropReturnFromIonAddr_;
     }
-    void *baselineGetPropReturnAddr() {
-        JS_ASSERT(baselineGetPropReturnAddr_ != nullptr);
-        return baselineGetPropReturnAddr_;
+
+    void initBaselineCallReturnFromStubAddr(void *addr) {
+        MOZ_ASSERT(baselineCallReturnFromStubAddr_ == nullptr);
+        baselineCallReturnFromStubAddr_ = addr;;
+    }
+    void *baselineCallReturnFromStubAddr() {
+        JS_ASSERT(baselineCallReturnFromStubAddr_ != nullptr);
+        return baselineCallReturnFromStubAddr_;
     }
-    void initBaselineSetPropReturnAddr(void *addr) {
-        JS_ASSERT(baselineSetPropReturnAddr_ == nullptr);
-        baselineSetPropReturnAddr_ = addr;
+    void initBaselineGetPropReturnFromStubAddr(void *addr) {
+        JS_ASSERT(baselineGetPropReturnFromStubAddr_ == nullptr);
+        baselineGetPropReturnFromStubAddr_ = addr;
+    }
+    void *baselineGetPropReturnFromStubAddr() {
+        JS_ASSERT(baselineGetPropReturnFromStubAddr_ != nullptr);
+        return baselineGetPropReturnFromStubAddr_;
     }
-    void *baselineSetPropReturnAddr() {
-        JS_ASSERT(baselineSetPropReturnAddr_ != nullptr);
-        return baselineSetPropReturnAddr_;
+    void initBaselineSetPropReturnFromStubAddr(void *addr) {
+        JS_ASSERT(baselineSetPropReturnFromStubAddr_ == nullptr);
+        baselineSetPropReturnFromStubAddr_ = addr;
+    }
+    void *baselineSetPropReturnFromStubAddr() {
+        JS_ASSERT(baselineSetPropReturnFromStubAddr_ != nullptr);
+        return baselineSetPropReturnFromStubAddr_;
     }
 
     bool notifyOfActiveParallelEntryScript(JSContext *cx, HandleScript script);
 
     void toggleBaselineStubBarriers(bool enabled);
 
     JSC::ExecutableAllocator *createIonAlloc();
 
--- a/js/src/jit/JitFrameIterator.h
+++ b/js/src/jit/JitFrameIterator.h
@@ -113,16 +113,19 @@ class JitFrameIterator
 
     // Current frame information.
     FrameType type() const {
         return type_;
     }
     uint8_t *fp() const {
         return current_;
     }
+    const JitActivation *activation() const {
+        return activation_;
+    }
 
     IonCommonFrameLayout *current() const {
         return (IonCommonFrameLayout *)current_;
     }
 
     inline uint8_t *returnAddress() const;
 
     IonJSFrameLayout *jsFrame() const {
@@ -362,31 +365,42 @@ class SnapshotIterator
         RValueAllocation a = readAllocation();
         if (allocationReadable(a))
             return allocationValue(a);
         if (!silentFailure)
             warnUnreadableAllocation();
         return UndefinedValue();
     }
 
-    template <class Op>
-    void readFrameArgs(Op &op, Value *scopeChain, Value *thisv,
-                       unsigned start, unsigned end, JSScript *script)
-    {
+    void readCommonFrameSlots(Value *scopeChain, Value *rval) {
         if (scopeChain)
             *scopeChain = read();
         else
             skip();
 
-        // Skip slot for return value.
-        skip();
+        if (rval)
+            *rval = read();
+        else
+            skip();
+    }
 
-        // Skip slot for arguments object.
-        if (script->argumentsHasVarBinding())
-            skip();
+    template <class Op>
+    void readFunctionFrameArgs(Op &op, ArgumentsObject **argsObj, Value *thisv,
+                               unsigned start, unsigned end, JSScript *script)
+    {
+        // Assumes that the common frame arguments have already been read.
+        if (script->argumentsHasVarBinding()) {
+            if (argsObj) {
+                Value v = read();
+                if (v.isObject())
+                    *argsObj = &v.toObject().as<ArgumentsObject>();
+            } else {
+                skip();
+            }
+        }
 
         if (thisv)
             *thisv = read();
         else
             skip();
 
         unsigned i = 0;
         if (end < start)
@@ -441,16 +455,32 @@ class InlineFrameIteratorMaybeGC
 
     struct Nop {
         void operator()(const Value &v) { }
     };
 
   private:
     void findNextFrame();
 
+    JSObject *computeScopeChain(Value scopeChainValue) const {
+        if (scopeChainValue.isObject())
+            return &scopeChainValue.toObject();
+
+        if (isFunctionFrame()) {
+            // Heavyweight functions should always have a scope chain.
+            MOZ_ASSERT(!callee()->isHeavyweight());
+            return callee()->environment();
+        }
+
+        // Ion does not handle scripts that are not compile-and-go.
+        MOZ_ASSERT(!script()->isForEval());
+        MOZ_ASSERT(script()->compileAndGo());
+        return &script()->global();
+    }
+
   public:
     InlineFrameIteratorMaybeGC(JSContext *cx, const JitFrameIterator *iter)
       : callee_(cx),
         script_(cx)
     {
         resetOn(iter);
     }
 
@@ -499,73 +529,87 @@ class InlineFrameIteratorMaybeGC
         if (more())
             return numActualArgs_;
 
         return frame_->numActualArgs();
     }
 
     template <class ArgOp, class LocalOp>
     void readFrameArgsAndLocals(JSContext *cx, ArgOp &argOp, LocalOp &localOp,
-                                Value *scopeChain, Value *thisv,
+                                JSObject **scopeChain, Value *rval,
+                                ArgumentsObject **argsObj, Value *thisv,
                                 ReadFrameArgsBehavior behavior) const
     {
-        unsigned nactual = numActualArgs();
-        unsigned nformal = callee()->nargs();
+        SnapshotIterator s(si_);
+
+        // Read frame slots common to both function and global frames.
+        Value scopeChainValue;
+        s.readCommonFrameSlots(&scopeChainValue, rval);
+
+        if (scopeChain)
+            *scopeChain = computeScopeChain(scopeChainValue);
 
-        // Get the non overflown arguments, which are taken from the inlined
-        // frame, because it will have the updated value when JSOP_SETARG is
-        // done.
-        SnapshotIterator s(si_);
-        if (behavior != ReadFrame_Overflown)
-            s.readFrameArgs(argOp, scopeChain, thisv, 0, nformal, script());
+        // Read arguments, which only function frames have.
+        if (isFunctionFrame()) {
+            unsigned nactual = numActualArgs();
+            unsigned nformal = callee()->nargs();
 
-        if (behavior != ReadFrame_Formals) {
-            if (more()) {
-                // There is still a parent frame of this inlined frame.  All
-                // arguments (also the overflown) are the last pushed values
-                // in the parent frame.  To get the overflown arguments, we
-                // need to take them from there.
+            // Get the non overflown arguments, which are taken from the inlined
+            // frame, because it will have the updated value when JSOP_SETARG is
+            // done.
+            if (behavior != ReadFrame_Overflown)
+                s.readFunctionFrameArgs(argOp, argsObj, thisv, 0, nformal, script());
+
+            if (behavior != ReadFrame_Formals) {
+                if (more()) {
+                    // There is still a parent frame of this inlined frame.  All
+                    // arguments (also the overflown) are the last pushed values
+                    // in the parent frame.  To get the overflown arguments, we
+                    // need to take them from there.
 
-                // The overflown arguments are not available in current frame.
-                // They are the last pushed arguments in the parent frame of
-                // this inlined frame.
-                InlineFrameIteratorMaybeGC it(cx, this);
-                ++it;
-                unsigned argsObjAdj = it.script()->argumentsHasVarBinding() ? 1 : 0;
-                SnapshotIterator parent_s(it.snapshotIterator());
+                    // The overflown arguments are not available in current frame.
+                    // They are the last pushed arguments in the parent frame of
+                    // this inlined frame.
+                    InlineFrameIteratorMaybeGC it(cx, this);
+                    ++it;
+                    unsigned argsObjAdj = it.script()->argumentsHasVarBinding() ? 1 : 0;
+                    SnapshotIterator parent_s(it.snapshotIterator());
 
-                // Skip over all slots until we get to the last slots
-                // (= arguments slots of callee) the +3 is for [this], [returnvalue],
-                // [scopechain], and maybe +1 for [argsObj]
-                JS_ASSERT(parent_s.numAllocations() >= nactual + 3 + argsObjAdj);
-                unsigned skip = parent_s.numAllocations() - nactual - 3 - argsObjAdj;
-                for (unsigned j = 0; j < skip; j++)
-                    parent_s.skip();
+                    // Skip over all slots until we get to the last slots
+                    // (= arguments slots of callee) the +3 is for [this], [returnvalue],
+                    // [scopechain], and maybe +1 for [argsObj]
+                    JS_ASSERT(parent_s.numAllocations() >= nactual + 3 + argsObjAdj);
+                    unsigned skip = parent_s.numAllocations() - nactual - 3 - argsObjAdj;
+                    for (unsigned j = 0; j < skip; j++)
+                        parent_s.skip();
 
-                // Get the overflown arguments
-                parent_s.readFrameArgs(argOp, nullptr, nullptr, nformal, nactual, it.script());
-            } else {
-                // There is no parent frame to this inlined frame, we can read
-                // from the frame's Value vector directly.
-                Value *argv = frame_->actualArgs();
-                for (unsigned i = nformal; i < nactual; i++)
-                    argOp(argv[i]);
+                    // Get the overflown arguments
+                    parent_s.readCommonFrameSlots(nullptr, nullptr);
+                    parent_s.readFunctionFrameArgs(argOp, nullptr, nullptr,
+                                                   nformal, nactual, it.script());
+                } else {
+                    // There is no parent frame to this inlined frame, we can read
+                    // from the frame's Value vector directly.
+                    Value *argv = frame_->actualArgs();
+                    for (unsigned i = nformal; i < nactual; i++)
+                        argOp(argv[i]);
+                }
             }
         }
 
         // At this point we've read all the formals in s, and can read the
         // locals.
         for (unsigned i = 0; i < script()->nfixed(); i++)
             localOp(s.read());
     }
 
     template <class Op>
     void unaliasedForEachActual(JSContext *cx, Op op, ReadFrameArgsBehavior behavior) const {
         Nop nop;
-        readFrameArgsAndLocals(cx, op, nop, nullptr, nullptr, behavior);
+        readFrameArgsAndLocals(cx, op, nop, nullptr, nullptr, nullptr, nullptr, behavior);
     }
 
     JSScript *script() const {
         return script_;
     }
     jsbytecode *pc() const {
         return pc_;
     }
@@ -575,41 +619,42 @@ class InlineFrameIteratorMaybeGC
     bool isFunctionFrame() const;
     bool isConstructing() const;
 
     JSObject *scopeChain() const {
         SnapshotIterator s(si_);
 
         // scopeChain
         Value v = s.read();
-        if (v.isObject())
-            return &v.toObject();
-
-        return callee()->environment();
+        return computeScopeChain(v);
     }
 
     JSObject *thisObject() const {
+        // In strict modes, |this| may not be an object and thus may not be
+        // readable which can either segv in read or trigger the assertion.
+        Value v = thisValue();
+        JS_ASSERT(v.isObject());
+        return &v.toObject();
+    }
+
+    Value thisValue() const {
         // JS_ASSERT(isConstructing(...));
         SnapshotIterator s(si_);
 
         // scopeChain
         s.skip();
 
         // return value
         s.skip();
 
         // Arguments object.
         if (script()->argumentsHasVarBinding())
             s.skip();
 
-        // In strict modes, |this| may not be an object and thus may not be
-        // readable which can either segv in read or trigger the assertion.
-        Value v = s.read();
-        JS_ASSERT(v.isObject());
-        return &v.toObject();
+        return s.read();
     }
 
     InlineFrameIteratorMaybeGC &operator++() {
         findNextFrame();
         return *this;
     }
 
     void dump() const;
@@ -617,18 +662,21 @@ class InlineFrameIteratorMaybeGC
     void resetOn(const JitFrameIterator *iter);
 
     const JitFrameIterator &frame() const {
         return *frame_;
     }
 
     // Inline frame number, 0 for the outermost (non-inlined) frame.
     size_t frameNo() const {
+        return frameCount() - framesRead_;
+    }
+    size_t frameCount() const {
         MOZ_ASSERT(frameCount_ != UINT32_MAX);
-        return frameCount_ - framesRead_;
+        return frameCount_;
     }
 
   private:
     InlineFrameIteratorMaybeGC() MOZ_DELETE;
     InlineFrameIteratorMaybeGC(const InlineFrameIteratorMaybeGC &iter) MOZ_DELETE;
 };
 typedef InlineFrameIteratorMaybeGC<CanGC> InlineFrameIterator;
 typedef InlineFrameIteratorMaybeGC<NoGC> InlineFrameIteratorNoGC;
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -1377,16 +1377,44 @@ class LApplyArgsGeneric : public LCallIn
     const LDefinition *getTempObject() {
         return getTemp(0);
     }
     const LDefinition *getTempCopy() {
         return getTemp(1);
     }
 };
 
+class LArraySplice : public LCallInstructionHelper<0, 3, 0>
+{
+  public:
+    LIR_HEADER(ArraySplice)
+
+    LArraySplice(const LAllocation &object, const LAllocation &start,
+                 const LAllocation &deleteCount)
+    {
+        setOperand(0, object);
+        setOperand(1, start);
+        setOperand(2, deleteCount);
+    }
+
+    MArraySplice *mir() const {
+        return mir_->toArraySplice();
+    }
+
+    const LAllocation *getObject() {
+        return getOperand(0);
+    }
+    const LAllocation *getStart() {
+        return getOperand(1);
+    }
+    const LAllocation *getDeleteCount() {
+        return getOperand(2);
+    }
+};
+
 class LGetDynamicName : public LCallInstructionHelper<BOX_PIECES, 2, 3>
 {
   public:
     LIR_HEADER(GetDynamicName)
 
     LGetDynamicName(const LAllocation &scopeChain, const LAllocation &name,
                     const LDefinition &temp1, const LDefinition &temp2, const LDefinition &temp3)
     {
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -1586,16 +1586,20 @@ LAllocation::toRegister() const
 # if defined(JS_CODEGEN_X86)
 #  include "jit/x86/LIR-x86.h"
 # elif defined(JS_CODEGEN_X64)
 #  include "jit/x64/LIR-x64.h"
 # endif
 # include "jit/shared/LIR-x86-shared.h"
 #elif defined(JS_CODEGEN_ARM)
 # include "jit/arm/LIR-arm.h"
+#elif defined(JS_CODEGEN_MIPS)
+# include "jit/mips/LIR-mips.h"
+#else
+# error "Unknown architecture!"
 #endif
 
 #undef LIR_HEADER
 
 namespace js {
 namespace jit {
 
 #define LIROP(name)                                                         \
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -19,16 +19,17 @@
     _(Value)                        \
     _(CloneLiteral)                 \
     _(Parameter)                    \
     _(Callee)                       \
     _(TableSwitch)                  \
     _(TableSwitchV)                 \
     _(Goto)                         \
     _(NewArray)                     \
+    _(ArraySplice)                  \
     _(NewObject)                    \
     _(NewSlots)                     \
     _(NewDeclEnvObject)             \
     _(NewCallObject)                \
     _(NewSingletonCallObject)       \
     _(NewStringObject)              \
     _(NewPar)                       \
     _(NewDenseArrayPar)             \
@@ -300,15 +301,19 @@
     _(AssertRangeV)
 
 #if defined(JS_CODEGEN_X86)
 # include "jit/x86/LOpcodes-x86.h"
 #elif defined(JS_CODEGEN_X64)
 # include "jit/x64/LOpcodes-x64.h"
 #elif defined(JS_CODEGEN_ARM)
 # include "jit/arm/LOpcodes-arm.h"
+#elif defined(JS_CODEGEN_MIPS)
+# include "jit/mips/LOpcodes-mips.h"
+#else
+# error "Unknown architecture!"
 #endif
 
 #define LIR_OPCODE_LIST(_)          \
     LIR_COMMON_OPCODE_LIST(_)       \
     LIR_CPU_OPCODE_LIST(_)
 
 #endif /* jit_LOpcodes_h */
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -562,16 +562,25 @@ LIRGenerator::visitAssertFloat32(MAssert
     if (type != MIRType_Value && !js_JitOptions.eagerCompilation) {
         JS_ASSERT_IF(checkIsFloat32, type == MIRType_Float32);
         JS_ASSERT_IF(!checkIsFloat32, type != MIRType_Float32);
     }
     return true;
 }
 
 bool
+LIRGenerator::visitArraySplice(MArraySplice *ins)
+{
+    LArraySplice *lir = new(alloc()) LArraySplice(useRegisterAtStart(ins->object()),
+                                                  useRegisterAtStart(ins->start()),
+                                                  useRegisterAtStart(ins->deleteCount()));
+    return add(lir, ins) && assignSafepoint(lir, ins);
+}
+
+bool
 LIRGenerator::visitGetDynamicName(MGetDynamicName *ins)
 {
     MDefinition *scopeChain = ins->getScopeChain();
     JS_ASSERT(scopeChain->type() == MIRType_Object);
 
     MDefinition *name = ins->getName();
     JS_ASSERT(name->type() == MIRType_String);
 
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -12,18 +12,20 @@
 
 #include "jit/LIR.h"
 #if defined(JS_CODEGEN_X86)
 # include "jit/x86/Lowering-x86.h"
 #elif defined(JS_CODEGEN_X64)
 # include "jit/x64/Lowering-x64.h"
 #elif defined(JS_CODEGEN_ARM)
 # include "jit/arm/Lowering-arm.h"
+#elif defined(JS_CODEGEN_MIPS)
+# include "jit/mips/Lowering-mips.h"
 #else
-# error "CPU!"
+# error "Unknown architecture!"
 #endif
 
 namespace js {
 namespace jit {
 
 class LIRGenerator : public LIRGeneratorSpecific
 {
     void updateResumeState(MInstruction *ins);
@@ -93,16 +95,17 @@ class LIRGenerator : public LIRGenerator
     bool visitCreateArgumentsObject(MCreateArgumentsObject *ins);
     bool visitGetArgumentsObjectArg(MGetArgumentsObjectArg *ins);
     bool visitSetArgumentsObjectArg(MSetArgumentsObjectArg *ins);
     bool visitReturnFromCtor(MReturnFromCtor *ins);
     bool visitComputeThis(MComputeThis *ins);
     bool visitLoadArrowThis(MLoadArrowThis *ins);
     bool visitCall(MCall *call);
     bool visitApplyArgs(MApplyArgs *apply);
+    bool visitArraySplice(MArraySplice *splice);
     bool visitBail(MBail *bail);
     bool visitAssertFloat32(MAssertFloat32 *ins);
     bool visitGetDynamicName(MGetDynamicName *ins);
     bool visitFilterArgumentsOrEval(MFilterArgumentsOrEval *ins);
     bool visitCallDirectEval(MCallDirectEval *ins);
     bool visitTest(MTest *test);
     bool visitFunctionDispatch(MFunctionDispatch *ins);
     bool visitTypeObjectDispatch(MTypeObjectDispatch *ins);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -33,16 +33,18 @@ IonBuilder::inlineNativeCall(CallInfo &c
     if (native == js::array_pop)
         return inlineArrayPopShift(callInfo, MArrayPopShift::Pop);
     if (native == js::array_shift)
         return inlineArrayPopShift(callInfo, MArrayPopShift::Shift);
     if (native == js::array_push)
         return inlineArrayPush(callInfo);
     if (native == js::array_concat)
         return inlineArrayConcat(callInfo);
+    if (native == js::array_splice)
+        return inlineArraySplice(callInfo);
 
     // Math natives.
     if (native == js_math_abs)
         return inlineMathAbs(callInfo);
     if (native == js::math_floor)
         return inlineMathFloor(callInfo);
     if (native == js::math_ceil)
         return inlineMathCeil(callInfo);
@@ -391,16 +393,52 @@ IonBuilder::inlineArrayPopShift(CallInfo
 
     if (!pushTypeBarrier(ins, returnTypes, barrier))
         return InliningStatus_Error;
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
+IonBuilder::inlineArraySplice(CallInfo &callInfo)
+{
+    if (callInfo.argc() != 2 || callInfo.constructing())
+        return InliningStatus_NotInlined;
+
+    // Ensure |this|, argument and result are objects.
+    if (getInlineReturnType() != MIRType_Object)
+        return InliningStatus_NotInlined;
+    if (callInfo.thisArg()->type() != MIRType_Object)
+        return InliningStatus_NotInlined;
+    if (callInfo.getArg(0)->type() != MIRType_Int32)
+        return InliningStatus_NotInlined;
+    if (callInfo.getArg(1)->type() != MIRType_Int32)
+        return InliningStatus_NotInlined;
+
+    callInfo.setImplicitlyUsedUnchecked();
+
+    // Specialize arr.splice(start, deleteCount) with unused return value and
+    // avoid creating the result array in this case.
+    if (!BytecodeIsPopped(pc))
+        return InliningStatus_NotInlined;
+
+    MArraySplice *ins = MArraySplice::New(alloc(),
+                                          callInfo.thisArg(),
+                                          callInfo.getArg(0),
+                                          callInfo.getArg(1));
+
+    current->add(ins);
+    pushConstant(UndefinedValue());
+
+    if (!resumeAfter(ins))
+        return InliningStatus_Error;
+    return InliningStatus_Inlined;
+}
+
+IonBuilder::InliningStatus
 IonBuilder::inlineArrayPush(CallInfo &callInfo)
 {
     if (callInfo.argc() != 1 || callInfo.constructing())
         return InliningStatus_NotInlined;
 
     MDefinition *obj = callInfo.thisArg();
     MDefinition *value = callInfo.getArg(0);
     if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -41,16 +41,18 @@ static const inline
 MIRType MIRTypeFromValue(const js::Value &vp)
 {
     if (vp.isDouble())
         return MIRType_Double;
     if (vp.isMagic()) {
         switch (vp.whyMagic()) {
           case JS_OPTIMIZED_ARGUMENTS:
             return MIRType_MagicOptimizedArguments;
+          case JS_OPTIMIZED_OUT:
+            return MIRType_MagicOptimizedOut;
           case JS_ELEMENTS_HOLE:
             return MIRType_MagicHole;
           case JS_IS_CONSTRUCTING:
             return MIRType_MagicIsConstructing;
           default:
             MOZ_ASSERT(!"Unexpected magic constant");
         }
     }
@@ -1936,16 +1938,56 @@ class MCallDOMNative : public MCall
 
     virtual bool isCallDOMNative() const MOZ_OVERRIDE {
         return true;
     }
 
     virtual void computeMovable() MOZ_OVERRIDE;
 };
 
+// arr.splice(start, deleteCount) with unused return value.
+class MArraySplice
+  : public MTernaryInstruction,
+    public Mix3Policy<ObjectPolicy<0>, IntPolicy<1>, IntPolicy<2> >
+{
+  private:
+
+    MArraySplice(MDefinition *object, MDefinition *start, MDefinition *deleteCount)
+      : MTernaryInstruction(object, start, deleteCount)
+    { }
+
+  public:
+    INSTRUCTION_HEADER(ArraySplice)
+    static MArraySplice *New(TempAllocator &alloc, MDefinition *object,
+                             MDefinition *start, MDefinition *deleteCount)
+    {
+        return new(alloc) MArraySplice(object, start, deleteCount);
+    }
+
+    MDefinition *object() const {
+        return getOperand(0);
+    }
+
+    MDefinition *start() const {
+        return getOperand(1);
+    }
+
+    MDefinition *deleteCount() const {
+        return getOperand(2);
+    }
+
+    bool possiblyCalls() const {
+        return true;
+    }
+
+    TypePolicy *typePolicy() {
+        return this;
+    }
+};
+
 // fun.apply(self, arguments)
 class MApplyArgs
   : public MAryInstruction<3>,
     public MixPolicy<ObjectPolicy<0>, MixPolicy<IntPolicy<1>, BoxPolicy<2> > >
 {
   protected:
     // Monomorphic cache of single target from TI, or nullptr.
     CompilerRootFunction target_;
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -36,16 +36,17 @@ namespace jit {
     _(CreateThisWithTemplate)                                               \
     _(CreateArgumentsObject)                                                \
     _(GetArgumentsObjectArg)                                                \
     _(SetArgumentsObjectArg)                                                \
     _(ComputeThis)                                                          \
     _(LoadArrowThis)                                                        \
     _(Call)                                                                 \
     _(ApplyArgs)                                                            \
+    _(ArraySplice)                                                          \
     _(Bail)                                                                 \
     _(AssertFloat32)                                                        \
     _(GetDynamicName)                                                       \
     _(FilterArgumentsOrEval)                                                \
     _(CallDirectEval)                                                       \
     _(BitNot)                                                               \
     _(TypeOf)                                                               \
     _(ToId)                                                                 \
--- a/js/src/jit/MoveEmitter.h
+++ b/js/src/jit/MoveEmitter.h
@@ -6,13 +6,15 @@
 
 #ifndef jit_MoveEmitter_h
 #define jit_MoveEmitter_h
 
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
 # include "jit/shared/MoveEmitter-x86-shared.h"
 #elif defined(JS_CODEGEN_ARM)
 # include "jit/arm/MoveEmitter-arm.h"
+#elif defined(JS_CODEGEN_MIPS)
+# include "jit/mips/MoveEmitter-mips.h"
 #else
-# error "CPU Not Supported"
+# error "Unknown architecture!"
 #endif
 
 #endif /* jit_MoveEmitter_h */
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -134,16 +134,17 @@ class ParallelSafetyVisitor : public MIn
     UNSAFE_OP(CreateThisWithProto)
     UNSAFE_OP(CreateArgumentsObject)
     UNSAFE_OP(GetArgumentsObjectArg)
     UNSAFE_OP(SetArgumentsObjectArg)
     UNSAFE_OP(ComputeThis)
     UNSAFE_OP(LoadArrowThis)
     CUSTOM_OP(Call)
     UNSAFE_OP(ApplyArgs)
+    UNSAFE_OP(ArraySplice)
     UNSAFE_OP(Bail)
     UNSAFE_OP(AssertFloat32)
     UNSAFE_OP(GetDynamicName)
     UNSAFE_OP(FilterArgumentsOrEval)
     UNSAFE_OP(CallDirectEval)
     SAFE_OP(BitNot)
     SAFE_OP(TypeOf)
     UNSAFE_OP(ToId)
--- a/js/src/jit/RegisterAllocator.h
+++ b/js/src/jit/RegisterAllocator.h
@@ -306,17 +306,17 @@ class RegisterAllocator
         graph(graph),
         allRegisters_(RegisterSet::All())
     {
         if (FramePointer != InvalidReg && mir->instrumentedProfiling())
             allRegisters_.take(AnyRegister(FramePointer));
 #if defined(JS_CODEGEN_X64)
         if (mir->compilingAsmJS())
             allRegisters_.take(AnyRegister(HeapReg));
-#elif defined(JS_CODEGEN_ARM)
+#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
         if (mir->compilingAsmJS()) {
             allRegisters_.take(AnyRegister(HeapReg));
             allRegisters_.take(AnyRegister(GlobalReg));
             allRegisters_.take(AnyRegister(NANReg));
         }
 #endif
     }
 
--- a/js/src/jit/RegisterSets.h
+++ b/js/src/jit/RegisterSets.h
@@ -839,17 +839,17 @@ class AsmJSHeapAccess
       : offset_(offset),
 # if defined(JS_CODEGEN_X86)
         cmpDelta_(cmp == UINT32_MAX ? 0 : offset - cmp),
 # endif
         opLength_(after - offset),
         isFloat32Load_(false),
         loadedReg_(UINT8_MAX)
     {}
-#elif defined(JS_CODEGEN_ARM)
+#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
     explicit AsmJSHeapAccess(uint32_t offset)
       : offset_(offset)
     {}
 #endif
 
     uint32_t offset() const { return offset_; }
     void setOffset(uint32_t offset) { offset_ = offset; }
 #if defined(JS_CODEGEN_X86)
--- a/js/src/jit/Registers.h
+++ b/js/src/jit/Registers.h
@@ -11,16 +11,20 @@
 
 #include "jit/IonTypes.h"
 #if defined(JS_CODEGEN_X86)
 # include "jit/x86/Architecture-x86.h"
 #elif defined(JS_CODEGEN_X64)
 # include "jit/x64/Architecture-x64.h"
 #elif defined(JS_CODEGEN_ARM)
 # include "jit/arm/Architecture-arm.h"
+#elif defined(JS_CODEGEN_MIPS)
+# include "jit/mips/Architecture-mips.h"
+#else
+# error "Unknown architecture!"
 #endif
 
 namespace js {
 namespace jit {
 
 struct Register {
     typedef Registers Codes;
     typedef Codes::Code Code;
new file mode 100644
--- /dev/null
+++ b/js/src/jit/RematerializedFrame.cpp
@@ -0,0 +1,148 @@
+/* -*- 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/RematerializedFrame.h"
+#include "jit/IonFrames.h"
+
+#include "vm/ArgumentsObject.h"
+
+#include "jsscriptinlines.h"
+#include "jit/IonFrames-inl.h"
+
+using namespace js;
+using namespace jit;
+
+struct CopyValueToRematerializedFrame
+{
+    Value *slots;
+
+    CopyValueToRematerializedFrame(Value *slots)
+      : slots(slots)
+    { }
+
+    void operator()(const Value &v) {
+        *slots++ = v;
+    }
+};
+
+RematerializedFrame::RematerializedFrame(JSContext *cx, uint8_t *top, InlineFrameIterator &iter)
+  : prevUpToDate_(false),
+    top_(top),
+    frameNo_(iter.frameNo()),
+    numActualArgs_(iter.numActualArgs()),
+    script_(iter.script())
+{
+    CopyValueToRematerializedFrame op(slots_);
+    iter.readFrameArgsAndLocals(cx, op, op, &scopeChain_, &returnValue_,
+                                &argsObj_, &thisValue_, ReadFrame_Actuals);
+}
+
+/* static */ RematerializedFrame *
+RematerializedFrame::New(JSContext *cx, uint8_t *top, InlineFrameIterator &iter)
+{
+    unsigned numFormals = iter.isFunctionFrame() ? iter.callee()->nargs() : 0;
+    size_t numBytes = sizeof(RematerializedFrame) +
+        (Max(numFormals, iter.numActualArgs()) +
+         iter.script()->nfixed()) * sizeof(Value) -
+        sizeof(Value); // 1 Value included in sizeof(RematerializedFrame)
+
+    void *buf = cx->calloc_(numBytes);
+    if (!buf)
+        return nullptr;
+
+    return new (buf) RematerializedFrame(cx, top, iter);
+}
+
+CallObject &
+RematerializedFrame::callObj() const
+{
+    JS_ASSERT(hasCallObj());
+
+    JSObject *scope = scopeChain();
+    while (!scope->is<CallObject>())
+        scope = scope->enclosingScope();
+    return scope->as<CallObject>();
+}
+
+void
+RematerializedFrame::mark(JSTracer *trc)
+{
+    gc::MarkScriptRoot(trc, &script_, "remat ion frame script");
+    gc::MarkObjectRoot(trc, &scopeChain_, "remat ion frame scope chain");
+    gc::MarkValueRoot(trc, &returnValue_, "remat ion frame return value");
+    gc::MarkValueRoot(trc, &thisValue_, "remat ion frame this");
+    gc::MarkValueRootRange(trc, slots_, slots_ + numActualArgs_ + script_->nfixed(),
+                           "remat ion frame stack");
+}
+
+void
+RematerializedFrame::dump()
+{
+    fprintf(stderr, " Rematerialized Optimized Frame%s\n", inlined() ? " (inlined)" : "");
+    if (isFunctionFrame()) {
+        fprintf(stderr, "  callee fun: ");
+#ifdef DEBUG
+        js_DumpObject(callee());
+#else
+        fprintf(stderr, "?\n");
+#endif
+    } else {
+        fprintf(stderr, "  global frame, no callee\n");
+    }
+
+    fprintf(stderr, "  file %s line %u\n",
+            script()->filename(), (unsigned) script()->lineno());
+
+    fprintf(stderr, "  script = %p\n", (void*) script());
+
+    if (isFunctionFrame()) {
+        fprintf(stderr, "  scope chain: ");
+#ifdef DEBUG
+        js_DumpObject(scopeChain());
+#else
+        fprintf(stderr, "?\n");
+#endif
+
+        if (hasArgsObj()) {
+            fprintf(stderr, "  args obj: ");
+#ifdef DEBUG
+            js_DumpObject(&argsObj());
+#else
+            fprintf(stderr, "?\n");
+#endif
+        }
+
+        fprintf(stderr, "  this: ");
+#ifdef DEBUG
+        js_DumpValue(thisValue());
+#else
+        fprintf(stderr, "?\n");
+#endif
+
+        for (unsigned i = 0; i < numActualArgs(); i++) {
+            if (i < numFormalArgs())
+                fprintf(stderr, "  formal (arg %d): ", i);
+            else
+                fprintf(stderr, "  overflown (arg %d): ", i);
+#ifdef DEBUG
+            js_DumpValue(argv()[i]);
+#else
+            fprintf(stderr, "?\n");
+#endif
+        }
+
+        for (unsigned i = 0; i < script()->nfixed(); i++) {
+            fprintf(stderr, "  local %d: ", i);
+#ifdef DEBUG
+            js_DumpValue(locals()[i]);
+#else
+            fprintf(stderr, "?\n");
+#endif
+        }
+    }
+
+    fputc('\n', stderr);
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit/RematerializedFrame.h
@@ -0,0 +1,165 @@
+/* -*- 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_RematerializedFrame_h
+#define jit_RematerializedFrame_h
+
+#ifdef JS_ION
+
+#include "jsfun.h"
+
+#include "jit/JitFrameIterator.h"
+
+#include "vm/Stack.h"
+
+namespace js {
+namespace jit {
+
+//
+// An optimized frame that has been rematerialized with values read out of
+// Snapshots.
+//
+class RematerializedFrame
+{
+    // See DebugScopes::updateLiveScopes.
+    bool prevUpToDate_;
+
+    // The fp of the top frame associated with this possibly inlined frame.
+    uint8_t *top_;
+
+    size_t frameNo_;
+    unsigned numActualArgs_;
+
+    JSScript *script_;
+    JSObject *scopeChain_;
+    ArgumentsObject *argsObj_;
+
+    Value returnValue_;
+    Value thisValue_;
+    Value slots_[1];
+
+    RematerializedFrame(JSContext *cx, uint8_t *top, InlineFrameIterator &iter);
+
+  public:
+    static RematerializedFrame *New(JSContext *cx, uint8_t *top, InlineFrameIterator &iter);
+
+    bool prevUpToDate() const {
+        return prevUpToDate_;
+    }
+    void setPrevUpToDate() {
+        prevUpToDate_ = true;
+    }
+
+    uint8_t *top() const {
+        return top_;
+    }
+    size_t frameNo() const {
+        return frameNo_;
+    }
+    bool inlined() const {
+        return frameNo_ > 0;
+    }
+
+    JSObject *scopeChain() const {
+        return scopeChain_;
+    }
+    bool hasCallObj() const {
+        return maybeFun() && fun()->isHeavyweight();
+    }
+    CallObject &callObj() const;
+
+    bool hasArgsObj() const {
+        return !!argsObj_;
+    }
+    ArgumentsObject &argsObj() const {
+        MOZ_ASSERT(hasArgsObj());
+        MOZ_ASSERT(script()->needsArgsObj());
+        return *argsObj_;
+    }
+
+    bool isFunctionFrame() const {
+        return !!script_->functionNonDelazifying();
+    }
+    bool isGlobalFrame() const {
+        return !isFunctionFrame();
+    }
+    bool isNonEvalFunctionFrame() const {
+        // Ion doesn't support eval frames.
+        return isFunctionFrame();
+    }
+
+    JSScript *script() const {
+        return script_;
+    }
+    JSFunction *fun() const {
+        MOZ_ASSERT(isFunctionFrame());
+        return script_->functionNonDelazifying();
+    }
+    JSFunction *maybeFun() const {
+        return isFunctionFrame() ? fun() : nullptr;
+    }
+    JSFunction *callee() const {
+        return fun();
+    }
+    Value calleev() const {
+        return ObjectValue(*fun());
+    }
+    Value &thisValue() {
+        return thisValue_;
+    }
+
+    unsigned numFormalArgs() const {
+        return maybeFun() ? fun()->nargs() : 0;
+    }
+    unsigned numActualArgs() const {
+        return numActualArgs_;
+    }
+
+    Value *argv() {
+        return slots_;
+    }
+    Value *locals() {
+        return slots_ + numActualArgs_;
+    }
+
+    Value &unaliasedVar(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) {
+        JS_ASSERT_IF(checkAliasing, !script()->varIsAliased(i));
+        JS_ASSERT(i < script()->nfixed());
+        return locals()[i];
+    }
+    Value &unaliasedLocal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) {
+        JS_ASSERT(i < script()->nfixed());
+#ifdef DEBUG
+        CheckLocalUnaliased(checkAliasing, script(), i);
+#endif
+        return locals()[i];
+    }
+    Value &unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) {
+        JS_ASSERT(i < numFormalArgs());
+        JS_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals() &&
+                                    !script()->formalIsAliased(i));
+        return argv()[i];
+    }
+    Value &unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING) {
+        JS_ASSERT(i < numActualArgs());
+        JS_ASSERT_IF(checkAliasing, !script()->argsObjAliasesFormals());
+        JS_ASSERT_IF(checkAliasing && i < numFormalArgs(), !script()->formalIsAliased(i));
+        return argv()[i];
+    }
+
+    Value returnValue() const {
+        return returnValue_;
+    }
+
+    void mark(JSTracer *trc);
+    void dump();
+};
+
+} // namespace jit
+} // namespace js
+
+#endif // JS_ION
+#endif // jit_RematerializedFrame_h
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -358,16 +358,30 @@ NewInitObjectWithClassPrototype(JSContex
         return nullptr;
 
     obj->setType(templateObject->type());
 
     return obj;
 }
 
 bool
+ArraySpliceDense(JSContext *cx, HandleObject obj, uint32_t start, uint32_t deleteCount)
+{
+    JS_ASSERT(obj->is<ArrayObject>());
+
+    JS::AutoValueArray<4> argv(cx);
+    argv[0].setUndefined();
+    argv[1].setObject(*obj);
+    argv[2].set(Int32Value(start));
+    argv[3].set(Int32Value(deleteCount));
+
+    return js::array_splice_impl(cx, 2, argv.begin(), false);
+}
+
+bool
 ArrayPopDense(JSContext *cx, HandleObject obj, MutableHandleValue rval)
 {
     JS_ASSERT(obj->is<ArrayObject>());
 
     AutoDetectInvalidation adi(cx, rval.address());
 
     JS::AutoValueArray<2> argv(cx);
     argv[0].setUndefined();
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -666,16 +666,18 @@ bool PopBlockScope(JSContext *cx, Baseli
 bool DebugLeaveBlock(JSContext *cx, BaselineFrame *frame, jsbytecode *pc);
 
 bool InitBaselineFrameForOsr(BaselineFrame *frame, InterpreterFrame *interpFrame,
                              uint32_t numStackValues);
 
 JSObject *CreateDerivedTypedObj(JSContext *cx, HandleObject descr,
                                 HandleObject owner, int32_t offset);
 
+bool ArraySpliceDense(JSContext *cx, HandleObject obj, uint32_t start, uint32_t deleteCount);
+
 bool Recompile(JSContext *cx);
 JSString *RegExpReplace(JSContext *cx, HandleString string, HandleObject regexp,
                         HandleString repl);
 JSString *StringReplace(JSContext *cx, HandleString string, HandleString pattern,
                         HandleString repl);
 
 bool SetDenseElement(JSContext *cx, HandleObject obj, int32_t index, HandleValue value,
                      bool strict);
--- a/js/src/jit/arm/BaselineCompiler-arm.cpp
+++ b/js/src/jit/arm/BaselineCompiler-arm.cpp
@@ -4,12 +4,12 @@
  * 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/arm/BaselineCompiler-arm.h"
 
 using namespace js;
 using namespace js::jit;
 
-BaselineCompilerARM::BaselineCompilerARM(JSContext *cx, TempAllocator &alloc, HandleScript script)
+BaselineCompilerARM::BaselineCompilerARM(JSContext *cx, TempAllocator