Merge inbound to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 26 Mar 2014 20:58:31 -0400
changeset 175584 441f5fd256e229b1839ff4bb7feb0e8fe5be7be0
parent 175583 da3198b95e6b9948133865375c393a0c72444efa (current diff)
parent 175428 3620f69143ddbc6f114f8ef1e126d99992e715cb (diff)
child 175585 dedeaf94545c6d79c1b482f1e9d2aa4ed8341d0f
child 175609 b7663848b8593f6a2144bf9a6d9235644889be71
child 175628 47dcbe2b1fd07f7ca3cca8d886e4db3300d7284e
push id41554
push userryanvm@gmail.com
push dateThu, 27 Mar 2014 03:09:09 +0000
treeherdermozilla-inbound@dedeaf94545c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone31.0a1
first release with
nightly linux32
441f5fd256e2 / 31.0a1 / 20140327030203 / files
nightly linux64
441f5fd256e2 / 31.0a1 / 20140327030203 / files
nightly mac
441f5fd256e2 / 31.0a1 / 20140327030203 / files
nightly win32
441f5fd256e2 / 31.0a1 / 20140327030203 / files
nightly win64
441f5fd256e2 / 31.0a1 / 20140327030203 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c.
b2g/app/b2g.js
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
js/src/builtin/Parallel.js
netwerk/protocol/http/huff_incoming.txt
netwerk/protocol/http/huff_outgoing.txt
testing/mochitest/runtests.py
testing/xpcshell/node-http2/doc/docco.css
testing/xpcshell/node-http2/doc/http.html
testing/xpcshell/node-http2/doc/index.html
testing/xpcshell/node-http2/doc/public/fonts/aller-bold.eot
testing/xpcshell/node-http2/doc/public/fonts/aller-bold.ttf
testing/xpcshell/node-http2/doc/public/fonts/aller-bold.woff
testing/xpcshell/node-http2/doc/public/fonts/aller-light.eot
testing/xpcshell/node-http2/doc/public/fonts/aller-light.ttf
testing/xpcshell/node-http2/doc/public/fonts/aller-light.woff
testing/xpcshell/node-http2/doc/public/fonts/novecento-bold.eot
testing/xpcshell/node-http2/doc/public/fonts/novecento-bold.ttf
testing/xpcshell/node-http2/doc/public/fonts/novecento-bold.woff
testing/xpcshell/node-http2/doc/public/stylesheets/normalize.css
testing/xpcshell/node-http2/node_modules/http2-protocol/doc/compressor.html
testing/xpcshell/node-http2/node_modules/http2-protocol/doc/connection.html
testing/xpcshell/node-http2/node_modules/http2-protocol/doc/docco.css
testing/xpcshell/node-http2/node_modules/http2-protocol/doc/endpoint.html
testing/xpcshell/node-http2/node_modules/http2-protocol/doc/flow.html
testing/xpcshell/node-http2/node_modules/http2-protocol/doc/framer.html
testing/xpcshell/node-http2/node_modules/http2-protocol/doc/index.html
testing/xpcshell/node-http2/node_modules/http2-protocol/doc/public/fonts/aller-bold.eot
testing/xpcshell/node-http2/node_modules/http2-protocol/doc/public/fonts/aller-bold.ttf
testing/xpcshell/node-http2/node_modules/http2-protocol/doc/public/fonts/aller-bold.woff
testing/xpcshell/node-http2/node_modules/http2-protocol/doc/public/fonts/aller-light.eot
testing/xpcshell/node-http2/node_modules/http2-protocol/doc/public/fonts/aller-light.ttf
testing/xpcshell/node-http2/node_modules/http2-protocol/doc/public/fonts/aller-light.woff
testing/xpcshell/node-http2/node_modules/http2-protocol/doc/public/fonts/novecento-bold.eot
testing/xpcshell/node-http2/node_modules/http2-protocol/doc/public/fonts/novecento-bold.ttf
testing/xpcshell/node-http2/node_modules/http2-protocol/doc/public/fonts/novecento-bold.woff
testing/xpcshell/node-http2/node_modules/http2-protocol/doc/public/stylesheets/normalize.css
testing/xpcshell/node-http2/node_modules/http2-protocol/doc/stream.html
xpcom/base/nsCycleCollector.cpp
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -64,16 +64,17 @@ pref("network.http.max-persistent-connec
 // spdy
 pref("network.http.spdy.push-allowance", 32768);
 
 // See bug 545869 for details on why these are set the way they are
 pref("network.buffer.cache.count", 24);
 pref("network.buffer.cache.size",  16384);
 
 // predictive actions
+pref("network.seer.enable", false); // disabled on b2g
 pref("network.seer.max-db-size", 2097152); // bytes
 pref("network.seer.preserve", 50); // percentage of seer data to keep when cleaning up
 
 /* session history */
 pref("browser.sessionhistory.max_total_viewers", 1);
 pref("browser.sessionhistory.max_entries", 50);
 
 /* session store */
--- a/browser/experiments/Experiments.jsm
+++ b/browser/experiments/Experiments.jsm
@@ -977,16 +977,17 @@ Experiments.ExperimentEntry.prototype = 
     "_enabled",
     "_manifestData",
     "_needsUpdate",
     "_randomValue",
     "_failedStart",
     "_name",
     "_description",
     "_homepageURL",
+    "_addonId",
     "_startDate",
     "_endDate",
   ]),
 
   DATE_KEYS: new Set([
     "_startDate",
     "_endDate",
   ]),
@@ -1045,55 +1046,66 @@ Experiments.ExperimentEntry.prototype = 
 
   /*
    * Initialize entry from the cache.
    * @param data The entry data from the cache.
    * @return boolean Whether initialization succeeded.
    */
   initFromCacheData: function (data) {
     for (let key of this.SERIALIZE_KEYS) {
-      if (!(key in data)) {
+      if (!(key in data) && !this.DATE_KEYS.has(key)) {
+        gLogger.error("ExperimentEntry::initFromCacheData() - missing required key " + key);
         return false;
       }
     };
 
     if (!this._isManifestDataValid(data._manifestData)) {
       return false;
     }
 
-    this._lastChangedDate = this._policy.now();
+    // Dates are restored separately from epoch ms, everything else is just
+    // copied in.
+
     this.SERIALIZE_KEYS.forEach(key => {
-      this[key] = data[key];
+      if (!this.DATE_KEYS.has(key)) {
+        this[key] = data[key];
+      }
     });
 
     this.DATE_KEYS.forEach(key => {
-      if (key in this) {
+      if (key in data) {
         let date = new Date();
-        date.setTime(this[key]);
+        date.setTime(data[key]);
         this[key] = date;
       }
     });
 
+    this._lastChangedDate = this._policy.now();
+
     return true;
   },
 
   /*
    * Returns a JSON representation of this object.
    */
   toJSON: function () {
     let obj = {};
 
+    // Dates are serialized separately as epoch ms.
+
     this.SERIALIZE_KEYS.forEach(key => {
       if (!this.DATE_KEYS.has(key)) {
         obj[key] = this[key];
       }
     });
 
     this.DATE_KEYS.forEach(key => {
-      obj[key] = this[key] ? this[key].getTime() : null;
+      if (this[key]) {
+        obj[key] = this[key].getTime();
+      }
     });
 
     return obj;
   },
 
   /*
    * Update from the experiment data from the manifest.
    * @param data The experiment data from the manifest.
--- a/browser/experiments/test/xpcshell/test_api.js
+++ b/browser/experiments/test/xpcshell/test_api.js
@@ -1257,16 +1257,17 @@ add_task(function* test_unexpectedUninst
   Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
   Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
   Assert.equal(list[0].active, true, "Experiment 1 should be active.");
 
   // Uninstall the addon through the addon manager instead of stopping it through
   // the experiments API.
 
   let success = yield uninstallAddon(EXPERIMENT1_ID);
+  yield experiments._mainTask;
   Assert.ok(success, "Addon should have been uninstalled.");
 
   yield experiments.notify();
 
   list = yield experiments.getExperiments();
   Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
   Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
   Assert.equal(list[0].active, false, "Experiment 1 should not be active anymore.");
new file mode 100644
--- /dev/null
+++ b/browser/experiments/test/xpcshell/test_cache.js
@@ -0,0 +1,271 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+Cu.import("resource://testing-common/httpd.js");
+XPCOMUtils.defineLazyModuleGetter(this, "Experiments",
+  "resource:///modules/experiments/Experiments.jsm");
+
+const FILE_MANIFEST            = "experiments.manifest";
+const PREF_EXPERIMENTS_ENABLED = "experiments.enabled";
+const PREF_LOGGING_LEVEL       = "experiments.logging.level";
+const PREF_LOGGING_DUMP        = "experiments.logging.dump";
+const PREF_MANIFEST_URI        = "experiments.manifest.uri";
+const PREF_FETCHINTERVAL       = "experiments.manifest.fetchIntervalSeconds";
+
+const MANIFEST_HANDLER         = "manifests/handler";
+
+const SEC_IN_ONE_DAY  = 24 * 60 * 60;
+const MS_IN_ONE_DAY   = SEC_IN_ONE_DAY * 1000;
+
+let gProfileDir          = null;
+let gHttpServer          = null;
+let gHttpRoot            = null;
+let gDataRoot            = null;
+let gReporter            = null;
+let gPolicy              = null;
+let gManifestObject      = null;
+let gManifestHandlerURI  = null;
+let gTimerScheduleOffset = -1;
+
+let gGlobalScope = this;
+function loadAddonManager() {
+  let ns = {};
+  Cu.import("resource://gre/modules/Services.jsm", ns);
+  let head = "../../../../toolkit/mozapps/extensions/test/xpcshell/head_addons.js";
+  let file = do_get_file(head);
+  let uri = ns.Services.io.newFileURI(file);
+  ns.Services.scriptloader.loadSubScript(uri.spec, gGlobalScope);
+  createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+  startupManager();
+}
+
+function run_test() {
+  run_next_test();
+}
+
+add_task(function* test_setup() {
+  loadAddonManager();
+  gProfileDir = do_get_profile();
+  yield removeCacheFile();
+
+  gHttpServer = new HttpServer();
+  gHttpServer.start(-1);
+  let port = gHttpServer.identity.primaryPort;
+  gHttpRoot = "http://localhost:" + port + "/";
+  gDataRoot = gHttpRoot + "data/";
+  gManifestHandlerURI = gHttpRoot + MANIFEST_HANDLER;
+  gHttpServer.registerDirectory("/data/", do_get_cwd());
+  gHttpServer.registerPathHandler("/" + MANIFEST_HANDLER, (request, response) => {
+    response.setStatusLine(null, 200, "OK");
+    response.write(JSON.stringify(gManifestObject));
+    response.processAsync();
+    response.finish();
+  });
+  do_register_cleanup(() => gHttpServer.stop(() => {}));
+
+  disableCertificateChecks();
+
+  Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
+  Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0);
+  Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true);
+  Services.prefs.setCharPref(PREF_MANIFEST_URI, gManifestHandlerURI);
+  Services.prefs.setIntPref(PREF_FETCHINTERVAL, 0);
+
+  gReporter = yield getReporter("json_payload_simple");
+  yield gReporter.collectMeasurements();
+  let payload = yield gReporter.getJSONPayload(true);
+
+  gPolicy = new Experiments.Policy();
+  patchPolicy(gPolicy, {
+    updatechannel: () => "nightly",
+    healthReportPayload: () => {},
+    oneshotTimer: (callback, timeout, thisObj, name) => gTimerScheduleOffset = timeout,
+  });
+});
+
+function checkExperimentListsEqual(list, list2) {
+  Assert.equal(list.length, list2.length, "Lists should have the same length.")
+
+  for (let i=0; i<list.length; ++i) {
+    for (let k of Object.keys(list[i])) {
+      Assert.equal(list[i][k], list2[i][k],
+                   "Field '" + k + "' should match for list entry " + i + ".");
+    }
+  }
+}
+
+function checkExperimentSerializations(experimentEntryIterator) {
+  for (let experiment of experimentEntryIterator) {
+    let experiment2 = new Experiments.ExperimentEntry(gPolicy);
+    let jsonStr = JSON.stringify(experiment.toJSON());
+    Assert.ok(experiment2.initFromCacheData(JSON.parse(jsonStr)),
+              "Should have initialized successfully from JSON serialization.");
+    Assert.equal(JSON.stringify(experiment), JSON.stringify(experiment2),
+                 "Object stringifications should match.");
+  }
+}
+
+// Set up an experiments instance and check if it is properly restored from cache.
+
+add_task(function* test_cache() {
+  // The manifest data we test with.
+
+  gManifestObject = {
+    "version": 1,
+    experiments: [
+      {
+        id:               EXPERIMENT1_ID,
+        xpiURL:           gDataRoot + EXPERIMENT1_XPI_NAME,
+        xpiHash:          EXPERIMENT1_XPI_SHA1,
+        maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
+        appName:          ["XPCShell"],
+        channel:          ["nightly"],
+      },
+      {
+        id:               EXPERIMENT2_ID,
+        xpiURL:           gDataRoot + EXPERIMENT2_XPI_NAME,
+        xpiHash:          EXPERIMENT2_XPI_SHA1,
+        maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
+        appName:          ["XPCShell"],
+        channel:          ["nightly"],
+      },
+      {
+        id:               EXPERIMENT3_ID,
+        xpiURL:           "https://inval.id/foo.xpi",
+        xpiHash:          "sha1:0000000000000000000000000000000000000000",
+        maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
+        appName:          ["XPCShell"],
+        channel:          ["nightly"],
+      },
+    ],
+  };
+
+  // Setup dates for the experiments.
+
+  let baseDate   = new Date(2014, 5, 1, 12);
+  let startDates = [];
+  let endDates   = [];
+
+  for (let i=0; i<gManifestObject.experiments.length; ++i) {
+    let experiment = gManifestObject.experiments[i];
+    startDates.push(futureDate(baseDate, (50 + (150 * i)) * MS_IN_ONE_DAY));
+    endDates  .push(futureDate(startDates[i], 50 * MS_IN_ONE_DAY));
+    experiment.startTime = dateToSeconds(startDates[i]);
+    experiment.endTime   = dateToSeconds(endDates[i]);
+  }
+
+  // Data to compare the result of Experiments.getExperiments() against.
+
+  let experimentListData = [
+    {
+      id: EXPERIMENT2_ID,
+      name: "Test experiment 2",
+      description: "And yet another experiment that experiments experimentally.",
+    },
+    {
+      id: EXPERIMENT1_ID,
+      name: EXPERIMENT1_NAME,
+      description: "Yet another experiment that experiments experimentally.",
+    },
+  ];
+
+  // Trigger update & re-init, clock set to before any activation.
+
+  let now = baseDate;
+  defineNow(gPolicy, now);
+
+  let experiments = new Experiments.Experiments(gPolicy);
+  yield experiments.updateManifest();
+  let list = yield experiments.getExperiments();
+  Assert.equal(list.length, 0, "Experiment list should be empty.");
+  checkExperimentSerializations(experiments._experiments.values());
+
+  yield experiments.uninit();
+  experiments = new Experiments.Experiments(gPolicy);
+
+  yield experiments._run();
+  list = yield experiments.getExperiments();
+  Assert.equal(list.length, 0, "Experiment list should be empty.");
+  checkExperimentSerializations(experiments._experiments.values());
+
+  // Re-init, clock set for experiment 1 to start.
+
+  now = futureDate(startDates[0], 5 * MS_IN_ONE_DAY);
+  defineNow(gPolicy, now);
+
+  yield experiments.uninit();
+  experiments = new Experiments.Experiments(gPolicy);
+  yield experiments._run();
+
+  list = yield experiments.getExperiments();
+  Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
+
+  experimentListData[1].active = true;
+  experimentListData[1].endDate = now.getTime() + 10 * MS_IN_ONE_DAY;
+  checkExperimentListsEqual(experimentListData.slice(1), list);
+  checkExperimentSerializations(experiments._experiments.values());
+
+  // Re-init, clock set for experiment 1 to stop.
+
+  now = futureDate(now, 20 * MS_IN_ONE_DAY);
+  defineNow(gPolicy, now);
+
+  yield experiments.uninit();
+  experiments = new Experiments.Experiments(gPolicy);
+  yield experiments._run();
+
+  list = yield experiments.getExperiments();
+  Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
+
+  experimentListData[1].active = false;
+  experimentListData[1].endDate = now.getTime();
+  checkExperimentListsEqual(experimentListData.slice(1), list);
+  checkExperimentSerializations(experiments._experiments.values());
+
+  // Re-init, clock set for experiment 2 to start.
+
+  now = futureDate(startDates[1], 20 * MS_IN_ONE_DAY);
+  defineNow(gPolicy, now);
+
+  yield experiments.uninit();
+  experiments = new Experiments.Experiments(gPolicy);
+  yield experiments._run();
+
+  list = yield experiments.getExperiments();
+  Assert.equal(list.length, 2, "Experiment list should have 2 entries.");
+
+  experimentListData[0].active = true;
+  experimentListData[0].endDate = now.getTime() + 10 * MS_IN_ONE_DAY;
+  checkExperimentListsEqual(experimentListData, list);
+  checkExperimentSerializations(experiments._experiments.values());
+
+  // Re-init, clock set for experiment 2 to stop.
+
+  now = futureDate(now, 20 * MS_IN_ONE_DAY);
+  defineNow(gPolicy, now);
+
+  yield experiments.uninit();
+  experiments = new Experiments.Experiments(gPolicy);
+  yield experiments._run();
+
+  list = yield experiments.getExperiments();
+  Assert.equal(list.length, 2, "Experiment list should have 2 entries.");
+
+  experimentListData[0].active = false;
+  experimentListData[0].endDate = now.getTime();
+  checkExperimentListsEqual(experimentListData, list);
+  checkExperimentSerializations(experiments._experiments.values());
+
+  // Cleanup.
+
+  yield experiments.disableExperiment();
+  yield experiments.uninit();
+  yield removeCacheFile();
+});
+
+add_task(function* shutdown() {
+  yield gReporter._shutdown();
+  yield removeCacheFile();
+});
--- a/browser/experiments/test/xpcshell/xpcshell.ini
+++ b/browser/experiments/test/xpcshell/xpcshell.ini
@@ -5,12 +5,13 @@ firefox-appdir = browser
 support-files =
   experiments_1.manifest
   ../experiment-1.xpi
   ../experiment-1a.xpi
   ../experiment-2.xpi
 
 [test_activate.js]
 [test_api.js]
+[test_cache.js]
 [test_conditions.js]
 [test_fetch.js]
 [test_telemetry.js]
 [test_healthreport.js]
--- a/browser/installer/windows/Makefile.in
+++ b/browser/installer/windows/Makefile.in
@@ -95,13 +95,16 @@ endif
 	  $(srcdir)/nsis/defines.nsi.in -o $(CONFIG_DIR)/defines.nsi)
 	$(PYTHON) $(topsrcdir)/toolkit/mozapps/installer/windows/nsis/preprocess-locale.py \
 	  --preprocess-locale $(topsrcdir) \
 	  $(PPL_LOCALE_ARGS) $(AB_CD) $(CONFIG_DIR)
 	$(PYTHON) $(topsrcdir)/toolkit/mozapps/installer/windows/nsis/preprocess-locale.py \
 	  --preprocess-single-file $(topsrcdir) \
 	  $(PPL_LOCALE_ARGS) $(CONFIG_DIR) \
 	  nsisstrings.properties nsisstrings.nlf
+	$(PYTHON) $(topsrcdir)/toolkit/mozapps/installer/windows/nsis/preprocess-locale.py \
+	  --convert-utf8-utf16le \
+	  $(srcdir)/nsis/oneoff_en-US.nsh $(CONFIG_DIR)/oneoff_en-US.nsh
 
 GARBARGE_DIRS += instgen
 
 include $(topsrcdir)/config/rules.mk
 include $(topsrcdir)/toolkit/mozapps/installer/windows/nsis/makensis.mk
--- a/browser/installer/windows/nsis/installer.nsi
+++ b/browser/installer/windows/nsis/installer.nsi
@@ -16,16 +16,24 @@
 ; 7-Zip provides better compression than the lzma from NSIS so we add the files
 ; uncompressed and use 7-Zip to create a SFX archive of it
 SetDatablockOptimize on
 SetCompress off
 CRCCheck on
 
 RequestExecutionLevel user
 
+; The commands inside this ifdef require NSIS 3.0a2 or greater so the ifdef can
+; be removed after we require NSIS 3.0a2 or greater.
+!ifdef NSIS_PACKEDVERSION
+  Unicode true
+  ManifestSupportedOS all
+  ManifestDPIAware true
+!endif
+
 !addplugindir ./
 
 Var TmpVal
 Var InstallType
 Var AddStartMenuSC
 Var AddQuickLaunchSC
 Var AddDesktopSC
 Var InstallMaintenanceService
@@ -1086,19 +1094,23 @@ Function .onInit
   System::Call 'kernel32::SetDllDirectoryW(w "")'
 
   StrCpy $PageName ""
   StrCpy $LANGUAGE 0
   ${SetBrandNameVars} "$EXEDIR\core\distribution\setup.ini"
 
   ${InstallOnInitCommon} "$(WARN_MIN_SUPPORTED_OS_MSG)"
 
+; The commands inside this ifndef are needed prior to NSIS 3.0a2 and can be
+; removed after we require NSIS 3.0a2 or greater.
+!ifndef NSIS_PACKEDVERSION
   ${If} ${AtLeastWinVista}
     System::Call 'user32::SetProcessDPIAware()'
   ${EndIf}
+!endif
 
   !insertmacro InitInstallOptionsFile "options.ini"
   !insertmacro InitInstallOptionsFile "shortcuts.ini"
   !insertmacro InitInstallOptionsFile "components.ini"
   !insertmacro InitInstallOptionsFile "summary.ini"
 
   WriteINIStr "$PLUGINSDIR\options.ini" "Settings" NumFields "5"
 
--- a/browser/installer/windows/nsis/maintenanceservice_installer.nsi
+++ b/browser/installer/windows/nsis/maintenanceservice_installer.nsi
@@ -7,16 +7,25 @@
 
 ; 7-Zip provides better compression than the lzma from NSIS so we add the files
 ; uncompressed and use 7-Zip to create a SFX archive of it
 SetDatablockOptimize on
 SetCompress off
 CRCCheck on
 
 RequestExecutionLevel admin
+
+; The commands inside this ifdef require NSIS 3.0a2 or greater so the ifdef can
+; be removed after we require NSIS 3.0a2 or greater.
+!ifdef NSIS_PACKEDVERSION
+  Unicode true
+  ManifestSupportedOS all
+  ManifestDPIAware true
+!endif
+
 !addplugindir ./
 
 ; Variables
 Var TempMaintServiceName
 Var BrandFullNameDA
 Var BrandFullName
 
 ; Other included files may depend upon these includes!
@@ -127,19 +136,23 @@ Function .onInit
   ${EndUnless}
 FunctionEnd
 
 Function un.onInit
   ; Remove the current exe directory from the search order.
   ; This only effects LoadLibrary calls and not implicitly loaded DLLs.
   System::Call 'kernel32::SetDllDirectoryW(w "")'
 
+; The commands inside this ifndef are needed prior to NSIS 3.0a2 and can be
+; removed after we require NSIS 3.0a2 or greater.
+!ifndef NSIS_PACKEDVERSION
   ${If} ${AtLeastWinVista}
     System::Call 'user32::SetProcessDPIAware()'
   ${EndIf}
+!endif
 
   StrCpy $BrandFullNameDA "${MaintFullName}"
   StrCpy $BrandFullName "${MaintFullName}"
 FunctionEnd
 
 Section "MaintenanceService"
   AllowSkipFiles off
 
new file mode 100644
--- /dev/null
+++ b/browser/installer/windows/nsis/oneoff_en-US.nsh
@@ -0,0 +1,12 @@
+# 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/.
+
+; Custom strings for en-US. This is not in the locale directory so these strings
+; aren't translated.
+!define INDENT "     "
+!define INTRO_BLURB "Thanks for choosing $BrandFullName. We’re not just designed to be different, we’re different by design."
+!define INSTALL_BLURB1 "You're about to enjoy the very latest in speed, flexibility and security so you're always in control."
+!define INSTALL_BLURB2 "And you're joining a global community of users, contributors and developers working to make the best browser in the world."
+!define INSTALL_BLURB3 "You even get a haiku:$\n${INDENT}Proudly non-profit$\n${INDENT}Free to innovate for you$\n${INDENT}And a better Web"
+!undef INDENT
--- a/browser/installer/windows/nsis/stub.nsi
+++ b/browser/installer/windows/nsis/stub.nsi
@@ -13,16 +13,24 @@
 !verbose 3
 
 SetDatablockOptimize on
 SetCompress off
 CRCCheck on
 
 RequestExecutionLevel user
 
+; The commands inside this ifdef require NSIS 3.0a2 or greater so the ifdef can
+; be removed after we require NSIS 3.0a2 or greater.
+!ifdef NSIS_PACKEDVERSION
+  Unicode true
+  ManifestSupportedOS all
+  ManifestDPIAware true
+!endif
+
 !addplugindir ./
 
 Var Dialog
 Var Progressbar
 Var LabelDownloading
 Var LabelInstalling
 Var LabelFreeSpace
 Var CheckboxSetAsDefault
@@ -268,22 +276,17 @@ ChangeUI all "nsisui.exe"
 !else
   LoadLanguageFile "locale.nlf"
 !endif
 
 !include "nsisstrings.nlf"
 
 !if "${AB_CD}" == "en-US"
   ; Custom strings for en-US. This is done here so they aren't translated.
-  !define INDENT "     "
-  !define INTRO_BLURB "Thanks for choosing $BrandFullName. We’re not just designed to be different, we’re different by design."
-  !define INSTALL_BLURB1 "You're about to enjoy the very latest in speed, flexibility and security so you're always in control."
-  !define INSTALL_BLURB2 "And you're joining a global community of users, contributors and developers working to make the best browser in the world."
-  !define INSTALL_BLURB3 "You even get a haiku:$\n${INDENT}Proudly non-profit$\n${INDENT}Free to innovate for you$\n${INDENT}And a better Web"
-  !undef INDENT
+  !include oneoff_en-US.nsh
 !else
   !define INTRO_BLURB "$(INTRO_BLURB1)"
   !define INSTALL_BLURB1 "$(INSTALL_BLURB1)"
   !define INSTALL_BLURB2 "$(INSTALL_BLURB2)"
   !define INSTALL_BLURB3 "$(INSTALL_BLURB3)"
 !endif
 
 Caption "$(WIN_CAPTION)"
@@ -342,19 +345,23 @@ Function .onInit
       Quit
     ${EndIf}
   ${EndUnless}
 !endif
 
   ; Require elevation if the user can elevate
   ${ElevateUAC}
 
+; The commands inside this ifndef are needed prior to NSIS 3.0a2 and can be
+; removed after we require NSIS 3.0a2 or greater.
+!ifndef NSIS_PACKEDVERSION
   ${If} ${AtLeastWinVista}
     System::Call 'user32::SetProcessDPIAware()'
   ${EndIf}
+!endif
 
   SetShellVarContext all ; Set SHCTX to HKLM
   ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $R9
 
   ${If} "$R9" == "false"
     SetShellVarContext current ; Set SHCTX to HKCU
     ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $R9
   ${EndIf}
--- a/browser/installer/windows/nsis/uninstaller.nsi
+++ b/browser/installer/windows/nsis/uninstaller.nsi
@@ -14,16 +14,24 @@
 ; 7-Zip provides better compression than the lzma from NSIS so we add the files
 ; uncompressed and use 7-Zip to create a SFX archive of it
 SetDatablockOptimize on
 SetCompress off
 CRCCheck on
 
 RequestExecutionLevel user
 
+; The commands inside this ifdef require NSIS 3.0a2 or greater so the ifdef can
+; be removed after we require NSIS 3.0a2 or greater.
+!ifdef NSIS_PACKEDVERSION
+  Unicode true
+  ManifestSupportedOS all
+  ManifestDPIAware true
+!endif
+
 !addplugindir ./
 
 ; On Vista and above attempt to elevate Standard Users in addition to users that
 ; are a member of the Administrators group.
 !define NONADMIN_ELEVATE
 
 ; prevents compiling of the reg write logging.
 !define NO_LOG
@@ -690,19 +698,23 @@ Function un.onInit
   ; Remove the current exe directory from the search order.
   ; This only effects LoadLibrary calls and not implicitly loaded DLLs.
   System::Call 'kernel32::SetDllDirectoryW(w "")'
 
   StrCpy $LANGUAGE 0
 
   ${un.UninstallUnOnInitCommon}
 
+; The commands inside this ifndef are needed prior to NSIS 3.0a2 and can be
+; removed after we require NSIS 3.0a2 or greater.
+!ifndef NSIS_PACKEDVERSION
   ${If} ${AtLeastWinVista}
     System::Call 'user32::SetProcessDPIAware()'
   ${EndIf}
+!endif
 
   !insertmacro InitInstallOptionsFile "unconfirm.ini"
 FunctionEnd
 
 Function .onGUIEnd
   ${OnEndCommon}
 FunctionEnd
 
--- a/build/autoconf/compiler-opts.m4
+++ b/build/autoconf/compiler-opts.m4
@@ -94,19 +94,25 @@ dnl ====================================
 dnl =
 dnl = Debugging Options
 dnl =
 dnl ========================================================
 AC_DEFUN([MOZ_DEBUGGING_OPTS],
 [
 dnl Debug info is ON by default.
 if test -z "$MOZ_DEBUG_FLAGS"; then
-  MOZ_DEBUG_FLAGS="-g"
+  if test -n "$_MSC_VER"; then
+    MOZ_DEBUG_FLAGS="-Zi"
+  else
+    MOZ_DEBUG_FLAGS="-g"
+  fi
 fi
 
+AC_SUBST(MOZ_DEBUG_FLAGS)
+
 MOZ_ARG_ENABLE_STRING(debug,
 [  --enable-debug[=DBG]    Enable building with developer debug info
                            (using compiler flags DBG)],
 [ if test "$enableval" != "no"; then
     MOZ_DEBUG=1
     if test -n "$enableval" -a "$enableval" != "yes"; then
         MOZ_DEBUG_FLAGS=`echo $enableval | sed -e 's|\\\ | |g'`
         _MOZ_DEBUG_FLAGS_SET=1
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -444,34 +444,37 @@ class Automation(object):
     sslTunnelConfig.close()
 
     # Pre-create the certification database for the profile
     env = self.environment(xrePath = xrePath)
     certutil = os.path.join(utilityPath, "certutil" + self.BIN_SUFFIX)
     pk12util = os.path.join(utilityPath, "pk12util" + self.BIN_SUFFIX)
 
     status = self.Process([certutil, "-N", "-d", profileDir, "-f", pwfilePath], env = env).wait()
+    automationutils.printstatus(status, "certutil")
     if status != 0:
       return status
 
     # Walk the cert directory and add custom CAs and client certs
     files = os.listdir(certPath)
     for item in files:
       root, ext = os.path.splitext(item)
       if ext == ".ca":
         trustBits = "CT,,"
         if root.endswith("-object"):
           trustBits = "CT,,CT"
-        self.Process([certutil, "-A", "-i", os.path.join(certPath, item),
+        status = self.Process([certutil, "-A", "-i", os.path.join(certPath, item),
                     "-d", profileDir, "-f", pwfilePath, "-n", root, "-t", trustBits],
                     env = env).wait()
+        automationutils.printstatus(status, "certutil")
       if ext == ".client":
-        self.Process([pk12util, "-i", os.path.join(certPath, item), "-w",
+        status = self.Process([pk12util, "-i", os.path.join(certPath, item), "-w",
                     pwfilePath, "-d", profileDir], 
                     env = env).wait()
+        automationutils.printstatus(status, "pk12util")
 
     os.unlink(pwfilePath)
     return 0
 
   def environment(self, env=None, xrePath=None, crashreporter=True, debugger=False, dmdPath=None):
     if xrePath == None:
       xrePath = self.DIST_BIN
     if env == None:
@@ -649,18 +652,21 @@ class Automation(object):
     if self.CRASHREPORTER and not debuggerInfo:
       if not self.IS_WIN32:
         # ABRT will get picked up by Breakpad's signal handler
         os.kill(processPID, signal.SIGABRT)
         return
       else:
         # We should have a "crashinject" program in our utility path
         crashinject = os.path.normpath(os.path.join(utilityPath, "crashinject.exe"))
-        if os.path.exists(crashinject) and subprocess.Popen([crashinject, str(processPID)]).wait() == 0:
-          return
+        if os.path.exists(crashinject):
+          status = subprocess.Popen([crashinject, str(processPID)]).wait()
+          automationutils.printstatus(status, "crashinject")
+          if status == 0:
+            return
     self.log.info("Can't trigger Breakpad, just killing process")
     self.killPid(processPID)
 
   def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath):
     """ Look for timeout or crashes and return the status after the process terminates """
     stackFixerProcess = None
     stackFixerFunction = None
     didTimeout = False
@@ -723,22 +729,24 @@ class Automation(object):
         if line:
           self.log.info(line.rstrip().decode("UTF-8", "ignore"))
         self.log.info("TEST-UNEXPECTED-FAIL | %s | application timed out after %d seconds with no output", self.lastTestSeen, int(timeout))
         if browserProcessId == -1:
           browserProcessId = proc.pid
         self.killAndGetStack(browserProcessId, utilityPath, debuggerInfo)
 
     status = proc.wait()
+    automationutils.printstatus(status, "Main app process")
     if status == 0:
       self.lastTestSeen = "Main app process exited normally"
     if status != 0 and not didTimeout and not hitMaxTime:
       self.log.info("TEST-UNEXPECTED-FAIL | %s | Exited with code %d during test run", self.lastTestSeen, status)
     if stackFixerProcess is not None:
       fixerStatus = stackFixerProcess.wait()
+      automationutils.printstatus(status, "stackFixerProcess")
       if fixerStatus != 0 and not didTimeout and not hitMaxTime:
         self.log.info("TEST-UNEXPECTED-FAIL | automation.py | Stack fixer process exited with code %d during test run", fixerStatus)
     return status
 
   def buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs):
     """ build the application command line """
 
     cmd = os.path.abspath(app)
--- a/build/automationutils.py
+++ b/build/automationutils.py
@@ -2,18 +2,20 @@
 # 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/.
 
 from __future__ import with_statement
 import glob, logging, os, platform, shutil, subprocess, sys, tempfile, urllib2, zipfile
 import base64
 import re
+import os
 from urlparse import urlparse
 from operator import itemgetter
+import signal
 
 try:
   import mozinfo
 except ImportError:
   # Stub out fake mozinfo since this is not importable on Android 4.0 Opt.
   # This should be fixed; see
   # https://bugzilla.mozilla.org/show_bug.cgi?id=650881
   mozinfo = type('mozinfo', (), dict(info={}))()
@@ -150,16 +152,53 @@ class ZipFileReader(object):
 
 log = logging.getLogger()
 
 def isURL(thing):
   """Return True if |thing| looks like a URL."""
   # We want to download URLs like http://... but not Windows paths like c:\...
   return len(urlparse(thing).scheme) >= 2
 
+# Python does not provide strsignal() even in the very latest 3.x.
+# This is a reasonable fake.
+def strsig(n):
+  # Signal numbers run 0 through NSIG-1; an array with NSIG members
+  # has exactly that many slots
+  _sigtbl = [None]*signal.NSIG
+  for k in dir(signal):
+    if k.startswith("SIG") and not k.startswith("SIG_") and k != "SIGCLD" and k != "SIGPOLL":
+      _sigtbl[getattr(signal, k)] = k
+  # Realtime signals mostly have no names
+  if hasattr(signal, "SIGRTMIN") and hasattr(signal, "SIGRTMAX"):
+    for r in range(signal.SIGRTMIN+1, signal.SIGRTMAX+1):
+      _sigtbl[r] = "SIGRTMIN+" + str(r - signal.SIGRTMIN)
+  # Fill in any remaining gaps
+  for i in range(signal.NSIG):
+    if _sigtbl[i] is None:
+      _sigtbl[i] = "unrecognized signal, number " + str(i)
+  if n < 0 or n >= signal.NSIG:
+    return "out-of-range signal, number "+str(n)
+  return _sigtbl[n]
+
+def printstatus(status, name = ""):
+  # 'status' is the exit status
+  if os.name != 'posix':
+    # Windows error codes are easier to look up if printed in hexadecimal
+    if status < 0:
+      status += 2**32
+    print "TEST-INFO | %s: exit status %x\n" % (name, status)
+  elif os.WIFEXITED(status):
+    print "TEST-INFO | %s: exit %d\n" % (name, os.WEXITSTATUS(status))
+  elif os.WIFSIGNALED(status):
+    # The python stdlib doesn't appear to have strsignal(), alas
+    print "TEST-INFO | {}: killed by {}".format(name,strsig(os.WTERMSIG(status)))
+  else:
+    # This is probably a can't-happen condition on Unix, but let's be defensive
+    print "TEST-INFO | %s: undecodable exit status %04x\n" % (name, status)
+
 def addCommonOptions(parser, defaults={}):
   parser.add_option("--xre-path",
                     action = "store", type = "string", dest = "xrePath",
                     # individual scripts will set a sane default
                     default = None,
                     help = "absolute path to directory containing XRE (probably xulrunner)")
   if 'SYMBOLS_PATH' not in defaults:
     defaults['SYMBOLS_PATH'] = None
@@ -494,41 +533,41 @@ def environment(xrePath, env=None, crash
 def dumpScreen(utilityPath):
   """dumps a screenshot of the entire screen to a directory specified by
   the MOZ_UPLOAD_DIR environment variable"""
   import mozfile
 
   # Need to figure out which OS-dependent tool to use
   if mozinfo.isUnix:
     utility = [os.path.join(utilityPath, "screentopng")]
+    utilityname = "screentopng"
   elif mozinfo.isMac:
     utility = ['/usr/sbin/screencapture', '-C', '-x', '-t', 'png']
+    utilityname = "screencapture"
   elif mozinfo.isWin:
     utility = [os.path.join(utilityPath, "screenshot.exe")]
+    utilityname = "screenshot"
 
   # Get dir where to write the screenshot file
   parent_dir = os.environ.get('MOZ_UPLOAD_DIR', None)
   if not parent_dir:
     log.info('Failed to retrieve MOZ_UPLOAD_DIR env var')
     return
 
   # Run the capture
   try:
     tmpfd, imgfilename = tempfile.mkstemp(prefix='mozilla-test-fail-screenshot_', suffix='.png', dir=parent_dir)
     os.close(tmpfd)
     returncode = subprocess.call(utility + [imgfilename])
+    printstatus(returncode, utilityname)
   except OSError, err:
     log.info("Failed to start %s for screenshot: %s",
              utility[0], err.strerror)
     return
 
-  # Check whether the capture utility ran successfully
-  if returncode != 0:
-    log.info("%s exited with code %d", utility, returncode)
-
 class ShutdownLeaks(object):
   """
   Parses the mochitest run log when running a debug build, assigns all leaked
   DOM windows (that are still around after test suite shutdown, despite running
   the GC) to the tests that created them and prints leak statistics.
   """
 
   def __init__(self, logger):
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -226,20 +226,22 @@ ifndef GNU_CC
 # without SP1, don't do parallel builds.
 #
 # The final PDB for libraries and programs is created by the linker and uses
 # a different name from the single PDB file created by the compiler. See
 # bug 462740.
 #
 
 ifdef SIMPLE_PROGRAMS
-COMPILE_PDBFILE = $(basename $(@F)).pdb
+COMPILE_PDB_FLAG ?= -Fd$(basename $(@F)).pdb
 else
-COMPILE_PDBFILE = generated.pdb
+COMPILE_PDB_FLAG ?= -Fdgenerated.pdb
 endif
+COMPILE_CFLAGS += $(COMPILE_PDB_FLAG)
+COMPILE_CXXFLAGS += $(COMPILE_PDB_FLAG)
 
 LINK_PDBFILE = $(basename $(@F)).pdb
 ifdef MOZ_DEBUG
 CODFILE=$(basename $(@F)).cod
 endif
 
 ifdef DEFFILE
 OS_LDFLAGS += -DEF:$(call normalizepath,$(DEFFILE))
@@ -260,16 +262,19 @@ endif # WINNT
 ifeq ($(SOLARIS_SUNPRO_CXX),1)
 ifeq (86,$(findstring 86,$(OS_TEST)))
 OS_LDFLAGS += -M $(topsrcdir)/config/solaris_ia32.map
 endif # x86
 endif # Solaris Sun Studio C++
 
 ifeq ($(HOST_OS_ARCH),WINNT)
 HOST_PDBFILE=$(basename $(@F)).pdb
+HOST_PDB_FLAG ?= -Fd$(HOST_PDBFILE)
+HOST_CFLAGS += $(HOST_PDB_FLAG)
+HOST_CXXFLAGS += $(HOST_PDB_FLAG)
 endif
 
 # Don't build SIMPLE_PROGRAMS during the MOZ_PROFILE_GENERATE pass
 ifdef MOZ_PROFILE_GENERATE
 EXCLUDED_OBJS := $(SIMPLE_PROGRAMS:$(BIN_SUFFIX)=.$(OBJ_SUFFIX))
 SIMPLE_PROGRAMS :=
 endif
 
--- a/configure.in
+++ b/configure.in
@@ -66,17 +66,17 @@ GTK3_VERSION=3.0.0
 WINDRES_VERSION=2.14.90
 W32API_VERSION=3.14
 GNOMEVFS_VERSION=2.0
 GNOMEUI_VERSION=2.2.0
 GCONF_VERSION=1.2.1
 GIO_VERSION=2.20
 STARTUP_NOTIFICATION_VERSION=0.8
 DBUS_VERSION=0.60
-SQLITE_VERSION=3.8.4.1
+SQLITE_VERSION=3.8.4.2
 
 MSMANIFEST_TOOL=
 
 dnl Set various checks
 dnl ========================================================
 MISSING_X=
 AC_PROG_AWK
 
@@ -457,24 +457,24 @@ case "$target" in
             AC_MSG_ERROR([\$(CXX) test failed.  You must have MS VC++ in your path to build.]) )
         AC_LANG_RESTORE
 
         changequote(,)
         _MSVC_VER_FILTER='s|.*[^!-~]([0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?).*|\1|p'
         changequote([,])
 
         # Determine compiler version
-        CC_VERSION=`"${CC}" -v 2>&1 | sed -nre "$_MSVC_VER_FILTER"`
+        CC_VERSION=`${CC} -v 2>&1 | sed -nre "$_MSVC_VER_FILTER"`
         _CC_MAJOR_VERSION=`echo ${CC_VERSION} | $AWK -F\. '{ print $1 }'`
         _CC_MINOR_VERSION=`echo ${CC_VERSION} | $AWK -F\. '{ print $2 }'`
         _CC_RELEASE=`echo ${CC_VERSION} | $AWK -F\. '{ print $3 }'`
         _CC_BUILD=`echo ${CC_VERSION} | $AWK -F\. '{ print $4 }'`
         _MSC_VER=${_CC_MAJOR_VERSION}${_CC_MINOR_VERSION}
 
-        CXX_VERSION=`"${CXX}" -v 2>&1 | sed -nre "$_MSVC_VER_FILTER"`
+        CXX_VERSION=`${CXX} -v 2>&1 | sed -nre "$_MSVC_VER_FILTER"`
         _CXX_MAJOR_VERSION=`echo ${CXX_VERSION} | $AWK -F\. '{ print $1 }'`
 
         if test "$_CC_MAJOR_VERSION" != "$_CXX_MAJOR_VERSION"; then
             AC_MSG_ERROR([The major versions of \$CC and \$CXX do not match.])
         fi
 
         AC_DEFINE(_CRT_SECURE_NO_WARNINGS)
         AC_DEFINE(_CRT_NONSTDC_NO_WARNINGS)
@@ -1738,17 +1738,17 @@ fi
 dnl ========================================================
 dnl System overrides of the defaults for host
 dnl ========================================================
 case "$host" in
 *mingw*)
     if test -n "$_WIN32_MSVC"; then
         HOST_AR=lib
         HOST_AR_FLAGS='-NOLOGO -OUT:$@'
-        HOST_CFLAGS="$HOST_CFLAGS -TC -nologo -Fd\$(HOST_PDBFILE)"
+        HOST_CFLAGS="$HOST_CFLAGS -TC -nologo"
         HOST_RANLIB='echo ranlib'
     else
         HOST_CFLAGS="$HOST_CFLAGS -mwindows"
     fi
     HOST_CFLAGS="$HOST_CFLAGS -DXP_WIN32 -DXP_WIN -DWIN32 -D_WIN32 -DNO_X11 -D_CRT_SECURE_NO_WARNINGS"
     HOST_NSPR_MDCPUCFG='\"md/_winnt.cfg\"'
     HOST_OPTIMIZE_FLAGS="${HOST_OPTIMIZE_FLAGS=-O2}"
     HOST_BIN_SUFFIX=.exe
@@ -2120,18 +2120,18 @@ ia64*-hpux*)
         MKSHLIB='$(LD) -NOLOGO -DLL -OUT:$@ -PDB:$(LINK_PDBFILE) $(DSO_LDOPTS)'
         MKCSHLIB='$(LD) -NOLOGO -DLL -OUT:$@ -PDB:$(LINK_PDBFILE) $(DSO_LDOPTS)'
         MKSHLIB_FORCE_ALL=
         MKSHLIB_UNFORCE_ALL=
         DSO_LDOPTS=-SUBSYSTEM:WINDOWS
         _USE_CPP_INCLUDE_FLAG=1
         _DEFINES_CFLAGS='-FI $(DEPTH)/dist/include/mozilla-config.h -DMOZILLA_CLIENT'
         _DEFINES_CXXFLAGS='-FI $(DEPTH)/dist/include/mozilla-config.h -DMOZILLA_CLIENT'
-        CFLAGS="$CFLAGS -W3 -Gy -Fd\$(COMPILE_PDBFILE)"
-        CXXFLAGS="$CXXFLAGS -W3 -Gy -Fd\$(COMPILE_PDBFILE)"
+        CFLAGS="$CFLAGS -W3 -Gy"
+        CXXFLAGS="$CXXFLAGS -W3 -Gy"
         if test "$_CC_SUITE" -ge "12"; then
             dnl VS2013+ requires -FS when parallel building by make -jN.
             dnl If nothing, compiler sometimes causes C1041 error.
             dnl
             dnl Visual Studio 2013 supports -Gw flags
             dnl http://blogs.msdn.com/b/vcblog/archive/2013/09/11/introducing-gw-compiler-switch.aspx
             CFLAGS="$CFLAGS -FS -Gw"
             CXXFLAGS="$CXXFLAGS -FS -Gw"
@@ -2153,17 +2153,16 @@ ia64*-hpux*)
         # MSVC warning C4819 warns some UTF-8 characters (e.g. copyright sign)
         # on non-Western system locales even if it is in a comment.
         CFLAGS="$CFLAGS -wd4244 -wd4819"
         CXXFLAGS="$CXXFLAGS -wd4251 -wd4244 -wd4345 -wd4351 -wd4482 -wd4800 -wd4819"
         # make 'foo == bar;' error out
         CFLAGS="$CFLAGS -we4553"
         CXXFLAGS="$CXXFLAGS -we4553"
         LIBS="$LIBS kernel32.lib user32.lib gdi32.lib winmm.lib wsock32.lib advapi32.lib secur32.lib netapi32.lib"
-        MOZ_DEBUG_FLAGS='-Zi'
         MOZ_DEBUG_LDFLAGS='-DEBUG -DEBUGTYPE:CV'
         WARNINGS_AS_ERRORS='-WX'
         MOZ_OPTIMIZE_FLAGS='-O1'
         MOZ_FIX_LINK_PATHS=
         MOZ_COMPONENT_NSPR_LIBS='$(NSPR_LIBS)'
         LDFLAGS="$LDFLAGS -LARGEADDRESSAWARE -NXCOMPAT"
         if test -z "$DEVELOPER_OPTIONS"; then
             LDFLAGS="$LDFLAGS -RELEASE"
@@ -8415,17 +8414,16 @@ AC_SUBST(MOZ_UPDATE_XTERM)
 AC_SUBST(MOZ_AUTH_EXTENSION)
 AC_SUBST(MOZ_PERMISSIONS)
 AC_SUBST(MOZ_PREF_EXTENSIONS)
 AC_SUBST(MOZ_JS_LIBS)
 AC_SUBST(MOZ_DEBUG)
 AC_SUBST(MOZ_DEBUG_SYMBOLS)
 AC_SUBST(MOZ_DEBUG_ENABLE_DEFS)
 AC_SUBST(MOZ_DEBUG_DISABLE_DEFS)
-AC_SUBST(MOZ_DEBUG_FLAGS)
 AC_SUBST(MOZ_DEBUG_LDFLAGS)
 AC_SUBST(WARNINGS_AS_ERRORS)
 AC_SUBST(MOZ_EXTENSIONS)
 AC_SUBST(MOZ_JSDEBUGGER)
 AC_SUBST(MOZ_ENABLE_PROFILER_SPS)
 AC_SUBST(MOZ_JPROF)
 AC_SUBST(MOZ_SHARK)
 AC_SUBST(MOZ_INSTRUMENTS)
@@ -9134,34 +9132,53 @@ if test -z "$MOZ_NATIVE_NSPR"; then
         ac_configure_args="$ac_configure_args --disable-optimize"
     fi
     if test -n "$HAVE_64BIT_OS"; then
         ac_configure_args="$ac_configure_args --enable-64bit"
     fi
     if test -n "$USE_ARM_KUSER"; then
         ac_configure_args="$ac_configure_args --with-arm-kuser"
     fi
+    # A configure script generated by autoconf 2.68 does not allow the cached
+    # values of "precious" variables such as CFLAGS and LDFLAGS to differ from
+    # the values passed to the configure script. Since we modify CFLAGS and
+    # LDFLAGS before passing them to NSPR's configure script, we cannot share
+    # config.cache with NSPR. As a result, we cannot pass AS, CC, CXX, etc. to
+    # NSPR via a shared config.cache file and must pass them to NSPR on the
+    # configure command line.
+    for var in AS CC CXX CPP LD AR RANLIB STRIP; do
+        ac_configure_args="$ac_configure_args $var='`eval echo \\${${var}}`'"
+    done
+    # A configure script generated by autoconf 2.68 warns if --host is
+    # specified but --build isn't. So we always pass --build to NSPR's
+    # configure script.
+    ac_configure_args="$ac_configure_args --build=$build"
     ac_configure_args="$ac_configure_args $NSPR_CONFIGURE_ARGS"
 
     # Save these, so we can mess with them for the subconfigure ..
     _SAVE_CFLAGS="$CFLAGS"
     _SAVE_CPPFLAGS="$CPPFLAGS"
     _SAVE_LDFLAGS="$LDFLAGS"
 
     if test -n "$MOZ_LINKER" -a "$ac_cv_func_dladdr" = no ; then
       # dladdr is supported by the new linker, even when the system linker doesn't
       # support it. Trick nspr into using dladdr when it's not supported.
       export CPPFLAGS="-include $_topsrcdir/mozglue/linker/dladdr.h $CPPFLAGS"
     fi
     export LDFLAGS="$LDFLAGS $NSPR_LDFLAGS"
     export CFLAGS="$CFLAGS $MOZ_FRAMEPTR_FLAGS"
 
+    # Use a separate cache file for NSPR since it uses autoconf 2.68.
+    _save_cache_file="$cache_file"
+    cache_file=$_objdir/nsprpub/config.cache
+
     AC_OUTPUT_SUBDIRS(nsprpub)
 
     # .. and restore them
+    cache_file="$_save_cache_file"
     CFLAGS="$_SAVE_CFLAGS"
     CPPFLAGS="$_SAVE_CPPFLAGS"
     LDFLAGS="$_SAVE_LDFLAGS"
 
     ac_configure_args="$_SUBDIR_CONFIG_ARGS"
 fi
 
 dnl ========================================================
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -3206,17 +3206,17 @@ nsGenericHTMLElement::Properties()
 NS_IMETHODIMP
 nsGenericHTMLElement::GetProperties(nsISupports** aProperties)
 {
   NS_ADDREF(*aProperties = static_cast<nsIHTMLCollection*>(Properties()));
   return NS_OK;
 }
 
 nsSize
-nsGenericHTMLElement::GetWidthHeightForImage(imgIRequest *aImageRequest)
+nsGenericHTMLElement::GetWidthHeightForImage(nsRefPtr<imgRequestProxy>& aImageRequest)
 {
   nsSize size(0,0);
 
   nsIFrame* frame = GetPrimaryFrame(Flush_Layout);
 
   if (frame) {
     size = frame->GetContentRect().Size();
 
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -308,18 +308,20 @@ public:
   virtual already_AddRefed<mozilla::dom::UndoManager> GetUndoManager();
   virtual bool UndoScope() MOZ_OVERRIDE;
   virtual void SetUndoScope(bool aUndoScope, mozilla::ErrorResult& aError) MOZ_OVERRIDE;
   // Callback for destructor of of dataset to ensure to null out weak pointer.
   nsresult ClearDataset();
 
   /**
    * Get width and height, using given image request if attributes are unset.
+   * Pass a reference to the image request, since the method may change the
+   * value and we want to use the updated value.
    */
-  nsSize GetWidthHeightForImage(imgIRequest *aImageRequest);
+  nsSize GetWidthHeightForImage(nsRefPtr<imgRequestProxy>& aImageRequest);
 
   // XPIDL methods
   NS_FORWARD_NSIDOMNODE_TO_NSINODE
 
   NS_FORWARD_NSIDOMELEMENT_TO_GENERIC
 
   NS_IMETHOD GetId(nsAString& aId) MOZ_FINAL {
     mozilla::dom::Element::GetId(aId);
--- a/content/media/MediaRecorder.cpp
+++ b/content/media/MediaRecorder.cpp
@@ -17,22 +17,29 @@
 #include "nsIDOMFile.h"
 #include "mozilla/dom/BlobEvent.h"
 #include "nsIPrincipal.h"
 #include "nsMimeTypes.h"
 
 #include "mozilla/dom/AudioStreamTrack.h"
 #include "mozilla/dom/VideoStreamTrack.h"
 
+#ifdef PR_LOGGING
+PRLogModuleInfo* gMediaRecorderLog;
+#define LOG(type, msg) PR_LOG(gMediaRecorderLog, type, msg)
+#else
+#define LOG(type, msg)
+#endif
+
 namespace mozilla {
 
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED_2(MediaRecorder, nsDOMEventTargetHelper,
-                                     mStream, mSession)
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(MediaRecorder, nsDOMEventTargetHelper,
+                                     mStream)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaRecorder)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(MediaRecorder, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(MediaRecorder, nsDOMEventTargetHelper)
 
 /**
@@ -76,26 +83,32 @@ class MediaRecorder::Session: public nsI
   {
   public:
     PushBlobRunnable(Session* aSession)
       : mSession(aSession)
     { }
 
     NS_IMETHODIMP Run()
     {
+      LOG(PR_LOG_DEBUG, ("Session.PushBlobRunnable s=(%p)", mSession.get()));
       MOZ_ASSERT(NS_IsMainThread());
 
-      MediaRecorder *recorder = mSession->mRecorder;
+      nsRefPtr<MediaRecorder> recorder = mSession->mRecorder;
+      if (!recorder) {
+	 return NS_OK;
+      }
+      recorder->SetMimeType(mSession->mMimeType);
       if (mSession->IsEncoderError()) {
         recorder->NotifyError(NS_ERROR_UNEXPECTED);
       }
       nsresult rv = recorder->CreateAndDispatchBlobEvent(mSession->GetEncodedData());
       if (NS_FAILED(rv)) {
         recorder->NotifyError(rv);
       }
+
       return NS_OK;
     }
 
   private:
     nsRefPtr<Session> mSession;
   };
 
   // Record thread task and it run in Media Encoder thread.
@@ -106,29 +119,30 @@ class MediaRecorder::Session: public nsI
     ExtractRunnable(Session *aSession)
       : mSession(aSession) {}
 
     NS_IMETHODIMP Run()
     {
       MOZ_ASSERT(NS_GetCurrentThread() == mSession->mReadThread);
 
       mSession->Extract();
+      LOG(PR_LOG_DEBUG, ("Session.ExtractRunnable shutdown = %d", mSession->mEncoder->IsShutdown()));
       if (!mSession->mEncoder->IsShutdown()) {
         NS_DispatchToCurrentThread(new ExtractRunnable(mSession));
       } else {
         // Flush out remainding encoded data.
         NS_DispatchToMainThread(new PushBlobRunnable(mSession));
         // Destroy this Session object in main thread.
         NS_DispatchToMainThread(new DestroyRunnable(already_AddRefed<Session>(mSession)));
       }
       return NS_OK;
     }
 
   private:
-    Session* mSession;
+    nsRefPtr<Session> mSession;
   };
 
   // For Ensure recorder has tracks to record.
   class TracksAvailableCallback : public DOMMediaStream::OnTracksAvailableCallback
   {
   public:
     TracksAvailableCallback(Session *aSession)
      : mSession(aSession) {}
@@ -144,53 +158,59 @@ class MediaRecorder::Session: public nsI
         // What is inside the track
         if (videoTracks.Length() > 0) {
           trackType |= DOMMediaStream::HINT_CONTENTS_VIDEO;
         }
         if (audioTracks.Length() > 0) {
           trackType |= DOMMediaStream::HINT_CONTENTS_AUDIO;
         }
       }
+      LOG(PR_LOG_DEBUG, ("Session.NotifyTracksAvailable track type = (%d)", trackType));
       mSession->AfterTracksAdded(trackType);
     }
   private:
     nsRefPtr<Session> mSession;
   };
-
   // Main thread task.
   // To delete RecordingSession object.
   class DestroyRunnable : public nsRunnable
   {
   public:
     DestroyRunnable(already_AddRefed<Session>&& aSession)
       : mSession(aSession) {}
 
     NS_IMETHODIMP Run()
     {
+      LOG(PR_LOG_DEBUG, ("Session.DestroyRunnable session refcnt = (%d) stopIssued %d s=(%p)",
+                         (int)mSession->mRefCnt, mSession->mStopIssued, mSession.get()));
       MOZ_ASSERT(NS_IsMainThread() && mSession.get());
-      MediaRecorder *recorder = mSession->mRecorder;
-
+      nsRefPtr<MediaRecorder> recorder = mSession->mRecorder;
+      if (!recorder) {
+        return NS_OK;
+      }
       // SourceMediaStream is ended, and send out TRACK_EVENT_END notification.
       // Read Thread will be terminate soon.
       // We need to switch MediaRecorder to "Stop" state first to make sure
       // MediaRecorder is not associated with this Session anymore, then, it's
       // safe to delete this Session.
       // Also avoid to run if this session already call stop before
       if (!mSession->mStopIssued) {
         ErrorResult result;
+        mSession->mStopIssued = true;
         recorder->Stop(result);
         NS_DispatchToMainThread(new DestroyRunnable(mSession.forget()));
-
         return NS_OK;
       }
 
       // Dispatch stop event and clear MIME type.
-      recorder->DispatchSimpleEvent(NS_LITERAL_STRING("stop"));
       mSession->mMimeType = NS_LITERAL_STRING("");
       recorder->SetMimeType(mSession->mMimeType);
+      recorder->DispatchSimpleEvent(NS_LITERAL_STRING("stop"));
+      recorder->RemoveSession(mSession);
+      mSession->mRecorder = nullptr;
       return NS_OK;
     }
 
   private:
     // Call mSession::Release automatically while DestroyRunnable be destroy.
     nsRefPtr<Session> mSession;
   };
 
@@ -210,82 +230,91 @@ public:
     AddRef();
     mEncodedBufferCache = new EncodedBufferCache(MAX_ALLOW_MEMORY_BUFFER);
     mLastBlobTimeStamp = TimeStamp::Now();
   }
 
   // Only DestroyRunnable is allowed to delete Session object.
   virtual ~Session()
   {
+    LOG(PR_LOG_DEBUG, ("Session.~Session (%p)", this));
     CleanupStreams();
   }
 
   void Start()
   {
+    LOG(PR_LOG_DEBUG, ("Session.Start %p", this));
     MOZ_ASSERT(NS_IsMainThread());
 
     SetupStreams();
   }
 
   void Stop()
   {
+    LOG(PR_LOG_DEBUG, ("Session.Stop %p", this));
     MOZ_ASSERT(NS_IsMainThread());
-
     mStopIssued = true;
     CleanupStreams();
     nsContentUtils::UnregisterShutdownObserver(this);
   }
 
   nsresult Pause()
   {
+    LOG(PR_LOG_DEBUG, ("Session.Pause"));
     MOZ_ASSERT(NS_IsMainThread());
 
     NS_ENSURE_TRUE(mTrackUnionStream, NS_ERROR_FAILURE);
     mTrackUnionStream->ChangeExplicitBlockerCount(-1);
 
     return NS_OK;
   }
 
   nsresult Resume()
   {
+    LOG(PR_LOG_DEBUG, ("Session.Resume"));
     MOZ_ASSERT(NS_IsMainThread());
 
     NS_ENSURE_TRUE(mTrackUnionStream, NS_ERROR_FAILURE);
     mTrackUnionStream->ChangeExplicitBlockerCount(1);
 
     return NS_OK;
   }
 
   already_AddRefed<nsIDOMBlob> GetEncodedData()
   {
+    MOZ_ASSERT(NS_IsMainThread());
     return mEncodedBufferCache->ExtractBlob(mMimeType);
   }
 
   bool IsEncoderError()
   {
     if (mEncoder && mEncoder->HasError()) {
       return true;
     }
     return false;
   }
+  void ForgetMediaRecorder()
+  {
+    LOG(PR_LOG_DEBUG, ("Session.ForgetMediaRecorder (%p)", mRecorder));
+    mRecorder = nullptr;
+  }
 private:
 
   // Pull encoded meida data from MediaEncoder and put into EncodedBufferCache.
   // Destroy this session object in the end of this function.
   void Extract()
   {
     MOZ_ASSERT(NS_GetCurrentThread() == mReadThread);
-
+    LOG(PR_LOG_DEBUG, ("Session.Extract %p", this));
     // Whether push encoded data back to onDataAvailable automatically.
     const bool pushBlob = (mTimeSlice > 0) ? true : false;
 
     // Pull encoded media data from MediaEncoder
     nsTArray<nsTArray<uint8_t> > encodedBuf;
     mEncoder->GetEncodedData(&encodedBuf, mMimeType);
-    mRecorder->SetMimeType(mMimeType);
 
     // Append pulled data into cache buffer.
     for (uint32_t i = 0; i < encodedBuf.Length(); i++) {
       mEncodedBufferCache->AppendBuffer(encodedBuf[i]);
     }
 
     if (pushBlob) {
       if ((TimeStamp::Now() - mLastBlobTimeStamp).ToMilliseconds() > mTimeSlice) {
@@ -306,22 +335,23 @@ private:
     MOZ_ASSERT(mTrackUnionStream, "CreateTrackUnionStream failed");
 
     mTrackUnionStream->SetAutofinish(true);
 
     // Bind this Track Union Stream with Source Media
     mInputPort = mTrackUnionStream->AllocateInputPort(mRecorder->mStream->GetStream(), MediaInputPort::FLAG_BLOCK_OUTPUT);
 
     // Allocate encoder and bind with the Track Union Stream.
-    TracksAvailableCallback* tracksAvailableCallback = new TracksAvailableCallback(mRecorder->mSession);
+    TracksAvailableCallback* tracksAvailableCallback = new TracksAvailableCallback(mRecorder->mSessions.LastElement());
     mRecorder->mStream->OnTracksAvailable(tracksAvailableCallback);
   }
 
   void AfterTracksAdded(uint8_t aTrackTypes)
   {
+    LOG(PR_LOG_DEBUG, ("Session.AfterTracksAdded %p", this));
     MOZ_ASSERT(NS_IsMainThread());
 
     // Allocate encoder and bind with union stream.
     // At this stage, the API doesn't allow UA to choose the output mimeType format.
 
     nsCOMPtr<nsIDocument> doc = mRecorder->GetOwner()->GetExtantDoc();
     uint16_t appStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
     if (doc) {
@@ -362,20 +392,20 @@ private:
     nsContentUtils::RegisterShutdownObserver(this);
 
     mReadThread->Dispatch(new ExtractRunnable(this), NS_DISPATCH_NORMAL);
   }
   // application should get blob and onstop event
   void DoSessionEndTask(nsresult rv)
   {
     MOZ_ASSERT(NS_IsMainThread());
-
     if (NS_FAILED(rv)) {
       mRecorder->NotifyError(rv);
     }
+
     CleanupStreams();
     // Destroy this session object in main thread.
     NS_DispatchToMainThread(new PushBlobRunnable(this));
     NS_DispatchToMainThread(new DestroyRunnable(already_AddRefed<Session>(this)));
   }
   void CleanupStreams()
   {
     if (mInputPort.get()) {
@@ -387,29 +417,33 @@ private:
       mTrackUnionStream->Destroy();
       mTrackUnionStream = nullptr;
     }
   }
 
   NS_IMETHODIMP Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData)
   {
     MOZ_ASSERT(NS_IsMainThread());
-
+    LOG(PR_LOG_DEBUG, ("Session.Observe XPCOM_SHUTDOWN %p", this));
     if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
       // Force stop Session to terminate Read Thread.
+      mEncoder->Cancel();
+      if (mRecorder) {
+        mRecorder->RemoveSession(this);
+        mRecorder = nullptr;
+      }
       Stop();
     }
 
     return NS_OK;
   }
 
 private:
-  // Hold a reference to MediaRecoder to make sure MediaRecoder be
-  // destroyed after all session object dead.
-  nsRefPtr<MediaRecorder> mRecorder;
+  // Hold weak a reference to MediaRecoder and can be accessed ONLY on main thread.
+  MediaRecorder* mRecorder;
 
   // Receive track data from source and dispatch to Encoder.
   // Pause/ Resume controller.
   nsRefPtr<ProcessedMediaStream> mTrackUnionStream;
   nsRefPtr<MediaInputPort> mInputPort;
 
   // Runnable thread for read data from MediaEncode.
   nsCOMPtr<nsIThread> mReadThread;
@@ -429,28 +463,38 @@ private:
   // Indicate this session's stop has been called.
   bool mStopIssued;
 };
 
 NS_IMPL_ISUPPORTS1(MediaRecorder::Session, nsIObserver)
 
 MediaRecorder::~MediaRecorder()
 {
-  MOZ_ASSERT(mSession == nullptr);
+  LOG(PR_LOG_DEBUG, ("~MediaRecorder (%p)", this));
+  for (uint32_t i = 0; i < mSessions.Length(); i ++) {
+    if (mSessions[i]) {
+      mSessions[i]->ForgetMediaRecorder();
+      mSessions[i]->Stop();
+    }
+  }
 }
 
 MediaRecorder::MediaRecorder(DOMMediaStream& aStream, nsPIDOMWindow* aOwnerWindow)
   : nsDOMEventTargetHelper(aOwnerWindow),
     mState(RecordingState::Inactive),
-    mSession(nullptr),
     mMutex("Session.Data.Mutex")
 {
   MOZ_ASSERT(aOwnerWindow);
   MOZ_ASSERT(aOwnerWindow->IsInnerWindow());
   mStream = &aStream;
+#ifdef PR_LOGGING
+  if (!gMediaRecorderLog) {
+    gMediaRecorderLog = PR_NewLogModule("MediaRecorder");
+  }
+#endif
 }
 
 void
 MediaRecorder::SetMimeType(const nsString &aMimeType)
 {
   MutexAutoLock lock(mMutex);
   mMimeType = aMimeType;
 }
@@ -460,16 +504,17 @@ MediaRecorder::GetMimeType(nsString &aMi
 {
   MutexAutoLock lock(mMutex);
   aMimeType = mMimeType;
 }
 
 void
 MediaRecorder::Start(const Optional<int32_t>& aTimeSlice, ErrorResult& aResult)
 {
+  LOG(PR_LOG_DEBUG, ("MediaRecorder.Start %p", this));
   if (mState != RecordingState::Inactive) {
     aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   if (mStream->GetStream()->IsFinished() || mStream->GetStream()->IsDestroyed()) {
     aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
@@ -492,69 +537,70 @@ MediaRecorder::Start(const Optional<int3
       return;
     }
 
     timeSlice = aTimeSlice.Value();
   }
 
   mState = RecordingState::Recording;
   // Start a session
-  mSession = new Session(this, timeSlice);
-  mSession->Start();
+
+  mSessions.AppendElement();
+  mSessions.LastElement() = new Session(this, timeSlice);
+  mSessions.LastElement()->Start();
 }
 
 void
 MediaRecorder::Stop(ErrorResult& aResult)
 {
+  LOG(PR_LOG_DEBUG, ("MediaRecorder.Stop %p", this));
   if (mState == RecordingState::Inactive) {
     aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   mState = RecordingState::Inactive;
-
-  mSession->Stop();
-  mSession = nullptr;
+  if (mSessions.Length() > 0) {
+    mSessions.LastElement()->Stop();
+  }
 }
 
 void
 MediaRecorder::Pause(ErrorResult& aResult)
 {
+  LOG(PR_LOG_DEBUG, ("MediaRecorder.Pause"));
   if (mState != RecordingState::Recording) {
     aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
-  MOZ_ASSERT(mSession != nullptr);
-  if (mSession) {
-    nsresult rv = mSession->Pause();
-    if (NS_FAILED(rv)) {
-      NotifyError(rv);
-      return;
-    }
-    mState = RecordingState::Paused;
+  MOZ_ASSERT(mSessions.Length() > 0);
+  nsresult rv = mSessions.LastElement()->Pause();
+  if (NS_FAILED(rv)) {
+    NotifyError(rv);
+    return;
   }
+  mState = RecordingState::Paused;
 }
 
 void
 MediaRecorder::Resume(ErrorResult& aResult)
 {
+  LOG(PR_LOG_DEBUG, ("MediaRecorder.Resume"));
   if (mState != RecordingState::Paused) {
     aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
-  MOZ_ASSERT(mSession != nullptr);
-  if (mSession) {
-    nsresult rv = mSession->Resume();
-    if (NS_FAILED(rv)) {
-      NotifyError(rv);
-      return;
-    }
-    mState = RecordingState::Recording;
+  MOZ_ASSERT(mSessions.Length() > 0);
+  nsresult rv = mSessions.LastElement()->Resume();
+  if (NS_FAILED(rv)) {
+    NotifyError(rv);
+    return;
   }
+  mState = RecordingState::Recording;
 }
 
 class CreateAndDispatchBlobEventRunnable : public nsRunnable {
   nsCOMPtr<nsIDOMBlob> mBlob;
   nsRefPtr<MediaRecorder> mRecorder;
 public:
   CreateAndDispatchBlobEventRunnable(already_AddRefed<nsIDOMBlob>&& aBlob,
                                      MediaRecorder* aRecorder)
@@ -575,18 +621,19 @@ void
 MediaRecorder::RequestData(ErrorResult& aResult)
 {
   if (mState != RecordingState::Recording) {
     aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   NS_DispatchToMainThread(
-    new CreateAndDispatchBlobEventRunnable(mSession->GetEncodedData(), this),
-    NS_DISPATCH_NORMAL);
+    new CreateAndDispatchBlobEventRunnable(mSessions.LastElement()->GetEncodedData(),
+                                           this),
+                                           NS_DISPATCH_NORMAL);
 }
 
 JSObject*
 MediaRecorder::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return MediaRecorderBinding::Wrap(aCx, aScope, this);
 }
 
@@ -612,22 +659,21 @@ MediaRecorder::Constructor(const GlobalO
   object->SetMimeType(aInitDict.mMimeType);
   return object.forget();
 }
 
 nsresult
 MediaRecorder::CreateAndDispatchBlobEvent(already_AddRefed<nsIDOMBlob>&& aBlob)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
-
   if (!CheckPrincipal()) {
     // Media is not same-origin, don't allow the data out.
+    nsRefPtr<nsIDOMBlob> blob = aBlob;
     return NS_ERROR_DOM_SECURITY_ERR;
   }
-
   BlobEventInit init;
   init.mBubbles = false;
   init.mCancelable = false;
   init.mData = aBlob;
   nsRefPtr<BlobEvent> event =
     BlobEvent::Constructor(this,
                            NS_LITERAL_STRING("dataavailable"),
                            init);
@@ -699,24 +745,35 @@ MediaRecorder::NotifyError(nsresult aRv)
     NS_ERROR("Failed to dispatch the error event!!!");
     return;
   }
   return;
 }
 
 bool MediaRecorder::CheckPrincipal()
 {
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
+  if (!mStream) {
+    return false;
+  }
   nsCOMPtr<nsIPrincipal> principal = mStream->GetPrincipal();
   if (!GetOwner())
     return false;
   nsCOMPtr<nsIDocument> doc = GetOwner()->GetExtantDoc();
   if (!doc || !principal)
     return false;
 
   bool subsumes;
   if (NS_FAILED(doc->NodePrincipal()->Subsumes(principal, &subsumes)))
     return false;
 
   return subsumes;
 }
 
+void
+MediaRecorder::RemoveSession(Session* aSession)
+{
+  LOG(PR_LOG_DEBUG, ("MediaRecorder.RemoveSession (%p)", aSession));
+  mSessions.RemoveElement(aSession);
+}
+
 }
 }
--- a/content/media/MediaRecorder.h
+++ b/content/media/MediaRecorder.h
@@ -95,23 +95,24 @@ protected:
   // Creating a error event with message.
   void NotifyError(nsresult aRv);
   // Check if the recorder's principal is the subsume of mediaStream
   bool CheckPrincipal();
   // Set encoded MIME type.
   void SetMimeType(const nsString &aMimeType);
 
   MediaRecorder(const MediaRecorder& x) MOZ_DELETE; // prevent bad usage
-
+  // Remove session pointer.
+  void RemoveSession(Session* aSession);
   // MediaStream passed from js context
   nsRefPtr<DOMMediaStream> mStream;
   // The current state of the MediaRecorder object.
   RecordingState mState;
-  // Current recording session.
-  nsRefPtr<Session> mSession;
+  // Hold the sessions pointer in media recorder and clean in the destructor of recorder.
+  nsTArray<Session*> mSessions;
   // Thread safe for mMimeType.
   Mutex mMutex;
   // It specifies the container format as well as the audio and video capture formats.
   nsString mMimeType;
 };
 
 }
 }
--- a/content/media/test/mochitest.ini
+++ b/content/media/test/mochitest.ini
@@ -398,16 +398,17 @@ skip-if = (buildapp == 'b2g' && (toolkit
 [test_volume.html]
 [test_video_to_canvas.html]
 [test_audiowrite.html]
 [test_mediarecorder_creation.html]
 [test_mediarecorder_creation_fail.html]
 [test_mediarecorder_avoid_recursion.html]
 [test_mediarecorder_record_timeslice.html]
 [test_mediarecorder_record_audiocontext.html]
+[test_mediarecorder_record_audiocontext_mlk.html]
 [test_mediarecorder_record_4ch_audiocontext.html]
 skip-if = (toolkit == 'gonk' && !debug)
 [test_mediarecorder_record_stopms.html]
 [test_mediarecorder_record_nosrc.html]
 [test_mozHasAudio.html]
 skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug))
 [test_source_media.html]
 skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug))
new file mode 100644
--- /dev/null
+++ b/content/media/test/test_mediarecorder_record_audiocontext_mlk.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>capture for possible memory leak when record AudioContext</title>
+  <script type="text/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=973765">Mozill
+a Bug 973765</a>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+   // This test case want to capture the memory leak if exit the browser after running those script.
+   var ac = new window.AudioContext();
+   var destStream = ac.createMediaStreamDestination().stream;
+   var recorder = new MediaRecorder(destStream);
+   recorder.start(1000);
+   is(recorder.state, 'recording', 'Media recorder should be recording');
+   is(recorder.stream, destStream,
+      'Media recorder stream = element stream at the start of recording');
+</script>
+</pre>
+</body>
+</html>
--- a/db/sqlite3/README.MOZILLA
+++ b/db/sqlite3/README.MOZILLA
@@ -1,9 +1,9 @@
-This is sqlite 3.8.4.1
+This is sqlite 3.8.4.2
 
 -- Ryan VanderMeulen <ryanvm@gmail.com>, 03/2014
 
 See http://www.sqlite.org/ for more info.
 
 We have a mozilla-specific Makefile.in in src/ (normally no
 Makefile.in there) that we use to build.
 
--- a/db/sqlite3/src/sqlite3.c
+++ b/db/sqlite3/src/sqlite3.c
@@ -1,11 +1,11 @@
 /******************************************************************************
 ** This file is an amalgamation of many separate C source files from SQLite
-** version 3.8.4.1.  By combining all the individual C code files into this 
+** version 3.8.4.2.  By combining all the individual C code files into this 
 ** single large file, the entire code can be compiled as a single translation
 ** unit.  This allows many compilers to do optimizations that would not be
 ** possible if the files were compiled separately.  Performance improvements
 ** of 5% or more are commonly seen when SQLite is compiled as a single
 ** translation unit.
 **
 ** This file is all you need to compile SQLite.  To use SQLite in other
 ** programs, you need this file and the "sqlite3.h" header file that defines
@@ -217,19 +217,19 @@ extern "C" {
 ** within its configuration management system.  ^The SQLITE_SOURCE_ID
 ** string contains the date and time of the check-in (UTC) and an SHA1
 ** hash of the entire source tree.
 **
 ** See also: [sqlite3_libversion()],
 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
 ** [sqlite_version()] and [sqlite_source_id()].
 */
-#define SQLITE_VERSION        "3.8.4.1"
+#define SQLITE_VERSION        "3.8.4.2"
 #define SQLITE_VERSION_NUMBER 3008004
-#define SQLITE_SOURCE_ID      "2014-03-11 15:27:36 018d317b1257ce68a92908b05c9c7cf1494050d0"
+#define SQLITE_SOURCE_ID      "2014-03-26 18:51:19 02ea166372bdb2ef9d8dfbb05e78a97609673a8e"
 
 /*
 ** CAPI3REF: Run-Time Library Version Numbers
 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
 **
 ** These interfaces provide the same information as the [SQLITE_VERSION],
 ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros
 ** but are associated with the library instead of the header file.  ^(Cautious
@@ -64777,16 +64777,17 @@ SQLITE_PRIVATE int sqlite3VdbeRecordComp
     idx1 = 1 + getVarint32(&aKey1[1], s1);
     szHdr1 = aKey1[0];
     d1 = szHdr1 + sqlite3VdbeSerialTypeLen(s1);
     i = 1;
     pRhs++;
   }else{
     idx1 = getVarint32(aKey1, szHdr1);
     d1 = szHdr1;
+    if( d1>(unsigned)nKey1 ) return 1;  /* Corruption */
     i = 0;
   }
 
   VVA_ONLY( mem1.zMalloc = 0; ) /* Only needed by assert() statements */
   assert( pPKey2->pKeyInfo->nField+pPKey2->pKeyInfo->nXField>=pPKey2->nField 
        || CORRUPT_DB );
   assert( pPKey2->pKeyInfo->aSortOrder!=0 );
   assert( pPKey2->pKeyInfo->nField>0 );
--- a/db/sqlite3/src/sqlite3.h
+++ b/db/sqlite3/src/sqlite3.h
@@ -102,19 +102,19 @@ extern "C" {
 ** within its configuration management system.  ^The SQLITE_SOURCE_ID
 ** string contains the date and time of the check-in (UTC) and an SHA1
 ** hash of the entire source tree.
 **
 ** See also: [sqlite3_libversion()],
 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
 ** [sqlite_version()] and [sqlite_source_id()].
 */
-#define SQLITE_VERSION        "3.8.4.1"
+#define SQLITE_VERSION        "3.8.4.2"
 #define SQLITE_VERSION_NUMBER 3008004
-#define SQLITE_SOURCE_ID      "2014-03-11 15:27:36 018d317b1257ce68a92908b05c9c7cf1494050d0"
+#define SQLITE_SOURCE_ID      "2014-03-26 18:51:19 02ea166372bdb2ef9d8dfbb05e78a97609673a8e"
 
 /*
 ** CAPI3REF: Run-Time Library Version Numbers
 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
 **
 ** These interfaces provide the same information as the [SQLITE_VERSION],
 ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros
 ** but are associated with the library instead of the header file.  ^(Cautious
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -86,16 +86,18 @@
 
 #include "nsScriptNameSpaceManager.h"
 
 #include "mozilla/dom/NavigatorBinding.h"
 #include "mozilla/dom/Promise.h"
 
 #include "nsIUploadChannel2.h"
 #include "nsFormData.h"
+#include "nsIPrivateBrowsingChannel.h"
+#include "nsIDocShell.h"
 
 namespace mozilla {
 namespace dom {
 
 static bool sDoNotTrackEnabled = false;
 static bool sVibratorEnabled   = false;
 static uint32_t sMaxVibrateMS  = 0;
 static uint32_t sMaxVibrateListLen = 0;
@@ -1133,16 +1135,28 @@ Navigator::SendBeacon(const nsAString& a
                      nullptr,
                      nsIRequest::LOAD_NORMAL,
                      channelPolicy);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return false;
   }
 
+  nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(channel);
+  if (pbChannel) {
+    nsIDocShell* docShell = mWindow->GetDocShell();
+    nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
+    if (loadContext) {
+      rv = pbChannel->SetPrivate(loadContext->UsePrivateBrowsing());
+      if (NS_FAILED(rv)) {
+        NS_WARNING("Setting the privacy status on the beacon channel failed");
+      }
+    }
+  }
+
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
   if (!httpChannel) {
     // Beacon spec only supports HTTP requests at this time
     aRv.Throw(NS_ERROR_DOM_BAD_URI);
     return false;
   }
   httpChannel->SetReferrer(documentURI);
 
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -115,16 +115,22 @@ nsJSUtils::ReportPendingException(JSCont
       // information from it to script via onerror handlers.  So it's very
       // important that the duck typing happen in the same compartment as the
       // onerror handler.  In practice, that's the compartment of the window (or
       // otherwise default global) of aContext, so use that here.
       nsIScriptContext* scx = GetScriptContextFromJSContext(aContext);
       JS::Rooted<JSObject*> scope(aContext);
       scope = scx ? scx->GetWindowProxy()
                   : js::DefaultObjectForContextOrNull(aContext);
+      if (!scope) {
+        // The SafeJSContext has no default object associated with it.
+        MOZ_ASSERT(NS_IsMainThread());
+        MOZ_ASSERT(aContext == nsContentUtils::GetSafeJSContext());
+        scope = xpc::GetSafeJSContextGlobal();
+      }
       JSAutoCompartment ac(aContext, scope);
       JS_ReportPendingException(aContext);
     }
     if (saved) {
       JS_RestoreFrameChain(aContext);
     }
   }
 }
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -1122,24 +1122,25 @@ XrayResolveProperty(JSContext* cx, JS::H
   }
 
   return true;
 }
 
 static bool
 ResolvePrototypeOrConstructor(JSContext* cx, JS::Handle<JSObject*> wrapper,
                               JS::Handle<JSObject*> obj,
-                              size_t protoAndIfaceArrayIndex, unsigned attrs,
+                              size_t protoAndIfaceCacheIndex, unsigned attrs,
                               JS::MutableHandle<JSPropertyDescriptor> desc)
 {
   JS::Rooted<JSObject*> global(cx, js::GetGlobalForObjectCrossCompartment(obj));
   {
     JSAutoCompartment ac(cx, global);
-    ProtoAndIfaceArray& protoAndIfaceArray = *GetProtoAndIfaceArray(global);
-    JSObject* protoOrIface = protoAndIfaceArray[protoAndIfaceArrayIndex];
+    ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global);
+    JSObject* protoOrIface =
+      protoAndIfaceCache.EntrySlotIfExists(protoAndIfaceCacheIndex);
     if (!protoOrIface) {
       return false;
     }
     desc.object().set(wrapper);
     desc.setAttributes(attrs);
     desc.setGetter(JS_PropertyStub);
     desc.setSetter(JS_StrictPropertyStub);
     desc.value().set(JS::ObjectValue(*protoOrIface));
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -280,75 +280,232 @@ IsConvertibleToDictionary(JSContext* cx,
 }
 
 MOZ_ALWAYS_INLINE bool
 IsConvertibleToCallbackInterface(JSContext* cx, JS::Handle<JSObject*> obj)
 {
   return IsNotDateOrRegExp(cx, obj);
 }
 
-// The items in the protoAndIfaceArray are indexed by the prototypes::id::ID and
+// The items in the protoAndIfaceCache are indexed by the prototypes::id::ID and
 // constructors::id::ID enums, in that order. The end of the prototype objects
 // should be the start of the interface objects.
 static_assert((size_t)constructors::id::_ID_Start ==
               (size_t)prototypes::id::_ID_Count,
               "Overlapping or discontiguous indexes.");
 const size_t kProtoAndIfaceCacheCount = constructors::id::_ID_Count;
 
-class ProtoAndIfaceArray : public Array<JS::Heap<JSObject*>, kProtoAndIfaceCacheCount>
+class ProtoAndIfaceCache
 {
+  // The caching strategy we use depends on what sort of global we're dealing
+  // with.  For a window-like global, we want everything to be as fast as
+  // possible, so we use a flat array, indexed by prototype/constructor ID.
+  // For everything else (e.g. globals for JSMs), space is more important than
+  // speed, so we use a two-level lookup table.
+
+  class ArrayCache : public Array<JS::Heap<JSObject*>, kProtoAndIfaceCacheCount>
+  {
+  public:
+    JSObject* EntrySlotIfExists(size_t i) {
+      return (*this)[i];
+    }
+
+    JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) {
+      return (*this)[i];
+    }
+
+    JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) {
+      MOZ_ASSERT((*this)[i]);
+      return (*this)[i];
+    }
+
+    void Trace(JSTracer* aTracer) {
+      for (size_t i = 0; i < ArrayLength(*this); ++i) {
+        if ((*this)[i]) {
+          JS_CallHeapObjectTracer(aTracer, &(*this)[i], "protoAndIfaceCache[i]");
+        }
+      }
+    }
+
+    size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
+      return aMallocSizeOf(this);
+    }
+  };
+
+  class PageTableCache
+  {
+  public:
+    PageTableCache() {
+      memset(&mPages, 0, sizeof(mPages));
+    }
+
+    ~PageTableCache() {
+      for (size_t i = 0; i < ArrayLength(mPages); ++i) {
+        delete mPages[i];
+      }
+    }
+
+    JSObject* EntrySlotIfExists(size_t i) {
+      MOZ_ASSERT(i < kProtoAndIfaceCacheCount);
+      size_t pageIndex = i / kPageSize;
+      size_t leafIndex = i % kPageSize;
+      Page* p = mPages[pageIndex];
+      if (!p) {
+        return nullptr;
+      }
+      return (*p)[leafIndex];
+    }
+
+    JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) {
+      MOZ_ASSERT(i < kProtoAndIfaceCacheCount);
+      size_t pageIndex = i / kPageSize;
+      size_t leafIndex = i % kPageSize;
+      Page* p = mPages[pageIndex];
+      if (!p) {
+        p = new Page;
+        mPages[pageIndex] = p;
+      }
+      return (*p)[leafIndex];
+    }
+
+    JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) {
+      MOZ_ASSERT(i < kProtoAndIfaceCacheCount);
+      size_t pageIndex = i / kPageSize;
+      size_t leafIndex = i % kPageSize;
+      Page* p = mPages[pageIndex];
+      MOZ_ASSERT(p);
+      return (*p)[leafIndex];
+    }
+
+    void Trace(JSTracer* trc) {
+      for (size_t i = 0; i < ArrayLength(mPages); ++i) {
+        Page* p = mPages[i];
+        if (p) {
+          for (size_t j = 0; j < ArrayLength(*p); ++j) {
+            if ((*p)[j]) {
+              JS_CallHeapObjectTracer(trc, &(*p)[j], "protoAndIfaceCache[i]");
+            }
+          }
+        }
+      }
+    }
+
+    size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
+      size_t n = aMallocSizeOf(this);
+      for (size_t i = 0; i < ArrayLength(mPages); ++i) {
+        n += aMallocSizeOf(mPages[i]);
+      }
+      return n;
+    }
+
+  private:
+    static const size_t kPageSize = 16;
+    typedef Array<JS::Heap<JSObject*>, kPageSize> Page;
+    static const size_t kNPages = kProtoAndIfaceCacheCount / kPageSize +
+      size_t(bool(kProtoAndIfaceCacheCount % kPageSize));
+    Array<Page*, kNPages> mPages;
+  };
+
 public:
-  ProtoAndIfaceArray() {
-    MOZ_COUNT_CTOR(ProtoAndIfaceArray);
+  enum Kind {
+    WindowLike,
+    NonWindowLike
+  };
+
+  ProtoAndIfaceCache(Kind aKind) : mKind(aKind) {
+    MOZ_COUNT_CTOR(ProtoAndIfaceCache);
+    if (aKind == WindowLike) {
+      mArrayCache = new ArrayCache();
+    } else {
+      mPageTableCache = new PageTableCache();
+    }
   }
 
-  ~ProtoAndIfaceArray() {
-    MOZ_COUNT_DTOR(ProtoAndIfaceArray);
+  ~ProtoAndIfaceCache() {
+    if (mKind == WindowLike) {
+      delete mArrayCache;
+    } else {
+      delete mPageTableCache;
+    }
+    MOZ_COUNT_DTOR(ProtoAndIfaceCache);
+  }
+
+#define FORWARD_OPERATION(opName, args)              \
+  do {                                               \
+    if (mKind == WindowLike) {                       \
+      return mArrayCache->opName args;               \
+    } else {                                         \
+      return mPageTableCache->opName args;           \
+    }                                                \
+  } while(0)
+
+  JSObject* EntrySlotIfExists(size_t i) {
+    FORWARD_OPERATION(EntrySlotIfExists, (i));
+  }
+
+  JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) {
+    FORWARD_OPERATION(EntrySlotOrCreate, (i));
+  }
+
+  JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) {
+    FORWARD_OPERATION(EntrySlotMustExist, (i));
+  }
+
+  void Trace(JSTracer *aTracer) {
+    FORWARD_OPERATION(Trace, (aTracer));
   }
 
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
-    return aMallocSizeOf(this);
+    size_t n = aMallocSizeOf(this);
+    n += (mKind == WindowLike
+          ? mArrayCache->SizeOfIncludingThis(aMallocSizeOf)
+          : mPageTableCache->SizeOfIncludingThis(aMallocSizeOf));
+    return n;
   }
+#undef FORWARD_OPERATION
+
+private:
+  union {
+    ArrayCache *mArrayCache;
+    PageTableCache *mPageTableCache;
+  };
+  Kind mKind;
 };
 
 inline void
-AllocateProtoAndIfaceCache(JSObject* obj)
+AllocateProtoAndIfaceCache(JSObject* obj, ProtoAndIfaceCache::Kind aKind)
 {
   MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL);
   MOZ_ASSERT(js::GetReservedSlot(obj, DOM_PROTOTYPE_SLOT).isUndefined());
 
-  ProtoAndIfaceArray* protoAndIfaceArray = new ProtoAndIfaceArray();
+  ProtoAndIfaceCache* protoAndIfaceCache = new ProtoAndIfaceCache(aKind);
 
   js::SetReservedSlot(obj, DOM_PROTOTYPE_SLOT,
-                      JS::PrivateValue(protoAndIfaceArray));
+                      JS::PrivateValue(protoAndIfaceCache));
 }
 
 inline void
 TraceProtoAndIfaceCache(JSTracer* trc, JSObject* obj)
 {
   MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL);
 
-  if (!HasProtoAndIfaceArray(obj))
+  if (!HasProtoAndIfaceCache(obj))
     return;
-  ProtoAndIfaceArray& protoAndIfaceArray = *GetProtoAndIfaceArray(obj);
-  for (size_t i = 0; i < ArrayLength(protoAndIfaceArray); ++i) {
-    if (protoAndIfaceArray[i]) {
-      JS_CallHeapObjectTracer(trc, &protoAndIfaceArray[i], "protoAndIfaceArray[i]");
-    }
-  }
+  ProtoAndIfaceCache* protoAndIfaceCache = GetProtoAndIfaceCache(obj);
+  protoAndIfaceCache->Trace(trc);
 }
 
 inline void
 DestroyProtoAndIfaceCache(JSObject* obj)
 {
   MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL);
 
-  ProtoAndIfaceArray* protoAndIfaceArray = GetProtoAndIfaceArray(obj);
+  ProtoAndIfaceCache* protoAndIfaceCache = GetProtoAndIfaceCache(obj);
 
-  delete protoAndIfaceArray;
+  delete protoAndIfaceCache;
 }
 
 /**
  * Add constants to an object.
  */
 bool
 DefineConstants(JSContext* cx, JS::Handle<JSObject*> obj,
                 const ConstantSpec* cs);
@@ -507,22 +664,16 @@ CouldBeDOMBinding(void*)
 }
 
 MOZ_ALWAYS_INLINE bool
 CouldBeDOMBinding(nsWrapperCache* aCache)
 {
   return aCache->IsDOMBinding();
 }
 
-inline bool
-GetSameCompartmentWrapperForDOMBinding(JSObject*& obj)
-{
-  return IsDOMObject(obj);
-}
-
 // Make sure to wrap the given string value into the right compartment, as
 // needed.
 MOZ_ALWAYS_INLINE
 bool
 MaybeWrapStringValue(JSContext* cx, JS::MutableHandle<JS::Value> rval)
 {
   MOZ_ASSERT(rval.isString());
   JSString* str = rval.toString();
@@ -535,25 +686,24 @@ MaybeWrapStringValue(JSContext* cx, JS::
 // Make sure to wrap the given object value into the right compartment as
 // needed.  This will work correctly, but possibly slowly, on all objects.
 MOZ_ALWAYS_INLINE
 bool
 MaybeWrapObjectValue(JSContext* cx, JS::MutableHandle<JS::Value> rval)
 {
   MOZ_ASSERT(rval.isObject());
 
+  // Cross-compartment always requires wrapping.
   JSObject* obj = &rval.toObject();
   if (js::GetObjectCompartment(obj) != js::GetContextCompartment(cx)) {
     return JS_WrapValue(cx, rval);
   }
 
-  // We're same-compartment, but even then we might need to wrap
-  // objects specially.  Check for that.
-  if (GetSameCompartmentWrapperForDOMBinding(obj)) {
-    // We're a new-binding object, and "obj" now points to the right thing
+  // We're same-compartment. If we're a WebIDL object, we're done.
+  if (IsDOMObject(obj)) {
     rval.set(JS::ObjectValue(*obj));
     return true;
   }
 
   // It's not a WebIDL object.  But it might be an XPConnect one, in which case
   // we may need to outerize here, so make sure to call JS_WrapValue.
   return JS_WrapValue(cx, rval);
 }
@@ -2125,18 +2275,18 @@ InterfaceHasInstance(JSContext* cx, int 
 // Helper for lenient getters/setters to report to console.  If this
 // returns false, we couldn't even get a global.
 bool
 ReportLenientThisUnwrappingFailure(JSContext* cx, JSObject* obj);
 
 inline JSObject*
 GetUnforgeableHolder(JSObject* aGlobal, prototypes::ID aId)
 {
-  ProtoAndIfaceArray& protoAndIfaceArray = *GetProtoAndIfaceArray(aGlobal);
-  JSObject* interfaceProto = protoAndIfaceArray[aId];
+  ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(aGlobal);
+  JSObject* interfaceProto = protoAndIfaceCache.EntrySlotMustExist(aId);
   return &js::GetReservedSlot(interfaceProto,
                               DOM_INTERFACE_PROTO_SLOTS_BASE).toObject();
 }
 
 // Given a JSObject* that represents the chrome side of a JS-implemented WebIDL
 // interface, get the nsPIDOMWindow corresponding to the content side, if any.
 // A false return means an exception was thrown.
 bool
@@ -2384,17 +2534,17 @@ CreateGlobal(JSContext* aCx, T* aObject,
                        aOptions));
   if (!global) {
     NS_WARNING("Failed to create global");
     return nullptr;
   }
 
   JSAutoCompartment ac(aCx, global);
 
-  dom::AllocateProtoAndIfaceCache(global);
+  dom::AllocateProtoAndIfaceCache(global, ProtoAndIfaceCache::WindowLike);
 
   js::SetReservedSlot(global, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(aObject));
   NS_ADDREF(aObject);
 
   aCache->SetIsDOMBinding();
   aCache->SetWrapper(global);
 
   /* Intl API is broken and makes this fail intermittently, see bug 934889.
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1944,17 +1944,17 @@ class CGCreateInterfaceObjectsMethod(CGA
     """
     Generate the CreateInterfaceObjects method for an interface descriptor.
 
     properties should be a PropertyArrays instance.
     """
     def __init__(self, descriptor, properties):
         args = [Argument('JSContext*', 'aCx'),
                 Argument('JS::Handle<JSObject*>', 'aGlobal'),
-                Argument('ProtoAndIfaceArray&', 'aProtoAndIfaceArray'),
+                Argument('ProtoAndIfaceCache&', 'aProtoAndIfaceCache'),
                 Argument('bool', 'aDefineOnGlobal')]
         CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'void', args)
         self.properties = properties
     def definition_body(self):
         if len(self.descriptor.prototypeChain) == 1:
             parentProtoType = "Rooted"
             if self.descriptor.interface.getExtendedAttribute("ArrayClass"):
                 getParentProto = "aCx, JS_GetArrayPrototype(aCx, aGlobal)"
@@ -2069,23 +2069,23 @@ if (!unforgeableHolder) {
             constructArgs = 0
         if len(self.descriptor.interface.namedConstructors) > 0:
             namedConstructors = "namedConstructors"
         else:
             namedConstructors = "nullptr"
 
         if needInterfacePrototypeObject:
             protoClass = "&PrototypeClass.mBase"
-            protoCache = "&aProtoAndIfaceArray[prototypes::id::%s]" % self.descriptor.name
+            protoCache = "&aProtoAndIfaceCache.EntrySlotOrCreate(prototypes::id::%s)" % self.descriptor.name
         else:
             protoClass = "nullptr"
             protoCache = "nullptr"
         if needInterfaceObject:
             interfaceClass = "&InterfaceObjectClass.mBase"
-            interfaceCache = "&aProtoAndIfaceArray[constructors::id::%s]" % self.descriptor.name
+            interfaceCache = "&aProtoAndIfaceCache.EntrySlotOrCreate(constructors::id::%s)" % self.descriptor.name
         else:
             # We don't have slots to store the named constructors.
             assert len(self.descriptor.interface.namedConstructors) == 0
             interfaceClass = "nullptr"
             interfaceCache = "nullptr"
 
         if self.descriptor.concrete:
             domClass = "&Class.mClass"
@@ -2115,17 +2115,17 @@ if (!unforgeableHolder) {
             interfaceCache,
             domClass,
             properties,
             chromeProperties,
             '"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "nullptr"))
         if UseHolderForUnforgeable(self.descriptor):
             assert needInterfacePrototypeObject
             setUnforgeableHolder = CGGeneric(
-                "JSObject* proto = aProtoAndIfaceArray[prototypes::id::%s];\n"
+                "JSObject* proto = aProtoAndIfaceCache.EntrySlotOrCreate(prototypes::id::%s);\n"
                 "if (proto) {\n"
                 "  js::SetReservedSlot(proto, DOM_INTERFACE_PROTO_SLOTS_BASE,\n"
                 "                      JS::ObjectValue(*unforgeableHolder));\n"
                 "}" % self.descriptor.name)
         else:
             setUnforgeableHolder = None
         functionBody = CGList(
             [CGGeneric(getParentProto), CGGeneric(getConstructorProto), initIds,
@@ -2147,30 +2147,29 @@ class CGGetPerInterfaceObject(CGAbstract
     def definition_body(self):
         return ("""
 
   /* Make sure our global is sane.  Hopefully we can remove this sometime */
   if (!(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL)) {
     return JS::NullPtr();
   }
   /* Check to see whether the interface objects are already installed */
-  ProtoAndIfaceArray& protoAndIfaceArray = *GetProtoAndIfaceArray(aGlobal);
-  if (!protoAndIfaceArray[%s]) {
-    CreateInterfaceObjects(aCx, aGlobal, protoAndIfaceArray, aDefineOnGlobal);
+  ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(aGlobal);
+  if (!protoAndIfaceCache.EntrySlotIfExists(%s)) {
+    CreateInterfaceObjects(aCx, aGlobal, protoAndIfaceCache, aDefineOnGlobal);
   }
 
   /*
    * The object might _still_ be null, but that's OK.
    *
-   * Calling fromMarkedLocation() is safe because protoAndIfaceArray is
+   * Calling fromMarkedLocation() is safe because protoAndIfaceCache is
    * traced by TraceProtoAndIfaceCache() and its contents are never
    * changed after they have been set.
    */
-  return JS::Handle<JSObject*>::fromMarkedLocation(protoAndIfaceArray[%s].address());""" %
-                (self.id, self.id))
+  return JS::Handle<JSObject*>::fromMarkedLocation(protoAndIfaceCache.EntrySlotMustExist(%s).address());""" % (self.id, self.id))
 
 class CGGetProtoObjectMethod(CGGetPerInterfaceObject):
     """
     A method for getting the interface prototype object.
     """
     def __init__(self, descriptor):
         CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObject",
                                          "prototypes::")
@@ -9342,17 +9341,18 @@ if (!*reinterpret_cast<jsid**>(atomsCach
             Argument('JSContext*', 'cx'),
             Argument('JS::Handle<JSObject*>', 'parentObject'),
             Argument('JS::MutableHandle<JS::Value>', 'rval'),
         ], const=True, body=body)
 
     def initIdsMethod(self):
         assert self.needToInitIds
         idinit = [CGGeneric('!atomsCache->%s.init(cx, "%s")' %
-                            (m.identifier.name + "_id", m.identifier.name))
+                            (CGDictionary.makeIdName(m.identifier.name),
+                             m.identifier.name))
                   for m in self.dictionary.members]
         idinit.reverse();
         idinit = CGList(idinit, " ||\n")
         idinit = CGWrapper(idinit, pre="""
 // Initialize these in reverse order so that any failure leaves the first one
 // uninitialized.
 if (""",
                            post=(") {\n"
@@ -9563,16 +9563,31 @@ if (""",
         declType = memberInfo[1].declType
         memberLoc = self.makeMemberName(member.identifier.name)
         if member.defaultValue:
             memberData = memberLoc
         else:
             # The data is inside the Optional<>
             memberData = "%s.InternalValue()" % memberLoc
 
+        # If you have to change this list (which you shouldn't!), make sure it
+        # continues to match the list in test_Object.prototype_props.html
+        if (member.identifier.name in
+            ["constructor", "toSource", "toString", "toLocaleString", "valueOf",
+             "watch", "unwatch", "hasOwnProperty", "isPrototypeOf",
+             "propertyIsEnumerable", "__defineGetter__", "__defineSetter__",
+             "__lookupGetter__", "__lookupSetter__", "__proto__"]):
+            raise TypeError("'%s' member of %s dictionary shadows "
+                            "a property of Object.prototype, and Xrays to "
+                            "Object can't handle that.\n"
+                            "%s" %
+                            (member.identifier.name,
+                             self.dictionary.identifier.name,
+                             member.location))
+
         propDef = (
             'JS_DefinePropertyById(cx, obj, atomsCache->%s, temp, nullptr, nullptr, JSPROP_ENUMERATE)' %
             self.makeIdName(member.identifier.name))
 
         innerTemplate = wrapForType(
             member.type, self.descriptorProvider,
             {
                 'result' : "currentValue",
@@ -9874,17 +9889,17 @@ class CGForwardDeclarations(CGWrapper):
             # There may be some other cases we are missing.
 
         # Needed for at least Wrap.
         for d in descriptors:
             builder.add(d.nativeType)
 
         # We just about always need NativePropertyHooks
         builder.addInMozillaDom("NativePropertyHooks")
-        builder.addInMozillaDom("ProtoAndIfaceArray")
+        builder.addInMozillaDom("ProtoAndIfaceCache")
 
         for callback in mainCallbacks:
             forwardDeclareForType(callback)
             for t in getTypesFromCallback(callback):
                 forwardDeclareForType(t, workerness='mainthreadonly')
 
         for callback in workerCallbacks:
             forwardDeclareForType(callback)
@@ -11743,17 +11758,17 @@ class GlobalGenRoots():
 
         structs = []
 
         for dict in dictionaries:
             dictMembers = dict.members
             if len(dictMembers) == 0:
                 continue
 
-            classMembers = [ClassMember(m.identifier.name + "_id",
+            classMembers = [ClassMember(CGDictionary.makeIdName(m.identifier.name),
                                         "InternedStringId",
                                         visibility="public") for m in dictMembers]
 
             structName = dict.identifier.name + "Atoms"
             structs.append((structName,
                             CGWrapper(CGClass(structName,
                 bases=None,
                 isStruct=True,
--- a/dom/bindings/DOMJSClass.h
+++ b/dom/bindings/DOMJSClass.h
@@ -230,30 +230,30 @@ struct DOMIfaceAndProtoJSClass
   }
   static const DOMIfaceAndProtoJSClass* FromJSClass(const js::Class* base) {
     return FromJSClass(Jsvalify(base));
   }
 
   const JSClass* ToJSClass() const { return &mBase; }
 };
 
-class ProtoAndIfaceArray;
+class ProtoAndIfaceCache;
 
 inline bool
-HasProtoAndIfaceArray(JSObject* global)
+HasProtoAndIfaceCache(JSObject* global)
 {
   MOZ_ASSERT(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL);
   // This can be undefined if we GC while creating the global
   return !js::GetReservedSlot(global, DOM_PROTOTYPE_SLOT).isUndefined();
 }
 
-inline ProtoAndIfaceArray*
-GetProtoAndIfaceArray(JSObject* global)
+inline ProtoAndIfaceCache*
+GetProtoAndIfaceCache(JSObject* global)
 {
   MOZ_ASSERT(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL);
-  return static_cast<ProtoAndIfaceArray*>(
+  return static_cast<ProtoAndIfaceCache*>(
     js::GetReservedSlot(global, DOM_PROTOTYPE_SLOT).toPrivate());
 }
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_DOMJSClass_h */
--- a/dom/bindings/test/mochitest.ini
+++ b/dom/bindings/test/mochitest.ini
@@ -30,13 +30,14 @@ skip-if = true
 [test_forOf.html]
 [test_integers.html]
 [test_interfaceToString.html]
 [test_exceptions_from_jsimplemented.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only failure; bug 926547
 [test_lenientThis.html]
 [test_lookupGetter.html]
 [test_namedNoIndexed.html]
+[test_Object.prototype_props.html]
 [test_queryInterface.html]
 [test_sequence_wrapping.html]
 [test_throwing_method_noDCE.html]
 [test_treat_non_object_as_null.html]
 [test_traceProtos.html]
new file mode 100644
--- /dev/null
+++ b/dom/bindings/test/test_Object.prototype_props.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test for bug 987110</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+  var props = Object.getOwnPropertyNames(Object.prototype);
+  // getOwnPropertyNames doesn't include "__proto__" yet for some reason
+  props.push("__proto__");
+  // If you change this list, make sure it continues to match the list in
+  // Codegen.py's CGDictionary.getMemberDefinition method.
+  var expected = [
+      "constructor", "toSource", "toString", "toLocaleString", "valueOf",
+      "watch", "unwatch", "hasOwnProperty", "isPrototypeOf",
+      "propertyIsEnumerable", "__defineGetter__", "__defineSetter__",
+      "__lookupGetter__", "__lookupSetter__", "__proto__"
+    ];
+  assert_array_equals(props, expected);
+}, "Own properties of Object.prototype");
+</script>
--- a/dom/browser-element/mochitest/priority/mochitest.ini
+++ b/dom/browser-element/mochitest/priority/mochitest.ini
@@ -13,17 +13,18 @@ support-files = file_HighPriority.html
 [test_HighPriorityDowngrade.html]
 [test_HighPriorityDowngrade2.html]
 [test_Background.html]
 [test_BackgroundLRU.html]
 [test_Audio.html]
 support-files = file_Audio.html silence.ogg
 [test_MultipleFrames.html]
 support-files = file_MultipleFrames.html
-[test_Preallocated.html]
+# This test is disabled temporarily in bug 968604. It will be enabled after bug 987164 is fixed.
+#[test_Preallocated.html]
 [test_ExpectingSystemMessage.html]
 [test_ExpectingSystemMessage2.html]
 [test_NestedFrames.html]
 support-files = file_NestedFramesOuter.html
 [test_WebGLContextLost.html]
 # This test disabled due to bug 865844.  In fact, it was never enabled!
 skip-if = true
 support-files = file_WebGLContextLost.html
--- a/dom/browser-element/mochitest/priority/test_Preallocated.html
+++ b/dom/browser-element/mochitest/priority/test_Preallocated.html
@@ -58,18 +58,22 @@ function runTest()
     return;
   }
 
   // Ensure that the preallocated process initially gets BACKGROUND priority.
   // That's it.
   expectProcessCreated().then(function(childID) {
     return expectPriorityChange(childID, 'PREALLOC');
   }).then(function() {
-    cleanUp();
-    SimpleTest.finish();
+    // We need to set the pref asynchoronously or the preallocated process won't
+    // be shut down.
+    SimpleTest.executeSoon(function(){
+      cleanUp();
+      SimpleTest.finish();
+    });
   });
 
   // Setting this pref to true should cause us to prelaunch a process.
   SpecialPowers.setBoolPref('dom.ipc.processPrelaunch.enabled', true);
 }
 
 addEventListener('testready', runTest);
 </script>
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -434,33 +434,40 @@ ContentChild::Init(MessageLoop* aIOLoop,
     SendBackUpXResources(FileDescriptor(xSocketFd));
 #endif
 
 #ifdef MOZ_CRASHREPORTER
     SendPCrashReporterConstructor(CrashReporter::CurrentThreadId(),
                                   XRE_GetProcessType());
 #endif
 
-    SendGetProcessAttributes(&mID, &mIsForApp, &mIsForBrowser);
+    GetCPOWManager();
+
+    InitProcessAttributes();
 
-    GetCPOWManager();
+    return true;
+}
+
+void
+ContentChild::InitProcessAttributes()
+{
+    SendGetProcessAttributes(&mID, &mIsForApp, &mIsForBrowser);
 
 #ifdef MOZ_NUWA_PROCESS
     if (IsNuwaProcess()) {
         SetProcessName(NS_LITERAL_STRING("(Nuwa)"), false);
-        return true;
+        return;
     }
 #endif
     if (mIsForApp && !mIsForBrowser) {
         SetProcessName(NS_LITERAL_STRING("(Preallocated app)"), false);
     } else {
         SetProcessName(NS_LITERAL_STRING("Browser"), false);
     }
 
-    return true;
 }
 
 void
 ContentChild::SetProcessName(const nsAString& aName, bool aDontOverride)
 {
     if (!mCanOverrideProcessName) {
         return;
     }
@@ -798,16 +805,20 @@ ContentChild::RecvPBrowserConstructor(PB
 
     static bool hasRunOnce = false;
     if (!hasRunOnce) {
         hasRunOnce = true;
 
         MOZ_ASSERT(!sFirstIdleTask);
         sFirstIdleTask = NewRunnableFunction(FirstIdle);
         MessageLoop::current()->PostIdleTask(FROM_HERE, sFirstIdleTask);
+
+        // Redo InitProcessAttributes() when the app or browser is really
+        // launching so the attributes will be correct.
+        InitProcessAttributes();
     }
 
     return true;
 }
 
 PFileDescriptorSetChild*
 ContentChild::AllocPFileDescriptorSetChild(const FileDescriptor& aFD)
 {
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -60,16 +60,17 @@ public:
         nsCString buildID;
         nsCString name;
         nsCString UAName;
     };
 
     bool Init(MessageLoop* aIOLoop,
               base::ProcessHandle aParentHandle,
               IPC::Channel* aChannel);
+    void InitProcessAttributes();
     void InitXPCOM();
 
     static ContentChild* GetSingleton() {
         return sSingleton;
     }
 
     const AppInfo& GetAppInfo() {
         return mAppInfo;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -572,21 +572,34 @@ ContentParent::GetNewOrUsed(bool aForBro
 
     if (sNonAppContentParents->Length() >= uint32_t(maxContentProcesses)) {
         uint32_t idx = rand() % sNonAppContentParents->Length();
         nsRefPtr<ContentParent> p = (*sNonAppContentParents)[idx];
         NS_ASSERTION(p->IsAlive(), "Non-alive contentparent in sNonAppContentParents?");
         return p.forget();
     }
 
-    nsRefPtr<ContentParent> p =
-        new ContentParent(/* app = */ nullptr,
-                          aForBrowserElement,
-                          /* isForPreallocated = */ false,
-                          PROCESS_PRIORITY_FOREGROUND);
+    // Try to take and transform the preallocated process into browser.
+    nsRefPtr<ContentParent> p = PreallocatedProcessManager::Take();
+    if (p) {
+        p->TransformPreallocatedIntoBrowser();
+    } else {
+      // Failed in using the preallocated process: fork from the chrome process.
+#ifdef MOZ_NUWA_PROCESS
+        if (Preferences::GetBool("dom.ipc.processPrelaunch.enabled", false)) {
+            // Wait until the Nuwa process forks a new process.
+            return nullptr;
+        }
+#endif
+        p = new ContentParent(/* app = */ nullptr,
+                              aForBrowserElement,
+                              /* isForPreallocated = */ false,
+                              PROCESS_PRIORITY_FOREGROUND);
+    }
+
     p->Init();
     sNonAppContentParents->AppendElement(p);
     return p.forget();
 }
 
 /*static*/ ProcessPriority
 ContentParent::GetInitialProcessPriority(Element* aFrameElement)
 {
@@ -999,16 +1012,24 @@ void
 ContentParent::TransformPreallocatedIntoApp(const nsAString& aAppManifestURL)
 {
     MOZ_ASSERT(IsPreallocated());
     mAppManifestURL = aAppManifestURL;
     TryGetNameFromManifestURL(aAppManifestURL, mAppName);
 }
 
 void
+ContentParent::TransformPreallocatedIntoBrowser()
+{
+    // Reset mAppManifestURL, mIsForBrowser and mOSPrivileges for browser.
+    mAppManifestURL.Truncate();
+    mIsForBrowser = true;
+}
+
+void
 ContentParent::ShutDownProcess(bool aCloseWithError)
 {
     const InfallibleTArray<PIndexedDBParent*>& idbParents =
         ManagedPIndexedDBParent();
     for (uint32_t i = 0; i < idbParents.Length(); ++i) {
         static_cast<IndexedDBParent*>(idbParents[i])->Disconnect();
     }
 
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -292,16 +292,20 @@ private:
     // otherwise.  If you pass a FOREGROUND* priority here, it's (hopefully)
     // unlikely that the process will be killed after this point.
     bool SetPriorityAndCheckIsAlive(hal::ProcessPriority aPriority);
 
     // Transform a pre-allocated app process into a "real" app
     // process, for the specified manifest URL.
     void TransformPreallocatedIntoApp(const nsAString& aAppManifestURL);
 
+    // Transform a pre-allocated app process into a browser process. If this
+    // returns false, the child process has died.
+    void TransformPreallocatedIntoBrowser();
+
     /**
      * Mark this ContentParent as dead for the purposes of Get*().
      * This method is idempotent.
      */
     void MarkAsDead();
 
     /**
      * Exit the subprocess and vamoose.  After this call IsAlive()
--- a/dom/media/IdpProxy.jsm
+++ b/dom/media/IdpProxy.jsm
@@ -155,16 +155,20 @@ IdpProxy.prototype = {
     this.channel = null;
     this.ready = false;
 
     this.counter = 0;
     this.tracking = {};
     this.pending = [];
   },
 
+  isSame: function(domain, protocol) {
+    return this.domain === domain && ((protocol || "default") === this.protocol);
+  },
+
   /**
    * Get a sandboxed iframe for hosting the idp-proxy's js. Create a message
    * channel down to the frame.
    *
    * @param errorCallback (function) a callback that will be invoked if there
    *                is a fatal error starting the proxy
    */
   start: function(errorCallback) {
@@ -255,13 +259,13 @@ IdpProxy.prototype = {
       this.trackingCopy[k](error);
     }, this);
     pendingCopy.forEach(function(p) {
       p.callback(error);
     }, this);
   },
 
   toString: function() {
-    return this.domain + '/' + this.protocol;
+    return this.domain + '/.../' + this.protocol;
   }
 };
 
 this.IdpProxy = IdpProxy;
--- a/dom/media/PeerConnectionIdp.jsm
+++ b/dom/media/PeerConnectionIdp.jsm
@@ -33,20 +33,26 @@ function PeerConnectionIdp(window, timeo
   // attributes are funny, the 'a' is case sensitive, the name isn't
   let pattern = "^a=[iI][dD][eE][nN][tT][iI][tT][yY]:(\\S+)";
   PeerConnectionIdp._identityPattern = new RegExp(pattern, "m");
   pattern = "^a=[fF][iI][nN][gG][eE][rR][pP][rR][iI][nN][tT]:(\\S+) (\\S+)";
   PeerConnectionIdp._fingerprintPattern = new RegExp(pattern, "m");
 })();
 
 PeerConnectionIdp.prototype = {
-  setIdentityProvider: function(
-      provider, protocol, username) {
+  setIdentityProvider: function(provider, protocol, username) {
     this.provider = provider;
-    this._idpchannel = new IdpProxy(provider, protocol, username);
+    this.username = username;
+    if (this._idpchannel) {
+      if (this._idpchannel.isSame(provider, protocol)) {
+        return;
+      }
+      this._idpchannel.close();
+    }
+    this._idpchannel = new IdpProxy(provider, protocol);
   },
 
   close: function() {
     this.assertion = null;
     this.provider = null;
     if (this._idpchannel) {
       this._idpchannel.close();
       this._idpchannel = null;
@@ -123,19 +129,17 @@ PeerConnectionIdp.prototype = {
     let identity = this._getIdentityFromSdp(sdp);
     let fingerprint = this._getFingerprintFromSdp(sdp);
     // it's safe to use the fingerprint from the SDP here,
     // only because we ensure that there is only one
     if (!fingerprint || !identity) {
       callback(null);
       return;
     }
-    if (!this._idpchannel) {
-      this.setIdentityProvider(identity.idp.domain, identity.idp.protocol);
-    }
+    this.setIdentityProvider(identity.idp.domain, identity.idp.protocol);
 
     this._verifyIdentity(identity.assertion, fingerprint, callback);
   },
 
   /**
    * Checks that the name in the identity provided by the IdP is OK.
    *
    * @param name (string) the name to validate
@@ -211,46 +215,42 @@ PeerConnectionIdp.prototype = {
       }
       if (this._checkVerifyResponse(message, fingerprint)) {
         callback(message);
       } else {
         callback(null);
       }
     }
 
-    this._sendToIdp("VERIFY", assertion, onVerification.bind(this));
+    let request = {
+      type: "VERIFY",
+      message: assertion
+    };
+    this._sendToIdp(request, onVerification.bind(this));
   },
 
   /**
    * Asks the IdP proxy for an identity assertion and, on success, enriches the
    * given SDP with an a=identity line and calls callback with the new SDP as
    * parameter. If no IdP is configured the original SDP (without a=identity
    * line) is passed to the callback.
    */
-  appendIdentityToSDP: function(
-      sdp, fingerprint, callback) {
+  appendIdentityToSDP: function(sdp, fingerprint, callback) {
     if (!this._idpchannel) {
       callback(sdp);
       return;
     }
 
     if (this.assertion) {
       callback(this.wrapSdp(sdp));
       return;
     }
 
     function onAssertion(assertion) {
-      if (!assertion) {
-        this._warning("RTC identity: assertion generation failure", null, 0);
-        callback(sdp);
-        return;
-      }
-
-      this.assertion = btoa(JSON.stringify(assertion));
-      callback(this.wrapSdp(sdp), this.assertion);
+      callback(this.wrapSdp(sdp), assertion);
     }
 
     this._getIdentityAssertion(fingerprint, onAssertion.bind(this));
   },
 
   /**
    * Inserts an identity assertion into the given SDP.
    */
@@ -261,51 +261,62 @@ PeerConnectionIdp.prototype = {
 
     // yes, we assume that this matches; if it doesn't something is *wrong*
     let match = sdp.match(PeerConnectionIdp._mLinePattern);
     return sdp.substring(0, match.index) +
       "a=identity:" + this.assertion + "\r\n" +
       sdp.substring(match.index);
   },
 
-  getIdentityAssertion: function(
-      fingerprint, callback) {
+  getIdentityAssertion: function(fingerprint, callback) {
     if (!this._idpchannel) {
       throw new Error("IdP not set");
     }
 
     this._getIdentityAssertion(fingerprint, callback);
   },
 
-  _getIdentityAssertion: function(
-      fingerprint, callback) {
+  _getIdentityAssertion: function(fingerprint, callback) {
     let [algorithm, digest] = fingerprint.split(" ");
     let message = {
       fingerprint: {
         algorithm: algorithm,
         digest: digest
       }
     };
-    this._sendToIdp("SIGN", JSON.stringify(message), callback);
+    let request = {
+      type: "SIGN",
+      message: JSON.stringify(message),
+      username: this.username
+    };
+
+    // catch the assertion, clean it up, warn if absent
+    function trapAssertion(assertion) {
+      if (!assertion) {
+        this._warning("RTC identity: assertion generation failure", null, 0);
+        this.assertion = null;
+      } else {
+        this.assertion = btoa(JSON.stringify(assertion));
+      }
+      callback(this.assertion);
+    }
+
+    this._sendToIdp(request, trapAssertion.bind(this));
   },
 
   /**
    * Packages a message and sends it to the IdP.
    */
-  _sendToIdp: function(type, message, callback) {
+  _sendToIdp: function(request, callback) {
     // this is not secure
     // but there are no good alternatives until bug 968335 lands
     // when that happens, change this to use the new mechanism
-    let origin = this._win.document.nodePrincipal.origin;
+    request.origin = this._win.document.nodePrincipal.origin;
 
-    this._idpchannel.send({
-      type: type,
-      message: message,
-      origin: origin
-    }, this._wrapCallback(callback));
+    this._idpchannel.send(request, this._wrapCallback(callback));
   },
 
   /**
    * Wraps a callback, adding a timeout and ensuring that the callback doesn't
    * receive any message other than one where the IdP generated a "SUCCESS"
    * response.
    */
   _wrapCallback: function(callback) {
--- a/dom/media/tests/identity/idp-proxy.js
+++ b/dom/media/tests/identity/idp-proxy.js
@@ -1,13 +1,14 @@
 (function(global) {
   "use strict";
 
   function IDPJS() {
     this.domain = window.location.host;
+    this.username = "someone@" + this.domain;
     // so rather than create a million different IdP configurations and litter
     // the world with files all containing near-identical code, let's use the
     // hash/URL fragment as a way of generating instructions for the IdP
     this.instructions = window.location.hash.replace("#", "").split(":");
     this.port = window.rtcwebIdentityPort;
     this.port.onmessage = this.receiveMessage.bind(this);
     this.sendResponse({
       type : "READY"
@@ -50,47 +51,58 @@
       this.port.postMessage(response);
     }.bind(this), this.getDelay());
   };
 
   IDPJS.prototype.receiveMessage = function(ev) {
     var message = ev.data;
     switch (message.type) {
     case "SIGN":
+      if (message.username) {
+        var at = message.username.indexOf("@");
+        if (at < 0) {
+          this.username = message.username + "@" + this.domain;
+        } else if (message.username.substring(at + 1) === this.domain) {
+          this.username = message.username;
+        }
+      }
       this.sendResponse({
         type : "SUCCESS",
         id : message.id,
         message : {
           idp : {
             domain : this.domain,
             protocol : "idp.html"
           },
           assertion : JSON.stringify({
-            identity : "someone@" + this.domain,
+            username : this.username,
             contents : message.message
           })
         }
       });
       break;
+
     case "VERIFY":
+      var payload = JSON.parse(message.message);
       this.sendResponse({
         type : "SUCCESS",
         id : message.id,
         message : {
           identity : {
-            name : "someone@" + this.domain,
-            displayname : "Someone"
+            name : payload.username
           },
-          contents : JSON.parse(message.message).contents
+          contents : payload.contents
         }
       });
       break;
+
     default:
       this.sendResponse({
         type : "ERROR",
+        id : message.id,
         error : JSON.stringify(message)
       });
       break;
     }
   };
 
   global.idp = new IDPJS();
 }(this));
--- a/dom/media/tests/identity/test_getIdentityAssertion.html
+++ b/dom/media/tests/identity/test_getIdentityAssertion.html
@@ -9,16 +9,24 @@
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     title: "getIdentityAssertion Tests"
   });
 
+function checkIdentity(assertion, identity) {
+  // here we dig into the payload, which means we need to know something
+  // about how the IdP actually works (not good in general, but OK here)
+  var assertion = JSON.parse(atob(assertion)).assertion;
+  var user = JSON.parse(assertion).username;
+  is(user, identity, "id should be '" + identity + "' is '" + user + "'");
+}
+
 var test;
 function theTest() {
   test = new PeerConnectionTest();
   test.setMediaConstraints([{audio: true}], [{audio: true}]);
   test.chain.append([
   [
     "GET_IDENTITY_ASSERTION_FAILS_WITHOUT_PROVIDER",
     function(test) {
@@ -27,36 +35,38 @@ function theTest() {
         test.next();
       });
     },
   ],
   [
     "GET_IDENTITY_ASSERTION_FIRES_EVENTUALLY_AND_SUBSEQUENTLY",
     function(test) {
       var fired = 0;
-      test.setIdentityProvider(test.pcLocal, 'example.com', 'idp.html', 'nobody');
-      test.pcLocal._pc.onidentityresult = function() {
+      test.setIdentityProvider(test.pcLocal, 'example.com', 'idp.html');
+      test.pcLocal._pc.onidentityresult = function(e) {
         fired++;
         if (fired == 1) {
           ok(true, "identityresult fired");
+          checkIdentity(e.assertion, 'someone@example.com');
         } else if (fired == 2) {
           ok(true, "identityresult fired 2x");
+          checkIdentity(e.assertion, 'someone@example.com');
           test.next();
         }
       };
       test.pcLocal._pc.getIdentityAssertion();
       test.pcLocal._pc.getIdentityAssertion();
     }
   ],
   [
     "GET_IDENTITY_ASSERTION_FAILS",
     function(test) {
       test.setIdentityProvider(test.pcLocal, 'example.com', 'idp.html#error');
       test.pcLocal._pc.onidentityresult = function(e) {
-       ok(false, "Should not get an identity result");
+        ok(false, "Should not get an identity result");
         test.next();
       };
       test.pcLocal._pc.getIdentityAssertion(function(err) {
         ok(err, "Got error callback from getIdentityAssertion");
         test.next();
       });
     }
   ],
@@ -68,16 +78,30 @@ function theTest() {
         ok(false, "Should not get an identity result");
         test.next();
       };
       test.pcLocal._pc.getIdentityAssertion(function(err) {
         ok(err, "Got error callback from getIdentityAssertion");
         test.next();
       });
     }
+  ],
+  [
+    "GET_IDENTITY_ASSERTION_WITH_SPECIFIC_NAME",
+    function(test) {
+      test.setIdentityProvider(test.pcLocal, 'example.com', 'idp.html', 'user@example.com');
+      test.pcLocal._pc.onidentityresult = function(e) {
+        checkIdentity(e.assertion, 'user@example.com');
+        test.next();
+      };
+      test.pcLocal._pc.getIdentityAssertion(function(err) {
+        ok(false, "Got error callback from getIdentityAssertion");
+        test.next();
+      });
+    }
   ]
   ]);
   test.run();
 }
 runTest(theTest);
 
 </script>
 </pre>
--- a/dom/media/tests/mochitest/head.js
+++ b/dom/media/tests/mochitest/head.js
@@ -122,17 +122,17 @@ function runTest(aCallback) {
       ['media.peerconnection.identity.enabled', true],
       ['media.peerconnection.identity.timeout', 3000],
       ['media.navigator.permission.disabled', true]]
     }, function () {
       try {
         aCallback();
       }
       catch (err) {
-        unexpectedCallbackAndFinish()(err);
+        generateErrorCallback()(err);
       }
     });
   } else {
     // Steeplechase, let it call the callback.
     window.run_test = function(is_initiator) {
       var options = {is_local: is_initiator,
                      is_remote: !is_initiator};
       aCallback(options);
@@ -209,28 +209,34 @@ function getBlobContent(blob, onSuccess)
  * Generates a callback function fired only under unexpected circumstances
  * while running the tests. The generated function kills off the test as well
  * gracefully.
  *
  * @param {String} [message]
  *        An optional message to show if no object gets passed into the
  *        generated callback method.
  */
-function unexpectedCallbackAndFinish(message) {
+function generateErrorCallback(message) {
   var stack = new Error().stack.split("\n");
   stack.shift(); // Don't include this instantiation frame
 
   /**
    * @param {object} aObj
    *        The object fired back from the callback
    */
   return function (aObj) {
-    if (aObj && aObj.name && aObj.message) {
-      ok(false, "Unexpected callback for '" + aObj.name + "' with message = '" +
-         aObj.message + "' at " + JSON.stringify(stack));
+    if (aObj) {
+      if (aObj.name && aObj.message) {
+        ok(false, "Unexpected callback for '" + aObj.name +
+           "' with message = '" + aObj.message + "' at " +
+           JSON.stringify(stack));
+      } else {
+        ok(false, "Unexpected callback with = '" + aObj +
+           "' at: " + JSON.stringify(stack));
+      }
     } else {
       ok(false, "Unexpected callback with message = '" + message +
          "' at: " + JSON.stringify(stack));
     }
     SimpleTest.finish();
   }
 }
 
--- a/dom/media/tests/mochitest/pc.js
+++ b/dom/media/tests/mochitest/pc.js
@@ -1269,17 +1269,17 @@ PeerConnectionWrapper.prototype = {
 
           if (constraints.video) {
             type += 'video';
           }
 
           self.attachMedia(stream, type, 'local');
 
           _getAllUserMedia(constraintsList, index + 1);
-        }, unexpectedCallbackAndFinish());
+        }, generateErrorCallback());
       } else {
         onSuccess();
       }
     }
 
     info("Get " + this.constraints.length + " local streams");
     _getAllUserMedia(this.constraints, 0);
   },
@@ -1318,64 +1318,64 @@ PeerConnectionWrapper.prototype = {
    */
   createOffer : function PCW_createOffer(onSuccess) {
     var self = this;
 
     this._pc.createOffer(function (offer) {
       info("Got offer: " + JSON.stringify(offer));
       self._last_offer = offer;
       onSuccess(offer);
-    }, unexpectedCallbackAndFinish(), this.offerConstraints);
+    }, generateErrorCallback(), this.offerConstraints);
   },
 
   /**
    * Creates an answer and automatically handles the failure case.
    *
    * @param {function} onSuccess
    *        Callback to execute if the answer was created successfully
    */
   createAnswer : function PCW_createAnswer(onSuccess) {
     var self = this;
 
     this._pc.createAnswer(function (answer) {
       info(self + ": Got answer: " + JSON.stringify(answer));
       self._last_answer = answer;
       onSuccess(answer);
-    }, unexpectedCallbackAndFinish());
+    }, generateErrorCallback());
   },
 
   /**
    * Sets the local description and automatically handles the failure case.
    *
    * @param {object} desc
    *        mozRTCSessionDescription for the local description request
    * @param {function} onSuccess
    *        Callback to execute if the local description was set successfully
    */
   setLocalDescription : function PCW_setLocalDescription(desc, onSuccess) {
     var self = this;
     this._pc.setLocalDescription(desc, function () {
       info(self + ": Successfully set the local description");
       onSuccess();
-    }, unexpectedCallbackAndFinish());
+    }, generateErrorCallback());
   },
 
   /**
    * Tries to set the local description and expect failure. Automatically
    * causes the test case to fail if the call succeeds.
    *
    * @param {object} desc
    *        mozRTCSessionDescription for the local description request
    * @param {function} onFailure
    *        Callback to execute if the call fails.
    */
   setLocalDescriptionAndFail : function PCW_setLocalDescriptionAndFail(desc, onFailure) {
     var self = this;
     this._pc.setLocalDescription(desc,
-      unexpectedCallbackAndFinish("setLocalDescription should have failed."),
+      generateErrorCallback("setLocalDescription should have failed."),
       function (err) {
         info(self + ": As expected, failed to set the local description");
         onFailure(err);
     });
   },
 
   /**
    * Sets the remote description and automatically handles the failure case.
@@ -1385,32 +1385,32 @@ PeerConnectionWrapper.prototype = {
    * @param {function} onSuccess
    *        Callback to execute if the remote description was set successfully
    */
   setRemoteDescription : function PCW_setRemoteDescription(desc, onSuccess) {
     var self = this;
     this._pc.setRemoteDescription(desc, function () {
       info(self + ": Successfully set remote description");
       onSuccess();
-    }, unexpectedCallbackAndFinish());
+    }, generateErrorCallback());
   },
 
   /**
    * Tries to set the remote description and expect failure. Automatically
    * causes the test case to fail if the call succeeds.
    *
    * @param {object} desc
    *        mozRTCSessionDescription for the remote description request
    * @param {function} onFailure
    *        Callback to execute if the call fails.
    */
   setRemoteDescriptionAndFail : function PCW_setRemoteDescriptionAndFail(desc, onFailure) {
     var self = this;
     this._pc.setRemoteDescription(desc,
-      unexpectedCallbackAndFinish("setRemoteDescription should have failed."),
+      generateErrorCallback("setRemoteDescription should have failed."),
       function (err) {
         info(self + ": As expected, failed to set the remote description");
         onFailure(err);
     });
   },
 
   /**
    * Adds an ICE candidate and automatically handles the failure case.
@@ -1421,33 +1421,33 @@ PeerConnectionWrapper.prototype = {
    *        Callback to execute if the local description was set successfully
    */
   addIceCandidate : function PCW_addIceCandidate(candidate, onSuccess) {
     var self = this;
 
     this._pc.addIceCandidate(candidate, function () {
       info(self + ": Successfully added an ICE candidate");
       onSuccess();
-    }, unexpectedCallbackAndFinish());
+    }, generateErrorCallback());
   },
 
   /**
    * Tries to add an ICE candidate and expects failure. Automatically
    * causes the test case to fail if the call succeeds.
    *
    * @param {object} candidate
    *        SDP candidate
    * @param {function} onFailure
    *        Callback to execute if the call fails.
    */
   addIceCandidateAndFail : function PCW_addIceCandidateAndFail(candidate, onFailure) {
     var self = this;
 
     this._pc.addIceCandidate(candidate,
-      unexpectedCallbackAndFinish("addIceCandidate should have failed."),
+      generateErrorCallback("addIceCandidate should have failed."),
       function (err) {
         info(self + ": As expected, failed to add an ICE candidate");
         onFailure(err);
     }) ;
   },
 
   /**
    * Returns if the ICE the connection state is "connected".
@@ -1563,17 +1563,17 @@ PeerConnectionWrapper.prototype = {
    */
   getStats : function PCW_getStats(selector, onSuccess) {
     var self = this;
 
     this._pc.getStats(selector, function(stats) {
       info(self + ": Got stats: " + JSON.stringify(stats));
       self._last_stats = stats;
       onSuccess(stats);
-    }, unexpectedCallbackAndFinish());
+    }, generateErrorCallback());
   },
 
   /**
    * Checks that we are getting the media streams we expect.
    *
    * @param {object} stats
    *        The stats to check from this PeerConnectionWrapper
    */
--- a/dom/media/tests/mochitest/templates.js
+++ b/dom/media/tests/mochitest/templates.js
@@ -142,60 +142,60 @@ var commandsPeerConnection = [
       var myTest = test;
       var myPc = myTest.pcLocal;
 
       function onIceConnectedSuccess () {
         ok(true, "pc_local: ICE switched to 'connected' state");
         myTest.next();
       };
       function onIceConnectedFailed () {
-        info("pc_local: SDP offer: " + myTest._local_offer.sdp.replace(/[\r]/g, ''));
-        info("pc_local: SDP answer: " + myTest._remote_answer.sdp.replace(/[\r]/g, ''));
+        dump("ERROR: pc_local: SDP offer: " + myTest._local_offer.sdp.replace(/[\r]/g, ''));
+        dump("ERROR: pc_local: SDP answer: " + myTest._remote_answer.sdp.replace(/[\r]/g, ''));
         ok(false, "pc_local: ICE failed to switch to 'connected' state: " + myPc.iceConnectionState);
         myTest.next();
       };
 
       if (myPc.isIceConnected()) {
         ok(true, "pc_local: ICE is in connected state");
         myTest.next();
       } else if (myPc.isIceConnectionPending()) {
         myPc.waitForIceConnected(onIceConnectedSuccess, onIceConnectedFailed);
       } else {
-        info("pc_local: SDP offer: " + myTest._local_offer.sdp.replace(/[\r]/g, ''));
-        info("pc_local: SDP answer: " + myTest._remote_answer.sdp.replace(/[\r]/g, ''));
+        dump("ERROR: pc_local: SDP offer: " + myTest._local_offer.sdp.replace(/[\r]/g, ''));
+        dump("ERROR: pc_local: SDP answer: " + myTest._remote_answer.sdp.replace(/[\r]/g, ''));
         ok(false, "pc_local: ICE is already in bad state: " + myPc.iceConnectionState);
         myTest.next();
       }
     }
   ],
   [
     'PC_REMOTE_WAIT_FOR_ICE_CONNECTED',
     function (test) {
       var myTest = test;
       var myPc = myTest.pcRemote;
 
       function onIceConnectedSuccess () {
         ok(true, "pc_remote: ICE switched to 'connected' state");
         myTest.next();
       };
       function onIceConnectedFailed () {
-        info("pc_remote: SDP offer: " + myTest._local_offer.sdp.replace(/[\r]/g, ''));
-        info("pc_remote: SDP answer: " + myTest._remote_answer.sdp.replace(/[\r]/g, ''));
+        dump("ERROR: pc_remote: SDP offer: " + myTest._local_offer.sdp.replace(/[\r]/g, ''));
+        dump("ERROR: pc_remote: SDP answer: " + myTest._remote_answer.sdp.replace(/[\r]/g, ''));
         ok(false, "pc_remote: ICE failed to switch to 'connected' state: " + myPc.iceConnectionState);
         myTest.next();
       };
 
       if (myPc.isIceConnected()) {
         ok(true, "pc_remote: ICE is in connected state");
         myTest.next();
       } else if (myPc.isIceConnectionPending()) {
         myPc.waitForIceConnected(onIceConnectedSuccess, onIceConnectedFailed);
       } else {
-        info("pc_remote: SDP offer: " + myTest._local_offer.sdp.replace(/[\r]/g, ''));
-        info("pc_remote: SDP answer: " + myTest._remote_answer.sdp.replace(/[\r]/g, ''));
+        dump("ERROR: pc_remote: SDP offer: " + myTest._local_offer.sdp.replace(/[\r]/g, ''));
+        dump("ERROR: pc_remote: SDP answer: " + myTest._remote_answer.sdp.replace(/[\r]/g, ''));
         ok(false, "pc_remote: ICE is already in bad state: " + myPc.iceConnectionState);
         myTest.next();
       }
     }
   ],
   [
     'PC_LOCAL_CHECK_MEDIA_STREAMS',
     function (test) {
--- a/dom/media/tests/mochitest/test_dataChannel_noOffer.html
+++ b/dom/media/tests/mochitest/test_dataChannel_noOffer.html
@@ -19,15 +19,15 @@
     // necessary to circumvent bug 864109
     var options = { mandatory: { OfferToReceiveAudio: true} };
 
     pc.createOffer(function (offer) {
       ok(!offer.sdp.contains("m=application"),
         "m=application is not contained in the SDP");
 
       SimpleTest.finish();
-    }, unexpectedCallbackAndFinish(), options);
+    }, generateErrorCallback(), options);
   }, true);
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_basicAudio.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicAudio.html
@@ -29,18 +29,18 @@ https://bugzilla.mozilla.org/show_bug.cg
 
     getUserMedia(constraints, function (aStream) {
       checkMediaStreamTracks(constraints, aStream);
 
       var playback = new LocalMediaStreamPlayback(testAudio, aStream);
       playback.playMedia(false, function () {
         aStream.stop();
         SimpleTest.finish();
-      }, unexpectedCallbackAndFinish());
+      }, generateErrorCallback());
 
-    }, unexpectedCallbackAndFinish());
+    }, generateErrorCallback());
 
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_basicVideo.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicVideo.html
@@ -29,18 +29,18 @@ https://bugzilla.mozilla.org/show_bug.cg
 
     getUserMedia(constraints, function (aStream) {
       checkMediaStreamTracks(constraints, aStream);
 
       var playback = new LocalMediaStreamPlayback(testVideo, aStream);
       playback.playMedia(false, function () {
         aStream.stop();
         SimpleTest.finish();
-      }, unexpectedCallbackAndFinish());
+      }, generateErrorCallback());
 
-    }, unexpectedCallbackAndFinish());
+    }, generateErrorCallback());
 
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_basicVideoAudio.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicVideoAudio.html
@@ -29,17 +29,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 
     getUserMedia(constraints, function (aStream) {
       checkMediaStreamTracks(constraints, aStream);
 
       var playback = new LocalMediaStreamPlayback(testVideoAudio, aStream);
       playback.playMedia(false, function () {
         aStream.stop();
         SimpleTest.finish();
-      }, unexpectedCallbackAndFinish());
+      }, generateErrorCallback());
 
-    }, unexpectedCallbackAndFinish());
+    }, generateErrorCallback());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_gumWithinGum.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_gumWithinGum.html
@@ -37,21 +37,21 @@ https://bugzilla.mozilla.org/show_bug.cg
           var testAudio = document.getElementById('testAudio');
           var audioStreamPlayback = new LocalMediaStreamPlayback(testAudio,
             audioStream);
 
           audioStreamPlayback.playMedia(false, function() {
             audioStream.stop();
             videoStream.stop();
             SimpleTest.finish();
-          }, unexpectedCallbackAndFinish());
+          }, generateErrorCallback());
 
-        }, unexpectedCallbackAndFinish());
+        }, generateErrorCallback());
 
-      }, unexpectedCallbackAndFinish());
+      }, generateErrorCallback());
 
-    }, unexpectedCallbackAndFinish());
+    }, generateErrorCallback());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_playAudioTwice.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_playAudioTwice.html
@@ -29,19 +29,19 @@ https://bugzilla.mozilla.org/show_bug.cg
       var audioStreamPlayback = new LocalMediaStreamPlayback(testAudio,
         audioStream);
 
       audioStreamPlayback.playMedia(false, function() {
 
         audioStreamPlayback.playMedia(true, function() {
           audioStream.stop();
           SimpleTest.finish();
-        }, unexpectedCallbackAndFinish());
+        }, generateErrorCallback());
 
-      }, unexpectedCallbackAndFinish());
+      }, generateErrorCallback());
 
-    }, unexpectedCallbackAndFinish());
+    }, generateErrorCallback());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_playVideoAudioTwice.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_playVideoAudioTwice.html
@@ -28,19 +28,19 @@ https://bugzilla.mozilla.org/show_bug.cg
       var testVideo = document.getElementById('testVideo');
       var streamPlayback = new LocalMediaStreamPlayback(testVideo, stream);
 
       streamPlayback.playMedia(false, function() {
 
         streamPlayback.playMedia(true, function() {
           stream.stop();
           SimpleTest.finish();
-        }, unexpectedCallbackAndFinish());
+        }, generateErrorCallback());
 
-      }, unexpectedCallbackAndFinish());
+      }, generateErrorCallback());
 
-    }, unexpectedCallbackAndFinish());
+    }, generateErrorCallback());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_playVideoTwice.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_playVideoTwice.html
@@ -29,19 +29,19 @@ https://bugzilla.mozilla.org/show_bug.cg
       var videoStreamPlayback = new LocalMediaStreamPlayback(testVideo,
         videoStream);
 
       videoStreamPlayback.playMedia(false, function() {
 
         videoStreamPlayback.playMedia(true, function() {
           videoStream.stop();
           SimpleTest.finish();
-        }, unexpectedCallbackAndFinish());
+        }, generateErrorCallback());
 
-      }, unexpectedCallbackAndFinish());
+      }, generateErrorCallback());
 
-    }, unexpectedCallbackAndFinish());
+    }, generateErrorCallback());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_stopAudioStream.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_stopAudioStream.html
@@ -25,16 +25,16 @@ https://bugzilla.mozilla.org/show_bug.cg
    * call stop() on the stream, and successfully get an ended event fired.
    */
   runTest(function () {
     getUserMedia({audio: true}, function(stream) {
       var testAudio = document.getElementById('testAudio');
       var audioStreamPlayback = new LocalMediaStreamPlayback(testAudio, stream);
 
       audioStreamPlayback.playMediaWithStreamStop(false, SimpleTest.finish,
-        unexpectedCallbackAndFinish());
-    }, unexpectedCallbackAndFinish());
+        generateErrorCallback());
+    }, generateErrorCallback());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_stopAudioStreamWithFollowupAudio.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_stopAudioStreamWithFollowupAudio.html
@@ -32,21 +32,21 @@ https://bugzilla.mozilla.org/show_bug.cg
 
       streamPlayback.playMediaWithStreamStop(false, function() {
         getUserMedia({audio: true}, function(secondStream) {
           streamPlayback.mediaStream = secondStream;
 
           streamPlayback.playMedia(false, function() {
             secondStream.stop();
             SimpleTest.finish();
-          }, unexpectedCallbackAndFinish());
+          }, generateErrorCallback());
 
-        }, unexpectedCallbackAndFinish());
+        }, generateErrorCallback());
 
-      }, unexpectedCallbackAndFinish());
+      }, generateErrorCallback());
 
-    }, unexpectedCallbackAndFinish());
+    }, generateErrorCallback());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStream.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStream.html
@@ -25,16 +25,16 @@ https://bugzilla.mozilla.org/show_bug.cg
    * ended event fired.
    */
   runTest(function () {
     getUserMedia({video: true, audio: true}, function(stream) {
       var testVideo = document.getElementById('testVideo');
       var streamPlayback = new LocalMediaStreamPlayback(testVideo, stream);
 
       streamPlayback.playMediaWithStreamStop(false, SimpleTest.finish,
-        unexpectedCallbackAndFinish());
-    }, unexpectedCallbackAndFinish());
+        generateErrorCallback());
+    }, generateErrorCallback());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStreamWithFollowupVideoAudio.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStreamWithFollowupVideoAudio.html
@@ -32,21 +32,21 @@ https://bugzilla.mozilla.org/show_bug.cg
 
       streamPlayback.playMediaWithStreamStop(false, function() {
         getUserMedia({video: true, audio: true}, function(secondStream) {
           streamPlayback.mediaStream = secondStream;
 
           streamPlayback.playMedia(false, function() {
             secondStream.stop();
             SimpleTest.finish();
-          }, unexpectedCallbackAndFinish());
+          }, generateErrorCallback());
 
-        }, unexpectedCallbackAndFinish());
+        }, generateErrorCallback());
 
-      }, unexpectedCallbackAndFinish());
+      }, generateErrorCallback());
 
-    }, unexpectedCallbackAndFinish());
+    }, generateErrorCallback());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_stopVideoStream.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_stopVideoStream.html
@@ -25,16 +25,16 @@ https://bugzilla.mozilla.org/show_bug.cg
    * call stop() on the stream, and successfully get an ended event fired.
    */
   runTest(function () {
     getUserMedia({video: true}, function(stream) {
       var testVideo = document.getElementById('testVideo');
       var videoStreamPlayback = new LocalMediaStreamPlayback(testVideo, stream);
 
       videoStreamPlayback.playMediaWithStreamStop(false, SimpleTest.finish,
-        unexpectedCallbackAndFinish());
-    }, unexpectedCallbackAndFinish());
+        generateErrorCallback());
+    }, generateErrorCallback());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_stopVideoStreamWithFollowupVideo.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_stopVideoStreamWithFollowupVideo.html
@@ -33,21 +33,21 @@ https://bugzilla.mozilla.org/show_bug.cg
 
       streamPlayback.playMediaWithStreamStop(false, function() {
         getUserMedia({video: true}, function(secondStream) {
           streamPlayback.mediaStream = secondStream;
 
           streamPlayback.playMedia(false, function() {
             secondStream.stop();
             SimpleTest.finish();
-          }, unexpectedCallbackAndFinish());
+          }, generateErrorCallback());
 
-        }, unexpectedCallbackAndFinish());
+        }, generateErrorCallback());
 
-      }, unexpectedCallbackAndFinish());
+      }, generateErrorCallback());
 
-    }, unexpectedCallbackAndFinish());
+    }, generateErrorCallback());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_peerConnection_errorCallbacks.html
+++ b/dom/media/tests/mochitest/test_peerConnection_errorCallbacks.html
@@ -20,45 +20,45 @@
       ok(err.message && err.message.length, "Error message = " + err.message);
       nextStep();
     }
   };
 
   function testCreateAnswerError() {
     var pc = new mozRTCPeerConnection();
     info ("Testing createAnswer error callback");
-    pc.createAnswer(unexpectedCallbackAndFinish("createAnswer before offer should fail"),
+    pc.createAnswer(generateErrorCallback("createAnswer before offer should fail"),
                     errorCallback(testSetLocalDescriptionError));
   };
 
   function testSetLocalDescriptionError() {
     var pc = new mozRTCPeerConnection();
     info ("Testing setLocalDescription error callback");
     pc.setLocalDescription(new mozRTCSessionDescription({ sdp: "Picklechips!",
                                                           type: "offer" }),
-      unexpectedCallbackAndFinish("setLocalDescription with nonsense SDP should fail"),
+      generateErrorCallback("setLocalDescription with nonsense SDP should fail"),
       errorCallback(testSetRemoteDescriptionError));
   };
 
   function testSetRemoteDescriptionError() {
     var pc = new mozRTCPeerConnection();
     info ("Testing setRemoteDescription error callback");
     pc.setRemoteDescription(new mozRTCSessionDescription({ sdp: "Who?",
                                                            type: "offer" }),
-      unexpectedCallbackAndFinish("setRemoteDescription with nonsense SDP should fail"),
+      generateErrorCallback("setRemoteDescription with nonsense SDP should fail"),
       errorCallback(testAddIceCandidateError));
   };
 
   function testAddIceCandidateError() {
     var pc = new mozRTCPeerConnection();
     info ("Testing addIceCandidate error callback");
     pc.addIceCandidate(new mozRTCIceCandidate({ candidate: "Pony Lords, jump!",
                                                 sdpMid: "whee",
                                                 sdpMLineIndex: 1 }),
-      unexpectedCallbackAndFinish("addIceCandidate with nonsense candidate should fail"),
+      generateErrorCallback("addIceCandidate with nonsense candidate should fail"),
       errorCallback(SimpleTest.finish));
   };
 
   // No test for createOffer errors -- there's nothing we can do at this
   // level to evoke an error in createOffer.
 
   runTest(function () {
     testCreateAnswerError();
--- a/dom/media/tests/mochitest/test_peerConnection_throwInCallbacks.html
+++ b/dom/media/tests/mochitest/test_peerConnection_throwInCallbacks.html
@@ -70,15 +70,15 @@
     window.onerror = oldOnError;
     is(error_count, 7, "Seven expected errors verified.");
     SimpleTest.finish();
   }
 
   function getFail() {
     return function (err) {
       window.onerror = oldOnError;
-      unexpectedCallbackAndFinish()(err);
+      generateErrorCallback()(err);
     };
   }
 </script>
 </pre>
 </body>
 </html>
--- a/dom/mobilemessage/src/ipc/SmsParent.cpp
+++ b/dom/mobilemessage/src/ipc/SmsParent.cpp
@@ -19,16 +19,17 @@
 #include "MobileMessageThread.h"
 #include "nsIDOMFile.h"
 #include "mozilla/dom/ipc/Blob.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/mobilemessage/Constants.h" // For MessageType
 #include "nsContentUtils.h"
 #include "nsTArrayHelpers.h"
 #include "nsCxPusher.h"
+#include "xpcpublic.h"
 #include "nsServiceManagerUtils.h"
 
 namespace mozilla {
 namespace dom {
 namespace mobilemessage {
 
 static JSObject*
 MmsAttachmentDataToJSObject(JSContext* aContext,
@@ -472,17 +473,22 @@ SmsRequestParent::DoRequest(const SendMe
       smsService->Send(req.serviceId(), req.number(), req.message(),
                        req.silent(), this);
     }
     break;
   case SendMessageRequest::TSendMmsMessageRequest: {
       nsCOMPtr<nsIMmsService> mmsService = do_GetService(MMS_SERVICE_CONTRACTID);
       NS_ENSURE_TRUE(mmsService, true);
 
+      // There are cases (see bug 981202) where this is called with no JS on the
+      // stack. And since mmsService might be JS-Implemented, we need to pass a
+      // jsval to ::Send. Only system code should be looking at the result here,
+      // so we just create it in the System-Principaled Junk Scope.
       AutoJSContext cx;
+      JSAutoCompartment ac(cx, xpc::GetJunkScope());
       JS::Rooted<JS::Value> params(cx);
       const SendMmsMessageRequest &req = aRequest.get_SendMmsMessageRequest();
       if (!GetParamsFromSendMmsMessageRequest(cx,
                                               req,
                                               params.address())) {
         NS_WARNING("SmsRequestParent: Fail to build MMS params.");
         return true;
       }
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -974,17 +974,17 @@ public:
     mPermitSubpixelAA = aPermitSubpixelAA;
   }
 
   bool GetPermitSubpixelAA() {
     return mPermitSubpixelAA;
   }
 
 #ifdef USE_SKIA_GPU
-  virtual void InitWithGrContext(GrContext* aGrContext,
+  virtual bool InitWithGrContext(GrContext* aGrContext,
                                  const IntSize &aSize,
                                  SurfaceFormat aFormat)
   {
     MOZ_CRASH();
   }
 #endif
 
 protected:
--- a/gfx/2d/DrawTargetCG.cpp
+++ b/gfx/2d/DrawTargetCG.cpp
@@ -297,38 +297,53 @@ DrawTargetCG::DrawSurface(SourceSurface 
 
   CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
   UnboundnessFixer fixer;
   CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
   CGContextSetAlpha(cg, aDrawOptions.mAlpha);
   CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
 
   CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
-  CGImageRef image = GetRetainedImageFromSourceSurface(aSurface);
-
-  /* we have two options here:
-   *  - create a subimage -- this is slower
-   *  - fancy things with clip and different dest rects */
-  CGImageRef subimage = CGImageCreateWithImageInRect(image, RectToCGRect(aSource));
-  CGImageRelease(image);
-
-  CGContextScaleCTM(cg, 1, -1);
-
-  CGRect flippedRect = CGRectMake(aDest.x, -(aDest.y + aDest.height),
-                                  aDest.width, aDest.height);
 
   CGContextSetInterpolationQuality(cg, InterpolationQualityFromFilter(aSurfOptions.mFilter));
 
-  CGContextDrawImage(cg, flippedRect, subimage);
+  CGImageRef image = GetRetainedImageFromSourceSurface(aSurface);
+
+  if (aSurfOptions.mFilter == Filter::POINT) {
+    CGImageRef subimage = CGImageCreateWithImageInRect(image, RectToCGRect(aSource));
+    CGImageRelease(image);
+
+    CGContextScaleCTM(cg, 1, -1);
+
+    CGRect flippedRect = CGRectMake(aDest.x, -(aDest.y + aDest.height),
+                                    aDest.width, aDest.height);
+
+    CGContextDrawImage(cg, flippedRect, subimage);
+    CGImageRelease(subimage);
+  } else {
+    CGRect destRect = CGRectMake(aDest.x, aDest.y, aDest.width, aDest.height);
+    CGContextClipToRect(cg, destRect);
+
+    float xScale = aSource.width / aDest.width;
+    float yScale = aSource.height / aDest.height;
+    CGContextTranslateCTM(cg, aDest.x - aSource.x / xScale, aDest.y - aSource.y / yScale);
+
+    CGRect adjustedDestRect = CGRectMake(0, 0, CGImageGetWidth(image) / xScale,
+                                         CGImageGetHeight(image) / yScale);
+
+    CGContextTranslateCTM(cg, 0, CGRectGetHeight(adjustedDestRect));
+    CGContextScaleCTM(cg, 1, -1);
+
+    CGContextDrawImage(cg, adjustedDestRect, image);
+    CGImageRelease(image);
+  }
 
   fixer.Fix(mCg);
 
   CGContextRestoreGState(mCg);
-
-  CGImageRelease(subimage);
 }
 
 TemporaryRef<FilterNode>
 DrawTargetCG::CreateFilter(FilterType aType)
 {
   return FilterNodeSoftware::Create(aType);
 }
 
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -654,17 +654,17 @@ DrawTargetSkia::Init(const IntSize &aSiz
   mSize = aSize;
 
   mCanvas = canvas.get();
   mFormat = aFormat;
   return true;
 }
 
 #ifdef USE_SKIA_GPU
-void
+bool
 DrawTargetSkia::InitWithGrContext(GrContext* aGrContext,
                                   const IntSize &aSize,
                                   SurfaceFormat aFormat)
 {
   MOZ_ASSERT(aGrContext, "null GrContext");
 
   mGrContext = aGrContext;
 
@@ -676,22 +676,27 @@ DrawTargetSkia::InitWithGrContext(GrCont
   targetDescriptor.fFlags = kRenderTarget_GrTextureFlagBit;
   targetDescriptor.fWidth = mSize.width;
   targetDescriptor.fHeight = mSize.height;
   targetDescriptor.fConfig = GfxFormatToGrConfig(mFormat);
   targetDescriptor.fOrigin = kBottomLeft_GrSurfaceOrigin;
   targetDescriptor.fSampleCnt = 0;
 
   SkAutoTUnref<GrTexture> skiaTexture(mGrContext->createUncachedTexture(targetDescriptor, NULL, 0));
+  if (!skiaTexture) {
+    return false;
+  }
 
   mTexture = (uint32_t)skiaTexture->getTextureHandle();
 
   SkAutoTUnref<SkBaseDevice> device(new SkGpuDevice(mGrContext.get(), skiaTexture->asRenderTarget()));
   SkAutoTUnref<SkCanvas> canvas(new SkCanvas(device.get()));
   mCanvas = canvas.get();
+
+  return true;
 }
 
 #endif
 
 void
 DrawTargetSkia::Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat)
 {
   SkAlphaType alphaType = kPremul_SkAlphaType;
--- a/gfx/2d/DrawTargetSkia.h
+++ b/gfx/2d/DrawTargetSkia.h
@@ -101,17 +101,17 @@ public:
   virtual TemporaryRef<FilterNode> CreateFilter(FilterType aType);
   virtual void SetTransform(const Matrix &aTransform);
   virtual void *GetNativeSurface(NativeSurfaceType aType);
 
   bool Init(const IntSize &aSize, SurfaceFormat aFormat);
   void Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat);
 
 #ifdef USE_SKIA_GPU
-  void InitWithGrContext(GrContext* aGrContext,
+  bool InitWithGrContext(GrContext* aGrContext,
                          const IntSize &aSize,
                          SurfaceFormat aFormat) MOZ_OVERRIDE;
 #endif
 
   operator std::string() const {
     std::stringstream stream;
     stream << "DrawTargetSkia(" << this << ")";
     return stream.str();
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -585,19 +585,20 @@ Factory::D2DCleanup()
 #endif // XP_WIN
 
 #ifdef USE_SKIA_GPU
 TemporaryRef<DrawTarget>
 Factory::CreateDrawTargetSkiaWithGrContext(GrContext* aGrContext,
                                            const IntSize &aSize,
                                            SurfaceFormat aFormat)
 {
-  DrawTargetSkia* newDrawTargetSkia = new DrawTargetSkia();
-  newDrawTargetSkia->InitWithGrContext(aGrContext, aSize, aFormat);
-  RefPtr<DrawTarget> newTarget = newDrawTargetSkia;
+  RefPtr<DrawTarget> newTarget = new DrawTargetSkia();
+  if (!newTarget->InitWithGrContext(aGrContext, aSize, aFormat)) {
+    return nullptr;
+  }
   return newTarget;
 }
 
 #endif // USE_SKIA_GPU
 
 void
 Factory::PurgeAllCaches()
 {
--- a/gfx/2d/SourceSurfaceD2D.cpp
+++ b/gfx/2d/SourceSurfaceD2D.cpp
@@ -163,21 +163,26 @@ DataSourceSurfaceD2D::DataSourceSurfaceD
                                                                &rtProps,
                                                                byRef(renderTarget));
   if (FAILED(hr)) {
     gfxWarning() << "Failed to create render target. Code: " << hr;
     return;
   }
 
   renderTarget->BeginDraw();
+  renderTarget->Clear(D2D1::ColorF(0, 0.0f));
   renderTarget->DrawBitmap(aSourceSurface->mBitmap,
                            D2D1::RectF(0, 0,
                                        Float(mSize.width),
                                        Float(mSize.height)));
-  renderTarget->EndDraw();
+  hr = renderTarget->EndDraw();
+  if (FAILED(hr)) {
+    gfxWarning() << "Failed to draw bitmap. Code: " << hr;
+    return;
+  }
 
   desc.CPUAccessFlags = D3D10_CPU_ACCESS_READ | D3D10_CPU_ACCESS_WRITE;
   desc.Usage = D3D10_USAGE_STAGING;
   desc.BindFlags = 0;
   hr = aSourceSurface->mDevice->CreateTexture2D(&desc, nullptr, byRef(mTexture));
   if (FAILED(hr)) {
     gfxWarning() << "Failed to create staging texture. Code: " << hr;
     mTexture = nullptr;
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -369,18 +369,18 @@ GLContextEGL::MakeCurrentImpl(bool aForc
                               ? mSurfaceOverride
                               : mSurface;
         if (surface == EGL_NO_SURFACE) {
             return false;
         }
         succeeded = sEGLLibrary.fMakeCurrent(EGL_DISPLAY(),
                                               surface, surface,
                                               mContext);
-        int eglError = sEGLLibrary.fGetError();
         if (!succeeded) {
+            int eglError = sEGLLibrary.fGetError();
             if (eglError == LOCAL_EGL_CONTEXT_LOST) {
                 mContextLost = true;
                 NS_WARNING("EGL context has been lost.");
             } else {
                 NS_WARNING("Failed to make GL context current!");
 #ifdef DEBUG
                 printf_stderr("EGL Error: 0x%04x\n", eglError);
 #endif
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -4,16 +4,19 @@
 
 #include "GLLibraryEGL.h"
 
 #include "gfxCrashReporterUtils.h"
 #include "mozilla/Preferences.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsPrintfCString.h"
+#ifdef XP_WIN
+#include "nsWindowsHelpers.h"
+#endif
 #include "prenv.h"
 #include "GLContext.h"
 #include "gfxPrefs.h"
 
 namespace mozilla {
 namespace gl {
 
 GLLibraryEGL sEGLLibrary;
@@ -107,17 +110,24 @@ GLLibraryEGL::EnsureInitialized()
         // we should look for them there. We have to load the libs in this
         // order, because libEGL.dll depends on libGLESv2.dll which depends on the DXSDK
         // libraries. This matters especially for WebRT apps which are in a different directory.
         // See bug 760323 and bug 749459
 
 #ifndef MOZ_D3DCOMPILER_DLL
 #error MOZ_D3DCOMPILER_DLL should have been defined by the Makefile
 #endif
-        LoadLibraryForEGLOnWindows(NS_LITERAL_STRING(NS_STRINGIFY(MOZ_D3DCOMPILER_DLL)));
+        // Windows 8.1 has d3dcompiler_47.dll in the system directory.
+        // Try it first. Note that _46 will never be in the system
+        // directory and we ship with at least _43. So there is no point
+        // trying _46 and _43 in the system directory.
+        if (!LoadLibrarySystem32(L"d3dcompiler_47.dll")) {
+            // Fall back to the version that we shipped with.
+            LoadLibraryForEGLOnWindows(NS_LITERAL_STRING(NS_STRINGIFY(MOZ_D3DCOMPILER_DLL)));
+        }
         // intentionally leak the D3DCOMPILER_DLL library
 
         LoadLibraryForEGLOnWindows(NS_LITERAL_STRING("libGLESv2.dll"));
         // intentionally leak the libGLESv2.dll library
 
         mEGLLibrary = LoadLibraryForEGLOnWindows(NS_LITERAL_STRING("libEGL.dll"));
 
         if (!mEGLLibrary)
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -275,18 +275,19 @@ CompositorOGL::Initialize()
     * this fails we'll try ARB_texture_rectangle.
     */
 
     GLenum textureTargets[] = {
       LOCAL_GL_TEXTURE_2D,
       LOCAL_GL_NONE
     };
 
-    if (mGLContext->IsGLES2()) {
-        textureTargets[1] = LOCAL_GL_TEXTURE_RECTANGLE_ARB;
+    if (!mGLContext->IsGLES2()) {
+      // No TEXTURE_RECTANGLE_ARB available on ES2
+      textureTargets[1] = LOCAL_GL_TEXTURE_RECTANGLE_ARB;
     }
 
     mFBOTextureTarget = LOCAL_GL_NONE;
 
     GLuint testFBO = 0;
     mGLContext->fGenFramebuffers(1, &testFBO);
     GLuint testTexture = 0;
 
@@ -1186,23 +1187,31 @@ CompositorOGL::EndFrame()
   }
 
   mGLContext->SwapBuffers();
   mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
 
   // Unbind all textures
   mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
   mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, 0);
-  mGLContext->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
+  if (!mGLContext->IsGLES2()) {
+    mGLContext->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
+  }
+
   mGLContext->fActiveTexture(LOCAL_GL_TEXTURE1);
   mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, 0);
-  mGLContext->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
+  if (!mGLContext->IsGLES2()) {
+    mGLContext->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
+  }
+
   mGLContext->fActiveTexture(LOCAL_GL_TEXTURE2);
   mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, 0);
-  mGLContext->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
+  if (!mGLContext->IsGLES2()) {
+    mGLContext->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
+  }
 }
 
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
 void
 CompositorOGL::SetFBAcquireFence(Layer* aLayer)
 {
   if (!aLayer) {
     return;
@@ -1255,16 +1264,20 @@ CompositorOGL::EndFrameForExternalCompos
 }
 
 void
 CompositorOGL::AbortFrame()
 {
   mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
   mFrameInProgress = false;
   mCurrentRenderTarget = nullptr;
+
+  if (mTexturePool) {
+    mTexturePool->EndFrame();
+  }
 }
 
 void
 CompositorOGL::SetDestinationSurfaceSize(const gfx::IntSize& aSize)
 {
   mSurfaceSize.width = aSize.width;
   mSurfaceSize.height = aSize.height;
 }
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -1352,18 +1352,19 @@ gfxFT2FontList::GetFontList(InfallibleTA
 {
     mFontFamilies.Enumerate(AddFamilyToFontList, retValue);
 }
 
 static void
 LoadSkipSpaceLookupCheck(nsTHashtable<nsStringHashKey>& aSkipSpaceLookupCheck)
 {
     nsAutoTArray<nsString, 5> skiplist;
-    gfxFontUtils::GetPrefsFontList("font.whitelist.skip_space_lookup_check",
-                                   skiplist);
+    gfxFontUtils::GetPrefsFontList(
+        "font.whitelist.skip_default_features_space_check",
+        skiplist);
     uint32_t numFonts = skiplist.Length();
     for (uint32_t i = 0; i < numFonts; i++) {
         ToLowerCase(skiplist[i]);
         aSkipSpaceLookupCheck.PutEntry(skiplist[i]);
     }
 }
 
 nsresult
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -467,16 +467,17 @@ gfxPlatform::Shutdown()
 
         NS_ASSERTION(gPlatform->mMemoryPressureObserver, "mMemoryPressureObserver has already gone");
         nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
         if (obs) {
             obs->RemoveObserver(gPlatform->mMemoryPressureObserver, "memory-pressure");
         }
 
         gPlatform->mMemoryPressureObserver = nullptr;
+        gPlatform->mSkiaGlue = nullptr;
     }
 
 #ifdef MOZ_WIDGET_ANDROID
     // Shut down the texture pool
     mozilla::gl::TexturePoolOGL::Shutdown();
 #endif
 
     // Shut down the default GL context provider.
@@ -918,16 +919,20 @@ gfxPlatform::GetThebesSurfaceForDrawTarg
   IntSize size = data->GetSize();
   gfxImageFormat format = SurfaceFormatToImageFormat(data->GetFormat());
 
 
   nsRefPtr<gfxASurface> surf =
     new gfxImageSurface(data->GetData(), gfxIntSize(size.width, size.height),
                         data->Stride(), format);
 
+  if (surf->CairoStatus()) {
+    return nullptr;
+  }
+
   surf->SetData(&kDrawSourceSurface, data.forget().drop(), DataSourceSurfaceDestroy);
   // keep the draw target alive as long as we need its data
   aTarget->AddRef();
   surf->SetData(&kDrawTargetForSurface, aTarget, DataDrawTargetDestroy);
 
   return surf.forget();
 }
 
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -66,71 +66,34 @@ extern JS_PUBLIC_API(void) JS_Abort(void
 /*
  * In order to test OOM conditions, when the testing function
  * oomAfterAllocations COUNT is passed, we fail continuously after the NUM'th
  * allocation from now.
  */
 extern JS_PUBLIC_DATA(uint32_t) OOM_maxAllocations; /* set in builtins/TestingFunctions.cpp */
 extern JS_PUBLIC_DATA(uint32_t) OOM_counter; /* data race, who cares. */
 
-#ifdef JS_OOM_DO_BACKTRACES
-#define JS_OOM_BACKTRACE_SIZE 32
-static MOZ_ALWAYS_INLINE void
-PrintBacktrace()
-{
-    void* OOM_trace[JS_OOM_BACKTRACE_SIZE];
-    char** OOM_traceSymbols = nullptr;
-    int32_t OOM_traceSize = 0;
-    int32_t OOM_traceIdx = 0;
-    OOM_traceSize = backtrace(OOM_trace, JS_OOM_BACKTRACE_SIZE);
-    OOM_traceSymbols = backtrace_symbols(OOM_trace, OOM_traceSize);
-
-    if (!OOM_traceSymbols)
-        return;
-
-    for (OOM_traceIdx = 0; OOM_traceIdx < OOM_traceSize; ++OOM_traceIdx) {
-        fprintf(stderr, "#%d %s\n", OOM_traceIdx, OOM_traceSymbols[OOM_traceIdx]);
-    }
-
-    // This must be free(), not js_free(), because backtrace_symbols()
-    // allocates with malloc().
-    free(OOM_traceSymbols);
-}
-
-#define JS_OOM_EMIT_BACKTRACE() \
-    do {\
-        fprintf(stderr, "Forcing artificial memory allocation function failure:\n");\
-	PrintBacktrace();\
-    } while (0)
-# else
-#  define JS_OOM_EMIT_BACKTRACE() do {} while(0)
-#endif /* JS_OOM_DO_BACKTRACES */
+#ifdef JS_OOM_BREAKPOINT
+static MOZ_NEVER_INLINE void js_failedAllocBreakpoint() { asm(""); }
+#define JS_OOM_CALL_BP_FUNC() js_failedAllocBreakpoint()
+#else
+#define JS_OOM_CALL_BP_FUNC() do {} while(0)
+#endif
 
 #  define JS_OOM_POSSIBLY_FAIL() \
     do \
     { \
         if (++OOM_counter > OOM_maxAllocations) { \
-            JS_OOM_EMIT_BACKTRACE();\
+            JS_OOM_CALL_BP_FUNC();\
             return nullptr; \
         } \
     } while (0)
 
-#  define JS_OOM_POSSIBLY_FAIL_REPORT(cx) \
-    do \
-    { \
-        if (++OOM_counter > OOM_maxAllocations) { \
-            JS_OOM_EMIT_BACKTRACE();\
-            js_ReportOutOfMemory(cx);\
-            return false; \
-        } \
-    } while (0)
-
 # else
 #  define JS_OOM_POSSIBLY_FAIL() do {} while(0)
-#  define JS_OOM_POSSIBLY_FAIL_REPORT(cx) do {} while(0)
 # endif /* JS_DEBUG */
 
 static inline void* js_malloc(size_t bytes)
 {
     JS_OOM_POSSIBLY_FAIL();
     return malloc(bytes);
 }
 
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -361,24 +361,24 @@ ifdef HAVE_DTRACE
 endif
 
 # Prepare self-hosted JS code for embedding
 export:: selfhosting
 selfhosting:: selfhosted.out.h
 
 selfhosting_srcs := \
   $(srcdir)/builtin/Utilities.js \
+  $(srcdir)/builtin/ParallelUtilities.js \
   $(srcdir)/builtin/Array.js \
   $(srcdir)/builtin/Date.js \
   $(srcdir)/builtin/Intl.js \
   $(srcdir)/builtin/IntlData.js \
   $(srcdir)/builtin/Iterator.js \
   $(srcdir)/builtin/Map.js \
   $(srcdir)/builtin/Number.js \
-  $(srcdir)/builtin/Parallel.js \
   $(srcdir)/builtin/String.js \
   $(srcdir)/builtin/Set.js \
   $(srcdir)/builtin/TypedObject.js \
   $(NULL)
 
 selfhosted_out_h_deps := \
   $(selfhosting_srcs) \
   $(srcdir)/js.msg \
--- a/js/src/builtin/Array.js
+++ b/js/src/builtin/Array.js
@@ -590,37 +590,36 @@ function ArrayMapPar(func, mode) {
     // - Breaking out of named blocks does not currently work (bug 684384);
     // - Unreachable Code Elim. can't properly handle if (a && b) (bug 669796)
     if (ShouldForceSequential())
       break parallel;
     if (!TRY_PARALLEL(mode))
       break parallel;
 
     var slicesInfo = ComputeSlicesInfo(length);
-    ForkJoin(mapThread, ShrinkLeftmost(slicesInfo), ForkJoinMode(mode));
+    ForkJoin(mapThread, 0, slicesInfo.count, ForkJoinMode(mode));
     return buffer;
   }
 
   // Sequential fallback:
   ASSERT_SEQUENTIAL_IS_OK(mode);
   for (var i = 0; i < length; i++)
     UnsafePutElements(buffer, i, func(self[i], i, self));
   return buffer;
 
-  function mapThread(_, warmup) {
+  function mapThread(workerId, sliceStart, sliceEnd) {
+    var sliceShift = slicesInfo.shift;
     var sliceId;
-    while (GET_SLICE(slicesInfo, sliceId)) {
-      var indexStart = SLICE_START(slicesInfo, sliceId);
-      var indexEnd = SLICE_END(slicesInfo, indexStart, length);
+    while (GET_SLICE(sliceStart, sliceEnd, sliceId)) {
+      var indexStart = SLICE_START_INDEX(sliceShift, sliceId);
+      var indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length);
       for (var i = indexStart; i < indexEnd; i++)
         UnsafePutElements(buffer, i, func(self[i], i, self));
-      MARK_SLICE_DONE(slicesInfo, sliceId);
-      if (warmup)
-        return;
     }
+    return sliceId;
   }
 
   return undefined;
 }
 
 /**
  * Reduces the elements in an array in parallel. Order is not fixed and |func|
  * is assumed to be associative.
@@ -637,47 +636,46 @@ function ArrayReducePar(func, mode) {
 
   parallel: for (;;) { // see ArrayMapPar() to explain why for(;;) etc
     if (ShouldForceSequential())
       break parallel;
     if (!TRY_PARALLEL(mode))
       break parallel;
 
     var slicesInfo = ComputeSlicesInfo(length);
-    var numSlices = SLICE_COUNT(slicesInfo);
+    var numSlices = slicesInfo.count;
     var subreductions = NewDenseArray(numSlices);
 
-    ForkJoin(reduceThread, ShrinkLeftmost(slicesInfo), ForkJoinMode(mode));
+    ForkJoin(reduceThread, 0, numSlices, ForkJoinMode(mode));
 
     var accumulator = subreductions[0];
     for (var i = 1; i < numSlices; i++)
       accumulator = func(accumulator, subreductions[i]);
     return accumulator;
   }
 
   // Sequential fallback:
   ASSERT_SEQUENTIAL_IS_OK(mode);
   var accumulator = self[0];
   for (var i = 1; i < length; i++)
     accumulator = func(accumulator, self[i]);
   return accumulator;
 
-  function reduceThread(_, warmup) {
+  function reduceThread(workerId, sliceStart, sliceEnd) {
+    var sliceShift = slicesInfo.shift;
     var sliceId;
-    while (GET_SLICE(slicesInfo, sliceId)) {
-      var indexStart = SLICE_START(slicesInfo, sliceId);
-      var indexEnd = SLICE_END(slicesInfo, indexStart, length);
+    while (GET_SLICE(sliceStart, sliceEnd, sliceId)) {
+      var indexStart = SLICE_START_INDEX(sliceShift, sliceId);
+      var indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length);
       var accumulator = self[indexStart];
       for (var i = indexStart + 1; i < indexEnd; i++)
         accumulator = func(accumulator, self[i]);
       UnsafePutElements(subreductions, sliceId, accumulator);
-      MARK_SLICE_DONE(slicesInfo, sliceId);
-      if (warmup)
-        return;
     }
+    return sliceId;
   }
 
   return undefined;
 }
 
 /**
  * Returns an array [s_0, ..., s_N] where |s_i| is equal to the reduction (as
  * per |reduce()|) of elements |0..i|. This is the generalization of partial
@@ -697,38 +695,36 @@ function ArrayScanPar(func, mode) {
 
   parallel: for (;;) { // see ArrayMapPar() to explain why for(;;) etc
     if (ShouldForceSequential())
       break parallel;
     if (!TRY_PARALLEL(mode))
       break parallel;
 
     var slicesInfo = ComputeSlicesInfo(length);
-    var numSlices = SLICE_COUNT(slicesInfo);
+    var numSlices = slicesInfo.count;
 
     // Scan slices individually (see comment on phase1()).
-    ForkJoin(phase1, ShrinkLeftmost(slicesInfo), ForkJoinMode(mode));
+    ForkJoin(phase1, 0, numSlices, ForkJoinMode(mode));
 
     // Compute intermediates array (see comment on phase2()).
     var intermediates = [];
     var accumulator = buffer[finalElement(0)];
     ARRAY_PUSH(intermediates, accumulator);
     for (var i = 1; i < numSlices - 1; i++) {
       accumulator = func(accumulator, buffer[finalElement(i)]);
       ARRAY_PUSH(intermediates, accumulator);
     }
 
-    // Clear the slices' statuses in between phases.
-    SlicesInfoClearStatuses(slicesInfo);
-
-    // There is no work to be done for slice 0, so mark it as done.
-    MARK_SLICE_DONE(slicesInfo, 0);
-
     // Complete each slice using intermediates array (see comment on phase2()).
-    ForkJoin(phase2, ShrinkLeftmost(slicesInfo), ForkJoinMode(mode));
+    //
+    // We start from slice 1 instead of 0 since there is no work to be done
+    // for slice 0.
+    if (numSlices > 1)
+      ForkJoin(phase2, 1, numSlices, ForkJoinMode(mode));
     return buffer;
   }
 
   // Sequential fallback:
   ASSERT_SEQUENTIAL_IS_OK(mode);
   scan(self[0], 0, length);
   return buffer;
 
@@ -752,33 +748,33 @@ function ArrayScanPar(func, mode) {
    * result array like:
    *
    *     [A, A+B, A+B+C, D, D+E, D+E+F, G, G+H, G+H+I]
    *      ^~~~~~~~~~~~^  ^~~~~~~~~~~~^  ^~~~~~~~~~~~~^
    *      Slice 0        Slice 1        Slice 2
    *
    * Read on in phase2 to see what we do next!
    */
-  function phase1(_, warmup) {
+  function phase1(workerId, sliceStart, sliceEnd) {
+    var sliceShift = slicesInfo.shift;
     var sliceId;
-    while (GET_SLICE(slicesInfo, sliceId)) {
-      var indexStart = SLICE_START(slicesInfo, sliceId);
-      var indexEnd = SLICE_END(slicesInfo, indexStart, length);
+    while (GET_SLICE(sliceStart, sliceEnd, sliceId)) {
+      var indexStart = SLICE_START_INDEX(sliceShift, sliceId);
+      var indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length);
       scan(self[indexStart], indexStart, indexEnd);
-      MARK_SLICE_DONE(slicesInfo, sliceId);
-      if (warmup)
-        return;
     }
+    return sliceId;
   }
 
   /**
    * Computes the index of the final element computed by the slice |sliceId|.
    */
   function finalElement(sliceId) {
-    return SLICE_END(slicesInfo, SLICE_START(slicesInfo, sliceId), length) - 1;
+    var sliceShift = slicesInfo.shift;
+    return SLICE_END_INDEX(sliceShift, SLICE_START_INDEX(sliceShift, sliceId), length) - 1;
   }
 
   /**
    * After computing the phase1 results, we compute an
    * |intermediates| array. |intermediates[i]| contains the result
    * of reducing the final value from each preceding slice j<i with
    * the final value of slice i. So, to continue our previous
    * example, the intermediates array would contain:
@@ -804,30 +800,27 @@ function ArrayScanPar(func, mode) {
    * reducing everything in the input array prior to the slice.
    *
    * To continue with our example, in phase 1 we computed slice 1 to
    * be [D, D+E, D+E+F]. We will combine those results with
    * |intermediates[1-1]|, which is |A+B+C|, so that the final
    * result is [(A+B+C)+D, (A+B+C)+(D+E), (A+B+C)+(D+E+F)]. Again I
    * am using parentheses to clarify how these results were reduced.
    */
-  function phase2(_, warmup) {
+  function phase2(workerId, sliceStart, sliceEnd) {
+    var sliceShift = slicesInfo.shift;
     var sliceId;
-    while (GET_SLICE(slicesInfo, sliceId)) {
-      var indexPos = SLICE_START(slicesInfo, sliceId);
-      var indexEnd = SLICE_END(slicesInfo, indexPos, length);
-
+    while (GET_SLICE(sliceStart, sliceEnd, sliceId)) {
+      var indexPos = SLICE_START_INDEX(sliceShift, sliceId);
+      var indexEnd = SLICE_END_INDEX(sliceShift, indexPos, length);
       var intermediate = intermediates[sliceId - 1];
       for (; indexPos < indexEnd; indexPos++)
         UnsafePutElements(buffer, indexPos, func(intermediate, buffer[indexPos]));
-
-      MARK_SLICE_DONE(slicesInfo, sliceId);
-      if (warmup)
-        return;
     }
+    return sliceId;
   }
 
   return undefined;
 }
 
 /**
  * |scatter()| redistributes the elements in the array into a new array.
  *
@@ -932,33 +925,30 @@ function ArrayFilterPar(func, mode) {
     // Step 1. Compute which items from each slice of the result
     // buffer should be preserved. When we're done, we have an array
     // |survivors| containing a bitset for each chunk, indicating
     // which members of the chunk survived. We also keep an array
     // |counts| containing the total number of items that are being
     // preserved from within one slice.
     //
     // FIXME(bug 844890): Use typed arrays here.
-    var numSlices = SLICE_COUNT(slicesInfo);
+    var numSlices = slicesInfo.count;
     var counts = NewDenseArray(numSlices);
     for (var i = 0; i < numSlices; i++)
       UnsafePutElements(counts, i, 0);
     var survivors = NewDenseArray(computeNum32BitChunks(length));
-    ForkJoin(findSurvivorsThread, ShrinkLeftmost(slicesInfo), ForkJoinMode(mode));
-
-    // Clear the slices' statuses in between phases.
-    SlicesInfoClearStatuses(slicesInfo);
+    ForkJoin(findSurvivorsThread, 0, numSlices, ForkJoinMode(mode));
 
     // Step 2. Compress the slices into one contiguous set.
     var count = 0;
     for (var i = 0; i < numSlices; i++)
       count += counts[i];
     var buffer = NewDenseArray(count);
     if (count > 0)
-      ForkJoin(copySurvivorsThread, ShrinkLeftmost(slicesInfo), ForkJoinMode(mode));
+      ForkJoin(copySurvivorsThread, 0, numSlices, ForkJoinMode(mode));
 
     return buffer;
   }
 
   // Sequential fallback:
   ASSERT_SEQUENTIAL_IS_OK(mode);
   var buffer = [];
   for (var i = 0; i < length; i++) {
@@ -979,68 +969,65 @@ function ArrayFilterPar(func, mode) {
   }
 
   /**
    * As described above, our goal is to determine which items we
    * will preserve from a given slice. We do this one chunk at a
    * time. When we finish a chunk, we record our current count and
    * the next chunk sliceId, lest we should bail.
    */
-  function findSurvivorsThread(_, warmup) {
+  function findSurvivorsThread(workerId, sliceStart, sliceEnd) {
+    var sliceShift = slicesInfo.shift;
     var sliceId;
-    while (GET_SLICE(slicesInfo, sliceId)) {
+    while (GET_SLICE(sliceStart, sliceEnd, sliceId)) {
       var count = 0;
-      var indexStart = SLICE_START(slicesInfo, sliceId);
-      var indexEnd = SLICE_END(slicesInfo, indexStart, length);
+      var indexStart = SLICE_START_INDEX(sliceShift, sliceId);
+      var indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length);
       var chunkStart = computeNum32BitChunks(indexStart);
       var chunkEnd = computeNum32BitChunks(indexEnd);
       for (var chunkPos = chunkStart; chunkPos < chunkEnd; chunkPos++, indexStart += 32) {
         var chunkBits = 0;
         for (var bit = 0, indexPos = indexStart; bit < 32 && indexPos < indexEnd; bit++, indexPos++) {
           var keep = !!func(self[indexPos], indexPos, self);
           chunkBits |= keep << bit;
           count += keep;
         }
         UnsafePutElements(survivors, chunkPos, chunkBits);
       }
       UnsafePutElements(counts, sliceId, count);
-
-      MARK_SLICE_DONE(slicesInfo, sliceId);
-      if (warmup)
-        return;
     }
+    return sliceId;
   }
 
-  function copySurvivorsThread(_, warmup) {
+  function copySurvivorsThread(workerId, sliceStart, sliceEnd) {
+    var sliceShift = slicesInfo.shift;
     var sliceId;
-    while (GET_SLICE(slicesInfo, sliceId)) {
+    while (GET_SLICE(sliceStart, sliceEnd, sliceId)) {
       // Copies the survivors from this slice into the correct position.
       // Note that this is an idempotent operation that does not invoke
       // user code. Therefore, we don't expect bailouts and make an
       // effort to proceed chunk by chunk or avoid duplicating work.
 
       // Total up the items preserved by previous slices.
       var total = 0;
       for (var i = 0; i < sliceId + 1; i++)
         total += counts[i];
 
       // Compute the final index we expect to write.
       var count = total - counts[sliceId];
-      if (count === total) {
-        MARK_SLICE_DONE(slicesInfo, sliceId);
+      if (count === total)
         continue;
-      }
 
       // Iterate over the chunks assigned to us. Read the bitset for
       // each chunk. Copy values where a 1 appears until we have
       // written all the values that we expect to. We can just iterate
       // from 0...CHUNK_SIZE without fear of a truncated final chunk
       // because we are already checking for when count==total.
-      var indexStart = SLICE_START(slicesInfo, sliceId);
-      var indexEnd = SLICE_END(slicesInfo, indexStart, length);
+      var indexStart = SLICE_START_INDEX(sliceShift, sliceId);
+      var indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length);
       var chunkStart = computeNum32BitChunks(indexStart);
       var chunkEnd = computeNum32BitChunks(indexEnd);
       for (var chunkPos = chunkStart; chunkPos < chunkEnd; chunkPos++, indexStart += 32) {
         var chunkBits = survivors[chunkPos];
         if (!chunkBits)
           continue;
 
         for (var i = 0; i < 32; i++) {
@@ -1049,21 +1036,19 @@ function ArrayFilterPar(func, mode) {
             if (count === total)
               break;
           }
         }
 
         if (count == total)
           break;
       }
+    }
 
-      MARK_SLICE_DONE(slicesInfo, sliceId);
-      if (warmup)
-        return;
-    }
+    return sliceId;
   }
 
   return undefined;
 }
 
 /**
  * "Comprehension form": This is the function invoked for
  * |Array.{build,buildPar}(len, fn)| It creates a new array with length |len|
@@ -1096,37 +1081,36 @@ function ArrayStaticBuildPar(length, fun
 
   parallel: for (;;) {
     if (ShouldForceSequential())
       break parallel;
     if (!TRY_PARALLEL(mode))
       break parallel;
 
     var slicesInfo = ComputeSlicesInfo(length);
-    ForkJoin(constructThread, ShrinkLeftmost(slicesInfo), ForkJoinMode(mode));
+    ForkJoin(constructThread, 0, slicesInfo.count, ForkJoinMode(mode));
     return buffer;
   }
 
   // Sequential fallback:
   ASSERT_SEQUENTIAL_IS_OK(mode);
   for (var i = 0; i < length; i++)
     UnsafePutElements(buffer, i, func(i));
   return buffer;
 
-  function constructThread(_, warmup) {
+  function constructThread(workerId, sliceStart, sliceEnd) {
+    var sliceShift = slicesInfo.shift;
     var sliceId;
-    while (GET_SLICE(slicesInfo, sliceId)) {
-      var indexStart = SLICE_START(slicesInfo, sliceId);
-      var indexEnd = SLICE_END(slicesInfo, indexStart, length);
+    while (GET_SLICE(sliceStart, sliceEnd, sliceId)) {
+      var indexStart = SLICE_START_INDEX(sliceShift, sliceId);
+      var indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length);
       for (var i = indexStart; i < indexEnd; i++)
         UnsafePutElements(buffer, i, func(i));
-      MARK_SLICE_DONE(slicesInfo, sliceId);
-      if (warmup)
-        return;
     }
+    return sliceId;
   }
 
   return undefined;
 }
 
 /*
  * Mark the main operations as clone-at-callsite for better precision.
  * This is slightly overkill, as all that we really need is to
--- a/js/src/builtin/Map.js
+++ b/js/src/builtin/Map.js
@@ -7,27 +7,27 @@
 function MapForEach(callbackfn, thisArg = undefined) {
     /* Step 1-2. */
     var M = this;
     if (!IsObject(M))
         ThrowError(JSMSG_BAD_TYPE, typeof M);
 
     /* Step 3-4. */
     try {
-        std_Map_has.call(M);
+        callFunction(std_Map_has, M);
     } catch (e) {
         ThrowError(JSMSG_BAD_TYPE, typeof M);
     }
 
     /* Step 5. */
     if (!IsCallable(callbackfn))
         ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
 
     /* Step 6-8. */
-    var entries = std_Map_iterator.call(M);
+    var entries = callFunction(std_Map_iterator, M);
     while (true) {
-        var result = std_Map_iterator_next.call(entries);
+        var result = callFunction(std_Map_iterator_next, entries);
         if (result.done)
             break;
         var entry = result.value;
         callFunction(callbackfn, thisArg, entry[1], entry[0], M);
     }
 }
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -534,16 +534,70 @@ obj_getPrototypeOf(JSContext *cx, unsign
     args2.setCallee(cx->global()->protoGetter());
     args2.setThis(args[0]);
     if (!Invoke(cx, args2))
         return false;
     args.rval().set(args2.rval());
     return true;
 }
 
+static bool
+obj_setPrototypeOf(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    RootedObject setPrototypeOf(cx, &args.callee());
+    if (!GlobalObject::warnOnceAboutPrototypeMutation(cx, setPrototypeOf))
+        return false;
+
+    if (args.length() < 2) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
+                             "Object.setPrototypeOf", "1", "");
+        return false;
+    }
+
+    /* Step 1-2. */
+    if (args[0].isNullOrUndefined()) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
+                             args[0].isNull() ? "null" : "undefined", "object");
+        return false;
+    }
+
+    /* Step 3. */
+    if (!args[1].isObjectOrNull()) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
+                             "Object.setPrototypeOf", "an object or null", InformalValueTypeName(args[1]));
+        return false;
+    }
+
+    /* Step 4. */
+    if (!args[0].isObject()) {
+        args.rval().set(args[0]);
+        return true;
+    }
+
+    /* Step 5-6. */
+    RootedObject obj(cx, &args[0].toObject());
+    RootedObject newProto(cx, args[1].toObjectOrNull());
+
+    bool success;
+    if (!JSObject::setProto(cx, obj, newProto, &success))
+        return false;
+
+    /* Step 7. */
+    if (!success) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_OBJECT_NOT_EXTENSIBLE, "object");
+        return false;
+    }
+
+    /* Step 8. */
+    args.rval().set(args[0]);
+    return true;
+}
+
 #if JS_HAS_OBJ_WATCHPOINT
 
 bool
 js::WatchHandler(JSContext *cx, JSObject *obj_, jsid id_, JS::Value old,
                  JS::Value *nvp, void *closure)
 {
     RootedObject obj(cx, obj_);
     RootedId id(cx, id_);
@@ -1009,16 +1063,17 @@ const JSFunctionSpec js::object_methods[
     JS_FN(js_lookupGetter_str,         obj_lookupGetter,            1,0),
     JS_FN(js_lookupSetter_str,         obj_lookupSetter,            1,0),
 #endif
     JS_FS_END
 };
 
 const JSFunctionSpec js::object_static_methods[] = {
     JS_FN("getPrototypeOf",            obj_getPrototypeOf,          1,0),
+    JS_FN("setPrototypeOf",            obj_setPrototypeOf,          2,0),
     JS_FN("getOwnPropertyDescriptor",  obj_getOwnPropertyDescriptor,2,0),
     JS_FN("keys",                      obj_keys,                    1,0),
     JS_FN("is",                        obj_is,                      2,0),
     JS_FN("defineProperty",            obj_defineProperty,          3,0),
     JS_FN("defineProperties",          obj_defineProperties,        2,0),
     JS_FN("create",                    obj_create,                  2,0),
     JS_FN("getOwnPropertyNames",       obj_getOwnPropertyNames,     1,0),
     JS_FN("isExtensible",              obj_isExtensible,            1,0),
rename from js/src/builtin/Parallel.js
rename to js/src/builtin/ParallelUtilities.js
--- a/js/src/builtin/Parallel.js
+++ b/js/src/builtin/ParallelUtilities.js
@@ -1,18 +1,61 @@
 /* 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/. */
 
-// Shared utility functions for parallel operations in `Array.js`
+// Shared utility functions for and macros parallel operations in `Array.js`
 // and `TypedObject.js`.
 
+#ifdef ENABLE_PARALLEL_JS
+
+/* The mode asserts options object. */
+#define TRY_PARALLEL(MODE) \
+  ((!MODE || MODE.mode !== "seq"))
+#define ASSERT_SEQUENTIAL_IS_OK(MODE) \
+  do { if (MODE) AssertSequentialIsOK(MODE) } while(false)
+
+/**
+ * The ParallelSpew intrinsic is only defined in debug mode, so define a dummy
+ * if debug is not on.
+ */
+#ifndef DEBUG
+#define ParallelSpew(args)
+#endif
+
+#define MAX_SLICE_SHIFT 6
+#define MAX_SLICE_SIZE 64
+#define MAX_SLICES_PER_WORKER 8
 
 /**
- * Determine the number and size of slices.
+ * Macros to help compute the start and end indices of slices based on id. Use
+ * with the object returned by ComputeSliceInfo.
+ */
+#define SLICE_START_INDEX(shift, id) \
+    (id << shift)
+#define SLICE_END_INDEX(shift, start, length) \
+    std_Math_min(start + (1 << shift), length)
+
+/**
+ * ForkJoinGetSlice acts as identity when we are not in a parallel section, so
+ * pass in the next sequential value when we are in sequential mode. The
+ * reason for this odd API is because intrinsics *need* to be called during
+ * ForkJoin's warmup to fill the TI info.
+ */
+#define GET_SLICE(sliceStart, sliceEnd, id) \
+    ((id = ForkJoinGetSlice((InParallelSection() ? -1 : sliceStart++) | 0)) < sliceEnd)
+
+/**
+ * Determine the number and size of slices. The info object has the following
+ * properties:
+ *
+ *  - shift: amount to shift by to compute indices
+ *  - count: number of slices
+ *  - seqSliceId: the slice id for which slices [0,id] have been run
+ *                sequentially and cannot be re-run in parallel.
  */
 function ComputeSlicesInfo(length) {
   var count = length >>> MAX_SLICE_SHIFT;
   var numWorkers = ForkJoinNumWorkers();
   if (count < numWorkers)
     count = numWorkers;
   else if (count >= numWorkers * MAX_SLICES_PER_WORKER)
     count = numWorkers * MAX_SLICES_PER_WORKER;
@@ -20,48 +63,12 @@ function ComputeSlicesInfo(length) {
   // Round the slice size to be a power of 2.
   var shift = std_Math_max(std_Math_log2(length / count) | 0, 1);
 
   // Recompute count with the rounded size.
   count = length >>> shift;
   if (count << shift !== length)
     count += 1;
 
-  return { shift: shift, statuses: new Uint8Array(count), lastSequentialId: 0 };
-}
-
-/**
- * Reset the status array of the slices info object.
- */
-function SlicesInfoClearStatuses(info) {
-  var statuses = info.statuses;
-  var length = statuses.length;
-  for (var i = 0; i < length; i++)
-    UnsafePutElements(statuses, i, 0);
-  info.lastSequentialId = 0;
+  return { shift: shift, count: count };
 }
 
-/**
- * Compute the slice such that all slices before it (but not including it) are
- * completed.
- */
-function NextSequentialSliceId(info, doneMarker) {
-  var statuses = info.statuses;
-  var length = statuses.length;
-  for (var i = info.lastSequentialId; i < length; i++) {
-    if (statuses[i] === SLICE_STATUS_DONE)
-      continue;
-    info.lastSequentialId = i;
-    return i;
-  }
-  return doneMarker == undefined ? length : doneMarker;
-}
-
-/**
- * Determinism-preserving bounds function.
- */
-function ShrinkLeftmost(info) {
-  return function () {
-    return [NextSequentialSliceId(info), SLICE_COUNT(info)]
-  };
-}
-
-
+#endif // ENABLE_PARALLEL_JS
--- a/js/src/builtin/Set.js
+++ b/js/src/builtin/Set.js
@@ -7,27 +7,27 @@
 function SetForEach(callbackfn, thisArg = undefined) {
     /* Step 1-2. */
     var S = this;
     if (!IsObject(S))
         ThrowError(JSMSG_BAD_TYPE, typeof S);
 
     /* Step 3-4. */
     try {
-        std_Set_has.call(S);
+        callFunction(std_Set_has, S);
     } catch (e) {
         ThrowError(JSMSG_BAD_TYPE, typeof S);
     }
 
     /* Step 5-6. */
     if (!IsCallable(callbackfn))
         ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
 
     /* Step 7-8. */
-    var values = std_Set_iterator.call(S);
+    var values = callFunction(std_Set_iterator, S);
     while (true) {
-        var result = std_Set_iterator_next.call(values);
+        var result = callFunction(std_Set_iterator_next, values);
         if (result.done)
             break;
         var value = result.value;
         callFunction(callbackfn, thisArg, value, value, S);
     }
 }
--- a/js/src/builtin/TypedObject.js
+++ b/js/src/builtin/TypedObject.js
@@ -1429,33 +1429,36 @@ function MapTypedParImplDepth1(inArray, 
                             outTypedObject: outTypedObject }));
   }
 
   // Below we will be adjusting offsets within the input to point at
   // successive entries; we'll need to know the offset of inArray
   // relative to its owner (which is often but not always 0).
   const inBaseOffset = TYPEDOBJ_BYTEOFFSET(inArray);
 
-  ForkJoin(mapThread, ShrinkLeftmost(slicesInfo), ForkJoinMode(mode));
+  ForkJoin(mapThread, 0, slicesInfo.count, ForkJoinMode(mode));
   return outArray;
 
-  function mapThread(workerId, warmup) {
+  function mapThread(workerId, sliceStart, sliceEnd) {
     assert(TO_INT32(workerId) === workerId,
            "workerId not int: " + workerId);
-    assert(workerId >= 0 && workerId < pointers.length,
-          "workerId too large: " + workerId + " >= " + pointers.length);
-    assert(!!pointers[workerId],
+    assert(workerId < pointers.length,
+           "workerId too large: " + workerId + " >= " + pointers.length);
+
+    var pointerIndex = InParallelSection() ? workerId : 0;
+    assert(!!pointers[pointerIndex],
           "no pointer data for workerId: " + workerId);
 
+    const { inTypedObject, outTypedObject } = pointers[pointerIndex];
+    const sliceShift = slicesInfo.shift;
     var sliceId;
-    const { inTypedObject, outTypedObject } = pointers[workerId];
 
-    while (GET_SLICE(slicesInfo, sliceId)) {
-      const indexStart = SLICE_START(slicesInfo, sliceId);
-      const indexEnd = SLICE_END(slicesInfo, indexStart, length);
+    while (GET_SLICE(sliceStart, sliceEnd, sliceId)) {
+      const indexStart = SLICE_START_INDEX(sliceShift, sliceId);
+      const indexEnd = SLICE_END_INDEX(sliceShift, indexStart, length);
 
       var inOffset = inBaseOffset + std_Math_imul(inGrainTypeSize, indexStart);
       var outOffset = std_Math_imul(outGrainTypeSize, indexStart);
 
       // Set the target region so that user is only permitted to write
       // within the range set aside for this slice. This prevents user
       // from writing to typed objects that escaped from prior slices
       // during sequential iteration. Note that, for any particular
@@ -1477,32 +1480,30 @@ function MapTypedParImplDepth1(inArray, 
                       ? RedirectPointer(outTypedObject, outOffset,
                                         outGrainTypeIsTransparent)
                       : undefined);
         const r = func(inVal, i, inArray, outVal);
         if (r !== undefined) {
           if (outGrainTypeIsComplex)
             SetTypedObjectValue(outGrainType, outArray, outOffset, r);
           else
-          UnsafePutElements(outArray, i, r);
+            UnsafePutElements(outArray, i, r);
         }
         inOffset += inGrainTypeSize;
         outOffset += outGrainTypeSize;
       }
 
       // A transparent result type cannot contain references, and
       // hence there is no way for a pointer to a thread-local object
       // to escape.
       if (outGrainTypeIsTransparent)
         ClearThreadLocalArenas();
+    }
 
-      MARK_SLICE_DONE(slicesInfo, sliceId);
-      if (warmup)
-        return;
-    }
+    return sliceId;
   }
 
   return undefined;
 }
 SetScriptHints(MapTypedParImplDepth1,         { cloneAtCallsite: true });
 
 function ReduceTypedSeqImpl(array, outputType, func, initial) {
   assert(IsObject(array) && ObjectIsTypedObject(array), "Reduce called on non-object or untyped input array.");
--- a/js/src/builtin/Utilities.js
+++ b/js/src/builtin/Utilities.js
@@ -91,67 +91,16 @@ var std_Map_iterator_next = Object.getPr
 var std_Set_iterator_next = Object.getPrototypeOf(Set()[std_iterator]()).next;
 
 /* Safe versions of ARRAY.push(ELEMENT) */
 #define ARRAY_PUSH(ARRAY, ELEMENT) \
   callFunction(std_Array_push, ARRAY, ELEMENT);
 #define ARRAY_SLICE(ARRAY, ELEMENT) \
   callFunction(std_Array_slice, ARRAY, ELEMENT);
 
-/********** Parallel JavaScript macros and so on **********/
-
-#ifdef ENABLE_PARALLEL_JS
-
-/* The mode asserts options object. */
-#define TRY_PARALLEL(MODE) \
-  ((!MODE || MODE.mode !== "seq"))
-#define ASSERT_SEQUENTIAL_IS_OK(MODE) \
-  do { if (MODE) AssertSequentialIsOK(MODE) } while(false)
-
-/**
- * The ParallelSpew intrinsic is only defined in debug mode, so define a dummy
- * if debug is not on.
- */
-#ifndef DEBUG
-#define ParallelSpew(args)
-#endif
-
-#define MAX_SLICE_SHIFT 6
-#define MAX_SLICE_SIZE 64
-#define MAX_SLICES_PER_WORKER 8
-
-/**
- * Macros to help compute the start and end indices of slices based on id. Use
- * with the object returned by ComputeSliceInfo.
- */
-#define SLICE_START(info, id) \
-    (id << info.shift)
-#define SLICE_END(info, start, length) \
-    std_Math_min(start + (1 << info.shift), length)
-#define SLICE_COUNT(info) \
-    info.statuses.length
-
-/**
- * ForkJoinGetSlice acts as identity when we are not in a parallel section, so
- * pass in the next sequential value when we are in sequential mode. The
- * reason for this odd API is because intrinsics *need* to be called during
- * ForkJoin's warmup to fill the TI info.
- */
-#define GET_SLICE(info, id) \
-    ((id = ForkJoinGetSlice(InParallelSection() ? -1 : NextSequentialSliceId(info, -1))) >= 0)
-
-#define SLICE_STATUS_DONE 1
-
-/**
- * Macro to mark a slice as completed in the info object.
- */
-#define MARK_SLICE_DONE(info, id) \
-    UnsafePutElements(info.statuses, id, SLICE_STATUS_DONE)
-
-#endif // ENABLE_PARALLEL_JS
 
 /********** List specification type **********/
 
 
 /* Spec: ECMAScript Language Specification, 5.1 edition, 8.8 */
 function List() {}
 {
   let ListProto = std_Object_create(null);
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -409,24 +409,24 @@ case "$target" in
             AC_MSG_ERROR([\$(CXX) test failed.  You must have MS VC++ in your path to build.]) )
         AC_LANG_RESTORE
 
         changequote(,)
         _MSVC_VER_FILTER='s|.*[^!-~]([0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?).*|\1|p'
         changequote([,])
 
         # Determine compiler version
-        CC_VERSION=`"${CC}" -v 2>&1 | sed -nre "$_MSVC_VER_FILTER"`
+        CC_VERSION=`${CC} -v 2>&1 | sed -nre "$_MSVC_VER_FILTER"`
         _CC_MAJOR_VERSION=`echo ${CC_VERSION} | $AWK -F\. '{ print $1 }'`
         _CC_MINOR_VERSION=`echo ${CC_VERSION} | $AWK -F\. '{ print $2 }'`
         _CC_RELEASE=`echo ${CC_VERSION} | $AWK -F\. '{ print $3 }'`
         _CC_BUILD=`echo ${CC_VERSION} | $AWK -F\. '{ print $4 }'`
         _MSC_VER=${_CC_MAJOR_VERSION}${_CC_MINOR_VERSION}
 
-        CXX_VERSION=`"${CXX}" -v 2>&1 | sed -nre "$_MSVC_VER_FILTER"`
+        CXX_VERSION=`${CXX} -v 2>&1 | sed -nre "$_MSVC_VER_FILTER"`
         _CXX_MAJOR_VERSION=`echo ${CXX_VERSION} | $AWK -F\. '{ print $1 }'`
 
         if test "$_CC_MAJOR_VERSION" != "$_CXX_MAJOR_VERSION"; then
             AC_MSG_ERROR([The major versions of \$CC and \$CXX do not match.])
         fi
 
         AC_DEFINE(_CRT_SECURE_NO_WARNINGS)
         AC_DEFINE(_CRT_NONSTDC_NO_WARNINGS)
@@ -1326,17 +1326,17 @@ MOZ_ARG_ENABLE_BOOL(profiling,
 dnl ========================================================
 dnl System overrides of the defaults for host
 dnl ========================================================
 case "$host" in
 *mingw*)
     if test -n "$_WIN32_MSVC"; then
         HOST_AR=lib
         HOST_AR_FLAGS='-NOLOGO -OUT:$@'
-        HOST_CFLAGS="$HOST_CFLAGS -TC -nologo -Fd\$(HOST_PDBFILE)"
+        HOST_CFLAGS="$HOST_CFLAGS -TC -nologo"
         HOST_RANLIB='echo ranlib'
     else
         HOST_CFLAGS="$HOST_CFLAGS -mwindows"
     fi
     HOST_CFLAGS="$HOST_CFLAGS -DXP_WIN32 -DXP_WIN -DWIN32 -D_WIN32 -DNO_X11 -D_CRT_SECURE_NO_WARNINGS"
     HOST_NSPR_MDCPUCFG='\"md/_winnt.cfg\"'
     HOST_OPTIMIZE_FLAGS="${HOST_OPTIMIZE_FLAGS=-O2}"
     HOST_BIN_SUFFIX=.exe
@@ -1637,18 +1637,18 @@ ia64*-hpux*)
         MKSHLIB='$(LD) -NOLOGO -DLL -OUT:$@ -PDB:$(LINK_PDBFILE) $(DSO_LDOPTS)'
         MKCSHLIB='$(LD) -NOLOGO -DLL -OUT:$@ -PDB:$(LINK_PDBFILE) $(DSO_LDOPTS)'
         MKSHLIB_FORCE_ALL=
         MKSHLIB_UNFORCE_ALL=
         DSO_LDOPTS=-SUBSYSTEM:WINDOWS
         _USE_CPP_INCLUDE_FLAG=1
         _DEFINES_CFLAGS='-FI $(DEPTH)/js/src/js-confdefs.h -DMOZILLA_CLIENT'
         _DEFINES_CXXFLAGS='-FI $(DEPTH)/js/src/js-confdefs.h -DMOZILLA_CLIENT'
-        CFLAGS="$CFLAGS -W3 -Gy -Fd\$(COMPILE_PDBFILE)"
-        CXXFLAGS="$CXXFLAGS -W3 -Gy -Fd\$(COMPILE_PDBFILE)"
+        CFLAGS="$CFLAGS -W3 -Gy"
+        CXXFLAGS="$CXXFLAGS -W3 -Gy"
         if test "$_CC_SUITE" -ge "12"; then
             dnl VS2013+ requires -FS when parallel building by make -jN.
             dnl If nothing, compiler sometimes causes C1041 error.
             dnl
             dnl Visual Studio 2013 supports -Gw flags
             dnl http://blogs.msdn.com/b/vcblog/archive/2013/09/11/introducing-gw-compiler-switch.aspx
             CFLAGS="$CFLAGS -FS -Gw"
             CXXFLAGS="$CXXFLAGS -FS -Gw"
@@ -1659,17 +1659,16 @@ ia64*-hpux*)
         # don't warn about it by default. So for consistency/sanity, we turn
         # it off on MSVC, too.
         CFLAGS="$CFLAGS -wd4244"
         CXXFLAGS="$CXXFLAGS -wd4244 -wd4251"
         # make 'foo == bar;' error out
         CFLAGS="$CFLAGS -we4553"
         CXXFLAGS="$CXXFLAGS -we4553"
         LIBS="$LIBS kernel32.lib user32.lib gdi32.lib winmm.lib wsock32.lib advapi32.lib psapi.lib"
-        MOZ_DEBUG_FLAGS='-Zi'
         MOZ_DEBUG_LDFLAGS='-DEBUG -DEBUGTYPE:CV'
         WARNINGS_AS_ERRORS='-WX'
         MOZ_OPTIMIZE_FLAGS="-O2"
         MOZ_FIX_LINK_PATHS=
         MOZ_COMPONENT_NSPR_LIBS='$(NSPR_LIBS)'
         LDFLAGS="$LDFLAGS -LARGEADDRESSAWARE -NXCOMPAT"
         if test -z "$DEVELOPER_OPTIONS"; then
             LDFLAGS="$LDFLAGS -RELEASE"
@@ -3452,25 +3451,25 @@ MOZ_ARG_ENABLE_BOOL(more-deterministic,
                           Enable changes that make the shell more deterministic],
     JS_MORE_DETERMINISTIC=1,
     JS_MORE_DETERMINISTIC= )
 if test -n "$JS_MORE_DETERMINISTIC"; then
     AC_DEFINE(JS_MORE_DETERMINISTIC)
 fi
 
 dnl ========================================================
-dnl Enable output of backtraces on artificial OOMs
+dnl Enable breakpoint for artificial OOMs
 dnl ========================================================
-MOZ_ARG_ENABLE_BOOL(oom-backtrace,
-[  --enable-oom-backtrace
-                          Enable output of backtraces on artificial OOMs (-A)],
-    JS_OOM_DO_BACKTRACES=1,
-    JS_OOM_DO_BACKTRACES= )
-if test -n "$JS_OOM_DO_BACKTRACES"; then
-    AC_DEFINE(JS_OOM_DO_BACKTRACES)
+MOZ_ARG_ENABLE_BOOL(oom-breakpoint,
+[  --enable-oom-breakpoint
+                          Enable a breakpoint function for artificial OOMs],
+    JS_OOM_BREAKPOINT=1,
+    JS_OOM_BREAKPOINT= )
+if test -n "$JS_OOM_BREAKPOINT"; then
+    AC_DEFINE(JS_OOM_BREAKPOINT)
 fi
 
 MOZ_CHECK_CCACHE
 
 dnl ========================================================
 dnl = Enable static checking using gcc-dehydra
 dnl ========================================================
 
@@ -3880,17 +3879,16 @@ AC_SUBST(MC)
 AC_SUBST(WINDRES)
 AC_SUBST(IMPLIB)
 AC_SUBST(FILTER)
 AC_SUBST(BIN_FLAGS)
 AC_SUBST(MOZ_DEBUG)
 AC_SUBST(MOZ_DEBUG_SYMBOLS)
 AC_SUBST(MOZ_DEBUG_ENABLE_DEFS)
 AC_SUBST(MOZ_DEBUG_DISABLE_DEFS)
-AC_SUBST(MOZ_DEBUG_FLAGS)
 AC_SUBST(MOZ_DEBUG_LDFLAGS)
 AC_SUBST(WARNINGS_AS_ERRORS)
 AC_SUBST(MOZ_JPROF)
 AC_SUBST(MOZ_SHARK)
 AC_SUBST(MOZ_INSTRUMENTS)
 AC_SUBST(MOZ_CALLGRIND)
 AC_SUBST(MOZ_VTUNE)
 AC_SUBST(MOZ_PROFILING)
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/setPrototypeOf.js
@@ -0,0 +1,102 @@
+load(libdir + 'asserts.js');
+
+function getObjects() {
+    function func(){}
+    return [func,
+            new func(),
+            {x: 5},
+            /regexp/,
+            [1, 2, 3],
+            new Date(),
+            new Number(1),
+            new Boolean(true),
+            new String('str'),
+            Object.create(null)];
+}
+
+var coercibleValues = [1,
+                       true,
+                       'string'];
+
+var nonCoercibleValues = [undefined,
+                          null];
+
+var valuesWithoutNull = coercibleValues.concat(undefined);
+
+function TestSetPrototypeOf(object, proto) {
+    assertEq(Object.setPrototypeOf(object, proto), object);
+    assertEq(Object.getPrototypeOf(object), proto);
+}
+
+// check if Object.setPrototypeOf works with coercible values
+for(var value of coercibleValues) {
+    assertEq(Object.setPrototypeOf(value, {}), value);
+
+    assertThrowsInstanceOf(() => Object.getPrototypeOf(value),
+        TypeError, "Coercible values should not have a prototype");
+}
+
+// check if Object.setPrototypeOf fails on non-coercible values
+for (var value of nonCoercibleValues) {
+    assertThrowsInstanceOf(() => Object.setPrototypeOf(value, {}),
+        TypeError, "Object.setPrototypeOf shouldn't work on non-coercible values");
+}
+
+// check if Object.setPrototypeOf works when prototype is set to non-objects
+var objects = getObjects();
+for (var object of objects) {
+    for (var proto of valuesWithoutNull) {
+        assertThrowsInstanceOf(() => Object.setPrototypeOf(object, proto),
+            TypeError, "Object.setPrototypeOf fails when the prototype is set to non-objects");
+    }
+}
+
+// check if Object.setPrototypeOf works when prototype is set to objects
+var objects1 = getObjects();
+var objects2 = getObjects();
+for (var object1 of objects1) {
+    for (var object2 of objects2) {
+        TestSetPrototypeOf(object1, object2);
+    }
+}
+
+// check if Object.setPrototypeOf works when prototype is set to null
+objects = getObjects();
+for (var object of objects) {
+    TestSetPrototypeOf(object, null);
+}
+
+// check if Object.setPrototypeOf fails when object is not extensible
+var objects = getObjects();
+var proto = {};
+for (var object of objects) {
+    Object.preventExtensions(object);
+    assertThrowsInstanceOf(() => Object.setPrototypeOf(object, proto),
+        TypeError, "Object.setPrototypeOf should fail when the object is not extensible");
+}
+
+// check if Object.setPrototypeOf works with prototype lookup
+var object = {};
+assertEq('x' in object, false);
+assertEq('y' in object, false);
+
+var oldProto = {
+    x: 'old x',
+    y: 'old y'
+};
+Object.setPrototypeOf(object, oldProto);
+assertEq(object.x, 'old x');
+assertEq(object.y, 'old y');
+
+var newProto = {
+    x: 'new x'
+};
+Object.setPrototypeOf(object, newProto);
+assertEq(object.x, 'new x');
+assertEq('y' in object, false);
+
+// check if Object.setPrototypeOf throws TypeError on fewer arguments
+assertThrowsInstanceOf(() => Object.setPrototypeOf(),
+    TypeError, "Object.setPrototypeOf throws TypeError when called without any parameters");
+assertThrowsInstanceOf(() => Object.setPrototypeOf({}),
+    TypeError, "Object.setPrototypeOf throws TypeError when called with 1 parameter");
--- a/js/src/jit-test/tests/parallel/ic-setelement.js
+++ b/js/src/jit-test/tests/parallel/ic-setelement.js
@@ -108,28 +108,27 @@ function set(a, n) {
   var foo = 0;
   var foo = 0;
   var foo = 0;
   var foo = 0;
   var foo = 0;
   var foo = 0;
   var foo = 0;
 }
-set({}, 1024);
-set({}, 1024);
+set({}, 256);
 function Foo() { }
-set(new Foo, 1024);
+set(new Foo, 256);
 
 function testSetDense() {
   assertArraySeqParResultsEq(
     range(0, minItemsTestingThreshold),
     "map",
     function (i) {
       var a1 = [];
       // Defines .foo
-      set(a1, i+1);
+      set(a1, 32);
       return a1[i];
     });
 }
 
 if (getBuildConfiguration().parallelJS) {
   testSetDense();
 }
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -1420,39 +1420,36 @@ class MOZ_STACK_CLASS ModuleCompiler
     bool collectAccesses(MIRGenerator &gen) {
         if (!module_->addHeapAccesses(gen.heapAccesses()))
             return false;
         if (!globalAccesses_.appendAll(gen.globalAccesses()))
             return false;
         return true;
     }
 
-#ifdef MOZ_VTUNE
+#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
     bool trackProfiledFunction(const Func &func, unsigned endCodeOffset) {
-        unsigned startCodeOffset = func.code()->offset();
-        return module_->trackProfiledFunction(func.name(), startCodeOffset, endCodeOffset);
-    }
-#endif
-#ifdef JS_ION_PERF
-    bool trackPerfProfiledFunction(const Func &func, unsigned endCodeOffset) {
         unsigned lineno = 0U, columnIndex = 0U;
         parser().tokenStream.srcCoords.lineNumAndColumnIndex(func.srcOffset(), &lineno, &columnIndex);
         unsigned startCodeOffset = func.code()->offset();
-        return module_->trackPerfProfiledFunction(func.name(), startCodeOffset, endCodeOffset,
-                                                  lineno, columnIndex);
-    }
-
+        return module_->trackProfiledFunction(func.name(), startCodeOffset, endCodeOffset,
+                                              lineno, columnIndex);
+    }
+#endif
+
+#ifdef JS_ION_PERF
     bool trackPerfProfiledBlocks(AsmJSPerfSpewer &perfSpewer, const Func &func, unsigned endCodeOffset) {
         unsigned startCodeOffset = func.code()->offset();
         perfSpewer.noteBlocksOffsets();
         unsigned endInlineCodeOffset = perfSpewer.endInlineCode.offset();
         return module_->trackPerfProfiledBlocks(func.name(), startCodeOffset, endInlineCodeOffset,
                                                 endCodeOffset, perfSpewer.basicBlocks());
     }
 #endif
+
     bool addFunctionCounts(IonScriptCounts *counts) {
         return module_->addFunctionCounts(counts);
     }
 
     void finishFunctionBodies() {
         JS_ASSERT(!finishedFunctionBodies_);
         masm_.align(AsmJSPageSize);
         finishedFunctionBodies_ = true;
@@ -1528,28 +1525,30 @@ class MOZ_STACK_CLASS ModuleCompiler
             return false;
 
         // c.f. JitCode::copyFrom
         JS_ASSERT(masm_.jumpRelocationTableBytes() == 0);
         JS_ASSERT(masm_.dataRelocationTableBytes() == 0);
         JS_ASSERT(masm_.preBarrierTableBytes() == 0);
         JS_ASSERT(!masm_.hasEnteredExitFrame());
 
-#ifdef JS_ION_PERF
+#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
         // Fix up the code offsets.  Note the endCodeOffset should not be
         // filtered through 'actualOffset' as it is generated using 'size()'
         // rather than a label.
-        for (unsigned i = 0; i < module_->numPerfFunctions(); i++) {
-            AsmJSModule::ProfiledFunction &func = module_->perfProfiledFunction(i);
-            func.startCodeOffset = masm_.actualOffset(func.startCodeOffset);
+        for (unsigned i = 0; i < module_->numProfiledFunctions(); i++) {
+            AsmJSModule::ProfiledFunction &func = module_->profiledFunction(i);
+            func.pod.startCodeOffset = masm_.actualOffset(func.pod.startCodeOffset);
         }
-
+#endif
+
+#ifdef JS_ION_PERF
         for (unsigned i = 0; i < module_->numPerfBlocksFunctions(); i++) {
             AsmJSModule::ProfiledBlocksFunction &func = module_->perfProfiledBlocksFunction(i);
-            func.startCodeOffset = masm_.actualOffset(func.startCodeOffset);
+            func.pod.startCodeOffset = masm_.actualOffset(func.pod.startCodeOffset);
             func.endInlineCodeOffset = masm_.actualOffset(func.endInlineCodeOffset);
             BasicBlocksVector &basicBlocks = func.blocks;
             for (uint32_t i = 0; i < basicBlocks.length(); i++) {
                 Record &r = basicBlocks[i];
                 r.startOffset = masm_.actualOffset(r.startOffset);
                 r.endOffset = masm_.actualOffset(r.endOffset);
             }
         }
@@ -5457,28 +5456,31 @@ GenerateCode(ModuleCompiler &m, ModuleCo
         return false;
 
     jit::IonScriptCounts *counts = codegen->extractUnassociatedScriptCounts();
     if (counts && !m.addFunctionCounts(counts)) {
         js_delete(counts);
         return false;
     }
 
-#ifdef MOZ_VTUNE
-    if (IsVTuneProfilingActive() && !m.trackProfiledFunction(func, m.masm().size()))
+#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
+    // Profiling might not be active now, but it may be activated later (perhaps
+    // after the module has been cached and reloaded from the cache). Function
+    // profiling info isn't huge, so store it always (in --enable-profiling
+    // builds, which is only Nightly builds, but default).
+    if (!m.trackProfiledFunction(func, m.masm().size()))
         return false;
 #endif
 
 #ifdef JS_ION_PERF
+    // Per-block profiling info uses significantly more memory so only store
+    // this information if it is actively requested.
     if (PerfBlockEnabled()) {
         if (!m.trackPerfProfiledBlocks(mir.perfSpewer(), func, m.masm().size()))
             return false;
-    } else if (PerfFuncEnabled()) {
-        if (!m.trackPerfProfiledFunction(func, m.masm().size()))
-            return false;
     }
 #endif
 
     // Align internal function headers.
     m.masm().align(CodeAlignment);
 
     func.accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);
     if (!m.maybeReportCompileTime(func))
--- a/js/src/jit/AsmJSLink.cpp
+++ b/js/src/jit/AsmJSLink.cpp
@@ -473,18 +473,18 @@ HandleDynamicLinkFailure(JSContext *cx, 
 static bool
 SendFunctionsToVTune(JSContext *cx, AsmJSModule &module)
 {
     uint8_t *base = module.codeBase();
 
     for (unsigned i = 0; i < module.numProfiledFunctions(); i++) {
         const AsmJSModule::ProfiledFunction &func = module.profiledFunction(i);
 
-        uint8_t *start = base + func.startCodeOffset;
-        uint8_t *end   = base + func.endCodeOffset;
+        uint8_t *start = base + func.pod.startCodeOffset;
+        uint8_t *end   = base + func.pod.endCodeOffset;
         JS_ASSERT(end >= start);
 
         unsigned method_id = iJIT_GetNewMethodID();
         if (method_id == 0)
             return false;
 
         JSAutoByteString bytes;
         const char *method_name = AtomToPrintableString(cx, func.name, &bytes);
@@ -514,29 +514,30 @@ static bool
 SendFunctionsToPerf(JSContext *cx, AsmJSModule &module)
 {
     if (!PerfFuncEnabled())
         return true;
 
     uintptr_t base = (uintptr_t) module.codeBase();
     const char *filename = module.scriptSource()->filename();
 
-    for (unsigned i = 0; i < module.numPerfFunctions(); i++) {
-        const AsmJSModule::ProfiledFunction &func = module.perfProfiledFunction(i);
-        uintptr_t start = base + (unsigned long) func.startCodeOffset;
-        uintptr_t end   = base + (unsigned long) func.endCodeOffset;
+    for (unsigned i = 0; i < module.numProfiledFunctions(); i++) {
+        const AsmJSModule::ProfiledFunction &func = module.profiledFunction(i);
+        uintptr_t start = base + (unsigned long) func.pod.startCodeOffset;
+        uintptr_t end   = base + (unsigned long) func.pod.endCodeOffset;
         JS_ASSERT(end >= start);
         size_t size = end - start;
 
         JSAutoByteString bytes;
         const char *name = AtomToPrintableString(cx, func.name, &bytes);
         if (!name)
             return false;
 
-        writePerfSpewerAsmJSFunctionMap(start, size, filename, func.lineno, func.columnIndex, name);
+        writePerfSpewerAsmJSFunctionMap(start, size, filename, func.pod.lineno,
+                                        func.pod.columnIndex, name);
     }
 
     return true;
 }
 
 static bool
 SendBlocksToPerf(JSContext *cx, AsmJSModule &module)
 {
@@ -544,24 +545,24 @@ SendBlocksToPerf(JSContext *cx, AsmJSMod
         return true;
 
     unsigned long funcBaseAddress = (unsigned long) module.codeBase();
     const char *filename = module.scriptSource()->filename();
 
     for (unsigned i = 0; i < module.numPerfBlocksFunctions(); i++) {
         const AsmJSModule::ProfiledBlocksFunction &func = module.perfProfiledBlocksFunction(i);
 
-        size_t size = func.endCodeOffset - func.startCodeOffset;
+        size_t size = func.pod.endCodeOffset - func.pod.startCodeOffset;
 
         JSAutoByteString bytes;
         const char *name = AtomToPrintableString(cx, func.name, &bytes);
         if (!name)
             return false;
 
-        writePerfSpewerAsmJSBlocksMap(funcBaseAddress, func.startCodeOffset,
+        writePerfSpewerAsmJSBlocksMap(funcBaseAddress, func.pod.startCodeOffset,
                                       func.endInlineCodeOffset, size, filename, name, func.blocks);
     }
 
     return true;
 }
 #endif
 
 static bool
--- a/js/src/jit/AsmJSModule.cpp
+++ b/js/src/jit/AsmJSModule.cpp
@@ -400,21 +400,20 @@ AsmJSModule::addSizeOfMisc(mozilla::Mall
                            size_t *asmJSModuleData)
 {
     *asmJSModuleCode += pod.totalBytes_;
     *asmJSModuleData += mallocSizeOf(this) +
                         globals_.sizeOfExcludingThis(mallocSizeOf) +
                         exits_.sizeOfExcludingThis(mallocSizeOf) +
                         exports_.sizeOfExcludingThis(mallocSizeOf) +
                         heapAccesses_.sizeOfExcludingThis(mallocSizeOf) +
-#if defined(MOZ_VTUNE)
+#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
                         profiledFunctions_.sizeOfExcludingThis(mallocSizeOf) +
 #endif
 #if defined(JS_ION_PERF)
-                        profiledFunctions_.sizeOfExcludingThis(mallocSizeOf) +
                         perfProfiledBlocksFunctions_.sizeOfExcludingThis(mallocSizeOf) +
 #endif
                         functionCounts_.sizeOfExcludingThis(mallocSizeOf) +
                         staticLinkData_.sizeOfExcludingThis(mallocSizeOf);
 }
 
 static void
 AsmJSModuleObject_finalize(FreeOp *fop, JSObject *obj)
@@ -756,16 +755,41 @@ const uint8_t *
 AsmJSModule::StaticLinkData::deserialize(ExclusiveContext *cx, const uint8_t *cursor)
 {
     (cursor = ReadScalar<uint32_t>(cursor, &interruptExitOffset)) &&
     (cursor = DeserializePodVector(cx, cursor, &relativeLinks)) &&
     (cursor = DeserializePodVector(cx, cursor, &absoluteLinks));
     return cursor;
 }
 
+#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
+size_t
+AsmJSModule::ProfiledFunction::serializedSize() const
+{
+    return SerializedNameSize(name) +
+           sizeof(pod);
+}
+
+uint8_t *
+AsmJSModule::ProfiledFunction::serialize(uint8_t *cursor) const
+{
+    cursor = SerializeName(cursor, name);
+    cursor = WriteBytes(cursor, &pod, sizeof(pod));
+    return cursor;
+}
+
+const uint8_t *
+AsmJSModule::ProfiledFunction::deserialize(ExclusiveContext *cx, const uint8_t *cursor)
+{
+    (cursor = DeserializeName(cx, cursor, &name)) &&
+    (cursor = ReadBytes(cursor, &pod, sizeof(pod)));
+    return cursor;
+}
+#endif
+
 bool
 AsmJSModule::StaticLinkData::clone(ExclusiveContext *cx, StaticLinkData *out) const
 {
     out->interruptExitOffset = interruptExitOffset;
     return ClonePodVector(cx, relativeLinks, &out->relativeLinks) &&
            ClonePodVector(cx, absoluteLinks, &out->absoluteLinks);
 }
 
@@ -783,31 +807,37 @@ AsmJSModule::serializedSize() const
            pod.codeBytes_ +
            SerializedNameSize(globalArgumentName_) +
            SerializedNameSize(importArgumentName_) +
            SerializedNameSize(bufferArgumentName_) +
            SerializedVectorSize(globals_) +
            SerializedVectorSize(exits_) +
            SerializedVectorSize(exports_) +
            SerializedPodVectorSize(heapAccesses_) +
+#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
+           SerializedVectorSize(profiledFunctions_) +
+#endif
            staticLinkData_.serializedSize();
 }
 
 uint8_t *
 AsmJSModule::serialize(uint8_t *cursor) const
 {
     cursor = WriteBytes(cursor, &pod, sizeof(pod));
     cursor = WriteBytes(cursor, code_, pod.codeBytes_);
     cursor = SerializeName(cursor, globalArgumentName_);
     cursor = SerializeName(cursor, importArgumentName_);
     cursor = SerializeName(cursor, bufferArgumentName_);
     cursor = SerializeVector(cursor, globals_);
     cursor = SerializeVector(cursor, exits_);
     cursor = SerializeVector(cursor, exports_);
     cursor = SerializePodVector(cursor, heapAccesses_);
+#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
+    cursor = SerializeVector(cursor, profiledFunctions_);
+#endif
     cursor = staticLinkData_.serialize(cursor);
     return cursor;
 }
 
 const uint8_t *
 AsmJSModule::deserialize(ExclusiveContext *cx, const uint8_t *cursor)
 {
     // To avoid GC-during-deserialization corner cases, prevent atoms from
@@ -819,16 +849,19 @@ AsmJSModule::deserialize(ExclusiveContex
     (cursor = ReadBytes(cursor, code_, pod.codeBytes_)) &&
     (cursor = DeserializeName(cx, cursor, &globalArgumentName_)) &&
     (cursor = DeserializeName(cx, cursor, &importArgumentName_)) &&
     (cursor = DeserializeName(cx, cursor, &bufferArgumentName_)) &&
     (cursor = DeserializeVector(cx, cursor, &globals_)) &&
     (cursor = DeserializeVector(cx, cursor, &exits_)) &&
     (cursor = DeserializeVector(cx, cursor, &exports_)) &&
     (cursor = DeserializePodVector(cx, cursor, &heapAccesses_)) &&
+#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
+    (cursor = DeserializeVector(cx, cursor, &profiledFunctions_)) &&
+#endif
     (cursor = staticLinkData_.deserialize(cx, cursor));
 
     loadedFromCache_ = true;
     return cursor;
 }
 
 // When a module is cloned, we memcpy its executable code. If, right before or
 // during the clone, another thread calls AsmJSModule::protectCode() then the
--- a/js/src/jit/AsmJSModule.h
+++ b/js/src/jit/AsmJSModule.h
@@ -290,55 +290,67 @@ class AsmJSModule
         const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor);
         bool clone(ExclusiveContext *cx, ExportedFunction *out) const;
     };
 
 #if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
     // Function information to add to the VTune JIT profiler following linking.
     struct ProfiledFunction
     {
-        JSAtom *name;
-        unsigned startCodeOffset;
-        unsigned endCodeOffset;
-        unsigned lineno;
-        unsigned columnIndex;
+        PropertyName *name;
+        struct Pod {
+            unsigned startCodeOffset;
+            unsigned endCodeOffset;
+            unsigned lineno;
+            unsigned columnIndex;
+        } pod;
 
-        ProfiledFunction(JSAtom *name, unsigned start, unsigned end,
-                         unsigned line = 0U, unsigned column = 0U)
-          : name(name),
-            startCodeOffset(start),
-            endCodeOffset(end),
-            lineno(line),
-            columnIndex(column)
+        explicit ProfiledFunction()
+          : name(nullptr)
+        { }
+
+        ProfiledFunction(PropertyName *name, unsigned start, unsigned end,
+                         unsigned line = 0, unsigned column = 0)
+          : name(name)
         {
             JS_ASSERT(name->isTenured());
+
+            pod.startCodeOffset = start;
+            pod.endCodeOffset = end;
+            pod.lineno = line;
+            pod.columnIndex = column;
         }
 
         void trace(JSTracer *trc) {
-            MarkStringUnbarriered(trc, &name, "asm.js profiled function name");
+            if (name)
+                MarkStringUnbarriered(trc, &name, "asm.js profiled function name");
         }
+
+        size_t serializedSize() const;
+        uint8_t *serialize(uint8_t *cursor) const;
+        const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor);
     };
 #endif
 
 #if defined(JS_ION_PERF)
     struct ProfiledBlocksFunction : public ProfiledFunction
     {
         unsigned endInlineCodeOffset;
         jit::BasicBlocksVector blocks;
 
-        ProfiledBlocksFunction(JSAtom *name, unsigned start, unsigned endInline, unsigned end,
+        ProfiledBlocksFunction(PropertyName *name, unsigned start, unsigned endInline, unsigned end,
                                jit::BasicBlocksVector &blocksVector)
           : ProfiledFunction(name, start, end), endInlineCodeOffset(endInline),
             blocks(mozilla::Move(blocksVector))
         {
             JS_ASSERT(name->isTenured());
         }
 
         ProfiledBlocksFunction(ProfiledBlocksFunction &&copy)
-          : ProfiledFunction(copy.name, copy.startCodeOffset, copy.endCodeOffset),
+          : ProfiledFunction(copy.name, copy.pod.startCodeOffset, copy.pod.endCodeOffset),
             endInlineCodeOffset(copy.endInlineCodeOffset), blocks(mozilla::Move(copy.blocks))
         { }
     };
 #endif
 
     struct RelativeLink
     {
         uint32_t patchAtOffset;
@@ -439,23 +451,21 @@ class AsmJSModule
         for (unsigned i = 0; i < globals_.length(); i++)
             globals_[i].trace(trc);
         for (unsigned i = 0; i < exports_.length(); i++)
             exports_[i].trace(trc);
         for (unsigned i = 0; i < exits_.length(); i++) {
             if (exitIndexToGlobalDatum(i).fun)
                 MarkObject(trc, &exitIndexToGlobalDatum(i).fun, "asm.js imported function");
         }
-#if defined(MOZ_VTUNE)
+#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
         for (unsigned i = 0; i < profiledFunctions_.length(); i++)
             profiledFunctions_[i].trace(trc);
 #endif
 #if defined(JS_ION_PERF)
-        for (unsigned i = 0; i < profiledFunctions_.length(); i++)
-            profiledFunctions_[i].trace(trc);
         for (unsigned i = 0; i < perfProfiledBlocksFunctions_.length(); i++)
             perfProfiledBlocksFunctions_[i].trace(trc);
 #endif
         if (maybeHeap_)
             gc::MarkObject(trc, &maybeHeap_, "asm.js heap");
 
         if (globalArgumentName_)
             MarkStringUnbarriered(trc, &globalArgumentName_, "asm.js global argument name");
@@ -566,54 +576,46 @@ class AsmJSModule
     }
     ExportedFunction &exportedFunction(unsigned i) {
         return exports_[i];
     }
     CodePtr entryTrampoline(const ExportedFunction &func) const {
         JS_ASSERT(func.pod.codeOffset_ != UINT32_MAX);
         return JS_DATA_TO_FUNC_PTR(CodePtr, code_ + func.pod.codeOffset_);
     }
-#ifdef MOZ_VTUNE
-    bool trackProfiledFunction(JSAtom *name, unsigned startCodeOffset, unsigned endCodeOffset) {
-        ProfiledFunction func(name, startCodeOffset, endCodeOffset);
+
+#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
+    bool trackProfiledFunction(PropertyName *name, unsigned startCodeOffset, unsigned endCodeOffset,
+                               unsigned line, unsigned column)
+    {
+        ProfiledFunction func(name, startCodeOffset, endCodeOffset, line, column);
         return profiledFunctions_.append(func);
     }
     unsigned numProfiledFunctions() const {
         return profiledFunctions_.length();
     }
     ProfiledFunction &profiledFunction(unsigned i) {
         return profiledFunctions_[i];
     }
 #endif
+
 #ifdef JS_ION_PERF
-    bool trackPerfProfiledFunction(JSAtom *name, unsigned startCodeOffset, unsigned endCodeOffset,
-                                   unsigned line, unsigned column)
-    {
-        ProfiledFunction func(name, startCodeOffset, endCodeOffset, line, column);
-        return profiledFunctions_.append(func);
-    }
-    unsigned numPerfFunctions() const {
-        return profiledFunctions_.length();
-    }
-    ProfiledFunction &perfProfiledFunction(unsigned i) {
-        return profiledFunctions_[i];
-    }
-
-    bool trackPerfProfiledBlocks(JSAtom *name, unsigned startCodeOffset, unsigned endInlineCodeOffset,
+    bool trackPerfProfiledBlocks(PropertyName *name, unsigned startCodeOffset, unsigned endInlineCodeOffset,
                                  unsigned endCodeOffset, jit::BasicBlocksVector &basicBlocks) {
         ProfiledBlocksFunction func(name, startCodeOffset, endInlineCodeOffset, endCodeOffset, basicBlocks);
         return perfProfiledBlocksFunctions_.append(mozilla::Move(func));
     }
     unsigned numPerfBlocksFunctions() const {
         return perfProfiledBlocksFunctions_.length();
     }
     ProfiledBlocksFunction &perfProfiledBlocksFunction(unsigned i) {
         return perfProfiledBlocksFunctions_[i];
     }
 #endif
+
     bool hasArrayView() const {
         return pod.hasArrayView_;
     }
     unsigned numFFIs() const {
         return pod.numFFIs_;
     }
     unsigned numGlobalVars() const {
         return pod.numGlobalVars_;
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -3532,71 +3532,100 @@ CodeGenerator::visitNewDeclEnvObject(LNe
         return false;
 
     masm.newGCThing(objReg, tempReg, templateObj, ool->entry(), gc::DefaultHeap);
     masm.initGCThing(objReg, tempReg, templateObj);
     masm.bind(ool->rejoin());
     return true;
 }
 
-typedef JSObject *(*NewCallObjectFn)(JSContext *, HandleScript, HandleShape,
-                                     HandleTypeObject, HeapSlot *);
+typedef JSObject *(*NewCallObjectFn)(JSContext *, HandleShape, HandleTypeObject, HeapSlot *);
 static const VMFunction NewCallObjectInfo =
     FunctionInfo<NewCallObjectFn>(NewCallObject);
 
 bool
 CodeGenerator::visitNewCallObject(LNewCallObject *lir)
 {
     Register objReg = ToRegister(lir->output());
     Register tempReg = ToRegister(lir->temp());
 
     JSObject *templateObj = lir->mir()->templateObject();
 
-    // If we have a template object, we can inline call object creation.
     OutOfLineCode *ool;
     if (lir->slots()->isRegister()) {
         ool = oolCallVM(NewCallObjectInfo, lir,
-                        (ArgList(), ImmGCPtr(lir->mir()->block()->info().script()),
-                                    ImmGCPtr(templateObj->lastProperty()),
-                                    ImmGCPtr(templateObj->hasSingletonType() ? nullptr : templateObj->type()),
+                        (ArgList(), ImmGCPtr(templateObj->lastProperty()),
+                                    ImmGCPtr(templateObj->type()),
                                     ToRegister(lir->slots())),
                         StoreRegisterTo(objReg));
     } else {
         ool = oolCallVM(NewCallObjectInfo, lir,
-                        (ArgList(), ImmGCPtr(lir->mir()->block()->info().script()),
-                                    ImmGCPtr(templateObj->lastProperty()),
-                                    ImmGCPtr(templateObj->hasSingletonType() ? nullptr : templateObj->type()),
+                        (ArgList(), ImmGCPtr(templateObj->lastProperty()),
+                                    ImmGCPtr(templateObj->type()),
                                     ImmPtr(nullptr)),
                         StoreRegisterTo(objReg));
     }
     if (!ool)
         return false;
 
-    if (lir->mir()->needsSingletonType()
 #ifdef JSGC_GENERATIONAL
-        // Slot initialization is not barriered in this case, so we must either
+    if (templateObj->hasDynamicSlots()) {
+        // Slot initialization is unbarriered in this case, so we must either
         // allocate in the nursery or bail if that is not possible.
-        || lir->mir()->templateObject()->hasDynamicSlots()
+        masm.jump(ool->entry());
+    } else
 #endif
-       )
     {
-        // Objects can only be given singleton types in VM calls.
-        masm.jump(ool->entry());
-    } else {
+        // Inline call object creation, using the OOL path only for tricky cases.
         masm.newGCThing(objReg, tempReg, templateObj, ool->entry(), gc::DefaultHeap);
         masm.initGCThing(objReg, tempReg, templateObj);
-
-        if (lir->slots()->isRegister())
-            masm.storePtr(ToRegister(lir->slots()), Address(objReg, JSObject::offsetOfSlots()));
-    }
+    }
+
+    if (lir->slots()->isRegister())
+        masm.storePtr(ToRegister(lir->slots()), Address(objReg, JSObject::offsetOfSlots()));
 
     masm.bind(ool->rejoin());
     return true;
 }
 
+typedef JSObject *(*NewSingletonCallObjectFn)(JSContext *, HandleShape, HeapSlot *);
+static const VMFunction NewSingletonCallObjectInfo =
+    FunctionInfo<NewSingletonCallObjectFn>(NewSingletonCallObject);
+
+bool
+CodeGenerator::visitNewSingletonCallObject(LNewSingletonCallObject *lir)
+{
+    Register objReg = ToRegister(lir->output());
+
+    JSObject *templateObj = lir->mir()->templateObject();
+
+    OutOfLineCode *ool;
+    if (lir->slots()->isRegister()) {
+        ool = oolCallVM(NewSingletonCallObjectInfo, lir,
+                        (ArgList(), ImmGCPtr(templateObj->lastProperty()),
+                                    ToRegister(lir->slots())),
+                        StoreRegisterTo(objReg));
+    } else {
+        ool = oolCallVM(NewSingletonCallObjectInfo, lir,
+                        (ArgList(), ImmGCPtr(templateObj->lastProperty()),
+                                    ImmPtr(nullptr)),
+                        StoreRegisterTo(objReg));
+    }
+    if (!ool)
+        return false;
+
+    // Objects can only be given singleton types in VM calls.  We make the call
+    // out of line to not bloat inline code, even if (naively) this seems like
+    // extra work.
+    masm.jump(ool->entry());
+    masm.bind(ool->rejoin());
+
+    return true;
+}
+
 bool
 CodeGenerator::visitNewCallObjectPar(LNewCallObjectPar *lir)
 {
     Register resultReg = ToRegister(lir->output());
     Register cxReg = ToRegister(lir->forkJoinContext());
     Register tempReg1 = ToRegister(lir->getTemp0());
     Register tempReg2 = ToRegister(lir->getTemp1());
     JSObject *templateObj = lir->mir()->templateObj();
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -135,16 +135,17 @@ class CodeGenerator : public CodeGenerat
     bool visitNewArrayCallVM(LNewArray *lir);
     bool visitNewArray(LNewArray *lir);
     bool visitOutOfLineNewArray(OutOfLineNewArray *ool);
     bool visitNewObjectVMCall(LNewObject *lir);
     bool visitNewObject(LNewObject *lir);
     bool visitOutOfLineNewObject(OutOfLineNewObject *ool);
     bool visitNewDeclEnvObject(LNewDeclEnvObject *lir);
     bool visitNewCallObject(LNewCallObject *lir);
+    bool visitNewSingletonCallObject(LNewSingletonCallObject *lir);
     bool visitNewCallObjectPar(LNewCallObjectPar *lir);
     bool visitNewStringObject(LNewStringObject *lir);
     bool visitNewPar(LNewPar *lir);
     bool visitNewDenseArrayPar(LNewDenseArrayPar *lir);
     bool visitNewDerivedTypedObject(LNewDerivedTypedObject *lir);
     bool visitAbortPar(LAbortPar *lir);
     bool visitInitElem(LInitElem *lir);
     bool visitInitElemGetterSetter(LInitElemGetterSetter *lir);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -4677,17 +4677,21 @@ IonBuilder::createCallObject(MDefinition
         slots = MConstant::New(alloc(), NullValue());
     }
     current->add(slots);
 
     // Allocate the actual object. It is important that no intervening
     // instructions could potentially bailout, thus leaking the dynamic slots
     // pointer. Run-once scripts need a singleton type, so always do a VM call
     // in such cases.
-    MNewCallObject *callObj = MNewCallObject::New(alloc(), templateObj, script()->treatAsRunOnce(), slots);
+    MUnaryInstruction *callObj;
+    if (script()->treatAsRunOnce())
+        callObj = MNewRunOnceCallObject::New(alloc(), templateObj, slots);
+    else
+        callObj = MNewCallObject::New(alloc(), templateObj, slots);
     current->add(callObj);
 
     // Initialize the object's reserved slots. No post barrier is needed here,
     // for the same reason as in createDeclEnvObject.
     current->add(MStoreFixedSlot::New(alloc(), callObj, CallObject::enclosingScopeSlot(), scope));
     current->add(MStoreFixedSlot::New(alloc(), callObj, CallObject::calleeSlot(), callee));
 
     // Initialize argument slots.
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -485,21 +485,46 @@ class LNewCallObject : public LInstructi
 
     const LDefinition *temp() {
         return getTemp(0);
     }
 
     const LAllocation *slots() {
         return getOperand(0);
     }
+
     MNewCallObject *mir() const {
         return mir_->toNewCallObject();
     }
 };
 
+// Allocates a new CallObject with singleton type through an out-of-line VM
+// call. The inputs are:
+//      slots: either a reg representing a HeapSlot *, or a placeholder
+//             meaning that no slots pointer is needed.
+//
+class LNewSingletonCallObject : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(NewSingletonCallObject)
+
+    LNewSingletonCallObject(const LAllocation &slots) {
+        setOperand(0, slots);
+    }
+
+    const LAllocation *slots() {
+        return getOperand(0);
+    }
+
+    MNewCallObjectBase * mir() const {
+        MOZ_ASSERT(mir_->isNewCallObject() || mir_->isNewRunOnceCallObject());
+        return static_cast<MNewCallObjectBase *>(mir_);
+    }
+};
+
 class LNewCallObjectPar : public LInstructionHelper<1, 2, 2>
 {
     LNewCallObjectPar(const LAllocation &cx, const LAllocation &slots,
                       const LDefinition &temp1, const LDefinition &temp2)
     {
         setOperand(0, cx);
         setOperand(1, slots);
         setTemp(0, temp1);
@@ -2405,17 +2430,17 @@ class LMinMaxI : public LInstructionHelp
         return mir()->isMax() ? "Max" : "Min";
     }
 };
 
 class LMinMaxD : public LInstructionHelper<1, 2, 0>
 {
   public:
     LIR_HEADER(MinMaxD)
-    LMinMaxD(const LAllocation &first, const LAllocation &second) 
+    LMinMaxD(const LAllocation &first, const LAllocation &second)
     {
         setOperand(0, first);
         setOperand(1, second);
     }
 
     const LAllocation *first() {
         return this->getOperand(0);
     }
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -23,16 +23,17 @@
     _(TableSwitch)                  \
     _(TableSwitchV)                 \
     _(Goto)                         \
     _(NewArray)                     \
     _(NewObject)                    \
     _(NewSlots)                     \
     _(NewDeclEnvObject)             \
     _(NewCallObject)                \
+    _(NewSingletonCallObject)       \
     _(NewStringObject)              \
     _(NewPar)                       \
     _(NewDenseArrayPar)             \
     _(NewCallObjectPar)             \
     _(NewDerivedTypedObject)        \
     _(AbortPar)                     \
     _(InitElem)                     \
     _(InitElemGetterSetter)         \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -196,17 +196,45 @@ bool
 LIRGenerator::visitNewCallObject(MNewCallObject *ins)
 {
     LAllocation slots;
     if (ins->slots()->type() == MIRType_Slots)
         slots = useRegister(ins->slots());
     else
         slots = LConstantIndex::Bogus();
 
-    LNewCallObject *lir = new(alloc()) LNewCallObject(slots, temp());
+    LInstruction *lir;
+    if (ins->templateObject()->hasSingletonType()) {
+        LNewSingletonCallObject *singletonLir = new(alloc()) LNewSingletonCallObject(slots);
+        if (!define(singletonLir, ins))
+            return false;
+        lir = singletonLir;
+    } else {
+        LNewCallObject *normalLir = new(alloc()) LNewCallObject(slots, temp());
+        if (!define(normalLir, ins))
+            return false;
+        lir = normalLir;
+    }
+
+    if (!assignSafepoint(lir, ins))
+        return false;
+
+    return true;
+}
+
+bool
+LIRGenerator::visitNewRunOnceCallObject(MNewRunOnceCallObject *ins)
+{
+    LAllocation slots;
+    if (ins->slots()->type() == MIRType_Slots)
+        slots = useRegister(ins->slots());
+    else
+        slots = LConstantIndex::Bogus();
+
+    LNewSingletonCallObject *lir = new(alloc()) LNewSingletonCallObject(slots);
     if (!define(lir, ins))
         return false;
 
     if (!assignSafepoint(lir, ins))
         return false;
 
     return true;
 }
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -66,16 +66,17 @@ class LIRGenerator : public LIRGenerator
     bool visitCallee(MCallee *callee);
     bool visitGoto(MGoto *ins);
     bool visitTableSwitch(MTableSwitch *tableswitch);
     bool visitNewSlots(MNewSlots *ins);
     bool visitNewArray(MNewArray *ins);
     bool visitNewObject(MNewObject *ins);
     bool visitNewDeclEnvObject(MNewDeclEnvObject *ins);
     bool visitNewCallObject(MNewCallObject *ins);
+    bool visitNewRunOnceCallObject(MNewRunOnceCallObject *ins);
     bool visitNewStringObject(MNewStringObject *ins);
     bool visitNewDerivedTypedObject(MNewDerivedTypedObject *ins);
     bool visitNewPar(MNewPar *ins);
     bool visitNewCallObjectPar(MNewCallObjectPar *ins);
     bool visitNewDenseArrayPar(MNewDenseArrayPar *ins);
     bool visitAbortPar(MAbortPar *ins);
     bool visitInitElem(MInitElem *ins);
     bool visitInitElemGetterSetter(MInitElemGetterSetter *ins);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -9013,67 +9013,87 @@ class MNewDeclEnvObject : public MNullar
     JSObject *templateObj() {
         return templateObj_;
     }
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
-class MNewCallObject : public MUnaryInstruction
+class MNewCallObjectBase : public MUnaryInstruction
 {
     CompilerRootObject templateObj_;
-    bool needsSingletonType_;
-
-    MNewCallObject(JSObject *templateObj, bool needsSingletonType, MDefinition *slots)
+
+  protected:
+    MNewCallObjectBase(JSObject *templateObj, MDefinition *slots)
       : MUnaryInstruction(slots),
-        templateObj_(templateObj),
-        needsSingletonType_(needsSingletonType)
+        templateObj_(templateObj)
     {
         setResultType(MIRType_Object);
     }
 
   public:
-    INSTRUCTION_HEADER(NewCallObject)
-
-    static MNewCallObject *New(TempAllocator &alloc, JSObject *templateObj, bool needsSingletonType,
-                               MDefinition *slots)
-    {
-        return new(alloc) MNewCallObject(templateObj, needsSingletonType, slots);
-    }
-
     MDefinition *slots() {
         return getOperand(0);
     }
     JSObject *templateObject() {
         return templateObj_;
     }
-    bool needsSingletonType() const {
-        return needsSingletonType_;
-    }
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
+class MNewCallObject : public MNewCallObjectBase
+{
+  public:
+    INSTRUCTION_HEADER(NewCallObject)
+
+    MNewCallObject(JSObject *templateObj, MDefinition *slots)
+      : MNewCallObjectBase(templateObj, slots)
+    {}
+
+    static MNewCallObject *
+    New(TempAllocator &alloc, JSObject *templateObj, MDefinition *slots)
+    {
+        return new(alloc) MNewCallObject(templateObj, slots);
+    }
+};
+
+class MNewRunOnceCallObject : public MNewCallObjectBase
+{
+  public:
+    INSTRUCTION_HEADER(NewRunOnceCallObject)
+
+    MNewRunOnceCallObject(JSObject *templateObj, MDefinition *slots)
+      : MNewCallObjectBase(templateObj, slots)
+    {}
+
+    static MNewRunOnceCallObject *
+    New(TempAllocator &alloc, JSObject *templateObj, MDefinition *slots)
+    {
+        return new(alloc) MNewRunOnceCallObject(templateObj, slots);
+    }
+};
+
 class MNewCallObjectPar : public MBinaryInstruction
 {
     CompilerRootObject templateObj_;
 
     MNewCallObjectPar(MDefinition *cx, JSObject *templateObj, MDefinition *slots)
         : MBinaryInstruction(cx, slots),
           templateObj_(templateObj)
     {
         setResultType(MIRType_Object);
     }
 
   public:
     INSTRUCTION_HEADER(NewCallObjectPar);
 
-    static MNewCallObjectPar *New(TempAllocator &alloc, MDefinition *cx, MNewCallObject *callObj) {
+    static MNewCallObjectPar *New(TempAllocator &alloc, MDefinition *cx, MNewCallObjectBase *callObj) {
         return new(alloc) MNewCallObjectPar(cx, callObj->templateObject(), callObj->slots());
     }
 
     MDefinition *forkJoinContext() const {
         return getOperand(0);
     }
 
     MDefinition *slots() const {
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -85,16 +85,17 @@ namespace jit {
     _(ToInt32)                                                              \
     _(TruncateToInt32)                                                      \
     _(ToString)                                                             \
     _(NewSlots)                                                             \
     _(NewArray)                                                             \
     _(NewObject)                                                            \
     _(NewDeclEnvObject)                                                     \
     _(NewCallObject)                                                        \
+    _(NewRunOnceCallObject)                                                 \
     _(NewStringObject)                                                      \
     _(InitElem)                                                             \
     _(InitElemGetterSetter)                                                 \
     _(MutateProto)                                                          \
     _(InitProp)                                                             \
     _(InitPropGetterSetter)                                                 \
     _(Start)                                                                \
     _(OsrEntry)                                                             \
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -178,16 +178,17 @@ class ParallelSafetyVisitor : public MIn
     SAFE_OP(ToInt32)
     SAFE_OP(TruncateToInt32)
     SAFE_OP(MaybeToDoubleElement)
     CUSTOM_OP(ToString)
     SAFE_OP(NewSlots)
     CUSTOM_OP(NewArray)
     CUSTOM_OP(NewObject)
     CUSTOM_OP(NewCallObject)
+    CUSTOM_OP(NewRunOnceCallObject)
     CUSTOM_OP(NewDerivedTypedObject)
     UNSAFE_OP(InitElem)
     UNSAFE_OP(InitElemGetterSetter)
     UNSAFE_OP(MutateProto)
     UNSAFE_OP(InitProp)
     UNSAFE_OP(InitPropGetterSetter)
     SAFE_OP(Start)
     UNSAFE_OP(OsrEntry)
@@ -527,16 +528,23 @@ ParallelSafetyVisitor::visitCreateThisWi
 bool
 ParallelSafetyVisitor::visitNewCallObject(MNewCallObject *ins)
 {
     replace(ins, MNewCallObjectPar::New(alloc(), ForkJoinContext(), ins));
     return true;
 }
 
 bool
+ParallelSafetyVisitor::visitNewRunOnceCallObject(MNewRunOnceCallObject *ins)
+{
+    replace(ins, MNewCallObjectPar::New(alloc(), ForkJoinContext(), ins));
+    return true;
+}
+
+bool
 ParallelSafetyVisitor::visitLambda(MLambda *ins)
 {
     if (ins->info().singletonType || ins->info().useNewTypeForClone) {
         // slow path: bail on parallel execution.
         return markUnsafe();
     }
 
     // fast path: replace with LambdaPar op
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -520,35 +520,53 @@ NewSlots(JSRuntime *rt, unsigned nslots)
 
     for (unsigned i = 0; i < nslots; i++)
         slots[i] = UndefinedValue();
 
     return reinterpret_cast<HeapSlot *>(slots);
 }
 
 JSObject *
-NewCallObject(JSContext *cx, HandleScript script,
-              HandleShape shape, HandleTypeObject type, HeapSlot *slots)
+NewCallObject(JSContext *cx, HandleShape shape, HandleTypeObject type, HeapSlot *slots)
 {
-    JSObject *obj = CallObject::create(cx, script, shape, type, slots);
+    JSObject *obj = CallObject::create(cx, shape, type, slots);
     if (!obj)
         return nullptr;
 
 #ifdef JSGC_GENERATIONAL
     // The JIT creates call objects in the nursery, so elides barriers for
     // the initializing writes. The interpreter, however, may have allocated
     // the call object tenured, so barrier as needed before re-entering.
     if (!IsInsideNursery(cx->runtime(), obj))
         cx->runtime()->gcStoreBuffer.putWholeCell(obj);
 #endif
 
     return obj;
 }
 
 JSObject *
+NewSingletonCallObject(JSContext *cx, HandleShape shape, HeapSlot *slots)
+{
+    JSObject *obj = CallObject::createSingleton(cx, shape, slots);
+    if (!obj)
+        return nullptr;
+
+#ifdef JSGC_GENERATIONAL
+    // The JIT creates call objects in the nursery, so elides barriers for
+    // the initializing writes. The interpreter, however, may have allocated
+    // the call object tenured, so barrier as needed before re-entering.
+    MOZ_ASSERT(!IsInsideNursery(cx->runtime(), obj),
+               "singletons are created in the tenured heap");
+    cx->runtime()->gcStoreBuffer.putWholeCell(obj);
+#endif
+
+    return obj;
+}
+
+JSObject *
 NewStringObject(JSContext *cx, HandleString str)
 {
     return StringObject::create(cx, str);
 }
 
 bool
 SPSEnter(JSContext *cx, HandleScript script)
 {
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -613,18 +613,18 @@ bool CharCodeAt(JSContext *cx, HandleStr
 JSFlatString *StringFromCharCode(JSContext *cx, int32_t code);
 
 bool SetProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, HandleValue value,
                  bool strict, jsbytecode *pc);
 
 bool InterruptCheck(JSContext *cx);
 
 HeapSlot *NewSlots(JSRuntime *rt, unsigned nslots);
-JSObject *NewCallObject(JSContext *cx, HandleScript script,
-                        HandleShape shape, HandleTypeObject type, HeapSlot *slots);
+JSObject *NewCallObject(JSContext *cx, HandleShape shape, HandleTypeObject type, HeapSlot *slots);
+JSObject *NewSingletonCallObject(JSContext *cx, HandleShape shape, HeapSlot *slots);
 JSObject *NewStringObject(JSContext *cx, HandleString str);
 
 bool SPSEnter(JSContext *cx, HandleScript script);
 bool SPSExit(JSContext *cx, HandleScript script);
 
 bool OperatorIn(JSContext *cx, HandleValue key, HandleObject obj, bool *out);
 bool OperatorInI(JSContext *cx, uint32_t index, HandleObject obj, bool *out);
 
--- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp
@@ -2005,19 +2005,19 @@ JitRuntime::generateForkJoinGetSliceStub
 
     // If we successfully got a slice, decrement pool->pendingSlices_ and
     // return the slice.
     masm.bind(&gotSlice);
     masm.atomic_dec32(Operand(Address(pool, ThreadPool::offsetOfPendingSlices())));
     masm.pop(cxReg);
     masm.ret();
 
-    // There's no more slices to give out, return -1.
+    // There's no more slices to give out, return a sentinel value.
     masm.bind(&noMoreWork);
-    masm.move32(Imm32(-1), output);
+    masm.move32(Imm32(ThreadPool::MAX_SLICE_ID), output);
     masm.pop(cxReg);
     masm.ret();
 
     Linker linker(masm);
     JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "ForkJoinGetSliceStub");
--- a/js/src/jsapi-tests/testBug604087.cpp
+++ b/js/src/jsapi-tests/testBug604087.cpp
@@ -34,39 +34,31 @@ wrap(JSContext *cx, JS::HandleObject toW
     JSAutoCompartment ac(cx, target);
     JS::RootedObject wrapper(cx, toWrap);
     if (!JS_WrapObject(cx, &wrapper))
         return nullptr;
     return wrapper;
 }
 
 static JSObject *
-SameCompartmentWrap(JSContext *cx, JS::HandleObject obj)
-{
-    JS_GC(JS_GetRuntime(cx));
-    return obj;
-}
-
-static JSObject *
 PreWrap(JSContext *cx, JS::HandleObject scope, JS::HandleObject obj, unsigned flags)
 {
     JS_GC(JS_GetRuntime(cx));
     return obj;
 }
 
 static JSObject *
 Wrap(JSContext *cx, JS::HandleObject existing, JS::HandleObject obj,
      JS::HandleObject proto, JS::HandleObject parent, unsigned flags)
 {
     return js::Wrapper::New(cx, obj, parent, &js::CrossCompartmentWrapper::singleton);
 }
 
 static const JSWrapObjectCallbacks WrapObjectCallbacks = {
     Wrap,
-    SameCompartmentWrap,
     PreWrap
 };
 
 BEGIN_TEST(testBug604087)
 {
     js::WrapperOptions options;
     options.setClass(&OuterWrapperClass);
     options.setSingleton(true);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -797,33 +797,19 @@ typedef JSObject *
  * Callback used by the wrap hook to ask the embedding to prepare an object
  * for wrapping in a context. This might include unwrapping other wrappers
  * or even finding a more suitable object for the new compartment.
  */
 typedef JSObject *
 (* JSPreWrapCallback)(JSContext *cx, JS::HandleObject scope, JS::HandleObject obj,
                       unsigned flags);
 
-/*
- * Callback used when wrapping determines that the underlying object is already
- * in the compartment for which it is being wrapped. This allows consumers to
- * maintain same-compartment wrapping invariants.
- *
- * |obj| is guaranteed to be same-compartment as |cx|, but it may (or may not)
- * be a security or cross-compartment wrapper. This is an unfortunate contract,
- * but is important for to avoid unnecessarily recomputing every cross-
- * compartment wrapper that gets passed to wrap.
- */
-typedef JSObject *
-(* JSSameCompartmentWrapObjectCallback)(JSContext *cx, JS::HandleObject obj);
-
 struct JSWrapObjectCallbacks
 {
     JSWrapObjectCallback wrap;
-    JSSameCompartmentWrapObjectCallback sameCompartmentWrap;
     JSPreWrapCallback preWrap;
 };
 
 typedef void
 (* JSDestroyCompartmentCallback)(JSFreeOp *fop, JSCompartment *compartment);
 
 typedef void
 (* JSZoneCallback)(JS::Zone *zone);
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -177,30 +177,16 @@ JSCompartment::ensureJitCompartmentExist
         jitCompartment_ = nullptr;
         return false;
     }
 
     return true;
 }
 #endif
 
-static bool
-WrapForSameCompartment(JSContext *cx, MutableHandleObject obj, const JSWrapObjectCallbacks *cb)
-{
-    JS_ASSERT(cx->compartment() == obj->compartment());
-    if (!cb->sameCompartmentWrap)
-        return true;
-
-    RootedObject wrapped(cx, cb->sameCompartmentWrap(cx, obj));
-    if (!wrapped)
-        return false;
-    obj.set(wrapped);
-    return true;
-}
-
 #ifdef JSGC_GENERATIONAL
 
 /*
  * This class is used to add a post barrier on the crossCompartmentWrappers map,
  * as the key is calculated based on objects which may be moved by generational
  * GC.
  */
 class WrapperMapRef : public BufferableRef
@@ -358,32 +344,36 @@ JSCompartment::wrap(JSContext *cx, Mutab
     // This loses us some transparency, and is generally very cheesy.
     HandleObject global = cx->global();
     RootedObject objGlobal(cx, &obj->global());
     JS_ASSERT(global);
     JS_ASSERT(objGlobal);
 
     const JSWrapObjectCallbacks *cb = cx->runtime()->wrapObjectCallbacks;
 
-    if (obj->compartment() == this)
-        return WrapForSameCompartment(cx, obj, cb);
+    if (obj->compartment() == this) {
+        obj.set(GetOuterObject(cx, obj));
+        return true;
+    }
 
     // If we have a cross-compartment wrapper, make sure that the cx isn't
     // associated with the self-hosting global. We don't want to create
     // wrappers for objects in other runtimes, which may be the case for the
     // self-hosting global.
     JS_ASSERT(!cx->runtime()->isSelfHostingGlobal(global) &&
               !cx->runtime()->isSelfHostingGlobal(objGlobal));
 
     // Unwrap the object, but don't unwrap outer windows.
     unsigned flags = 0;
     obj.set(UncheckedUnwrap(obj, /* stopAtOuter = */ true, &flags));
 
-    if (obj->compartment() == this)
-        return WrapForSameCompartment(cx, obj, cb);
+    if (obj->compartment() == this) {
+        MOZ_ASSERT(obj == GetOuterObject(cx, obj));
+        return true;
+    }
 
     // Translate StopIteration singleton.
     if (obj->is<StopIterationObject>()) {
         // StopIteration isn't a constructor, but it's stored in GlobalObject
         // as one, out of laziness. Hence the GetBuiltinConstructor call here.
         RootedObject stopIteration(cx);
         if (!GetBuiltinConstructor(cx, JSProto_StopIteration, &stopIteration))
             return false;
@@ -394,26 +384,21 @@ JSCompartment::wrap(JSContext *cx, Mutab
     // Invoke the prewrap callback. We're a bit worried about infinite
     // recursion here, so we do a check - see bug 809295.
     JS_CHECK_CHROME_RECURSION(cx, return false);
     if (cb->preWrap) {
         obj.set(cb->preWrap(cx, global, obj, flags));
         if (!obj)
             return false;
     }
+    MOZ_ASSERT(obj == GetOuterObject(cx, obj));
 
     if (obj->compartment() == this)
-        return WrapForSameCompartment(cx, obj, cb);
+        return true;
 
-#ifdef DEBUG
-    {
-        JSObject *outer = GetOuterObject(cx, obj);
-        JS_ASSERT(outer && outer == obj);
-    }
-#endif
 
     // If we already have a wrapper for this value, use it.
     RootedValue key(cx, ObjectValue(*obj));
     if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(key)) {
         obj.set(&p->value().get().toObject());
         JS_ASSERT(obj->is<CrossCompartmentWrapperObject>());
         JS_ASSERT(obj->getParent() == global);
         return true;
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -520,16 +520,17 @@ inline js::Handle<js::GlobalObject*>
 ExclusiveContext::global() const
 {
     /*
      * It's safe to use |unsafeGet()| here because any compartment that is
      * on-stack will be marked automatically, so there's no need for a read
      * barrier on it. Once the compartment is popped, the handle is no longer
      * safe to use.
      */
+    MOZ_ASSERT(compartment_, "Caller needs to enter a compartment first");
     return Handle<GlobalObject*>::fromMarkedLocation(compartment_->global_.unsafeGet());
 }
 
 class AssertCompartmentUnchanged
 {
   public:
     AssertCompartmentUnchanged(JSContext *cx
                                 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -407,16 +407,18 @@ js::AssertSameCompartment(JSObject *objA
 {
     JS_ASSERT(objA->compartment() == objB->compartment());
 }
 #endif
 
 JS_FRIEND_API(JSObject *)
 js::DefaultObjectForContextOrNull(JSContext *cx)
 {
+    if (cx->options().noDefaultCompartmentObject())
+        return nullptr;
     return cx->maybeDefaultCompartmentObject();
 }
 
 JS_FRIEND_API(void)
 js::SetDefaultObjectForContext(JSContext *cx, JSObject *obj)
 {
     cx->setDefaultCompartmentObject(obj);
 }
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -419,17 +419,17 @@ CheckAllocatorState(ThreadSafeContext *c
                  kind == FINALIZE_STRING ||
                  kind == FINALIZE_SHORT_STRING ||
                  kind == FINALIZE_JITCODE);
     JS_ASSERT(!rt->isHeapBusy());
     JS_ASSERT(!rt->noGCOrAllocationCheck);
 #endif
 
     // For testing out of memory conditions
-    JS_OOM_POSSIBLY_FAIL_REPORT(ncx);
+    JS_OOM_POSSIBLY_FAIL();
 
     if (allowGC) {
 #ifdef JS_GC_ZEAL
         if (rt->needZealousGC())
             js::gc::RunDebugGC(ncx);
 #endif
 
         if (rt->interrupt) {
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Map/forEach-selfhosted-behavior.js
@@ -0,0 +1,51 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 987243;
+var summary = "Don't use .call(...) in the self-hosted Map.prototype.forEach";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+var functionCall = Function.prototype.call;
+
+function throwSyntaxError()
+{
+  throw new SyntaxError("Function.prototype.call incorrectly called");
+}
+
+function lalala() {}
+
+Function.prototype.call = throwSyntaxError;
+
+new Map().forEach(throwSyntaxError);
+new Map([[1, 2]]).forEach(lalala);
+new Map([[1, 2], [3, 4]]).forEach(lalala);
+
+Function.prototype.call = function() { this.set(42, "fnord"); };
+
+new Map().forEach(throwSyntaxError);
+new Map([[1, 2]]).forEach(lalala);
+new Map([[1, 2], [3, 4]]).forEach(lalala);
+
+var callCount = 0;
+Function.prototype.call = function() { callCount++; };
+
+new Map().forEach(throwSyntaxError);
+new Map([[1, 2]]).forEach(lalala);
+new Map([[1, 2], [3, 4]]).forEach(lalala);
+
+assertEq(callCount, 0);
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Set/forEach-selfhosted-behavior.js
@@ -0,0 +1,51 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 987243;
+var summary = "Don't use .call(...) in the self-hosted Set.prototype.forEach";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+var functionCall = Function.prototype.call;
+
+function throwSyntaxError()
+{
+  throw new SyntaxError("Function.prototype.call incorrectly called");
+}
+
+function lalala() {}
+
+Function.prototype.call = throwSyntaxError;
+
+new Set().forEach(throwSyntaxError);
+new Set([1]).forEach(lalala);
+new Set([{}, 4]).forEach(lalala);
+
+Function.prototype.call = function() { this.add(3.141592654); };
+
+new Set().forEach(throwSyntaxError);
+new Set(["foo"]).forEach(lalala);
+new Set([undefined, NaN]).forEach(lalala);
+
+var callCount = 0;
+Function.prototype.call = function() { callCount++; };
+
+new Set().forEach(throwSyntaxError);
+new Set([new Number]).forEach(lalala);
+new Set([true, new Boolean(false)]).forEach(lalala);
+
+assertEq(callCount, 0);
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");
--- a/js/src/vm/ForkJoin.cpp
+++ b/js/src/vm/ForkJoin.cpp
@@ -46,24 +46,30 @@ using mozilla::ThreadLocal;
 ///////////////////////////////////////////////////////////////////////////
 // Degenerate configurations
 //
 // When JS_THREADSAFE or JS_ION is not defined, we simply run the
 // |func| callback sequentially.  We also forego the feedback
 // altogether.
 
 static bool
-ExecuteSequentially(JSContext *cx_, HandleValue funVal);
+ExecuteSequentially(JSContext *cx_, HandleValue funVal, uint16_t *sliceStart,
+                    uint16_t sliceEnd);
 
 #if !defined(JS_THREADSAFE) || !defined(JS_ION)
 bool
 js::ForkJoin(JSContext *cx, CallArgs &args)
 {
     RootedValue argZero(cx, args[0]);
-    return ExecuteSequentially(cx, argZero);
+    uint16_t sliceStart = uint16_t(args[1].toInt32());
+    uint16_t sliceEnd = uint16_t(args[2].toInt32());
+    if (!ExecuteSequentially(cx, argZero, &sliceStart, sliceEnd))
+        return false;
+    MOZ_ASSERT(sliceStart == sliceEnd);
+    return true;
 }
 
 JSContext *
 ForkJoinContext::acquireJSContext()
 {
     return nullptr;
 }
 
@@ -179,27 +185,32 @@ JS_JITINFO_NATIVE_PARALLEL(js::intrinsic
 #endif // !JS_THREADSAFE || !JS_ION
 
 ///////////////////////////////////////////////////////////////////////////
 // All configurations
 //
 // Some code that is shared between degenerate and parallel configurations.
 
 static bool
-ExecuteSequentially(JSContext *cx, HandleValue funVal)
+ExecuteSequentially(JSContext *cx, HandleValue funVal, uint16_t *sliceStart,
+                    uint16_t sliceEnd)
 {
     FastInvokeGuard fig(cx, funVal);
     InvokeArgs &args = fig.args();
-    if (!args.init(2))
+    if (!args.init(3))
         return false;
     args.setCallee(funVal);
     args.setThis(UndefinedValue());
-    args[0].setInt32(0); // always worker 0 in seq
-    args[1].setBoolean(!!cx->runtime()->forkJoinWarmup);
-    return fig.invoke(cx);
+    args[0].setInt32(0);
+    args[1].setInt32(*sliceStart);
+    args[2].setInt32(sliceEnd);
+    if (!fig.invoke(cx))
+        return false;
+    *sliceStart = (uint16_t)(args.rval().toInt32());
+    return true;
 }
 
 ThreadLocal<ForkJoinContext*> ForkJoinContext::tlsForkJoinContext;
 
 /* static */ bool
 ForkJoinContext::initialize()
 {
     if (!tlsForkJoinContext.initialized()) {
@@ -262,18 +273,18 @@ class ForkJoinOperation
     static const uint32_t MAX_BAILOUTS = 3;
     uint32_t bailouts;
 
     // Information about the bailout:
     ParallelBailoutCause bailoutCause;
     RootedScript bailoutScript;
     jsbytecode *bailoutBytecode;
 
-    ForkJoinOperation(JSContext *cx, HandleFunction fun, HandleFunction boundsFun,
-                      ForkJoinMode mode);
+    ForkJoinOperation(JSContext *cx, HandleFunction fun, uint16_t sliceStart,
+                      uint16_t sliceEnd, ForkJoinMode mode);
     ExecutionStatus apply();
 
   private:
     // Most of the functions involved in managing the parallel
     // compilation follow a similar control-flow. They return RedLight
     // if they have either encountered a fatal error or completed the
     // execution, such that no further work is needed. In that event,
     // they take an `ExecutionStatus*` which they use to report
@@ -302,17 +313,18 @@ class ForkJoinOperation
             calleesEnqueued = false;
             useCount = 0;
             stallCount = 0;
         }
     };
 
     JSContext *cx_;
     HandleFunction fun_;
-    HandleFunction boundsFun_;
+    uint16_t sliceStart_;
+    uint16_t sliceEnd_;
     Vector<ParallelBailoutRecord, 16> bailoutRecords_;
     AutoScriptVector worklist_;
     Vector<WorklistData, 16> worklistData_;
     ForkJoinMode mode_;
 
     TrafficLight enqueueInitialScript(ExecutionStatus *status);
     TrafficLight compileForParallelExecution(ExecutionStatus *status);
     TrafficLight warmupExecution(bool stopIfComplete, ExecutionStatus *status);
@@ -324,30 +336,28 @@ class ForkJoinOperation
     void determineBailoutCause();
     bool invalidateBailedOutScripts();
     ExecutionStatus sequentialExecution(bool disqualified);
 
     TrafficLight appendCallTargetsToWorklist(uint32_t index, ExecutionStatus *status);
     TrafficLight appendCallTargetToWorklist(HandleScript script, ExecutionStatus *status);
     bool addToWorklist(HandleScript script);
     inline bool hasScript(Vector<types::RecompileInfo> &scripts, JSScript *script);
-
-    bool computeBounds(uint16_t *start, uint16_t *end);
 }; // class ForkJoinOperation
 
 class ForkJoinShared : public ParallelJob, public Monitor
 {
     /////////////////////////////////////////////////////////////////////////
     // Constant fields
 
     JSContext *const cx_;                  // Current context
     ThreadPool *const threadPool_;         // The thread pool
     HandleFunction fun_;                   // The JavaScript function to execute
-    uint16_t sliceFrom_;                   // The starting slice id.
-    uint16_t sliceTo_;                     // The ending slice id + 1.
+    uint16_t sliceStart_;                  // The starting slice id.
+    uint16_t sliceEnd_;                    // The ending slice id + 1.
     PRLock *cxLock_;                       // Locks cx_ for parallel VM calls
     ParallelBailoutRecord *const records_; // Bailout records for each worker
 
     /////////////////////////////////////////////////////////////////////////
     // Per-thread arenas
     //
     // Each worker thread gets an arena to use when allocating.
 
@@ -372,18 +382,18 @@ class ForkJoinShared : public ParallelJo
 
     // Set to true when a worker bails for a fatal reason.
     mozilla::Atomic<bool, mozilla::ReleaseAcquire> fatal_;
 
   public:
     ForkJoinShared(JSContext *cx,
                    ThreadPool *threadPool,
                    HandleFunction fun,
-                   uint16_t sliceFrom,
-                   uint16_t sliceTo,
+                   uint16_t sliceStart,
+                   uint16_t sliceEnd,
                    ParallelBailoutRecord *records);
     ~ForkJoinShared();
 
     bool init();
 
     ParallelResult execute();
 
     // Invoked from parallel worker threads:
@@ -487,29 +497,34 @@ ForkJoinActivation::~ForkJoinActivation(
 // They handle parallel compilation (if necessary), triggering
 // parallel execution, and recovering from bailouts.
 
 static const char *ForkJoinModeString(ForkJoinMode mode);
 
 bool
 js::ForkJoin(JSContext *cx, CallArgs &args)
 {
-    JS_ASSERT(args.length() == 3); // else the self-hosted code is wrong
+    JS_ASSERT(args.length() == 4); // else the self-hosted code is wrong
     JS_ASSERT(args[0].isObject());
     JS_ASSERT(args[0].toObject().is<JSFunction>());
-    JS_ASSERT(args[1].isObject());
-    JS_ASSERT(args[1].toObject().is<JSFunction>());
+    JS_ASSERT(args[1].isInt32());
     JS_ASSERT(args[2].isInt32());
-    JS_ASSERT(args[2].toInt32() < NumForkJoinModes);
+    JS_ASSERT(args[3].isInt32());
+    JS_ASSERT(args[3].toInt32() < NumForkJoinModes);
 
     RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
-    RootedFunction boundsFun(cx, &args[1].toObject().as<JSFunction>());
-    ForkJoinMode mode = (ForkJoinMode) args[2].toInt32();
+    uint16_t sliceStart = (uint16_t)(args[1].toInt32());
+    uint16_t sliceEnd = (uint16_t)(args[2].toInt32());
+    ForkJoinMode mode = (ForkJoinMode)(args[3].toInt32());
 
-    ForkJoinOperation op(cx, fun, boundsFun, mode);
+    MOZ_ASSERT(sliceStart == args[1].toInt32());
+    MOZ_ASSERT(sliceEnd == args[2].toInt32());
+    MOZ_ASSERT(sliceStart < sliceEnd);
+
+    ForkJoinOperation op(cx, fun, sliceStart, sliceEnd, mode);
     ExecutionStatus status = op.apply();
     if (status == ExecutionFatal)
         return false;
 
     switch (mode) {
       case ForkJoinModeNormal:
       case ForkJoinModeCompile:
         return true;
@@ -557,25 +572,26 @@ ForkJoinModeString(ForkJoinMode mode) {
       case ForkJoinModeParallel: return "parallel";
       case ForkJoinModeRecover: return "recover";
       case ForkJoinModeBailout: return "bailout";
       case NumForkJoinModes: return "max";
     }
     return "???";
 }
 
-ForkJoinOperation::ForkJoinOperation(JSContext *cx, HandleFunction fun, HandleFunction boundsFun,
-                                     ForkJoinMode mode)
+ForkJoinOperation::ForkJoinOperation(JSContext *cx, HandleFunction fun, uint16_t sliceStart,
+                                     uint16_t sliceEnd, ForkJoinMode mode)
   : bailouts(0),
     bailoutCause(ParallelBailoutNone),
     bailoutScript(cx),
     bailoutBytecode(nullptr),
     cx_(cx),
     fun_(fun),
-    boundsFun_(boundsFun),
+    sliceStart_(sliceStart),
+    sliceEnd_(sliceEnd),
     bailoutRecords_(cx),
     worklist_(cx),
     worklistData_(cx),
     mode_(mode)
 { }
 
 ExecutionStatus
 ForkJoinOperation::apply()
@@ -1000,19 +1016,23 @@ ForkJoinOperation::sequentialExecution(b
 ExecutionStatus
 ForkJoinOperation::sequentialExecution(bool disqualified)
 {
     // XXX use disqualified to set parallelIon to ION_DISABLED_SCRIPT?
 
     Spew(SpewOps, "Executing sequential execution (disqualified=%d).",
          disqualified);
 
+    if (sliceStart_ == sliceEnd_)
+        return ExecutionSequential;
+
     RootedValue funVal(cx_, ObjectValue(*fun_));
-    if (!ExecuteSequentially(cx_, funVal))
+    if (!ExecuteSequentially(cx_, funVal, &sliceStart_, sliceEnd_))
         return ExecutionFatal;
+    MOZ_ASSERT(sliceStart_ == sliceEnd_);
     return ExecutionSequential;
 }
 
 ForkJoinOperation::TrafficLight
 ForkJoinOperation::fatalError(ExecutionStatus *status)
 {
     // RedLight: fatal error
 
@@ -1161,23 +1181,17 @@ ForkJoinOperation::invalidateBailedOutSc
 }
 
 ForkJoinOperation::TrafficLight
 ForkJoinOperation::warmupExecution(bool stopIfComplete, ExecutionStatus *status)
 {
     // GreenLight: warmup succeeded, still more work to do
     // RedLight: fatal error or warmup completed all work (check status)
 
-    uint16_t from, to;
-    if (!computeBounds(&from, &to)) {
-        *status = ExecutionFatal;
-        return RedLight;
-    }
-
-    if (from == to) {
+    if (sliceStart_ == sliceEnd_) {
         Spew(SpewOps, "Warmup execution finished all the work.");
 
         if (stopIfComplete) {
             *status = ExecutionWarmup;
             return RedLight;
         }
 
         // If we finished all slices in warmup, be sure check the
@@ -1187,21 +1201,21 @@ ForkJoinOperation::warmupExecution(bool 
         if (!CheckForInterrupt(cx_)) {
             *status = ExecutionFatal;
             return RedLight;
         }
 
         return GreenLight;
     }
 
-    Spew(SpewOps, "Executing warmup.");
+    Spew(SpewOps, "Executing warmup from slice %d.", sliceStart_);
 
     AutoEnterWarmup warmup(cx_->runtime());
     RootedValue funVal(cx_, ObjectValue(*fun_));
-    if (!ExecuteSequentially(cx_, funVal)) {
+    if (!ExecuteSequentially(cx_, funVal, &sliceStart_, sliceStart_ + 1)) {
         *status = ExecutionFatal;
         return RedLight;
     }
 
     return GreenLight;
 }
 
 ForkJoinOperation::TrafficLight
@@ -1210,31 +1224,25 @@ ForkJoinOperation::parallelExecution(Exe
     // GreenLight: bailout occurred, keep trying
     // RedLight: fatal error or all work completed
 
     // Recursive use of the ThreadPool is not supported.  Right now we
     // cannot get here because parallel code cannot invoke native
     // functions such as ForkJoin().
     JS_ASSERT(ForkJoinContext::current() == nullptr);
 
-    uint16_t from, to;
-    if (!computeBounds(&from, &to)) {
-        *status = ExecutionFatal;
-        return RedLight;
-    }
-
-    if (from == to) {
+    if (sliceStart_ == sliceEnd_) {
         Spew(SpewOps, "Warmup execution finished all the work.");
         *status = ExecutionWarmup;
         return RedLight;
     }
 
     ForkJoinActivation activation(cx_);
     ThreadPool *threadPool = &cx_->runtime()->threadPool;
-    ForkJoinShared shared(cx_, threadPool, fun_, from, to, &bailoutRecords_[0]);
+    ForkJoinShared shared(cx_, threadPool, fun_, sliceStart_, sliceEnd_, &bailoutRecords_[0]);
     if (!shared.init()) {
         *status = ExecutionFatal;
         return RedLight;
     }
 
     switch (shared.execute()) {
       case TP_SUCCESS:
         *status = ExecutionParallel;
@@ -1285,46 +1293,16 @@ ForkJoinOperation::hasScript(Vector<type
 {
     for (uint32_t i = 0; i < scripts.length(); i++) {
         if (scripts[i] == script->parallelIonScript()->recompileInfo())
             return true;
     }
     return false;
 }
 
-bool
-ForkJoinOperation::computeBounds(uint16_t *start, uint16_t *end)
-{
-    RootedValue funVal(cx_, ObjectValue(*boundsFun_));
-    FastInvokeGuard fig(cx_, funVal);
-
-    InvokeArgs &args = fig.args();
-    if (!args.init(0))
-        return false;
-    args.setCallee(funVal);
-    args.setThis(UndefinedValue());
-
-    if (!fig.invoke(cx_))
-        return false;
-
-    MOZ_ASSERT(args.rval().toObject().is<ArrayObject>());
-    MOZ_ASSERT(args.rval().toObject().getDenseInitializedLength() == 2);
-
-    int32_t start32 = args.rval().toObject().getDenseElement(0).toInt32();
-    int32_t end32 = args.rval().toObject().getDenseElement(1).toInt32();
-
-    MOZ_ASSERT(int32_t(uint16_t(start32)) == start32);
-    MOZ_ASSERT(int32_t(uint16_t(end32)) == end32);
-
-    *start = uint16_t(start32);
-    *end = uint16_t(end32);
-
-    return true;
-}
-
 // Can only enter callees with a valid IonScript.
 template <uint32_t maxArgc>
 class ParallelIonInvoke
 {
     EnterJitCode enter_;
     void *jitcode_;
     void *calleeToken_;
     Value argv_[maxArgc + 2];
@@ -1363,24 +1341,24 @@ class ParallelIonInvoke
 
 /////////////////////////////////////////////////////////////////////////////
 // ForkJoinShared
 //
 
 ForkJoinShared::ForkJoinShared(JSContext *cx,
                                ThreadPool *threadPool,
                                HandleFunction fun,
-                               uint16_t sliceFrom,
-                               uint16_t sliceTo,
+                               uint16_t sliceStart,
+                               uint16_t sliceEnd,
                                ParallelBailoutRecord *records)
   : cx_(cx),
     threadPool_(threadPool),
     fun_(fun),
-    sliceFrom_(sliceFrom),
-    sliceTo_(sliceTo),
+    sliceStart_(sliceStart),
+    sliceEnd_(sliceEnd),
     cxLock_(nullptr),
     records_(records),
     allocators_(cx),
     gcRequested_(false),
     gcReason_(JS::gcreason::NUM_REASONS),
     gcZone_(nullptr),
     abort_(false),
     fatal_(false)
@@ -1440,33 +1418,33 @@ ForkJoinShared::execute()
 
     AutoLockMonitor lock(*this);
 
     ParallelResult jobResult = TP_SUCCESS;
     {
         AutoUnlockMonitor unlock(*this);
 
         // Push parallel tasks and wait until they're all done.
-        jobResult = threadPool_->executeJob(cx_, this, sliceFrom_, sliceTo_);
+        jobResult = threadPool_->executeJob(cx_, this, sliceStart_, sliceEnd_);
         if (jobResult == TP_FATAL)
             return TP_FATAL;
     }
 
     transferArenasToCompartmentAndProcessGCRequests();
 
     // Check if any of the workers failed.
     if (abort_) {
         if (fatal_)
             return TP_FATAL;
         return TP_RETRY_SEQUENTIALLY;
     }
 
 #ifdef DEBUG
     Spew(SpewOps, "Completed parallel job [slices: %d, threads: %d, stolen: %d (work stealing:%s)]",
-         sliceTo_ - sliceFrom_ + 1,
+         sliceEnd_ - sliceStart_,
          threadPool_->numWorkers(),
          threadPool_->stolenSlices(),
          threadPool_->workStealing() ? "ON" : "OFF");
 #endif
 
     // Everything went swimmingly. Give yourself a pat on the back.
     return jobResult;
 }
@@ -1552,20 +1530,21 @@ ForkJoinShared::executePortion(PerThread
         // Sometimes, particularly with GCZeal, the parallel ion
         // script can be collected between starting the parallel
         // op and reaching this point.  In that case, we just fail
         // and fallback.
         Spew(SpewOps, "Down (Script no longer present)");
         cx.bailoutRecord->setCause(ParallelBailoutMainScriptNotPresent);
         setAbortFlagAndRequestInterrupt(false);
     } else {
-        ParallelIonInvoke<2> fii(cx_->runtime(), fun_, 2);
+        ParallelIonInvoke<3> fii(cx_->runtime(), fun_, 3);
 
         fii.args[0] = Int32Value(worker->id());
-        fii.args[1] = BooleanValue(false);
+        fii.args[1] = Int32Value(sliceStart_);
+        fii.args[2] = Int32Value(sliceEnd_);
 
         bool ok = fii.invoke(perThread);
         JS_ASSERT(ok == !cx.bailoutRecord->topScript);
         if (!ok)
             setAbortFlagAndRequestInterrupt(false);
     }
 
     Spew(SpewOps, "Down");
--- a/js/src/vm/ForkJoin.h
+++ b/js/src/vm/ForkJoin.h
@@ -25,59 +25,60 @@
 // shared memory (as distinct from Web Workers).  The idea is that you have
 // some (typically data-parallel) operation which you wish to execute in
 // parallel across as many threads as you have available.
 //
 // The ForkJoin abstraction is intended to be used by self-hosted code
 // to enable parallel execution.  At the top-level, it consists of a native
 // function (exposed as the ForkJoin intrinsic) that is used like so:
 //
-//     ForkJoin(func, boundsFunc, mode)
+//     ForkJoin(func, sliceStart, sliceEnd, mode)
 //
 // The intention of this statement is to start some some number (usually the
 // number of hardware threads) of copies of |func()| running in parallel. Each
 // copy will then do a portion of the total work, depending on
 // workstealing-based load balancing.
 //
 // Typically, each of the N slices runs in a different worker thread, but that
 // is not something you should rely upon---if work-stealing is enabled it
 // could be that a single worker thread winds up handling multiple slices.
 //
-// The second argument, |boundsFunc|, is a function that must return an array
-// of exactly two integers. This function is called before every attempt at
-// execution: warmup, sequential, or parallel. The bounds are taken from a
-// function call instead of taken as two static integers so that the bounds
-// may be shrunk when recovering from bailout.
+// The second and third arguments, |sliceStart| and |sliceEnd|, are the slice
+// boundaries. These numbers must each fit inside an uint16_t.
 //
-// The third argument, |mode|, is an internal mode integer giving finer
+// The fourth argument, |mode|, is an internal mode integer giving finer
 // control over the behavior of ForkJoin. See the |ForkJoinMode| enum.
 //
 // func() should expect the following arguments:
 //
-//     func(warmup)
+//     func(workerId, sliceStart, sliceEnd)
+//
+// The |workerId| parameter is the id of the worker executing the function. It
+// is 0 in sequential mode.
 //
-// The parameter |warmup| is true for a *warmup or recovery phase*. Warmup
-// phases are discussed below in more detail, but the general idea is that if
-// |warmup| is true, |func| should only do a fixed amount of work. If |warmup|
-// is false, |func| should try to do all remaining work is assigned.
+// The |sliceStart| and |sliceEnd| parameters are the current bounds that that
+// the worker is handling. In parallel execution, these parameters are not
+// used. In sequential execution, they tell the worker what slices should be
+// processed. During the warm up phase, sliceEnd == sliceStart + 1.
 //
 // |func| can keep asking for more work from the scheduler by calling the
-// intrinsic |GetForkJoinSlice(id)|. When there are no more slices to hand
-// out, -1 is returned as a sentinel value. By exposing this function as an
-// intrinsic, we reduce the number of JS-C++ boundary crossings incurred by
-// workstealing, which may have many slices.
+// intrinsic |GetForkJoinSlice(sliceStart, sliceEnd, id)|. When there are no
+// more slices to hand out, ThreadPool::MAX_SLICE_ID is returned as a sentinel
+// value. By exposing this function as an intrinsic, we reduce the number of
+// JS-C++ boundary crossings incurred by workstealing, which may have many
+// slices.
 //
-// |func| MUST PROCESS ALL SLICES BEFORE RETURNING! Not doing so is an error
-// |and is protected by debug asserts in ThreadPool.
+// In sequential execution, |func| should return the maximum computed slice id
+// S for which all slices with id < S have already been processed. This is so
+// ThreadPool can track the leftmost completed slice id to maintain
+// determinism. Slices which have been completed in sequential execution
+// cannot be re-run in parallel execution.
 //
-// Note well that there is a separation of concern between *scheduling* slices
-// and *interpreting* slices. ForkJoin only schedules slices by handing out
-// slice ids; it does not interpret what slice ids mean. Instead, |func|
-// should track how much work it has accomplished thus far; consult |Array.js|
-// for some examples.
+// In parallel execution, |func| MUST PROCESS ALL SLICES BEFORE RETURNING!
+// Not doing so is an error and is protected by debug asserts in ThreadPool.
 //
 // Warmups and Sequential Fallbacks
 // --------------------------------
 //
 // ForkJoin can only execute code in parallel when it has been
 // ion-compiled in Parallel Execution Mode. ForkJoin handles this part
 // for you. However, because ion relies on having decent type
 // information available, it is necessary to run the code sequentially
@@ -187,17 +188,17 @@
 // upon entering a parallel section to ensure that any concurrent
 // marking or incremental GC has completed.
 //
 // In the future, it should be possible to lift the restriction that
 // we must block until inc. GC has completed and also to permit GC
 // during parallel exeution. But we're not there yet.
 //
 // Load balancing (work stealing):
-
+//
 // The ForkJoin job is dynamically divided into a fixed number of slices,
 // and is submitted for parallel execution in the pool. When the number
 // of slices is big enough (typically greater than the number of workers
 // in the pool) -and the workload is unbalanced- each worker thread
 // will perform load balancing through work stealing. The number
 // of slices is computed by the self-hosted function |ComputeNumSlices|
 // and can be used to know how many slices will be executed by the
 // runtime for an array of the given size.
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -100,17 +100,16 @@ PerThreadData::init()
     if (!dtoaState)
         return false;
 
     return true;
 }
 
 static const JSWrapObjectCallbacks DefaultWrapObjectCallbacks = {
     TransparentObjectWrapper,
-    nullptr,
     nullptr
 };
 
 JSRuntime::JSRuntime(JSRuntime *parentRuntime, JSUseHelperThreads useHelperThreads)
   : JS::shadow::Runtime(
 #ifdef JSGC_GENERATIONAL
         &gcStoreBuffer
 #endif
--- a/js/src/vm/SPSProfiler.h
+++ b/js/src/vm/SPSProfiler.h
@@ -286,16 +286,23 @@ class SPSInstrumentation
         int  left;        // number of leave() calls made without a matching reenter()
     };
 
     SPSProfiler *profiler_; // Instrumentation location management
 
     Vector<FrameState, 1, SystemAllocPolicy> frames;
     FrameState *frame;
 
+    static void clearFrame(FrameState *frame) {
+        frame->script = nullptr;
+        frame->pc = nullptr;
+        frame->skipNext = false;
+        frame->left = 0;
+    }
+
   public:
     /*
      * Creates instrumentation which writes information out the the specified
      * profiler's stack and constituent fields.
      */
     SPSInstrumentation(SPSProfiler *profiler)
       : profiler_(profiler), frame(nullptr)
     {
@@ -328,20 +335,17 @@ class SPSInstrumentation
         JS_ASSERT_IF(frame != nullptr, frame->left == 1);
         if (!frames.empty()) {
             JS_ASSERT(frame == &frames[frames.length() - 1]);
             frame->pc = callerPC;
         }
         if (!frames.growBy(1))
             return false;
         frame = &frames[frames.length() - 1];
-        frame->script = nullptr;
-        frame->pc = nullptr;
-        frame->skipNext = false;
-        frame->left = 0;
+        clearFrame(frame);
         return true;
     }
 
     /* Prepares the instrumenter state for generating OOL code, by
      * setting up the frame state to seem as if there are exactly
      * two pushed frames: a frame for the top-level script, and
      * a frame for the OOL code being generated.  Any
      * vm-calls from the OOL code will "leave" the OOL frame and
@@ -355,16 +359,17 @@ class SPSInstrumentation
             frames.shrinkBy(frames.length() - 2);
 
         } else { // frames.length() == 1
             if (!frames.growBy(1))
                 return false;
         }
         frames[0].pc = frames[0].script->code();
         frame = &frames[1];
+        clearFrame(frame);
         return true;
     }
     void finishOOL() {
         if (!enabled())
             return;
         JS_ASSERT(!frames.empty());
         frames.shrinkBy(frames.length() - 1);
     }
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -131,38 +131,49 @@ js::ScopeCoordinateFunctionScript(JSScri
 void
 ScopeObject::setEnclosingScope(HandleObject obj)
 {
     JS_ASSERT_IF(obj->is<CallObject>() || obj->is<DeclEnvObject>() || obj->is<BlockObject>(),
                  obj->isDelegate());
     setFixedSlot(SCOPE_CHAIN_SLOT, ObjectValue(*obj));
 }
 
-/*
- * Construct a bare-bones call object given a shape, type, and slots pointer.
- * The call object must be further initialized to be usable.
- */
 CallObject *
-CallObject::create(JSContext *cx, HandleScript script, HandleShape shape, HandleTypeObject type, HeapSlot *slots)
+CallObject::create(JSContext *cx, HandleShape shape, HandleTypeObject type, HeapSlot *slots)
 {
+    MOZ_ASSERT(!type->singleton(),
+               "passed a singleton type to create() (use createSingleton() "
+               "instead)");
     gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
-    JS_ASSERT(CanBeFinalizedInBackground(kind, &CallObject::class_));
+    MOZ_ASSERT(CanBeFinalizedInBackground(kind, &CallObject::class_));
     kind = gc::GetBackgroundAllocKind(kind);
 
-    gc::InitialHeap heap = script->treatAsRunOnce() ? gc::TenuredHeap : gc::DefaultHeap;
-    JSObject *obj = JSObject::create(cx, kind, heap, shape, type, slots);
+    JSObject *obj = JSObject::create(cx, kind, gc::DefaultHeap, shape, type, slots);
     if (!obj)
         return nullptr;
 
-    if (script->treatAsRunOnce()) {
-        RootedObject nobj(cx, obj);
-        if (!JSObject::setSingletonType(cx, nobj))
-            return nullptr;
-        return &nobj->as<CallObject>();
-    }
+    return &obj->as<CallObject>();
+}
+
+CallObject *
+CallObject::createSingleton(JSContext *cx, HandleShape shape, HeapSlot *slots)
+{
+    gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
+    MOZ_ASSERT(CanBeFinalizedInBackground(kind, &CallObject::class_));
+    kind = gc::GetBackgroundAllocKind(kind);
+
+    RootedTypeObject type(cx, cx->getSingletonType(&class_, nullptr));
+    if (!type)
+        return nullptr;
+    RootedObject obj(cx, JSObject::create(cx, kind, gc::TenuredHeap, shape, type, slots));
+    if (!obj)
+        return nullptr;
+
+    MOZ_ASSERT(obj->hasSingletonType(),
+               "type created inline above must be a singleton");
 
     return &obj->as<CallObject>();
 }
 
 /*
  * Create a CallObject for a JSScript that is not initialized to any particular
  * callsite. This object can either be initialized (with an enclosing scope and
  * callee) or used as a template for jit compilation.
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -231,18 +231,32 @@ class CallObject : public ScopeObject
 
     static CallObject *
     create(JSContext *cx, HandleScript script, HandleObject enclosing, HandleFunction callee);
 
   public:
     static const Class class_;
 
     /* These functions are internal and are exposed only for JITs. */
+
+    /*
+     * Construct a bare-bones call object given a shape, a non-singleton type,
+     * and slots pointer.  The call object must be further initialized to be
+     * usable.
+     */
     static CallObject *
-    create(JSContext *cx, HandleScript script, HandleShape shape, HandleTypeObject type, HeapSlot *slots);
+    create(JSContext *cx, HandleShape shape, HandleTypeObject type, HeapSlot *slots);
+
+    /*
+     * Construct a bare-bones call object given a shape and slots pointer, and
+     * make it have singleton type.  The call object must be initialized to be
+     * usable.
+     */
+    static CallObject *
+    createSingleton(JSContext *cx, HandleShape shape, HeapSlot *slots);
 
     static CallObject *
     createTemplateObject(JSContext *cx, HandleScript script, gc::InitialHeap heap);
 
     static const uint32_t RESERVED_SLOTS = 2;
 
     static CallObject *createForFunction(JSContext *cx, HandleObject enclosing, HandleFunction callee);
 
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -330,17 +330,17 @@ intrinsic_ForkJoinGetSlicePar(ForkJoinCo
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
     MOZ_ASSERT(args[0].isInt32());
 
     uint16_t sliceId;
     if (cx->getSlice(&sliceId))
         args.rval().setInt32(sliceId);
     else
-        args.rval().setInt32(-1);
+        args.rval().setInt32(ThreadPool::MAX_SLICE_ID);
 
     return true;
 }
 
 JS_JITINFO_NATIVE_PARALLEL(intrinsic_ForkJoinGetSlice_jitInfo,
                            intrinsic_ForkJoinGetSlicePar);
 
 /*
--- a/js/src/vm/ThreadPool.cpp
+++ b/js/src/vm/ThreadPool.cpp
@@ -94,18 +94,18 @@ ThreadPoolWorker::discardSlices()
     } while (!sliceBounds_.compareExchange(bounds, 0));
 
     pool_->pendingSlices_ -= to - from;
 }
 
 bool
 ThreadPoolWorker::stealFrom(ThreadPoolWorker *victim, uint16_t *sliceId)
 {
-    // Instead of popping the slice from the front by incrementing sliceFrom_,
-    // decrement sliceTo_. Usually this gives us better locality.
+    // Instead of popping the slice from the front by incrementing sliceStart_,
+    // decrement sliceEnd_. Usually this gives us better locality.
     if (!victim->popSliceBack(sliceId))
         return false;
 #ifdef DEBUG
     pool_->stolenSlices_++;
 #endif
     return true;
 }
 
@@ -193,20 +193,20 @@ ThreadPoolWorker::helperLoop()
         {
             AutoLockMonitor lock(*pool_);
             pool_->join(lock);
         }
     }
 }
 
 void
-ThreadPoolWorker::submitSlices(uint16_t sliceFrom, uint16_t sliceTo)
+ThreadPoolWorker::submitSlices(uint16_t sliceStart, uint16_t sliceEnd)
 {
     MOZ_ASSERT(!hasWork());
-    sliceBounds_ = ComposeSliceBounds(sliceFrom, sliceTo);
+    sliceBounds_ = ComposeSliceBounds(sliceStart, sliceEnd);
 }
 
 bool
 ThreadPoolWorker::getSlice(ForkJoinContext *cx, uint16_t *sliceId)
 {
     // First see whether we have any work ourself.
     if (popSliceFront(sliceId))
         return true;
@@ -387,40 +387,40 @@ ThreadPool::waitForWorkers(AutoLockMonit
 {
     MOZ_ASSERT(lock.isFor(*this));
     while (activeWorkers_ > 0)
         lock.wait(joinBarrier_);
     job_ = nullptr;
 }
 
 ParallelResult
-ThreadPool::executeJob(JSContext *cx, ParallelJob *job, uint16_t sliceFrom, uint16_t sliceMax)
+ThreadPool::executeJob(JSContext *cx, ParallelJob *job, uint16_t sliceStart, uint16_t sliceMax)
 {
-    MOZ_ASSERT(sliceFrom < sliceMax);
+    MOZ_ASSERT(sliceStart < sliceMax);
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
     MOZ_ASSERT(activeWorkers_ == 0);
     MOZ_ASSERT(!hasWork());
 
     if (!lazyStartWorkers(cx))
         return TP_FATAL;
 
     // Evenly distribute slices to the workers.
-    uint16_t numSlices = sliceMax - sliceFrom;
+    uint16_t numSlices = sliceMax - sliceStart;
     uint16_t slicesPerWorker = numSlices / numWorkers();
     uint16_t leftover = numSlices % numWorkers();
-    uint16_t sliceTo = sliceFrom;
+    uint16_t sliceEnd = sliceStart;
     for (uint32_t workerId = 0; workerId < numWorkers(); workerId++) {
         if (leftover > 0) {
-            sliceTo += slicesPerWorker + 1;
+            sliceEnd += slicesPerWorker + 1;
             leftover--;
         } else {
-            sliceTo += slicesPerWorker;
+            sliceEnd += slicesPerWorker;
         }
-        workers_[workerId]->submitSlices(sliceFrom, sliceTo);
-        sliceFrom = sliceTo;
+        workers_[workerId]->submitSlices(sliceStart, sliceEnd);
+        sliceStart = sliceEnd;
     }
     MOZ_ASSERT(leftover == 0);
 
     // Notify the worker threads that there's work now.
     {
         job_ = job;
         pendingSlices_ = numSlices;
 #ifdef DEBUG
--- a/js/src/vm/ThreadPool.h
+++ b/js/src/vm/ThreadPool.h
@@ -76,17 +76,17 @@ class ThreadPoolWorker
 
   public:
     ThreadPoolWorker(uint32_t workerId, uint32_t rngSeed, ThreadPool *pool);
 
     uint32_t id() const { return workerId_; }
     bool isMainThread() const { return id() == 0; }
 
     // Submits a new set of slices. Assumes !hasWork().
-    void submitSlices(uint16_t sliceFrom, uint16_t sliceTo);
+    void submitSlices(uint16_t sliceStart, uint16_t sliceEnd);
 
     // Get the next slice; work stealing happens here if work stealing is
     // on. Returns false if there are no more slices to hand out.
     bool getSlice(ForkJoinContext *cx, uint16_t *sliceId);
 
     // Discard remaining slices. Used for aborting jobs.
     void discardSlices();
 
@@ -203,16 +203,18 @@ class ThreadPool : public Monitor
 #endif
     static size_t offsetOfPendingSlices() {
         return offsetof(ThreadPool, pendingSlices_);
     }
     static size_t offsetOfWorkers() {
         return offsetof(ThreadPool, workers_);
     }
 
+    static const uint16_t MAX_SLICE_ID = UINT16_MAX;
+
     ThreadPool(JSRuntime *rt);
     ~ThreadPool();
 
     bool init();
 
     // Return number of worker threads in the pool, counting the main thread.
     uint32_t numWorkers() const;
 
--- a/js/xpconnect/src/XPCJSContextStack.cpp
+++ b/js/xpconnect/src/XPCJSContextStack.cpp
@@ -18,16 +18,21 @@ using namespace JS;
 using namespace xpc;
 using mozilla::dom::DestroyProtoAndIfaceCache;
 
 /***************************************************************************/
 
 XPCJSContextStack::~XPCJSContextStack()
 {
     if (mSafeJSContext) {
+        {
+            JSAutoRequest ar(mSafeJSContext);
+            JS_RemoveObjectRoot(mSafeJSContext, &mSafeJSContextGlobal);
+        }
+        mSafeJSContextGlobal = nullptr;
         JS_DestroyContextNoGC(mSafeJSContext);
         mSafeJSContext = nullptr;
     }
 }
 
 JSContext*
 XPCJSContextStack::Pop()
 {
@@ -69,27 +74,27 @@ XPCJSContextStack::Push(JSContext *cx)
     if (e.cx) {
         // The cx we're pushing is also stack-top. In general we still need to
         // call JS_SaveFrameChain here. But if that would put us in a
         // compartment that's same-origin with the current one, we can skip it.
         nsIScriptSecurityManager* ssm = XPCWrapper::GetSecurityManager();
         if ((e.cx == cx) && ssm) {
             // DOM JSContexts don't store their default compartment object on
             // the cx, so in those cases we need to fetch it via the scx
-            // instead.
+            // instead. And in some cases (i.e. the SafeJSContext), we have no
+            // default compartment object at all.
             RootedObject defaultScope(cx, GetDefaultScopeFromJSContext(cx));
-
-            nsIPrincipal *currentPrincipal =
-              GetCompartmentPrincipal(js::GetContextCompartment(cx));
-            nsIPrincipal *defaultPrincipal = GetObjectPrincipal(defaultScope);
-            bool equal = false;
-            currentPrincipal->Equals(defaultPrincipal, &equal);
-            if (equal) {
-                mStack.AppendElement(cx);
-                return true;
+            if (defaultScope) {
+                nsIPrincipal *currentPrincipal =
+                  GetCompartmentPrincipal(js::GetContextCompartment(cx));
+                nsIPrincipal *defaultPrincipal = GetObjectPrincipal(defaultScope);
+                if (currentPrincipal->Equals(defaultPrincipal)) {
+                    mStack.AppendElement(cx);
+                    return true;
+                }
             }
         }
 
         {
             // Push() can be called outside any request for e.cx.
             JSAutoRequest ar(e.cx);
             if (!JS_SaveFrameChain(e.cx))
                 return false;
@@ -137,16 +142,23 @@ const JSClass xpc::SafeJSContextGlobalCl
 
 JSContext*
 XPCJSContextStack::GetSafeJSContext()
 {
     MOZ_ASSERT(mSafeJSContext);
     return mSafeJSContext;
 }
 
+JSObject*
+XPCJSContextStack::GetSafeJSContextGlobal()
+{
+    MOZ_ASSERT(mSafeJSContextGlobal);
+    return mSafeJSContextGlobal;
+}
+
 JSContext*
 XPCJSContextStack::InitSafeJSContext()
 {
     MOZ_ASSERT(!mSafeJSContext);
 
     // Start by getting the principal holder and principal for this
     // context.  If we can't manage that, don't bother with the rest.
     nsRefPtr<nsNullPrincipal> principal = new nsNullPrincipal();
@@ -158,38 +170,38 @@ XPCJSContextStack::InitSafeJSContext()
     JSRuntime *rt = xpc->GetRuntime()->Runtime();
     if (!rt)
         MOZ_CRASH();
 
     mSafeJSContext = JS_NewContext(rt, 8192);
     if (!mSafeJSContext)
         MOZ_CRASH();
     JSAutoRequest req(mSafeJSContext);
+    ContextOptionsRef(mSafeJSContext).setNoDefaultCompartmentObject(true);
 
-    JS::RootedObject glob(mSafeJSContext);
     JS_SetErrorReporter(mSafeJSContext, xpc::SystemErrorReporter);
 
     JS::CompartmentOptions options;
     options.setZone(JS::SystemZone);
-    glob = xpc::CreateGlobalObject(mSafeJSContext, &SafeJSContextGlobalClass, principal, options);
-    if (!glob)
+    mSafeJSContextGlobal = CreateGlobalObject(mSafeJSContext,
+                                              &SafeJSContextGlobalClass,
+                                              principal, options);
+    if (!mSafeJSContextGlobal)
         MOZ_CRASH();
-
-    // Make sure the context is associated with a proper compartment
-    // and not the default compartment.
-    js::SetDefaultObjectForContext(mSafeJSContext, glob);
+    JS_AddNamedObjectRoot(mSafeJSContext, &mSafeJSContextGlobal, "SafeJSContext global");
 
     // Note: make sure to set the private before calling
     // InitClasses
-    nsRefPtr<SandboxPrivate> sp = new SandboxPrivate(principal, glob);
-    JS_SetPrivate(glob, sp.forget().take());
+    nsRefPtr<SandboxPrivate> sp = new SandboxPrivate(principal, mSafeJSContextGlobal);
+    JS_SetPrivate(mSafeJSContextGlobal, sp.forget().take());
 
     // After this point either glob is null and the
     // nsIScriptObjectPrincipal ownership is either handled by the
     // nsCOMPtr or dealt with, or we'll release in the finalize
     // hook.
-    if (NS_FAILED(xpc->InitClasses(mSafeJSContext, glob)))
+    if (NS_FAILED(xpc->InitClasses(mSafeJSContext, mSafeJSContextGlobal)))
         MOZ_CRASH();
 
+    JS::RootedObject glob(mSafeJSContext, mSafeJSContextGlobal);
     JS_FireOnNewGlobalObject(mSafeJSContext, glob);
 
     return mSafeJSContext;
 }
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -582,16 +582,22 @@ GetJunkScopeGlobal()
     JSObject *junkScope = GetJunkScope();
     // GetJunkScope would ideally never fail, currently it is not yet the case
     // unfortunately...(see Bug 874158)
     if (!junkScope)
         return nullptr;
     return GetNativeForGlobal(junkScope);
 }
 
+JSObject *
+GetSafeJSContextGlobal()
+{
+    return XPCJSRuntime::Get()->GetJSContextStack()->GetSafeJSContextGlobal();
+}
+
 nsGlobalWindow*
 WindowOrNull(JSObject *aObj)
 {
     MOZ_ASSERT(aObj);
     MOZ_ASSERT(!js::IsWrapper(aObj));
 
     // This will always return null until we have Window on WebIDL bindings,
     // at which point it will do the right thing.
@@ -2982,17 +2988,16 @@ class XPCJSSourceHook: public js::Source
         }
 
         return true;
     }
 };
 
 static const JSWrapObjectCallbacks WrapObjectCallbacks = {
     xpc::WrapperFactory::Rewrap,
-    xpc::WrapperFactory::WrapForSameCompartment,
     xpc::WrapperFactory::PrepareForWrapping
 };
 
 XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
    : CycleCollectedJSRuntime(nullptr, 32L * 1024L * 1024L, JS_USE_HELPER_THREADS),
    mJSContextStack(new XPCJSContextStack(MOZ_THIS_IN_INITIALIZER_LIST())),
    mCallContext(nullptr),
    mAutoRoots(nullptr),
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -710,18 +710,18 @@ void
 XPCWrappedNativeScope::AddSizeOfIncludingThis(ScopeSizeInfo* scopeSizeInfo)
 {
     scopeSizeInfo->mScopeAndMapSize += scopeSizeInfo->mMallocSizeOf(this);
     scopeSizeInfo->mScopeAndMapSize +=
         mWrappedNativeMap->SizeOfIncludingThis(scopeSizeInfo->mMallocSizeOf);
     scopeSizeInfo->mScopeAndMapSize +=
         mWrappedNativeProtoMap->SizeOfIncludingThis(scopeSizeInfo->mMallocSizeOf);
 
-    if (dom::HasProtoAndIfaceArray(mGlobalJSObject)) {
-        dom::ProtoAndIfaceArray* cache = dom::GetProtoAndIfaceArray(mGlobalJSObject);
+    if (dom::HasProtoAndIfaceCache(mGlobalJSObject)) {
+        dom::ProtoAndIfaceCache* cache = dom::GetProtoAndIfaceCache(mGlobalJSObject);
         scopeSizeInfo->mProtoAndIfaceCacheSize +=
             cache->SizeOfIncludingThis(scopeSizeInfo->mMallocSizeOf);
     }
 
     // There are other XPCWrappedNativeScope members that could be measured;
     // the above ones have been seen by DMD to be worth measuring.  More stuff
     // may be added later.
 }
--- a/js/xpconnect/src/nsCxPusher.cpp
+++ b/js/xpconnect/src/nsCxPusher.cpp
@@ -223,16 +223,17 @@ ThreadsafeAutoJSContext::operator JSCont
     return mCx;
   } else {
     return mAutoJSContext.ref();
   }
 }
 
 AutoSafeJSContext::AutoSafeJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
   : AutoJSContext(true MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
+  , mAc(mCx, XPCJSRuntime::Get()->GetJSContextStack()->GetSafeJSContextGlobal())
 {
 }
 
 ThreadsafeAutoSafeJSContext::ThreadsafeAutoSafeJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
 {
   MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 
   if (NS_IsMainThread()) {
--- a/js/xpconnect/src/nsCxPusher.h
+++ b/js/xpconnect/src/nsCxPusher.h
@@ -95,17 +95,16 @@ namespace mozilla {
 class MOZ_STACK_CLASS AutoJSContext {
 public:
   AutoJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
   operator JSContext*() const;
 
 protected:
   AutoJSContext(bool aSafe MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
 
-private:
   // We need this Init() method because we can't use delegating constructor for
   // the moment. It is a C++11 feature and we do not require C++11 to be
   // supported to be able to compile Gecko.
   void Init(bool aSafe MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
 
   JSContext* mCx;
   Maybe<AutoCxPusher> mPusher;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
@@ -129,16 +128,18 @@ private:
 
 /**
  * AutoSafeJSContext is similar to AutoJSContext but will only return the safe
  * JS context. That means it will never call ::GetCurrentJSContext().
  */
 class MOZ_STACK_CLASS AutoSafeJSContext : public AutoJSContext {
 public:
   AutoSafeJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
+private:
+  JSAutoCompartment mAc;
 };
 
 /**
  * Like AutoSafeJSContext but can be used safely on worker threads.
  */
 class MOZ_STACK_CLASS ThreadsafeAutoSafeJSContext {
 public:
   ThreadsafeAutoSafeJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -384,17 +384,22 @@ CreateGlobalObject(JSContext *cx, const 
         JS_TracerInit(&trc.base, JS_GetRuntime(cx), VerifyTraceXPCGlobalCalled);
         trc.ok = false;
         JS_TraceChildren(&trc.base, global, JSTRACE_OBJECT);
         MOZ_ASSERT(trc.ok, "Trace hook on global needs to call TraceXPCGlobal for XPConnect compartments.");
     }
 #endif
 
     if (clasp->flags & JSCLASS_DOM_GLOBAL) {
-        AllocateProtoAndIfaceCache(global);
+        const char* className = clasp->name;
+        AllocateProtoAndIfaceCache(global,
+                                   (strcmp(className, "Window") == 0 ||
+                                    strcmp(className, "ChromeWindow") == 0)
+                                   ? ProtoAndIfaceCache::WindowLike
+                                   : ProtoAndIfaceCache::NonWindowLike);
     }
 
     return global;
 }
 
 } // namespace xpc
 
 NS_IMETHODIMP
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -2762,32 +2762,34 @@ void PopJSContextNoScriptContext();
 } /* namespace xpc */
 
 class XPCJSContextStack
 {
 public:
     XPCJSContextStack(XPCJSRuntime *aRuntime)
       : mRuntime(aRuntime)
       , mSafeJSContext(nullptr)
+      , mSafeJSContextGlobal(nullptr)
     { }
 
     virtual ~XPCJSContextStack();
 
     uint32_t Count()
     {
         return mStack.Length();
     }
 
     JSContext *Peek()
     {
         return mStack.IsEmpty() ? nullptr : mStack[mStack.Length() - 1].cx;
     }
 
     JSContext *InitSafeJSContext();
     JSContext *GetSafeJSContext();
+    JSObject *GetSafeJSContextGlobal();
     bool HasJSContext(JSContext *cx);
 
     const InfallibleTArray<XPCJSContextInfo>* GetStack()
     { return &mStack; }
 
 private:
     friend class mozilla::AutoCxPusher;
     friend bool xpc::PushJSContextNoScriptContext(JSContext *aCx);;
@@ -2796,16 +2798,17 @@ private:
     // We make these private so that stack manipulation can only happen
     // through one of the above friends.
     JSContext *Pop();
     bool Push(JSContext *cx);
 
     AutoInfallibleTArray<XPCJSContextInfo, 16> mStack;
     XPCJSRuntime* mRuntime;
     JSContext*  mSafeJSContext;
+    JSObject* mSafeJSContextGlobal;
 };
 
 /***************************************************************************/
 // 'Components' object implementations. nsXPCComponentsBase has the
 // less-privileged stuff that we're willing to expose to XBL.
 
 class nsXPCComponentsBase : public nsIXPCComponentsBase
 {
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -436,16 +436,23 @@ GetJunkScopeGlobal();
 
 /**
  * If |aObj| is a window, returns the associated nsGlobalWindow.
  * Otherwise, returns null.
  */
 nsGlobalWindow*
 WindowOrNull(JSObject *aObj);
 
+/*
+ * Returns the dummy global associated with the SafeJSContext. Callers MUST
+ * consult with the XPConnect module owner before using this function.
+ */
+JSObject *
+GetSafeJSContextGlobal();
+
 /**
  * If |aObj| has a window for a global, returns the associated nsGlobalWindow.
  * Otherwise, returns null.
  */
 nsGlobalWindow*
 WindowGlobalOrNull(JSObject *aObj);
 
 // Error reporter used when there is no associated DOM window on to which to
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -484,36 +484,16 @@ WrapperFactory::Rewrap(JSContext *cx, Ha
     DEBUG_CheckUnwrapSafety(obj, wrapper, origin, target);
 
     if (existing)
         return Wrapper::Renew(cx, existing, obj, wrapper);
 
     return Wrapper::New(cx, obj, parent, wrapper);
 }
 
-JSObject *
-WrapperFactory::WrapForSameCompartment(JSContext *cx, HandleObject objArg)
-{
-    RootedObject obj(cx, objArg);
-    MOZ_ASSERT(js::IsObjectInContextCompartment(obj, cx));
-
-    // NB: The contract of WrapForSameCompartment says that |obj| may or may not
-    // be a security wrapper. These checks implicitly handle the security
-    // wrapper case.
-
-    // Outerize if necessary. This, in combination with the check in
-    // PrepareForUnwrapping, means that calling JS_Wrap* always outerizes.
-    obj = JS_ObjectToOuterObject(cx, obj);
-    NS_ENSURE_TRUE(obj, nullptr);
-
-    // The method below is a no-op for non-DOM objects.
-    dom::GetSameCompartmentWrapperForDOMBinding(*obj.address());
-    return obj;
-}
-
 // Call WaiveXrayAndWrap when you have a JS object that you don't want to be
 // wrapped in an Xray wrapper. cx->compartment is the compartment that will be
 // using the returned object. If the object to be wrapped is already in the
 // correct compartment, then this returns the unwrapped object.
 bool
 WrapperFactory::WaiveXrayAndWrap(JSContext *cx, MutableHandleValue vp)
 {
     if (vp.isPrimitive())
--- a/js/xpconnect/wrappers/WrapperFactory.h
+++ b/js/xpconnect/wrappers/WrapperFactory.h
@@ -53,20 +53,16 @@ class WrapperFactory {
     // Rewrap an object that is about to cross compartment boundaries.
     static JSObject *Rewrap(JSContext *cx,
                             JS::HandleObject existing,
                             JS::HandleObject obj,
                             JS::HandleObject wrappedProto,
                             JS::HandleObject parent,
                             unsigned flags);
 
-    // Wrap an object for same-compartment access.
-    static JSObject *WrapForSameCompartment(JSContext *cx,
-                                            JS::HandleObject obj);
-
     // Wrap wrapped object into a waiver wrapper and then re-wrap it.
     static bool WaiveXrayAndWrap(JSContext *cx, JS::MutableHandleValue vp);
     static bool WaiveXrayAndWrap(JSContext *cx, JS::MutableHandleObject object);
 
     // Returns true if the wrapper is in not shadowing mode for the id.
     static bool XrayWrapperNotShadowing(JSObject *wrapper, jsid id);
 };
 
--- a/layout/generic/StickyScrollContainer.cpp
+++ b/layout/generic/StickyScrollContainer.cpp
@@ -324,22 +324,22 @@ StickyScrollContainer::GetScrollRanges(n
   }
 }
 
 void
 StickyScrollContainer::PositionContinuations(nsIFrame* aFrame)
 {
   NS_ASSERTION(nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(aFrame),
                "Should be starting from the first continuation");
-  nsPoint translation = ComputePosition(aFrame) - aFrame->GetPosition();
+  nsPoint translation = ComputePosition(aFrame) - aFrame->GetNormalPosition();
 
   // Move all continuation frames by the same amount.
   for (nsIFrame* cont = aFrame; cont;
        cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
-    cont->SetPosition(cont->GetPosition() + translation);
+    cont->SetPosition(cont->GetNormalPosition() + translation);
   }
 }
 
 void
 StickyScrollContainer::UpdatePositions(nsPoint aScrollPosition,
                                        nsIFrame* aSubtreeRoot)
 {
 #ifdef DEBUG
--- a/layout/reftests/forms/select/reftest.list
+++ b/layout/reftests/forms/select/reftest.list
@@ -1,6 +1,7 @@
 skip-if(B2G) == out-of-bounds-selectedindex.html out-of-bounds-selectedindex-ref.html # test for bug 471741
 skip-if(B2G) == multiple.html multiple-ref.html
 == boguskids.html boguskids-ref.html
 == dynamic-boguskids.html boguskids-ref.html
 == option-children.html option-children-ref.html
 fuzzy(1,4) == padding-button-placement.html padding-button-placement-ref.html
+HTTP(../..) == vertical-centering.html vertical-centering-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/select/vertical-centering-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<style>
+  @font-face {
+    font-family: "Ahem";
+    src: url(../../fonts/Ahem.ttf);
+  }
+  select {
+    -moz-appearance: none;
+    border: none;
+    font-family: Ahem;
+    font-size: 20px;
+    box-sizing: content-box;
+    /*
+     * Why are these top/bottom paddings 7px rather than 10px?  1px each is
+     * eaten up by padding on the combobox display area, but I have no idea
+     * where the extra 4px somewhere else are coming from...
+     */
+    padding: 7px 0 7px 0;
+  }
+</style>
+<select>
+  <option>X</option>
+</select>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/select/vertical-centering.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<style>
+  @font-face {
+    font-family: "Ahem";
+    src: url(../../fonts/Ahem.ttf);
+  }
+  select {
+    -moz-appearance: none;
+    border: none;
+    font-family: Ahem;
+    font-size: 20px;
+    padding: 0;
+    box-sizing: content-box;
+    height: 40px;
+  }
+</style>
+<select>
+  <option>X</option>
+</select>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/position-sticky/block-in-inline-continuations-inner.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+  <head>
+    <title>iframe for CSS Test: Sticky Positioning - continuations have no effect when resizing</title>
+    <link rel="author" title="Abel Lin" href="mailto:alin@mozilla.com">
+    <link rel="stylesheet" type="text/css" href="ahem.css">
+    <style>
+      #scroll {
+        height: 100px;
+        overflow: hidden;
+        font: 20px/1 Ahem;
+      }
+      #sticky {
+        display: inline;
+        position: sticky;
+        top: 20px;
+      }
+    </style>
+  </head>
+  <body>
+    <div id="scroll">
+      <div id="sticky">
+          <div>in block</div>
+          after block
+      </div>
+    </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/position-sticky/block-in-inline-continuations-ref-inner.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+  <head>
+    <title>iframe for CSS Test Reference: Sticky Positioning - continuations have no effect when resizing</title>
+    <link rel="author" title="Abel Lin" href="mailto:alin@mozilla.com">
+    <link rel="stylesheet" type="text/css" href="ahem.css">
+    <style>
+      #scroll {
+        height: 100px;
+        overflow: hidden;
+        font: 20px/1 Ahem;
+      }
+    </style>
+  </head>
+  <body>
+    <div id="scroll">
+      <div style="height: 20px"></div>
+        <div><span class="sticky">in block</span></div>
+        <span class="sticky">after block</span>
+    </div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/position-sticky/block-in-inline-continuations-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+  <head>
+    <title>CSS Test Reference: Sticky Positioning - continuations have no effect when resizing</title>
+    <link rel="author" title="Abel Lin" href="mailto:alin@mozilla.com">
+    <script>
+    function run() {
+      document.getElementById("toresize").width = "750px";
+    }
+    </script>
+  </head>
+  <body onload="run()">
+    <iframe id="toresize" src="block-in-inline-continuations-ref-inner.html">
+    </iframe>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/position-sticky/block-in-inline-continuations.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+  <head>
+    <title>CSS Test: Sticky Positioning - continuations have no effect when resizing</title>
+    <link rel="author" title="Abel Lin" href="mailto:alin@mozilla.com">
+    <link rel="match" href="block-in-inline-continuations-ref.html">
+    <meta name="assert" content="Inline elements split and contain blocks should always have all parts moved the same offset from their normal position">
+    <script>
+    function run() {
+      document.getElementById("toresize").width = "750px";
+    }
+    </script>
+  </head>
+  <body onload="run()">
+    <iframe id="toresize" src="block-in-inline-continuations-inner.html">
+    </iframe>
+  </body>
+</html>
--- a/layout/reftests/position-sticky/reftest.list
+++ b/layout/reftests/position-sticky/reftest.list
@@ -45,9 +45,10 @@ fuzzy-if(Android,4,810) == containing-bl
 == inline-2.html inline-2-ref.html
 fuzzy-if(OSX==10.6||OSX==10.7,64,100) fuzzy-if(OSX==10.8,99,210) == inline-3.html inline-3-ref.html
 fails == column-contain-1a.html column-contain-1-ref.html
 == column-contain-1b.html column-contain-1-ref.html
 == column-contain-2.html column-contain-2-ref.html
 == block-in-inline-1.html block-in-inline-1-ref.html
 fuzzy-if(Android,8,1533) skip-if(B2G&&browserIsRemote) == block-in-inline-2.html block-in-inline-2-ref.html
 fuzzy-if(Android,8,630) fuzzy-if(OSX==10.8,1,11) skip-if(B2G&&browserIsRemote) == block-in-inline-3.html block-in-inline-3-ref.html
+== block-in-inline-continuations.html block-in-inline-continuations-ref.html
 == inner-table-1.html inner-table-1-ref.html
--- a/layout/style/forms.css
+++ b/layout/style/forms.css
@@ -277,16 +277,17 @@ select:empty {
   -moz-padding-end: 0;
   color: inherit;
   white-space: nowrap;
   text-align: inherit;
   -moz-user-select: none;
   /* Make sure to size correctly if the combobox has a non-auto height. */
   height: 100% ! important;
   box-sizing: border-box ! important;
+  line-height: -moz-block-height;
 }
 
 option {
   display: block;
   float: none !important;
   position: static !important;
   min-height: 1em;
   line-height: normal !important;
--- a/layout/style/test/test_animations_omta_start.html
+++ b/layout/style/test/test_animations_omta_start.html
@@ -48,21 +48,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 "use strict";
 
 var gUtils = SpecialPowers.DOMWindowUtils;
 var gOMTAPrefKey = "layers.offmainthreadcomposition.async-animations";
 var gOMTCEnabled = gUtils.layerManagerRemote;
 
 if (gOMTCEnabled && SpecialPowers.getBoolPref(gOMTAPrefKey)) {
   SimpleTest.waitForExplicitFinish();
-  window.addEventListener("load", function() {
-    // Using paint_listener.js functions inside an onload handler is not safe
-    // (bug 986367) so spin the event loop first
-    SimpleTest.executeSoon(testDelay);
-  });
+  window.addEventListener("load", testDelay);
 } else {
   ok(true, "OMTA not available");
 }
 
 function newTarget() {
   var target = document.createElement("div");
   target.classList.add("target");
   document.getElementById("display").appendChild(target);
--- a/media/libcubeb/src/cubeb_opensl.c
+++ b/media/libcubeb/src/cubeb_opensl.c
@@ -7,25 +7,29 @@
 #undef NDEBUG
 #include <assert.h>
 #include <dlfcn.h>
 #include <stdlib.h>
 #include <SLES/OpenSLES.h>
 #if defined(__ANDROID__)
 #include "android/sles_definitions.h"
 #include <SLES/OpenSLES_Android.h>
+#include <android/log.h>
+#define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "Cubeb_OpenSL" , ## args)
 #endif
 #include "cubeb/cubeb.h"
 #include "cubeb-internal.h"
 
 static struct cubeb_ops const opensl_ops;
 
 struct cubeb {
   struct cubeb_ops const * ops;
   void * lib;
+  void * libmedia;
+  int32_t (* get_output_latency)(uint32_t * latency, int stream_type);
   SLInterfaceID SL_IID_BUFFERQUEUE;
   SLInterfaceID SL_IID_PLAY;
 #if defined(__ANDROID__)
   SLInterfaceID SL_IID_ANDROIDCONFIGURATION;
 #endif
   SLObjectItf engObj;
   SLEngineItf eng;
   SLObjectItf outmixObj;
@@ -130,21 +134,40 @@ opensl_init(cubeb ** context, char const
   *context = NULL;
 
   ctx = calloc(1, sizeof(*ctx));
   assert(ctx);
 
   ctx->ops = &opensl_ops;
 
   ctx->lib = dlopen("libOpenSLES.so", RTLD_LAZY);
-  if (!ctx->lib) {
+  ctx->libmedia = dlopen("libmedia.so", RTLD_LAZY);
+  if (!ctx->lib || !ctx->libmedia) {
     free(ctx);
     return CUBEB_ERROR;
   }
 
+  /* Get the latency, in ms, from AudioFlinger */
+  /* status_t AudioSystem::getOutputLatency(uint32_t* latency,
+   *                                        audio_stream_type_t streamType) */
+  /* First, try the most recent signature. */
+  ctx->get_output_latency =
+    dlsym(ctx->libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPj19audio_stream_type_t");
+  if (!ctx->get_output_latency) {
+    /* in case of failure, try the legacy version. */
+    /* status_t AudioSystem::getOutputLatency(uint32_t* latency,
+     *                                        int streamType) */
+    ctx->get_output_latency =
+      dlsym(ctx->libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPji");
+    if (!ctx->get_output_latency) {
+      opensl_destroy(ctx);
+      return CUBEB_ERROR;
+    }
+  }
+
   typedef SLresult (*slCreateEngine_t)(SLObjectItf *,
                                        SLuint32,
                                        const SLEngineOption *,
                                        SLuint32,
                                        const SLInterfaceID *,
                                        const SLboolean *);
   slCreateEngine_t f_slCreateEngine =
     (slCreateEngine_t)dlsym(ctx->lib, "slCreateEngine");
@@ -346,16 +369,17 @@ opensl_get_min_latency(cubeb * ctx, cube
 static void
 opensl_destroy(cubeb * ctx)
 {
   if (ctx->outmixObj)
     (*ctx->outmixObj)->Destroy(ctx->outmixObj);
   if (ctx->engObj)
     (*ctx->engObj)->Destroy(ctx->engObj);
   dlclose(ctx->lib);
+  dlclose(ctx->libmedia);
   free(ctx);
 }
 
 static void opensl_stream_destroy(cubeb_stream * stm);
 
 static int
 opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
                   cubeb_stream_params stream_params, unsigned int latency,
@@ -484,29 +508,29 @@ opensl_stream_init(cubeb * ctx, cubeb_st
 
   res = (*stm->bufq)->RegisterCallback(stm->bufq, bufferqueue_callback, stm);
   if (res != SL_RESULT_SUCCESS) {
     opensl_stream_destroy(stm);
     return CUBEB_ERROR;
   }
 
   *stream = stm;
-
   return CUBEB_OK;
 }
 
 static void
 opensl_stream_destroy(cubeb_stream * stm)
 {
   if (stm->playerObj)
     (*stm->playerObj)->Destroy(stm->playerObj);
   int i;
   for (i = 0; i < NBUFS; i++) {
     free(stm->queuebuf[i]);
   }
+
   free(stm);
 }
 
 static int
 opensl_stream_start(cubeb_stream * stm)
 {
   /* To refill the queues before starting playback in order to avoid racing
   * with refills started by SetPlayState on OpenSLES ndk threads. */
@@ -529,79 +553,61 @@ opensl_stream_stop(cubeb_stream * stm)
 }
 
 static int
 opensl_stream_get_position(cubeb_stream * stm, uint64_t * position)
 {
   SLmillisecond msec;
   uint64_t samplerate;
   SLresult res;
+  int rv;
+  int32_t mixer_latency;
 
   res = (*stm->play)->GetPosition(stm->play, &msec);
   if (res != SL_RESULT_SUCCESS)
     return CUBEB_ERROR;
 
   samplerate = stm->bytespersec / stm->framesize;
 
-  *position = samplerate * msec / 1000;
+  rv = stm->context->get_output_latency(&mixer_latency, stm->stream_type);
+  if (rv) {
+    return CUBEB_ERROR;
+  }
+
+  if (msec > mixer_latency) {
+    *position = samplerate * (msec - mixer_latency) / 1000;
+  } else {
+    *position = 0;
+  }
   return CUBEB_OK;
 }
 
 int
 opensl_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
 {
   int rv;
-  void * libmedia;
-  int32_t (*get_output_latency)(uint32_t * latency, int stream_type);
   uint32_t mixer_latency;
   uint32_t samplerate;
 
   /* The latency returned by AudioFlinger is in ms, so we have to get
    * AudioFlinger's samplerate to convert it to frames. */
   rv = opensl_get_preferred_sample_rate(stm->context, &samplerate);
   if (rv) {
     return CUBEB_ERROR;
   }
 
-  libmedia = dlopen("libmedia.so", RTLD_LAZY);
-  if (!libmedia) {
-    return CUBEB_ERROR;
-  }
-
-  /* Get the latency, in ms, from AudioFlinger */
-  /* status_t AudioSystem::getOutputLatency(uint32_t* latency,
-   *                                        audio_stream_type_t streamType) */
-  /* First, try the most recent signature. */
-    get_output_latency =
-      dlsym(libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPj19audio_stream_type_t");
-  if (!get_output_latency) {
-    /* in case of failure, try the legacy version. */
-    /* status_t AudioSystem::getOutputLatency(uint32_t* latency,
-     *                                        int streamType) */
-    get_output_latency =
-      dlsym(libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPji");
-    if (!get_output_latency) {
-      dlclose(libmedia);
-      return CUBEB_ERROR;
-    }
-  }
-
   /* audio_stream_type_t is an int, so this is okay. */
-  rv = get_output_latency(&mixer_latency, stm->stream_type);
-
+  rv = stm->context->get_output_latency(&mixer_latency, stm->stream_type);
   if (rv) {
-    dlclose(libmedia);
     return CUBEB_ERROR;
   }
 
   *latency = NBUFS * stm->queuebuf_len / stm->framesize + // OpenSL latency
              mixer_latency * samplerate / 1000; // AudioFlinger latency
 
-  dlclose(libmedia);
-
   return CUBEB_OK;
 }
 
 static struct cubeb_ops const opensl_ops = {
   .init = opensl_init,
   .get_backend_id = opensl_get_backend_id,
   .get_max_channel_count = opensl_get_max_channel_count,
   .get_min_latency = opensl_get_min_latency,
--- a/mfbt/Atomics.h
+++ b/mfbt/Atomics.h
@@ -36,16 +36,23 @@
     * Therefore, we require at least 4.7.0 for using libstdc++.
     */
 #  if MOZ_USING_LIBSTDCXX && MOZ_LIBSTDCXX_VERSION_AT_LEAST(4, 7, 0)
 #    define MOZ_HAVE_CXX11_ATOMICS
 #  elif MOZ_USING_LIBCXX
 #    define MOZ_HAVE_CXX11_ATOMICS
 #  endif
 #elif defined(_MSC_VER) && _MSC_VER >= 1700
+#  if defined(DEBUG)
+     /*
+      * Provide our own failure code since we're having trouble linking to
+      * std::_Debug_message (bug 982310).
+      */
+#    define _INVALID_MEMORY_ORDER MOZ_CRASH("Invalid memory order")
+#  endif
 #  define MOZ_HAVE_CXX11_ATOMICS
 #endif
 
 namespace mozilla {
 
 /**
  * An enum of memory ordering possibilities for atomics.
  *
--- a/mobile/android/base/webapp/Allocator.java
+++ b/mobile/android/base/webapp/Allocator.java
@@ -6,16 +6,17 @@
 package org.mozilla.gecko.webapp;
 
 import java.util.ArrayList;
 
 import org.mozilla.gecko.GeckoAppShell;
 
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
 import android.util.Log;
 
 public class Allocator {
 
     private final String LOGTAG = "GeckoWebappAllocator";
 
     private static final String PREFIX_ORIGIN = "webapp-origin-";
     private static final String PREFIX_PACKAGE_NAME = "webapp-package-name-";
@@ -64,16 +65,29 @@ public class Allocator {
     private static String oldAppKey(int index) {
         return PREFIX_OLD_APP + index;
     }
 
     private static String oldIconKey(int index) {
         return PREFIX_OLD_ICON + index;
     }
 
+    private static void save(Editor editor) {
+        // Use SharedPreferences.Editor.apply() where available and commit()
+        // where it isn't.  We could also use a background thread with commit(),
+        // but our callers might expect the changes we make to be available
+        // immediately, so we instead take the commit() performance hit
+        // on the small percentage of extant devices that don't support apply().
+        if (android.os.Build.VERSION.SDK_INT > 8) {
+            editor.apply();
+        } else {
+            editor.commit();
+        }
+    }
+
     public ArrayList<String> getInstalledPackageNames() {
         ArrayList<String> installedPackages = new ArrayList<String>();
 
         for (int i = 0; i < MAX_WEB_APPS; ++i) {
             if (mPrefs.contains(appKey(i))) {
                 installedPackages.add(mPrefs.getString(appKey(i), ""));
             }
         }
@@ -93,21 +107,21 @@ public class Allocator {
             }
         }
 
         // no more apps!
         return -1;
     }
 
     public synchronized void putPackageName(final int index, final String packageName) {
-        mPrefs.edit().putString(appKey(index), packageName).apply();
+        save(mPrefs.edit().putString(appKey(index), packageName));
     }
 
     public void updateColor(int index, int color) {
-        mPrefs.edit().putInt(iconKey(index), color).apply();
+        save(mPrefs.edit().putInt(iconKey(index), color));
     }
 
     public synchronized int getIndexForApp(String packageName) {
         return findSlotForPrefix(PREFIX_PACKAGE_NAME, packageName);
     }
 
     public synchronized int getIndexForOrigin(String origin) {
         return findSlotForPrefix(PREFIX_ORIGIN, origin);
@@ -131,25 +145,21 @@ public class Allocator {
         if (index == -1)
             return -1;
 
         releaseIndex(index);
         return index;
     }
 
     public synchronized void releaseIndex(final int index) {
-        mPrefs.edit()
-              .remove(appKey(index))
-              .remove(iconKey(index))
-              .remove(originKey(index))
-              .apply();
+        save(mPrefs.edit().remove(appKey(index)).remove(iconKey(index)).remove(originKey(index)));
     }
 
     public void putOrigin(int index, String origin) {
-        mPrefs.edit().putString(originKey(index), origin).apply();
+        save(mPrefs.edit().putString(originKey(index), origin));
     }
 
     public String getOrigin(int index) {
         return mPrefs.getString(originKey(index), null);
     }
 
     public int getColor(int index) {
         return mPrefs.getInt(iconKey(index), -1);
@@ -170,11 +180,11 @@ public class Allocator {
         // stores the packageName, so we migrate oldAppKey to the origin pref.
         putOrigin(index, mPrefs.getString(oldAppKey(index), null));
 
         // The old iconKey pref actually stored the splash screen background
         // color, so we migrate oldIconKey to the color pref.
         updateColor(index, mPrefs.getInt(oldIconKey(index), -1));
 
         // Remove the old prefs so we don't migrate them the next time around.
-        mPrefs.edit().remove(oldAppKey(index)).remove(oldIconKey(index)).apply();
+        save(mPrefs.edit().remove(oldAppKey(index)).remove(oldIconKey(index)));
     }
 }
--- a/netwerk/base/public/nsIUDPSocket.idl
+++ b/netwerk/base/public/nsIUDPSocket.idl
@@ -7,24 +7,26 @@
 
 interface nsINetAddr;
 interface nsIUDPSocketListener;
 interface nsIUDPMessage;
 interface nsISocketTransport;
 interface nsIOutputStream;
 
 %{ C++
+#include "nsTArrayForwardDeclare.h"
 namespace mozilla {
 namespace net {
 union NetAddr;
 }
 }
 %}
 native NetAddr(mozilla::net::NetAddr);
 [ptr] native NetAddrPtr(mozilla::net::NetAddr);
+[ref] native Uint8TArrayRef(FallibleTArray<uint8_t>);
 
 /**
  * nsIUDPSocket
  *
  * An interface to a UDP socket that can accept incoming connections.
  */
 [scriptable, uuid(6EFE692D-F0B0-4A9E-9E63-837C7452446D)]
 interface nsIUDPSocket : nsISupports
@@ -186,26 +188,32 @@ interface nsIUDPSocketListener : nsISupp
     void onStopListening(in nsIUDPSocket aSocket, in nsresult aStatus);
 };
 
 /**
  * nsIUDPMessage
  *
  * This interface is used to encapsulate an incomming UDP message
  */
-[scriptable, uuid(333D5D69-8117-4AA6-9E16-2DD4FD6AEBA6)]
+[scriptable, uuid(afdc743f-9cc0-40d8-b442-695dc54bbb74)]
 interface nsIUDPMessage : nsISupports
 {
     /**
      * Address of the source of the message
      */
     readonly attribute nsINetAddr fromAddr;
 
     /**
      * Data of the message
      */
     readonly attribute ACString data;
 
     /**
      * Stream to send a response
      */
     readonly attribute nsIOutputStream outputStream;
+
+    /**
+     * Raw Data of the message
+     */
+    [implicit_jscontext] readonly attribute jsval rawData;
+    [noscript, notxpcom, nostdcall] Uint8TArrayRef getDataAsTArray();
 };
--- a/netwerk/base/src/ProxyAutoConfig.cpp
+++ b/netwerk/base/src/ProxyAutoConfig.cpp
@@ -408,16 +408,20 @@ bool PACDnsResolve(JSContext *cx, unsign
 
   nsDependentJSString hostName;
   nsAutoCString dottedDecimal;
 
   if (!hostName.init(cx, arg1))
     return false;
   if (PACResolveToString(NS_ConvertUTF16toUTF8(hostName), dottedDecimal, 0)) {
     JSString *dottedDecimalString = JS_NewStringCopyZ(cx, dottedDecimal.get());
+    if (!dottedDecimalString) {
+      return false;
+    }
+
     args.rval().setString(dottedDecimalString);
   }
   else {
     args.rval().setNull();
   }
 
   return true;
 }
@@ -768,82 +772,106 @@ ProxyAutoConfig::SrcAddress(const NetAdd
   
   localAddress.Assign(dottedDecimal);
 
   return true;
 }
 
 // hostName is run through a dns lookup and then a udp socket is connected
 // to the result. If that all works, the local IP address of the socket is
-// returned to the javascript caller and true is returned from this function.
-// otherwise false is returned.
+// returned to the javascript caller and |*aResult| is set to true. Otherwise
+// |*aResult| is set to false.
 bool
 ProxyAutoConfig::MyIPAddressTryHost(const nsCString &hostName,
                                     unsigned int timeout,
-                                    const JS::CallArgs &aArgs)
+                                    const JS::CallArgs &aArgs,
+                                    bool* aResult)
 {
+  *aResult = false;
+
   NetAddr remoteAddress;
   nsAutoCString localDottedDecimal;
   JSContext *cx = mJSRuntime->Context();
 
   if (PACResolve(hostName, &remoteAddress, timeout) &&
       SrcAddress(&remoteAddress, localDottedDecimal)) {
     JSString *dottedDecimalString =
       JS_NewStringCopyZ(cx, localDottedDecimal.get());
+    if (!dottedDecimalString) {
+      return false;
+    }
+
+    *aResult = true;
     aArgs.rval().setString(dottedDecimalString);
-    return true;
   }
-  return false;
+  return true;
 }
 
 bool
 ProxyAutoConfig::MyIPAddress(const JS::CallArgs &aArgs)
 {
   nsAutoCString remoteDottedDecimal;
   nsAutoCString localDottedDecimal;
   JSContext *cx = mJSRuntime->Context();
 
   // first, lookup the local address of a socket connected
   // to the host of uri being resolved by the pac file. This is
   // v6 safe.. but is the last step like that
-  if (MyIPAddressTryHost(mRunningHost, kTimeout, aArgs))
-    return true;
+  bool rvalAssigned = false;
+  if (!MyIPAddressTryHost(mRunningHost, kTimeout, aArgs, &rvalAssigned) ||
+      rvalAssigned) {
+    return rvalAssigned;
+  }
 
   // next, look for a route to a public internet address that doesn't need DNS.
   // This is the google anycast dns address, but it doesn't matter if it
   // remains operable (as we don't contact it) as long as the address stays
   // in commonly routed IP address space.
   remoteDottedDecimal.AssignLiteral("8.8.8.8");
-  if (MyIPAddressTryHost(remoteDottedDecimal, 0, aArgs))
-    return true;
+  if (!MyIPAddressTryHost(remoteDottedDecimal, 0, aArgs, &rvalAssigned) ||
+      rvalAssigned) {
+    return rvalAssigned;
+  }
   
   // next, use the old algorithm based on the local hostname
   nsAutoCString hostName;
   nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
   if (dns && NS_SUCCEEDED(dns->GetMyHostName(hostName)) &&
       PACResolveToString(hostName, localDottedDecimal, kTimeout)) {
     JSString *dottedDecimalString =
       JS_NewStringCopyZ(cx, localDottedDecimal.get());
+    if (!dottedDecimalString) {
+      return false;
+    }
+
     aArgs.rval().setString(dottedDecimalString);
     return true;
   }
 
   // next try a couple RFC 1918 variants.. maybe there is a
   // local route
   remoteDottedDecimal.AssignLiteral("192.168.0.1");
-  if (MyIPAddressTryHost(remoteDottedDecimal, 0, aArgs))
-    return true;
+  if (!MyIPAddressTryHost(remoteDottedDecimal, 0, aArgs, &rvalAssigned) ||
+      rvalAssigned) {
+    return rvalAssigned;
+  }
 
   // more RFC 1918
   remoteDottedDecimal.AssignLiteral("10.0.0.1");
-  if (MyIPAddressTryHost(remoteDottedDecimal, 0, aArgs))
-    return true;
+  if (!MyIPAddressTryHost(remoteDottedDecimal, 0, aArgs, &rvalAssigned) ||
+      rvalAssigned) {
+    return rvalAssigned;
+  }
 
   // who knows? let's fallback to localhost
   localDottedDecimal.AssignLiteral("127.0.0.1");
   JSString *dottedDecimalString =
     JS_NewStringCopyZ(cx, localDottedDecimal.get());
+  if (!dottedDecimalString) {
+    return false;
+  }
+
   aArgs.rval().setString(dottedDecimalString);
   return true;
 }
 
 } // namespace mozilla
 } // namespace mozilla::net
--- a/netwerk/base/src/ProxyAutoConfig.h
+++ b/netwerk/base/src/ProxyAutoConfig.h
@@ -78,17 +78,17 @@ public:
 private:
   const static unsigned int kTimeout = 1000; // ms to allow for myipaddress dns queries
 
   // used to compile the PAC file and setup the execution context
   nsresult SetupJS();
 
   bool SrcAddress(const NetAddr *remoteAddress, nsCString &localAddress);
   bool MyIPAddressTryHost(const nsCString &hostName, unsigned int timeout,
-                          const JS::CallArgs &aArgs);
+                          const JS::CallArgs &aArgs, bool* aResult);
 
   JSRuntimeWrapper *mJSRuntime;
   bool              mJSNeedsSetup;
   bool              mShutdown;
   nsCString         mPACScript;
   nsCString         mPACURI;
   nsCString         mRunningHost;
   nsCOMPtr<nsITimer> mTimer;
--- a/netwerk/base/src/nsUDPSocket.cpp
+++ b/netwerk/base/src/nsUDPSocket.cpp
@@ -1,15 +1,17 @@
 /* vim:set ts=2 sw=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Attributes.h"
 #include "mozilla/Endian.h"
+#include "mozilla/dom/TypedArray.h"
+#include "mozilla/HoldDropJSObjects.h"
 
 #include "nsSocketTransport2.h"
 #include "nsUDPSocket.h"
 #include "nsProxyRelease.h"
 #include "nsAutoPtr.h"
 #include "nsError.h"
 #include "nsNetCID.h"
 #include "prnetdb.h"
@@ -134,60 +136,101 @@ NS_IMETHODIMP nsUDPOutputStream::IsNonBl
 {
   *_retval = true;
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsUDPMessage impl
 //-----------------------------------------------------------------------------
-NS_IMPL_ISUPPORTS1(nsUDPMessage, nsIUDPMessage)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsUDPMessage)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsUDPMessage)
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsUDPMessage)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsUDPMessage)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_INTERFACE_MAP_ENTRY(nsIUDPMessage)
+NS_INTERFACE_MAP_END
 
-nsUDPMessage::nsUDPMessage(PRNetAddr* aAddr,
-             nsIOutputStream* aOutputStream,
-             const nsACString& aData)
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsUDPMessage)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJsobj)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsUDPMessage)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsUDPMessage)
+  tmp->mJsobj = nullptr;
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+nsUDPMessage::nsUDPMessage(NetAddr* aAddr,
+                           nsIOutputStream* aOutputStream,
+                           FallibleTArray<uint8_t>& aData)
   : mOutputStream(aOutputStream)
-  , mData(aData)
 {
   memcpy(&mAddr, aAddr, sizeof(NetAddr));
+  aData.SwapElements(mData);
 }
 
 nsUDPMessage::~nsUDPMessage()
 {
+  mozilla::DropJSObjects(this);
 }
 
 /* readonly attribute nsINetAddr from; */
-NS_IMETHODIMP nsUDPMessage::GetFromAddr(nsINetAddr * *aFromAddr)
+NS_IMETHODIMP
+nsUDPMessage::GetFromAddr(nsINetAddr * *aFromAddr)
 {
   NS_ENSURE_ARG_POINTER(aFromAddr);
 
-  NetAddr clientAddr;
-  PRNetAddrToNetAddr(&mAddr, &clientAddr);
-
-  nsCOMPtr<nsINetAddr> result = new nsNetAddr(&clientAddr);
+  nsCOMPtr<nsINetAddr> result = new nsNetAddr(&mAddr);
   result.forget(aFromAddr);
 
   return NS_OK;
 }
 
 /* readonly attribute ACString data; */
-NS_IMETHODIMP nsUDPMessage::GetData(nsACString & aData)
+NS_IMETHODIMP
+nsUDPMessage::GetData(nsACString & aData)
 {
-  aData = mData;
+  aData.Assign(reinterpret_cast<const char*>(mData.Elements()), mData.Length());
   return NS_OK;
 }
 
 /* readonly attribute nsIOutputStream outputStream; */
-NS_IMETHODIMP nsUDPMessage::GetOutputStream(nsIOutputStream * *aOutputStream)
+NS_IMETHODIMP
+nsUDPMessage::GetOutputStream(nsIOutputStream * *aOutputStream)
 {
   NS_ENSURE_ARG_POINTER(aOutputStream);
   NS_IF_ADDREF(*aOutputStream = mOutputStream);
   return NS_OK;
 }
 
+/* readonly attribute jsval rawData; */
+NS_IMETHODIMP
+nsUDPMessage::GetRawData(JSContext* cx,
+                         JS::MutableHandleValue aRawData)
+{
+  if(!mJsobj){
+    mJsobj = mozilla::dom::Uint8Array::Create(cx, nullptr, mData.Length(), mData.Elements());
+    mozilla::HoldJSObjects(this);
+  }
+  aRawData.setObject(*mJsobj);
+  return NS_OK;
+}
+
+/* [noscript, notxpcom, nostdcall] Uint8ArrayRef getDataAsTArray(); */
+FallibleTArray<uint8_t>&
+nsUDPMessage::GetDataAsTArray()
+{
+  return mData;
+}
+
 //-----------------------------------------------------------------------------
 // nsUDPSocket
 //-----------------------------------------------------------------------------
 
 nsUDPSocket::nsUDPSocket()
   : mLock("nsUDPSocket.mLock")
   , mFD(nullptr)
   , mAttached(false)
@@ -298,16 +341,88 @@ nsUDPSocket::TryAttach()
 
   //
   // now, configure our poll flags for listening...
   //
   mPollFlags = (PR_POLL_READ | PR_POLL_EXCEPT);
   return NS_OK;
 }
 
+namespace {
+//-----------------------------------------------------------------------------
+// UDPMessageProxy
+//-----------------------------------------------------------------------------
+class UDPMessageProxy MOZ_FINAL : public nsIUDPMessage
+{
+public:
+  UDPMessageProxy(NetAddr* aAddr,
+                  nsIOutputStream* aOutputStream,
+                  FallibleTArray<uint8_t>& aData)
+  : mOutputStream(aOutputStream)
+  {
+    memcpy(&mAddr, aAddr, sizeof(NetAddr));
+    aData.SwapElements(mData);
+  }
+
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIUDPMESSAGE
+
+private:
+  NetAddr mAddr;
+  nsCOMPtr<nsIOutputStream> mOutputStream;
+  FallibleTArray<uint8_t> mData;
+};
+
+NS_IMPL_ISUPPORTS1(UDPMessageProxy, nsIUDPMessage)
+
+/* readonly attribute nsINetAddr from; */
+NS_IMETHODIMP
+UDPMessageProxy::GetFromAddr(nsINetAddr * *aFromAddr)
+{
+  NS_ENSURE_ARG_POINTER(aFromAddr);
+
+  nsCOMPtr<nsINetAddr> result = new nsNetAddr(&mAddr);
+  result.forget(aFromAddr);
+
+  return NS_OK;
+}
+
+/* readonly attribute ACString data; */
+NS_IMETHODIMP
+UDPMessageProxy::GetData(nsACString & aData)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* [noscript, notxpcom, nostdcall] Uint8TArrayRef getDataAsTArray(); */
+FallibleTArray<uint8_t>&
+UDPMessageProxy::GetDataAsTArray()
+{
+  return mData;
+}
+
+/* readonly attribute jsval rawData; */
+NS_IMETHODIMP
+UDPMessageProxy::GetRawData(JSContext* cx,
+                            JS::MutableHandleValue aRawData)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* readonly attribute nsIOutputStream outputStream; */
+NS_IMETHODIMP
+UDPMessageProxy::GetOutputStream(nsIOutputStream * *aOutputStream)
+{
+  NS_ENSURE_ARG_POINTER(aOutputStream);
+  NS_IF_ADDREF(*aOutputStream = mOutputStream);
+  return NS_OK;
+}
+
+} //anonymous namespace
+
 //-----------------------------------------------------------------------------
 // nsUDPSocket::nsASocketHandler
 //-----------------------------------------------------------------------------
 
 void
 nsUDPSocket::OnSocketReady(PRFileDesc *fd, int16_t outFlags)
 {
   NS_ASSERTION(NS_SUCCEEDED(mCondition), "oops");
@@ -328,19 +443,19 @@ nsUDPSocket::OnSocketReady(PRFileDesc *f
 
   if (count < 1) {
     NS_WARNING("error of recvfrom on UDP socket");
     mCondition = NS_ERROR_UNEXPECTED;
     return;
   }
   mByteReadCount += count;
 
-  nsCString data;
-  if (!data.Assign(buff, count, mozilla::fallible_t())) {
-    mCondition = NS_ERROR_OUT_OF_MEMORY;
+  FallibleTArray<uint8_t> data;
+  if(!data.AppendElements(buff, count)){
+    mCondition = NS_ERROR_UNEXPECTED;
     return;
   }
 
   nsCOMPtr<nsIAsyncInputStream> pipeIn;
   nsCOMPtr<nsIAsyncOutputStream> pipeOut;
 
   uint32_t segsize = 1400;
   uint32_t segcount = 0;
@@ -355,17 +470,19 @@ nsUDPSocket::OnSocketReady(PRFileDesc *f
   nsRefPtr<nsUDPOutputStream> os = new nsUDPOutputStream(this, mFD, prClientAddr);
   rv = NS_AsyncCopy(pipeIn, os, mSts,
                     NS_ASYNCCOPY_VIA_READSEGMENTS, 1400);
 
   if (NS_FAILED(rv)) {
     return;
   }
 
-  nsCOMPtr<nsIUDPMessage> message = new nsUDPMessage(&prClientAddr, pipeOut, data);
+  NetAddr netAddr;
+  PRNetAddrToNetAddr(&prClientAddr, &netAddr);
+  nsCOMPtr<nsIUDPMessage> message = new UDPMessageProxy(&netAddr, pipeOut, data);
   mListener->OnPacketReceived(this, message);
 }
 
 void
 nsUDPSocket::OnSocketDetached(PRFileDesc *fd)
 {
   // force a failure condition if none set; maybe the STS is shutting down :-/
   if (NS_SUCCEEDED(mCondition))
@@ -529,17 +646,19 @@ NS_IMETHODIMP
 nsUDPSocket::GetAddress(NetAddr *aResult)
 {
   // no need to enter the lock here
   memcpy(aResult, &mAddr, sizeof(mAddr));
   return NS_OK;
 }
 
 namespace {
-
+//-----------------------------------------------------------------------------
+// SocketListenerProxy
+//-----------------------------------------------------------------------------
 class SocketListenerProxy MOZ_FINAL : public nsIUDPSocketListener
 {
 public:
   SocketListenerProxy(nsIUDPSocketListener* aListener)
     : mListener(new nsMainThreadPtrHolder<nsIUDPSocketListener>(aListener))
     , mTargetThread(do_GetCurrentThread())
   { }
 
@@ -608,17 +727,30 @@ SocketListenerProxy::OnStopListening(nsI
   nsRefPtr<OnStopListeningRunnable> r =
     new OnStopListeningRunnable(mListener, aSocket, aStatus);
   return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL);
 }
 
 NS_IMETHODIMP
 SocketListenerProxy::OnPacketReceivedRunnable::Run()
 {
-  mListener->OnPacketReceived(mSocket, mMessage);
+  NetAddr netAddr;
+  nsCOMPtr<nsINetAddr> nsAddr;
+  mMessage->GetFromAddr(getter_AddRefs(nsAddr));
+  nsAddr->GetNetAddr(&netAddr);
+
+  nsCOMPtr<nsIOutputStream> outputStream;
+  mMessage->GetOutputStream(getter_AddRefs(outputStream));
+
+  FallibleTArray<uint8_t>& data = mMessage->GetDataAsTArray();
+
+  nsCOMPtr<nsIUDPMessage> message = new nsUDPMessage(&netAddr,
+                                                     outputStream,
+                                                     data);
+  mListener->OnPacketReceived(mSocket, message);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 SocketListenerProxy::OnStopListeningRunnable::Run()
 {
   mListener->OnStopListening(mSocket, mStatus);
   return NS_OK;
--- a/netwerk/base/src/nsUDPSocket.h
+++ b/netwerk/base/src/nsUDPSocket.h
@@ -5,16 +5,17 @@
 
 #ifndef nsUDPSocket_h__
 #define nsUDPSocket_h__
 
 #include "nsIUDPSocket.h"
 #include "mozilla/Mutex.h"
 #include "nsIOutputStream.h"
 #include "nsAutoPtr.h"
+#include "nsCycleCollectionParticipant.h"
 
 //-----------------------------------------------------------------------------
 
 class nsUDPSocket : public nsASocketHandler
                   , public nsIUDPSocket
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
@@ -56,29 +57,31 @@ private:
   uint64_t   mByteWriteCount;
 };
 
 //-----------------------------------------------------------------------------
 
 class nsUDPMessage : public nsIUDPMessage
 {
 public:
-  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsUDPMessage)
   NS_DECL_NSIUDPMESSAGE
 
-  nsUDPMessage(PRNetAddr* aAddr,
+  nsUDPMessage(mozilla::net::NetAddr* aAddr,
                nsIOutputStream* aOutputStream,
-               const nsACString& aData);
+               FallibleTArray<uint8_t>& aData);
 
 private:
   virtual ~nsUDPMessage();
 
-  PRNetAddr mAddr;
+  mozilla::net::NetAddr mAddr;
   nsCOMPtr<nsIOutputStream> mOutputStream;
-  nsCString mData;
+  FallibleTArray<uint8_t> mData;
+  JS::Heap<JSObject*> mJsobj;
 };
 
 
 //-----------------------------------------------------------------------------
 
 class nsUDPOutputStream : public nsIOutputStream
 {
 public:
--- a/netwerk/dns/effective_tld_names.dat
+++ b/netwerk/dns/effective_tld_names.dat
@@ -1187,383 +1187,383 @@ int.is
 it
 gov.it
 edu.it
 // Reserved geo-names:
 // http://www.nic.it/documenti/regolamenti-e-linee-guida/regolamento-assegnazione-versione-6.0.pdf
 // There is also a list of reserved geo-names corresponding to Italian municipalities
 // http://www.nic.it/documenti/appendice-c.pdf, but it is not included here.
 // Regions
-abr.it 
-abruzzo.it 
-aosta-valley.it 
-aostavalley.it 
-bas.it 
-basilicata.it 
-cal.it 
-calabria.it 
-cam.it 
-campania.it 
-emilia-romagna.it 
-emiliaromagna.it 
-emr.it 
-friuli-v-giulia.it 
-friuli-ve-giulia.it 
-friuli-vegiulia.it 
-friuli-venezia-giulia.it 
-friuli-veneziagiulia.it 
-friuli-vgiulia.it 
-friuliv-giulia.it 
-friulive-giulia.it 
-friulivegiulia.it 
-friulivenezia-giulia.it 
-friuliveneziagiulia.it 
-friulivgiulia.it 
-fvg.it 
-laz.it 
-lazio.it 
-lig.it 
-liguria.it 
-lom.it 
-lombardia.it 
-lombardy.it 
-lucania.it 
-mar.it 
-marche.it 
-mol.it 
-molise.it 
-piedmont.it 
-piemonte.it 
-pmn.it 
-pug.it 
-puglia.it 
+abr.it
+abruzzo.it
+aosta-valley.it
+aostavalley.it
+bas.it
+basilicata.it
+cal.it
+calabria.it
+cam.it
+campania.it
+emilia-romagna.it
+emiliaromagna.it
+emr.it
+friuli-v-giulia.it
+friuli-ve-giulia.it
+friuli-vegiulia.it
+friuli-venezia-giulia.it
+friuli-veneziagiulia.it
+friuli-vgiulia.it
+friuliv-giulia.it
+friulive-giulia.it
+friulivegiulia.it
+friulivenezia-giulia.it
+friuliveneziagiulia.it
+friulivgiulia.it
+fvg.it
+laz.it
+lazio.it
+lig.it
+liguria.it
+lom.it
+lombardia.it
+lombardy.it
+lucania.it
+mar.it
+marche.it
+mol.it
+molise.it
+piedmont.it
+piemonte.it
+pmn.it
+pug.it
+puglia.it
 sar.it
-sardegna.it 
-sardinia.it 
-sic.it 
-sicilia.it 
-sicily.it 
-taa.it 
-tos.it 
-toscana.it 
-trentino-a-adige.it 
-trentino-aadige.it 
-trentino-alto-adige.it 
-trentino-altoadige.it 
-trentino-s-tirol.it 
-trentino-stirol.it 
-trentino-sud-tirol.it 
-trentino-sudtirol.it 
-trentino-sued-tirol.it 
-trentino-suedtirol.it 
-trentinoa-adige.it 
-trentinoaadige.it 
-trentinoalto-adige.it 
-trentinoaltoadige.it 
-trentinos-tirol.it 
-trentinostirol.it 
-trentinosud-tirol.it 
-trentinosudtirol.it 
-trentinosued-tirol.it 
-trentinosuedtirol.it 
-tuscany.it 
-umb.it 
-umbria.it 
-val-d-aosta.it 
-val-daosta.it 
-vald-aosta.it 
-valdaosta.it 
-valle-aosta.it 
-valle-d-aosta.it 
-valle-daosta.it 
-valleaosta.it 
-valled-aosta.it 
-valledaosta.it 
-vallee-aoste.it 
-valleeaoste.it 
-vao.it 
-vda.it 
-ven.it 
-veneto.it 
+sardegna.it
+sardinia.it
+sic.it
+sicilia.it
+sicily.it
+taa.it
+tos.it
+toscana.it
+trentino-a-adige.it
+trentino-aadige.it
+trentino-alto-adige.it
+trentino-altoadige.it
+trentino-s-tirol.it
+trentino-stirol.it
+trentino-sud-tirol.it
+trentino-sudtirol.it
+trentino-sued-tirol.it
+trentino-suedtirol.it
+trentinoa-adige.it
+trentinoaadige.it
+trentinoalto-adige.it
+trentinoaltoadige.it
+trentinos-tirol.it
+trentinostirol.it
+trentinosud-tirol.it
+trentinosudtirol.it
+trentinosued-tirol.it
+trentinosuedtirol.it
+tuscany.it
+umb.it
+umbria.it
+val-d-aosta.it
+val-daosta.it
+vald-aosta.it
+valdaosta.it
+valle-aosta.it
+valle-d-aosta.it
+valle-daosta.it
+valleaosta.it
+valled-aosta.it
+valledaosta.it
+vallee-aoste.it
+valleeaoste.it
+vao.it
+vda.it
+ven.it
+veneto.it
 // Provinces
-ag.it 
-agrigento.it 
-al.it 
-alessandria.it 
-alto-adige.it 
-altoadige.it 
-an.it 
-ancona.it 
-andria-barletta-trani.it 
-andria-trani-barletta.it 
-andriabarlettatrani.it 
-andriatranibarletta.it 
-ao.it 
-aosta.it 
-aoste.it 
-ap.it 
-aq.it 
-aquila.it 
-ar.it 
-arezzo.it 
-ascoli-piceno.it 
-ascolipiceno.it 
-asti.it 
-at.it 
-av.it 
-avellino.it 
-ba.it 
-balsan.it 
-bari.it 
-barletta-trani-andria.it 
-barlettatraniandria.it 
-belluno.it 
-benevento.it 
-bergamo .it 
-bg.it 
-bi.it 
-biella.it 
-bl.it 
-bn.it 
-bo.it 
-bologna.it 
-bolzano.it 
-bozen.it 
-br.it 
-brescia.it 
-brindisi.it 
-bs.it 
-bt.it 
-bz.it 
-ca.it 
-cagliari.it 
-caltanissetta.it 
-campidano-medio.it 
-campidanomedio.it 
-campobasso.it 
-carbonia-iglesias.it 
-carboniaiglesias.it 
-carrara-massa.it 
-carraramassa.it 
-caserta.it 
-catania.it 
-catanzaro.it 
-cb.it 
-ce.it 
-cesena-forli.it 
-cesenaforli.it 
-ch.it 
-chieti.it 
-ci.it 
-cl.it 
-cn.it 
-co.it 
-como.it 
-cosenza.it 
-cr.it 
-cremona.it 
-crotone.it 
-cs.it 
-ct.it 
-cuneo.it 
-cz.it 
-dell-ogliastra.it 
-dellogliastra.it 
-en.it 
-enna.it 
-fc.it 
-fe.it 
-fermo.it 
-ferrara.it 
-fg.it 
-fi.it 
-firenze.it 
-florence.it 
-fm.it 
-foggia.it 
-forli-cesena.it 
+ag.it
+agrigento.it
+al.it
+alessandria.it
+alto-adige.it
+altoadige.it
+an.it
+ancona.it
+andria-barletta-trani.it
+andria-trani-barletta.it
+andriabarlettatrani.it
+andriatranibarletta.it
+ao.it
+aosta.it
+aoste.it
+ap.it
+aq.it
+aquila.it
+ar.it
+arezzo.it
+ascoli-piceno.it
+ascolipiceno.it
+asti.it
+at.it
+av.it
+avellino.it
+ba.it
+balsan.it
+bari.it
+barletta-trani-andria.it
+barlettatraniandria.it
+belluno.it
+benevento.it
+bergamo.it
+bg.it
+bi.it
+biella.it
+bl.it
+bn.it
+bo.it
+bologna.it
+bolzano.it
+bozen.it
+br.it
+brescia.it
+brindisi.it
+bs.it
+bt.it
+bz.it
+ca.it
+cagliari.it
+caltanissetta.it
+campidano-medio.it
+campidanomedio.it
+campobasso.it
+carbonia-iglesias.it
+carboniaiglesias.it
+carrara-massa.it
+carraramassa.it
+caserta.it
+catania.it
+catanzaro.it
+cb.it
+ce.it
+cesena-forli.it
+cesenaforli.it
+ch.it
+chieti.it
+ci.it
+cl.it
+cn.it
+co.it
+como.it
+cosenza.it
+cr.it
+cremona.it
+crotone.it
+cs.it
+ct.it
+cuneo.it
+cz.it
+dell-ogliastra.it
+dellogliastra.it
+en.it
+enna.it
+fc.it
+fe.it
+fermo.it
+ferrara.it
+fg.it
+fi.it
+firenze.it
+florence.it
+fm.it
+foggia.it
+forli-cesena.it
 forlicesena.it
-fr.it 
-frosinone.it 
-ge.it 
-genoa.it 
-genova.it 
-go.it 
-gorizia.it 
-gr.it 
-grosseto.it 
-iglesias-carbonia.it 
-iglesiascarbonia.it 
-im.it 
-imperia.it 
-is.it 
-isernia.it 
-kr.it 
-la-spezia.it 
-laquila.it 
-laspezia.it 
-latina.it 
-lc.it 
-le.it 
-lecce.it 
-lecco.it 
-li.it 
-livorno.it 
-lo.it 
-lodi.it 
-lt.it 
-lu.it 
-lucca.it 
-macerata.it 
-mantova.it 
-massa-carrara.it 
-massacarrara.it 
-matera.it 
-mb.it 
-mc.it 
-me.it 
-medio-campidano.it 
-mediocampidano.it 
-messina.it 
-mi.it 
-milan.it 
-milano.it 
-mn.it 
-mo.it 
-modena.it 
-monza-brianza.it 
-monza-e-della-brianza.it 
-monza.it 
-monzabrianza.it 
-monzaebrianza.it 
-monzaedellabrianza.it 
-ms.it 
-mt.it 
-na.it 
-naples.it 
-napoli.it 
-no.it 
-novara.it 
-nu.it 
-nuoro.it 
-og.it 
-ogliastra.it 
-olbia-tempio.it 
-olbiatempio.it 
-or.it 
-oristano.it 
-ot.it 
-pa.it 
-padova.it 
-padua.it 
-palermo.it 
-parma.it 
-pavia.it 
-pc.it 
-pd.it 
-pe.it 
-perugia.it 
-pesaro-urbino.it 
-pesarourbino.it 
-pescara.it 
-pg.it 
-pi.it 
-piacenza.it 
-pisa.it 
-pistoia.it 
-pn.it 
-po.it 
-pordenone.it 
-potenza .it 
-pr.it 
-prato.it 
-pt.it 
-pu.it 
-pv.it 
-pz.it 
-ra.it 
-ragusa.it 
-ravenna.it 
-rc.it 
-re.it 
+fr.it
+frosinone.it
+ge.it
+genoa.it
+genova.it
+go.it
+gorizia.it
+gr.it
+grosseto.it
+iglesias-carbonia.it
+iglesiascarbonia.it
+im.it
+imperia.it
+is.it
+isernia.it
+kr.it
+la-spezia.it
+laquila.it
+laspezia.it
+latina.it
+lc.it
+le.it
+lecce.it
+lecco.it
+li.it
+livorno.it
+lo.it
+lodi.it
+lt.it
+lu.it
+lucca.it
+macerata.it
+mantova.it
+massa-carrara.it
+massacarrara.it
+matera.it
+mb.it
+mc.it
+me.it
+medio-campidano.it
+mediocampidano.it
+messina.it
+mi.it
+milan.it
+milano.it
+mn.it
+mo.it
+modena.it
+monza-brianza.it
+monza-e-della-brianza.it
+monza.it
+monzabrianza.it
+monzaebrianza.it
+monzaedellabrianza.it
+ms.it
+mt.it
+na.it
+naples.it
+napoli.it
+no.it
+novara.it
+nu.it
+nuoro.it
+og.it
+ogliastra.it
+olbia-tempio.it
+olbiatempio.it
+or.it
+oristano.it
+ot.it
+pa.it
+padova.it
+padua.it
+palermo.it
+parma.it
+pavia.it
+pc.it
+pd.it
+pe.it
+perugia.it
+pesaro-urbino.it
+pesarourbino.it
+pescara.it
+pg.it
+pi.it
+piacenza.it
+pisa.it
+pistoia.it
+pn.it
+po.it
+pordenone.it
+potenza.it
+pr.it
+prato.it
+pt.it
+pu.it
+pv.it
+pz.it
+ra.it
+ragusa.it
+ravenna.it
+rc.it
+re.it
 reggio-calabria.it
-reggio-emilia.it 
-reggiocalabria.it 
-reggioemilia.it 
-rg.it 
-ri.it 
-rieti.it 
-rimini.it 
-rm.it 
-rn.it 
-ro.it 
-roma.it 
-rome.it 
-rovigo.it 
-sa.it 
-salerno.it 
-sassari.it 
-savona.it 
-si.it 
-siena.it 
-siracusa.it 
-so.it 
-sondrio.it 
-sp.it 
-sr.it 
-ss.it 
-suedtirol.it 
-sv.it 
-ta.it 
-taranto.it 
-te.it 
-tempio-olbia.it 
-tempioolbia.it 
-teramo.it 
-terni.it 
-tn.it 
-to.it 
-torino.it 
-tp.it 
-tr.it 
-trani-andria-barletta.it 
-trani-barletta-andria.it 
-traniandriabarletta.it 
-tranibarlettaandria.it 
-trapani.it 
-trentino.it 
-trento.it 
-treviso.it 
-trieste.it 
-ts.it 
-turin.it 
-tv.it 
-ud.it 
-udine.it 
-urbino-pesaro.it 
-urbinopesaro.it 
-va.it 
-varese.it 
-vb.it 
-vc.it 
-ve.it 
-venezia.it 
-venice.it 
-verbania.it 
-vercelli.it 
-verona.it 
-vi.it 
-vibo-valentia.it 
-vibovalentia.it 
-vicenza.it 
-viterbo.it 
-vr.it 
-vs.it 
-vt.it 
-vv.it 
+reggio-emilia.it
+reggiocalabria.it
+reggioemilia.it
+rg.it
+ri.it
+rieti.it
+rimini.it
+rm.it
+rn.it
+ro.it
+roma.it
+rome.it
+rovigo.it
+sa.it
+salerno.it
+sassari.it
+savona.it
+si.it
+siena.it
+siracusa.it
+so.it
+sondrio.it
+sp.it
+sr.it
+ss.it
+suedtirol.it
+sv.it
+ta.it
+taranto.it
+te.it
+tempio-olbia.it
+tempioolbia.it
+teramo.it
+terni.it
+tn.it
+to.it
+torino.it
+tp.it
+tr.it
+trani-andria-barletta.it
+trani-barletta-andria.it
+traniandriabarletta.it
+tranibarlettaandria.it
+trapani.it
+trentino.it
+trento.it
+treviso.it
+trieste.it
+ts.it
+turin.it
+tv.it
+ud.it
+udine.it
+urbino-pesaro.it
+urbinopesaro.it
+va.it
+varese.it
+vb.it
+vc.it
+ve.it
+venezia.it
+venice.it
+verbania.it
+vercelli.it
+verona.it
+vi.it
+vibo-valentia.it
+vibovalentia.it
+vicenza.it
+viterbo.it
+vr.it
+vs.it
+vt.it
+vv.it
 
 // je : http://www.channelisles.net/register-domains/
 // Confirmed by registry <nigel@channelisles.net> 2013-11-28
 je
 co.je
 net.je
 org.je
 
@@ -7637,26 +7637,26 @@ gripe
 engineering
 
 // associates : 2014-03-07 Baxter Hill, LLC
 associates
 
 // xn--mxtq1m : 2014-03-07 Net-Chinese Co., Ltd.
 政府
 
-// williamhill : 2014-03-13 William Hill Organization Limited 
+// williamhill : 2014-03-13 William Hill Organization Limited
 williamhill
 
-// hiv : 2014-03-13 dotHIV gemeinnuetziger e.V. 
+// hiv : 2014-03-13 dotHIV gemeinnuetziger e.V.
 hiv
 
-// sca : 2014-03-13 SVENSKA CELLULOSA AKTIEBOLAGET SCA (publ) 
+// sca : 2014-03-13 SVENSKA CELLULOSA AKTIEBOLAGET SCA (publ)
 sca
 
-// reise : 2014-03-13 dotreise GmbH 
+// reise : 2014-03-13 dotreise GmbH
 reise
 
 
 // ===END ICANN DOMAINS===
 // ===BEGIN PRIVATE DOMAINS===
 
 // Amazon CloudFront : https://aws.amazon.com/cloudfront/
 // Submitted by Donavan Miller <donavanm@amazon.com> 2013-03-22
--- a/netwerk/protocol/http/Http2Compression.cpp
+++ b/netwerk/protocol/http/Http2Compression.cpp
@@ -493,130 +493,113 @@ Http2Decompressor::CopyStringFromInput(u
   mOffset += bytes;
   return NS_OK;
 }
 
 nsresult
 Http2Decompressor::DecodeFinalHuffmanCharacter(HuffmanIncomingTable *table,
                                                uint8_t &c, uint8_t &bitsLeft)
 {
-  uint8_t idxLen = table->mPrefixLen;
   uint8_t mask = (1 << bitsLeft) - 1;
   uint8_t idx = mData[mOffset - 1] & mask;
-  if (idxLen < bitsLeft) {
-    mask &= ~((1 << (bitsLeft - idxLen)) - 1);
-    idx &= mask;
-    idx >>= (bitsLeft - idxLen);
-    idx &= ((1 << idxLen) - 1);
-  } else {
-    idx <<= (idxLen - bitsLeft);
-  }
+  idx <<= (8 - bitsLeft);
   // Don't update bitsLeft yet, because we need to check that value against the
   // number of bits used by our encoding later on. We'll update when we are sure
   // how many bits we've actually used.
 
-  if (table->mEntries[idx].mPtr) {
+  HuffmanIncomingEntry *entry = &(table->mEntries[idx]);
+
+  if (entry->mPtr) {
     // Can't chain to another table when we're all out of bits in the encoding
     LOG3(("DecodeFinalHuffmanCharacter trying to chain when we're out of bits"));
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
-  if (bitsLeft < table->mEntries[idx].mPrefixLen) {
+  if (bitsLeft < entry->mPrefixLen) {
     // We don't have enough bits to actually make a match, this is some sort of
     // invalid coding
     LOG3(("DecodeFinalHuffmanCharacter does't have enough bits to match"));
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
   // This is a character!
-  if (table->mEntries[idx].mValue == 256) {
+  if (entry->mValue == 256) {
     // EOS
     LOG3(("DecodeFinalHuffmanCharacter actually decoded an EOS"));
     return NS_ERROR_ILLEGAL_VALUE;
   }
-  c = static_cast<uint8_t>(table->mEntries[idx].mValue & 0xFF);
-  bitsLeft -= table->mEntries[idx].mPrefixLen;
+  c = static_cast<uint8_t>(entry->mValue & 0xFF);
+  bitsLeft -= entry->mPrefixLen;
 
   return NS_OK;
 }
 
+uint8_t
+Http2Decompressor::ExtractByte(uint8_t bitsLeft, uint32_t &bytesConsumed)
+{
+  uint8_t rv;
+
+  if (bitsLeft) {
+    // Need to extract bitsLeft bits from the previous byte, and 8 - bitsLeft
+    // bits from the current byte
+    uint8_t mask = (1 << bitsLeft) - 1;
+    rv = (mData[mOffset - 1] & mask) << (8 - bitsLeft);
+    rv |= (mData[mOffset] & ~mask) >> bitsLeft;
+  } else {
+    rv = mData[mOffset];
+  }
+
+  // We always update these here, under the assumption that all 8 bits we got
+  // here will be used. These may be re-adjusted later in the case that we don't
+  // use up all 8 bits of the byte.
+  ++mOffset;
+  ++bytesConsumed;
+
+  return rv;
+}
+
 nsresult
 Http2Decompressor::DecodeHuffmanCharacter(HuffmanIncomingTable *table,
                                           uint8_t &c, uint32_t &bytesConsumed,
                                           uint8_t &bitsLeft)
 {
-  uint8_t idxLen = table->mPrefixLen;
-  uint8_t idx;
-  uint8_t mask;
+  uint8_t idx = ExtractByte(bitsLeft, bytesConsumed);
+  HuffmanIncomingEntry *entry = &(table->mEntries[idx]);
 
-  if (idxLen < bitsLeft) {
-    // Only need to consume part of the rest of the previous byte
-    mask = (1 << bitsLeft) - 1;
-    bitsLeft -= idxLen;
-    mask &= ~((1 << bitsLeft) - 1);
-    idx = (mData[mOffset - 1] & mask) >> bitsLeft;
-    idx &= ((1 << idxLen) - 1);
-  } else if (bitsLeft) {
-    // Need to consume all of the rest of the previous byte, and possibly some
-    // of the current byte
-    mask = (1 << bitsLeft) - 1;
-    idxLen -= bitsLeft;
-    idx = (mData[mOffset - 1] & mask) << idxLen;
-    bitsLeft = 0;
-    if (idxLen) {
-      // Need to consume some of the current byte
-      bitsLeft = 8 - idxLen;
-      mask = ~((1 << bitsLeft) - 1);
-      uint8_t lastBits = (mData[mOffset] & mask) >> bitsLeft;
-      idx |= (lastBits & ((1 << idxLen) - 1));
-      bytesConsumed++;
-      mOffset++;
-    }
-  } else {
-    // byte-aligned already
-    mask = (1 << 8) - 1;
-    bitsLeft = 8 - idxLen;
-    mask &= ~((1 << bitsLeft) - 1);
-    idx = (mData[mOffset] & mask) >> bitsLeft;
-    idx &= ((1 << idxLen) - 1);
-    bytesConsumed++;
-    mOffset++;
-  }
-
-  if (table->mEntries[idx].mPtr) {
+  if (entry->mPtr) {
     if (bytesConsumed >= mDataLen) {
       if (!bitsLeft || (bytesConsumed > mDataLen)) {
+        // TODO - does this get me into trouble in the new world?
         // No info left in input to try to consume, we're done
         LOG3(("DecodeHuffmanCharacter all out of bits to consume, can't chain"));
         return NS_ERROR_ILLEGAL_VALUE;
       }
 
       // We might get lucky here!
-      return DecodeFinalHuffmanCharacter(table->mEntries[idx].mPtr, c,
-                                         bitsLeft);
+      return DecodeFinalHuffmanCharacter(entry->mPtr, c, bitsLeft);
     }
 
     // We're sorry, Mario, but your princess is in another castle
-    return DecodeHuffmanCharacter(table->mEntries[idx].mPtr, c, bytesConsumed,
-                                  bitsLeft);
+    return DecodeHuffmanCharacter(entry->mPtr, c, bytesConsumed, bitsLeft);
   }
 
-  if (table->mEntries[idx].mValue == 256) {
+  if (entry->mValue == 256) {
     LOG3(("DecodeHuffmanCharacter found an actual EOS"));
     return NS_ERROR_ILLEGAL_VALUE;
   }
-  c = static_cast<uint8_t>(table->mEntries[idx].mValue & 0xFF);
+  c = static_cast<uint8_t>(entry->mValue & 0xFF);
 
   // Need to adjust bitsLeft (and possibly other values) because we may not have
-  // consumed all of the bits that the table requires for indexing.
-  bitsLeft += (table->mPrefixLen - table->mEntries[idx].mPrefixLen);
-  if (bitsLeft >= 8) {
-    mOffset--;
-    bytesConsumed--;
-    bitsLeft -= 8;
+  // consumed all of the bits of the byte we extracted.
+  if (entry->mPrefixLen <= bitsLeft) {
+    bitsLeft -= entry->mPrefixLen;
+    --mOffset;
+    --bytesConsumed;
+  } else {
+    bitsLeft = 8 - (entry->mPrefixLen - bitsLeft);
   }
   MOZ_ASSERT(bitsLeft < 8);
 
   return NS_OK;
 }
 
 nsresult
 Http2Decompressor::CopyHuffmanStringFromInput(uint32_t bytes, nsACString &val)
@@ -706,20 +689,33 @@ Http2Decompressor::DoIndexed()
   uint32_t index;
   nsresult rv = DecodeInteger(7, index);
   if (NS_FAILED(rv))
     return rv;
 
   LOG3(("HTTP decompressor indexed entry %u\n", index));
 
   if (index == 0) {
-    // Index 0 is a special case - it clear out the reference set
-    mReferenceSet.Clear();
-    mAlternateReferenceSet.Clear();
-    return NS_OK;
+    // Index 0 is a special case - it has extra data tacked on the end to
+    // determine what kind of change to make to the encoding context.
+    //
+    if (mData[mOffset] & 0x80) {
+      // This means we have to clear out the reference set
+      mReferenceSet.Clear();
+      mAlternateReferenceSet.Clear();
+      ++mOffset;
+      return NS_OK;
+    }
+
+    // Getting here means we have to adjust the max table size
+    uint32_t newMaxSize;
+    rv = DecodeInteger(7, newMaxSize);
+    if (NS_FAILED(rv))
+      return rv;
+    return mCompressor->SetMaxBufferSizeInternal(newMaxSize);
   }
   index--; // Internally, we 0-index everything, since this is, y'know, C++
 
   // Toggle this in the reference set..
   // if its not currently in the reference set then add it and
   // also emit it. If it