Merge mozilla-central to autoland
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 20 Feb 2017 14:12:30 +0100
changeset 372896 33cca98b18813c1ecc04aaad1030bc10ca9660a1
parent 372895 d32422be2613db9595365a6d9142375d1ededac4 (current diff)
parent 372875 24931c1b6d9e5c0b1e496a2428f2012428715956 (diff)
child 372897 139038cf6a9c8f0ddcb83dbbab29b33d146c67b4
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone54.0a1
Merge mozilla-central to autoland
dom/canvas/test/chrome/nonchrome_webgl_debug_renderer_info.html
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
js/src/jit-test/tests/wasm/spec/soft-fail.wast
js/src/jit-test/tests/wasm/spec/soft-fail.wast.js
js/src/jit-test/tests/wasm/unreachable.js
layout/base/nsRefreshDriver.cpp
modules/libpref/init/all.js
netwerk/base/nsNetUtil.cpp
toolkit/locales/en-US/chrome/global/dateFormat.properties
tools/profiler/core/platform-linux.cc
tools/profiler/core/platform-macos.cc
tools/profiler/core/platform-win32.cc
--- a/browser/components/extensions/test/browser/head.js
+++ b/browser/components/extensions/test/browser/head.js
@@ -21,17 +21,16 @@ const {AppConstants} = Cu.import("resour
 const {CustomizableUI} = Cu.import("resource:///modules/CustomizableUI.jsm", {});
 
 // We run tests under two different configurations, from browser.ini and
 // browser-remote.ini. When running from browser-remote.ini, the tests are
 // copied to the sub-directory "test-oop-extensions", which we detect here, and
 // use to select our configuration.
 if (gTestPath.includes("test-oop-extensions")) {
   SpecialPowers.pushPrefEnv({set: [
-    ["dom.ipc.processCount.extension", 1],
     ["extensions.webextensions.remote", true],
   ]});
   // We don't want to reset this at the end of the test, so that we don't have
   // to spawn a new extension child process for each test unit.
   SpecialPowers.setIntPref("dom.ipc.keepProcessesAlive.extension", 1);
 }
 
 // Bug 1239884: Our tests occasionally hit a long GC pause at unpredictable
--- a/browser/components/migration/tests/marionette/test_refresh_firefox.py
+++ b/browser/components/migration/tests/marionette/test_refresh_firefox.py
@@ -1,13 +1,14 @@
 import os
 import shutil
 import time
 
 from marionette_harness import MarionetteTestCase
+from marionette_driver.errors import NoAlertPresentException
 
 
 class TestFirefoxRefresh(MarionetteTestCase):
     _username = "marionette-test-login"
     _password = "marionette-test-password"
     _bookmarkURL = "about:mozilla"
     _bookmarkText = "Some bookmark from Marionette"
 
@@ -264,28 +265,45 @@ class TestFirefoxRefresh(MarionetteTestC
         self.assertEqual(cookieInfo['name'], self._cookieName)
 
     def checkSession(self):
         tabURIs = self.runCode("""
           return [... gBrowser.browsers].map(b => b.currentURI && b.currentURI.spec)
         """)
         self.assertSequenceEqual(tabURIs, ["about:welcomeback"])
 
+        # Dismiss modal dialog if any. This is mainly to dismiss the check for
+        # default browser dialog if it shows up.
+        try:
+          alert = self.marionette.switch_to_alert()
+          alert.dismiss()
+        except NoAlertPresentException:
+          pass
+
         tabURIs = self.runAsyncCode("""
           let mm = gBrowser.selectedBrowser.messageManager;
-          let fs = function() {
-            content.document.getElementById("errorTryAgain").click();
-          };
+
           let {TabStateFlusher} = Cu.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
           window.addEventListener("SSWindowStateReady", function testSSPostReset() {
             window.removeEventListener("SSWindowStateReady", testSSPostReset, false);
             Promise.all(gBrowser.browsers.map(b => TabStateFlusher.flush(b))).then(function() {
               marionetteScriptFinished([... gBrowser.browsers].map(b => b.currentURI && b.currentURI.spec));
             });
           }, false);
+
+          let fs = function() {
+            if (content.document.readyState === "complete") {
+              content.document.getElementById("errorTryAgain").click();
+            } else {
+              content.window.addEventListener("load", function(event) {
+                content.document.getElementById("errorTryAgain").click();
+              }, { once: true });
+            }
+          };
+
           mm.loadFrameScript("data:application/javascript,(" + fs.toString() + ")()", true);
         """)
         self.assertSequenceEqual(tabURIs, self._expectedURLs)
 
     def checkProfile(self, hasMigrated=False):
         self.checkPassword()
         self.checkBookmark()
         self.checkHistory()
--- a/browser/extensions/aushelper/bootstrap.js
+++ b/browser/extensions/aushelper/bootstrap.js
@@ -3,32 +3,58 @@
  * 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/. */
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 const APP_UPDATE_URL_PREF = "app.update.url";
 const REPLACE_KEY = "%OS_VERSION%";
 
+const AUSHELPER_CPU_RESULT_CODE_HISTOGRAM_ID = "AUSHELPER_CPU_RESULT_CODE";
+// The system is not vulnerable to Bug 1296630.
+const CPU_NO_BUG1296630 = 1;
+// The system is vulnerable to Bug 1296630.
+const CPU_YES_BUG1296630 = 2;
+// An error occured when checking if the system is vulnerable to Bug 1296630.
+const CPU_ERR_BUG1296630 = 3;
+// It is unknown whether the system is vulnerable to Bug 1296630 (should never happen).
+const CPU_UNKNOWN_BUG1296630 = 4;
+
+const AUSHELPER_CPU_ERROR_CODE_HISTOGRAM_ID = "AUSHELPER_CPU_ERROR_CODE";
+const CPU_SUCCESS = 0;
+const CPU_REG_OPEN_ERROR = 1;
+const CPU_VENDOR_ID_ERROR = 2;
+const CPU_ID_ERROR = 4;
+const CPU_REV_ERROR = 8;
+
+const AUSHELPER_WEBSENSE_REG_VERSION_SCALAR_NAME = "aushelper.websense_reg_version";
+const AUSHELPER_WEBSENSE_REG_EXISTS_HISTOGRAM_ID = "AUSHELPER_WEBSENSE_REG_EXISTS";
+
+const AUSHELPER_WEBSENSE_ERROR_CODE_HISTOGRAM_ID = "AUSHELPER_WEBSENSE_ERROR_CODE";
+const WEBSENSE_SUCCESS = 0;
+const WEBSENSE_REG_OPEN_ERROR = 1;
+const WEBSENSE_REG_READ_ERROR = 2;
+const WEBSENSE_ALREADY_MODIFIED = 4;
+
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/TelemetryLog.jsm");
 
 function startup() {
   if (Services.appinfo.OS != "WINNT") {
     return;
   }
 
   const regCPUPath = "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
   let wrk;
+  let cpuErrorCode = CPU_SUCCESS;
   try {
     wrk = Cc["@mozilla.org/windows-registry-key;1"].createInstance(Ci.nsIWindowsRegKey);
     wrk.open(wrk.ROOT_KEY_LOCAL_MACHINE, regCPUPath, wrk.ACCESS_READ);
   } catch (e) {
-    Cu.reportError("Unable to open registry. Exception: " + e);
-    TelemetryLog.log("AUSHELPER_FATAL_ERROR", [e]);
+    Cu.reportError("AUSHelper - unable to open registry. Exception: " + e);
+    cpuErrorCode |= CPU_REG_OPEN_ERROR;
   }
 
   // If any of the following values are successfully retrieved and they don't
   // match the condition for that value then it is safe to update. Hence why the
   // following checks are somewhat convoluted. The possible values for the
   // variable set by each check is as follows:
   //
   //          | Match | No Match | Error |
@@ -36,44 +62,44 @@ function startup() {
 
   let cpuVendorIDMatch = false;
   try {
     let cpuVendorID = wrk.readStringValue("VendorIdentifier");
     if (cpuVendorID.toLowerCase() == "genuineintel") {
       cpuVendorIDMatch = true;
     }
   } catch (e) {
+    Cu.reportError("AUSHelper - error getting CPU vendor indentifier. Exception: " + e);
     cpuVendorIDMatch = null;
-    Cu.reportError("Error getting CPU vendor indentifier. Exception: " + e);
-    TelemetryLog.log("AUSHELPER_CPU_VENDOR_ID_ERROR", [e]);
+    cpuErrorCode |= CPU_VENDOR_ID_ERROR;
   }
 
   let cpuIDMatch = false;
   try {
     let cpuID = wrk.readStringValue("Identifier");
     if (cpuID.toLowerCase().indexOf("family 6 model 61 stepping 4") != -1) {
       cpuIDMatch = true;
     }
   } catch (e) {
+    Cu.reportError("AUSHelper - error getting CPU indentifier. Exception: " + e);
     cpuIDMatch = null;
-    Cu.reportError("Error getting CPU indentifier. Exception: " + e);
-    TelemetryLog.log("AUSHELPER_CPU_ID_ERROR", [e]);
+    cpuErrorCode |= CPU_ID_ERROR;
   }
 
   let microCodeVersions = [0xe, 0x11, 0x12, 0x13, 0x16, 0x18, 0x19];
   let cpuRevMatch = null;
   try {
     let keyNames = ["Update Revision", "Update Signature"];
     for (let i = 0; i < keyNames.length; ++i) {
       try {
         let regVal = wrk.readBinaryValue(keyNames[i]);
         if (regVal.length == 8) {
           let hexVal = [];
-          // We are only inyterested in the highest byte and return the little
-          // endian value for it.
+          // We are only inyterested in the upper 4 bytes and the little endian
+          // value for it.
           for (let j = 4; j < 8; j++) {
             let c = regVal.charCodeAt(j).toString(16);
             if (c.length == 1) {
               c = "0" + c;
             }
             hexVal.unshift(c);
           }
           cpuRevMatch = false;
@@ -81,49 +107,83 @@ function startup() {
             cpuRevMatch = true;
           }
           break;
         }
       } catch (e) {
         if (i == keyNames.length - 1) {
           // The registry key name's value was not successfully queried.
           cpuRevMatch = null;
-          TelemetryLog.log("AUSHELPER_CPU_REV_ERROR", [e]);
+          cpuErrorCode |= CPU_REV_ERROR;
         }
       }
     }
+    wrk.close();
   } catch (ex) {
+    Cu.reportError("AUSHelper - error getting CPU revision. Exception: " + ex);
     cpuRevMatch = null;
-    Cu.reportError("Error getting CPU revision. Exception: " + ex);
-    TelemetryLog.log("AUSHELPER_CPU_REV_ERROR", [ex]);
+    cpuErrorCode |= CPU_REV_ERROR;
   }
 
-  let resultCode = 3;
-  let newValue = "(unkBug1296630v1)";
+  let cpuResult = CPU_UNKNOWN_BUG1296630;
+  let cpuValue = "(unkBug1296630v1)";
   // The following uses strict equality checks since the values can be true,
   // false, or null.
   if (cpuVendorIDMatch === false || cpuIDMatch === false || cpuRevMatch === false) {
     // Since one of the values is false then the system won't be affected by
     // bug 1296630 according to the conditions set out in bug 1311515.
-    newValue = "(noBug1296630v1)";
-    resultCode = 0;
+    cpuValue = "(noBug1296630v1)";
+    cpuResult = CPU_NO_BUG1296630;
   } else if (cpuVendorIDMatch === null || cpuIDMatch === null || cpuRevMatch === null) {
     // Since one of the values is null we can't say for sure if the system will
     // be affected by bug 1296630.
-    newValue = "(errBug1296630v1)";
-    resultCode = 2;
+    cpuValue = "(errBug1296630v1)";
+    cpuResult = CPU_ERR_BUG1296630;
   } else if (cpuVendorIDMatch === true && cpuIDMatch === true && cpuRevMatch === true) {
     // Since all of the values are true we can say that the system will be
     // affected by bug 1296630.
-    newValue = "(yesBug1296630v1)";
-    resultCode = 1;
+    cpuValue = "(yesBug1296630v1)";
+    cpuResult = CPU_YES_BUG1296630;
   }
 
-  let defaultBranch = Services.prefs.getDefaultBranch("");
-  let curPrefValue = defaultBranch.getCharPref(APP_UPDATE_URL_PREF);
-  let newPrefValue = curPrefValue.replace(REPLACE_KEY + "/", REPLACE_KEY + newValue + "/");
-  defaultBranch.setCharPref(APP_UPDATE_URL_PREF, newPrefValue);
-  TelemetryLog.log("AUSHELPER_RESULT", [resultCode]);
+  Services.telemetry.getHistogramById(AUSHELPER_CPU_RESULT_CODE_HISTOGRAM_ID).add(cpuResult);
+  Services.telemetry.getHistogramById(AUSHELPER_CPU_ERROR_CODE_HISTOGRAM_ID).add(cpuErrorCode);
+
+  const regWebsensePath = "Websense\\Agent";
+  let websenseErrorCode = WEBSENSE_SUCCESS;
+  let websenseVersion = "";
+  try {
+    let regModes = [wrk.ACCESS_READ, wrk.ACCESS_READ | wrk.WOW64_64];
+    for (let i = 0; i < regModes.length; ++i) {
+      wrk.open(wrk.ROOT_KEY_LOCAL_MACHINE, "SOFTWARE", regModes[i]);
+      try {
+        if (wrk.hasChild(regWebsensePath)) {
+          let childKey = wrk.openChild(regWebsensePath, wrk.ACCESS_READ);
+          websenseVersion = childKey.readStringValue("InstallVersion");
+          Services.telemetry.scalarSet(AUSHELPER_WEBSENSE_REG_VERSION_SCALAR_NAME, websenseVersion);
+        }
+        wrk.close();
+      } catch (e) {
+        Cu.reportError("AUSHelper - unable to read registry. Exception: " + e);
+        websenseErrorCode |= WEBSENSE_REG_READ_ERROR;
+      }
+    }
+  } catch (ex) {
+    Cu.reportError("AUSHelper - unable to open registry. Exception: " + ex);
+    websenseErrorCode |= WEBSENSE_REG_OPEN_ERROR;
+  }
+
+  Services.telemetry.getHistogramById(AUSHELPER_WEBSENSE_REG_EXISTS_HISTOGRAM_ID).add(!!websenseVersion);
+  let websenseValue = "(" + (websenseVersion ? "websense-" + websenseVersion : "nowebsense") + ")";
+
+  let branch = Services.prefs.getDefaultBranch("");
+  let curValue = branch.getCharPref(APP_UPDATE_URL_PREF);
+  if (curValue.indexOf(REPLACE_KEY + "/") > -1) {
+    let newValue = curValue.replace(REPLACE_KEY + "/", REPLACE_KEY + cpuValue + websenseValue + "/");
+    branch.setCharPref(APP_UPDATE_URL_PREF, newValue);
+  } else {
+    websenseErrorCode |= WEBSENSE_ALREADY_MODIFIED;
+  }
+  Services.telemetry.getHistogramById(AUSHELPER_WEBSENSE_ERROR_CODE_HISTOGRAM_ID).add(websenseErrorCode);
 }
-
 function shutdown() {}
 function install() {}
 function uninstall() {}
--- a/browser/extensions/aushelper/install.rdf.in
+++ b/browser/extensions/aushelper/install.rdf.in
@@ -5,17 +5,17 @@
 
 #filter substitution
 
 <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
      xmlns:em="http://www.mozilla.org/2004/em-rdf#">
 
   <Description about="urn:mozilla:install-manifest">
     <em:id>aushelper@mozilla.org</em:id>
-    <em:version>1.0</em:version>
+    <em:version>2.0</em:version>
     <em:type>2</em:type>
     <em:bootstrap>true</em:bootstrap>
     <em:multiprocessCompatible>true</em:multiprocessCompatible>
 
     <!-- Target Application this extension can install into,
         with minimum and maximum supported versions. -->
     <em:targetApplication>
       <Description>
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,3 +1,3 @@
 This is the pdf.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 1.7.290
+Current extension version is: 1.7.297
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -3627,18 +3627,18 @@ var _UnsupportedManager = function Unsup
   },
   notify: function (featureId) {
    for (var i = 0, ii = listeners.length; i < ii; i++) {
     listeners[i](featureId);
    }
   }
  };
 }();
-exports.version = '1.7.290';
-exports.build = 'b509a3f8';
+exports.version = '1.7.297';
+exports.build = '425ad309';
 exports.getDocument = getDocument;
 exports.PDFDataRangeTransport = PDFDataRangeTransport;
 exports.PDFWorker = PDFWorker;
 exports.PDFDocumentProxy = PDFDocumentProxy;
 exports.PDFPageProxy = PDFPageProxy;
 exports._UnsupportedManager = _UnsupportedManager;
 
 /***/ }),
@@ -4645,18 +4645,18 @@ var deprecated = sharedUtil.deprecated;
 var warn = sharedUtil.warn;
 var LinkTarget = displayDOMUtils.LinkTarget;
 var DEFAULT_LINK_REL = displayDOMUtils.DEFAULT_LINK_REL;
 var isWorker = typeof window === 'undefined';
 if (!globalScope.PDFJS) {
  globalScope.PDFJS = {};
 }
 var PDFJS = globalScope.PDFJS;
-PDFJS.version = '1.7.290';
-PDFJS.build = 'b509a3f8';
+PDFJS.version = '1.7.297';
+PDFJS.build = '425ad309';
 PDFJS.pdfBug = false;
 if (PDFJS.verbosity !== undefined) {
  sharedUtil.setVerbosityLevel(PDFJS.verbosity);
 }
 delete PDFJS.verbosity;
 Object.defineProperty(PDFJS, 'verbosity', {
  get: function () {
   return sharedUtil.getVerbosityLevel();
@@ -7138,18 +7138,18 @@ exports.getShadingPatternFromIR = getSha
 exports.TilingPattern = TilingPattern;
 
 /***/ }),
 /* 13 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
-var pdfjsVersion = '1.7.290';
-var pdfjsBuild = 'b509a3f8';
+var pdfjsVersion = '1.7.297';
+var pdfjsBuild = '425ad309';
 var pdfjsSharedUtil = __w_pdfjs_require__(0);
 var pdfjsDisplayGlobal = __w_pdfjs_require__(8);
 var pdfjsDisplayAPI = __w_pdfjs_require__(3);
 var pdfjsDisplayTextLayer = __w_pdfjs_require__(5);
 var pdfjsDisplayAnnotationLayer = __w_pdfjs_require__(2);
 var pdfjsDisplayDOMUtils = __w_pdfjs_require__(1);
 var pdfjsDisplaySVG = __w_pdfjs_require__(4);
 exports.PDFJS = pdfjsDisplayGlobal.PDFJS;
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -83,17 +83,17 @@ return /******/ (function(modules) { // 
 
 /******/ 	// Object.prototype.hasOwnProperty.call
 /******/ 	__w_pdfjs_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
 
 /******/ 	// __webpack_public_path__
 /******/ 	__w_pdfjs_require__.p = "";
 
 /******/ 	// Load entry module and return exports
-/******/ 	return __w_pdfjs_require__(__w_pdfjs_require__.s = 36);
+/******/ 	return __w_pdfjs_require__(__w_pdfjs_require__.s = 35);
 /******/ })
 /************************************************************************/
 /******/ ([
 /* 0 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 /* WEBPACK VAR INJECTION */(function(global) {
@@ -1727,18 +1727,18 @@ exports.isStream = isStream;
 /***/ }),
 /* 2 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 var sharedUtil = __w_pdfjs_require__(0);
 var corePrimitives = __w_pdfjs_require__(1);
-var coreJbig2 = __w_pdfjs_require__(28);
-var coreJpg = __w_pdfjs_require__(29);
+var coreJbig2 = __w_pdfjs_require__(27);
+var coreJpg = __w_pdfjs_require__(28);
 var coreJpx = __w_pdfjs_require__(13);
 var Util = sharedUtil.Util;
 var error = sharedUtil.error;
 var info = sharedUtil.info;
 var isInt = sharedUtil.isInt;
 var isArray = sharedUtil.isArray;
 var createObjectURL = sharedUtil.createObjectURL;
 var shadow = sharedUtil.shadow;
@@ -12352,17 +12352,17 @@ exports.Parser = Parser;
 /***/ }),
 /* 6 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 var sharedUtil = __w_pdfjs_require__(0);
 var corePrimitives = __w_pdfjs_require__(1);
-var corePsParser = __w_pdfjs_require__(34);
+var corePsParser = __w_pdfjs_require__(33);
 var error = sharedUtil.error;
 var info = sharedUtil.info;
 var isArray = sharedUtil.isArray;
 var isBool = sharedUtil.isBool;
 var isDict = corePrimitives.isDict;
 var isStream = corePrimitives.isStream;
 var PostScriptLexer = corePsParser.PostScriptLexer;
 var PostScriptParser = corePsParser.PostScriptParser;
@@ -18274,17 +18274,17 @@ exports.ArithmeticDecoder = ArithmeticDe
 
 /***/ }),
 /* 9 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 var sharedUtil = __w_pdfjs_require__(0);
-var coreCharsets = __w_pdfjs_require__(22);
+var coreCharsets = __w_pdfjs_require__(21);
 var coreEncodings = __w_pdfjs_require__(4);
 var error = sharedUtil.error;
 var info = sharedUtil.info;
 var bytesToString = sharedUtil.bytesToString;
 var warn = sharedUtil.warn;
 var isArray = sharedUtil.isArray;
 var Util = sharedUtil.Util;
 var stringToBytes = sharedUtil.stringToBytes;
@@ -24763,25 +24763,25 @@ exports.calculateSHA512 = calculateSHA51
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 var sharedUtil = __w_pdfjs_require__(0);
 var corePrimitives = __w_pdfjs_require__(1);
 var coreStream = __w_pdfjs_require__(2);
 var coreParser = __w_pdfjs_require__(5);
-var coreImage = __w_pdfjs_require__(27);
+var coreImage = __w_pdfjs_require__(26);
 var coreColorSpace = __w_pdfjs_require__(3);
-var coreMurmurHash3 = __w_pdfjs_require__(31);
-var coreFonts = __w_pdfjs_require__(26);
+var coreMurmurHash3 = __w_pdfjs_require__(30);
+var coreFonts = __w_pdfjs_require__(25);
 var coreFunction = __w_pdfjs_require__(6);
-var corePattern = __w_pdfjs_require__(32);
-var coreCMap = __w_pdfjs_require__(23);
-var coreMetrics = __w_pdfjs_require__(30);
-var coreBidi = __w_pdfjs_require__(21);
+var corePattern = __w_pdfjs_require__(31);
+var coreCMap = __w_pdfjs_require__(22);
+var coreMetrics = __w_pdfjs_require__(29);
+var coreBidi = __w_pdfjs_require__(20);
 var coreEncodings = __w_pdfjs_require__(4);
 var coreStandardFonts = __w_pdfjs_require__(15);
 var coreUnicode = __w_pdfjs_require__(16);
 var coreGlyphList = __w_pdfjs_require__(7);
 var FONT_IDENTITY_MATRIX = sharedUtil.FONT_IDENTITY_MATRIX;
 var IDENTITY_MATRIX = sharedUtil.IDENTITY_MATRIX;
 var UNSUPPORTED_FEATURES = sharedUtil.UNSUPPORTED_FEATURES;
 var ImageKind = sharedUtil.ImageKind;
@@ -33901,20 +33901,20 @@ exports.getUnicodeRangeFor = getUnicodeR
 exports.getNormalizedUnicodes = getNormalizedUnicodes;
 exports.getUnicodeForGlyph = getUnicodeForGlyph;
 
 /***/ }),
 /* 17 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
-/* WEBPACK VAR INJECTION */(function(module) {
+
 var sharedUtil = __w_pdfjs_require__(0);
 var corePrimitives = __w_pdfjs_require__(1);
-var corePdfManager = __w_pdfjs_require__(33);
+var corePdfManager = __w_pdfjs_require__(32);
 var UNSUPPORTED_FEATURES = sharedUtil.UNSUPPORTED_FEATURES;
 var InvalidPDFException = sharedUtil.InvalidPDFException;
 var MessageHandler = sharedUtil.MessageHandler;
 var MissingPDFException = sharedUtil.MissingPDFException;
 var UnexpectedResponseException = sharedUtil.UnexpectedResponseException;
 var PasswordException = sharedUtil.PasswordException;
 var UnknownErrorException = sharedUtil.UnknownErrorException;
 var XRefParseException = sharedUtil.XRefParseException;
@@ -34583,23 +34583,28 @@ var WorkerMessageHandler = {
   return workerHandlerName;
  }
 };
 function initializeWorker() {
  var handler = new MessageHandler('worker', 'main', self);
  WorkerMessageHandler.setup(handler, self);
  handler.send('ready', null);
 }
-if (typeof window === 'undefined' && !(typeof module !== 'undefined' && module.require)) {
+function isNodeJS() {
+ if (typeof __pdfjsdev_webpack__ === 'undefined') {
+  return typeof process === 'object' && process + '' === '[object process]';
+ }
+ return false;
+}
+if (typeof window === 'undefined' && !isNodeJS()) {
  initializeWorker();
 }
 exports.setPDFNetworkStreamClass = setPDFNetworkStreamClass;
 exports.WorkerTask = WorkerTask;
 exports.WorkerMessageHandler = WorkerMessageHandler;
-/* WEBPACK VAR INJECTION */}.call(exports, __w_pdfjs_require__(19)(module)))
 
 /***/ }),
 /* 18 */
 /***/ (function(module, exports) {
 
 var g;
 g = function () {
  return this;
@@ -34609,44 +34614,16 @@ try {
 } catch (e) {
  if (typeof window === "object")
   g = window;
 }
 module.exports = g;
 
 /***/ }),
 /* 19 */
-/***/ (function(module, exports) {
-
-module.exports = function (module) {
- if (!module.webpackPolyfill) {
-  module.deprecate = function () {
-  };
-  module.paths = [];
-  if (!module.children)
-   module.children = [];
-  Object.defineProperty(module, "loaded", {
-   enumerable: true,
-   get: function () {
-    return module.l;
-   }
-  });
-  Object.defineProperty(module, "id", {
-   enumerable: true,
-   get: function () {
-    return module.i;
-   }
-  });
-  module.webpackPolyfill = 1;
- }
- return module;
-};
-
-/***/ }),
-/* 20 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 var sharedUtil = __w_pdfjs_require__(0);
 var corePrimitives = __w_pdfjs_require__(1);
 var coreStream = __w_pdfjs_require__(2);
 var coreColorSpace = __w_pdfjs_require__(3);
@@ -34662,16 +34639,17 @@ var isArray = sharedUtil.isArray;
 var isInt = sharedUtil.isInt;
 var stringToBytes = sharedUtil.stringToBytes;
 var stringToPDFString = sharedUtil.stringToPDFString;
 var warn = sharedUtil.warn;
 var Dict = corePrimitives.Dict;
 var isDict = corePrimitives.isDict;
 var isName = corePrimitives.isName;
 var isRef = corePrimitives.isRef;
+var isStream = corePrimitives.isStream;
 var Stream = coreStream.Stream;
 var ColorSpace = coreColorSpace.ColorSpace;
 var Catalog = coreObj.Catalog;
 var ObjectLoader = coreObj.ObjectLoader;
 var FileSpec = coreObj.FileSpec;
 var OperatorList = coreEvaluator.OperatorList;
 function AnnotationFactory() {
 }
@@ -34755,40 +34733,23 @@ var Annotation = function AnnotationClos
    xRatio,
    0,
    0,
    yRatio,
    rect[0] - minX * xRatio,
    rect[1] - minY * yRatio
   ];
  }
- function getDefaultAppearance(dict) {
-  var appearanceState = dict.get('AP');
-  if (!isDict(appearanceState)) {
-   return;
-  }
-  var appearance;
-  var appearances = appearanceState.get('N');
-  if (isDict(appearances)) {
-   var as = dict.get('AS');
-   if (as && appearances.has(as.name)) {
-    appearance = appearances.get(as.name);
-   }
-  } else {
-   appearance = appearances;
-  }
-  return appearance;
- }
  function Annotation(params) {
   var dict = params.dict;
   this.setFlags(dict.get('F'));
   this.setRectangle(dict.getArray('Rect'));
   this.setColor(dict.getArray('C'));
   this.setBorderStyle(dict);
-  this.appearance = getDefaultAppearance(dict);
+  this.setAppearance(dict);
   this.data = {};
   this.data.id = params.id;
   this.data.subtype = params.subtype;
   this.data.annotationFlags = this.flags;
   this.data.rect = this.rectangle;
   this.data.color = this.color;
   this.data.borderStyle = this.borderStyle;
   this.data.hasAppearance = !!this.appearance;
@@ -34882,16 +34843,36 @@ var Annotation = function AnnotationClos
      if (array.length === 4) {
       this.borderStyle.setDashArray(array[3]);
      }
     }
    } else {
     this.borderStyle.setWidth(0);
    }
   },
+  setAppearance: function Annotation_setAppearance(dict) {
+   this.appearance = null;
+   var appearanceStates = dict.get('AP');
+   if (!isDict(appearanceStates)) {
+    return;
+   }
+   var normalAppearanceState = appearanceStates.get('N');
+   if (isStream(normalAppearanceState)) {
+    this.appearance = normalAppearanceState;
+    return;
+   }
+   if (!isDict(normalAppearanceState)) {
+    return;
+   }
+   var as = dict.get('AS');
+   if (!isName(as) || !normalAppearanceState.has(as.name)) {
+    return;
+   }
+   this.appearance = normalAppearanceState.get(as.name);
+  },
   _preparePopup: function Annotation_preparePopup(dict) {
    if (!dict.has('C')) {
     this.data.color = null;
    }
    this.data.hasPopup = dict.has('Popup');
    this.data.title = stringToPDFString(dict.get('T') || '');
    this.data.contents = stringToPDFString(dict.get('Contents') || '');
   },
@@ -35327,17 +35308,17 @@ var FileAttachmentAnnotation = function 
  Util.inherit(FileAttachmentAnnotation, Annotation, {});
  return FileAttachmentAnnotation;
 }();
 exports.Annotation = Annotation;
 exports.AnnotationBorderStyle = AnnotationBorderStyle;
 exports.AnnotationFactory = AnnotationFactory;
 
 /***/ }),
-/* 21 */
+/* 20 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 var sharedUtil = __w_pdfjs_require__(0);
 var warn = sharedUtil.warn;
 var baseTypes = [
  'BN',
@@ -36080,17 +36061,17 @@ function bidi(str, startLevel, vertical)
    chars[i] = '';
   }
  }
  return createBidiText(chars.join(''), isLTR);
 }
 exports.bidi = bidi;
 
 /***/ }),
-/* 22 */
+/* 21 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 var ISOAdobeCharset = [
  '.notdef',
  'space',
  'exclam',
@@ -36578,17 +36559,17 @@ var ExpertSubsetCharset = [
  'periodinferior',
  'commainferior'
 ];
 exports.ISOAdobeCharset = ISOAdobeCharset;
 exports.ExpertCharset = ExpertCharset;
 exports.ExpertSubsetCharset = ExpertSubsetCharset;
 
 /***/ }),
-/* 23 */
+/* 22 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 var sharedUtil = __w_pdfjs_require__(0);
 var corePrimitives = __w_pdfjs_require__(1);
 var coreStream = __w_pdfjs_require__(2);
 var coreParser = __w_pdfjs_require__(5);
@@ -37483,29 +37464,29 @@ var CMapFactory = function CMapFactoryCl
   }
  };
 }();
 exports.CMap = CMap;
 exports.CMapFactory = CMapFactory;
 exports.IdentityCMap = IdentityCMap;
 
 /***/ }),
-/* 24 */
+/* 23 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 var sharedUtil = __w_pdfjs_require__(0);
 var corePrimitives = __w_pdfjs_require__(1);
 var coreStream = __w_pdfjs_require__(2);
 var coreObj = __w_pdfjs_require__(14);
 var coreParser = __w_pdfjs_require__(5);
 var coreCrypto = __w_pdfjs_require__(11);
 var coreEvaluator = __w_pdfjs_require__(12);
-var coreAnnotation = __w_pdfjs_require__(20);
+var coreAnnotation = __w_pdfjs_require__(19);
 var MissingDataException = sharedUtil.MissingDataException;
 var Util = sharedUtil.Util;
 var assert = sharedUtil.assert;
 var error = sharedUtil.error;
 var info = sharedUtil.info;
 var isArray = sharedUtil.isArray;
 var isArrayBuffer = sharedUtil.isArrayBuffer;
 var isNum = sharedUtil.isNum;
@@ -37983,17 +37964,17 @@ var PDFDocument = function PDFDocumentCl
   }
  };
  return PDFDocument;
 }();
 exports.Page = Page;
 exports.PDFDocument = PDFDocument;
 
 /***/ }),
-/* 25 */
+/* 24 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 var sharedUtil = __w_pdfjs_require__(0);
 var coreStream = __w_pdfjs_require__(2);
 var coreGlyphList = __w_pdfjs_require__(7);
 var coreEncodings = __w_pdfjs_require__(4);
@@ -38769,30 +38750,30 @@ var FontRendererFactory = function FontR
    }
    return new Type2Compiled(cff, cmap, font.fontMatrix, font.glyphNameMap);
   }
  };
 }();
 exports.FontRendererFactory = FontRendererFactory;
 
 /***/ }),
-/* 26 */
+/* 25 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 var sharedUtil = __w_pdfjs_require__(0);
 var corePrimitives = __w_pdfjs_require__(1);
 var coreStream = __w_pdfjs_require__(2);
 var coreGlyphList = __w_pdfjs_require__(7);
-var coreFontRenderer = __w_pdfjs_require__(25);
+var coreFontRenderer = __w_pdfjs_require__(24);
 var coreEncodings = __w_pdfjs_require__(4);
 var coreStandardFonts = __w_pdfjs_require__(15);
 var coreUnicode = __w_pdfjs_require__(16);
-var coreType1Parser = __w_pdfjs_require__(35);
+var coreType1Parser = __w_pdfjs_require__(34);
 var coreCFFParser = __w_pdfjs_require__(9);
 var FONT_IDENTITY_MATRIX = sharedUtil.FONT_IDENTITY_MATRIX;
 var FontType = sharedUtil.FontType;
 var assert = sharedUtil.assert;
 var bytesToString = sharedUtil.bytesToString;
 var error = sharedUtil.error;
 var info = sharedUtil.info;
 var isArray = sharedUtil.isArray;
@@ -39998,17 +39979,17 @@ var Font = function FontClosure() {
      var platformId = font.getUint16();
      var encodingId = font.getUint16();
      var offset = font.getInt32() >>> 0;
      var useTable = false;
      if (platformId === 0 && encodingId === 0) {
       useTable = true;
      } else if (platformId === 1 && encodingId === 0) {
       useTable = true;
-     } else if (platformId === 3 && encodingId === 1 && (!isSymbolicFont && hasEncoding || !potentialTable)) {
+     } else if (platformId === 3 && encodingId === 1 && (hasEncoding || !potentialTable)) {
       useTable = true;
       if (!isSymbolicFont) {
        canBreak = true;
       }
      } else if (isSymbolicFont && platformId === 3 && encodingId === 0) {
       useTable = true;
       canBreak = true;
      }
@@ -41735,17 +41716,17 @@ var CFFFont = function CFFFontClosure() 
 exports.ErrorFont = ErrorFont;
 exports.Font = Font;
 exports.FontFlags = FontFlags;
 exports.IdentityToUnicodeMap = IdentityToUnicodeMap;
 exports.ToUnicodeMap = ToUnicodeMap;
 exports.getFontType = getFontType;
 
 /***/ }),
-/* 27 */
+/* 26 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 var sharedUtil = __w_pdfjs_require__(0);
 var corePrimitives = __w_pdfjs_require__(1);
 var coreColorSpace = __w_pdfjs_require__(3);
 var coreStream = __w_pdfjs_require__(2);
@@ -42226,17 +42207,17 @@ var PDFImage = function PDFImageClosure(
    return this.image.getBytes(length);
   }
  };
  return PDFImage;
 }();
 exports.PDFImage = PDFImage;
 
 /***/ }),
-/* 28 */
+/* 27 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 var sharedUtil = __w_pdfjs_require__(0);
 var coreArithmeticDecoder = __w_pdfjs_require__(8);
 var error = sharedUtil.error;
 var log2 = sharedUtil.log2;
@@ -43427,17 +43408,17 @@ var Jbig2Image = function Jbig2ImageClos
    return parseJbig2Chunks(chunks);
   }
  };
  return Jbig2Image;
 }();
 exports.Jbig2Image = Jbig2Image;
 
 /***/ }),
-/* 29 */
+/* 28 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 var sharedUtil = __w_pdfjs_require__(0);
 var error = sharedUtil.error;
 var JpegImage = function JpegImageClosure() {
  var dctZigZag = new Uint8Array([
@@ -44344,17 +44325,17 @@ var JpegImage = function JpegImageClosur
    return data;
   }
  };
  return JpegImage;
 }();
 exports.JpegImage = JpegImage;
 
 /***/ }),
-/* 30 */
+/* 29 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 var sharedUtil = __w_pdfjs_require__(0);
 var getLookupTableFactory = sharedUtil.getLookupTableFactory;
 var getMetrics = getLookupTableFactory(function (t) {
  t['Courier'] = 600;
@@ -47292,17 +47273,17 @@ var getMetrics = getLookupTableFactory(f
   t['a189'] = 927;
   t['a190'] = 970;
   t['a191'] = 918;
  });
 });
 exports.getMetrics = getMetrics;
 
 /***/ }),
-/* 31 */
+/* 30 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 var sharedUtil = __w_pdfjs_require__(0);
 var Uint32ArrayView = sharedUtil.Uint32ArrayView;
 var MurmurHash3_64 = function MurmurHash3_64Closure(seed) {
  var MASK_HIGH = 0xffff0000;
@@ -47413,17 +47394,17 @@ var MurmurHash3_64 = function MurmurHash
    return str;
   }
  };
  return MurmurHash3_64;
 }();
 exports.MurmurHash3_64 = MurmurHash3_64;
 
 /***/ }),
-/* 32 */
+/* 31 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 var sharedUtil = __w_pdfjs_require__(0);
 var corePrimitives = __w_pdfjs_require__(1);
 var coreFunction = __w_pdfjs_require__(6);
 var coreColorSpace = __w_pdfjs_require__(3);
@@ -48252,25 +48233,25 @@ function getTilingPatternIR(operatorList
   paintType,
   tilingType
  ];
 }
 exports.Pattern = Pattern;
 exports.getTilingPatternIR = getTilingPatternIR;
 
 /***/ }),
-/* 33 */
+/* 32 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 var sharedUtil = __w_pdfjs_require__(0);
 var coreStream = __w_pdfjs_require__(2);
 var coreChunkedStream = __w_pdfjs_require__(10);
-var coreDocument = __w_pdfjs_require__(24);
+var coreDocument = __w_pdfjs_require__(23);
 var warn = sharedUtil.warn;
 var createValidAbsoluteUrl = sharedUtil.createValidAbsoluteUrl;
 var shadow = sharedUtil.shadow;
 var NotImplementedException = sharedUtil.NotImplementedException;
 var MissingDataException = sharedUtil.MissingDataException;
 var createPromiseCapability = sharedUtil.createPromiseCapability;
 var Util = sharedUtil.Util;
 var Stream = coreStream.Stream;
@@ -48438,17 +48419,17 @@ var NetworkPdfManager = function Network
   }
  });
  return NetworkPdfManager;
 }();
 exports.LocalPdfManager = LocalPdfManager;
 exports.NetworkPdfManager = NetworkPdfManager;
 
 /***/ }),
-/* 34 */
+/* 33 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 var sharedUtil = __w_pdfjs_require__(0);
 var corePrimitives = __w_pdfjs_require__(1);
 var error = sharedUtil.error;
 var isSpace = sharedUtil.isSpace;
@@ -48637,17 +48618,17 @@ var PostScriptLexer = function PostScrip
   }
  };
  return PostScriptLexer;
 }();
 exports.PostScriptLexer = PostScriptLexer;
 exports.PostScriptParser = PostScriptParser;
 
 /***/ }),
-/* 35 */
+/* 34 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 var sharedUtil = __w_pdfjs_require__(0);
 var coreStream = __w_pdfjs_require__(2);
 var coreEncodings = __w_pdfjs_require__(4);
 var warn = sharedUtil.warn;
@@ -49183,22 +49164,22 @@ var Type1Parser = function Type1ParserCl
    }
   }
  };
  return Type1Parser;
 }();
 exports.Type1Parser = Type1Parser;
 
 /***/ }),
-/* 36 */
+/* 35 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
-var pdfjsVersion = '1.7.290';
-var pdfjsBuild = 'b509a3f8';
+var pdfjsVersion = '1.7.297';
+var pdfjsBuild = '425ad309';
 var pdfjsCoreWorker = __w_pdfjs_require__(17);
 ;
 exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
 
 /***/ })
 /******/ ]);
 });
\ No newline at end of file
--- a/build/build-clang/clang-tidy-linux64.json
+++ b/build/build-clang/clang-tidy-linux64.json
@@ -1,10 +1,10 @@
 {
-    "llvm_revision": "292415",
+    "llvm_revision": "295482",
     "stages": "1",
     "build_libcxx": true,
     "build_type": "Release",
     "assertions": false,
     "build_clang_tidy": true,
     "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
     "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
     "extra_repo": "https://llvm.org/svn/llvm-project/clang-tools-extra/trunk",
--- a/build/build-clang/clang-tidy-macosx64.json
+++ b/build/build-clang/clang-tidy-macosx64.json
@@ -1,10 +1,10 @@
 {
-    "llvm_revision": "292415",
+    "llvm_revision": "295482",
     "stages": "1",
     "build_libcxx": true,
     "build_type": "Release",
     "assertions": false,
     "build_clang_tidy": true,
     "osx_cross_compile": true,
     "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
     "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
--- a/build/build-clang/clang-tidy-win32.json
+++ b/build/build-clang/clang-tidy-win32.json
@@ -1,10 +1,10 @@
 {
-    "llvm_revision": "292415",
+    "llvm_revision": "295482",
     "stages": "1",
     "build_libcxx": false,
     "build_type": "Release",
     "assertions": false,
     "build_clang_tidy": true,
     "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
     "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
     "extra_repo": "https://llvm.org/svn/llvm-project/clang-tools-extra/trunk",
--- a/build/build-clang/clang-tidy-win64.json
+++ b/build/build-clang/clang-tidy-win64.json
@@ -1,10 +1,10 @@
 {
-    "llvm_revision": "292415",
+    "llvm_revision": "295482",
     "stages": "1",
     "build_libcxx": false,
     "build_type": "Release",
     "assertions": false,
     "build_clang_tidy": true,
     "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
     "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
     "extra_repo": "https://llvm.org/svn/llvm-project/clang-tools-extra/trunk",
--- a/devtools/client/commandline/test/browser_cmd_highlight_04.js
+++ b/devtools/client/commandline/test/browser_cmd_highlight_04.js
@@ -2,17 +2,17 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 /* eslint key-spacing: 0 */
 
 // Tests the various highlight command parameters and options
 
-requestLongerTimeout(3);
+requestLongerTimeout(4);
 
 // Creating a test page with many elements to test the --showall option
 var TEST_PAGE = "data:text/html;charset=utf-8,<body><ul>";
 for (let i = 0; i < 101; i++) {
   TEST_PAGE += "<li class='item'>" + i + "</li>";
 }
 TEST_PAGE += "</ul></body>";
 
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -1089,60 +1089,19 @@ Inspector.prototype = {
 
       menu.append(menuitem);
     }
 
     menu.append(new MenuItem({
       type: "separator",
     }));
 
-    let copySubmenu = new Menu();
-    copySubmenu.append(new MenuItem({
-      id: "node-menu-copyinner",
-      label: INSPECTOR_L10N.getStr("inspectorCopyInnerHTML.label"),
-      accesskey: INSPECTOR_L10N.getStr("inspectorCopyInnerHTML.accesskey"),
-      disabled: !isSelectionElement,
-      click: () => this.copyInnerHTML(),
-    }));
-    copySubmenu.append(new MenuItem({
-      id: "node-menu-copyouter",
-      label: INSPECTOR_L10N.getStr("inspectorCopyOuterHTML.label"),
-      accesskey: INSPECTOR_L10N.getStr("inspectorCopyOuterHTML.accesskey"),
-      disabled: !isSelectionElement,
-      click: () => this.copyOuterHTML(),
-    }));
-    copySubmenu.append(new MenuItem({
-      id: "node-menu-copyuniqueselector",
-      label: INSPECTOR_L10N.getStr("inspectorCopyCSSSelector.label"),
-      accesskey:
-        INSPECTOR_L10N.getStr("inspectorCopyCSSSelector.accesskey"),
-      disabled: !isSelectionElement,
-      hidden: !this.canGetUniqueSelector,
-      click: () => this.copyUniqueSelector(),
-    }));
-    copySubmenu.append(new MenuItem({
-      id: "node-menu-copycsspath",
-      label: INSPECTOR_L10N.getStr("inspectorCopyCSSPath.label"),
-      accesskey:
-        INSPECTOR_L10N.getStr("inspectorCopyCSSPath.accesskey"),
-      disabled: !isSelectionElement,
-      hidden: !this.canGetCssPath,
-      click: () => this.copyCssPath(),
-    }));
-    copySubmenu.append(new MenuItem({
-      id: "node-menu-copyimagedatauri",
-      label: INSPECTOR_L10N.getStr("inspectorImageDataUri.label"),
-      disabled: !isSelectionElement || !markupContainer ||
-                !markupContainer.isPreviewable(),
-      click: () => this.copyImageDataUri(),
-    }));
-
     menu.append(new MenuItem({
       label: INSPECTOR_L10N.getStr("inspectorCopyHTMLSubmenu.label"),
-      submenu: copySubmenu,
+      submenu: this._getCopySubmenu(markupContainer, isSelectionElement),
     }));
 
     menu.append(new MenuItem({
       label: INSPECTOR_L10N.getStr("inspectorPasteHTMLSubmenu.label"),
       submenu: this._getPasteSubmenu(isEditableElement),
     }));
 
     menu.append(new MenuItem({
@@ -1205,16 +1164,61 @@ Inspector.prototype = {
     for (let menuitem of nodeLinkMenuItems) {
       menu.append(menuitem);
     }
 
     menu.popup(screenX, screenY, this._toolbox);
     return menu;
   },
 
+  _getCopySubmenu: function (markupContainer, isSelectionElement) {
+    let copySubmenu = new Menu();
+    copySubmenu.append(new MenuItem({
+      id: "node-menu-copyinner",
+      label: INSPECTOR_L10N.getStr("inspectorCopyInnerHTML.label"),
+      accesskey: INSPECTOR_L10N.getStr("inspectorCopyInnerHTML.accesskey"),
+      disabled: !isSelectionElement,
+      click: () => this.copyInnerHTML(),
+    }));
+    copySubmenu.append(new MenuItem({
+      id: "node-menu-copyouter",
+      label: INSPECTOR_L10N.getStr("inspectorCopyOuterHTML.label"),
+      accesskey: INSPECTOR_L10N.getStr("inspectorCopyOuterHTML.accesskey"),
+      disabled: !isSelectionElement,
+      click: () => this.copyOuterHTML(),
+    }));
+    copySubmenu.append(new MenuItem({
+      id: "node-menu-copyuniqueselector",
+      label: INSPECTOR_L10N.getStr("inspectorCopyCSSSelector.label"),
+      accesskey:
+        INSPECTOR_L10N.getStr("inspectorCopyCSSSelector.accesskey"),
+      disabled: !isSelectionElement,
+      hidden: !this.canGetUniqueSelector,
+      click: () => this.copyUniqueSelector(),
+    }));
+    copySubmenu.append(new MenuItem({
+      id: "node-menu-copycsspath",
+      label: INSPECTOR_L10N.getStr("inspectorCopyCSSPath.label"),
+      accesskey:
+        INSPECTOR_L10N.getStr("inspectorCopyCSSPath.accesskey"),
+      disabled: !isSelectionElement,
+      hidden: !this.canGetCssPath,
+      click: () => this.copyCssPath(),
+    }));
+    copySubmenu.append(new MenuItem({
+      id: "node-menu-copyimagedatauri",
+      label: INSPECTOR_L10N.getStr("inspectorImageDataUri.label"),
+      disabled: !isSelectionElement || !markupContainer ||
+                !markupContainer.isPreviewable(),
+      click: () => this.copyImageDataUri(),
+    }));
+
+    return copySubmenu;
+  },
+
   _getPasteSubmenu: function (isEditableElement) {
     let isPasteable = isEditableElement && this._getClipboardContentForPaste();
     let disableAdjacentPaste = !isPasteable ||
           !this.canPasteInnerOrAdjacentHTML || this.selection.isRoot() ||
           this.selection.isBodyNode() || this.selection.isHeadNode();
     let disableFirstLastPaste = !isPasteable ||
           !this.canPasteInnerOrAdjacentHTML || (this.selection.isHTMLNode() &&
           this.selection.isRoot());
@@ -1279,30 +1283,36 @@ Inspector.prototype = {
     attributesSubmenu.append(new MenuItem({
       id: "node-menu-add-attribute",
       label: INSPECTOR_L10N.getStr("inspectorAddAttribute.label"),
       accesskey: INSPECTOR_L10N.getStr("inspectorAddAttribute.accesskey"),
       disabled: !isEditableElement,
       click: () => this.onAddAttribute(),
     }));
     attributesSubmenu.append(new MenuItem({
+      id: "node-menu-copy-attribute",
+      label: INSPECTOR_L10N.getFormatStr("inspectorCopyAttributeValue.label",
+                                        isAttributeClicked ? `"${nodeInfo.value}"` : ""),
+      accesskey: INSPECTOR_L10N.getStr("inspectorCopyAttributeValue.accesskey"),
+      disabled: !isAttributeClicked,
+      click: () => this.onCopyAttributeValue(),
+    }));
+    attributesSubmenu.append(new MenuItem({
       id: "node-menu-edit-attribute",
       label: INSPECTOR_L10N.getFormatStr("inspectorEditAttribute.label",
                                         isAttributeClicked ? `"${nodeInfo.name}"` : ""),
       accesskey: INSPECTOR_L10N.getStr("inspectorEditAttribute.accesskey"),
       disabled: !isAttributeClicked,
       click: () => this.onEditAttribute(),
     }));
-
     attributesSubmenu.append(new MenuItem({
       id: "node-menu-remove-attribute",
       label: INSPECTOR_L10N.getFormatStr("inspectorRemoveAttribute.label",
                                         isAttributeClicked ? `"${nodeInfo.name}"` : ""),
-      accesskey:
-        INSPECTOR_L10N.getStr("inspectorRemoveAttribute.accesskey"),
+      accesskey: INSPECTOR_L10N.getStr("inspectorRemoveAttribute.accesskey"),
       disabled: !isAttributeClicked,
       click: () => this.onRemoveAttribute(),
     }));
 
     return attributesSubmenu;
   },
 
   /**
@@ -1813,16 +1823,24 @@ Inspector.prototype = {
    * Used for node context menu and shouldn't be called directly.
    */
   onAddAttribute: function () {
     let container = this.markup.getContainer(this.selection.nodeFront);
     container.addAttribute();
   },
 
   /**
+   * Copy attribute value for node.
+   * Used for node context menu and shouldn't be called directly.
+   */
+  onCopyAttributeValue: function () {
+    clipboardHelper.copyString(this.nodeMenuTriggerInfo.value);
+  },
+
+  /**
    * Edit attribute for node.
    * Used for node context menu and shouldn't be called directly.
    */
   onEditAttribute: function () {
     let container = this.markup.getContainer(this.selection.nodeFront);
     container.editAttribute(this.nodeMenuTriggerInfo.name);
   },
 
--- a/devtools/client/inspector/rules/test/browser.ini
+++ b/devtools/client/inspector/rules/test/browser.ini
@@ -210,16 +210,17 @@ subsuite = clipboard
 skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
 [browser_rules_selector-highlighter-on-navigate.js]
 [browser_rules_selector-highlighter_01.js]
 [browser_rules_selector-highlighter_02.js]
 [browser_rules_selector-highlighter_03.js]
 [browser_rules_selector-highlighter_04.js]
 [browser_rules_selector-highlighter_05.js]
 [browser_rules_selector_highlight.js]
+[browser_rules_shorthand-overridden-lists.js]
 [browser_rules_strict-search-filter-computed-list_01.js]
 [browser_rules_strict-search-filter_01.js]
 [browser_rules_strict-search-filter_02.js]
 [browser_rules_strict-search-filter_03.js]
 [browser_rules_style-editor-link.js]
 [browser_rules_urls-clickable.js]
 [browser_rules_user-agent-styles.js]
 [browser_rules_user-agent-styles-uneditable.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/rules/test/browser_rules_shorthand-overridden-lists.js
@@ -0,0 +1,70 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the rule view shorthand overridden list works correctly,
+// can be shown and hidden correctly, and contain the right subproperties.
+
+var TEST_URI = `
+  <style type="text/css">
+    div {
+      margin: 0px 1px 2px 3px;
+      top: 0px;
+    }
+    #testid {
+        margin-left: 10px;
+        margin-right: 10px;
+    }
+  </style>
+  <div id="testid">Styled Node</div>
+`;
+
+add_task(function* () {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+  yield selectNode("#testid", inspector);
+  yield testComputedList(inspector, view);
+});
+
+function* testComputedList(inspector, view) {
+  let rule = getRuleViewRuleEditor(view, 2).rule;
+  let propEditor = rule.textProps[0].editor;
+  let expander = propEditor.expander;
+  let overriddenItems = propEditor.shorthandOverridden.children;
+  let propNames = [
+    "margin-right",
+    "margin-left"
+  ];
+
+  ok(!expander.hasAttribute("open"), "margin computed list is closed.");
+  ok(!propEditor.shorthandOverridden.hasAttribute("hidden"),
+      "The shorthandOverridden list should be open.");
+
+  is(overriddenItems.length, propNames.length,
+      "There should be 2 overridden shorthand value.");
+  for (let i = 0; i < propNames.length; i++) {
+    let overriddenItem = overriddenItems[i].querySelector(".ruleview-propertyname");
+    is(overriddenItem.textContent, propNames[i],
+        "The overridden item #" + i + " should be " + propNames[i]);
+  }
+
+  info("Opening the computed list of margin property.");
+  expander.click();
+  ok(expander.hasAttribute("open"), "margin computed list is open.");
+  ok(propEditor.shorthandOverridden.hasAttribute("hidden"),
+      "The shorthandOverridden list should be hidden.");
+
+  info("Closing the computed list of margin property.");
+  expander.click();
+  ok(!expander.hasAttribute("open"), "margin computed list is closed.");
+  ok(!propEditor.shorthandOverridden.hasAttribute("hidden"),
+      "The shorthandOverridden list should be open.");
+
+  for (let i = 0; i < propNames.length; i++) {
+    let overriddenItem = overriddenItems[i].querySelector(".ruleview-propertyname");
+    is(overriddenItem.textContent, propNames[i],
+        "The overridden item #" + i + " should still be " + propNames[i]);
+  }
+}
--- a/devtools/client/inspector/rules/views/text-property-editor.js
+++ b/devtools/client/inspector/rules/views/text-property-editor.js
@@ -192,16 +192,22 @@ TextPropertyEditor.prototype = {
     });
 
     // Holds the viewers for the computed properties.
     // will be populated in |_updateComputed|.
     this.computed = createChild(this.element, "ul", {
       class: "ruleview-computedlist",
     });
 
+    // Holds the viewers for the overridden shorthand properties.
+    // will be populated in |_updateShorthandOverridden|.
+    this.shorthandOverridden = createChild(this.element, "ul", {
+      class: "ruleview-overridden-items",
+    });
+
     // Only bind event handlers if the rule is editable.
     if (this.ruleEditor.isEditable) {
       this.enable.addEventListener("click", this._onEnableClicked, true);
 
       this.nameContainer.addEventListener("click", (event) => {
         // Clicks within the name shouldn't propagate any further.
         event.stopPropagation();
 
@@ -449,32 +455,33 @@ TextPropertyEditor.prototype = {
       }
 
       if (!elToClick) {
         elToClick = this.valueSpan;
       }
       elToClick.click();
     }
 
-    // Populate the computed styles.
+    // Populate the computed styles and shorthand overridden styles.
     this._updateComputed();
+    this._updateShorthandOverridden();
 
     // Update the rule property highlight.
     this.ruleView._updatePropertyHighlight(this);
   },
 
   _onStartEditing: function () {
     this.element.classList.remove("ruleview-overridden");
     this.filterProperty.hidden = true;
     this.enable.style.visibility = "hidden";
   },
 
   /**
    * Update the visibility of the enable checkbox, the warning indicator and
-   * the filter property, as well as the overriden state of the property.
+   * the filter property, as well as the overridden state of the property.
    */
   updatePropertyState: function () {
     if (this.prop.enabled) {
       this.enable.style.removeProperty("visibility");
       this.enable.setAttribute("checked", "");
     } else {
       this.enable.style.visibility = "visible";
       this.enable.removeAttribute("checked");
@@ -522,53 +529,94 @@ TextPropertyEditor.prototype = {
 
     for (let computed of this.prop.computed) {
       // Don't bother to duplicate information already
       // shown in the text property.
       if (computed.name === this.prop.name) {
         continue;
       }
 
-      let li = createChild(this.computed, "li", {
-        class: "ruleview-computed"
-      });
+      // Store the computed style element for easy access when highlighting
+      // styles
+      computed.element = this._createComputedListItem(this.computed, computed,
+        "ruleview-computed");
+    }
+  },
+
+  /**
+   * Update the indicator for overridden shorthand styles. The shorthand
+   * overridden styles themselves are populated on demand, when they
+   * become visible.
+   */
+  _updateShorthandOverridden: function () {
+    this.shorthandOverridden.innerHTML = "";
 
-      if (computed.overridden) {
-        li.classList.add("ruleview-overridden");
+    this._populatedShorthandOverridden = false;
+    this._populateShorthandOverridden();
+  },
+
+  /**
+   * Populate the list of overridden shorthand styles.
+   */
+  _populateShorthandOverridden: function () {
+    if (this._populatedShorthandOverridden || this.prop.overridden) {
+      return;
+    }
+    this._populatedShorthandOverridden = true;
+
+    for (let computed of this.prop.computed) {
+      // Don't display duplicate information or show properties
+      // that are completely overridden.
+      if (computed.name === this.prop.name || !computed.overridden) {
+        continue;
       }
 
-      createChild(li, "span", {
-        class: "ruleview-propertyname theme-fg-color5",
-        textContent: computed.name
-      });
-      appendText(li, ": ");
+      this._createComputedListItem(this.shorthandOverridden, computed,
+        "ruleview-overridden-item");
+    }
+  },
 
-      let outputParser = this.ruleView._outputParser;
-      let frag = outputParser.parseCssProperty(
-        computed.name, computed.value, {
-          colorSwatchClass: "ruleview-swatch ruleview-colorswatch",
-          urlClass: "theme-link",
-          baseURI: this.sheetHref
-        }
-      );
+  /**
+   * Creates and populates a list item with the computed CSS property.
+   */
+  _createComputedListItem: function (parentEl, computed, className) {
+    let li = createChild(parentEl, "li", {
+      class: className
+    });
+
+    if (computed.overridden) {
+      li.classList.add("ruleview-overridden");
+    }
 
-      // Store the computed property value that was parsed for output
-      computed.parsedValue = frag.textContent;
+    createChild(li, "span", {
+      class: "ruleview-propertyname theme-fg-color5",
+      textContent: computed.name
+    });
+    appendText(li, ": ");
 
-      createChild(li, "span", {
-        class: "ruleview-propertyvalue theme-fg-color1",
-        child: frag
-      });
+    let outputParser = this.ruleView._outputParser;
+    let frag = outputParser.parseCssProperty(
+      computed.name, computed.value, {
+        colorSwatchClass: "ruleview-swatch ruleview-colorswatch",
+        urlClass: "theme-link",
+        baseURI: this.sheetHref
+      }
+    );
 
-      appendText(li, ";");
+    // Store the computed property value that was parsed for output
+    computed.parsedValue = frag.textContent;
 
-      // Store the computed style element for easy access when highlighting
-      // styles
-      computed.element = li;
-    }
+    createChild(li, "span", {
+      class: "ruleview-propertyvalue theme-fg-color1",
+      child: frag
+    });
+
+    appendText(li, ";");
+
+    return li;
   },
 
   /**
    * Handles clicks on the disabled property.
    */
   _onEnableClicked: function (event) {
     let checked = this.enable.hasAttribute("checked");
     if (checked) {
@@ -588,19 +636,22 @@ TextPropertyEditor.prototype = {
    * expanded by manually by the user.
    */
   _onExpandClicked: function (event) {
     if (this.computed.hasAttribute("filter-open") ||
         this.computed.hasAttribute("user-open")) {
       this.expander.removeAttribute("open");
       this.computed.removeAttribute("filter-open");
       this.computed.removeAttribute("user-open");
+      this.shorthandOverridden.removeAttribute("hidden");
+      this._populateShorthandOverridden();
     } else {
       this.expander.setAttribute("open", "true");
       this.computed.setAttribute("user-open", "");
+      this.shorthandOverridden.setAttribute("hidden", "true");
       this._populateComputed();
     }
 
     event.stopPropagation();
   },
 
   /**
    * Expands the computed list when a computed property is matched by the style
--- a/devtools/client/inspector/test/browser_inspector_menu-01-sensitivity.js
+++ b/devtools/client/inspector/test/browser_inspector_menu-01-sensitivity.js
@@ -30,16 +30,17 @@ const ALL_MENU_ITEMS = [
   "node-menu-copyimagedatauri",
   "node-menu-delete",
   "node-menu-pseudo-hover",
   "node-menu-pseudo-active",
   "node-menu-pseudo-focus",
   "node-menu-scrollnodeintoview",
   "node-menu-screenshotnode",
   "node-menu-add-attribute",
+  "node-menu-copy-attribute",
   "node-menu-edit-attribute",
   "node-menu-remove-attribute"
 ].concat(PASTE_MENU_ITEMS, ACTIVE_ON_DOCTYPE_ITEMS);
 
 const INACTIVE_ON_DOCTYPE_ITEMS =
   ALL_MENU_ITEMS.filter(item => ACTIVE_ON_DOCTYPE_ITEMS.indexOf(item) === -1);
 
 /**
@@ -65,145 +66,157 @@ const TEST_CASES = [
     disabled: INACTIVE_ON_DOCTYPE_ITEMS,
   },
   {
     desc: "element node HTML on the clipboard",
     clipboardData: "<p>some text</p>",
     clipboardDataType: "html",
     disabled: [
       "node-menu-copyimagedatauri",
+      "node-menu-copy-attribute",
       "node-menu-edit-attribute",
       "node-menu-remove-attribute"
     ],
     selector: "#sensitivity",
   },
   {
     desc: "<html> element",
     clipboardData: "<p>some text</p>",
     clipboardDataType: "html",
     selector: "html",
     disabled: [
       "node-menu-copyimagedatauri",
       "node-menu-pastebefore",
       "node-menu-pasteafter",
       "node-menu-pastefirstchild",
       "node-menu-pastelastchild",
+      "node-menu-copy-attribute",
       "node-menu-edit-attribute",
       "node-menu-remove-attribute"
     ],
   },
   {
     desc: "<body> with HTML on clipboard",
     clipboardData: "<p>some text</p>",
     clipboardDataType: "html",
     selector: "body",
     disabled: [
       "node-menu-copyimagedatauri",
       "node-menu-pastebefore",
       "node-menu-pasteafter",
+      "node-menu-copy-attribute",
       "node-menu-edit-attribute",
       "node-menu-remove-attribute"
     ]
   },
   {
     desc: "<img> with HTML on clipboard",
     clipboardData: "<p>some text</p>",
     clipboardDataType: "html",
     selector: "img",
     disabled: [
+      "node-menu-copy-attribute",
       "node-menu-edit-attribute",
       "node-menu-remove-attribute"
     ]
   },
   {
     desc: "<head> with HTML on clipboard",
     clipboardData: "<p>some text</p>",
     clipboardDataType: "html",
     selector: "head",
     disabled: [
       "node-menu-copyimagedatauri",
       "node-menu-pastebefore",
       "node-menu-pasteafter",
       "node-menu-screenshotnode",
+      "node-menu-copy-attribute",
       "node-menu-edit-attribute",
       "node-menu-remove-attribute"
     ],
   },
   {
     desc: "<head> with no html on clipboard",
     selector: "head",
     disabled: PASTE_MENU_ITEMS.concat([
       "node-menu-copyimagedatauri",
       "node-menu-screenshotnode",
+      "node-menu-copy-attribute",
       "node-menu-edit-attribute",
       "node-menu-remove-attribute"
     ]),
   },
   {
     desc: "<element> with text on clipboard",
     clipboardData: "some text",
     clipboardDataType: undefined,
     selector: "#paste-area",
     disabled: [
       "node-menu-copyimagedatauri",
+      "node-menu-copy-attribute",
       "node-menu-edit-attribute",
       "node-menu-remove-attribute"
     ]
   },
   {
     desc: "<element> with base64 encoded image data uri on clipboard",
     clipboardData:
       "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABC" +
       "AAAAAA6fptVAAAACklEQVQYV2P4DwABAQEAWk1v8QAAAABJRU5ErkJggg==",
     clipboardDataType: undefined,
     selector: "#paste-area",
     disabled: PASTE_MENU_ITEMS.concat([
       "node-menu-copyimagedatauri",
+      "node-menu-copy-attribute",
       "node-menu-edit-attribute",
       "node-menu-remove-attribute"
     ]),
   },
   {
     desc: "<element> with empty string on clipboard",
     clipboardData: "",
     clipboardDataType: undefined,
     selector: "#paste-area",
     disabled: PASTE_MENU_ITEMS.concat([
       "node-menu-copyimagedatauri",
+      "node-menu-copy-attribute",
       "node-menu-edit-attribute",
       "node-menu-remove-attribute"
     ]),
   },
   {
     desc: "<element> with whitespace only on clipboard",
     clipboardData: " \n\n\t\n\n  \n",
     clipboardDataType: undefined,
     selector: "#paste-area",
     disabled: PASTE_MENU_ITEMS.concat([
       "node-menu-copyimagedatauri",
+      "node-menu-copy-attribute",
       "node-menu-edit-attribute",
       "node-menu-remove-attribute"
     ]),
   },
   {
     desc: "<element> that isn't visible on the page, empty clipboard",
     selector: "#hiddenElement",
     disabled: PASTE_MENU_ITEMS.concat([
       "node-menu-copyimagedatauri",
       "node-menu-screenshotnode",
+      "node-menu-copy-attribute",
       "node-menu-edit-attribute",
       "node-menu-remove-attribute"
     ]),
   },
   {
     desc: "<element> nested in another hidden element, empty clipboard",
     selector: "#nestedHiddenElement",
     disabled: PASTE_MENU_ITEMS.concat([
       "node-menu-copyimagedatauri",
       "node-menu-screenshotnode",
+      "node-menu-copy-attribute",
       "node-menu-edit-attribute",
       "node-menu-remove-attribute"
     ]),
   },
   {
     desc: "<element> with context menu triggered on attribute, empty clipboard",
     selector: "#attributes",
     disabled: PASTE_MENU_ITEMS.concat(["node-menu-copyimagedatauri"]),
--- a/devtools/client/inspector/test/browser_inspector_menu-05-attribute-items.js
+++ b/devtools/client/inspector/test/browser_inspector_menu-05-attribute-items.js
@@ -7,16 +7,17 @@ http://creativecommons.org/publicdomain/
 
 const TEST_URL = URL_ROOT + "doc_inspector_menu.html";
 
 add_task(function* () {
   let { inspector, testActor } = yield openInspectorForURL(TEST_URL);
   yield selectNode("#attributes", inspector);
 
   yield testAddAttribute();
+  yield testCopyAttributeValue();
   yield testEditAttribute();
   yield testRemoveAttribute();
 
   function* testAddAttribute() {
     info("Triggering 'Add Attribute' and waiting for mutation to occur");
     let addAttribute = getMenuItem("node-menu-add-attribute");
     addAttribute.click();
 
@@ -24,16 +25,30 @@ add_task(function* () {
     let onMutation = inspector.once("markupmutation");
     EventUtils.synthesizeKey("VK_RETURN", {});
     yield onMutation;
 
     let hasAttribute = testActor.hasNode("#attributes.u-hidden");
     ok(hasAttribute, "attribute was successfully added");
   }
 
+  function* testCopyAttributeValue() {
+    info("Testing 'Copy Attribute Value' and waiting for clipboard promise to resolve");
+    let copyAttributeValue = getMenuItem("node-menu-copy-attribute");
+
+    info("Triggering 'Copy Attribute Value' and waiting for clipboard to copy the value");
+    inspector.nodeMenuTriggerInfo = {
+      type: "attribute",
+      name: "data-edit",
+      value: "the"
+    };
+
+    yield waitForClipboardPromise(() => copyAttributeValue.click(), "the");
+  }
+
   function* testEditAttribute() {
     info("Testing 'Edit Attribute' menu item");
     let editAttribute = getMenuItem("node-menu-edit-attribute");
 
     info("Triggering 'Edit Attribute' and waiting for mutation to occur");
     inspector.nodeMenuTriggerInfo = {
       type: "attribute",
       name: "data-edit"
--- a/devtools/client/inspector/test/doc_inspector_menu.html
+++ b/devtools/client/inspector/test/doc_inspector_menu.html
@@ -18,12 +18,12 @@
       <p class="duplicate">This will be duplicated</p>
       <p id="delete">This has to be deleted</p>
       <img id="copyimage" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQYV2P4DwABAQEAWk1v8QAAAABJRU5ErkJggg==" />
       <div id="hiddenElement" style="display: none;">
         <p id="nestedHiddenElement">Visible element nested inside a non-visible element</p>
       </div>
       <p id="console-var">Paragraph for testing console variables</p>
       <p id="console-var-multi">Paragraph for testing multiple console variables</p>
-      <p id="attributes" data-edit="original" data-remove="thing">Attributes are going to be changed here</p>
+      <p id="attributes" data-copy="the" data-edit="original" data-remove="thing">Attributes are going to be changed here</p>
     </div>
   </body>
 </html>
--- a/devtools/client/locales/en-US/inspector.properties
+++ b/devtools/client/locales/en-US/inspector.properties
@@ -112,16 +112,23 @@ inspectorEditAttribute.accesskey=E
 
 # LOCALIZATION NOTE (inspectorRemoveAttribute.label): This is the label of a
 # sub-menu "Attribute" in the inspector contextual-menu that appears
 # when the user right-clicks on the attribute of a node in the inspector,
 # and that allows to remove this attribute.
 inspectorRemoveAttribute.label=Remove Attribute %S
 inspectorRemoveAttribute.accesskey=R
 
+# LOCALIZATION NOTE (inspectorCopyAttributeValue.label): This is the label of a
+# sub-menu "Attribute" in the inspector contextual-menu that appears
+# when the user right-clicks on the attribute of a node in the inspector,
+# and that allows to copy the attribute value to clipboard.
+inspectorCopyAttributeValue.label=Copy Attribute Value %S
+inspectorCopyAttributeValue.accesskey=V
+
 # LOCALIZATION NOTE (inspector.nodePreview.selectNodeLabel):
 # This string is displayed in a tooltip that is shown when hovering over a DOM
 # node preview (e.g. something like "div#foo.bar").
 # DOM node previews can be displayed in places like the animation-inspector, the
 # console or the object inspector.
 # The tooltip invites the user to click on the node in order to select it in the
 # inspector panel.
 inspector.nodePreview.selectNodeLabel=Click to select this node in the Inspector
--- a/devtools/client/themes/rules.css
+++ b/devtools/client/themes/rules.css
@@ -118,23 +118,25 @@
   padding-right: 5px;
 }
 
 .ruleview-propertyvaluecontainer a {
   cursor: pointer;
 }
 
 .ruleview-computedlist,
+.ruleview-overridden-items[hidden],
 .ruleview-overridden-rule-filter[hidden],
 .ruleview-warning[hidden] {
   display: none;
 }
 
 .ruleview-computedlist[user-open],
-.ruleview-computedlist[filter-open] {
+.ruleview-computedlist[filter-open],
+.ruleview-overridden-items {
   display: block;
 }
 
 .ruleview-rule-source {
   text-align: end;
   float: right;
   max-width: 100%;
 
@@ -357,16 +359,52 @@
   list-style: none;
   padding: 0;
 }
 
 .ruleview-computed {
   margin-inline-start: 35px;
 }
 
+.ruleview-overridden-items {
+  margin: 0px 0px 0px 5px;
+  list-style: none;
+  line-height: 1.5em;
+}
+
+.ruleview-overridden-item {
+  position: relative;
+}
+
+.ruleview-overridden-item::before {
+  position: absolute;
+  left: -15px;
+  top: 0px;
+  content: '';
+  display: block;
+  border-left: 1px solid var(--theme-highlight-gray);
+  height: 0.7em;
+  border-bottom: 1px solid var(--theme-highlight-gray);
+  width: 10px;
+}
+
+.ruleview-overridden-item::after {
+  position: absolute;
+  left: -15px;
+  bottom: -7px;
+  content: '';
+  display: block;
+  border-left: 1px solid var(--theme-highlight-gray);
+  height: 100%;
+}
+
+.ruleview-overridden-item:last-child:after {
+  display: none;
+}
+
 .ruleview-grid,
 .ruleview-swatch {
   cursor: pointer;
   border-radius: 50%;
   width: 1em;
   height: 1em;
   vertical-align: middle;
   /* align the swatch with its value */
--- a/dom/base/nsContentAreaDragDrop.cpp
+++ b/dom/base/nsContentAreaDragDrop.cpp
@@ -348,17 +348,17 @@ DragDataProducer::GetNodeString(nsIConte
                                 nsAString & outNodeString)
 {
   nsCOMPtr<nsINode> node = inNode;
 
   outNodeString.Truncate();
 
   // use a range to get the text-equivalent of the node
   nsCOMPtr<nsIDocument> doc = node->OwnerDoc();
-  mozilla::ErrorResult rv;
+  mozilla::IgnoredErrorResult rv;
   RefPtr<nsRange> range = doc->CreateRange(rv);
   if (range) {
     range->SelectNode(*node, rv);
     range->ToString(outNodeString);
   }
 }
 
 nsresult
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -7962,23 +7962,18 @@ nsDocument::FlushPendingNotifications(Fl
   // correct size to determine the correct style.
   if (mParentDocument && IsSafeToFlush()) {
     FlushType parentType = aType;
     if (aType >= FlushType::Style)
       parentType = std::max(FlushType::Layout, aType);
     mParentDocument->FlushPendingNotifications(parentType);
   }
 
-  // Call nsIPresShell::NeedFlush (inline, non-virtual) to check whether we
-  // really need to flush the shell (virtual, and needs a strong reference).
   if (nsIPresShell* shell = GetShell()) {
-    if (shell->NeedFlush(aType)) {
-      nsCOMPtr<nsIPresShell> presShell = shell;
-      presShell->FlushPendingNotifications(aType);
-    }
+    shell->FlushPendingNotifications(aType);
   }
 }
 
 static bool
 Copy(nsIDocument* aDocument, void* aData)
 {
   nsTArray<nsCOMPtr<nsIDocument> >* resources =
     static_cast<nsTArray<nsCOMPtr<nsIDocument> >* >(aData);
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -2174,17 +2174,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStorage)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplicationCache)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuspendedDoc)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleService)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWakeLock)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingStorageEvents)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleRequestExecutor)
   for (IdleRequest* request : tmp->mIdleRequestCallbacks) {
     cb.NoteNativeChild(request, NS_CYCLE_COLLECTION_PARTICIPANT(IdleRequest));
   }
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleObservers)
 
@@ -2255,17 +2254,16 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplicationCache)
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuspendedDoc)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexedDB)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleService)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWakeLock)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingStorageEvents)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleObservers)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mGamepads)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCacheStorage)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mVRDisplays)
 
   // Unlink stuff from nsPIDOMWindow
@@ -3235,17 +3233,21 @@ nsGlobalWindow::PreloadLocalStorage()
     do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
   if (NS_FAILED(rv)) {
     return;
   }
 
   // private browsing windows do not persist local storage to disk so we should
   // only try to precache storage when we're not a private browsing window.
   if (principal->GetPrivateBrowsingId() == 0) {
-    storageManager->PrecacheStorage(principal);
+    nsCOMPtr<nsIDOMStorage> storage;
+    rv = storageManager->PrecacheStorage(principal, getter_AddRefs(storage));
+    if (NS_SUCCEEDED(rv)) {
+      mLocalStorage = static_cast<Storage*>(storage.get());
+    }
   }
 }
 
 void
 nsGlobalWindow::DispatchDOMWindowCreated()
 {
   MOZ_ASSERT(IsOuterWindow());
 
@@ -11901,59 +11903,76 @@ nsGlobalWindow::Observe(nsISupports* aSu
       mNotifyIdleObserversIdleOnThaw = false;
     } else if (AsInner()->IsCurrentInnerWindow()) {
       MOZ_ASSERT(IsInnerWindow());
       ScheduleActiveTimerCallback();
     }
     return NS_OK;
   }
 
-  bool isPrivateBrowsing = IsPrivateBrowsing();
-  if ((!nsCRT::strcmp(aTopic, "dom-storage2-changed") && !isPrivateBrowsing) ||
-      (!nsCRT::strcmp(aTopic, "dom-private-storage2-changed") && isPrivateBrowsing)) {
-    if (!IsInnerWindow() || !AsInner()->IsCurrentInnerWindow()) {
+  // We need these for our private-browsing check below; so save them off.
+  // (And we can't do the private-browsing enforcement check as part of our
+  // outer conditional because otherwise we'll trigger an NS_WARNING if control
+  // flow reaches the bottom of this method.)
+  bool isNonPrivateLocalStorageChange =
+    !nsCRT::strcmp(aTopic, "dom-storage2-changed");
+  bool isPrivateLocalStorageChange =
+    !nsCRT::strcmp(aTopic, "dom-private-storage2-changed");
+  if (isNonPrivateLocalStorageChange || isPrivateLocalStorageChange) {
+    // Enforce that the source storage area's private browsing state matches
+    // this window's state.  These flag checks and their maintenance independent
+    // from the principal's OriginAttributes matter because chrome docshells
+    // that are part of private browsing windows can be private browsing without
+    // having their OriginAttributes set (because they have the system
+    // principal).
+    bool isPrivateBrowsing = IsPrivateBrowsing();
+    if ((isNonPrivateLocalStorageChange && isPrivateBrowsing) ||
+        (isPrivateLocalStorageChange && !isPrivateBrowsing)) {
       return NS_OK;
     }
 
-    nsIPrincipal *principal;
-    nsresult rv;
+    // We require that aData be either u"SessionStorage" or u"localStorage".
+    // Assert under debug, but ignore the bogus event under non-debug.
+    MOZ_ASSERT(aData);
+    if (!aData) {
+      return NS_ERROR_INVALID_ARG;
+    }
+
+    // LocalStorage can only exist on an inner window, and we don't want to
+    // generate events on frozen or otherwise-navigated-away from windows.
+    // (Actually, this code used to try and buffer events for frozen windows,
+    // but it never worked, so we've removed it.  See bug 1285898.)
+    if (!IsInnerWindow() || !AsInner()->IsCurrentInnerWindow() || IsFrozen()) {
+      return NS_OK;
+    }
+
+    nsIPrincipal *principal = GetPrincipal();
+    if (!principal) {
+      return NS_OK;
+    }
 
     RefPtr<StorageEvent> event = static_cast<StorageEvent*>(aSubject);
     if (!event) {
       return NS_ERROR_FAILURE;
     }
 
-    RefPtr<Storage> changingStorage = event->GetStorageArea();
-    if (!changingStorage) {
-      return NS_ERROR_FAILURE;
-    }
-
-    nsCOMPtr<nsIDOMStorage> istorage = changingStorage.get();
-
     bool fireMozStorageChanged = false;
     nsAutoString eventType;
     eventType.AssignLiteral("storage");
-    principal = GetPrincipal();
-    if (!principal) {
-      return NS_OK;
-    }
-
-    if (changingStorage->IsPrivate() != IsPrivateBrowsing()) {
-      return NS_OK;
-    }
-
-    switch (changingStorage->GetType())
-    {
-    case Storage::SessionStorage:
-    {
+
+    if (!NS_strcmp(aData, u"sessionStorage")) {
+      nsCOMPtr<nsIDOMStorage> changingStorage = event->GetStorageArea();
+      MOZ_ASSERT(changingStorage);
+
       bool check = false;
 
       nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(GetDocShell());
       if (storageManager) {
-        rv = storageManager->CheckStorage(principal, istorage, &check);
+        nsresult rv = storageManager->CheckStorage(principal, changingStorage,
+                                                   &check);
         if (NS_FAILED(rv)) {
           return rv;
         }
       }
 
       if (!check) {
         // This storage event is not coming from our storage or is coming
         // from a different docshell, i.e. it is a clone, ignore this event.
@@ -11964,68 +11983,58 @@ nsGlobalWindow::Observe(nsISupports* aSu
         PR_LogPrint("nsGlobalWindow %p with sessionStorage %p passing event from %p",
                     this, mSessionStorage.get(), changingStorage.get());
       }
 
       fireMozStorageChanged = mSessionStorage == changingStorage;
       if (fireMozStorageChanged) {
         eventType.AssignLiteral("MozSessionStorageChanged");
       }
-      break;
-    }
-
-    case Storage::LocalStorage:
-    {
-      // Allow event fire only for the same principal storages
-      // XXX We have to use EqualsIgnoreDomain after bug 495337 lands
-      nsIPrincipal* storagePrincipal = changingStorage->GetPrincipal();
+    }
+
+    else {
+      MOZ_ASSERT(!NS_strcmp(aData, u"localStorage"));
+      nsIPrincipal* storagePrincipal = event->GetPrincipal();
+      if (!storagePrincipal) {
+        return NS_OK;
+      }
 
       bool equals = false;
-      rv = storagePrincipal->Equals(principal, &equals);
+      nsresult rv = storagePrincipal->Equals(principal, &equals);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      if (!equals)
+      if (!equals) {
         return NS_OK;
-
-      fireMozStorageChanged = mLocalStorage == changingStorage;
+      }
+
+      fireMozStorageChanged = mLocalStorage == event->GetStorageArea();
+
       if (fireMozStorageChanged) {
         eventType.AssignLiteral("MozLocalStorageChanged");
       }
-      break;
-    }
-    default:
-      return NS_OK;
     }
 
     // Clone the storage event included in the observer notification. We want
     // to dispatch clones rather than the original event.
     ErrorResult error;
-    RefPtr<StorageEvent> newEvent = CloneStorageEvent(eventType, event, error);
+    RefPtr<StorageEvent> clonedEvent =
+      CloneStorageEvent(eventType, event, error);
     if (error.Failed()) {
       return error.StealNSResult();
     }
 
-    newEvent->SetTrusted(true);
+    clonedEvent->SetTrusted(true);
 
     if (fireMozStorageChanged) {
-      WidgetEvent* internalEvent = newEvent->WidgetEventPtr();
+      WidgetEvent* internalEvent = clonedEvent->WidgetEventPtr();
       internalEvent->mFlags.mOnlyChromeDispatch = true;
     }
 
-    if (IsFrozen()) {
-      // This window is frozen, rather than firing the events here,
-      // store the domain in which the change happened and fire the
-      // events if we're ever thawed.
-
-      mPendingStorageEvents.AppendElement(newEvent);
-      return NS_OK;
-    }
-
     bool defaultActionEnabled;
-    DispatchEvent(newEvent, &defaultActionEnabled);
+    DispatchEvent(clonedEvent, &defaultActionEnabled);
 
     return NS_OK;
   }
 
   if (!nsCRT::strcmp(aTopic, "offline-cache-update-added")) {
     if (mApplicationCache)
       return NS_OK;
 
@@ -12106,32 +12115,41 @@ nsGlobalWindow::CloneStorageEvent(const 
   dict.mBubbles = aEvent->Bubbles();
   dict.mCancelable = aEvent->Cancelable();
   aEvent->GetKey(dict.mKey);
   aEvent->GetOldValue(dict.mOldValue);
   aEvent->GetNewValue(dict.mNewValue);
   aEvent->GetUrl(dict.mUrl);
 
   RefPtr<Storage> storageArea = aEvent->GetStorageArea();
-  MOZ_ASSERT(storageArea);
 
   RefPtr<Storage> storage;
-  if (storageArea->GetType() == Storage::LocalStorage) {
+
+  // If null, this is a localStorage event received by IPC.
+  if (!storageArea) {
+    storage = GetLocalStorage(aRv);
+    if (aRv.Failed() || !storage) {
+      return nullptr;
+    }
+
+    // We must apply the current change to the 'local' localStorage.
+    storage->ApplyEvent(aEvent);
+  } else if (storageArea->GetType() == Storage::LocalStorage) {
     storage = GetLocalStorage(aRv);
   } else {
     MOZ_ASSERT(storageArea->GetType() == Storage::SessionStorage);
     storage = GetSessionStorage(aRv);
   }
 
   if (aRv.Failed() || !storage) {
     return nullptr;
   }
 
   MOZ_ASSERT(storage);
-  MOZ_ASSERT(storage->IsForkOf(storageArea));
+  MOZ_ASSERT_IF(storageArea, storage->IsForkOf(storageArea));
 
   dict.mStorageArea = storage;
 
   RefPtr<StorageEvent> event = StorageEvent::Constructor(this, aType, dict);
   return event.forget();
 }
 
 void
@@ -12414,21 +12432,16 @@ nsGlobalWindow::CallOnChildren(Method aM
   }
 }
 
 nsresult
 nsGlobalWindow::FireDelayedDOMEvents()
 {
   FORWARD_TO_INNER(FireDelayedDOMEvents, (), NS_ERROR_UNEXPECTED);
 
-  for (uint32_t i = 0, len = mPendingStorageEvents.Length(); i < len; ++i) {
-    Observe(mPendingStorageEvents[i], "dom-storage2-changed", nullptr);
-    Observe(mPendingStorageEvents[i], "dom-private-storage2-changed", nullptr);
-  }
-
   if (mApplicationCache) {
     static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())->FirePendingEvents();
   }
 
   // Fires an offline status event if the offline status has changed
   FireOfflineStatusEventIfChanged();
 
   if (mNotifyIdleObserversIdleOnThaw) {
@@ -13288,16 +13301,23 @@ nsGlobalWindow::EventListenerAdded(nsIAt
 {
   if (aType == nsGkAtoms::onvrdisplayactivate ||
       aType == nsGkAtoms::onvrdisplayconnect ||
       aType == nsGkAtoms::onvrdisplaydeactivate ||
       aType == nsGkAtoms::onvrdisplaydisconnect ||
       aType == nsGkAtoms::onvrdisplaypresentchange) {
     NotifyVREventListenerAdded();
   }
+
+  // We need to initialize localStorage in order to receive notifications.
+  if (aType == nsGkAtoms::onstorage) {
+    ErrorResult rv;
+    GetLocalStorage(rv);
+    rv.SuppressException();
+  }
 }
 
 void
 nsGlobalWindow::NotifyVREventListenerAdded()
 {
   MOZ_ASSERT(IsInnerWindow());
   mHasVREvents = true;
   EnableVRUpdates();
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -1941,19 +1941,16 @@ protected:
   RefPtr<mozilla::EventListenerManager> mListenerManager;
   RefPtr<mozilla::dom::Location> mLocation;
   RefPtr<nsHistory>           mHistory;
   RefPtr<mozilla::dom::CustomElementRegistry> mCustomElements;
 
   // These member variables are used on both inner and the outer windows.
   nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
 
-  typedef nsTArray<RefPtr<mozilla::dom::StorageEvent>> nsStorageEventArray;
-  nsStorageEventArray mPendingStorageEvents;
-
   uint32_t mSuspendDepth;
   uint32_t mFreezeDepth;
 
   // the method that was used to focus mFocusedNode
   uint32_t mFocusMethod;
 
   uint32_t mSerial;
 
--- a/dom/canvas/test/chrome/chrome.ini
+++ b/dom/canvas/test/chrome/chrome.ini
@@ -1,8 +1,7 @@
 [DEFAULT]
-skip-if = os == 'android'
-support-files = nonchrome_webgl_debug_renderer_info.html
 
 [test_webgl_debug_renderer_info.html]
 subsuite = gpu
 [test_drawWindow_widget_layers.html]
+skip-if = os == 'android'
 support-files = ../file_drawWindow_source.html ../file_drawWindow_common.js
deleted file mode 100644
--- a/dom/canvas/test/chrome/nonchrome_webgl_debug_renderer_info.html
+++ /dev/null
@@ -1,83 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<script>
-
-
-// This file has the portion of the test_webgl_renderer_info chrome mochitest
-// that has to run as non-chrome to check that this WebGL extension is not exposed to content
-
-// we can't call the chrome Mochitest ok() function ourselves from non-chrome code.
-// So we remote it to the chrome test.
-
-function ok(res, msg) {
-  // Note we post to ourselves as posting to the chrome code doesn't seem to work here.
-  // This works by having the chrome code put an event handler on our own window.
-  window.postMessage({ subTestFinished: true, result: res, message: msg }, "*");
-}
-
-function messageListener(e) {
-  // This is how the chrome test tells us to start running -- we have to wait for this
-  // message to avoid running before it's set up its event handler.
-  if (e.data.run) {
-    var canBeUnprivileged = e.data.canBeUnprivileged;
-    run(canBeUnprivileged);
-  }
-}
-
-window.addEventListener("message", messageListener, true);
-
-function run(canBeUnprivileged) {
-  const UNMASKED_VENDOR_WEBGL = 0x9245;
-  const UNMASKED_RENDERER_WEBGL = 0x9246;
-
-  var canvas = document.createElement("canvas");
-  var gl = canvas.getContext("experimental-webgl");
-
-  ok(!gl.getError(), "getError on newly created WebGL context should return NO_ERROR");
-
-  ok(!gl.getParameter(UNMASKED_VENDOR_WEBGL) && gl.getError() == gl.INVALID_ENUM,
-      "Should not be able to query UNMASKED_VENDOR_WEBGL without having enabled the"
-      + " WEBGL_debug_renderer_info extension");
-  ok(!gl.getParameter(UNMASKED_RENDERER_WEBGL) && gl.getError() == gl.INVALID_ENUM,
-      "Should not be able to query UNMASKED_RENDERER_WEBGL without having enabled the"
-      + " WEBGL_debug_renderer_info extension");
-
-  var exts = gl.getSupportedExtensions();
-  if (canBeUnprivileged) {
-    ok(exts.indexOf("WEBGL_debug_renderer_info") != -1,
-       "WEBGL_debug_renderer_info should be listed by getSupportedExtensions in"
-       + " non-chrome contexts on non-RELEASE_OR_BETAs");
-
-    var ext = gl.getExtension("WEBGL_debug_renderer_info");
-    ok(!!ext,
-       "WEBGL_debug_renderer_info should be available through getExtension in non-chrome"
-       + " contexts on non-RELEASE_OR_BETAs");
-
-    ok(gl.getParameter(UNMASKED_VENDOR_WEBGL) && gl.getError() == gl.NO_ERROR,
-       "Should be able to query UNMASKED_VENDOR_WEBGL if enabling"
-       + " WEBGL_debug_renderer_info succeeded");
-    ok(gl.getParameter(UNMASKED_RENDERER_WEBGL) && gl.getError() == gl.NO_ERROR,
-       "Should be able to query UNMASKED_RENDERER_WEBGL if enabling"
-       + " WEBGL_debug_renderer_info succeeded");
-  } else {
-    ok(exts.indexOf("WEBGL_debug_renderer_info") == -1,
-       "WEBGL_debug_renderer_info should not be listed by getSupportedExtensions in"
-       + " non-chrome contexts");
-    var ext = gl.getExtension("WEBGL_debug_renderer_info");
-    ok(!ext,
-       "WEBGL_debug_renderer_info should not be available through getExtension in"
-       + " non-chrome contexts");
-
-    ok(!gl.getParameter(UNMASKED_VENDOR_WEBGL) && gl.getError() == gl.INVALID_ENUM,
-        "Should not be able to query UNMASKED_VENDOR_WEBGL if enabling"
-        + " WEBGL_debug_renderer_info failed");
-    ok(!gl.getParameter(UNMASKED_RENDERER_WEBGL) && gl.getError() == gl.INVALID_ENUM,
-        "Should not be able to query UNMASKED_RENDERER_WEBGL if enabling"
-        + " WEBGL_debug_renderer_info failed");
-
-  }
-  window.postMessage({allTestsFinished: true}, "*");
-}
-
-</script>
-</html>
--- a/dom/canvas/test/chrome/test_webgl_debug_renderer_info.html
+++ b/dom/canvas/test/chrome/test_webgl_debug_renderer_info.html
@@ -1,102 +1,51 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=666446
 -->
 <head>
-  <title>Test for WEBGL_debug_renderer_info chrome-only extension</title>
+  <title>Test that WEBGL_debug_renderer_info works in chrome code</title>
   <script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 </head>
 <body>
 
-<pre id="test">
 <script>
 
 const UNMASKED_VENDOR_WEBGL = 0x9245;
 const UNMASKED_RENDERER_WEBGL = 0x9246;
 
-
-var Cu = parent.Components.utils;
-Cu.import("resource://gre/modules/AppConstants.jsm");
-// This gives us `AppConstants` in the global scope.
-// We need this because we only expose debug_renderer_info #ifndef RELEASE_OR_BETA.
-// This should match AppConstants.RELEASE_OR_BETA.
-
-const canBeUnprivileged = !AppConstants.RELEASE_OR_BETA;
-
-
-function isNonEmptyString(s)
-{
+function isNonEmptyString(s) {
   return s && (typeof s) == "string";
 }
 
-function messageListener(e) {
-  if (e.data.allTestsFinished) {
-    SimpleTest.finish();
-  } else if (e.data.subTestFinished) {
-    ok(e.data.result, "content iframe: " + e.data.message);
-  }
-}
-
-function checkChromeCase(canvas) {
-
-  var gl = canvas.getContext("experimental-webgl");
-  ok(!gl.getError(), "getError on newly created WebGL context should return NO_ERROR");
+var canvas = document.createElement("canvas");
+var gl = canvas.getContext("experimental-webgl");
+ok(!gl.getError(), "getError on newly created WebGL context should return NO_ERROR");
 
-  ok(!gl.getParameter(UNMASKED_VENDOR_WEBGL) && gl.getError() == gl.INVALID_ENUM,
-     "Should not be able to query UNMASKED_VENDOR_WEBGL without having enabled the WEBGL_debug_renderer_info extension");
-  ok(!gl.getParameter(UNMASKED_RENDERER_WEBGL) && gl.getError() == gl.INVALID_ENUM,
-     "Should not be able to query UNMASKED_RENDERER_WEBGL without having enabled the WEBGL_debug_renderer_info extension");
-
-  var exts = gl.getSupportedExtensions();
-  ok(exts.indexOf("WEBGL_debug_renderer_info") != -1,
-     "WEBGL_debug_renderer_info should be listed by getSupportedExtensions in chrome contexts");
-  var ext = gl.getExtension("WEBGL_debug_renderer_info");
-  ok(ext,
-     "WEBGL_debug_renderer_info should be available through getExtension in chrome contexts");
-
-  ok(ext.UNMASKED_VENDOR_WEBGL == UNMASKED_VENDOR_WEBGL,
-     "UNMASKED_VENDOR_WEBGL has the correct value");
-  ok(ext.UNMASKED_RENDERER_WEBGL == UNMASKED_RENDERER_WEBGL,
-     "UNMASKED_RENDERER_WEBGL has the correct value");
+ok(!gl.getParameter(UNMASKED_VENDOR_WEBGL) && gl.getError() == gl.INVALID_ENUM,
+   "Should not be able to query UNMASKED_VENDOR_WEBGL without having enabled the WEBGL_debug_renderer_info extension");
+ok(!gl.getParameter(UNMASKED_RENDERER_WEBGL) && gl.getError() == gl.INVALID_ENUM,
+   "Should not be able to query UNMASKED_RENDERER_WEBGL without having enabled the WEBGL_debug_renderer_info extension");
 
-  ok(isNonEmptyString(gl.getParameter(UNMASKED_VENDOR_WEBGL)) && gl.getError() == gl.NO_ERROR,
-     "Should be able to query UNMASKED_VENDOR_WEBGL in chrome context with WEBGL_debug_renderer_info enabled");
-  ok(isNonEmptyString(gl.getParameter(UNMASKED_RENDERER_WEBGL)) && gl.getError() == gl.NO_ERROR,
-     "Should be able to query UNMASKED_RENDERER_WEBGL in chrome context with WEBGL_debug_renderer_info enabled");
-}
-
-function main()
-{
-  SimpleTest.waitForExplicitFinish();
-
-  checkChromeCase(document.createElement("canvas"));
-
-  // Now run the non-chrome code to verify the security of this WebGL chrome-only extension.
-
-  var iframe = document.createElement("iframe");
-  iframe.src = "http://mochi.test:8888/chrome/dom/canvas/test/chrome/nonchrome_webgl_debug_renderer_info.html";
+var exts = gl.getSupportedExtensions();
+ok(exts.indexOf("WEBGL_debug_renderer_info") != -1,
+   "WEBGL_debug_renderer_info should be listed by getSupportedExtensions in chrome contexts");
+var ext = gl.getExtension("WEBGL_debug_renderer_info");
+ok(ext,
+   "WEBGL_debug_renderer_info should be available through getExtension in chrome contexts");
 
-  iframe.onload = function () {
-
-    // test that chrome can get WEBGL_debug_renderer_info on a canvas on the iframe...
-    // this is useful to check in itself, and is also useful so the subsequent non-chrome test
-    // will also test that doing so doesn't confuse our chrome-only check.
-    checkChromeCase(iframe.contentDocument.createElement("canvas"));
+ok(ext.UNMASKED_VENDOR_WEBGL == UNMASKED_VENDOR_WEBGL,
+   "UNMASKED_VENDOR_WEBGL has the correct value");
+ok(ext.UNMASKED_RENDERER_WEBGL == UNMASKED_RENDERER_WEBGL,
+   "UNMASKED_RENDERER_WEBGL has the correct value");
 
-    iframe.contentWindow.addEventListener("message", messageListener);
-    iframe.contentWindow.postMessage({run: true,
-                                      canBeUnprivileged: canBeUnprivileged},
-                                     "*");
-  };
+ok(isNonEmptyString(gl.getParameter(UNMASKED_VENDOR_WEBGL)) && gl.getError() == gl.NO_ERROR,
+   "Should be able to query UNMASKED_VENDOR_WEBGL in chrome context with WEBGL_debug_renderer_info enabled");
+ok(isNonEmptyString(gl.getParameter(UNMASKED_RENDERER_WEBGL)) && gl.getError() == gl.NO_ERROR,
+   "Should be able to query UNMASKED_RENDERER_WEBGL in chrome context with WEBGL_debug_renderer_info enabled");
 
-  document.body.appendChild(iframe);
-}
-
-window.onload = main;
 </script>
-</pre>
 </body>
 </html>
--- a/dom/canvas/test/webgl-mochitest/ensure-exts/test_common.html
+++ b/dom/canvas/test/webgl-mochitest/ensure-exts/test_common.html
@@ -26,17 +26,17 @@ var defaultExts = [
     ['OES_element_index_uint'        , [ENSURE          , FORBID          ]],
     ['OES_standard_derivatives'      , [MACHINE_SPECIFIC, FORBID          ]],
     ['OES_texture_float'             , [ENSURE          , FORBID          ]],
     ['OES_texture_float_linear'      , [ENSURE          , ENSURE          ]],
     ['OES_texture_half_float'        , [ENSURE          , FORBID          ]],
     ['OES_texture_half_float_linear' , [ENSURE          , FORBID          ]],
     ['OES_vertex_array_object'       , [ENSURE          , FORBID          ]],
     ['WEBGL_compressed_texture_s3tc' , [MACHINE_SPECIFIC, MACHINE_SPECIFIC]],
-//  ['WEBGL_debug_renderer_info'     , [FORBID          , FORBID          ]], // Complicated!
+    ['WEBGL_debug_renderer_info'     , [ENSURE          , ENSURE          ]],
     ['WEBGL_debug_shaders'           , [FORBID          , FORBID          ]],
     ['WEBGL_depth_texture'           , [MACHINE_SPECIFIC, FORBID          ]],
     ['WEBGL_draw_buffers'            , [MACHINE_SPECIFIC, FORBID          ]],
     ['WEBGL_lose_context'            , [ENSURE          , ENSURE          ]],
 
     // Community Approved
     ['EXT_color_buffer_float'        , [FORBID          , ENSURE          ]],
     ['EXT_color_buffer_half_float'   , [MACHINE_SPECIFIC, FORBID          ]],
--- a/dom/events/StorageEvent.h
+++ b/dom/events/StorageEvent.h
@@ -8,16 +8,18 @@
 #define mozilla_dom_StorageEvent_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/StorageEventBinding.h"
 
+class nsIPrincipal;
+
 namespace mozilla {
 namespace dom {
 
 class Storage;
 
 class StorageEvent : public Event
 {
 public:
@@ -29,16 +31,17 @@ public:
 protected:
   virtual ~StorageEvent();
 
   nsString mKey;
   nsString mOldValue;
   nsString mNewValue;
   nsString mUrl;
   RefPtr<Storage> mStorageArea;
+  nsCOMPtr<nsIPrincipal> mPrincipal;
 
 public:
   virtual StorageEvent* AsStorageEvent();
 
   virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   static already_AddRefed<StorageEvent>
   Constructor(EventTarget* aOwner, const nsAString& aType,
@@ -74,14 +77,26 @@ public:
   {
     aRetVal = mUrl;
   }
 
   Storage* GetStorageArea() const
   {
     return mStorageArea;
   }
+
+  // Non WebIDL methods
+  void SetPrincipal(nsIPrincipal* aPrincipal)
+  {
+    MOZ_ASSERT(!mPrincipal);
+    mPrincipal = aPrincipal;
+  }
+
+  nsIPrincipal* GetPrincipal() const
+  {
+    return mPrincipal;
+  }
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_StorageEvent_h
--- a/dom/html/HTMLTrackElement.cpp
+++ b/dom/html/HTMLTrackElement.cpp
@@ -288,37 +288,66 @@ HTMLTrackElement::LoadResource()
   LOG(LogLevel::Info, ("%p Trying to load from src=%s", this,
       NS_ConvertUTF16toUTF8(src).get()));
 
   if (mChannel) {
     mChannel->Cancel(NS_BINDING_ABORTED);
     mChannel = nullptr;
   }
 
+  // According to https://www.w3.org/TR/html5/embedded-content-0.html#sourcing-out-of-band-text-tracks
+  //
+  // "8: If the track element's parent is a media element then let CORS mode
+  // be the state of the parent media element's crossorigin content attribute.
+  // Otherwise, let CORS mode be No CORS."
+  //
+  CORSMode corsMode = mMediaParent ? mMediaParent->GetCORSMode() : CORS_NONE;
+
+  // Determine the security flag based on corsMode.
+  nsSecurityFlags secFlags;
+  if (CORS_NONE == corsMode) {
+    // Same-origin is required for track element.
+    secFlags = nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS;
+  } else {
+    secFlags = nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
+    if (CORS_ANONYMOUS == corsMode) {
+      secFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN;
+    } else if (CORS_USE_CREDENTIALS == corsMode) {
+      secFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
+    } else {
+      NS_WARNING("Unknown CORS mode.");
+      secFlags = nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS;
+    }
+  }
+
   nsCOMPtr<nsIChannel> channel;
   nsCOMPtr<nsILoadGroup> loadGroup = OwnerDoc()->GetDocumentLoadGroup();
   rv = NS_NewChannel(getter_AddRefs(channel),
                      uri,
                      static_cast<Element*>(this),
-                     nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS,
+                     secFlags,
                      nsIContentPolicy::TYPE_INTERNAL_TRACK,
                      loadGroup,
                      nullptr,   // aCallbacks
                      nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI);
 
   NS_ENSURE_TRUE_VOID(NS_SUCCEEDED(rv));
 
   mListener = new WebVTTListener(this);
   rv = mListener->LoadResource();
   NS_ENSURE_TRUE_VOID(NS_SUCCEEDED(rv));
   channel->SetNotificationCallbacks(mListener);
 
   LOG(LogLevel::Debug, ("opening webvtt channel"));
   rv = channel->AsyncOpen2(mListener);
-  NS_ENSURE_TRUE_VOID(NS_SUCCEEDED(rv));
+
+  if (NS_FAILED(rv)) {
+    SetReadyState(TextTrackReadyState::FailedToLoad);
+    return;
+  }
 
   mChannel = channel;
 }
 
 nsresult
 HTMLTrackElement::BindToTree(nsIDocument* aDocument,
                              nsIContent* aParent,
                              nsIContent* aBindingParent,
--- a/dom/indexedDB/test/test_filehandle_workers.html
+++ b/dom/indexedDB/test/test_filehandle_workers.html
@@ -46,54 +46,52 @@
       onmessage = function(event) {
         throw("Shouldn't be called!");
       }
     }
 
     let url =
       URL.createObjectURL(new Blob(["(", dummyWorkerScript.toSource(), ")()"]));
 
-    let worker = new Worker(url);
+    let worker1 = new Worker(url);
     try {
-      worker.postMessage(mutableFile);
+      worker1.postMessage(mutableFile);
       ok(false, "Should have thrown!");
     }
     catch (e) {
       ok(e instanceof DOMException, "Got exception.");
       is(e.name, "DataCloneError", "Good error.");
       is(e.code, DOMException.DATA_CLONE_ERR, "Good error code.")
     }
-    worker.terminate();
 
     mutableFile.onerror = errorHandler;
 
     let fileHandle = mutableFile.open("readwrite");
 
     request = fileHandle.write(testBuffer);
     request.onsuccess = grabEventAndContinueHandler;
     event = yield undefined;
 
     request = mutableFile.getFile();
     request.onsuccess = grabEventAndContinueHandler;
     event = yield undefined;
 
     let file = event.target.result;
 
-    worker = new Worker(url);
+    let worker2 = new Worker(url);
     URL.revokeObjectURL(url);
     try {
-      worker.postMessage(file);
+      worker2.postMessage(file);
       ok(false, "Should have thrown!");
     }
     catch (e) {
       ok(e instanceof DOMException, "Got exception.");
       is(e.name, "DataCloneError", "Good error.");
       is(e.code, DOMException.DATA_CLONE_ERR, "Good error code.")
     }
-    worker.terminate();
 
     let objectStore =
       db.transaction("Foo", "readwrite").objectStore("Foo");
 
     request = objectStore.add(mutableFile, 42);
     request.onsuccess = grabEventAndContinueHandler;
     event = yield undefined;
 
@@ -121,24 +119,28 @@
         request.onerror = function(event) {
           postMessage("error");
         }
       }
     }
 
     url = URL.createObjectURL(new Blob(["(", workerScript.toSource(), ")()"]));
 
-    worker = new Worker(url);
+    let worker3 = new Worker(url);
     URL.revokeObjectURL(url);
-    worker.postMessage(name);
-    worker.onmessage = grabEventAndContinueHandler;
+    worker3.postMessage(name);
+    worker3.onmessage = grabEventAndContinueHandler;
     event = yield undefined;
 
     is(event.data, "success", "Good response.");
-    worker.terminate();
+
+    todo(false, "Terminate all workers at the end of the test to work around bug 1340941.");
+    worker1.terminate();
+    worker2.terminate();
+    worker3.terminate();
 
     finishTest();
   }
   </script>
   <script type="text/javascript;version=1.7" src="file.js"></script>
   <script type="text/javascript;version=1.7" src="helpers.js"></script>
 
 </head>
--- a/dom/interfaces/storage/nsIDOMStorageManager.idl
+++ b/dom/interfaces/storage/nsIDOMStorageManager.idl
@@ -15,18 +15,25 @@ interface mozIDOMWindow;
  * "@mozilla.org/dom/sessionStorage-manager;1" contract IDs.
  */
 [scriptable, uuid(a20c742e-3ed1-44fb-b897-4080a75b1662)]
 interface nsIDOMStorageManager : nsISupports
 {
   /**
    * This starts async preloading of a storage cache for scope
    * defined by the principal.
+   *
+   * Because of how multi-e10s support was implemented in bug 1285898, the
+   * StorageCache instance can no longer use a timer to keep itself alive.  So a
+   * Storage instance is returned if precaching believes the principal may have
+   * localStorage data.  (Previously the StorageCache would be brought into
+   * existence and kept alive by the timer so that it could be returned if a
+   * call to createStorage was made due to a request by the page.)
    */
-  void precacheStorage(in nsIPrincipal aPrincipal);
+  nsIDOMStorage precacheStorage(in nsIPrincipal aPrincipal);
 
   /**
    * Returns instance of DOM storage object for given principal.
    * A new object is always returned and it is ensured there is
    * a storage for the scope created.
    *
    * @param aWindow
    *    The parent window.
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -29,16 +29,17 @@
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/ExternalHelperAppChild.h"
 #include "mozilla/dom/FileCreatorHelper.h"
 #include "mozilla/dom/FlyWebPublishedServerIPC.h"
 #include "mozilla/dom/GetFilesHelper.h"
 #include "mozilla/dom/MemoryReportRequest.h"
 #include "mozilla/dom/ProcessGlobal.h"
 #include "mozilla/dom/PushNotifier.h"
+#include "mozilla/dom/Storage.h"
 #include "mozilla/dom/StorageIPC.h"
 #include "mozilla/dom/TabGroup.h"
 #include "mozilla/dom/workers/ServiceWorkerManager.h"
 #include "mozilla/dom/nsIContentChild.h"
 #include "mozilla/dom/URLClassifierChild.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/psm/PSMContentListener.h"
@@ -3030,16 +3031,30 @@ ContentChild::RecvBlobURLRegistration(co
 
 mozilla::ipc::IPCResult
 ContentChild::RecvBlobURLUnregistration(const nsCString& aURI)
 {
   nsHostObjectProtocolHandler::RemoveDataEntry(aURI);
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult
+ContentChild::RecvDispatchLocalStorageChange(const nsString& aDocumentURI,
+                                             const nsString& aKey,
+                                             const nsString& aOldValue,
+                                             const nsString& aNewValue,
+                                             const IPC::Principal& aPrincipal,
+                                             const bool& aIsPrivate)
+{
+  Storage::DispatchStorageEvent(Storage::LocalStorage,
+                                aDocumentURI, aKey, aOldValue, aNewValue,
+                                aPrincipal, aIsPrivate, nullptr, true);
+  return IPC_OK();
+}
+
 #if defined(XP_WIN) && defined(ACCESSIBILITY)
 bool
 ContentChild::SendGetA11yContentId()
 {
   return PContentChild::SendGetA11yContentId(&mMsaaID);
 }
 #endif // defined(XP_WIN) && defined(ACCESSIBILITY)
 
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -386,16 +386,24 @@ public:
   const nsAString& GetRemoteType() const;
 
   virtual mozilla::ipc::IPCResult
   RecvInitServiceWorkers(const ServiceWorkerConfiguration& aConfig) override;
 
   virtual mozilla::ipc::IPCResult
   RecvInitBlobURLs(nsTArray<BlobURLRegistrationData>&& aRegistations) override;
 
+  virtual mozilla::ipc::IPCResult
+  RecvDispatchLocalStorageChange(const nsString& aDocumentURI,
+                                 const nsString& aKey,
+                                 const nsString& aOldValue,
+                                 const nsString& aNewValue,
+                                 const IPC::Principal& aPrincipal,
+                                 const bool& aIsPrivate) override;
+
   virtual mozilla::ipc::IPCResult RecvLastPrivateDocShellDestroyed() override;
 
   virtual mozilla::ipc::IPCResult RecvFilePathUpdate(const nsString& aStorageType,
                                                      const nsString& aStorageName,
                                                      const nsString& aPath,
                                                      const nsCString& aReason) override;
 
   virtual mozilla::ipc::IPCResult
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -46,16 +46,17 @@
 #include "mozilla/dom/GetFilesHelper.h"
 #include "mozilla/dom/GeolocationBinding.h"
 #include "mozilla/dom/MemoryReportRequest.h"
 #include "mozilla/dom/Notification.h"
 #include "mozilla/dom/PContentBridgeParent.h"
 #include "mozilla/dom/PContentPermissionRequestParent.h"
 #include "mozilla/dom/PCycleCollectWithLogsParent.h"
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
+#include "mozilla/dom/Storage.h"
 #include "mozilla/dom/StorageIPC.h"
 #include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h"
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "mozilla/dom/Permissions.h"
 #include "mozilla/dom/PresentationParent.h"
 #include "mozilla/dom/PPresentationParent.h"
 #include "mozilla/dom/PushNotifier.h"
 #include "mozilla/dom/FlyWebPublishedServerIPC.h"
@@ -4767,16 +4768,35 @@ ContentParent::RecvUnstoreAndBroadcastBl
                                                false /* Don't broadcast */);
   BroadcastBlobURLUnregistration(aURI, this);
   mBlobURLs.RemoveElement(aURI);
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
+ContentParent::RecvBroadcastLocalStorageChange(const nsString& aDocumentURI,
+                                               const nsString& aKey,
+                                               const nsString& aOldValue,
+                                               const nsString& aNewValue,
+                                               const Principal& aPrincipal,
+                                               const bool& aIsPrivate)
+{
+  for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
+    if (cp != this) {
+      Unused << cp->SendDispatchLocalStorageChange(
+        nsString(aDocumentURI), nsString(aKey), nsString(aOldValue),
+        nsString(aNewValue), IPC::Principal(aPrincipal), aIsPrivate);
+    }
+  }
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 ContentParent::RecvGetA11yContentId(uint32_t* aContentId)
 {
 #if defined(XP_WIN32) && defined(ACCESSIBILITY)
   *aContentId = a11y::AccessibleWrap::GetContentProcessIdFor(ChildID());
   MOZ_ASSERT(*aContentId);
   return IPC_OK();
 #else
   return IPC_FAIL_NO_REASON(this);
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -570,16 +570,24 @@ public:
   RecvStoreAndBroadcastBlobURLRegistration(const nsCString& aURI,
                                            PBlobParent* aBlobParent,
                                            const Principal& aPrincipal) override;
 
   virtual mozilla::ipc::IPCResult
   RecvUnstoreAndBroadcastBlobURLUnregistration(const nsCString& aURI) override;
 
   virtual mozilla::ipc::IPCResult
+  RecvBroadcastLocalStorageChange(const nsString& aDocumentURI,
+                                  const nsString& aKey,
+                                  const nsString& aOldValue,
+                                  const nsString& aNewValue,
+                                  const IPC::Principal& aPrincipal,
+                                  const bool& aIsPrivate) override;
+
+  virtual mozilla::ipc::IPCResult
   RecvGetA11yContentId(uint32_t* aContentId) override;
 
   virtual int32_t Pid() const override;
 
   virtual PURLClassifierParent*
   AllocPURLClassifierParent(const Principal& aPrincipal,
                             const bool& aUseTrackingProtection,
                             bool* aSuccess) override;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -671,16 +671,22 @@ child:
 
     async GetFilesResponse(nsID aID, GetFilesResponseResult aResult);
 
     async BlobURLRegistration(nsCString aURI, PBlob aBlob,
                               Principal aPrincipal);
 
     async BlobURLUnregistration(nsCString aURI);
 
+    async DispatchLocalStorageChange(nsString documentURI,
+                                     nsString key,
+                                     nsString oldValue,
+                                     nsString newValue,
+                                     Principal principal,
+                                     bool isPrivate);
 
     async GMPsChanged(GMPCapabilityData[] capabilities);
 
 
     async FileCreationResponse(nsID aID, FileCreationResult aResult);
 
     /**
      * Sending an activate message moves focus to the child.
@@ -1173,16 +1179,23 @@ parent:
                                nsString aName, bool lastModifiedPassed,
                                int64_t lastModified, bool aIsFromNsIFile);
 
      async StoreAndBroadcastBlobURLRegistration(nsCString url, PBlob blob,
                                                 Principal principal);
 
      async UnstoreAndBroadcastBlobURLUnregistration(nsCString url);
 
+     async BroadcastLocalStorageChange(nsString documentURI,
+                                       nsString key,
+                                       nsString oldValue,
+                                       nsString newValue,
+                                       Principal principal,
+                                       bool isPrivate);
+
     /**
      * Messages for communicating child Telemetry to the parent process
      */
     async AccumulateChildHistograms(Accumulation[] accumulations);
     async AccumulateChildKeyedHistograms(KeyedAccumulation[] accumulations);
     async UpdateChildScalars(ScalarAction[] updates);
     async UpdateChildKeyedScalars(KeyedScalarAction[] updates);
 
--- a/dom/media/MediaStreamGraph.h
+++ b/dom/media/MediaStreamGraph.h
@@ -764,17 +764,17 @@ public:
   void FinishAddTracks();
 
   /**
    * Append media data to a track. Ownership of aSegment remains with the caller,
    * but aSegment is emptied.
    * Returns false if the data was not appended because no such track exists
    * or the stream was already finished.
    */
-  bool AppendToTrack(TrackID aID, MediaSegment* aSegment, MediaSegment *aRawSegment = nullptr);
+  virtual bool AppendToTrack(TrackID aID, MediaSegment* aSegment, MediaSegment *aRawSegment = nullptr);
   /**
    * Get the stream time of the end of the data that has been appended so far.
    * Can be called from any thread but won't be useful if it can race with
    * an AppendToTrack call, so should probably just be called from the thread
    * that also calls AppendToTrack.
    */
   StreamTime GetEndOfAppendedData(TrackID aID);
   /**
--- a/dom/media/MediaStreamTrack.cpp
+++ b/dom/media/MediaStreamTrack.cpp
@@ -125,21 +125,22 @@ MediaStreamTrack::MediaStreamTrack(DOMMe
                                    MediaStreamTrackSource* aSource,
                                    const MediaTrackConstraints& aConstraints)
   : mOwningStream(aStream), mTrackID(aTrackID),
     mInputTrackID(aInputTrackID), mSource(aSource),
     mPrincipal(aSource->GetPrincipal()),
     mReadyState(MediaStreamTrackState::Live),
     mEnabled(true), mConstraints(aConstraints)
 {
-
   GetSource().RegisterSink(this);
 
-  mPrincipalHandleListener = new PrincipalHandleListener(this);
-  AddListener(mPrincipalHandleListener);
+  if (GetOwnedStream()) {
+    mPrincipalHandleListener = new PrincipalHandleListener(this);
+    AddListener(mPrincipalHandleListener);
+  }
 
   nsresult rv;
   nsCOMPtr<nsIUUIDGenerator> uuidgen =
     do_GetService("@mozilla.org/uuid-generator;1", &rv);
 
   nsID uuid;
   memset(&uuid, 0, sizeof(uuid));
   if (uuidgen) {
--- a/dom/media/MediaStreamTrack.h
+++ b/dom/media/MediaStreamTrack.h
@@ -389,31 +389,31 @@ public:
    * Remove an added MediaStreamTrackConsumer from this track.
    */
   void RemoveConsumer(MediaStreamTrackConsumer* aConsumer);
 
   /**
    * Adds a MediaStreamTrackListener to the MediaStreamGraph representation of
    * this track.
    */
-  void AddListener(MediaStreamTrackListener* aListener);
+  virtual void AddListener(MediaStreamTrackListener* aListener);
 
   /**
    * Removes a MediaStreamTrackListener from the MediaStreamGraph representation
    * of this track.
    */
   void RemoveListener(MediaStreamTrackListener* aListener);
 
   /**
    * Attempts to add a direct track listener to this track.
    * Callers must listen to the NotifyInstalled event to know if installing
    * the listener succeeded (tracks originating from SourceMediaStreams) or
    * failed (e.g., WebAudio originated tracks).
    */
-  void AddDirectListener(DirectMediaStreamTrackListener *aListener);
+  virtual void AddDirectListener(DirectMediaStreamTrackListener *aListener);
   void RemoveDirectListener(DirectMediaStreamTrackListener  *aListener);
 
   /**
    * Sets up a MediaInputPort from the underlying track that this
    * MediaStreamTrack represents, to aStream, and returns it.
    */
   already_AddRefed<MediaInputPort> ForwardTrackContentsTo(ProcessedMediaStream* aStream,
                                                           TrackID aDestinationTrackID = TRACK_ANY);
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -2715,16 +2715,36 @@ ForceWindowless(InfallibleTArray<nsCStri
             values[wmodeAttributeIndex].Assign(opaqueAttributeValue);
         }
     } else {
         names.AppendElement(wmodeAttributeName);
         values.AppendElement(opaqueAttributeValue);
     }
 }
 #endif // windows or linux
+#if defined(XP_WIN)
+static void
+ForceDirect(InfallibleTArray<nsCString>& names,
+            InfallibleTArray<nsCString>& values)
+{
+    nsCaseInsensitiveUTF8StringArrayComparator comparator;
+    NS_NAMED_LITERAL_CSTRING(wmodeAttributeName, "wmode");
+    NS_NAMED_LITERAL_CSTRING(directAttributeValue, "direct");
+    auto wmodeAttributeIndex =
+        names.IndexOf(wmodeAttributeName, 0, comparator);
+    if (wmodeAttributeIndex != names.NoIndex) {
+        if (values[wmodeAttributeIndex].EqualsLiteral("window")) {
+            values[wmodeAttributeIndex].Assign(directAttributeValue);
+        }
+    } else {
+        names.AppendElement(wmodeAttributeName);
+        values.AppendElement(directAttributeValue);
+    }
+}
+#endif // windows
 
 nsresult
 PluginModuleParent::NPP_NewInternal(NPMIMEType pluginType, NPP instance,
                                     uint16_t mode,
                                     InfallibleTArray<nsCString>& names,
                                     InfallibleTArray<nsCString>& values,
                                     NPSavedData* saved, NPError* error)
 {
@@ -2753,16 +2773,18 @@ PluginModuleParent::NPP_NewInternal(NPMI
     PluginInstanceParent* parentInstance =
         new PluginInstanceParent(this, instance, strPluginType, mNPNIface);
 
     if (mIsFlashPlugin) {
         parentInstance->InitMetadata(strPluginType, srcAttribute);
 #ifdef XP_WIN
         bool supportsAsyncRender =
           Preferences::GetBool("dom.ipc.plugins.asyncdrawing.enabled", false);
+        bool supportsForceDirect =
+          Preferences::GetBool("dom.ipc.plugins.forcedirect.enabled", false);
         if (supportsAsyncRender) {
           // Prefs indicates we want async plugin rendering, make sure
           // the flash module has support.
           CallModuleSupportsAsyncRender(&supportsAsyncRender);
         }
 #ifdef _WIN64
         // For 64-bit builds force windowless if the flash library doesn't support
         // async rendering regardless of sandbox level.
@@ -2773,16 +2795,24 @@ PluginModuleParent::NPP_NewInternal(NPMI
         if (!supportsAsyncRender && mSandboxLevel >= 2) {
 #endif
             ForceWindowless(names, values);
         }
 #elif defined(MOZ_WIDGET_GTK)
         // We no longer support windowed mode on Linux.
         ForceWindowless(names, values);
 #endif
+#ifdef XP_WIN
+        // For all builds that use async rendering force use of the accelerated
+        // direct path for flash objects that have wmode=window or no wmode
+        // specified.
+        if (supportsAsyncRender && supportsForceDirect) {
+            ForceDirect(names, values);
+        }
+#endif
     }
 
     // Release the surrogate reference that was in pdata
     RefPtr<PluginAsyncSurrogate> surrogate(
         dont_AddRef(PluginAsyncSurrogate::Cast(instance)));
     // Now replace it with the instance
     instance->pdata = static_cast<PluginDataResolver*>(parentInstance);
 
--- a/dom/storage/Storage.cpp
+++ b/dom/storage/Storage.cpp
@@ -9,27 +9,33 @@
 #include "StorageManager.h"
 
 #include "nsIObserverService.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
 #include "nsICookiePermission.h"
 
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/StorageBinding.h"
 #include "mozilla/dom/StorageEvent.h"
 #include "mozilla/dom/StorageEventBinding.h"
 #include "mozilla/Services.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/EnumSet.h"
 #include "nsThreadUtils.h"
 #include "nsContentUtils.h"
 #include "nsServiceManagerUtils.h"
 
 namespace mozilla {
+
+using namespace ipc;
+
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Storage, mManager, mPrincipal, mWindow)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(Storage)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(Storage)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Storage)
@@ -53,17 +59,16 @@ Storage::Storage(nsPIDOMWindowInner* aWi
   , mIsPrivate(aIsPrivate)
   , mIsSessionOnly(false)
 {
   mCache->Preload();
 }
 
 Storage::~Storage()
 {
-  mCache->KeepAlive();
 }
 
 /* virtual */ JSObject*
 Storage::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return StorageBinding::Wrap(aCx, this, aGivenProto);
 }
 
@@ -211,37 +216,107 @@ StorageNotifierRunnable::Run()
 
 } // namespace
 
 void
 Storage::BroadcastChangeNotification(const nsSubstring& aKey,
                                      const nsSubstring& aOldValue,
                                      const nsSubstring& aNewValue)
 {
+  if (!XRE_IsParentProcess() && GetType() == LocalStorage && mPrincipal) {
+    // If we are in a child process, we want to send a message to the parent in
+    // order to broadcast the StorageEvent correctly to any child process.
+    dom::ContentChild* cc = dom::ContentChild::GetSingleton();
+    Unused << NS_WARN_IF(!cc->SendBroadcastLocalStorageChange(
+      mDocumentURI, nsString(aKey), nsString(aOldValue), nsString(aNewValue),
+      IPC::Principal(mPrincipal), mIsPrivate));
+  }
+
+  DispatchStorageEvent(GetType(), mDocumentURI, aKey, aOldValue, aNewValue,
+                       mPrincipal, mIsPrivate, this, false);
+}
+
+/* static */ void
+Storage::DispatchStorageEvent(StorageType aStorageType,
+                              const nsAString& aDocumentURI,
+                              const nsAString& aKey,
+                              const nsAString& aOldValue,
+                              const nsAString& aNewValue,
+                              nsIPrincipal* aPrincipal,
+                              bool aIsPrivate,
+                              Storage* aStorage,
+                              bool aImmediateDispatch)
+{
   StorageEventInit dict;
   dict.mBubbles = false;
   dict.mCancelable = false;
   dict.mKey = aKey;
   dict.mNewValue = aNewValue;
   dict.mOldValue = aOldValue;
-  dict.mStorageArea = this;
-  dict.mUrl = mDocumentURI;
+  dict.mStorageArea = aStorage;
+  dict.mUrl = aDocumentURI;
 
   // Note, this DOM event should never reach JS. It is cloned later in
   // nsGlobalWindow.
   RefPtr<StorageEvent> event =
     StorageEvent::Constructor(nullptr, NS_LITERAL_STRING("storage"), dict);
 
+  event->SetPrincipal(aPrincipal);
+
   RefPtr<StorageNotifierRunnable> r =
     new StorageNotifierRunnable(event,
-                                GetType() == LocalStorage
+                                aStorageType == LocalStorage
                                   ? u"localStorage"
                                   : u"sessionStorage",
-                                IsPrivate());
-  NS_DispatchToMainThread(r);
+                                aIsPrivate);
+
+  if (aImmediateDispatch) {
+    Unused << r->Run();
+  } else {
+    NS_DispatchToMainThread(r);
+  }
+
+  // If we are in the parent process and we have the principal, we want to
+  // broadcast this event to every other process.
+  if (aStorageType == LocalStorage && XRE_IsParentProcess() && aPrincipal) {
+    for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
+      Unused << cp->SendDispatchLocalStorageChange(
+        nsString(aDocumentURI), nsString(aKey), nsString(aOldValue),
+        nsString(aNewValue), IPC::Principal(aPrincipal), aIsPrivate);
+    }
+  }
+}
+
+void
+Storage::ApplyEvent(StorageEvent* aStorageEvent)
+{
+  MOZ_ASSERT(aStorageEvent);
+
+  nsAutoString key;
+  nsAutoString old;
+  nsAutoString value;
+
+  aStorageEvent->GetKey(key);
+  aStorageEvent->GetNewValue(value);
+
+  // No key means clearing the full storage.
+  if (key.IsVoid()) {
+    MOZ_ASSERT(value.IsVoid());
+    mCache->Clear(this, StorageCache::E10sPropagated);
+    return;
+  }
+
+  // No new value means removing the key.
+  if (value.IsVoid()) {
+    mCache->RemoveItem(this, key, old, StorageCache::E10sPropagated);
+    return;
+  }
+
+  // Otherwise, we set the new value.
+  mCache->SetItem(this, key, value, old, StorageCache::E10sPropagated);
 }
 
 static const char kPermissionType[] = "cookie";
 static const char kStorageEnabled[] = "dom.storage.enabled";
 
 bool
 Storage::CanUseStorage(nsIPrincipal& aSubjectPrincipal)
 {
--- a/dom/storage/Storage.h
+++ b/dom/storage/Storage.h
@@ -19,16 +19,17 @@
 class nsIPrincipal;
 class nsPIDOMWindowInner;
 
 namespace mozilla {
 namespace dom {
 
 class StorageManagerBase;
 class StorageCache;
+class StorageEvent;
 
 class Storage final
   : public nsIDOMStorage
   , public nsSupportsWeakReference
   , public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
@@ -124,16 +125,38 @@ public:
   bool IsSessionOnly() const { return mIsSessionOnly; }
 
   bool IsForkOf(const Storage* aOther) const
   {
     MOZ_ASSERT(aOther);
     return mCache == aOther->mCache;
   }
 
+  // aStorage can be null if this method is called by ContentChild.
+  //
+  // aImmediateDispatch is for use by (main-thread) IPC code so that PContent
+  // ordering can be maintained.  Without this, the event would be enqueued and
+  // run in a future turn of the event loop, potentially allowing other PContent
+  // Recv* methods to trigger script that wants to assume our localstorage
+  // changes have already been applied.  This is the case for message manager
+  // messages which are used by ContentTask testing logic and webextensions.
+  static void
+  DispatchStorageEvent(StorageType aStorageType,
+                       const nsAString& aDocumentURI,
+                       const nsAString& aKey,
+                       const nsAString& aOldValue,
+                       const nsAString& aNewValue,
+                       nsIPrincipal* aPrincipal,
+                       bool aIsPrivate,
+                       Storage* aStorage,
+                       bool aImmediateDispatch);
+
+  void
+  ApplyEvent(StorageEvent* aStorageEvent);
+
 protected:
   // The method checks whether the caller can use a storage.
   // CanUseStorage is called before any DOM initiated operation
   // on a storage is about to happen and ensures that the storage's
   // session-only flag is properly set according the current settings.
   // It is an optimization since the privileges check and session only
   // state determination are complex and share the code (comes hand in
   // hand together).
--- a/dom/storage/StorageCache.cpp
+++ b/dom/storage/StorageCache.cpp
@@ -54,17 +54,17 @@ GetDataSetIndex(const Storage* aStorage)
 }
 
 } // namespace
 
 // StorageCacheBridge
 
 NS_IMPL_ADDREF(StorageCacheBridge)
 
-// Since there is no consumer of return value of Release, we can turn this 
+// Since there is no consumer of return value of Release, we can turn this
 // method to void to make implementation of asynchronous StorageCache::Release
 // much simpler.
 NS_IMETHODIMP_(void) StorageCacheBridge::Release(void)
 {
   MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
   nsrefcnt count = --mRefCnt;
   NS_LOG_RELEASE(this, count, "StorageCacheBridge");
   if (0 == count) {
@@ -144,17 +144,17 @@ StorageCache::Init(StorageManagerBase* a
   if (mPersistent) {
     mManager = aManager;
     Preload();
   }
 
   // Check the quota string has (or has not) the identical origin suffix as
   // this storage cache is bound to.
   MOZ_ASSERT(StringBeginsWith(mQuotaOriginScope, mOriginSuffix));
-  MOZ_ASSERT(mOriginSuffix.IsEmpty() != StringBeginsWith(mQuotaOriginScope, 
+  MOZ_ASSERT(mOriginSuffix.IsEmpty() != StringBeginsWith(mQuotaOriginScope,
                                                          NS_LITERAL_CSTRING("^")));
 
   mUsage = aManager->GetOriginUsage(mQuotaOriginScope);
 }
 
 inline bool
 StorageCache::Persist(const Storage* aStorage) const
 {
@@ -193,38 +193,43 @@ StorageCache::DataSet(const Storage* aSt
     // for all session only data
     ProcessUsageDelta(kSessionSet, defaultSet.mOriginQuotaUsage);
   }
 
   return mData[index];
 }
 
 bool
-StorageCache::ProcessUsageDelta(const Storage* aStorage, int64_t aDelta)
+StorageCache::ProcessUsageDelta(const Storage* aStorage, int64_t aDelta,
+                                const MutationSource aSource)
 {
-  return ProcessUsageDelta(GetDataSetIndex(aStorage), aDelta);
+  return ProcessUsageDelta(GetDataSetIndex(aStorage), aDelta, aSource);
 }
 
 bool
-StorageCache::ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta)
+StorageCache::ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta,
+                                const MutationSource aSource)
 {
   // Check if we are in a low disk space situation
-  if (aDelta > 0 && mManager && mManager->IsLowDiskSpace()) {
+  if (aSource == ContentMutation &&
+      aDelta > 0 && mManager && mManager->IsLowDiskSpace()) {
     return false;
   }
 
   // Check limit per this origin
   Data& data = mData[aGetDataSetIndex];
   uint64_t newOriginUsage = data.mOriginQuotaUsage + aDelta;
-  if (aDelta > 0 && newOriginUsage > StorageManagerBase::GetQuota()) {
+  if (aSource == ContentMutation &&
+      aDelta > 0 && newOriginUsage > StorageManagerBase::GetQuota()) {
     return false;
   }
 
   // Now check eTLD+1 limit
-  if (mUsage && !mUsage->CheckAndSetETLD1UsageDelta(aGetDataSetIndex, aDelta)) {
+  if (mUsage &&
+      !mUsage->CheckAndSetETLD1UsageDelta(aGetDataSetIndex, aDelta, aSource)) {
     return false;
   }
 
   // Update size in our data set
   data.mOriginQuotaUsage = newOriginUsage;
   return true;
 }
 
@@ -241,83 +246,16 @@ StorageCache::Preload()
     return;
   }
 
   sDatabase->AsyncPreload(this);
 }
 
 namespace {
 
-// This class is passed to timer as a tick observer.  It refers the cache
-// and keeps it alive for a time.
-class StorageCacheHolder : public nsITimerCallback, public nsINamed
-{
-  virtual ~StorageCacheHolder() {}
-
-  NS_DECL_ISUPPORTS
-
-  NS_IMETHOD
-  Notify(nsITimer* aTimer) override
-  {
-    mCache = nullptr;
-    return NS_OK;
-  }
-
-  NS_IMETHOD
-  GetName(nsACString& aName) override
-  {
-    aName.AssignASCII("StorageCacheHolder_timer");
-    return NS_OK;
-  }
-
-  NS_IMETHOD
-  SetName(const char* aName) override
-  {
-    return NS_ERROR_NOT_IMPLEMENTED;
-  }
-
-  RefPtr<StorageCache> mCache;
-
-public:
-  explicit StorageCacheHolder(StorageCache* aCache) : mCache(aCache) {}
-};
-
-NS_IMPL_ISUPPORTS(StorageCacheHolder, nsITimerCallback, nsINamed)
-
-} // namespace
-
-void
-StorageCache::KeepAlive()
-{
-  // Missing reference back to the manager means the cache is not responsible
-  // for its lifetime.  Used for keeping sessionStorage live forever.
-  if (!mManager) {
-    return;
-  }
-
-  if (!NS_IsMainThread()) {
-    // Timer and the holder must be initialized on the main thread.
-    NS_DispatchToMainThread(NewRunnableMethod(this, &StorageCache::KeepAlive));
-    return;
-  }
-
-  nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
-  if (!timer) {
-    return;
-  }
-
-  RefPtr<StorageCacheHolder> holder = new StorageCacheHolder(this);
-  timer->InitWithCallback(holder, DOM_STORAGE_CACHE_KEEP_ALIVE_TIME_MS,
-                          nsITimer::TYPE_ONE_SHOT);
-
-  mKeepAliveTimer.swap(timer);
-}
-
-namespace {
-
 // The AutoTimer provided by telemetry headers is only using static,
 // i.e. compile time known ID, but here we know the ID only at run time.
 // Hence a new class.
 class TelemetryAutoTimer
 {
 public:
   explicit TelemetryAutoTimer(Telemetry::HistogramID aId)
     : id(aId), start(TimeStamp::Now())
@@ -447,17 +385,18 @@ StorageCache::GetItem(const Storage* aSt
 
   aRetval = value;
 
   return NS_OK;
 }
 
 nsresult
 StorageCache::SetItem(const Storage* aStorage, const nsAString& aKey,
-                      const nsString& aValue, nsString& aOld)
+                      const nsString& aValue, nsString& aOld,
+                      const MutationSource aSource)
 {
   // Size of the cache that will change after this action.
   int64_t delta = 0;
 
   if (Persist(aStorage)) {
     WaitForPreload(Telemetry::LOCALDOMSTORAGE_SETVALUE_BLOCKING_MS);
     if (NS_FAILED(mLoadResult)) {
       return mLoadResult;
@@ -470,27 +409,27 @@ StorageCache::SetItem(const Storage* aSt
 
     // We only consider key size if the key doesn't exist before.
     delta += static_cast<int64_t>(aKey.Length());
   }
 
   delta += static_cast<int64_t>(aValue.Length()) -
            static_cast<int64_t>(aOld.Length());
 
-  if (!ProcessUsageDelta(aStorage, delta)) {
+  if (!ProcessUsageDelta(aStorage, delta, aSource)) {
     return NS_ERROR_DOM_QUOTA_REACHED;
   }
 
   if (aValue == aOld && DOMStringIsNull(aValue) == DOMStringIsNull(aOld)) {
     return NS_SUCCESS_DOM_NO_OPERATION;
   }
 
   data.mKeys.Put(aKey, aValue);
 
-  if (Persist(aStorage)) {
+  if (aSource == ContentMutation && Persist(aStorage)) {
     if (!sDatabase) {
       NS_ERROR("Writing to localStorage after the database has been shut down"
                ", data lose!");
       return NS_ERROR_NOT_INITIALIZED;
     }
 
     if (DOMStringIsNull(aOld)) {
       return sDatabase->AsyncAddItem(this, aKey, aValue);
@@ -499,17 +438,17 @@ StorageCache::SetItem(const Storage* aSt
     return sDatabase->AsyncUpdateItem(this, aKey, aValue);
   }
 
   return NS_OK;
 }
 
 nsresult
 StorageCache::RemoveItem(const Storage* aStorage, const nsAString& aKey,
-                         nsString& aOld)
+                         nsString& aOld, const MutationSource aSource)
 {
   if (Persist(aStorage)) {
     WaitForPreload(Telemetry::LOCALDOMSTORAGE_REMOVEKEY_BLOCKING_MS);
     if (NS_FAILED(mLoadResult)) {
       return mLoadResult;
     }
   }
 
@@ -517,34 +456,34 @@ StorageCache::RemoveItem(const Storage* 
   if (!data.mKeys.Get(aKey, &aOld)) {
     SetDOMStringToNull(aOld);
     return NS_SUCCESS_DOM_NO_OPERATION;
   }
 
   // Recalculate the cached data size
   const int64_t delta = -(static_cast<int64_t>(aOld.Length()) +
                           static_cast<int64_t>(aKey.Length()));
-  Unused << ProcessUsageDelta(aStorage, delta);
+  Unused << ProcessUsageDelta(aStorage, delta, aSource);
   data.mKeys.Remove(aKey);
 
-  if (Persist(aStorage)) {
+  if (aSource == ContentMutation && Persist(aStorage)) {
     if (!sDatabase) {
       NS_ERROR("Writing to localStorage after the database has been shut down"
                ", data lose!");
       return NS_ERROR_NOT_INITIALIZED;
     }
 
     return sDatabase->AsyncRemoveItem(this, aKey);
   }
 
   return NS_OK;
 }
 
 nsresult
-StorageCache::Clear(const Storage* aStorage)
+StorageCache::Clear(const Storage* aStorage, const MutationSource aSource)
 {
   bool refresh = false;
   if (Persist(aStorage)) {
     // We need to preload all data (know the size) before we can proceeed
     // to correctly decrease cached usage number.
     // XXX as in case of unload, this is not technically needed now, but
     // after super-scope quota introduction we have to do this.  Get telemetry
     // right now.
@@ -556,21 +495,21 @@ StorageCache::Clear(const Storage* aStor
       mLoadResult = NS_OK;
     }
   }
 
   Data& data = DataSet(aStorage);
   bool hadData = !!data.mKeys.Count();
 
   if (hadData) {
-    Unused << ProcessUsageDelta(aStorage, -data.mOriginQuotaUsage);
+    Unused << ProcessUsageDelta(aStorage, -data.mOriginQuotaUsage, aSource);
     data.mKeys.Clear();
   }
 
-  if (Persist(aStorage) && (refresh || hadData)) {
+  if (aSource == ContentMutation && Persist(aStorage) && (refresh || hadData)) {
     if (!sDatabase) {
       NS_ERROR("Writing to localStorage after the database has been shut down"
                ", data lose!");
       return NS_ERROR_NOT_INITIALIZED;
     }
 
     return sDatabase->AsyncClear(this);
   }
@@ -675,19 +614,16 @@ StorageCache::LoadItem(const nsAString& 
   data.mKeys.Put(aKey, aValue);
   data.mOriginQuotaUsage += aKey.Length() + aValue.Length();
   return true;
 }
 
 void
 StorageCache::LoadDone(nsresult aRv)
 {
-  // Keep the preloaded cache alive for a time
-  KeepAlive();
-
   MonitorAutoLock monitor(mMonitor);
   mLoadResult = aRv;
   mLoaded = true;
   monitor.Notify();
 }
 
 void
 StorageCache::LoadWait()
@@ -738,22 +674,23 @@ StorageUsage::LoadUsage(const int64_t aU
   } else {
     // On a child process we get this on the main thread already
     mUsage[kDefaultSet] += aUsage;
   }
 }
 
 bool
 StorageUsage::CheckAndSetETLD1UsageDelta(uint32_t aDataSetIndex,
-                                         const int64_t aDelta)
+  const int64_t aDelta, const StorageCache::MutationSource aSource)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   int64_t newUsage = mUsage[aDataSetIndex] + aDelta;
-  if (aDelta > 0 && newUsage > StorageManagerBase::GetQuota()) {
+  if (aSource == StorageCache::ContentMutation &&
+      aDelta > 0 && newUsage > StorageManagerBase::GetQuota()) {
     return false;
   }
 
   mUsage[aDataSetIndex] = newUsage;
   return true;
 }
 
 
--- a/dom/storage/StorageCache.h
+++ b/dom/storage/StorageCache.h
@@ -73,51 +73,66 @@ protected:
 // for persistent storage (localStorage) and hold data for non-private,
 // private and session-only cookie modes.  It is also responsible for
 // persisting data changes using the database, works as a write-back cache.
 class StorageCache : public StorageCacheBridge
 {
 public:
   NS_IMETHOD_(void) Release(void);
 
-  // Note: We pass aOriginNoSuffix through the ctor here, because 
-  // StorageCacheHashKey's ctor is creating this class and 
+  enum MutationSource {
+    // The mutation is a result of an explicit JS mutation in this process.
+    // The mutation should be sent to the sDatabase. Quota will be checked and
+    // QuotaExceededError may be returned without the mutation being applied.
+    ContentMutation,
+    // The mutation initially was triggered in a different process and is being
+    // propagated to this cache via Storage::ApplyEvent.  The mutation should
+    // not be sent to the sDatabase because the originating process is already
+    // doing that.  (In addition to the redundant writes being wasteful, there
+    // is the potential for other processes to see inconsistent state from the
+    // database while preloading.)  Quota will be updated but not checked
+    // because it's assumed it was checked in another process and data-coherency
+    // is more important than slightly exceeding quota.
+    E10sPropagated
+  };
+
+  // Note: We pass aOriginNoSuffix through the ctor here, because
+  // StorageCacheHashKey's ctor is creating this class and
   // accepts reversed-origin-no-suffix as an argument - the hashing key.
   explicit StorageCache(const nsACString* aOriginNoSuffix);
 
 protected:
   virtual ~StorageCache();
 
 public:
   void Init(StorageManagerBase* aManager, bool aPersistent,
             nsIPrincipal* aPrincipal, const nsACString& aQuotaOriginScope);
 
   // Copies all data from the other storage.
   void CloneFrom(const StorageCache* aThat);
 
   // Starts async preload of this cache if it persistent and not loaded.
   void Preload();
 
-  // Keeps the cache alive (i.e. present in the manager's hash table) for a
-  // time.
-  void KeepAlive();
-
   // The set of methods that are invoked by DOM storage web API.
   // We are passing the Storage object just to let the cache
   // read properties like mPrivate, mPrincipal and mSessionOnly.
   // Get* methods return error when load from the database has failed.
   nsresult GetLength(const Storage* aStorage, uint32_t* aRetval);
   nsresult GetKey(const Storage* aStorage, uint32_t index, nsAString& aRetval);
   nsresult GetItem(const Storage* aStorage, const nsAString& aKey,
                    nsAString& aRetval);
   nsresult SetItem(const Storage* aStorage, const nsAString& aKey,
-                   const nsString& aValue, nsString& aOld);
+                   const nsString& aValue, nsString& aOld,
+                   const MutationSource aSource=ContentMutation);
   nsresult RemoveItem(const Storage* aStorage, const nsAString& aKey,
-                      nsString& aOld);
-  nsresult Clear(const Storage* aStorage);
+                      nsString& aOld,
+                      const MutationSource aSource=ContentMutation);
+  nsresult Clear(const Storage* aStorage,
+                 const MutationSource aSource=ContentMutation);
 
   void GetKeys(const Storage* aStorage, nsTArray<nsString>& aKeys);
 
   // Whether the principal equals principal the cache was created for
   bool CheckPrincipal(nsIPrincipal* aPrincipal) const;
   nsIPrincipal* Principal() const { return mPrincipal; }
 
   // Starts the database engine thread or the IPC bridge
@@ -177,46 +192,53 @@ private:
   // Helper to get one of the 3 data sets (regular, private, session)
   Data& DataSet(const Storage* aStorage);
 
   // Whether the storage change is about to persist
   bool Persist(const Storage* aStorage) const;
 
   // Changes the quota usage on the given data set if it fits the quota.
   // If not, then false is returned and no change to the set must be done.
-  bool ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta);
-  bool ProcessUsageDelta(const Storage* aStorage, const int64_t aDelta);
+  // A special case is if aSource==E10sPropagated, then we will return true even
+  // if the change would put us over quota.  This is done to ensure coherency of
+  // caches between processes in the face of races.  It does allow an attacker
+  // to potentially use N multiples of the quota storage limit if they can
+  // arrange for their origin to execute code in N processes.  However, this is
+  // not considered a particularly concerning threat model because it's already
+  // very possible for a rogue page to attempt to intentionally fill up the
+  // user's storage through the use of multiple domains.
+  bool ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta,
+                         const MutationSource aSource=ContentMutation);
+  bool ProcessUsageDelta(const Storage* aStorage, const int64_t aDelta,
+                         const MutationSource aSource=ContentMutation);
 
 private:
   // When a cache is reponsible for its life time (in case of localStorage data
   // cache) we need to refer our manager since removal of the cache from the
   // hash table is handled in the destructor by call to the manager.  Cache
   // could potentially overlive the manager, hence the hard ref.
   RefPtr<StorageManagerBase> mManager;
 
   // Reference to the usage counter object we check on for eTLD+1 quota limit.
   // Obtained from the manager during initialization (Init method).
   RefPtr<StorageUsage> mUsage;
 
-  // Timer that holds this cache alive for a while after it has been preloaded.
-  nsCOMPtr<nsITimer> mKeepAliveTimer;
-
   // Principal the cache has been initially created for, this is used only for
   // sessionStorage access checks since sessionStorage objects are strictly
   // scoped by a principal. localStorage objects on the other hand are scoped
   // by origin only.
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
   // The origin this cache belongs to in the "DB format", i.e. reversed
   nsCString mOriginNoSuffix;
 
   // The origin attributes suffix
   nsCString mOriginSuffix;
 
-  // The eTLD+1 scope used to count quota usage.  It is in the reversed format 
+  // The eTLD+1 scope used to count quota usage.  It is in the reversed format
   // and contains the origin attributes suffix.
   nsCString mQuotaOriginScope;
 
   // Non-private Browsing, Private Browsing and Session Only sets.
   Data mData[kDataSetCount];
 
   // This monitor is used to wait for full load of data.
   mozilla::Monitor mMonitor;
@@ -272,17 +294,18 @@ protected:
   virtual ~StorageUsageBridge() {}
 };
 
 class StorageUsage : public StorageUsageBridge
 {
 public:
   explicit StorageUsage(const nsACString& aOriginScope);
 
-  bool CheckAndSetETLD1UsageDelta(uint32_t aDataSetIndex, int64_t aUsageDelta);
+  bool CheckAndSetETLD1UsageDelta(uint32_t aDataSetIndex, int64_t aUsageDelta,
+                                  const StorageCache::MutationSource aSource);
 
 private:
   virtual const nsCString& OriginScope() { return mOriginScope; }
   virtual void LoadUsage(const int64_t aUsage);
 
   nsCString mOriginScope;
   int64_t mUsage[StorageCache::kDataSetCount];
 };
--- a/dom/storage/StorageIPC.cpp
+++ b/dom/storage/StorageIPC.cpp
@@ -219,16 +219,22 @@ StorageDBChild::RecvObserve(const nsCStr
   StorageObserver::Self()->Notify(
     aTopic.get(), aOriginAttributesPattern, aOriginScope);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 StorageDBChild::RecvOriginsHavingData(nsTArray<nsCString>&& aOrigins)
 {
+  // Force population of mOriginsHavingData even if there are no origins so that
+  // ShouldPreloadOrigin does not generate false positives for all origins.
+  if (!aOrigins.Length()) {
+    Unused << OriginsHavingData();
+  }
+
   for (uint32_t i = 0; i < aOrigins.Length(); ++i) {
     OriginsHavingData().PutEntry(aOrigins[i]);
   }
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
--- a/dom/storage/StorageManager.cpp
+++ b/dom/storage/StorageManager.cpp
@@ -304,17 +304,17 @@ StorageManagerBase::DropCache(StorageCac
     NS_WARNING("StorageManager::DropCache called on a non-main thread, shutting down?");
   }
 
   CacheOriginHashtable* table = mCaches.LookupOrAdd(aCache->OriginSuffix());
   table->RemoveEntry(aCache->OriginNoSuffix());
 }
 
 nsresult
-StorageManagerBase::GetStorageInternal(bool aCreate,
+StorageManagerBase::GetStorageInternal(CreateMode aCreateMode,
                                        mozIDOMWindow* aWindow,
                                        nsIPrincipal* aPrincipal,
                                        const nsAString& aDocumentURI,
                                        bool aPrivate,
                                        nsIDOMStorage** aRetval)
 {
   nsresult rv;
 
@@ -326,22 +326,22 @@ StorageManagerBase::GetStorageInternal(b
   if (NS_FAILED(rv)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   RefPtr<StorageCache> cache = GetCache(originAttrSuffix, originKey);
 
   // Get or create a cache for the given scope
   if (!cache) {
-    if (!aCreate) {
+    if (aCreateMode == CreateMode::UseIfExistsNeverCreate) {
       *aRetval = nullptr;
       return NS_OK;
     }
 
-    if (!aRetval) {
+    if (aCreateMode == CreateMode::CreateIfShouldPreload) {
       // This is a demand to just preload the cache, if the scope has
       // no data stored, bypass creation and preload of the cache.
       StorageDBBridge* db = StorageCache::GetDatabase();
       if (db) {
         if (!db->ShouldPreloadOrigin(StorageManagerBase::CreateOrigin(originAttrSuffix, originKey))) {
           return NS_OK;
         }
       } else {
@@ -367,41 +367,42 @@ StorageManagerBase::GetStorageInternal(b
       inner, this, cache, aDocumentURI, aPrincipal, aPrivate);
     storage.forget(aRetval);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-StorageManagerBase::PrecacheStorage(nsIPrincipal* aPrincipal)
+StorageManagerBase::PrecacheStorage(nsIPrincipal* aPrincipal,
+                                    nsIDOMStorage** aRetval)
 {
-  return GetStorageInternal(true, nullptr, aPrincipal, EmptyString(), false,
-                            nullptr);
+  return GetStorageInternal(CreateMode::CreateIfShouldPreload, nullptr,
+                            aPrincipal, EmptyString(), false, aRetval);
 }
 
 NS_IMETHODIMP
 StorageManagerBase::CreateStorage(mozIDOMWindow* aWindow,
                                   nsIPrincipal* aPrincipal,
                                   const nsAString& aDocumentURI,
                                   bool aPrivate,
                                   nsIDOMStorage** aRetval)
 {
-  return GetStorageInternal(true, aWindow, aPrincipal, aDocumentURI, aPrivate,
-                            aRetval);
+  return GetStorageInternal(CreateMode::CreateAlways, aWindow, aPrincipal,
+                            aDocumentURI, aPrivate, aRetval);
 }
 
 NS_IMETHODIMP
 StorageManagerBase::GetStorage(mozIDOMWindow* aWindow,
                                nsIPrincipal* aPrincipal,
                                bool aPrivate,
                                nsIDOMStorage** aRetval)
 {
-  return GetStorageInternal(false, aWindow, aPrincipal, EmptyString(), aPrivate,
-                            aRetval);
+  return GetStorageInternal(CreateMode::UseIfExistsNeverCreate, aWindow,
+                            aPrincipal, EmptyString(), aPrivate, aRetval);
 }
 
 NS_IMETHODIMP
 StorageManagerBase::CloneStorage(nsIDOMStorage* aStorage)
 {
   if (mType != SessionStorage) {
     // Cloning is supported only for sessionStorage
     return NS_ERROR_NOT_IMPLEMENTED;
--- a/dom/storage/StorageManager.h
+++ b/dom/storage/StorageManager.h
@@ -85,18 +85,27 @@ private:
   };
 
   // Ensures cache for a scope, when it doesn't exist it is created and
   // initalized, this also starts preload of persistent data.
   already_AddRefed<StorageCache> PutCache(const nsACString& aOriginSuffix,
                                           const nsACString& aOriginNoSuffix,
                                           nsIPrincipal* aPrincipal);
 
+  enum class CreateMode {
+    // GetStorage: do not create if it's not already in memory.
+    UseIfExistsNeverCreate,
+    // CreateStorage: Create it if it's not already in memory.
+    CreateAlways,
+    // PrecacheStorage: Create only if the database says we ShouldPreloadOrigin.
+    CreateIfShouldPreload
+  };
+
   // Helper for creation of DOM storage objects
-  nsresult GetStorageInternal(bool aCreate,
+  nsresult GetStorageInternal(CreateMode aCreate,
                               mozIDOMWindow* aWindow,
                               nsIPrincipal* aPrincipal,
                               const nsAString& aDocumentURI,
                               bool aPrivate,
                               nsIDOMStorage** aRetval);
 
   // Suffix->origin->cache map
   typedef nsTHashtable<StorageCacheHashKey> CacheOriginHashtable;
--- a/dom/tests/browser/browser.ini
+++ b/dom/tests/browser/browser.ini
@@ -1,12 +1,13 @@
 [DEFAULT]
 support-files =
   browser_frame_elements.html
   page_privatestorageevent.html
+  page_localstorage_e10s.html
   position.html
   test-console-api.html
   test_bug1004814.html
   worker_bug1004814.js
   geo_leak_test.html
   dummy.html
   test_largeAllocation.html
   test_largeAllocation.html^headers^
@@ -38,16 +39,18 @@ skip-if = e10s
 [browser_ConsoleStoragePBTest_perwindowpb.js]
 [browser_focus_steal_from_chrome.js]
 [browser_focus_steal_from_chrome_during_mousedown.js]
 [browser_frame_elements.js]
 [browser_largeAllocation_win32.js]
 skip-if = !e10s || os != "win" || processor != "x86" # Large-Allocation requires e10s
 [browser_largeAllocation_non_win32.js]
 skip-if = !e10s || (os == "win" && processor == "x86") # Large-Allocation requires e10s
+[browser_localStorage_e10s.js]
+skip-if = !e10s # This is a test of e10s functionality.
 [browser_localStorage_privatestorageevent.js]
 [browser_test__content.js]
 [browser_test_focus_after_modal_state.js]
 support-files =
   focus_after_prompt.html
 [browser_test_new_window_from_content.js]
 tags = openwindow
 skip-if = toolkit == 'android'  || (os == "linux" && debug) # see bug 1261495 for Linux debug time outs
new file mode 100644
--- /dev/null
+++ b/dom/tests/browser/browser_localStorage_e10s.js
@@ -0,0 +1,360 @@
+const HELPER_PAGE_URL =
+  "http://example.com/browser/dom/tests/browser/page_localstorage_e10s.html";
+const HELPER_PAGE_ORIGIN = "http://example.com/";
+
+// Simple tab wrapper abstracting our messaging mechanism;
+class KnownTab {
+  constructor(name, tab) {
+    this.name = name;
+    this.tab = tab;
+  }
+
+  cleanup() {
+    this.tab = null;
+  }
+}
+
+// Simple data structure class to help us track opened tabs and their pids.
+class KnownTabs {
+  constructor() {
+    this.byPid = new Map();
+    this.byName = new Map();
+  }
+
+  cleanup() {
+    this.byPid = null;
+    this.byName = null;
+  }
+}
+
+/**
+ * Open our helper page in a tab in its own content process, asserting that it
+ * really is in its own process.
+ */
+function* openTestTabInOwnProcess(name, knownTabs) {
+  let url = HELPER_PAGE_URL + '?' + encodeURIComponent(name);
+  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
+  let pid = tab.linkedBrowser.frameLoader.tabParent.osPid;
+  ok(!knownTabs.byName.has(name), "tab needs its own name: " + name);
+  ok(!knownTabs.byPid.has(pid), "tab needs to be in its own process: " + pid);
+
+  let knownTab = new KnownTab(name, tab);
+  knownTabs.byPid.set(pid, knownTab);
+  knownTabs.byName.set(name, knownTab);
+  return knownTab;
+}
+
+/**
+ * Close all the tabs we opened.
+ */
+function* cleanupTabs(knownTabs) {
+  for (let knownTab of knownTabs.byName.values()) {
+    yield BrowserTestUtils.removeTab(knownTab.tab);
+    knownTab.cleanup();
+  }
+  knownTabs.cleanup();
+}
+
+/**
+ * Clear the origin's storage so that "OriginsHavingData" will return false for
+ * our origin.  Note that this is only the case for AsyncClear() which is
+ * explicitly issued against a cache, or AsyncClearAll() which we can trigger
+ * by wiping all storage.  However, the more targeted domain clearings that
+ * we can trigger via observer, AsyncClearMatchingOrigin and
+ * AsyncClearMatchingOriginAttributes will not clear the hashtable entry for
+ * the origin.
+ *
+ * So we explicitly access the cache here in the parent for the origin and issue
+ * an explicit clear.  Clearing all storage might be a little easier but seems
+ * like asking for intermittent failures.
+ */
+function clearOriginStorageEnsuringNoPreload() {
+  let principal =
+    Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(
+      HELPER_PAGE_ORIGIN);
+  // We want to use createStorage to force the cache to be created so we can
+  // issue the clear.  It's possible for getStorage to return false but for the
+  // origin preload hash to still have our origin in it.
+  let storage = Services.domStorageManager.createStorage(null, principal, "");
+  storage.clear();
+  // We don't need to wait for anything.  The clear call will have queued the
+  // clear operation on the database thread, and the child process requests
+  // for origins will likewise be answered via the database thread.
+}
+
+function* verifyTabPreload(knownTab, expectStorageExists) {
+  let storageExists = yield ContentTask.spawn(
+    knownTab.tab.linkedBrowser,
+    HELPER_PAGE_ORIGIN,
+    function(origin) {
+      let principal =
+        Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(
+          origin);
+      return !!Services.domStorageManager.getStorage(null, principal);
+    });
+  is(storageExists, expectStorageExists, "Storage existence === preload");
+}
+
+/**
+ * Instruct the given tab to execute the given series of mutations.  For
+ * simplicity, the mutations representation matches the expected events rep.
+ */
+function* mutateTabStorage(knownTab, mutations) {
+  yield ContentTask.spawn(
+    knownTab.tab.linkedBrowser,
+    { mutations },
+    function(args) {
+      return content.wrappedJSObject.mutateStorage(args.mutations);
+    });
+}
+
+/**
+ * Instruct the given tab to add a "storage" event listener and record all
+ * received events.  verifyTabStorageEvents is the corresponding method to
+ * check and assert the recorded events.
+ */
+function* recordTabStorageEvents(knownTab) {
+  yield ContentTask.spawn(
+    knownTab.tab.linkedBrowser,
+    {},
+    function() {
+      return content.wrappedJSObject.listenForStorageEvents();
+    });
+}
+
+/**
+ * Retrieve the current localStorage contents perceived by the tab and assert
+ * that they match the provided expected state.
+ */
+function* verifyTabStorageState(knownTab, expectedState) {
+  let actualState = yield ContentTask.spawn(
+    knownTab.tab.linkedBrowser,
+    {},
+    function() {
+      return content.wrappedJSObject.getStorageState();
+    });
+
+  for (let [expectedKey, expectedValue] of Object.entries(expectedState)) {
+    ok(actualState.hasOwnProperty(expectedKey), "key present: " + expectedKey);
+    is(actualState[expectedKey], expectedValue, "value correct");
+  }
+  for (let actualKey of Object.keys(actualState)) {
+    if (!expectedState.hasOwnProperty(actualKey)) {
+      ok(false, "actual state has key it shouldn't have: " + actualKey);
+    }
+  }
+}
+
+/**
+ * Retrieve and clear the storage events recorded by the tab and assert that
+ * they match the provided expected events.  For simplicity, the expected events
+ * representation is the same as that used by mutateTabStorage.
+ */
+function* verifyTabStorageEvents(knownTab, expectedEvents) {
+  let actualEvents = yield ContentTask.spawn(
+    knownTab.tab.linkedBrowser,
+    {},
+    function() {
+      return content.wrappedJSObject.returnAndClearStorageEvents();
+    });
+
+  is(actualEvents.length, expectedEvents.length, "right number of events");
+  for (let i = 0; i < actualEvents.length; i++) {
+    let [actualKey, actualNewValue, actualOldValue] = actualEvents[i];
+    let [expectedKey, expectedNewValue, expectedOldValue] = expectedEvents[i];
+    is(actualKey, expectedKey, "keys match");
+    is(actualNewValue, expectedNewValue, "new values match");
+    is(actualOldValue, expectedOldValue, "old values match");
+  }
+}
+
+// We spin up a ton of child processes.
+requestLongerTimeout(4);
+
+/**
+ * Verify the basics of our multi-e10s localStorage support.  We are focused on
+ * whitebox testing two things.  When this is being written, broadcast filtering
+ * is not in place, but the test is intended to attempt to verify that its
+ * implementation does not break things.
+ *
+ * 1) That pages see the same localStorage state in a timely fashion when
+ *    engaging in non-conflicting operations.  We are not testing races or
+ *    conflict resolution; the spec does not cover that.
+ *
+ * 2) That there are no edge-cases related to when the Storage instance is
+ *    created for the page or the StorageCache for the origin.  (StorageCache is
+ *    what actually backs the Storage binding exposed to the page.)  This
+ *    matters because the following reasons can exist for them to be created:
+ *    - Preload, on the basis of knowing the origin uses localStorage.  The
+ *      interesting edge case is when we have the same origin open in different
+ *      processes and the origin starts using localStorage when it did not
+ *      before.  Preload will not have instantiated bindings, which could impact
+ *      correctness.
+ *    - The page accessing localStorage for read or write purposes.  This is the
+ *      obvious, boring one.
+ *    - The page adding a "storage" listener.  This is less obvious and
+ *      interacts with the preload edge-case mentioned above.  The page needs to
+ *      hear "storage" events even if the page has not touched localStorage
+ *      itself and its origin had nothing stored in localStorage when the page
+ *      was created.
+ *
+ * We use the same simple child page in all tabs that:
+ * - can be instructed to listen for and record "storage" events
+ * - can be instructed to issue a series of localStorage writes
+ * - can be instructed to return the current entire localStorage contents
+ *
+ * We open the 5 following tabs:
+ * - Open a "writer" tab that does not listen for "storage" events and will
+ *   issue only writes.
+ * - Open a "listener" tab instructed to listen for "storage" events
+ *   immediately.  We expect it to capture all events.
+ * - Open an "reader" tab that does not listen for "storage" events and will
+ *   only issue reads when instructed.
+ * - Open a "lateWriteThenListen" tab that initially does nothing.  We will
+ *   later tell it to issue a write and then listen for events to make sure it
+ *   captures the later events.
+ * - Open "lateOpenSeesPreload" tab after we've done everything and ensure that
+ *   it preloads/precaches the data without us having touched localStorage or
+ *   added an event listener.
+ */
+add_task(function*() {
+  // - Boost process count so all of our tabs get new processes.
+  // Our test wants to assert things about the precache status which is only
+  // populated at process startup and never updated.  (Per analysis at
+  // https://bugzilla.mozilla.org/show_bug.cgi?id=1312022 this still makes
+  // sense.)  https://bugzilla.mozilla.org/show_bug.cgi?id=1312022 introduced
+  // a mechanism for keeping an arbitrary number of processes alive, modifying
+  // all browser chrome tests to keep alive whatever dom.ipc.processCount is set
+  // to.  The mechanism was slightly modified later to be type-based, so now
+  // it's "dom.ipc.keepProcessesAlive.web" we care about.
+  //
+  // Our options for ensuring we get a new process are to either:
+  // 1) Try and push keepalive down to 1 and kill off the processes that are
+  //    already hanging around.
+  // 2) Just bump the process count up enough so that every tab we open will
+  //    get a new process.
+  //
+  // The first option turns out to be hard to get right.  Specifically,
+  // although one can set the keepalive and process counts to 1 and open and
+  // close tabs to try and trigger process termination down to 1, since we don't
+  // know how many processes might exist, we can't reliably listen for observer
+  // notifications of their shutdown to ensure we're avoiding shutdown races.
+  // (If there are races then the processes won't actually be shut down.)  So
+  // it's easiest to just boost the limit.
+  let keepAliveCount =
+    SpecialPowers.getIntPref("dom.ipc.keepProcessesAlive.web", 1);
+  let safeProcessCount = keepAliveCount + 6;
+  info("dom.ipc.keepProcessesAlive.web is " + keepAliveCount + ", boosting " +
+       "process count temporarily to " + safeProcessCount);
+
+  // (There's already one about:blank page open and we open 5 new tabs, so 6
+  // processes.  Actually, 7, just in case.)
+  yield SpecialPowers.pushPrefEnv({
+    set: [
+      ["dom.ipc.processCount", safeProcessCount],
+      ["dom.ipc.processCount.web", safeProcessCount]
+    ]
+  });
+
+  // Ensure that there is no localstorage data or potential false positives for
+  // localstorage preloads by forcing the origin to be cleared prior to the
+  // start of our test.
+  clearOriginStorageEnsuringNoPreload();
+
+  // - Open tabs.  Don't configure any of them yet.
+  const knownTabs = new KnownTabs();
+  const writerTab = yield* openTestTabInOwnProcess("writer", knownTabs);
+  const listenerTab = yield* openTestTabInOwnProcess("listener", knownTabs);
+  const readerTab = yield* openTestTabInOwnProcess("reader", knownTabs);
+  const lateWriteThenListenTab = yield* openTestTabInOwnProcess(
+    "lateWriteThenListen", knownTabs);
+
+  // Sanity check that preloading did not occur in the tabs.
+  yield* verifyTabPreload(writerTab, false);
+  yield* verifyTabPreload(listenerTab, false);
+  yield* verifyTabPreload(readerTab, false);
+
+  // - Configure the tabs.
+  yield* recordTabStorageEvents(listenerTab);
+
+  // - Issue the initial batch of writes and verify.
+  const initialWriteMutations = [
+    //[key (null=clear), newValue (null=delete), oldValue (verification)]
+    ["getsCleared", "1", null],
+    ["alsoGetsCleared", "2", null],
+    [null, null, null],
+    ["stays", "3", null],
+    ["clobbered", "pre", null],
+    ["getsDeletedLater", "4", null],
+    ["getsDeletedImmediately", "5", null],
+    ["getsDeletedImmediately", null, "5"],
+    ["alsoStays", "6", null],
+    ["getsDeletedLater", null, "4"],
+    ["clobbered", "post", "pre"]
+  ];
+  const initialWriteState = {
+    stays: "3",
+    clobbered: "post",
+    alsoStays: "6"
+  };
+
+  yield* mutateTabStorage(writerTab, initialWriteMutations);
+
+  yield* verifyTabStorageState(writerTab, initialWriteState);
+  yield* verifyTabStorageEvents(listenerTab, initialWriteMutations);
+  yield* verifyTabStorageState(listenerTab, initialWriteState);
+  yield* verifyTabStorageState(readerTab, initialWriteState);
+
+  // - Issue second set of writes from lateWriteThenListen
+  const lateWriteMutations = [
+    ["lateStays", "10", null],
+    ["lateClobbered", "latePre", null],
+    ["lateDeleted", "11", null],
+    ["lateClobbered", "lastPost", "latePre"],
+    ["lateDeleted", null, "11"]
+  ];
+  const lateWriteState = Object.assign({}, initialWriteState, {
+    lateStays: "10",
+    lateClobbered: "lastPost"
+  });
+
+  yield* mutateTabStorage(lateWriteThenListenTab, lateWriteMutations);
+  yield* recordTabStorageEvents(lateWriteThenListenTab);
+
+  yield* verifyTabStorageState(writerTab, lateWriteState);
+  yield* verifyTabStorageEvents(listenerTab, lateWriteMutations);
+  yield* verifyTabStorageState(listenerTab, lateWriteState);
+  yield* verifyTabStorageState(readerTab, lateWriteState);
+
+  // - Issue last set of writes from writerTab.
+  const lastWriteMutations = [
+    ["lastStays", "20", null],
+    ["lastDeleted", "21", null],
+    ["lastClobbered", "lastPre", null],
+    ["lastClobbered", "lastPost", "lastPre"],
+    ["lastDeleted", null, "21"]
+  ];
+  const lastWriteState = Object.assign({}, lateWriteState, {
+    lastStays: "20",
+    lastClobbered: "lastPost"
+  });
+
+  yield* mutateTabStorage(writerTab, lastWriteMutations);
+
+  yield* verifyTabStorageState(writerTab, lastWriteState);
+  yield* verifyTabStorageEvents(listenerTab, lastWriteMutations);
+  yield* verifyTabStorageState(listenerTab, lastWriteState);
+  yield* verifyTabStorageState(readerTab, lastWriteState);
+  yield* verifyTabStorageEvents(lateWriteThenListenTab, lastWriteMutations);
+  yield* verifyTabStorageState(lateWriteThenListenTab, lastWriteState);
+
+  // - Open a fresh tab and make sure it sees the precache/preload
+  const lateOpenSeesPreload =
+    yield* openTestTabInOwnProcess("lateOpenSeesPreload", knownTabs);
+  yield* verifyTabPreload(lateOpenSeesPreload, true);
+
+  // - Clean up.
+  yield* cleanupTabs(knownTabs);
+
+  clearOriginStorageEnsuringNoPreload();
+});
new file mode 100644
--- /dev/null
+++ b/dom/tests/browser/page_localstorage_e10s.html
@@ -0,0 +1,56 @@
+<!doctype html>
+<html>
+<head>
+  <meta charset="utf-8">
+<script>
+/**
+ * Helper page used by browser_localStorage_e10s.js.
+ **/
+var pageName = document.location.search.substring(1);
+window.addEventListener(
+  "load",
+  () => { document.getElementById("pageNameH").textContent = pageName; });
+
+var recordedEvents = null;
+function storageListener(event) {
+  recordedEvents.push([event.key, event.newValue, event.oldValue]);
+}
+
+function listenForStorageEvents() {
+  recordedEvents = [];
+  window.addEventListener("storage", storageListener);
+}
+
+function mutateStorage(mutations) {
+  mutations.forEach(function([key, value]) {
+    if (key !== null) {
+      if (value === null) {
+        localStorage.removeItem(key);
+      } else {
+        localStorage.setItem(key, value);
+      }
+    } else {
+      localStorage.clear();
+    }
+  });
+}
+
+function getStorageState() {
+  let numKeys = localStorage.length;
+  let state = {};
+  for (var iKey = 0; iKey < numKeys; iKey++) {
+    let key = localStorage.key(iKey);
+    state[key] = localStorage.getItem(key);
+  }
+  return state;
+}
+
+function returnAndClearStorageEvents() {
+  let loggedEvents = recordedEvents;
+  recordedEvents = [];
+  return loggedEvents;
+}
+</script>
+</head>
+<body><h2 id="pageNameH"></h2></body>
+</html>
--- a/editor/libeditor/tests/test_bug674770-1.html
+++ b/editor/libeditor/tests/test_bug674770-1.html
@@ -19,17 +19,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <a href="file_bug674770-1.html" id="link2">test</a>
 </div>
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 SimpleTest.waitForExplicitFinish();
 SimpleTest.waitForFocus(function() {
-  SpecialPowers.pushPrefEnv({"set":[["middlemouse.paste", true], ["dom.ipc.processCount", 1]]}, startTests);
+  SpecialPowers.pushPrefEnv({"set":[["middlemouse.paste", true]]}, startTests);
 });
 
 function startTests() {
   var tests = [
     { description: "Testing link in <div>: ",
       target: function () { return document.querySelector("#link1"); },
       linkShouldWork: true },
     { description: "Testing link in <div contenteditable>: ",
--- a/extensions/auth/nsHttpNegotiateAuth.cpp
+++ b/extensions/auth/nsHttpNegotiateAuth.cpp
@@ -28,26 +28,30 @@
 #include "nsIProxyInfo.h"
 #include "nsIURI.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsNetCID.h"
 #include "plbase64.h"
 #include "plstr.h"
 #include "mozilla/Logging.h"
+#include "mozilla/Tokenizer.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
 #include "prmem.h"
 #include "prnetdb.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Sprintf.h"
 #include "nsIChannel.h"
 #include "nsNetUtil.h"
 #include "nsThreadUtils.h"
 #include "nsIHttpAuthenticatorCallback.h"
 #include "mozilla/Mutex.h"
 #include "nsICancelable.h"
+#include "nsUnicharUtils.h"
 
 //-----------------------------------------------------------------------------
 
 static const char kNegotiate[] = "Negotiate";
 static const char kNegotiateAuthTrustedURIs[] = "network.negotiate-auth.trusted-uris";
 static const char kNegotiateAuthDelegationURIs[] = "network.negotiate-auth.delegation-uris";
 static const char kNegotiateAuthAllowProxies[] = "network.negotiate-auth.allow-proxies";
 static const char kNegotiateAuthAllowNonFqdn[] = "network.negotiate-auth.allow-non-fqdn";
@@ -628,103 +632,141 @@ nsHttpNegotiateAuth::TestPref(nsIURI *ur
 
     nsAutoCString scheme, host;
     int32_t port;
 
     if (NS_FAILED(uri->GetScheme(scheme)))
         return false;
     if (NS_FAILED(uri->GetAsciiHost(host)))
         return false;
-    if (NS_FAILED(uri->GetPort(&port)))
+
+    port = NS_GetRealPort(uri);
+    if (port == -1) {
         return false;
+    }
 
     char *hostList;
     if (NS_FAILED(prefs->GetCharPref(pref, &hostList)) || !hostList)
         return false;
 
+    struct FreePolicy { void operator()(void* p) { free(p); } };
+    mozilla::UniquePtr<char[], FreePolicy> hostListScope;
+    hostListScope.reset(hostList);
+
     // pseudo-BNF
     // ----------
     //
     // url-list       base-url ( base-url "," LWS )*
     // base-url       ( scheme-part | host-part | scheme-part host-part )
     // scheme-part    scheme "://"
     // host-part      host [":" port]
     //
     // for example:
     //   "https://, http://office.foo.com"
     //
 
-    char *start = hostList, *end;
-    for (;;) {
-        // skip past any whitespace
-        while (*start == ' ' || *start == '\t')
-            ++start;
-        end = strchr(start, ',');
-        if (!end)
-            end = start + strlen(start);
-        if (start == end)
-            break;
-        if (MatchesBaseURI(scheme, host, port, start, end))
+    mozilla::Tokenizer t(hostList);
+    while (!t.CheckEOF()) {
+        t.SkipWhites();
+        nsDependentCSubstring url;
+        mozilla::Unused << t.ReadUntil(mozilla::Tokenizer::Token::Char(','), url);
+        if (url.IsEmpty()) {
+            continue;
+        }
+        if (MatchesBaseURI(scheme, host, port, url)) {
             return true;
-        if (*end == '\0')
-            break;
-        start = end + 1;
+        }
     }
-    
-    free(hostList);
+
     return false;
 }
 
 bool
 nsHttpNegotiateAuth::MatchesBaseURI(const nsCSubstring &matchScheme,
                                     const nsCSubstring &matchHost,
                                     int32_t             matchPort,
-                                    const char         *baseStart,
-                                    const char         *baseEnd)
+                                    nsDependentCSubstring const& url)
 {
-    // check if scheme://host:port matches baseURI
+  // check if scheme://host:port matches baseURI
+
+  // parse the base URI
+  mozilla::Tokenizer t(url);
+  mozilla::Tokenizer::Token token;
+
+  t.SkipWhites();
 
-    // parse the base URI
-    const char *hostStart, *schemeEnd = strstr(baseStart, "://");
-    if (schemeEnd) {
-        // the given scheme must match the parsed scheme exactly
-        if (!matchScheme.Equals(Substring(baseStart, schemeEnd)))
-            return false;
-        hostStart = schemeEnd + 3;
-    }
-    else
-        hostStart = baseStart;
+  // We don't know if the url to check against starts with scheme
+  // or a host name.  Start recording here.
+  t.Record();
+
+  mozilla::Unused << t.Next(token);
 
-    // XXX this does not work for IPv6-literals
-    const char *hostEnd = strchr(hostStart, ':');
-    if (hostEnd && hostEnd < baseEnd) {
-        // the given port must match the parsed port exactly
-        int port = atoi(hostEnd + 1);
-        if (matchPort != (int32_t) port)
-            return false;
+  // The ipv6 literals MUST be enclosed with [] in the preference.
+  bool ipv6 = false;
+  if (token.Equals(mozilla::Tokenizer::Token::Char('['))) {
+    nsDependentCSubstring ipv6BareLiteral;
+    if (!t.ReadUntil(mozilla::Tokenizer::Token::Char(']'), ipv6BareLiteral)) {
+      // Broken ipv6 literal
+      return false;
     }
-    else
-        hostEnd = baseEnd;
 
-
-    // if we didn't parse out a host, then assume we got a match.
-    if (hostStart == hostEnd)
-        return true;
-
-    uint32_t hostLen = hostEnd - hostStart;
-
-    // matchHost must either equal host or be a subdomain of host
-    if (matchHost.Length() < hostLen)
-        return false;
-
-    const char *end = matchHost.EndReading();
-    if (PL_strncasecmp(end - hostLen, hostStart, hostLen) == 0) {
-        // if matchHost ends with host from the base URI, then make sure it is
-        // either an exact match, or prefixed with a dot.  we don't want
-        // "foobar.com" to match "bar.com"
-        if (matchHost.Length() == hostLen ||
-            *(end - hostLen) == '.' ||
-            *(end - hostLen - 1) == '.')
-            return true;
+    nsDependentCSubstring ipv6Literal;
+    t.Claim(ipv6Literal, mozilla::Tokenizer::INCLUDE_LAST);
+    if (!matchHost.Equals(ipv6Literal, nsCaseInsensitiveUTF8StringComparator()) &&
+        !matchHost.Equals(ipv6BareLiteral, nsCaseInsensitiveUTF8StringComparator())) {
+      return false;
     }
 
-    return false;
+    ipv6 = true;
+  } else if (t.CheckChar(':') && t.CheckChar('/') && t.CheckChar('/')) {
+    if (!matchScheme.Equals(token.Fragment())) {
+      return false;
+    }
+    // Re-start recording the hostname from the point after scheme://.
+    t.Record();
+  }
+
+  while (t.Next(token)) {
+    bool eof = token.Equals(mozilla::Tokenizer::Token::EndOfFile());
+    bool port = token.Equals(mozilla::Tokenizer::Token::Char(':'));
+
+    if (eof || port) {
+      if (!ipv6) { // Match already performed above.
+        nsDependentCSubstring hostName;
+        t.Claim(hostName);
+
+        // An empty hostname means to accept everything for the schema
+        if (!hostName.IsEmpty()) {
+          if (hostName.First() == '.') {
+            if (!StringEndsWith(matchHost, hostName, nsCaseInsensitiveUTF8StringComparator())) {
+              return false;
+            }
+          } else { // host to match doesn't begin with '.', do a full compare
+            if (!matchHost.Equals(hostName, nsCaseInsensitiveUTF8StringComparator())) {
+              return false;
+            }
+          }
+        }
+      }
+
+      if (port) {
+        uint16_t portNumber;
+        if (!t.ReadInteger(&portNumber)) {
+          // Missing port number
+          return false;
+        }
+        if (matchPort != portNumber) {
+          return false;
+        }
+        if (!t.CheckEOF()) {
+          return false;
+        }
+      }
+    } else if (ipv6) {
+      // After an ipv6 literal there can only be EOF or :port.  Everything else
+      // must be treated as non-match/broken input.
+      return false;
+    }
+  }
+
+  // All negative checks has passed positively.
+  return true;
 }
--- a/extensions/auth/nsHttpNegotiateAuth.h
+++ b/extensions/auth/nsHttpNegotiateAuth.h
@@ -29,16 +29,15 @@ private:
 
     // tests if the host part of an uri is fully qualified
     bool TestNonFqdn(nsIURI *uri);
 
     // returns true if URI is accepted by the list of hosts in the pref
     bool TestPref(nsIURI *, const char *pref);
 
     bool MatchesBaseURI(const nsCSubstring &scheme,
-                          const nsCSubstring &host,
-                          int32_t             port,
-                          const char         *baseStart,
-                          const char         *baseEnd);
+                        const nsCSubstring &host,
+                        int32_t             port,
+                        nsDependentCSubstring const& url);
     // Thread for GenerateCredentialsAsync
     RefPtr<mozilla::LazyIdleThread> mNegotiateThread;
 };
 #endif /* nsHttpNegotiateAuth_h__ */
--- a/extensions/pref/autoconfig/src/nsJSConfigTriggers.cpp
+++ b/extensions/pref/autoconfig/src/nsJSConfigTriggers.cpp
@@ -125,14 +125,14 @@ nsresult EvaluateAdminConfigScript(const
         /* If the length is 0, the conversion failed. Fallback to ASCII */
         convertedScript = NS_ConvertASCIItoUTF16(script);
     }
     JS::Rooted<JS::Value> value(cx, JS::BooleanValue(isUTF8));
     if (!JS_DefineProperty(cx, autoconfigSb, "gIsUTF8", value, JSPROP_ENUMERATE)) {
         return NS_ERROR_UNEXPECTED;
     }
     rv = xpc->EvalInSandboxObject(convertedScript, filename, cx,
-                                  autoconfigSb, JSVERSION_LATEST, &v);
+                                  autoconfigSb, &v);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return NS_OK;
 }
 
--- a/gfx/2d/SourceSurfaceSkia.cpp
+++ b/gfx/2d/SourceSurfaceSkia.cpp
@@ -35,24 +35,46 @@ SourceSurfaceSkia::GetSize() const
 }
 
 SurfaceFormat
 SourceSurfaceSkia::GetFormat() const
 {
   return mFormat;
 }
 
+static sk_sp<SkData>
+MakeSkData(unsigned char* aData, const IntSize& aSize, int32_t aStride)
+{
+  CheckedInt<size_t> size = aStride;
+  size *= aSize.height;
+  if (size.isValid()) {
+    void* mem = sk_malloc_flags(size.value(), 0);
+    if (mem) {
+      if (aData) {
+        memcpy(mem, aData, size.value());
+      }
+      return SkData::MakeFromMalloc(mem, size.value());
+    }
+  }
+  return nullptr;
+}
+
 bool
 SourceSurfaceSkia::InitFromData(unsigned char* aData,
                                 const IntSize &aSize,
                                 int32_t aStride,
                                 SurfaceFormat aFormat)
 {
-  SkPixmap pixmap(MakeSkiaImageInfo(aSize, aFormat), aData, aStride);
-  mImage = SkImage::MakeRasterCopy(pixmap);
+  sk_sp<SkData> data = MakeSkData(aData, aSize, aStride);
+  if (!data) {
+    return false;
+  }
+
+  SkImageInfo info = MakeSkiaImageInfo(aSize, aFormat);
+  mImage = SkImage::MakeRasterData(info, data, aStride);
   if (!mImage) {
     return false;
   }
 
   mSize = aSize;
   mFormat = aFormat;
   mStride = aStride;
   return true;
@@ -101,24 +123,20 @@ SourceSurfaceSkia::InitFromImage(const s
 }
 
 uint8_t*
 SourceSurfaceSkia::GetData()
 {
 #ifdef USE_SKIA_GPU
   if (mImage->isTextureBacked()) {
     sk_sp<SkImage> raster;
-    CheckedInt<size_t> size = mStride;
-    size *= mSize.height;
-    if (size.isValid()) {
-      if (sk_sp<SkData> data = SkData::MakeUninitialized(size.value())) {
-        SkImageInfo info = MakeSkiaImageInfo(mSize, mFormat);
-        if (mImage->readPixels(info, data->writable_data(), mStride, 0, 0, SkImage::kDisallow_CachingHint)) {
-          raster = SkImage::MakeRasterData(info, data, mStride);
-        }
+    if (sk_sp<SkData> data = MakeSkData(nullptr, mSize, mStride)) {
+      SkImageInfo info = MakeSkiaImageInfo(mSize, mFormat);
+      if (mImage->readPixels(info, data->writable_data(), mStride, 0, 0, SkImage::kDisallow_CachingHint)) {
+        raster = SkImage::MakeRasterData(info, data, mStride);
       }
     }
     if (raster) {
       mImage = raster;
     } else {
       gfxCriticalError() << "Failed making Skia raster image for GPU surface";
     }
   }
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -46,17 +46,18 @@ static const char* sEGLExtensionNames[] 
     "EGL_KHR_lock_surface",
     "EGL_ANGLE_surface_d3d_texture_2d_share_handle",
     "EGL_EXT_create_context_robustness",
     "EGL_KHR_image",
     "EGL_KHR_fence_sync",
     "EGL_ANDROID_native_fence_sync",
     "EGL_ANDROID_image_crop",
     "EGL_ANGLE_platform_angle",
-    "EGL_ANGLE_platform_angle_d3d"
+    "EGL_ANGLE_platform_angle_d3d",
+    "EGL_ANGLE_d3d_share_handle_client_buffer"
 };
 
 #if defined(ANDROID)
 
 static PRLibrary* LoadApitraceLibrary()
 {
     // Initialization of gfx prefs here is only needed during the unit tests...
     gfxPrefs::GetSingleton();
@@ -409,16 +410,17 @@ GLLibraryEGL::EnsureInitialized(bool for
         SYMBOL(GetProcAddress),
         SYMBOL(SwapBuffers),
         SYMBOL(CopyBuffers),
         SYMBOL(QueryString),
         SYMBOL(QueryContext),
         SYMBOL(BindTexImage),
         SYMBOL(ReleaseTexImage),
         SYMBOL(QuerySurface),
+        SYMBOL(CreatePbufferFromClientBuffer),
         { nullptr, { nullptr } }
     };
 
     if (!GLLibraryLoader::LoadSymbols(mEGLLibrary, &earlySymbols[0])) {
         NS_WARNING("Couldn't find required entry points in EGL library (early init)");
         *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_SYM");
         return false;
     }
--- a/gfx/gl/GLLibraryEGL.h
+++ b/gfx/gl/GLLibraryEGL.h
@@ -128,16 +128,17 @@ public:
         mSymbols.fGetCurrentSurface = nullptr;
         mSymbols.fGetCurrentContext = nullptr;
         mSymbols.fMakeCurrent = nullptr;
         mSymbols.fDestroyContext = nullptr;
         mSymbols.fCreateContext = nullptr;
         mSymbols.fDestroySurface = nullptr;
         mSymbols.fCreateWindowSurface = nullptr;
         mSymbols.fCreatePbufferSurface = nullptr;
+        mSymbols.fCreatePbufferFromClientBuffer = nullptr;
         mSymbols.fCreatePixmapSurface = nullptr;
         mSymbols.fBindAPI = nullptr;
         mSymbols.fInitialize = nullptr;
         mSymbols.fChooseConfig = nullptr;
         mSymbols.fGetError = nullptr;
         mSymbols.fGetConfigAttrib = nullptr;
         mSymbols.fGetConfigs = nullptr;
         mSymbols.fWaitNative = nullptr;
@@ -180,16 +181,17 @@ public:
         ANGLE_surface_d3d_texture_2d_share_handle,
         EXT_create_context_robustness,
         KHR_image,
         KHR_fence_sync,
         ANDROID_native_fence_sync,
         EGL_ANDROID_image_crop,
         ANGLE_platform_angle,
         ANGLE_platform_angle_d3d,
+        ANGLE_d3d_share_handle_client_buffer,
         Extensions_Max
     };
 
     bool IsExtensionSupported(EGLExtensions aKnownExtension) const {
         return mAvailableExtensions[aKnownExtension];
     }
 
     void MarkExtensionUnsupported(EGLExtensions aKnownExtension) {
@@ -284,16 +286,24 @@ public:
     EGLSurface fCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint* attrib_list)
     {
         BEFORE_GL_CALL;
         EGLSurface surf = mSymbols.fCreatePbufferSurface(dpy, config, attrib_list);
         AFTER_GL_CALL;
         return surf;
     }
 
+    EGLSurface fCreatePbufferFromClientBuffer(EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, EGLConfig config, const EGLint *attrib_list)
+    {
+        BEFORE_GL_CALL;
+        EGLSurface ret = mSymbols.fCreatePbufferFromClientBuffer(dpy, buftype, buffer, config, attrib_list);
+        AFTER_GL_CALL;
+        return ret;
+    }
+
     EGLSurface fCreatePixmapSurface(EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint* attrib_list)
     {
         BEFORE_GL_CALL;
         EGLSurface surf = mSymbols.fCreatePixmapSurface(dpy, config, pixmap, attrib_list);
         AFTER_GL_CALL;
         return surf;
     }
 
@@ -580,16 +590,18 @@ public:
         typedef EGLContext (GLAPIENTRY * pfnCreateContext)(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint* attrib_list);
         pfnCreateContext fCreateContext;
         typedef EGLBoolean (GLAPIENTRY * pfnDestroySurface)(EGLDisplay dpy, EGLSurface surface);
         pfnDestroySurface fDestroySurface;
         typedef EGLSurface (GLAPIENTRY * pfnCreateWindowSurface)(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint* attrib_list);
         pfnCreateWindowSurface fCreateWindowSurface;
         typedef EGLSurface (GLAPIENTRY * pfnCreatePbufferSurface)(EGLDisplay dpy, EGLConfig config, const EGLint* attrib_list);
         pfnCreatePbufferSurface fCreatePbufferSurface;
+        typedef EGLSurface (GLAPIENTRY * pfnCreatePbufferFromClientBuffer)(EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, EGLConfig config, const EGLint *attrib_list);
+        pfnCreatePbufferFromClientBuffer fCreatePbufferFromClientBuffer;
         typedef EGLSurface (GLAPIENTRY * pfnCreatePixmapSurface)(EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint* attrib_list);
         pfnCreatePixmapSurface fCreatePixmapSurface;
         typedef EGLBoolean (GLAPIENTRY * pfnBindAPI)(EGLenum api);
         pfnBindAPI fBindAPI;
         typedef EGLBoolean (GLAPIENTRY * pfnInitialize)(EGLDisplay dpy, EGLint* major, EGLint* minor);
         pfnInitialize fInitialize;
         typedef EGLBoolean (GLAPIENTRY * pfnChooseConfig)(EGLDisplay dpy, const EGLint* attrib_list, EGLConfig* configs, EGLint config_size, EGLint* num_config);
         pfnChooseConfig fChooseConfig;
--- a/intl/locale/DateTimeFormat.cpp
+++ b/intl/locale/DateTimeFormat.cpp
@@ -102,16 +102,18 @@ DateTimeFormat::FormatUDateTime(const ns
   switch (aDateFormatSelector) {
     case kDateFormatLong:
       dateStyle = UDAT_LONG;
       break;
     case kDateFormatShort:
       dateStyle = UDAT_SHORT;
       break;
     case kDateFormatYearMonth:
+    case kDateFormatYearMonthLong:
+    case kDateFormatMonthLong:
     case kDateFormatWeekday:
       dateStyle = UDAT_PATTERN;
       break;
     case kDateFormatNone:
       dateStyle = UDAT_NONE;
       break;
     default:
       NS_ERROR("Unknown nsDateFormatSelector");
@@ -164,17 +166,33 @@ DateTimeFormat::FormatUDateTime(const ns
         pattern.SetLength(patternLength);
 
         if (status == U_BUFFER_OVERFLOW_ERROR) {
           status = U_ZERO_ERROR;
           udat_toPattern(dateTimeFormat, FALSE, reinterpret_cast<UChar*>(pattern.BeginWriting()), patternLength, &status);
         }
       }
 
-      nsAutoString skeleton(aDateFormatSelector == kDateFormatYearMonth ? u"yyyyMM " : u"EEE ");
+      nsAutoString skeleton;
+      switch (aDateFormatSelector) {
+      case kDateFormatYearMonth:
+        skeleton.AssignLiteral("yyyyMM ");
+        break;
+      case kDateFormatYearMonthLong:
+        skeleton.AssignLiteral("yyyyMMMM ");
+        break;
+      case kDateFormatMonthLong:
+        skeleton.AssignLiteral("MMMM ");
+        break;
+      case kDateFormatWeekday:
+        skeleton.AssignLiteral("EEE ");
+        break;
+      default:
+        break;
+      }
       int32_t dateSkeletonLen = skeleton.Length();
 
       if (timeStyle != UDAT_NONE) {
         skeleton.SetLength(DATETIME_FORMAT_INITIAL_LEN);
         int32_t skeletonLength = udatpg_getSkeleton(nullptr, reinterpret_cast<const UChar*>(pattern.BeginReading()), patternLength,
           reinterpret_cast<UChar*>(skeleton.BeginWriting() + dateSkeletonLen), DATETIME_FORMAT_INITIAL_LEN - dateSkeletonLen, &status);
         skeleton.SetLength(dateSkeletonLen + skeletonLength);
 
--- a/intl/locale/nsIScriptableDateFormat.idl
+++ b/intl/locale/nsIScriptableDateFormat.idl
@@ -8,16 +8,18 @@
 typedef long nsDateFormatSelector;
 %{ C++
 enum
 {
     kDateFormatNone = 0,            // do not include the date  in the format string
     kDateFormatLong,                // provides the long date format for the given locale
     kDateFormatShort,               // provides the short date format for the given locale
     kDateFormatYearMonth,           // formats using only the year and month 
+    kDateFormatYearMonthLong,       // long version of kDateFormatYearMonth
+    kDateFormatMonthLong,           // long format of month name only
     kDateFormatWeekday              // week day (e.g. Mon, Tue)
 };
 %}
 
 typedef long nsTimeFormatSelector;
 %{ C++
 enum
 {
--- a/intl/locale/tests/gtest/TestDateTimeFormat.cpp
+++ b/intl/locale/tests/gtest/TestDateTimeFormat.cpp
@@ -53,16 +53,24 @@ TEST(DateTimeFormat, DateFormatSelectors
 
   mozilla::DateTimeFormat::mLocale = new nsCString("en-US");
 
   nsAutoString formattedTime;
   nsresult rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatYearMonth, kTimeFormatNone, &prExplodedTime, formattedTime);
   ASSERT_TRUE(NS_SUCCEEDED(rv));
   ASSERT_STREQ("01/1970", NS_ConvertUTF16toUTF8(formattedTime).get());
 
+  rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatYearMonthLong, kTimeFormatNone, &prExplodedTime, formattedTime);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+  ASSERT_STREQ("January 1970", NS_ConvertUTF16toUTF8(formattedTime).get());
+
+  rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatMonthLong, kTimeFormatNone, &prExplodedTime, formattedTime);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+  ASSERT_STREQ("January", NS_ConvertUTF16toUTF8(formattedTime).get());
+
   rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatYearMonth, kTimeFormatNoSeconds, &prExplodedTime, formattedTime);
   ASSERT_TRUE(NS_SUCCEEDED(rv));
   ASSERT_STREQ("01/1970, 12:00 AM", NS_ConvertUTF16toUTF8(formattedTime).get());
 
   rv = mozilla::DateTimeFormat::FormatPRExplodedTime(kDateFormatYearMonth, kTimeFormatSeconds, &prExplodedTime, formattedTime);
   ASSERT_TRUE(NS_SUCCEEDED(rv));
   ASSERT_STREQ("01/1970, 12:00:00 AM", NS_ConvertUTF16toUTF8(formattedTime).get());
 
--- a/ipc/glue/ProtocolUtils.cpp
+++ b/ipc/glue/ProtocolUtils.cpp
@@ -540,16 +540,32 @@ IProtocol::SetEventTargetForActor(IProto
 
 void
 IProtocol::SetEventTargetForActorInternal(IProtocol* aActor,
                                           nsIEventTarget* aEventTarget)
 {
   Manager()->SetEventTargetForActorInternal(aActor, aEventTarget);
 }
 
+nsIEventTarget*
+IProtocol::GetActorEventTarget()
+{
+  // We should only call this function when this actor has been registered and
+  // is not unregistered yet.
+  MOZ_RELEASE_ASSERT(mId != kNullActorId && mId != kFreedActorId);
+  RefPtr<nsIEventTarget> target = Manager()->GetActorEventTargetInternal(this);
+  return target;
+}
+
+already_AddRefed<nsIEventTarget>
+IProtocol::GetActorEventTargetInternal(IProtocol* aActor)
+{
+  return Manager()->GetActorEventTargetInternal(aActor);
+}
+
 IToplevelProtocol::IToplevelProtocol(ProtocolId aProtoId, Side aSide)
  : IProtocol(aSide),
    mProtocolId(aProtoId),
    mOtherPid(mozilla::ipc::kInvalidProcessId),
    mLastRouteId(aSide == ParentSide ? kFreedActorId : kNullActorId),
    mLastShmemId(aSide == ParentSide ? kFreedActorId : kNullActorId),
    mEventTargetMutex("ProtocolEventTargetMutex")
 {
@@ -797,29 +813,45 @@ IToplevelProtocol::GetMessageEventTarget
 
     mEventTargetMap.AddWithID(target, handle.mId);
   }
 
   return target.forget();
 }
 
 already_AddRefed<nsIEventTarget>
-IToplevelProtocol::GetActorEventTarget(IProtocol* aActor)
+IToplevelProtocol::GetActorEventTargetInternal(IProtocol* aActor)
 {
   MOZ_RELEASE_ASSERT(aActor->Id() != kNullActorId && aActor->Id() != kFreedActorId);
 
   MutexAutoLock lock(mEventTargetMutex);
   nsCOMPtr<nsIEventTarget> target = mEventTargetMap.Lookup(aActor->Id());
   return target.forget();
 }
 
+already_AddRefed<nsIEventTarget>
+IToplevelProtocol::GetActorEventTarget(IProtocol* aActor)
+{
+  return GetActorEventTargetInternal(aActor);
+}
+
+nsIEventTarget*
+IToplevelProtocol::GetActorEventTarget()
+{
+  // The EventTarget of a ToplevelProtocol shall never be set.
+  return nullptr;
+}
+
 void
 IToplevelProtocol::SetEventTargetForActorInternal(IProtocol* aActor,
                                                   nsIEventTarget* aEventTarget)
 {
+  // The EventTarget of a ToplevelProtocol shall never be set.
+  MOZ_RELEASE_ASSERT(aActor != this);
+
   // We should only call this function on actors that haven't been used for IPC
   // code yet. Otherwise we'll be posting stuff to the wrong event target before
   // we're called.
   MOZ_RELEASE_ASSERT(aActor->Id() == kNullActorId || aActor->Id() == kFreedActorId);
 
   // Register the actor early. When it's registered again, it will keep the same
   // ID.
   int32_t id = Register(aActor);
--- a/ipc/glue/ProtocolUtils.h
+++ b/ipc/glue/ProtocolUtils.h
@@ -184,25 +184,31 @@ public:
     // Sets an event target to which all messages for aActor will be
     // dispatched. This method must be called before right before the SendPFoo
     // message for aActor is sent. And SendPFoo *must* be called if
     // SetEventTargetForActor is called. The receiver when calling
     // SetEventTargetForActor must be the actor that will be the manager for
     // aActor.
     void SetEventTargetForActor(IProtocol* aActor, nsIEventTarget* aEventTarget);
 
+    // Returns the event target set by SetEventTargetForActor() if available.
+    virtual nsIEventTarget* GetActorEventTarget();
+
 protected:
     friend class IToplevelProtocol;
 
     void SetId(int32_t aId) { mId = aId; }
     void SetManager(IProtocol* aManager);
     void SetIPCChannel(MessageChannel* aChannel) { mChannel = aChannel; }
 
     virtual void SetEventTargetForActorInternal(IProtocol* aActor, nsIEventTarget* aEventTarget);
 
+    virtual already_AddRefed<nsIEventTarget>
+    GetActorEventTargetInternal(IProtocol* aActor);
+
     static const int32_t kNullActorId = 0;
     static const int32_t kFreedActorId = 1;
 
 private:
     int32_t mId;
     Side mSide;
     IProtocol* mManager;
     MessageChannel* mChannel;
@@ -360,22 +366,28 @@ public:
     }
 
     virtual already_AddRefed<nsIEventTarget>
     GetMessageEventTarget(const Message& aMsg);
 
     already_AddRefed<nsIEventTarget>
     GetActorEventTarget(IProtocol* aActor);
 
+    virtual nsIEventTarget*
+    GetActorEventTarget();
+
 protected:
     virtual already_AddRefed<nsIEventTarget>
     GetConstructedEventTarget(const Message& aMsg) { return nullptr; }
 
     virtual void SetEventTargetForActorInternal(IProtocol* aActor, nsIEventTarget* aEventTarget);
 
+    virtual already_AddRefed<nsIEventTarget>
+    GetActorEventTargetInternal(IProtocol* aActor);
+
   private:
     ProtocolId mProtocolId;
     UniquePtr<Transport> mTrans;
     base::ProcessId mOtherPid;
     IDMap<IProtocol*> mActorMap;
     int32_t mLastRouteId;
     IDMap<Shmem::SharedMemory*> mShmemMap;
     Shmem::id_t mLastShmemId;
--- a/ipc/ipdl/Makefile.in
+++ b/ipc/ipdl/Makefile.in
@@ -14,16 +14,17 @@ include $(topsrcdir)/config/rules.mk
 
 
 # NB: the IPDL compiler manages .ipdl-->.h/.cpp dependencies itself,
 # which is why we don't have explicit .h/.cpp targets here
 ipdl: $(ALL_IPDLSRCS)
 	$(PYTHON) $(topsrcdir)/config/pythonpath.py \
 	  $(PLY_INCLUDE) \
 	  $(srcdir)/ipdl.py \
+	  --sync-msg-list=$(srcdir)/sync-messages.ini \
 	  --outheaders-dir=_ipdlheaders \
 	  --outcpp-dir=. \
 	  $(IPDLDIRS:%=-I%) \
 	  $^
 
 .PHONY: ipdl
 
 export:: ipdl
--- a/ipc/ipdl/ipdl.py
+++ b/ipc/ipdl/ipdl.py
@@ -2,47 +2,50 @@
 # 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/.
 
 import optparse, os, re, sys
 from cStringIO import StringIO
 from mozbuild.pythonutil import iter_modules_in_path
 import mozpack.path as mozpath
 import itertools
+from ConfigParser import RawConfigParser
 
 import ipdl
 
 def log(minv, fmt, *args):
     if _verbosity >= minv:
         print fmt % args
 
 # process command line
 
 op = optparse.OptionParser(usage='ipdl.py [options] IPDLfiles...')
 op.add_option('-I', '--include', dest='includedirs', default=[ ],
               action='append',
               help='Additional directory to search for included protocol specifications')
+op.add_option('-s', '--sync-msg-list', dest='syncMsgList', default='sync-messages.ini',
+              help="Config file listing allowed sync messages")
 op.add_option('-v', '--verbose', dest='verbosity', default=1, action='count',
               help='Verbose logging (specify -vv or -vvv for very verbose logging)')
 op.add_option('-q', '--quiet', dest='verbosity', action='store_const', const=0,
               help="Suppress logging output")
 op.add_option('-d', '--outheaders-dir', dest='headersdir', default='.',
               help="""Directory into which C++ headers will be generated.
 A protocol Foo in the namespace bar will cause the headers
   dir/bar/Foo.h, dir/bar/FooParent.h, and dir/bar/FooParent.h
 to be generated""")
 op.add_option('-o', '--outcpp-dir', dest='cppdir', default='.',
               help="""Directory into which C++ sources will be generated
 A protocol Foo in the namespace bar will cause the sources
   cppdir/FooParent.cpp, cppdir/FooChild.cpp
 to be generated""")
 
-
 options, files = op.parse_args()
 _verbosity = options.verbosity
+syncMsgList = options.syncMsgList
 headersdir = options.headersdir
 cppdir = options.cppdir
 includedirs = [ os.path.abspath(incdir) for incdir in options.includedirs ]
 
 if not len(files):
     op.error("No IPDL files specified")
 
 ipcmessagestartpath = os.path.join(headersdir, 'IPCMessageStart.h')
@@ -108,16 +111,21 @@ log(2, 'Generated C++ sources will be ge
 allmessages = {}
 allprotocols = []
 
 def normalizedFilename(f):
     if f == '-':
         return '<stdin>'
     return f
 
+log(2, 'Reading sync message list')
+parser = RawConfigParser()
+parser.readfp(open(options.syncMsgList))
+syncMsgList = parser.sections()
+
 # First pass: parse and type-check all protocols
 for f in files:
     log(2, os.path.basename(f))
     filename = normalizedFilename(f)
     if f == '-':
         fd = sys.stdin
     else:
         fd = open(f)
@@ -130,16 +138,20 @@ for f in files:
         print >>sys.stderr, 'Specification could not be parsed.'
         sys.exit(1)
 
     log(2, 'checking types')
     if not ipdl.typecheck(ast):
         print >>sys.stderr, 'Specification is not well typed.'
         sys.exit(1)
 
+    if not ipdl.checkSyncMessage(ast, syncMsgList):
+        print >>sys.stderr, 'Error: New sync IPC messages must be reviewed by an IPC peer and recorded in %s' % options.syncMsgList
+        sys.exit(1)
+
     if _verbosity > 2:
         log(3, '  pretty printed code:')
         ipdl.genipdl(ast, codedir)
 
 # Second pass: generate code
 for f in files:
     # Read from parser cache
     filename = normalizedFilename(f)
--- a/ipc/ipdl/ipdl/__init__.py
+++ b/ipc/ipdl/ipdl/__init__.py
@@ -1,21 +1,22 @@
 # 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/.
 
-__all__ = [ 'gencxx', 'genipdl', 'parse', 'typecheck', 'writeifmodified' ]
+__all__ = [ 'gencxx', 'genipdl', 'parse', 'typecheck', 'writeifmodified', 'checkSyncMessage' ]
 
 import os, sys
 from cStringIO import StringIO
 
 from ipdl.cgen import IPDLCodeGen
 from ipdl.lower import LowerToCxx, msgenums
 from ipdl.parser import Parser, ParseError
 from ipdl.type import TypeCheck
+from ipdl.checker import checkSyncMessage
 
 from ipdl.cxx.cgen import CxxCodeGen
 
 
 def parse(specstring, filename='/stdin', includedirs=[ ], errout=sys.stderr):
     '''Return an IPDL AST if parsing was successful.  Print errors to |errout|
     if it is not.'''
     # The file type and name are later enforced by the type checker.
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/ipdl/checker.py
@@ -0,0 +1,43 @@
+# vim: set ts=4 sw=4 tw=99 et:
+# 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/.
+
+import sys
+from ipdl.ast import Visitor, ASYNC
+
+class SyncMessageChecker(Visitor):
+    def __init__(self, syncMsgList):
+        self.syncMsgList = syncMsgList
+        self.errors = []
+
+    def prettyMsgName(self, msg):
+        return "%s::%s" % (self.currentProtocol, msg)
+
+    def errorUnknownSyncMessage(self, loc, msg):
+        self.errors.append('%s: error: Unknown sync IPC message %s' %
+                           (str(loc), msg))
+
+    def errorAsyncMessageCanRemove(self, loc, msg):
+        self.errors.append('%s: error: IPC message %s is async, can be delisted' %
+                           (str(loc), msg))
+
+    def visitProtocol(self, p):
+        self.currentProtocol = p.name
+        Visitor.visitProtocol(self, p)
+
+    def visitMessageDecl(self, md):
+        pn = self.prettyMsgName(md.name)
+        if md.sendSemantics is not ASYNC and pn not in self.syncMsgList:
+            self.errorUnknownSyncMessage(md.loc, pn)
+        if md.sendSemantics is ASYNC and pn in self.syncMsgList:
+            self.errorAsyncMessageCanRemove(md.loc, pn)
+
+def checkSyncMessage(tu, syncMsgList, errout=sys.stderr):
+    checker = SyncMessageChecker(syncMsgList)
+    tu.accept(checker)
+    if len(checker.errors):
+        for error in checker.errors:
+            print >>errout, error
+        return False
+    return True
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/sync-messages.ini
@@ -0,0 +1,1013 @@
+#############################################################
+#                                                           #
+# Any changes to this file must be reviewed by an IPC peer. #
+#                                                           #
+#############################################################
+
+# C++ unit tests
+[PTestBadActorSub::__delete__]
+description =
+[PTestCancel::Test1_1]
+description =
+[PTestCancel::Test2_2]
+description =
+[PTestCancel::Test2_1]
+description =
+[PTestCancel::Test3_1]
+description =
+[PTestCancel::Test3_2]
+description =
+[PTestCancel::CheckChild]
+description =
+[PTestCancel::CheckParent]
+description =
+[PTestCrashCleanup::DIEDIEDIE]
+description =
+[PTestDataStructures::Test1]
+description =
+[PTestDataStructures::Test2]
+description =
+[PTestDataStructures::Test3]
+description =
+[PTestDataStructures::Test4]
+description =
+[PTestDataStructures::Test5]
+description =
+[PTestDataStructures::Test6]
+description =
+[PTestDataStructures::Test7_0]
+description =
+[PTestDataStructures::Test7]
+description =
+[PTestDataStructures::Test8]
+description =
+[PTestDataStructures::Test9]
+description =
+[PTestDataStructures::Test10]
+description =
+[PTestDataStructures::Test11]
+description =
+[PTestDataStructures::Test12]
+description =
+[PTestDataStructures::Test13]
+description =
+[PTestDataStructures::Test14]
+description =
+[PTestDataStructures::Test15]
+description =
+[PTestDataStructures::Test16]
+description =
+[PTestDataStructures::Test17]
+description =
+[PTestDataStructures::Test18]
+description =
+[PTestDataStructures::Dummy]
+description =
+[PTestDataStructuresSub::__delete__]
+description =
+[PTestDemon::HiPrioSyncMessage]
+description =
+[PTestDemon::SyncMessage]
+description =
+[PTestDemon::UrgentSyncMessage]
+description =
+[PTestDesc::PTestDescSub]
+description =
+[PTestDescSub::PTestDescSubsub]
+description =
+[PTestDescSubsub::__delete__]
+description =
+[PTestEndpointBridgeMainSub::HiRpc]
+description =
+[PTestEndpointBridgeMainSub::HelloSync]
+description =
+[PTestEndpointBridgeMainSub::HelloRpc]
+description =
+[PTestEndpointOpensOpened::HiRpc]
+description =
+[PTestEndpointOpensOpened::HelloSync]
+description =
+[PTestEndpointOpensOpened::HelloRpc]
+description =
+[PTestFailedCtor::PTestFailedCtorSub]
+description =
+[PTestFailedCtorSub::Sync]
+description =
+[PTestHangs::StackFrame]
+description =
+[PTestHangs::Hang]
+description =
+[PTestHighestPrio::Msg2]
+description =
+[PTestHighestPrio::Msg4]
+description =
+[PTestHighestPrio::StartInner]
+description =
+[PTestIndirectProtocolParamFirst::Test]
+description =
+[PTestInterruptErrorCleanup::Error]
+description =
+[PTestInterruptErrorCleanup::__delete__]
+description =
+[PTestInterruptRaces::Race]
+description =
+[PTestInterruptRaces::StackFrame]
+description =
+[PTestInterruptRaces::StackFrame3]
+description =
+[PTestInterruptRaces::StartRace]
+description =
+[PTestInterruptRaces::Parent]
+description =
+[PTestInterruptRaces::GetAnsweredParent]
+description =
+[PTestInterruptRaces::Child]
+description =
+[PTestInterruptShutdownRace::StartDeath]
+description =
+[PTestInterruptShutdownRace::Exit]
+description =
+[PTestJSON::Test]
+description =
+[PTestLatency::Rpc]
+description =
+[PTestLatency::Synchro]
+description =
+[PTestLatency::Synchro2]
+description =
+[PTestNestedLoops::R]
+description =
+[PTestPriority::Msg2]
+description =
+[PTestRPC::Test1_Start]
+description =
+[PTestRPC::Test1_InnerEvent]
+description =
+[PTestRPC::Test2_OutOfOrder]
+description =
+[PTestRPC::Test1_InnerQuery]
+description =
+[PTestRPC::Test1_NoReenter]
+description =
+[PTestRPC::Test2_FirstUrgent]
+description =
+[PTestRPC::Test2_SecondUrgent]
+description =
+[PTestRaceDeadlock::Lose]
+description =
+[PTestRaceDeadlock::Win]
+description =
+[PTestRaceDeadlock::Rpc]
+description =
+[PTestRaceDeferral::Lose]
+description =
+[PTestRaceDeferral::Win]
+description =
+[PTestRaceDeferral::Rpc]
+description =
+[PTestRacyInterruptReplies::R_]
+description =
+[PTestRacyInterruptReplies::_R]
+description =
+[PTestRacyReentry::E]
+description =
+[PTestRacyReentry::H]
+description =
+[PTestRacyUndefer::Race]
+description =
+[PTestRacyUndefer::Spam]
+description =
+[PTestRacyUndefer::RaceWinTwice]
+description =
+[PTestShutdown::Sync]
+description =
+[PTestShutdownSub::StackFrame]
+description =
+[PTestShutdownSub::__delete__]
+description =
+[PTestShutdownSubsub::__delete__]
+description =
+[PTestStackHooks::Sync]
+description =
+[PTestStackHooks::Rpc]
+description =
+[PTestStackHooks::StackFrame]
+description =
+[PTestSyncError::Error]
+description =
+[PTestSyncWakeup::StackFrame]
+description =
+[PTestSyncWakeup::Sync1]
+description =
+[PTestSyncWakeup::Sync2]
+description =
+[PTestUrgency::Test1]
+description =
+[PTestUrgency::Test3]
+description =
+[PTestUrgency::FinalTest_Begin]
+description =
+[PTestUrgency::Reply1]
+description =
+[PTestUrgency::Reply2]
+description =
+[PTestUrgentHangs::Test1_2]
+description =
+[PTestUrgentHangs::TestInner]
+description =
+[PTestUrgentHangs::TestInnerUrgent]
+description =
+[PTestUrgentHangs::Test1_1]
+description =
+[PTestUrgentHangs::Test1_3]
+description =
+[PTestUrgentHangs::Test2]
+description =
+[PTestUrgentHangs::Test3]
+description =
+[PTestUrgentHangs::Test4_1]
+description =
+[PTestUrgentHangs::Test5_1]
+description =
+
+# A11y code
+[PDocAccessible::State]
+description =
+[PDocAccessible::NativeState]
+description =
+[PDocAccessible::Name]
+description =
+[PDocAccessible::Value]
+description =
+[PDocAccessible::Help]
+description =
+[PDocAccessible::Description]
+description =
+[PDocAccessible::Attributes]
+description =
+[PDocAccessible::RelationByType]
+description =
+[PDocAccessible::Relations]
+description =
+[PDocAccessible::IsSearchbox]
+description =
+[PDocAccessible::LandmarkRole]
+description =
+[PDocAccessible::ARIARoleAtom]
+description =
+[PDocAccessible::GetLevelInternal]
+description =
+[PDocAccessible::CaretLineNumber]
+description =
+[PDocAccessible::CaretOffset]
+description =
+[PDocAccessible::CharacterCount]
+description =
+[PDocAccessible::SelectionCount]
+description =
+[PDocAccessible::TextSubstring]
+description =
+[PDocAccessible::GetTextAfterOffset]
+description =
+[PDocAccessible::GetTextAtOffset]
+description =
+[PDocAccessible::GetTextBeforeOffset]
+description =
+[PDocAccessible::CharAt]
+description =
+[PDocAccessible::TextAttributes]
+description =
+[PDocAccessible::DefaultTextAttributes]
+description =
+[PDocAccessible::TextBounds]
+description =
+[PDocAccessible::CharBounds]
+description =
+[PDocAccessible::OffsetAtPoint]
+description =
+[PDocAccessible::SelectionBoundsAt]
+description =
+[PDocAccessible::SetSelectionBoundsAt]
+description =
+[PDocAccessible::AddToSelection]
+description =
+[PDocAccessible::RemoveFromSelection]
+description =
+[PDocAccessible::Text]
+description =
+[PDocAccessible::ReplaceText]
+description =
+[PDocAccessible::InsertText]
+description =
+[PDocAccessible::CopyText]
+description =
+[PDocAccessible::CutText]
+description =
+[PDocAccessible::DeleteText]
+description =
+[PDocAccessible::PasteText]
+description =
+[PDocAccessible::ImagePosition]
+description =
+[PDocAccessible::ImageSize]
+description =
+[PDocAccessible::StartOffset]
+description =
+[PDocAccessible::EndOffset]
+description =
+[PDocAccessible::IsLinkValid]
+description =
+[PDocAccessible::AnchorCount]
+description =
+[PDocAccessible::AnchorURIAt]
+description =
+[PDocAccessible::AnchorAt]
+description =
+[PDocAccessible::LinkCount]
+description =
+[PDocAccessible::LinkAt]
+description =
+[PDocAccessible::LinkIndexOf]
+description =
+[PDocAccessible::LinkIndexAtOffset]
+description =
+[PDocAccessible::TableOfACell]
+description =
+[PDocAccessible::ColIdx]
+description =
+[PDocAccessible::RowIdx]
+description =
+[PDocAccessible::GetPosition]
+description =
+[PDocAccessible::ColExtent]
+description =
+[PDocAccessible::RowExtent]
+description =
+[PDocAccessible::GetColRowExtents]
+description =
+[PDocAccessible::ColHeaderCells]
+description =
+[PDocAccessible::RowHeaderCells]
+description =
+[PDocAccessible::IsCellSelected]
+description =
+[PDocAccessible::TableCaption]
+description =
+[PDocAccessible::TableSummary]
+description =
+[PDocAccessible::TableColumnCount]
+description =
+[PDocAccessible::TableRowCount]
+description =
+[PDocAccessible::TableCellAt]
+description =
+[PDocAccessible::TableCellIndexAt]
+description =
+[PDocAccessible::TableColumnIndexAt]
+description =
+[PDocAccessible::TableRowIndexAt]
+description =
+[PDocAccessible::TableRowAndColumnIndicesAt]
+description =
+[PDocAccessible::TableColumnExtentAt]
+description =
+[PDocAccessible::TableRowExtentAt]
+description =
+[PDocAccessible::TableColumnDescription]
+description =
+[PDocAccessible::TableRowDescription]
+description =
+[PDocAccessible::TableColumnSelected]
+description =
+[PDocAccessible::TableRowSelected]
+description =
+[PDocAccessible::TableCellSelected]
+description =
+[PDocAccessible::TableSelectedCellCount]
+description =
+[PDocAccessible::TableSelectedColumnCount]
+description =
+[PDocAccessible::TableSelectedRowCount]
+description =
+[PDocAccessible::TableSelectedCells]
+description =
+[PDocAccessible::TableSelectedCellIndices]
+description =
+[PDocAccessible::TableSelectedColumnIndices]
+description =
+[PDocAccessible::TableSelectedRowIndices]
+description =
+[PDocAccessible::TableSelectColumn]
+description =
+[PDocAccessible::TableSelectRow]
+description =
+[PDocAccessible::TableUnselectColumn]
+description =
+[PDocAccessible::TableUnselectRow]
+description =
+[PDocAccessible::TableIsProbablyForLayout]
+description =
+[PDocAccessible::AtkTableColumnHeader]
+description =
+[PDocAccessible::AtkTableRowHeader]
+description =
+[PDocAccessible::SelectedItems]
+description =
+[PDocAccessible::SelectedItemCount]
+description =
+[PDocAccessible::GetSelectedItem]
+description =
+[PDocAccessible::IsItemSelected]
+description =
+[PDocAccessible::AddItemToSelection]
+description =
+[PDocAccessible::RemoveItemFromSelection]
+description =
+[PDocAccessible::SelectAll]
+description =
+[PDocAccessible::UnselectAll]
+description =
+[PDocAccessible::DoAction]
+description =
+[PDocAccessible::ActionCount]
+description =
+[PDocAccessible::ActionDescriptionAt]
+description =
+[PDocAccessible::ActionNameAt]
+description =
+[PDocAccessible::AccessKey]
+description =
+[PDocAccessible::KeyboardShortcut]
+description =
+[PDocAccessible::AtkKeyBinding]
+description =
+[PDocAccessible::CurValue]
+description =
+[PDocAccessible::SetCurValue]
+description =
+[PDocAccessible::MinValue]
+description =
+[PDocAccessible::MaxValue]
+description =
+[PDocAccessible::Step]
+description =
+[PDocAccessible::FocusedChild]
+description =
+[PDocAccessible::Language]
+description =
+[PDocAccessible::DocType]
+description =
+[PDocAccessible::Title]
+description =
+[PDocAccessible::URL]
+description =
+[PDocAccessible::MimeType]
+description =
+[PDocAccessible::URLDocTypeMimeType]
+description =
+[PDocAccessible::AccessibleAtPoint]
+description =
+[PDocAccessible::Extents]
+description =
+[PDocAccessible::DOMNodeID]
+description =
+[PDocAccessible::GetWindowedPluginIAccessible]
+description =
+
+# CPOWs
+[PBrowser::RpcMessage]
+description =
+[PContent::RpcMessage]
+description =
+[PJavaScript::PreventExtensions]
+description =
+[PJavaScript::GetPropertyDescriptor]
+description =
+[PJavaScript::GetOwnPropertyDescriptor]
+description =
+[PJavaScript::DefineProperty]
+description =
+[PJavaScript::Delete]
+description =
+[PJavaScript::Has]
+description =
+[PJavaScript::HasOwn]
+description =
+[PJavaScript::Get]
+description =
+[PJavaScript::Set]
+description =
+[PJavaScript::IsExtensible]
+description =
+[PJavaScript::CallOrConstruct]
+description =
+[PJavaScript::HasInstance]
+description =
+[PJavaScript::GetBuiltinClass]
+description =
+[PJavaScript::IsArray]
+description =
+[PJavaScript::ClassName]
+description =
+[PJavaScript::GetPrototype]
+description =
+[PJavaScript::GetPrototypeIfOrdinary]
+description =
+[PJavaScript::RegExpToShared]
+description =
+[PJavaScript::GetPropertyKeys]
+description =
+[PJavaScript::InstanceOf]
+description =
+[PJavaScript::DOMInstanceOf]
+description =
+
+# Plugins
+[PPluginWidget::Create]
+description =
+[PPluginWidget::GetNativePluginPort]
+description =
+[PPluginWidget::SetNativeChildWindow]
+description =
+[PPluginInstance::__delete__]
+description =
+[PPluginInstance::CreateChildPluginWindow]
+description =
+[PPluginInstance::NPP_SetWindow]
+description =
+[PPluginInstance::NPP_GetValue_NPPVpluginWantsAllNetworkStreams]
+description =
+[PPluginInstance::NPP_GetValue_NPPVpluginNeedsXEmbed]
+description =
+[PPluginInstance::NPP_GetValue_NPPVpluginScriptableNPObject]
+description =
+[PPluginInstance::NPP_SetValue_NPNVprivateModeBool]
+description =
+[PPluginInstance::NPP_GetValue_NPPVpluginNativeAccessibleAtkPlugId]
+description =
+[PPluginInstance::NPP_SetValue_NPNVCSSZoomFactor]
+description =
+[PPluginInstance::NPP_SetValue_NPNVmuteAudioBool]
+description =
+[PPluginInstance::NPP_HandleEvent]
+description =
+[PPluginInstance::NPP_HandleEvent_Shmem]
+description =
+[PPluginInstance::NPP_HandleEvent_IOSurface]
+description =
+[PPluginInstance::Paint]
+description =
+[PPluginInstance::NPP_Destroy]
+description =
+[PPluginInstance::NPN_GetValue_NPNVWindowNPObject]
+description =
+[PPluginInstance::NPN_GetValue_NPNVPluginElementNPObject]
+description =
+[PPluginInstance::NPN_GetValue_NPNVprivateModeBool]
+description =
+[PPluginInstance::NPN_GetValue_NPNVnetscapeWindow]
+description =
+[PPluginInstance::NPN_GetValue_NPNVdocumentOrigin]
+description =
+[PPluginInstance::NPN_GetValue_DrawingModelSupport]
+description =
+[PPluginInstance::NPN_GetValue_SupportsAsyncBitmapSurface]
+description =
+[PPluginInstance::NPN_GetValue_SupportsAsyncDXGISurface]
+description =
+[PPluginInstance::NPN_GetValue_PreferredDXGIAdapter]
+description =
+[PPluginInstance::NPN_SetValue_NPPVpluginWindow]
+description =
+[PPluginInstance::NPN_SetValue_NPPVpluginTransparent]
+description =
+[PPluginInstance::NPN_SetValue_NPPVpluginUsesDOMForCursor]
+description =
+[PPluginInstance::NPN_SetValue_NPPVpluginDrawingModel]
+description =
+[PPluginInstance::NPN_SetValue_NPPVpluginEventModel]
+description =
+[PPluginInstance::NPN_SetValue_NPPVpluginIsPlayingAudio]
+description =
+[PPluginInstance::NPN_GetURL]
+description =
+[PPluginInstance::NPN_PostURL]
+description =
+[PPluginInstance::PStreamNotify]
+description =
+[PPluginInstance::RevokeCurrentDirectSurface]
+description =
+[PPluginInstance::InitDXGISurface]
+description =
+[PPluginInstance::FinalizeDXGISurface]
+description =
+[PPluginInstance::ShowDirectBitmap]
+description =
+[PPluginInstance::ShowDirectDXGISurface]
+description =
+[PPluginInstance::Show]
+description =
+[PPluginInstance::NPN_PushPopupsEnabledState]
+description =
+[PPluginInstance::NPN_PopPopupsEnabledState]
+description =
+[PPluginInstance::NPN_GetValueForURL]
+description =
+[PPluginInstance::NPN_SetValueForURL]
+description =
+[PPluginInstance::NPN_GetAuthenticationInfo]
+description =
+[PPluginInstance::NPN_ConvertPoint]
+description =
+[PPluginInstance::NegotiatedCarbon]
+description =
+[PPluginInstance::GetCompositionString]
+description =
+[PPluginInstance::NPP_NewStream]
+description =
+[PPluginInstance::PPluginStream]
+description =
+[PPluginInstance::PluginFocusChange]
+description =
+[PPluginInstance::SetPluginFocus]
+description =
+[PPluginInstance::UpdateWindow]
+description =
+[PPluginModule::ModuleSupportsAsyncRender]
+description =
+[PPluginModule::NP_GetEntryPoints]
+description =
+[PPluginModule::NP_Initialize]
+description =
+[PPluginModule::SyncNPP_New]
+description =
+[PPluginModule::NP_Shutdown]
+description =
+[PPluginModule::OptionalFunctionsSupported]
+description =
+[PPluginModule::PCrashReporter]
+description =
+[PPluginModule::ProcessSomeEvents]
+description =
+[PPluginModule::NPN_SetException]
+description =
+[PPluginModule::GetKeyState]
+description =
+[PPluginModule::NPN_SetValue_NPPVpluginRequiresAudioDeviceChanges]
+description =
+[PPluginModule::InitCrashReporter]
+description =
+[PPluginScriptableObject::NPN_Evaluate]
+description =
+[PPluginScriptableObject::Invalidate]
+description =
+[PPluginScriptableObject::HasMethod]
+description =
+[PPluginScriptableObject::Invoke]
+description =
+[PPluginScriptableObject::InvokeDefault]
+description =
+[PPluginScriptableObject::HasProperty]
+description =
+[PPluginScriptableObject::SetProperty]
+description =
+[PPluginScriptableObject::RemoveProperty]
+description =
+[PPluginScriptableObject::Enumerate]
+description =
+[PPluginScriptableObject::Construct]
+description =
+[PPluginScriptableObject::GetParentProperty]
+description =
+[PPluginScriptableObject::GetChildProperty]
+description =
+[PPluginStream::NPN_Write]
+description =
+[PPluginStream::__delete__]
+description =
+
+# The rest
+[PHeapSnapshotTempFileHelper::OpenHeapSnapshotTempFile]
+description =
+[PBlob::BlobStreamSync]
+description =
+[PBlob::WaitForSliceCreation]
+description =
+[PBlob::GetFileId]
+description =
+[PBlob::GetFilePath]
+description =
+[PBackgroundMutableFile::GetFileId]
+description =
+[PBackgroundIndexedDBUtils::GetFileReferences]
+description =
+[PBrowser::SyncMessage]
+description =
+[PBrowser::PPluginWidget]
+description =
+[PBrowser::GetWidgetNativeData]
+description =
+[PBrowser::DispatchFocusToTopLevelWindow]
+description =
+[PBrowser::NotifyIMEFocus]
+description =
+[PBrowser::NotifyIMEMouseButtonEvent]
+description =
+[PBrowser::RequestIMEToCommitComposition]
+description =
+[PBrowser::StartPluginIME]
+description =
+[PBrowser::GetInputContext]
+description =
+[PBrowser::IsParentWindowMainWidgetVisible]
+description =
+[PBrowser::GetDPI]
+description =
+[PBrowser::GetDefaultScale]
+description =
+[PBrowser::GetWidgetRounding]
+description =
+[PBrowser::GetMaxTouchPoints]
+description =
+[PBrowser::BrowserFrameOpenWindow]
+description =
+[PBrowser::RequestNativeKeyBindings]
+description =
+[PBrowser::GetTabCount]
+description =
+[PBrowser::DispatchWheelEvent]
+description =
+[PBrowser::DispatchMouseEvent]
+description =
+[PBrowser::DispatchKeyboardEvent]
+description =
+[PBrowser::EnsureLayersConnected]
+description =
+[PContent::SyncMessage]
+description =
+[PContent::CreateChildProcess]
+description =
+[PContent::BridgeToChildProcess]
+description =
+[PContent::LoadPlugin]
+description =
+[PContent::ConnectPluginBridge]
+description =
+[PContent::GetBlocklistState]
+description =
+[PContent::FindPlugins]
+description =
+[PContent::PCrashReporter]
+description =
+[PContent::NSSU2FTokenIsCompatibleVersion]
+description =
+[PContent::NSSU2FTokenIsRegistered]
+description =
+[PContent::NSSU2FTokenRegister]
+description =
+[PContent::NSSU2FTokenSign]
+description =
+[PContent::IsSecureURI]
+description =
+[PContent::PScreenManager]
+description =
+[PContent::PURLClassifier]
+description =
+[PContent::ClassifyLocal]
+description =
+[PContent::GetGfxVars]
+description =
+[PContent::ReadFontList]
+description =
+[PContent::ReadDataStorageArray]
+description =
+[PContent::ReadPermissions]
+description =
+[PContent::GetClipboard]
+description =
+[PContent::ClipboardHasType]
+description =
+[PContent::GetSystemColors]
+description =
+[PContent::GetIconForExtension]
+description =
+[PContent::GetShowPasswordSetting]
+description =
+[PContent::KeywordToURI]
+description =
+[PContent::NotifyKeywordSearchLoading]
+description =
+[PContent::AllocateLayerTreeId]
+description =
+[PContent::GetGfxInfoFeatureStatus]
+description =
+[PContent::BeginDriverCrashGuard]
+description =
+[PContent::EndDriverCrashGuard]
+description =
+[PContent::OpenAnonymousTemporaryFile]
+description =
+[PContent::KeygenProcessValue]
+description =
+[PContent::KeygenProvideContent]
+description =
+[PContent::AllocateTabId]
+description =
+[PContent::GetGraphicsDeviceInitData]
+description =
+[PContent::CreateWindow]
+description =
+[PContent::GetDeviceStorageLocation]
+description =
+[PContent::GetDeviceStorageLocations]
+description =
+[PContent::GetAndroidSystemInfo]
+description =
+[PContent::UngrabPointer]
+description =
+[PContent::RemovePermission]
+description =
+[PContent::GetA11yContentId]
+description =
+[PContentBridge::SyncMessage]
+description =
+[PScreenManager::Refresh]
+description =
+[PScreenManager::ScreenRefresh]
+description =
+[PScreenManager::GetPrimaryScreen]
+description =
+[PScreenManager::ScreenForRect]
+description =
+[PScreenManager::ScreenForBrowser]
+description =
+[PGMP::StartPlugin]
+description =
+[PGMPService::LaunchGMP]
+description =
+[PGMPService::GetGMPNodeId]
+description =
+[PGMPVideoDecoder::NeedShmem]
+description =
+[PGMPVideoEncoder::NeedShmem]
+description =
+[PVideoDecoderManager::PVideoDecoder]
+description =
+[PVideoDecoderManager::Readback]
+description =
+[PSpeechSynthesis::ReadVoicesAndState]
+description =
+[PBrowserStream::NPN_RequestRead]
+description =
+[PStorage::Preload]
+description =
+[PRemoteSpellcheckEngine::Check]
+description =
+[PRemoteSpellcheckEngine::CheckAndSuggest]
+description =
+[PRemoteSpellcheckEngine::SetDictionary]
+description =
+[PGPU::AddLayerTreeIdMapping]
+description =
+[PGPU::GetDeviceStatus]
+description =
+[PAPZCTreeManager::ReceiveMultiTouchInputEvent]
+description =
+[PAPZCTreeManager::ReceiveMouseInputEvent]
+description =
+[PAPZCTreeManager::ReceivePanGestureInputEvent]
+description =
+[PAPZCTreeManager::ReceivePinchGestureInputEvent]
+description =
+[PAPZCTreeManager::ReceiveTapGestureInputEvent]
+description =
+[PAPZCTreeManager::ReceiveScrollWheelInputEvent]
+description =
+[PAPZCTreeManager::TransformEventRefPoint]
+description =
+[PCompositorBridge::Initialize]
+description =
+[PCompositorBridge::Reset]
+description =
+[PCompositorBridge::GetCompositorOptions]
+description =
+[PCompositorBridge::GetFrameUniformity]
+description =
+[PCompositorBridge::WillClose]
+description =
+[PCompositorBridge::Pause]
+description =
+[PCompositorBridge::Resume]
+description =
+[PCompositorBridge::NotifyChildCreated]
+description =
+[PCompositorBridge::NotifyChildRecreated]
+description =
+[PCompositorBridge::MakeSnapshot]
+description =
+[PCompositorBridge::FlushRendering]
+description =
+[PCompositorBridge::StartFrameTimeRecording]
+description =
+[PCompositorBridge::StopFrameTimeRecording]
+description =
+[PCompositorBridge::PLayerTransaction]
+description =
+[PCompositorBridge::SyncWithCompositor]
+description =
+[PCompositorBridge::PWebRenderBridge]
+description =
+[PCompositorWidget::EnterPresentLock]
+description =
+[PCompositorWidget::LeavePresentLock]
+description =
+[PCompositorWidget::ClearTransparentWindow]
+description =
+[PImageBridge::WillClose]
+description =
+[PImageBridge::NewCompositable]
+description =
+[PLayerTransaction::SetTestSampleTime]
+description =
+[PLayerTransaction::LeaveTestMode]
+description =
+[PLayerTransaction::GetAnimationOpacity]
+description =
+[PLayerTransaction::GetAnimationTransform]
+description =
+[PLayerTransaction::SetAsyncScrollOffset]
+description =
+[PLayerTransaction::SetAsyncZoom]
+description =
+[PLayerTransaction::GetAPZTestData]
+description =
+[PLayerTransaction::RequestProperty]
+description =
+[PTexture::DestroySync]
+description =
+[PUiCompositorController::Pause]
+description =
+[PUiCompositorController::Resume]
+description =
+[PUiCompositorController::ResumeAndResize]
+description =
+[PWebRenderBridge::Create]
+description =
+[PWebRenderBridge::AddImage]
+description =
+[PWebRenderBridge::UpdateImage]
+description =
+[PWebRenderBridge::DeleteImage]
+description =
+[PWebRenderBridge::DPBegin]
+description =
+[PWebRenderBridge::DPSyncEnd]
+description =
+[PWebRenderBridge::DPGetSnapshot]
+description =
+[PVRManager::GetDisplays]
+description =
+[PVRManager::GetSensorState]
+description =
+[PVRManager::GetImmediateSensorState]
+description =
+[PVRManager::SetHaveEventListener]
+description =
+[PVRManager::GetControllers]
+description =
+[PHal::GetCurrentBatteryInformation]
+description =
+[PHal::GetCurrentNetworkInformation]
+description =
+[PHal::GetScreenEnabled]
+description =
+[PHal::GetKeyLightEnabled]
+description =
+[PHal::GetCpuSleepAllowed]
+description =
+[PHal::GetScreenBrightness]
+description =
+[PHal::GetTimezone]
+description =
+[PHal::GetTimezoneOffset]
+description =
+[PHal::GetWakeLockInfo]
+description =
+[PHal::GetCurrentScreenConfiguration]
+description =
+[PHal::LockScreenOrientation]
+description =
+[PHal::GetCurrentSwitchState]
+description =
+[PCookieService::GetCookieString]
+description =
+[PPrinting::ShowProgress]
+description =
+[PPrinting::SavePrintSettings]
+description =
+[PHandlerService::FillHandlerInfo]
+description =
+[PHandlerService::Exists]
+description =
+[PHandlerService::GetTypeFromExtension]
+description =
--- a/ipc/ipdl/test/ipdl/Makefile.in
+++ b/ipc/ipdl/test/ipdl/Makefile.in
@@ -8,10 +8,11 @@ OKTESTS := $(wildcard $(srcdir)/ok/*.ipd
 ERRORTESTS := $(wildcard $(srcdir)/error/*.ipdl) $(wildcard $(srcdir)/error/*.ipdlh)
 
 check::
 	@$(PYTHON) $(srcdir)/runtests.py  \
 		$(srcdir)/ok $(srcdir)/error  \
 		$(PYTHON) $(topsrcdir)/config/pythonpath.py  \
 		$(PLY_INCLUDE)  \
 		$(topsrcdir)/ipc/ipdl/ipdl.py  \
+		-s $(srcdir)/sync-messages.ini \
 		OKTESTS $(OKTESTS)  \
 		ERRORTESTS $(ERRORTESTS)
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/asyncMessageListed.ipdl
@@ -0,0 +1,4 @@
+protocol asyncMessageListed {
+parent:
+    async Msg();
+};
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/unknownIntrMessage.ipdl
@@ -0,0 +1,4 @@
+intr protocol unknownSyncMessage {
+parent:
+    intr Msg();
+};
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/error/unknownSyncMessage.ipdl
@@ -0,0 +1,4 @@
+sync protocol unknownSyncMessage {
+parent:
+    sync Msg();
+};
new file mode 100644
--- /dev/null
+++ b/ipc/ipdl/test/ipdl/sync-messages.ini
@@ -0,0 +1,22 @@
+[actorreturn::Msg]
+[array_Union::Msg]
+[union_Basic::Msg]
+[Struct::test]
+[intrProtocol::SyncMsg]
+[intrProtocol::InterruptMsg]
+[shmem::SyncMsg]
+[shmem::InterruptMsg]
+[syncProtocol::SyncMsg]
+[messageVerify::__delete__]
+[messageVerify::msg3]
+[messageVerify::msg4]
+[messageVerifyTopLevel::messageVerify]
+[DeleteSub::__delete__]
+[intrMessageCompress::foo]
+[intrMessageCompress::bar]
+[syncMessageCompress::foo]
+[syncParentToChild::Msg]
+[tooWeakIntrSync::Msg]
+[tooWeakSyncAsync::Msg]
+[undeclReturnType::Msg]
+[asyncMessageListed::Msg]
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -8047,16 +8047,18 @@ Parser<ParseHandler>::comprehensionFor(G
     uint32_t begin = pos().begin;
 
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
 
     // FIXME: Destructuring binding (bug 980828).
 
     MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifier, JSMSG_NO_VARIABLE_NAME);
     RootedPropertyName name(context, bindingIdentifier(YieldIsKeyword));
+    if (!name)
+        return null();
     if (name == context->names().let) {
         error(JSMSG_LET_COMP_BINDING);
         return null();
     }
     TokenPos namePos = pos();
     Node lhs = newName(name);
     if (!lhs)
         return null();
--- a/js/src/jit-test/lib/wasm.js
+++ b/js/src/jit-test/lib/wasm.js
@@ -30,16 +30,19 @@ function wasmFailValidateText(str, patte
     assertErrorMessage(() => new WebAssembly.Module(binary), WebAssembly.CompileError, pattern);
 }
 
 function mismatchError(actual, expect) {
     var str = `type mismatch: expression has type ${actual} but expected ${expect}`;
     return RegExp(str);
 }
 
+const emptyStackError = /from empty stack/;
+const unusedValuesError = /unused values not explicitly dropped by end of block/;
+
 function jsify(wasmVal) {
     if (wasmVal === 'nan')
         return NaN;
     if (wasmVal === 'infinity')
         return Infinity;
     if (wasmVal === '-infinity')
         return Infinity;
     if (wasmVal === '-0')
@@ -174,9 +177,9 @@ function wasmGetScriptBreakpoints(wasmSc
     sourceText.split('\n').forEach(function (line, i) {
         var lineOffsets = wasmScript.getLineOffsets(i + 1);
         if (lineOffsets.length === 0)
             return;
         assertEq(lineOffsets.length, 1);
         result.push({str: line.trim(), line: i + 1, offset: lineOffsets[0]});
     });
     return result;
-}
\ No newline at end of file
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/cooperative-threading-interrupt.js
@@ -0,0 +1,16 @@
+if (helperThreadCount() === 0)
+  quit();
+setInterruptCallback(function() { print("MainThread Interrupt!"); cooperativeYield(); return true; });
+evalInCooperativeThread('\
+  setInterruptCallback(function() { print("Worker Interrupt!"); cooperativeYield(); return true; });\
+  for (var i = 0; i < 10; i++) {\
+     print("Worker: " + i);\
+     interruptIf(true);\
+  }\
+  print("Worker Done!");\
+');
+for (var i = 0; i < 10; i++) {
+    print("MainThread: " + i);
+    interruptIf(true);
+}
+print("MainThread Done!");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/cooperative-threading.js
@@ -0,0 +1,7 @@
+if (helperThreadCount() === 0)
+  quit();
+evalInCooperativeThread("var x = 3");
+evalInCooperativeThread("cooperativeYield()");
+cooperativeYield();
+evalInCooperativeThread("cooperativeYield(); gc();");
+gc();
--- a/js/src/jit-test/tests/wasm/basic.js
+++ b/js/src/jit-test/tests/wasm/basic.js
@@ -53,18 +53,18 @@ assertEq(o.a(), 2);
 assertEq(o.b(), 1);
 
 wasmFailValidateText('(module (func) (export "a" 0) (export "a" 0))', /duplicate export/);
 wasmFailValidateText('(module (func) (func) (export "a" 0) (export "a" 1))', /duplicate export/);
 
 // ----------------------------------------------------------------------------
 // signatures
 
-wasmFailValidateText('(module (func (result i32)))', mismatchError("void", "i32"));
-wasmFailValidateText('(module (func (result i32) (nop)))', mismatchError("void", "i32"));
+wasmFailValidateText('(module (func (result i32)))', emptyStackError);
+wasmFailValidateText('(module (func (result i32) (nop)))', emptyStackError);
 
 wasmValidateText('(module (func (nop)))');
 wasmValidateText('(module (func (result i32) (i32.const 42)))');
 wasmValidateText('(module (func (param i32)))');
 wasmValidateText('(module (func (param i32) (result i32) (i32.const 42)))');
 wasmValidateText('(module (func (result i32) (param i32) (i32.const 42)))');
 wasmValidateText('(module (func (param f32)))');
 wasmValidateText('(module (func (param f64)))');
@@ -191,17 +191,17 @@ wasmValidateText('(module (func (local i
 wasmValidateText('(module (func (local i32) (local f32)))');
 
 wasmFullPass('(module (func (result i32) (local i32) (get_local 0)) (export "run" 0))', 0);
 wasmFullPass('(module (func (result i32) (param i32) (local f32) (get_local 0)) (export "run" 0))', 0);
 wasmFullPass('(module (func (result f32) (param i32) (local f32) (get_local 1)) (export "run" 0))', 0);
 
 wasmFailValidateText('(module (func (set_local 0 (i32.const 0))))', /set_local index out of range/);
 wasmFailValidateText('(module (func (local f32) (set_local 0 (i32.const 0))))', mismatchError("i32", "f32"));
-wasmFailValidateText('(module (func (local f32) (set_local 0 (nop))))', /popping value from empty stack/);
+wasmFailValidateText('(module (func (local f32) (set_local 0 (nop))))', emptyStackError);
 wasmFailValidateText('(module (func (local i32) (local f32) (set_local 0 (get_local 1))))', mismatchError("f32", "i32"));
 wasmFailValidateText('(module (func (local i32) (local f32) (set_local 1 (get_local 0))))', mismatchError("i32", "f32"));
 
 wasmValidateText('(module (func (local i32) (set_local 0 (i32.const 0))))');
 wasmValidateText('(module (func (local i32) (local f32) (set_local 0 (get_local 0))))');
 wasmValidateText('(module (func (local i32) (local f32) (set_local 1 (get_local 1))))');
 
 wasmFullPass('(module (func (result i32) (local i32) (tee_local 0 (i32.const 42))) (export "run" 0))', 42);
@@ -212,51 +212,51 @@ wasmFullPass('(module (func (param $a i3
 
 wasmValidateText('(module (func (local i32) (local $a f32) (set_local 0 (i32.const 1)) (set_local $a (f32.const nan))))');
 
 // ----------------------------------------------------------------------------
 // blocks
 
 wasmFullPass('(module (func (block )) (export "run" 0))', undefined);
 
-wasmFailValidateText('(module (func (result i32) (block )))', mismatchError("void", "i32"));
-wasmFailValidateText('(module (func (result i32) (block (block ))))', mismatchError("void", "i32"));
-wasmFailValidateText('(module (func (local i32) (set_local 0 (block ))))', /popping value from empty stack/);
+wasmFailValidateText('(module (func (result i32) (block )))', emptyStackError);
+wasmFailValidateText('(module (func (result i32) (block (block ))))', emptyStackError);
+wasmFailValidateText('(module (func (local i32) (set_local 0 (block ))))', emptyStackError);
 
 wasmFullPass('(module (func (block (block ))) (export "run" 0))', undefined);
 wasmFullPass('(module (func (result i32) (block i32 (i32.const 42))) (export "run" 0))', 42);
 wasmFullPass('(module (func (result i32) (block i32 (block i32 (i32.const 42)))) (export "run" 0))', 42);
 wasmFailValidateText('(module (func (result f32) (block i32 (i32.const 0))))', mismatchError("i32", "f32"));
 
 wasmFullPass('(module (func (result i32) (block i32 (drop (i32.const 13)) (block i32 (i32.const 42)))) (export "run" 0))', 42);
 wasmFailValidateText('(module (func (result f32) (param f32) (block i32 (drop (get_local 0)) (i32.const 0))))', mismatchError("i32", "f32"));
 
 wasmFullPass('(module (func (result i32) (local i32) (set_local 0 (i32.const 42)) (get_local 0)) (export "run" 0))', 42);
 
 // ----------------------------------------------------------------------------
 // calls
 
-wasmFailValidateText('(module (func (nop)) (func (call 0 (i32.const 0))))', /unused values not explicitly dropped by end of block/);
+wasmFailValidateText('(module (func (nop)) (func (call 0 (i32.const 0))))', unusedValuesError);
 
-wasmFailValidateText('(module (func (param i32) (nop)) (func (call 0)))', /peeking at value from outside block/);
+wasmFailValidateText('(module (func (param i32) (nop)) (func (call 0)))', emptyStackError);
 wasmFailValidateText('(module (func (param f32) (nop)) (func (call 0 (i32.const 0))))', mismatchError("i32", "f32"));
 wasmFailValidateText('(module (func (nop)) (func (call 3)))', /callee index out of range/);
 
 wasmValidateText('(module (func (nop)) (func (call 0)))');
 wasmValidateText('(module (func (param i32) (nop)) (func (call 0 (i32.const 0))))');
 
 wasmFullPass('(module (func (result i32) (i32.const 42)) (func (result i32) (call 0)) (export "run" 1))', 42);
 assertThrowsInstanceOf(() => wasmEvalText('(module (func (call 0)) (export "" 0))').exports[""](), InternalError);
 assertThrowsInstanceOf(() => wasmEvalText('(module (func (call 1)) (func (call 0)) (export "" 0))').exports[""](), InternalError);
 
 wasmValidateText('(module (func (param i32 f32)) (func (call 0 (i32.const 0) (f32.const nan))))');
 wasmFailValidateText('(module (func (param i32 f32)) (func (call 0 (i32.const 0) (i32.const 0))))', mismatchError("i32", "f32"));
 
-wasmFailValidateText('(module (import "a" "") (func (call 0 (i32.const 0))))', /unused values not explicitly dropped by end of block/);
-wasmFailValidateText('(module (import "a" "" (param i32)) (func (call 0)))', /peeking at value from outside block/);
+wasmFailValidateText('(module (import "a" "") (func (call 0 (i32.const 0))))', unusedValuesError);
+wasmFailValidateText('(module (import "a" "" (param i32)) (func (call 0)))', emptyStackError);
 wasmFailValidateText('(module (import "a" "" (param f32)) (func (call 0 (i32.const 0))))', mismatchError("i32", "f32"));
 
 assertErrorMessage(() => wasmEvalText('(module (import "a" "") (func (call 1)))'), TypeError, noImportObj);
 wasmEvalText('(module (import "" "a") (func (call 0)))', {"":{a:()=>{}}});
 wasmEvalText('(module (import "" "a" (param i32)) (func (call 0 (i32.const 0))))', {"":{a:()=>{}}});
 
 function checkF32CallImport(v) {
     wasmFullPass('(module (import "" "a" (result f32)) (func (result f32) (call 0)) (export "run" 1))',
@@ -457,21 +457,22 @@ wasmValidateText('(module (func (call $f
 wasmValidateText('(module (import $bar "" "a") (func (call $bar)) (func $foo (nop)))');
 
 // ----------------------------------------------------------------------------
 // select
 
 wasmFailValidateText('(module (func (select (i32.const 0) (i32.const 0) (f32.const 0))))', mismatchError("f32", "i32"));
 
 wasmFailValidateText('(module (func (select (i32.const 0) (f32.const 0) (i32.const 0))) (export "" 0))', /select operand types must match/);
-wasmFailValidateText('(module (func (select (block ) (i32.const 0) (i32.const 0))) (export "" 0))', /popping value from empty stack/);
-assertEq(wasmEvalText('(module (func (select (return) (i32.const 0) (i32.const 0))) (export "" 0))').exports[""](), undefined);
-assertEq(wasmEvalText('(module (func (i32.add (i32.const 0) (select (return) (i32.const 0) (i32.const 0)))) (export "" 0))').exports[""](), undefined);
+wasmFailValidateText('(module (func (select (block ) (i32.const 0) (i32.const 0))) (export "" 0))', emptyStackError);
+wasmFailValidateText('(module (func (select (return) (i32.const 0) (i32.const 0))) (export "" 0))', unusedValuesError);
+assertEq(wasmEvalText('(module (func (drop (select (return) (i32.const 0) (i32.const 0)))) (export "" 0))').exports[""](), undefined);
+assertEq(wasmEvalText('(module (func (result i32) (i32.add (i32.const 0) (select (return (i32.const 42)) (i32.const 0) (i32.const 0)))) (export "" 0))').exports[""](), 42);
 wasmFailValidateText('(module (func (select (if i32 (i32.const 1) (i32.const 0) (f32.const 0)) (i32.const 0) (i32.const 0))) (export "" 0))', mismatchError("f32", "i32"));
-wasmFailValidateText('(module (func) (func (select (call 0) (call 0) (i32.const 0))) (export "" 0))', /popping value from empty stack/);
+wasmFailValidateText('(module (func) (func (select (call 0) (call 0) (i32.const 0))) (export "" 0))', emptyStackError);
 
 (function testSideEffects() {
 
 var numT = 0;
 var numF = 0;
 
 var imports = {"": {
     ifTrue: () => 1 + numT++,
--- a/js/src/jit-test/tests/wasm/control-flow.js
+++ b/js/src/jit-test/tests/wasm/control-flow.js
@@ -152,64 +152,64 @@ wasmFullPass(`(module
         )
     )
     (export "run" 1)
 )`, 42, imports);
 assertEq(counter, 0);
 
 // "if" doesn't return an expression value
 wasmFailValidateText('(module (func (result i32) (if i32 (i32.const 42) (i32.const 0))))', /if without else with a result value/);
-wasmFailValidateText('(module (func (result i32) (if i32 (i32.const 42) (drop (i32.const 0)))))', mismatchError("void", "i32"));
+wasmFailValidateText('(module (func (result i32) (if i32 (i32.const 42) (drop (i32.const 0)))))', emptyStackError);
 wasmFailValidateText('(module (func (result i32) (if i32 (i32.const 1) (i32.const 0) (if i32 (i32.const 1) (i32.const 1)))))', /if without else with a result value/);
-wasmFailValidateText('(module (func (result i32) (if i32 (i32.const 1) (drop (i32.const 0)) (if (i32.const 1) (drop (i32.const 1))))))', mismatchError("void", "i32"));
+wasmFailValidateText('(module (func (result i32) (if i32 (i32.const 1) (drop (i32.const 0)) (if (i32.const 1) (drop (i32.const 1))))))', emptyStackError);
 wasmFailValidateText('(module (func (if i32 (i32.const 1) (i32.const 0) (if i32 (i32.const 1) (i32.const 1)))))', /if without else with a result value/);
-wasmFailValidateText('(module (func (if i32 (i32.const 1) (i32.const 0) (if (i32.const 1) (drop (i32.const 1))))))', mismatchError("void", "i32"));
+wasmFailValidateText('(module (func (if i32 (i32.const 1) (i32.const 0) (if (i32.const 1) (drop (i32.const 1))))))', emptyStackError);
 wasmFailValidateText('(module (func (if (i32.const 1) (drop (i32.const 0)) (if i32 (i32.const 1) (i32.const 1)))))', /if without else with a result value/);
 wasmEvalText('(module (func (if (i32.const 1) (drop (i32.const 0)) (if (i32.const 1) (drop (i32.const 1))))))');
 
 // ----------------------------------------------------------------------------
 // return
 
 wasmFullPass('(module (func (return)) (export "run" 0))', undefined);
 wasmFullPass('(module (func (result i32) (return (i32.const 1))) (export "run" 0))', 1);
-wasmFullPass('(module (func (if (return) (i32.const 0))) (export "run" 0))', undefined);
-wasmFailValidateText('(module (func (result i32) (return)) (export "" 0))', /popping value from empty stack/);
+wasmFailValidateText('(module (func (if (return) (i32.const 0))) (export "run" 0))', unusedValuesError);
+wasmFailValidateText('(module (func (result i32) (return)) (export "" 0))', emptyStackError);
 wasmFullPass('(module (func (return (i32.const 1))) (export "run" 0))', undefined);
 wasmFailValidateText('(module (func (result f32) (return (i32.const 1))) (export "" 0))', mismatchError("i32", "f32"));
-wasmFailValidateText('(module (func (result i32) (return)) (export "" 0))', /popping value from empty stack/);
+wasmFailValidateText('(module (func (result i32) (return)) (export "" 0))', emptyStackError);
 
 // ----------------------------------------------------------------------------
 // br / br_if
 
-wasmFailValidateText('(module (func (result i32) (block (br 0))) (export "" 0))', mismatchError("void", "i32"));
-wasmFailValidateText('(module (func (result i32) (br 0)) (export "" 0))', /popping value from empty stack/);
-wasmFailValidateText('(module (func (result i32) (block (br_if 0 (i32.const 0)))) (export "" 0))', mismatchError("void", "i32"));
+wasmFailValidateText('(module (func (result i32) (block (br 0))) (export "" 0))', emptyStackError);
+wasmFailValidateText('(module (func (result i32) (br 0)) (export "" 0))', emptyStackError);
+wasmFailValidateText('(module (func (result i32) (block (br_if 0 (i32.const 0)))) (export "" 0))', emptyStackError);
 
 const DEPTH_OUT_OF_BOUNDS = /branch depth exceeds current nesting level/;
 
 wasmFailValidateText('(module (func (br 1)))', DEPTH_OUT_OF_BOUNDS);
 wasmFailValidateText('(module (func (block (br 2))))', DEPTH_OUT_OF_BOUNDS);
 wasmFailValidateText('(module (func (loop (br 2))))', DEPTH_OUT_OF_BOUNDS);
 wasmFailValidateText('(module (func (if (i32.const 0) (br 2))))', DEPTH_OUT_OF_BOUNDS);
 wasmFailValidateText('(module (func (if (i32.const 0) (br 1) (br 2))))', DEPTH_OUT_OF_BOUNDS);
 wasmFailValidateText('(module (func (if (i32.const 0) (br 2) (br 1))))', DEPTH_OUT_OF_BOUNDS);
 
 wasmFailValidateText('(module (func (br_if 1 (i32.const 0))))', DEPTH_OUT_OF_BOUNDS);
 wasmFailValidateText('(module (func (block (br_if 2 (i32.const 0)))))', DEPTH_OUT_OF_BOUNDS);
 wasmFailValidateText('(module (func (loop (br_if 2 (i32.const 0)))))', DEPTH_OUT_OF_BOUNDS);
 
 wasmFailValidateText(`(module (func (result i32)
-  (block
-    (if
-      (br 0)
-      (i32.const 0)
-      (i32.const 2)
-    )
-  )
-) (export "" 0))`, mismatchError("void", "i32"));
+  block
+    br 0
+    if
+      i32.const 0
+      i32.const 2
+    end
+  end
+) (export "" 0))`, unusedValuesError);
 
 wasmFullPass(`(module (func (block $out (br_if $out (br 0)))) (export "run" 0))`, undefined);
 
 wasmFullPass('(module (func (br 0)) (export "run" 0))', undefined);
 wasmFullPass('(module (func (block (br 0))) (export "run" 0))', undefined);
 wasmFullPass('(module (func (block $l (br $l))) (export "run" 0))', undefined);
 
 wasmFullPass('(module (func (block (block (br 1)))) (export "run" 0))', undefined);
@@ -268,16 +268,17 @@ assertEq(notcalled, false);
 assertEq(called, true);
 
 wasmFullPass(`(module (func
   (block
     (i32.add
       (i32.const 0)
       (return (br 0))
     )
+    drop
   )
   (return)
 ) (export "run" 0))`, undefined);
 
 wasmFullPass(`(module (func (result i32)
   (block
     (if
       (i32.const 1)
@@ -303,29 +304,29 @@ var isNonZero = wasmEvalText(`(module (f
 ) (export "" 0))`).exports[""];
 
 assertEq(isNonZero(0), 0);
 assertEq(isNonZero(1), 1);
 assertEq(isNonZero(-1), 1);
 
 // branches with values
 // br/br_if and block
-wasmFailValidateText('(module (func (result i32) (br 0)))', /popping value from empty stack/);
+wasmFailValidateText('(module (func (result i32) (br 0)))', emptyStackError);
 wasmFailValidateText('(module (func (result i32) (br 0 (f32.const 42))))', mismatchError("f32", "i32"));
-wasmFailValidateText('(module (func (result i32) (block (br 0))))', mismatchError("void", "i32"));
+wasmFailValidateText('(module (func (result i32) (block (br 0))))', emptyStackError);
 wasmFailValidateText('(module (func (result i32) (block f32 (br 0 (f32.const 42)))))', mismatchError("f32", "i32"));
 
 wasmFailValidateText(`(module (func (result i32) (param i32) (block (if i32 (get_local 0) (br 0 (i32.const 42))))) (export "" 0))`, /if without else with a result value/);
 wasmFailValidateText(`(module (func (result i32) (param i32) (block i32 (if (get_local 0) (drop (i32.const 42))) (br 0 (f32.const 42)))) (export "" 0))`, mismatchError("f32", "i32"));
 
 wasmFullPass('(module (func (result i32) (br 0 (i32.const 42)) (i32.const 13)) (export "run" 0))', 42);
 wasmFullPass('(module (func (result i32) (block i32 (br 0 (i32.const 42)) (i32.const 13))) (export "run" 0))', 42);
 
-wasmFailValidateText('(module (func) (func (block i32 (br 0 (call 0)) (i32.const 13))) (export "" 0))', /popping value from empty stack/);
-wasmFailValidateText('(module (func) (func (block i32 (br_if 0 (call 0) (i32.const 1)) (i32.const 13))) (export "" 0))', /popping value from empty stack/);
+wasmFailValidateText('(module (func) (func (block i32 (br 0 (call 0)) (i32.const 13))) (export "" 0))', emptyStackError);
+wasmFailValidateText('(module (func) (func (block i32 (br_if 0 (call 0) (i32.const 1)) (i32.const 13))) (export "" 0))', emptyStackError);
 
 var f = wasmEvalText(`(module (func (result i32) (param i32) (block i32 (if (get_local 0) (drop (i32.const 42))) (i32.const 43))) (export "" 0))`).exports[""];
 assertEq(f(0), 43);
 assertEq(f(1), 43);
 
 wasmFailValidateText(`(module (func (result i32) (param i32) (block i32 (if i32 (get_local 0) (br 0 (i32.const 42))) (i32.const 43))) (export "" 0))`, /if without else with a result value/);
 
 var f = wasmEvalText(`(module (func (result i32) (param i32) (block i32 (if (get_local 0) (br 1 (i32.const 42))) (i32.const 43))) (export "" 0))`).exports[""];
@@ -409,17 +410,17 @@ var f = wasmEvalText(`(module
 (export "" 2))`, imports).exports[""];
 assertEq(f(0), 42);
 assertEq(called, -1);
 assertEq(f(1), 42);
 assertEq(called, 0);
 
 // br/br_if and loop
 wasmFullPass(`(module (func (param i32) (result i32) (loop $out $in i32 (br $out (get_local 0)))) (export "run" 0))`, 1, {}, 1);
-wasmFullPass(`(module (func (param i32) (result i32) (loop $in (br 1 (get_local 0)))) (export "run" 0))`, 1, {}, 1);
+wasmFullPass(`(module (func (param i32) (result i32) (loop $in i32 (br 1 (get_local 0)))) (export "run" 0))`, 1, {}, 1);
 wasmFullPass(`(module (func (param i32) (result i32) (block $out i32 (loop $in i32 (br $out (get_local 0))))) (export "run" 0))`, 1, {}, 1);
 
 wasmFailValidateText(`(module (func (param i32) (result i32)
   (loop $out $in
    (if (get_local 0) (br $in (i32.const 1)))
    (if (get_local 0) (br $in (f32.const 2)))
    (if (get_local 0) (br $in (f64.const 3)))
    (if (get_local 0) (br $in))
@@ -427,31 +428,34 @@ wasmFailValidateText(`(module (func (par
   )
 ) (export "" 0))`, /unused values not explicitly dropped by end of block/);
 
 wasmFullPass(`(module
  (func
   (result i32)
   (local i32)
   (block $out i32
-  (loop $in
-   (set_local 0 (i32.add (get_local 0) (i32.const 1)))
-   (if (i32.ge_s (get_local 0) (i32.const 7)) (br $out (get_local 0)))
-   (br $in)
-  )
+    (loop $in i32
+     (set_local 0 (i32.add (get_local 0) (i32.const 1)))
+     (if
+        (i32.ge_s (get_local 0) (i32.const 7))
+        (br $out (get_local 0))
+     )
+     (br $in)
+    )
   )
  )
 (export "run" 0))`, 7);
 
 wasmFullPass(`(module
  (func
   (result i32)
   (local i32)
   (block $out i32
-   (loop $in
+   (loop $in i32
     (set_local 0 (i32.add (get_local 0) (i32.const 1)))
     (br_if $out (get_local 0) (i32.ge_s (get_local 0) (i32.const 7)))
     (br $in)
    )
   )
  )
 (export "run" 0))`, 7);
 
@@ -682,17 +686,17 @@ var f = wasmEvalText(`(module (func (res
 assertEq(f(-2), -1);
 assertEq(f(-1), -1);
 assertEq(f(0), 0);
 assertEq(f(1), 0);
 assertEq(f(2), 2);
 assertEq(f(3), -1);
 
 // br_table with values
-wasmFailValidateText('(module (func (result i32) (block (br_table 0 (i32.const 0)))))', mismatchError("void", "i32"));
+wasmFailValidateText('(module (func (result i32) (block i32 (br_table 0 (i32.const 0)))))', emptyStackError);
 wasmFailValidateText('(module (func (result i32) (block i32 (br_table 0 (f32.const 0) (i32.const 0)))))', mismatchError("f32", "i32"));
 
 wasmFailValidateText(`(module
  (func
   (result i32)
   (block $outer f32
    (block $inner f32
     (br_table $outer $inner (f32.const 13.37) (i32.const 1))
@@ -725,12 +729,12 @@ assertEq(f(2), 9);
 assertEq(f(3), 11);
 assertEq(f(4), 13);
 
 // ----------------------------------------------------------------------------
 // unreachable
 
 const UNREACHABLE = /unreachable/;
 assertErrorMessage(wasmEvalText(`(module (func (unreachable)) (export "" 0))`).exports[""], RuntimeError, UNREACHABLE);
-assertErrorMessage(wasmEvalText(`(module (func (if (unreachable) (i32.const 0))) (export "" 0))`).exports[""], RuntimeError, UNREACHABLE);
+assertErrorMessage(wasmEvalText(`(module (func (if (unreachable) (nop))) (export "" 0))`).exports[""], RuntimeError, UNREACHABLE);
 assertErrorMessage(wasmEvalText(`(module (func (block (br_if 0 (unreachable)))) (export "" 0))`).exports[""], RuntimeError, UNREACHABLE);
 assertErrorMessage(wasmEvalText(`(module (func (block (br_table 0 (unreachable)))) (export "" 0))`).exports[""], RuntimeError, UNREACHABLE);
 assertErrorMessage(wasmEvalText(`(module (func (result i32) (i32.add (i32.const 0) (unreachable))) (export "" 0))`).exports[""], RuntimeError, UNREACHABLE);
--- a/js/src/jit-test/tests/wasm/full-cycle.js
+++ b/js/src/jit-test/tests/wasm/full-cycle.js
@@ -11,17 +11,17 @@ wasmFullPass(`(module
         i32.const 1
         i32.const 42
         i32.add
         return
         unreachable
         i32.const 0
         call 3
         i32.const 42
-        f32.add
+        i32.add
     )
     (func) (func) (func)
 (export "run" 0))`, 43);
 
 // Global section.
 wasmFullPass(`(module
  (import $imported "globals" "x" (global i32))
  (global $mut_local (mut i32) (i32.const 0))
--- a/js/src/jit-test/tests/wasm/regress/misc-control-flow.js
+++ b/js/src/jit-test/tests/wasm/regress/misc-control-flow.js
@@ -11,17 +11,17 @@ wasmFailValidateText(`(module
      (loop (if (i32.const 0) (br 0)) (get_local 0)))
    (export "" 0)
 )`, /unused values not explicitly dropped by end of block/);
 
 wasmFailValidateText(`(module
    (func (result i32) (param i32)
      (loop (if (i32.const 0) (br 0)) (drop (get_local 0))))
    (export "" 0)
-)`, mismatchError("void", "i32"));
+)`, emptyStackError);
 
 assertEq(wasmEvalText(`(module
    (func (result i32) (param i32)
      (loop (if (i32.const 0) (br 0))) (get_local 0))
    (export "" 0)
 )`).exports[""](42), 42);
 
 wasmEvalText(`(module (func $func$0
@@ -108,29 +108,30 @@ wasmEvalText(`
         two(x, y) {
             assertEq(x, 43);
             assertEq(y, 10);
         }
     }
 }).exports.foo();
 
 assertEq(wasmEvalText(`(module (func
- (return)
+ return
  (select
-  (loop (i32.const 1))
-  (loop (i32.const 2))
+  (loop i32 (i32.const 1))
+  (loop i32 (i32.const 2))
   (i32.const 3)
  )
+ drop
 ) (export "" 0))`).exports[""](), undefined);
 
 wasmEvalText(`(module (func (result i32)
  (return (i32.const 0))
  (select
-  (loop (i32.const 1))
-  (loop (i32.const 2))
+  (loop i32 (i32.const 1))
+  (loop i32 (i32.const 2))
   (i32.const 3)
  )
 ))`);
 
 wasmEvalText(`(module (func
  (block $return
   (block $beforeReturn
    (loop $out $in
@@ -198,27 +199,27 @@ wasmEvalText(`
 wasmFailValidateText(`
 (module
     (func (result i32)
       (loop
         (i32.const 0)
         (br_table 1 0 (i32.const 15))
       )
     )
-)`, mismatchError("i32", "void"));
+)`, /br_table targets must all have the same value type/);
 
 wasmFailValidateText(`
 (module
   (func (result i32)
     (loop i32
       (i32.const 0)
       (br_table 1 0 (i32.const 15))
     )
   )
-)`, mismatchError("i32", "void"));
+)`, /br_table targets must all have the same value type/);
 
 wasmValidateText(`
 (module
     (func
         (loop
           (i32.const 0)
           (br_table 1 0 (i32.const 15))
         )
--- a/js/src/jit-test/tests/wasm/regress/movable-traps.js
+++ b/js/src/jit-test/tests/wasm/regress/movable-traps.js
@@ -20,17 +20,17 @@ let bodies = [
     i32.trunc_s/f32
     `
 ];
 
 for (let body of bodies) {
     wasmFullPass(`
     (module
         (func $f (param $x i32) (result i32)
-            loop $top
+            loop $top i32
                 get_local $x
                 if
                     get_local $x
                     br 2
                 end
                 ${body}
                 br $top
             end
--- a/js/src/jit-test/tests/wasm/regress/select-any.js
+++ b/js/src/jit-test/tests/wasm/regress/select-any.js
@@ -1,31 +1,43 @@
 load(libdir + "wasm.js");
 
 // Bug 1280921
 
 var m1 = wasmEvalText(
 `(module
   (type $type0 (func))
   (func $func0
-   (select (unreachable) (return (nop)) (loop (i32.const 1))))
+   (select
+     (unreachable)
+     (return (nop))
+     (loop i32 (i32.const 1))
+   )
+   drop
+  )
   (export "" 0))`).exports[""];
 
 try {
     m1();
 } catch (e) {
     if (!(e instanceof Error && e.message.match(/unreachable executed/)))
 	throw e;
 }
 
 var m2 = wasmEvalText(
 `(module
   (type $type0 (func))
   (func $func0
-   (select (i32.const 26) (unreachable) (i32.const 3)))
+   (select
+    (i32.const 26)
+    (unreachable)
+    (i32.const 3)
+   )
+   drop
+  )
   (export "" 0))`).exports[""];
 
 try {
     m2();
 } catch (e) {
     if (!(e instanceof Error && e.message.match(/unreachable executed/)))
 	throw e;
 }
--- a/js/src/jit-test/tests/wasm/spec/br_table.wast
+++ b/js/src/jit-test/tests/wasm/spec/br_table.wast
@@ -1396,16 +1396,27 @@
   "type mismatch"
 )
 (assert_invalid
   (module (func $type-arg-num-vs-num (result i32)
     (block i32 (br_table 0 0 0 (i64.const 1) (i32.const 1)) (i32.const 1))
   ))
   "type mismatch"
 )
+(assert_invalid
+  (module (func $type-arg-num-vs-arg-num
+    (block
+      (block f32
+        (br_table 0 1 (f32.const 0) (i64.const 0))
+      )
+      (drop)
+    )
+  ))
+  "type mismatch"
+)
 
 (assert_invalid
   (module (func $type-index-void-vs-i32
     (block (br_table 0 0 0 (nop)))
   ))
   "type mismatch"
 )
 (assert_invalid
--- a/js/src/jit-test/tests/wasm/spec/call_indirect.wast
+++ b/js/src/jit-test/tests/wasm/spec/call_indirect.wast
@@ -356,13 +356,45 @@
 (assert_invalid
   (module
     (table 0 anyfunc)
     (func $large-type (call_indirect 1012321300 (i32.const 0)))
   )
   "unknown type"
 )
 
-;; invalid table
+
+;; Unbound function in table
+
 (assert_invalid 
   (module (table anyfunc (elem 0 0)))
   "unknown function 0"
 )
+
+
+;; Invalid bounds for elements
+
+(assert_unlinkable
+  (module
+    (table 10 anyfunc)
+    (elem (i32.const 10) $f)
+    (func $f)
+  )
+  "elements segment does not fit"
+)
+
+(assert_unlinkable
+  (module
+    (table 10 anyfunc)
+    (elem (i32.const -1) $f)
+    (func $f)
+  )
+  "elements segment does not fit"
+)
+
+(assert_unlinkable
+  (module
+    (table 10 anyfunc)
+    (elem (i32.const -10) $f)
+    (func $f)
+  )
+  "elements segment does not fit"
+)
--- a/js/src/jit-test/tests/wasm/spec/linking.wast
+++ b/js/src/jit-test/tests/wasm/spec/linking.wast
@@ -151,24 +151,33 @@
 (assert_trap (invoke $Mt "call" (i32.const 0)) "uninitialized")
 (assert_trap (invoke $Nt "Mt.call" (i32.const 0)) "uninitialized")
 (assert_return (invoke $Nt "call" (i32.const 0)) (i32.const 5))
 (assert_trap (invoke $Nt "call Mt.call" (i32.const 0)) "uninitialized")
 (assert_trap (invoke $Ot "call" (i32.const 0)) "uninitialized")
 
 (assert_trap (invoke $Ot "call" (i32.const 20)) "undefined")
 
-(module $Pt
+(module
   (table (import "Mt" "tab") 0 anyfunc)
   (elem (i32.const 9) $f)
   (func $f)
 )
 
 (assert_unlinkable
   (module
+    (table (import "Mt" "tab") 0 anyfunc)
+    (elem (i32.const 10) $f)
+    (func $f)
+  )
+  "elements segment does not fit"
+)
+
+(assert_unlinkable
+  (module
     (table (import "Mt" "tab") 10 anyfunc)
     (memory (import "Mt" "mem") 1)  ;; does not exist
     (func $f (result i32) (i32.const 0))
     (elem (i32.const 7) $f)
     (elem (i32.const 9) $f)
   )
   "unknown import"
 )
@@ -235,37 +244,45 @@
   )
 )
 
 (assert_return (invoke $Mm "load" (i32.const 12)) (i32.const 0xa7))
 (assert_return (invoke $Nm "Mm.load" (i32.const 12)) (i32.const 0xa7))
 (assert_return (invoke $Nm "load" (i32.const 12)) (i32.const 0xf2))
 (assert_return (invoke $Om "load" (i32.const 12)) (i32.const 0xa7))
 
-(module $Pm
+(module
   (memory (import "Mm" "mem") 0)
-  (data (i32.const 1000) "abc")
+  (data (i32.const 0xffff) "a")
 )
 
-(module $Qm
+(assert_unlinkable
+  (module
+    (memory (import "Mm" "mem") 0)
+    (data (i32.const 0x10000) "a")
+  )
+  "data segment does not fit"
+)
+
+(module $Pm
   (memory (import "Mm" "mem") 1 8)
 
   (func (export "grow") (param $a i32) (result i32)
     (grow_memory (get_local 0))
   )
 )
 
-(assert_return (invoke $Qm "grow" (i32.const 0)) (i32.const 1))
-(assert_return (invoke $Qm "grow" (i32.const 2)) (i32.const 1))
-(assert_return (invoke $Qm "grow" (i32.const 0)) (i32.const 3))
-(assert_return (invoke $Qm "grow" (i32.const 1)) (i32.const 3))
-(assert_return (invoke $Qm "grow" (i32.const 1)) (i32.const 4))
-(assert_return (invoke $Qm "grow" (i32.const 0)) (i32.const 5))
-(assert_return (invoke $Qm "grow" (i32.const 1)) (i32.const -1))
-(assert_return (invoke $Qm "grow" (i32.const 0)) (i32.const 5))
+(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 1))
+(assert_return (invoke $Pm "grow" (i32.const 2)) (i32.const 1))
+(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 3))
+(assert_return (invoke $Pm "grow" (i32.const 1)) (i32.const 3))
+(assert_return (invoke $Pm "grow" (i32.const 1)) (i32.const 4))
+(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 5))
+(assert_return (invoke $Pm "grow" (i32.const 1)) (i32.const -1))
+(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 5))
 
 (assert_unlinkable
   (module
     (func $host (import "spectest" "print"))
     (memory (import "Mm" "mem") 1)
     (table (import "Mm" "tab") 0 anyfunc)  ;; does not exist
     (data (i32.const 0) "abc")
   )
--- a/js/src/jit-test/tests/wasm/spec/memory.wast
+++ b/js/src/jit-test/tests/wasm/spec/memory.wast
@@ -78,34 +78,49 @@
   (module (memory 0 0) (data (i32.const 0) "a"))
   "data segment does not fit"
 )
 (assert_unlinkable
   (module (memory 0 1) (data (i32.const 0) "a"))
   "data segment does not fit"
 )
 (assert_unlinkable
+  (module (memory 1 2) (data (i32.const -1) "a"))
+  "data segment does not fit"
+)
+(assert_unlinkable
+  (module (memory 1 2) (data (i32.const -1000) "a"))
+  "data segment does not fit"
+)
+(assert_unlinkable
   (module (memory 1 2) (data (i32.const 0) "a") (data (i32.const 98304) "b"))
   "data segment does not fit"
 )
 (assert_unlinkable
   (module (memory 0 0) (data (i32.const 1) ""))
   "data segment does not fit"
 )
 (assert_unlinkable
   (module (memory 1) (data (i32.const 0x12000) ""))
   "data segment does not fit"
 )
+(assert_unlinkable
+  (module (memory 1 2) (data (i32.const -1) ""))
+  "data segment does not fit"
+)
 ;; This seems to cause a time-out on Travis.
 (;assert_unlinkable
   (module (memory 0x10000) (data (i32.const 0xffffffff) "ab"))
   ""  ;; either out of memory or segment does not fit
 ;)
 (assert_unlinkable
-  (module (global (import "spectest" "global") i32) (memory 0) (data (get_global 0) "a"))
+  (module
+    (global (import "spectest" "global") i32)
+    (memory 0) (data (get_global 0) "a")
+  )
   "data segment does not fit"
 )
 
 (module (memory 0 0) (data (i32.const 0) ""))
 (module (memory 1 1) (data (i32.const 0x10000) ""))
 (module (memory 1 2) (data (i32.const 0) "abc") (data (i32.const 0) "def"))
 (module (memory 1 2) (data (i32.const 3) "ab") (data (i32.const 0) "de"))
 (module
deleted file mode 100644
--- a/js/src/jit-test/tests/wasm/spec/soft-fail.wast
+++ /dev/null
@@ -1,577 +0,0 @@
-;; Test soft failures
-;; These are invalid Wasm, but the failure is in dead code, which
-;; implementations are not required to validate. If they do, they shall
-;; diagnose the correct error.
-
-(assert_soft_invalid
-  (module (func $type-num-vs-num
-    (unreachable) (drop (i64.eqz (i32.const 0))))
-  )
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-poly-num-vs-num (result i32)
-    (unreachable) (i64.const 0) (i32.const 0) (select)
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-poly-transitive-num-vs-num (result i32)
-    (unreachable)
-    (i64.const 0) (i32.const 0) (select)
-    (i32.const 0) (i32.const 0) (select)
-  ))
-  "type mismatch"
-)
-
-(assert_soft_invalid
-  (module (func $type-unconsumed-const (unreachable) (i32.const 0)))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-unconsumed-result (unreachable) (i32.eqz)))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-unconsumed-result2
-    (unreachable) (i32.const 0) (i32.add)
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-unconsumed-poly0 (unreachable) (select)))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-unconsumed-poly1 (unreachable) (i32.const 0) (select)))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-unconsumed-poly2
-    (unreachable) (i32.const 0) (i32.const 0) (select)
-  ))
-  "type mismatch"
-)
-
-(assert_soft_invalid
-  (module (func $type-unary-num-vs-void-after-break
-    (block (br 0) (block (drop (i32.eqz (nop)))))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-unary-num-vs-num-after-break
-    (block (br 0) (drop (i32.eqz (f32.const 1))))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-binary-num-vs-void-after-break
-    (block (br 0) (block (drop (f32.eq (i32.const 1)))))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-binary-num-vs-num-after-break
-    (block (br 0) (drop (f32.eq (i32.const 1) (f32.const 0))))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-block-value-num-vs-void-after-break
-    (block (br 0) (i32.const 1))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-block-value-num-vs-num-after-break (result i32)
-    (block i32 (i32.const 1) (br 0) (f32.const 0))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-loop-value-num-vs-void-after-break
-    (block (loop (br 1) (i32.const 1)))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-loop-value-num-vs-num-after-break (result i32)
-    (loop i32 (br 1 (i32.const 1)) (f32.const 0))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-func-value-num-vs-void-after-break
-    (br 0) (i32.const 1)
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-func-value-num-vs-num-after-break (result i32)
-    (br 0 (i32.const 1)) (f32.const 0)
-  ))
-  "type mismatch"
-)
-
-(assert_soft_invalid
-  (module (func $type-unary-num-vs-void-after-return
-    (return) (block (drop (i32.eqz (nop))))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-unary-num-vs-num-after-return
-    (return) (drop (i32.eqz (f32.const 1)))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-binary-num-vs-void-after-return
-    (return) (block (drop (f32.eq (i32.const 1))))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-binary-num-vs-num-after-return
-    (return) (drop (f32.eq (i32.const 1) (f32.const 0)))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-block-value-num-vs-void-after-return
-    (block (return) (i32.const 1))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-block-value-num-vs-num-after-return (result i32)
-    (block i32 (i32.const 1) (return (i32.const 0)) (f32.const 0))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-loop-value-num-vs-void-after-return
-    (block (loop (return) (i32.const 1)))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-loop-value-num-vs-num-after-return (result i32)
-    (loop i32 (return (i32.const 1)) (f32.const 0))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-func-value-num-vs-void-after-return
-    (return) (i32.const 1)
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-func-value-num-vs-num-after-return (result i32)
-    (return (i32.const 1)) (f32.const 0)
-  ))
-  "type mismatch"
-)
-
-(assert_soft_invalid
-  (module (func $type-unary-num-vs-void-after-unreachable
-    (unreachable) (block (drop (i32.eqz (nop))))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-unary-num-vs-num-after-unreachable
-    (unreachable) (drop (i32.eqz (f32.const 1)))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-binary-num-vs-void-after-unreachable
-    (unreachable) (block (drop (f32.eq (i32.const 1))))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-binary-num-vs-num-after-unreachable
-    (unreachable) (drop (f32.eq (i32.const 1) (f32.const 0)))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-block-value-num-vs-void-after-unreachable
-    (block (unreachable) (i32.const 1))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-block-value-num-vs-num-after-unreachable (result i32)
-    (block i32 (i32.const 1) (unreachable) (f32.const 0))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-loop-value-num-vs-void-after-unreachable
-    (block (loop (unreachable) (i32.const 1)))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-loop-value-num-vs-num-after-unreachable (result i32)
-    (loop i32 (unreachable) (f32.const 0))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-func-value-num-vs-void-after-unreachable
-    (unreachable) (i32.const 1)
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-func-value-num-vs-num-after-unreachable (result i32)
-    (unreachable) (f32.const 0)
-  ))
-  "type mismatch"
-)
-
-(assert_soft_invalid
-  (module (func $type-unary-num-vs-void-after-nested-unreachable
-    (block (unreachable)) (block (drop (i32.eqz (nop))))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-unary-num-vs-num-after-nested-unreachable
-    (block (unreachable)) (drop (i32.eqz (f32.const 1)))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-binary-num-vs-void-after-nested-unreachable
-    (block (unreachable)) (block (drop (f32.eq (i32.const 1))))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-binary-num-vs-num-after-nested-unreachable
-    (block (unreachable)) (drop (f32.eq (i32.const 1) (f32.const 0)))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-block-value-num-vs-void-after-nested-unreachable
-    (block (block (unreachable)) (i32.const 1))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-block-value-num-vs-num-after-nested-unreachable
-    (result i32)
-    (block i32 (i32.const 1) (block (unreachable)) (f32.const 0))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-loop-value-num-vs-void-after-nested-unreachable
-    (block (loop (block (unreachable)) (i32.const 1)))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-loop-value-num-vs-num-after-nested-unreachable
-    (result i32)
-    (loop i32 (block (unreachable)) (f32.const 0))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-func-value-num-vs-void-after-nested-unreachable
-    (block (unreachable)) (i32.const 1)
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-func-value-num-vs-num-after-nested-unreachable
-    (result i32)
-    (block (unreachable)) (f32.const 0)
-  ))
-  "type mismatch"
-)
-
-(assert_soft_invalid
-  (module (func $type-unary-num-vs-void-after-infinite-loop
-    (loop (br 0)) (block (drop (i32.eqz (nop))))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-unary-num-vs-num-after-infinite-loop
-    (loop (br 0)) (drop (i32.eqz (f32.const 1)))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-binary-num-vs-void-after-infinite-loop
-    (loop (br 0)) (block (drop (f32.eq (i32.const 1))))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-binary-num-vs-num-after-infinite-loop
-    (loop (br 0)) (drop (f32.eq (i32.const 1) (f32.const 0)))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-block-value-num-vs-void-after-infinite-loop
-    (block (loop (br 0)) (i32.const 1))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-block-value-num-vs-num-after-infinite-loop (result i32)
-    (block i32 (i32.const 1) (loop (br 0)) (f32.const 0))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-loop-value-num-vs-void-after-infinite-loop
-    (block (loop (loop (br 0)) (i32.const 1)))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-loop-value-num-vs-num-after-infinite-loop (result i32)
-    (loop i32 (loop (br 0)) (f32.const 0))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-func-value-num-vs-void-after-infinite-loop
-    (loop (br 0)) (i32.const 1)
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-func-value-num-vs-num-after-infinite-loop (result i32)
-    (loop (br 0)) (f32.const 0)
-  ))
-  "type mismatch"
-)
-
-(assert_soft_invalid
-  (module (func $type-unary-num-vs-void-in-dead-body
-    (if (i32.const 0) (then (drop (i32.eqz (nop)))))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-unary-num-vs-num-in-dead-body
-    (if (i32.const 0) (then (drop (i32.eqz (f32.const 1)))))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-binary-num-vs-void-in-dead-body
-    (if (i32.const 0) (then (drop (f32.eq (i32.const 1)))))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-binary-num-vs-num-in-dead-body
-    (if (i32.const 0) (then (drop (f32.eq (i32.const 1) (f32.const 0)))))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-if-value-num-vs-void-in-dead-body
-    (if (i32.const 0) (then (i32.const 1)))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-if-value-num-vs-num-in-dead-body (result i32)
-    (if i32 (i32.const 0) (then (f32.const 0)))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-block-value-num-vs-void-in-dead-body
-    (if (i32.const 0) (then (block (i32.const 1))))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-block-value-num-vs-num-in-dead-body (result i32)
-    (if i32 (i32.const 0) (then (block i32 (f32.const 0))))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-block-value-num-vs-void-in-dead-body
-    (if (i32.const 0) (then (loop (i32.const 1))))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-block-value-num-vs-num-in-dead-body (result i32)
-    (if i32 (i32.const 0) (then (loop i32 (f32.const 0))))
-  ))
-  "type mismatch"
-)
-
-(assert_soft_invalid
-  (module (func $type-return-second-num-vs-num (result i32)
-    (return (i32.const 1)) (return (f64.const 1))
-  ))
-  "type mismatch"
-)
-
-(assert_soft_invalid
-  (module (func $type-br-second-num-vs-num (result i32)
-    (block i32 (br 0 (i32.const 1)) (br 0 (f64.const 1)))
-  ))
-  "type mismatch"
-)
-
-(assert_soft_invalid
-  (module (func $type-br_if-cond-num-vs-num-after-unreachable
-    (block (br_if 0 (unreachable) (f32.const 0)))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-br_table-num-vs-num-after-unreachable
-    (block (br_table 0 (unreachable) (f32.const 1)))
-  ))
-  "type mismatch"
-)
-
-(assert_soft_invalid
-  (module (func $type-block-value-nested-unreachable-num-vs-void
-    (block (i32.const 3) (block (unreachable)))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-block-value-nested-unreachable-void-vs-num (result i32)
-    (block (block (unreachable)))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-block-value-nested-unreachable-num-vs-num (result i32)
-    (block i64 (i64.const 0) (block (unreachable)))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-block-value-nested-unreachable-num2-vs-void (result i32)
-    (block (i32.const 3) (block (i64.const 1) (unreachable))) (i32.const 9)
-  ))
-  "type mismatch"
-)
-
-(assert_soft_invalid
-  (module (func $type-block-value-nested-br-num-vs-void
-    (block (i32.const 3) (block (br 1)))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-block-value-nested-br-void-vs-num (result i32)
-    (block i32 (block (br 1 (i32.const 0))))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-block-value-nested-br-num-vs-num (result i32)
-    (block i32 (i64.const 0) (block (br 1 (i32.const 0))))
-  ))
-  "type mismatch"
-)
-
-(assert_soft_invalid
-  (module (func $type-block-value-nested2-br-num-vs-void
-    (block (block (i32.const 3) (block (br 2))))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-block-value-nested2-br-void-vs-num (result i32)
-    (block i32 (block (block (br 2 (i32.const 0)))))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-block-value-nested2-br-num-vs-num (result i32)
-    (block i32 (block i64 (i64.const 0) (block (br 2 (i32.const 0)))))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-block-value-nested2-br-num2-vs-void (result i32)
-    (block (i32.const 3) (block (i64.const 1) (br 1))) (i32.const 9)
-  ))
-  "type mismatch"
-)
-
-(assert_soft_invalid
-  (module (func $type-block-value-nested-return-num-vs-void
-    (block (i32.const 3) (block (return)))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-block-value-nested-return-void-vs-num (result i32)
-    (block (block (return (i32.const 0))))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-block-value-nested-return-num-vs-num (result i32)
-    (block i64 (i64.const 0) (block (return (i32.const 0))))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-block-value-nested-return-num2-vs-void (result i32)
-    (block (i32.const 3) (block (i64.const 1) (return (i32.const 0))))
-    (i32.const 9)
-  ))
-  "type mismatch"
-)
-
-(assert_soft_invalid
-  (module (func $type-loop-value-nested-unreachable-num-vs-void
-    (loop (i32.const 3) (block (unreachable)))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-loop-value-nested-unreachable-void-vs-num (result i32)
-    (loop (block (unreachable)))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-loop-value-nested-unreachable-num-vs-num (result i32)
-    (loop i64 (i64.const 0) (block (unreachable)))
-  ))
-  "type mismatch"
-)
-
-(assert_soft_invalid
-  (module (func $type-cont-last-void-vs-empty (result i32)
-    (loop (br 0 (nop)))
-  ))
-  "type mismatch"
-)
-(assert_soft_invalid
-  (module (func $type-cont-last-num-vs-empty (result i32)
-    (loop (br 0 (i32.const 0)))
-  ))
-  "type mismatch"
-)
deleted file mode 100644
--- a/js/src/jit-test/tests/wasm/spec/soft-fail.wast.js
+++ /dev/null
@@ -1,1 +0,0 @@
-var importedArgs = ['soft-fail.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/unreachable.wast
+++ b/js/src/jit-test/tests/wasm/spec/unreachable.wast
@@ -70,16 +70,19 @@
     (block (br_table 0 0 0 (unreachable)))
   )
   (func (export "as-br_table-value") (result i32)
     (block i32 (br_table 0 0 0 (unreachable) (i32.const 1)) (i32.const 7))
   )
   (func (export "as-br_table-value-index") (result i32)
     (block i32 (br_table 0 0 (i32.const 6) (unreachable)) (i32.const 7))
   )
+  (func (export "as-br_table-value-and-index") (result i32)
+    (block i32 (br_table 0 0 (unreachable)) (i32.const 8))
+  )
 
   (func (export "as-return-value") (result i64)
     (return (unreachable))
   )
 
   (func (export "as-if-cond") (result i32)
     (if i32 (unreachable) (i32.const 0) (i32.const 1))
   )
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/spec/unreached-invalid.wast
@@ -0,0 +1,627 @@
+;; Failures in unreachable code.
+
+(assert_invalid
+  (module (func $local-index (unreachable) (drop (get_local 0))))
+  "unknown local"
+)
+(assert_invalid
+  (module (func $global-index (unreachable) (drop (get_global 0))))
+  "unknown global"
+)
+(assert_invalid
+  (module (func $func-index (unreachable) (call 1)))
+  "unknown function"
+)
+(assert_invalid
+  (module (func $label-index (unreachable) (br 1)))
+  "unknown label"
+)
+
+(assert_invalid
+  (module (func $type-num-vs-num
+    (unreachable) (drop (i64.eqz (i32.const 0))))
+  )
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-poly-num-vs-num (result i32)
+    (unreachable) (i64.const 0) (i32.const 0) (select)
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-poly-transitive-num-vs-num (result i32)
+    (unreachable)
+    (i64.const 0) (i32.const 0) (select)
+    (i32.const 0) (i32.const 0) (select)
+  ))
+  "type mismatch"
+)
+
+(assert_invalid
+  (module (func $type-unconsumed-const (unreachable) (i32.const 0)))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-unconsumed-result (unreachable) (i32.eqz)))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-unconsumed-result2
+    (unreachable) (i32.const 0) (i32.add)
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-unconsumed-poly0 (unreachable) (select)))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-unconsumed-poly1 (unreachable) (i32.const 0) (select)))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-unconsumed-poly2
+    (unreachable) (i32.const 0) (i32.const 0) (select)
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-select-any (result i32) (i32.const 1) (if i64 (then (i64.const 0)) (else (unreachable) (select)))))
+  "type mismatch"
+)
+
+(assert_invalid
+  (module (func $type-unary-num-vs-void-after-break
+    (block (br 0) (block (drop (i32.eqz (nop)))))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-unary-num-vs-num-after-break
+    (block (br 0) (drop (i32.eqz (f32.const 1))))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-binary-num-vs-void-after-break
+    (block (br 0) (block (drop (f32.eq (i32.const 1)))))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-binary-num-vs-num-after-break
+    (block (br 0) (drop (f32.eq (i32.const 1) (f32.const 0))))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-block-value-num-vs-void-after-break
+    (block (br 0) (i32.const 1))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-block-value-num-vs-num-after-break (result i32)
+    (block i32 (i32.const 1) (br 0) (f32.const 0))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-loop-value-num-vs-void-after-break
+    (block (loop (br 1) (i32.const 1)))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-loop-value-num-vs-num-after-break (result i32)
+    (loop i32 (br 1 (i32.const 1)) (f32.const 0))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-func-value-num-vs-void-after-break
+    (br 0) (i32.const 1)
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-func-value-num-vs-num-after-break (result i32)
+    (br 0 (i32.const 1)) (f32.const 0)
+  ))
+  "type mismatch"
+)
+
+(assert_invalid
+  (module (func $type-unary-num-vs-void-after-return
+    (return) (block (drop (i32.eqz (nop))))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-unary-num-vs-num-after-return
+    (return) (drop (i32.eqz (f32.const 1)))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-binary-num-vs-void-after-return
+    (return) (block (drop (f32.eq (i32.const 1))))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-binary-num-vs-num-after-return
+    (return) (drop (f32.eq (i32.const 1) (f32.const 0)))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-block-value-num-vs-void-after-return
+    (block (return) (i32.const 1))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-block-value-num-vs-num-after-return (result i32)
+    (block i32 (i32.const 1) (return (i32.const 0)) (f32.const 0))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-loop-value-num-vs-void-after-return
+    (block (loop (return) (i32.const 1)))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-loop-value-num-vs-num-after-return (result i32)
+    (loop i32 (return (i32.const 1)) (f32.const 0))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-func-value-num-vs-void-after-return
+    (return) (i32.const 1)
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-func-value-num-vs-num-after-return (result i32)
+    (return (i32.const 1)) (f32.const 0)
+  ))
+  "type mismatch"
+)
+
+(assert_invalid
+  (module (func $type-unary-num-vs-void-after-unreachable
+    (unreachable) (block (drop (i32.eqz (nop))))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-unary-num-vs-num-after-unreachable
+    (unreachable) (drop (i32.eqz (f32.const 1)))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-binary-num-vs-void-after-unreachable
+    (unreachable) (block (drop (f32.eq (i32.const 1))))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-binary-num-vs-num-after-unreachable
+    (unreachable) (drop (f32.eq (i32.const 1) (f32.const 0)))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-block-value-num-vs-void-after-unreachable
+    (block (unreachable) (i32.const 1))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-block-value-num-vs-num-after-unreachable (result i32)
+    (block i32 (i32.const 1) (unreachable) (f32.const 0))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-loop-value-num-vs-void-after-unreachable
+    (block (loop (unreachable) (i32.const 1)))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-loop-value-num-vs-num-after-unreachable (result i32)
+    (loop i32 (unreachable) (f32.const 0))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-func-value-num-vs-void-after-unreachable
+    (unreachable) (i32.const 1)
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-func-value-num-vs-num-after-unreachable (result i32)
+    (unreachable) (f32.const 0)
+  ))
+  "type mismatch"
+)
+
+(assert_invalid
+  (module (func $type-unary-num-vs-void-after-nested-unreachable
+    (block (unreachable)) (block (drop (i32.eqz (nop))))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-unary-num-vs-num-after-nested-unreachable
+    (block (unreachable)) (drop (i32.eqz (f32.const 1)))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-binary-num-vs-void-after-nested-unreachable
+    (block (unreachable)) (block (drop (f32.eq (i32.const 1))))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-binary-num-vs-num-after-nested-unreachable
+    (block (unreachable)) (drop (f32.eq (i32.const 1) (f32.const 0)))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-block-value-num-vs-void-after-nested-unreachable
+    (block (block (unreachable)) (i32.const 1))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-block-value-num-vs-num-after-nested-unreachable
+    (result i32)
+    (block i32 (i32.const 1) (block (unreachable)) (f32.const 0))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-loop-value-num-vs-void-after-nested-unreachable
+    (block (loop (block (unreachable)) (i32.const 1)))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-loop-value-num-vs-num-after-nested-unreachable
+    (result i32)
+    (loop i32 (block (unreachable)) (f32.const 0))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-func-value-num-vs-void-after-nested-unreachable
+    (block (unreachable)) (i32.const 1)
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-func-value-num-vs-num-after-nested-unreachable
+    (result i32)
+    (block (unreachable)) (f32.const 0)
+  ))
+  "type mismatch"
+)
+
+(assert_invalid
+  (module (func $type-unary-num-vs-void-after-infinite-loop
+    (loop (br 0)) (block (drop (i32.eqz (nop))))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-unary-num-vs-num-after-infinite-loop
+    (loop (br 0)) (drop (i32.eqz (f32.const 1)))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-binary-num-vs-void-after-infinite-loop
+    (loop (br 0)) (block (drop (f32.eq (i32.const 1))))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-binary-num-vs-num-after-infinite-loop
+    (loop (br 0)) (drop (f32.eq (i32.const 1) (f32.const 0)))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-block-value-num-vs-void-after-infinite-loop
+    (block (loop (br 0)) (i32.const 1))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-block-value-num-vs-num-after-infinite-loop (result i32)
+    (block i32 (i32.const 1) (loop (br 0)) (f32.const 0))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-loop-value-num-vs-void-after-infinite-loop
+    (block (loop (loop (br 0)) (i32.const 1)))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-loop-value-num-vs-num-after-infinite-loop (result i32)
+    (loop i32 (loop (br 0)) (f32.const 0))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-func-value-num-vs-void-after-infinite-loop
+    (loop (br 0)) (i32.const 1)
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-func-value-num-vs-num-after-infinite-loop (result i32)
+    (loop (br 0)) (f32.const 0)
+  ))
+  "type mismatch"
+)
+
+(assert_invalid
+  (module (func $type-unary-num-vs-void-in-dead-body
+    (if (i32.const 0) (then (drop (i32.eqz (nop)))))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-unary-num-vs-num-in-dead-body
+    (if (i32.const 0) (then (drop (i32.eqz (f32.const 1)))))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-binary-num-vs-void-in-dead-body
+    (if (i32.const 0) (then (drop (f32.eq (i32.const 1)))))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-binary-num-vs-num-in-dead-body
+    (if (i32.const 0) (then (drop (f32.eq (i32.const 1) (f32.const 0)))))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-if-value-num-vs-void-in-dead-body
+    (if (i32.const 0) (then (i32.const 1)))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-if-value-num-vs-num-in-dead-body (result i32)
+    (if i32 (i32.const 0) (then (f32.const 0)))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-block-value-num-vs-void-in-dead-body
+    (if (i32.const 0) (then (block (i32.const 1))))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-block-value-num-vs-num-in-dead-body (result i32)
+    (if i32 (i32.const 0) (then (block i32 (f32.const 0))))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-block-value-num-vs-void-in-dead-body
+    (if (i32.const 0) (then (loop (i32.const 1))))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-block-value-num-vs-num-in-dead-body (result i32)
+    (if i32 (i32.const 0) (then (loop i32 (f32.const 0))))
+  ))
+  "type mismatch"
+)
+
+(assert_invalid
+  (module (func $type-return-second-num-vs-num (result i32)
+    (return (i32.const 1)) (return (f64.const 1))
+  ))
+  "type mismatch"
+)
+
+(assert_invalid
+  (module (func $type-br-second-num-vs-num (result i32)
+    (block i32 (br 0 (i32.const 1)) (br 0 (f64.const 1)))
+  ))
+  "type mismatch"
+)
+
+(assert_invalid
+  (module (func $type-br_if-cond-num-vs-num-after-unreachable
+    (block (br_if 0 (unreachable) (f32.const 0)))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-br_table-num-vs-num-after-unreachable
+    (block (br_table 0 (unreachable) (f32.const 1)))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-br_table-label-num-vs-num-after-unreachable (result i32)
+    (block i32 (unreachable) (br_table 0 (f32.const 0) (i32.const 1)))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-br_table-label-num-vs-label-void-after-unreachable
+    (block
+      (block f32
+        (unreachable)
+        (br_table 0 1 0 (i32.const 1))
+      )
+      (drop)
+    )
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-br_table-label-num-vs-label-num-after-unreachable
+    (block f64
+      (block f32
+        (unreachable)
+        (br_table 0 1 1 (i32.const 1))
+      )
+      (drop)
+      (f64.const 0)
+    )
+    (drop)
+  ))
+  "type mismatch"
+)
+
+(assert_invalid
+  (module (func $type-block-value-nested-unreachable-num-vs-void
+    (block (i32.const 3) (block (unreachable)))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-block-value-nested-unreachable-void-vs-num (result i32)
+    (block (block (unreachable)))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-block-value-nested-unreachable-num-vs-num (result i32)
+    (block i64 (i64.const 0) (block (unreachable)))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-block-value-nested-unreachable-num2-vs-void (result i32)
+    (block (i32.const 3) (block (i64.const 1) (unreachable))) (i32.const 9)
+  ))
+  "type mismatch"
+)
+
+(assert_invalid
+  (module (func $type-block-value-nested-br-num-vs-void
+    (block (i32.const 3) (block (br 1)))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-block-value-nested-br-void-vs-num (result i32)
+    (block i32 (block (br 1 (i32.const 0))))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-block-value-nested-br-num-vs-num (result i32)
+    (block i32 (i64.const 0) (block (br 1 (i32.const 0))))
+  ))
+  "type mismatch"
+)
+
+(assert_invalid
+  (module (func $type-block-value-nested2-br-num-vs-void
+    (block (block (i32.const 3) (block (br 2))))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-block-value-nested2-br-void-vs-num (result i32)
+    (block i32 (block (block (br 2 (i32.const 0)))))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-block-value-nested2-br-num-vs-num (result i32)
+    (block i32 (block i64 (i64.const 0) (block (br 2 (i32.const 0)))))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-block-value-nested2-br-num2-vs-void (result i32)
+    (block (i32.const 3) (block (i64.const 1) (br 1))) (i32.const 9)
+  ))
+  "type mismatch"
+)
+
+(assert_invalid
+  (module (func $type-block-value-nested-return-num-vs-void
+    (block (i32.const 3) (block (return)))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-block-value-nested-return-void-vs-num (result i32)
+    (block (block (return (i32.const 0))))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-block-value-nested-return-num-vs-num (result i32)
+    (block i64 (i64.const 0) (block (return (i32.const 0))))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-block-value-nested-return-num2-vs-void (result i32)
+    (block (i32.const 3) (block (i64.const 1) (return (i32.const 0))))
+    (i32.const 9)
+  ))
+  "type mismatch"
+)
+
+(assert_invalid
+  (module (func $type-loop-value-nested-unreachable-num-vs-void
+    (loop (i32.const 3) (block (unreachable)))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-loop-value-nested-unreachable-void-vs-num (result i32)
+    (loop (block (unreachable)))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-loop-value-nested-unreachable-num-vs-num (result i32)
+    (loop i64 (i64.const 0) (block (unreachable)))
+  ))
+  "type mismatch"
+)
+
+(assert_invalid
+  (module (func $type-cont-last-void-vs-empty (result i32)
+    (loop (br 0 (nop)))
+  ))
+  "type mismatch"
+)
+(assert_invalid
+  (module (func $type-cont-last-num-vs-empty (result i32)
+    (loop (br 0 (i32.const 0)))
+  ))
+  "type mismatch"
+)
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/spec/unreached-invalid.wast.js
@@ -0,0 +1,1 @@
+var importedArgs = ['unreached-invalid.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/to-text-experimental.js
+++ b/js/src/jit-test/tests/wasm/to-text-experimental.js
@@ -57,17 +57,17 @@ wasmFailValidateText(
         ))
      )
      (i32.store16 (i32.const 8) (i32.const 128))
 
      (return (f64.const 0))
   )
   (export "test" 0)
   (memory 1 10)
-)`, /popping value from empty stack/);
+)`, emptyStackError);
 
 // function calls
 runTest(`
 (module
   (type $type1 (func (param i32) (result i32)))
   (import $import1 "mod" "test" (param f32) (result f32))
   (table anyfunc (elem $func1 $func2))
   (func $func1 (param i32) (param f32) (nop))
deleted file mode 100644
--- a/js/src/jit-test/tests/wasm/unreachable.js
+++ /dev/null
@@ -1,30 +0,0 @@
-load(libdir + "wasm.js");
-
-// In unreachable code, the current design is that validation is disabled,
-// meaning we have to have a special mode in the decoder for decoding code
-// that won't actually run.
-
-wasmFullPass(`(module
-   (func (result i32)
-     (return (i32.const 42))
-     (i32.add (f64.const 1.0) (f32.const 0.0))
-     (return (f64.const 2.0))
-     (if (f32.const 3.0) (i64.const 2) (i32.const 1))
-     (select (f64.const -5.0) (f32.const 2.3) (f64.const 8.9))
-   )
-   (export "run" 0)
-)`, 42);
-
-wasmFullPass(`(module
-   (func (result i32) (param i32)
-     (block
-        (br_if 1 (i32.const 41) (get_local 0))
-        (br 1 (i32.const 42))
-     )
-     (i32.add (f32.const 0.0) (f64.const 1.0))
-     (return (f64.const 2.0))
-     (if (f32.const 3.0) (i64.const 2) (i32.const 1))
-     (select (f64.const -5.0) (f32.const 2.3) (f64.const 8.9))
-   )
-   (export "run" 0)
-)`, 42, {}, 0);
--- a/js/src/jit-test/tests/wasm/wast.js
+++ b/js/src/jit-test/tests/wasm/wast.js
@@ -430,33 +430,16 @@ function exec(e) {
         } catch (e) {
             caught = true;
             debug("Caught", e.toString(), ", expected:", errMsg);
         }
         assertEq(caught, true);
         return;
     }
 
-    if (exprName === "assert_soft_invalid") {
-        let moduleText = e.list[1].toString();
-        let errMsg = e.list[2];
-        if (errMsg) {
-            assert(errMsg.quoted, "assert_soft_invalid second argument must be a string");
-            errMsg.quoted = false;
-        }
-
-        try {
-            new WebAssembly.Module(wasmTextToBinary(moduleText));
-        } catch(e) {
-            debug('assert_soft_invalid caught:\nExpected:', errMsg, '\nActual:', e.toString());
-        }
-
-        return;
-    }
-
     if (exprName === 'assert_trap') {
         let caught = false;
         let errMsg = e.list[2];
         assert(errMsg.quoted, "assert_trap second argument must be a string");
         errMsg.quoted = false;
         try {
             exec(e.list[1]);
         } catch(err) {
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -469,16 +469,34 @@ JS_NewContext(uint32_t maxbytes, uint32_
 
     // Make sure that all parent runtimes are the topmost parent.
     while (parentRuntime && parentRuntime->parentRuntime)
         parentRuntime = parentRuntime->parentRuntime;
 
     return NewContext(maxbytes, maxNurseryBytes, parentRuntime);
 }
 
+JS_PUBLIC_API(JSContext*)
+JS_NewCooperativeContext(JSContext* siblingContext)
+{
+    return NewCooperativeContext(siblingContext);
+}
+
+JS_PUBLIC_API(void)
+JS_YieldCooperativeContext(JSContext* cx)
+{
+    YieldCooperativeContext(cx);
+}
+
+JS_PUBLIC_API(void)
+JS_ResumeCooperativeContext(JSContext* cx)
+{
+    ResumeCooperativeContext(cx);
+}
+
 JS_PUBLIC_API(void)
 JS_DestroyContext(JSContext* cx)
 {
     DestroyContext(cx);
 }
 
 JS_PUBLIC_API(void*)
 JS_GetContextPrivate(JSContext* cx)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -976,21 +976,47 @@ JS_IsBuiltinFunctionConstructor(JSFuncti
  * Locking, contexts, and memory allocation.
  *
  * It is important that SpiderMonkey be initialized, and the first context
  * be created, in a single-threaded fashion.  Otherwise the behavior of the
  * library is undefined.
  * See: http://developer.mozilla.org/en/docs/Category:JSAPI_Reference
  */
 
+// Create a new runtime, with a single cooperative context for this thread.
+// On success, the new context will be the active context for the runtime.
 extern JS_PUBLIC_API(JSContext*)
 JS_NewContext(uint32_t maxbytes,
               uint32_t maxNurseryBytes = JS::DefaultNurseryBytes,
               JSRuntime* parentRuntime = nullptr);
 
+// The methods below for controlling the active context in a cooperatively
+// multithreaded runtime are not threadsafe, and the caller must ensure they
+// are called serially if there is a chance for contention between threads.
+
+// Called from the active context for a runtime, yield execution so that
+// this context is no longer active and can no longer use the API.
+extern JS_PUBLIC_API(void)
+JS_YieldCooperativeContext(JSContext* cx);
+
+// Called from a context whose runtime has no active context, this thread
+// becomes the active context for that runtime and may use the API.
+extern JS_PUBLIC_API(void)
+JS_ResumeCooperativeContext(JSContext* cx);
+
+// Create a new context on this thread for cooperative multithreading in the
+// same runtime as siblingContext. Called on a runtime (as indicated by
+// siblingContet) which has no active context, on success the new context will
+// become the runtime's active context.
+extern JS_PUBLIC_API(JSContext*)
+JS_NewCooperativeContext(JSContext* siblingContext);
+
+// Destroy a context allocated with JS_NewContext or JS_NewCooperativeContext.
+// The context must be the current active context in the runtime, and after
+// this call the runtime will have no active context.
 extern JS_PUBLIC_API(void)
 JS_DestroyContext(JSContext* cx);
 
 JS_PUBLIC_API(void*)
 JS_GetContextPrivate(JSContext* cx);
 
 JS_PUBLIC_API(void)
 JS_SetContextPrivate(JSContext* cx, void* data);
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -94,46 +94,53 @@ js::AutoCycleDetector::~AutoCycleDetecto
 void
 js::TraceCycleDetectionSet(JSTracer* trc, AutoCycleDetector::Set& set)
 {
     for (AutoCycleDetector::Set::Enum e(set); !e.empty(); e.popFront())
         TraceRoot(trc, &e.mutableFront(), "cycle detector table entry");
 }
 
 bool
-JSContext::init()
+JSContext::init(ContextKind kind)
 {
-    // Get a platform-native handle for this thread, used by js::InterruptRunningJitCode.
+    // Skip most of the initialization if this thread will not be running JS.
+    if (kind == ContextKind::Cooperative) {
+        // Get a platform-native handle for this thread, used by js::InterruptRunningJitCode.
 #ifdef XP_WIN
-    size_t openFlags = THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME |
-                       THREAD_QUERY_INFORMATION;
-    HANDLE self = OpenThread(openFlags, false, GetCurrentThreadId());
-    if (!self)
+        size_t openFlags = THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME |
+                           THREAD_QUERY_INFORMATION;
+        HANDLE self = OpenThread(openFlags, false, GetCurrentThreadId());
+        if (!self)
         return false;
-    static_assert(sizeof(HANDLE) <= sizeof(threadNative_), "need bigger field");
-    threadNative_ = (size_t)self;
+        static_assert(sizeof(HANDLE) <= sizeof(threadNative_), "need bigger field");
+        threadNative_ = (size_t)self;
 #else
-    static_assert(sizeof(pthread_t) <= sizeof(threadNative_), "need bigger field");
-    threadNative_ = (size_t)pthread_self();
+        static_assert(sizeof(pthread_t) <= sizeof(threadNative_), "need bigger field");
+        threadNative_ = (size_t)pthread_self();
 #endif
 
-    if (!regexpStack.ref().init())
-        return false;
+        if (!regexpStack.ref().init())
+            return false;
 
-    if (!fx.initInstance())
-        return false;
+        if (!fx.initInstance())
+            return false;
 
 #ifdef JS_SIMULATOR
-    simulator_ = js::jit::Simulator::Create(this);
-    if (!simulator_)
-        return false;
+        simulator_ = js::jit::Simulator::Create(this);
+        if (!simulator_)
+            return false;
 #endif
 
-    if (!wasm::EnsureSignalHandlers(this))
-        return false;
+        if (!wasm::EnsureSignalHandlers(this))
+            return false;
+    }
+
+    // Set the ContextKind last, so that ProtectedData checks will allow us to
+    // initialize this context before it becomes the runtime's active context.
+    kind_ = kind;
 
     return true;
 }
 
 JSContext*
 js::NewContext(uint32_t maxBytes, uint32_t maxNurseryBytes, JSRuntime* parentRuntime)
 {
     AutoNoteSingleThreadedRegion anstr;
@@ -151,26 +158,58 @@ js::NewContext(uint32_t maxBytes, uint32
     }
 
     if (!runtime->init(cx, maxBytes, maxNurseryBytes)) {
         js_delete(cx);
         js_delete(runtime);
         return nullptr;
     }
 
-
-    if (!cx->init()) {
+    if (!cx->init(ContextKind::Cooperative)) {
         js_delete(cx);
         js_delete(runtime);
         return nullptr;
     }
 
     return cx;
 }
 
+JSContext*
+js::NewCooperativeContext(JSContext* siblingContext)
+{
+    MOZ_RELEASE_ASSERT(!TlsContext.get());
+
+    JSRuntime* runtime = siblingContext->runtime();
+
+    JSContext* cx = js_new<JSContext>(runtime, JS::ContextOptions());
+    if (!cx || !cx->init(ContextKind::Cooperative)) {
+        js_delete(cx);
+        return nullptr;
+    }
+
+    runtime->setNewbornActiveContext(cx);
+    return cx;
+}
+
+void
+js::YieldCooperativeContext(JSContext* cx)
+{
+    MOZ_ASSERT(cx == TlsContext.get());
+    MOZ_ASSERT(cx->runtime()->activeContext() == cx);
+    cx->runtime()->setActiveContext(nullptr);
+}
+
+void
+js::ResumeCooperativeContext(JSContext* cx)
+{
+    MOZ_ASSERT(cx == TlsContext.get());
+    MOZ_ASSERT(cx->runtime()->activeContext() == nullptr);
+    cx->runtime()->setActiveContext(cx);
+}
+
 void
 js::DestroyContext(JSContext* cx)
 {
     JS_AbortIfWrongThread(cx);
 
     if (cx->outstandingRequests != 0)
         MOZ_CRASH("Attempted to destroy a context while it is in a request.");
 
@@ -181,30 +220,32 @@ js::DestroyContext(JSContext* cx)
     // cooperative contexts which they have read off the owner context of a
     // zone group. See HelperThread::handleIonWorkload.
     CancelOffThreadIonCompile(cx->runtime());
 
     if (cx->runtime()->cooperatingContexts().length() == 1) {
         // Destroy the runtime along with its last context.
         cx->runtime()->destroyRuntime();
         js_delete(cx->runtime());
+
+        js_delete_poison(cx);
     } else {
         DebugOnly<bool> found = false;
         for (size_t i = 0; i < cx->runtime()->cooperatingContexts().length(); i++) {
             CooperatingContext& target = cx->runtime()->cooperatingContexts()[i];
             if (cx == target.context()) {
                 cx->runtime()->cooperatingContexts().erase(&target);
                 found = true;
                 break;
             }
         }
         MOZ_ASSERT(found);
+
+        cx->runtime()->deleteActiveContext(cx);
     }
-
-    js_delete_poison(cx);
 }
 
 void
 JS::RootingContext::checkNoGCRooters() {
 #ifdef DEBUG
     for (auto const& stackRootPtr : stackRoots_)
         MOZ_ASSERT(stackRootPtr == nullptr);
 #endif
@@ -1087,16 +1128,17 @@ JSContext::alreadyReportedError()
     if (!helperThread())
         MOZ_ASSERT(isExceptionPending());
 #endif
     return mozilla::MakeGenericErrorResult(reportedError);
 }
 
 JSContext::JSContext(JSRuntime* runtime, const JS::ContextOptions& options)
   : runtime_(runtime),
+    kind_(ContextKind::Background),
     threadNative_(0),
     helperThread_(nullptr),
     options_(options),
     arenas_(nullptr),
     enterCompartmentDepth_(0),
     jitActivation(nullptr),
     activation_(nullptr),
     profilingActivation_(nullptr),
@@ -1166,25 +1208,30 @@ JSContext::JSContext(JSRuntime* runtime,
     osrTempData_(nullptr),
     ionReturnOverride_(MagicValue(JS_ARG_POISON)),
     jitTop(nullptr),
     jitStackLimit(UINTPTR_MAX),
     jitStackLimitNoInterrupt(UINTPTR_MAX)
 {
     MOZ_ASSERT(static_cast<JS::RootingContext*>(this) ==
                JS::RootingContext::get(this));
-    for (size_t i = 0; i < mozilla::ArrayLength(nativeStackQuota); i++)
-        nativeStackQuota[i] = 0;
 
     MOZ_ASSERT(!TlsContext.get());
     TlsContext.set(this);
+
+    for (size_t i = 0; i < mozilla::ArrayLength(nativeStackQuota); i++)
+        nativeStackQuota[i] = 0;
 }
 
 JSContext::~JSContext()
 {
+    // Clear the ContextKind first, so that ProtectedData checks will allow us to
+    // destroy this context even if the runtime is already gone.
+    kind_ = ContextKind::Background;
+
 #ifdef XP_WIN
     if (threadNative_)
         CloseHandle((HANDLE)threadNative_.ref());
 #endif
 
     /* Free the stuff hanging off of cx. */
     MOZ_ASSERT(!resolvingList);
 
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -75,48 +75,56 @@ namespace frontend { class CompileError;
 
 struct HelperThread;
 
 void ReportOverRecursed(JSContext* cx, unsigned errorNumber);
 
 /* Thread Local Storage slot for storing the context for a thread. */
 extern MOZ_THREAD_LOCAL(JSContext*) TlsContext;
 
+enum class ContextKind
+{
+    Cooperative,
+    Background
+};
+
 } /* namespace js */
 
 /*
  * A JSContext encapsulates the thread local state used when using the JS
  * runtime.
  */
 struct JSContext : public JS::RootingContext,
                    public js::MallocProvider<JSContext>
 {
-    explicit JSContext(JSRuntime* runtime, const JS::ContextOptions& options);
+    JSContext(JSRuntime* runtime, const JS::ContextOptions& options);
     ~JSContext();
 
-    bool init();
+    bool init(js::ContextKind kind);
 
   private:
     js::UnprotectedData<JSRuntime*> runtime_;
+    js::WriteOnceData<js::ContextKind> kind_;
 
     // System handle for the thread this context is associated with.
     js::WriteOnceData<size_t> threadNative_;
 
     // The thread on which this context is running, if this is performing a parse task.
     js::ThreadLocalData<js::HelperThread*> helperThread_;
 
     js::ThreadLocalData<JS::ContextOptions> options_;
 
     js::ThreadLocalData<js::gc::ArenaLists*> arenas_;
 
   public:
     // This is used by helper threads to change the runtime their context is
     // currently operating on.
     void setRuntime(JSRuntime* rt);
 
+    bool isCooperativelyScheduled() const { return kind_ == js::ContextKind::Cooperative; }
     size_t threadNative() const { return threadNative_; }
 
     inline js::gc::ArenaLists* arenas() const { return arenas_; }
 
     template <typename T>
     bool isInsideCurrentZone(T thing) const {
         return thing->zoneFromAnyThread() == zone_;
     }
@@ -634,17 +642,17 @@ struct JSContext : public JS::RootingCon
     /* State for object and array toSource conversion. */
     js::ThreadLocalData<js::AutoCycleDetector::Set> cycleDetectorSet_;
 
   public:
     js::AutoCycleDetector::Set& cycleDetectorSet() { return cycleDetectorSet_.ref(); }
     const js::AutoCycleDetector::Set& cycleDetectorSet() const { return cycleDetectorSet_.ref(); }
 
     /* Client opaque pointer. */
-    void* data;
+    js::UnprotectedData<void*> data;
 
     void initJitStackLimit();
     void resetJitStackLimit();
 
   public:
     /*
      * Return:
      * - The newest scripted frame's version, if there is such a frame.
@@ -946,16 +954,25 @@ struct MOZ_RAII AutoResolving {
 
 /*
  * Create and destroy functions for JSContext, which is manually allocated
  * and exclusively owned.
  */
 extern JSContext*
 NewContext(uint32_t maxBytes, uint32_t maxNurseryBytes, JSRuntime* parentRuntime);
 
+extern JSContext*
+NewCooperativeContext(JSContext* siblingContext);
+
+extern void
+YieldCooperativeContext(JSContext* cx);
+
+extern void
+ResumeCooperativeContext(JSContext* cx);
+
 extern void
 DestroyContext(JSContext* cx);
 
 enum ErrorArgumentsType {
     ArgumentsAreUnicode,
     ArgumentsAreASCII,
     ArgumentsAreLatin1,
     ArgumentsAreUTF8
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -979,19 +979,20 @@ js::Disassemble1(JSContext* cx, HandleSc
       case JOF_BYTE:
           // Scan the trynotes to find the associated catch block
           // and make the try opcode look like a jump instruction
           // with an offset. This simplifies code coverage analysis
           // based on this disassembled output.
           if (op == JSOP_TRY) {
               TryNoteArray* trynotes = script->trynotes();
               uint32_t i;
+              size_t mainOffset = script->mainOffset();
               for(i = 0; i < trynotes->length; i++) {
                   JSTryNote note = trynotes->vector[i];
-                  if (note.kind == JSTRY_CATCH && note.start == loc + 1) {
+                  if (note.kind == JSTRY_CATCH && note.start + mainOffset == loc + 1) {
                       if (!sp->jsprintf(" %u (%+d)",
                                         unsigned(loc + note.length + 1),
                                         int(note.length + 1)))
                       {
                           return 0;
                       }
                       break;
                   }
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3357,79 +3357,178 @@ EvalInContext(JSContext* cx, unsigned ar
     }
 
     if (!cx->compartment()->wrap(cx, args.rval()))
         return false;
 
     return true;
 }
 
+struct CooperationState
+{
+    CooperationState()
+      : lock(mutexid::ShellThreadCooperation)
+      , idle(false)
+      , numThreads(0)
+      , yieldCount(0)
+    {}
+
+    Mutex lock;
+    ConditionVariable cvar;
+    bool idle;
+    size_t numThreads;
+    uint64_t yieldCount;
+};
+static CooperationState* cooperationState = nullptr;
+
+static void
+CooperativeBeginWait(JSContext* cx)
+{
+    MOZ_ASSERT(cx == TlsContext.get());
+    JS_YieldCooperativeContext(cx);
+}
+
+static void
+CooperativeEndWait(JSContext* cx)
+{
+    MOZ_ASSERT(cx == TlsContext.get());
+    LockGuard<Mutex> lock(cooperationState->lock);
+
+    cooperationState->cvar.wait(lock, [&] { return cooperationState->idle; });
+
+    JS_ResumeCooperativeContext(cx);
+    cooperationState->idle = false;
+    cooperationState->yieldCount++;
+    cooperationState->cvar.notify_all();
+}
+
+static void
+CooperativeYield()
+{
+    LockGuard<Mutex> lock(cooperationState->lock);
+    MOZ_ASSERT(!cooperationState->idle);
+    cooperationState->idle = true;
+    cooperationState->cvar.notify_all();
+
+    // Wait until another thread takes over control before returning, if there
+    // is another thread to do so.
+    if (cooperationState->numThreads) {
+        uint64_t count = cooperationState->yieldCount;
+        cooperationState->cvar.wait(lock, [&] { return cooperationState->yieldCount != count; });
+    }
+}
+
+static bool
+CooperativeYieldThread(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (!cooperationState) {
+        JS_ReportErrorASCII(cx, "No cooperative threads have been created");
+        return false;
+    }
+
+    if (GetShellContext(cx)->isWorker) {
+        JS_ReportErrorASCII(cx, "Worker threads cannot yield");
+        return false;
+    }
+
+    CooperativeBeginWait(cx);
+    CooperativeYield();
+    CooperativeEndWait(cx);
+
+    args.rval().setUndefined();
+    return true;
+}
+
 struct WorkerInput
 {
     JSRuntime* parentRuntime;
+    JSContext* siblingContext;
     char16_t* chars;
     size_t length;
 
     WorkerInput(JSRuntime* parentRuntime, char16_t* chars, size_t length)
-      : parentRuntime(parentRuntime), chars(chars), length(length)
+      : parentRuntime(parentRuntime), siblingContext(nullptr), chars(chars), length(length)
+    {}
+
+    WorkerInput(JSContext* siblingContext, char16_t* chars, size_t length)
+      : parentRuntime(nullptr), siblingContext(siblingContext), chars(chars), length(length)
     {}
 
     ~WorkerInput() {
         js_free(chars);
     }
 };
 
 static void SetWorkerContextOptions(JSContext* cx);
 
 static void
 WorkerMain(void* arg)
 {
     WorkerInput* input = (WorkerInput*) arg;
-
-    JSContext* cx = JS_NewContext(8L * 1024L * 1024L, 2L * 1024L * 1024L, input->parentRuntime);
-    if (!cx) {
-        js_delete(input);
+    MOZ_ASSERT(!!input->parentRuntime != !!input->siblingContext);
+
+    JSContext* cx = nullptr;
+
+    auto guard = mozilla::MakeScopeExit([&] {
+            if (cx)
+                JS_DestroyContext(cx);
+            if (input->siblingContext) {
+                cooperationState->numThreads--;
+                CooperativeYield();
+            }
+            js_delete(input);
+        });
+
+    cx = input->parentRuntime
+         ? JS_NewContext(8L * 1024L * 1024L, 2L * 1024L * 1024L, input->parentRuntime)
+         : JS_NewCooperativeContext(input->siblingContext);
+    if (!cx)
         return;
-    }
 
     UniquePtr<ShellContext> sc = MakeUnique<ShellContext>(cx);
-    if (!sc) {
-        JS_DestroyContext(cx);
-        js_delete(input);
+    if (!sc)
         return;
-    }
-
-    sc->isWorker = true;
+
+    if (input->parentRuntime)
+        sc->isWorker = true;
     JS_SetContextPrivate(cx, sc.get());
-    JS_SetFutexCanWait(cx);
-    JS::SetWarningReporter(cx, WarningReporter);
-    js::SetPreserveWrapperCallback(cx, DummyPreserveWrapperCallback);
-    JS_InitDestroyPrincipalsCallback(cx, ShellPrincipals::destroy);
     SetWorkerContextOptions(cx);
-
-    if (!JS::InitSelfHostedCode(cx)) {
-        JS_DestroyContext(cx);
-        js_delete(input);
-        return;
-    }
-
     sc->jobQueue.init(cx, JobQueue(SystemAllocPolicy()));
-    JS::SetEnqueuePromiseJobCallback(cx, ShellEnqueuePromiseJobCallback);
-    JS::SetGetIncumbentGlobalCallback(cx, ShellGetIncumbentGlobalCallback);
-    JS::SetAsyncTaskCallbacks(cx, ShellStartAsyncTaskCallback, ShellFinishAsyncTaskCallback);
-
-    EnvironmentPreparer environmentPreparer(cx);
-
-    JS::SetLargeAllocationFailureCallback(cx, my_LargeAllocFailCallback, (void*)cx);
+
+    Maybe<EnvironmentPreparer> environmentPreparer;
+    if (input->parentRuntime) {
+        JS_SetFutexCanWait(cx);
+        JS::SetWarningReporter(cx, WarningReporter);
+        js::SetPreserveWrapperCallback(cx, DummyPreserveWrapperCallback);
+        JS_InitDestroyPrincipalsCallback(cx, ShellPrincipals::destroy);
+
+        if (!JS::InitSelfHostedCode(cx))
+            return;
+
+        JS::SetEnqueuePromiseJobCallback(cx, ShellEnqueuePromiseJobCallback);
+        JS::SetGetIncumbentGlobalCallback(cx, ShellGetIncumbentGlobalCallback);
+        JS::SetAsyncTaskCallbacks(cx, ShellStartAsyncTaskCallback, ShellFinishAsyncTaskCallback);
+
+        environmentPreparer.emplace(cx);
+
+        JS::SetLargeAllocationFailureCallback(cx, my_LargeAllocFailCallback, (void*)cx);
+    } else {
+        JS_AddInterruptCallback(cx, ShellInterruptCallback);
+    }
 
     do {
         JSAutoRequest ar(cx);
 
         JS::CompartmentOptions compartmentOptions;
         SetStandardCompartmentOptions(compartmentOptions);
+        if (input->siblingContext)
+            compartmentOptions.creationOptions().setNewZoneInNewZoneGroup();
+
         RootedObject global(cx, NewGlobalObject(cx, compartmentOptions, nullptr));
         if (!global)
             break;
 
         JSAutoCompartment ac(cx, global);
 
         JS::CompileOptions options(cx);
         options.setFileAndLine("<string>", 1)
@@ -3445,20 +3544,16 @@ WorkerMain(void* arg)
 
     JS::SetLargeAllocationFailureCallback(cx, nullptr, nullptr);
 
     JS::SetGetIncumbentGlobalCallback(cx, nullptr);
     JS::SetEnqueuePromiseJobCallback(cx, nullptr);
     sc->jobQueue.reset();
 
     KillWatchdog(cx);
-
-    JS_DestroyContext(cx);
-
-    js_delete(input);
 }
 
 // Workers can spawn other workers, so we need a lock to access workerThreads.
 static Mutex* workerThreadsLock = nullptr;
 static Vector<js::Thread*, 0, SystemAllocPolicy> workerThreads;
 
 class MOZ_RAII AutoLockWorkerThreads : public LockGuard<Mutex>
 {
@@ -3467,36 +3562,45 @@ class MOZ_RAII AutoLockWorkerThreads : p
     AutoLockWorkerThreads()
       : Base(*workerThreadsLock)
     {
         MOZ_ASSERT(workerThreadsLock);
     }
 };
 
 static bool
-EvalInWorker(JSContext* cx, unsigned argc, Value* vp)
+EvalInThread(JSContext* cx, unsigned argc, Value* vp, bool cooperative)
 {
     if (!CanUseExtraThreads()) {
-        JS_ReportErrorASCII(cx, "Can't create worker threads with --no-threads");
+        JS_ReportErrorASCII(cx, "Can't create threads with --no-threads");
         return false;
     }
 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (!args.get(0).isString()) {
-        JS_ReportErrorASCII(cx, "Invalid arguments to evalInWorker");
+        JS_ReportErrorASCII(cx, "Invalid arguments");
         return false;
     }
 
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
     if (cx->runningOOMTest) {
-        JS_ReportErrorASCII(cx, "Can't create workers while running simulated OOM test");
+        JS_ReportErrorASCII(cx, "Can't create threads while running simulated OOM test");
         return false;
     }
 #endif
 
+    if (cooperative && GetShellContext(cx)->isWorker) {
+        // Disallowing cooperative multithreading in worker runtimes allows
+        // yield state to be process wide, and some other simplifications.
+        // When we have a better idea of how cooperative multithreading will be
+        // used in the browser this restriction might be relaxed.
+        JS_ReportErrorASCII(cx, "Cooperative multithreading in worker runtimes is not supported");
+        return false;
+    }
+
     if (!args[0].toString()->ensureLinear(cx))
         return false;
 
     if (!workerThreadsLock) {
         workerThreadsLock = js_new<Mutex>(mutexid::ShellWorkerThreads);
         if (!workerThreadsLock) {
             ReportOutOfMemory(cx);
             return false;
@@ -3508,40 +3612,71 @@ EvalInWorker(JSContext* cx, unsigned arg
     char16_t* chars = (char16_t*) js_malloc(str->length() * sizeof(char16_t));
     if (!chars) {
         ReportOutOfMemory(cx);
         return false;
     }
 
     CopyChars(chars, *str);
 
-    WorkerInput* input = js_new<WorkerInput>(JS_GetParentRuntime(cx), chars, str->length());
+    WorkerInput* input =
+        cooperative
+        ? js_new<WorkerInput>(cx, chars, str->length())
+        : js_new<WorkerInput>(JS_GetParentRuntime(cx), chars, str->length());
     if (!input) {
         ReportOutOfMemory(cx);
         return false;
     }
 
+    if (cooperative) {
+        if (!cooperationState)
+            cooperationState = js_new<CooperationState>();
+        cooperationState->numThreads++;
+        CooperativeBeginWait(cx);
+    }
+
     auto thread = js_new<Thread>(Thread::Options().setStackSize(gMaxStackSize + 128 * 1024));
     if (!thread || !thread->init(WorkerMain, input)) {
         ReportOutOfMemory(cx);
-        return false;
-    }
-
-    AutoLockWorkerThreads alwt;
-    if (!workerThreads.append(thread)) {
-        ReportOutOfMemory(cx);
-        thread->join();
-        return false;
+        if (cooperative) {
+            cooperationState->numThreads--;
+            CooperativeYield();
+            CooperativeEndWait(cx);
+        }
+        return false;
+    }
+
+    if (cooperative) {
+        CooperativeEndWait(cx);
+    } else {
+        AutoLockWorkerThreads alwt;
+        if (!workerThreads.append(thread)) {
+            ReportOutOfMemory(cx);
+            thread->join();
+            return false;
+        }
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
+EvalInWorker(JSContext* cx, unsigned argc, Value* vp)
+{
+    return EvalInThread(cx, argc, vp, false);
+}
+
+static bool
+EvalInCooperativeThread(JSContext* cx, unsigned argc, Value* vp)
+{
+    return EvalInThread(cx, argc, vp, true);
+}
+
+static bool
 ShapeOf(JSContext* cx, unsigned argc, JS::Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (!args.get(0).isObject()) {
         JS_ReportErrorASCII(cx, "shapeOf: object expected");
         return false;
     }
     JSObject* obj = &args[0].toObject();
@@ -3708,17 +3843,17 @@ ScheduleWatchdog(JSContext* cx, double t
     } else if (!sc->watchdogTimeout || timeout < sc->watchdogTimeout.value()) {
         sc->watchdogWakeup.notify_one();
     }
     sc->watchdogTimeout = Some(timeout);
     return true;
 }
 
 static void
-KillWorkerThreads()
+KillWorkerThreads(JSContext* cx)
 {
     MOZ_ASSERT_IF(!CanUseExtraThreads(), workerThreads.empty());
 
     if (!workerThreadsLock) {
         MOZ_ASSERT(workerThreads.empty());
         return;
     }
 
@@ -3733,16 +3868,26 @@ KillWorkerThreads()
                 break;
             thread = workerThreads.popCopy();
         }
         thread->join();
     }
 
     js_delete(workerThreadsLock);
     workerThreadsLock = nullptr;
+
+    // Yield until all other cooperative threads in the main runtime finish.
+    while (cooperationState && cooperationState->numThreads) {
+        CooperativeBeginWait(cx);
+        CooperativeYield();
+        CooperativeEndWait(cx);
+    }
+
+    js_delete(cooperationState);
+    cooperationState = nullptr;
 }
 
 static void
 CancelExecution(JSContext* cx)
 {
     ShellContext* sc = GetShellContext(cx);
     sc->serviceInterrupt = true;
     JS_RequestInterruptCallback(cx);
@@ -4817,16 +4962,17 @@ NewGlobal(JSContext* cx, unsigned argc, 
 {
     JSPrincipals* principals = nullptr;
 
     JS::CompartmentOptions options;
     JS::CompartmentCreationOptions& creationOptions = options.creationOptions();
     JS::CompartmentBehaviors& behaviors = options.behaviors();
 
     SetStandardCompartmentOptions(options);
+    options.creationOptions().setNewZoneInExistingZoneGroup(cx->global());
 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() == 1 && args[0].isObject()) {
         RootedObject opts(cx, &args[0].toObject());
         RootedValue v(cx);
 
         if (!JS_GetProperty(cx, opts, "invisibleToDebugger", &v))
             return false;
@@ -6015,16 +6161,24 @@ static const JSFunctionSpecWithHelp shel
 "  Evaluate s in optional sandbox object o.\n"
 "  if (s == '' && !o) return new o with eager standard classes\n"
 "  if (s == 'lazy' && !o) return new o with lazy standard classes"),
 
     JS_FN_HELP("evalInWorker", EvalInWorker, 1, 0,
 "evalInWorker(str)",
 "  Evaluate 'str' in a separate thread with its own runtime.\n"),
 
+    JS_FN_HELP("evalInCooperativeThread", EvalInCooperativeThread, 1, 0,
+"evalInCooperativeThread(str)",
+"  Evaluate 'str' in a separate cooperatively scheduled thread using the same runtime.\n"),
+
+    JS_FN_HELP("cooperativeYield", CooperativeYieldThread, 0, 0,
+"evalInCooperativeThread()",
+"  Yield execution to another cooperatively scheduled thread using the same runtime.\n"),
+
     JS_FN_HELP("getSharedArrayBuffer", GetSharedArrayBuffer, 0, 0,
 "getSharedArrayBuffer()",
 "  Retrieve the SharedArrayBuffer object from the cross-worker mailbox.\n"
 "  The object retrieved may not be identical to the object that was\n"
 "  installed, but it references the same shared memory.\n"
 "  getSharedArrayBuffer performs an ordering memory barrier.\n"),
 
     JS_FN_HELP("setSharedArrayBuffer", SetSharedArrayBuffer, 0, 0,
@@ -8173,16 +8327,16 @@ main(int argc, char** argv, char** envp)
     JS::SetLargeAllocationFailureCallback(cx, nullptr, nullptr);
 
     JS::SetGetIncumbentGlobalCallback(cx, nullptr);
     JS::SetEnqueuePromiseJobCallback(cx, nullptr);
     sc->jobQueue.reset();
 
     KillWatchdog(cx);
 
-    KillWorkerThreads();
+    KillWorkerThreads(cx);
 
     DestructSharedArrayBufferMailbox();
 
     JS_DestroyContext(cx);
     JS_ShutDown();
     return result;
 }
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Comprehensions/for-reserved-word.js
@@ -0,0 +1,107 @@
+var BUGNUMBER = 1340089;
+var summary = "Comprehension should check the binding names";
+
+print(BUGNUMBER + ": " + summary);
+
+// Non strict mode.
+// Keywords, literals, 'let', and 'yield' are not allowed.
+
+assertThrowsInstanceOf(function () {
+    eval("[for (true of [1]) 2]");
+}, SyntaxError);
+assertThrowsInstanceOf(function () {
+    eval("(for (true of [1]) 2)");
+}, SyntaxError);
+
+assertThrowsInstanceOf(function () {
+    eval("[for (throw of [1]) 2]");
+}, SyntaxError);
+assertThrowsInstanceOf(function () {
+    eval("(for (throw of [1]) 2)");
+}, SyntaxError);
+
+assertThrowsInstanceOf(function () {
+    eval("[for (let of [1]) 2]");
+}, SyntaxError);
+assertThrowsInstanceOf(function () {
+    eval("(for (let of [1]) 2)");
+}, SyntaxError);
+
+assertThrowsInstanceOf(function () {
+    eval("[for (yield of [1]) 2]");
+}, SyntaxError);
+assertThrowsInstanceOf(function () {
+    eval("(for (yield of [1]) 2)");
+}, SyntaxError);
+
+eval("[for (public of [1]) 2]");
+eval("(for (public of [1]) 2)");
+
+eval("[for (static of [1]) 2]");
+eval("(for (static of [1]) 2)");
+
+// Strict mode.
+// All reserved words are not allowed.
+
+assertThrowsInstanceOf(function () {
+    "use strict";
+    eval("[for (true of [1]) 2]");
+}, SyntaxError);
+assertThrowsInstanceOf(function () {
+    "use strict";
+    eval("(for (true of [1]) 2)");
+}, SyntaxError);
+
+assertThrowsInstanceOf(function () {
+    "use strict";
+    eval("[for (throw of [1]) 2]");
+}, SyntaxError);
+assertThrowsInstanceOf(function () {
+    "use strict";
+    eval("(for (throw of [1]) 2)");
+}, SyntaxError);
+
+assertThrowsInstanceOf(function () {
+    "use strict";
+    eval("[for (let of [1]) 2]");
+}, SyntaxError);
+assertThrowsInstanceOf(function () {
+    "use strict";
+    eval("(for (let of [1]) 2)");
+}, SyntaxError);
+
+assertThrowsInstanceOf(function () {
+    "use strict";
+    eval("[for (yield of [1]) 2]");
+}, SyntaxError);
+assertThrowsInstanceOf(function () {
+    "use strict";
+    eval("(for (yield of [1]) 2)");
+}, SyntaxError);
+
+assertThrowsInstanceOf(function () {
+    "use strict";
+    eval("[for (public of [1]) 2]");
+}, SyntaxError);
+assertThrowsInstanceOf(function () {
+    "use strict";
+    eval("(for (public of [1]) 2)");
+}, SyntaxError);
+
+assertThrowsInstanceOf(function () {
+    "use strict";
+    eval("[for (static of [1]) 2]");
+}, SyntaxError);
+assertThrowsInstanceOf(function () {
+    "use strict";
+    eval("(for (static of [1]) 2)");
+}, SyntaxError);
+
+(function () {
+    "use strict";
+    eval("[for (await of [1]) 2]");
+    eval("(for (await of [1]) 2)");
+})();
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedArray/set-detached.js
@@ -0,0 +1,240 @@
+// Tests for detached ArrayBuffer checks in %TypedArray%.prototype.set(array|typedArray, offset).
+
+function* createTypedArrays(lengths = [0, 1, 4, 4096]) {
+    for (let length of lengths) {
+        let buffer = new ArrayBuffer(length * Int32Array.BYTES_PER_ELEMENT);
+        let typedArray = new Int32Array(buffer);
+
+        yield {typedArray, buffer};
+    }
+}
+
+if (typeof detachArrayBuffer === "function") {
+    class ExpectedError extends Error {}
+
+    // No detached check on function entry.
+    for (let {typedArray, buffer} of createTypedArrays()) {
+        detachArrayBuffer(buffer);
+
+        assertThrowsInstanceOf(() => typedArray.set(null, {
+            valueOf() {
+                throw new ExpectedError();
+            }
+        }), ExpectedError);
+    }
+
+    // Check for detached buffer after calling ToInteger(offset). Test with:
+    // - valid offset,
+    // - too large offset,
+    // - and negative offset.
+    for (let [offset, error] of [[0, TypeError], [1000000, TypeError], [-1, RangeError]]) {
+        for (let source of [[], [0], new Int32Array(0), new Int32Array(1)]) {
+            for (let {typedArray, buffer} of createTypedArrays()) {
+                assertThrowsInstanceOf(() => typedArray.set(source, {
+                    valueOf() {
+                        detachArrayBuffer(buffer);
+                        return offset;
+                    }
+                }), error);
+            }
+        }
+    }
+
+    // Tests when called with detached typed array as source.
+    for (let {typedArray} of createTypedArrays()) {
+        for (let {typedArray: source, buffer: sourceBuffer} of createTypedArrays()) {
+            detachArrayBuffer(sourceBuffer);
+
+            assertThrowsInstanceOf(() => typedArray.set(source, {
+                valueOf() {
+                    throw new ExpectedError();
+                }
+            }), ExpectedError);
+        }
+    }
+
+    // Check when detaching source buffer in ToInteger(offset). Test with:
+    // - valid offset,
+    // - too large offset,
+    // - and negative offset.
+    for (let [offset, error] of [[0, TypeError], [1000000, TypeError], [-1, RangeError]]) {
+        for (let {typedArray} of createTypedArrays()) {
+            for (let {typedArray: source, buffer: sourceBuffer} of createTypedArrays()) {
+                assertThrowsInstanceOf(() => typedArray.set(source, {
+                    valueOf() {
+                        detachArrayBuffer(sourceBuffer);
+                        return offset;
+                    }
+                }), error);
+            }
+        }
+    }
+
+    // Test when target and source use the same underlying buffer and
+    // ToInteger(offset) detaches the buffer. Test with:
+    // - same typed array,
+    // - different typed array, but same element type,
+    // - and different element type.
+    for (let src of [ta => ta, ta => new Int32Array(ta.buffer), ta => new Float32Array(ta.buffer)]) {
+        for (let {typedArray, buffer} of createTypedArrays()) {
+            let source = src(typedArray);
+            assertThrowsInstanceOf(() => typedArray.set(source, {
+                valueOf() {
+                    detachArrayBuffer(buffer);
+                    return 0;
+                }
+            }), TypeError);
+        }
+    }
+
+    // Test when Get(src, "length") detaches the buffer, but srcLength is 0.
+    // Also use different offsets to ensure bounds checks use the typed array's
+    // length value from before detaching the buffer.
+    for (let offset of [() => 0, ta => Math.min(1, ta.length), ta => Math.max(0, ta.length - 1)]) {
+        for (let {typedArray, buffer} of createTypedArrays()) {
+            let source = {
+                get length() {
+                    detachArrayBuffer(buffer);
+                    return 0;
+                }
+            };
+            typedArray.set(source, offset(typedArray));
+        }
+    }
+
+    // Test when ToLength(Get(src, "length")) detaches the buffer, but
+    // srcLength is 0. Also use different offsets to ensure bounds checks use
+    // the typed array's length value from before detaching the buffer.
+    for (let offset of [() => 0, ta => Math.min(1, ta.length), ta => Math.max(0, ta.length - 1)]) {
+        for (let {typedArray, buffer} of createTypedArrays()) {
+            let source = {
+                length: {
+                    valueOf() {
+                        detachArrayBuffer(buffer);
+                        return 0;
+                    }
+                }
+            };
+            typedArray.set(source, offset(typedArray));
+        }
+    }
+
+    // Test a TypeError is thrown when the typed array is detached and
+    // srcLength > 0.
+    for (let {typedArray, buffer} of createTypedArrays()) {
+        let source = {
+            length: {
+                valueOf() {
+                    detachArrayBuffer(buffer);
+                    return 1;
+                }
+            }
+        };
+        let err = typedArray.length === 0 ? RangeError : TypeError;
+        assertThrowsInstanceOf(() => typedArray.set(source), err);
+    }
+
+    // Same as above, but with side-effect when executing Get(src, "0").
+    for (let {typedArray, buffer} of createTypedArrays()) {
+        let source = {
+            get 0() {
+                throw new ExpectedError();
+            },
+            length: {
+                valueOf() {
+                    detachArrayBuffer(buffer);
+                    return 1;
+                }
+            }
+        };
+        let err = typedArray.length === 0 ? RangeError : ExpectedError;
+        assertThrowsInstanceOf(() => typedArray.set(source), err);
+    }
+
+    // Same as above, but with side-effect when executing ToNumber(Get(src, "0")).
+    for (let {typedArray, buffer} of createTypedArrays()) {
+        let source = {
+            get 0() {
+                return {
+                    valueOf() {
+                        throw new ExpectedError();
+                    }
+                };
+            },
+            length: {
+                valueOf() {
+                    detachArrayBuffer(buffer);
+                    return 1;
+                }
+            }
+        };
+        let err = typedArray.length === 0 ? RangeError : ExpectedError;
+        assertThrowsInstanceOf(() => typedArray.set(source), err);
+    }
+
+    // Side-effects when getting the source elements detach the buffer.
+    for (let {typedArray, buffer} of createTypedArrays()) {
+        let source = Object.defineProperties([], {
+            0: {
+                get() {
+                    detachArrayBuffer(buffer);
+                    return 1;
+                }
+            }
+        });
+        let err = typedArray.length === 0 ? RangeError : TypeError;
+        assertThrowsInstanceOf(() => typedArray.set(source), err);
+    }
+
+    // Side-effects when getting the source elements detach the buffer. Also
+    // ensure other elements aren't accessed.
+    for (let {typedArray, buffer} of createTypedArrays()) {
+        let source = Object.defineProperties([], {
+            0: {
+                get() {
+                    detachArrayBuffer(buffer);
+                    return 1;
+                }
+            },
+            1: {
+                get() {
+                    throw new Error("Unexpected access");
+                }
+            }
+        });
+        let err = typedArray.length <= 1 ? RangeError : TypeError;
+        assertThrowsInstanceOf(() => typedArray.set(source), err);
+    }
+
+    // Side-effects when converting the source elements detach the buffer.
+    for (let {typedArray, buffer} of createTypedArrays()) {
+        let source = [{
+            valueOf() {
+                detachArrayBuffer(buffer);
+                return 1;
+            }
+        }];
+        let err = typedArray.length === 0 ? RangeError : TypeError;
+        assertThrowsInstanceOf(() => typedArray.set(source), err);
+    }
+
+    // Side-effects when converting the source elements detach the buffer. Also
+    // ensure other elements aren't accessed.
+    for (let {typedArray, buffer} of createTypedArrays()) {
+        let source = [{
+            valueOf() {
+                detachArrayBuffer(buffer);
+                return 1;
+            }
+        }, {
+            valueOf() {
+                throw new Error("Unexpected access");
+            }
+        }];
+        let err = typedArray.length <= 1 ? RangeError : TypeError;
+        assertThrowsInstanceOf(() => typedArray.set(source), err);
+    }
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedArray/set-tointeger.js
@@ -0,0 +1,95 @@
+// Test ToInteger conversion in %TypedArray%.prototype.set(array|typedArray, offset).
+
+let ta = new Int32Array(4);
+
+// %TypedArray%.prototype.set has two different implementations for typed array
+// and non-typed array arguments. Test with both input types.
+let emptySources = [[], new Int32Array(0)];
+let nonEmptySource = [[0], new Int32Array(1)];
+let sources = [...emptySources, ...nonEmptySource];
+
+// Test when ToInteger(offset) is in (-1, 4).
+let validOffsets = [
+    // Values in [+0, 4).
+    0,
+    0.1,
+    3,
+    3.9,
+
+    // Values in (-1, -0].
+    -0,
+    -0.1,
+    -0.9,
+
+    NaN,
+
+    // Also include some non-number values.
+    undefined,
+    null,
+    true,
+    "",
+    "3",
+    "  1\t\n",
+    "some string",
+    {valueOf() { return 2; }},
+];
+
+for (let offset of validOffsets) {
+    for (let source of sources) {
+        ta.set(source, offset);
+    }
+}
+
+// Test when ToInteger(offset) isn't in (-1, 4).
+let invalidOffsets = [
+    // Values exceeding the typed array's length.
+    5,
+    2147483647,
+    2147483648,
+    2147483649,
+    4294967295,
+    4294967296,
+    4294967297,
+    Infinity,
+
+    // Negative values.
+    -1,
+    -1.1,
+    -2147483647,
+    -2147483648,
+    -2147483649,
+    -4294967295,
+    -4294967296,
+    -4294967297,
+    -Infinity,
+
+    // Also include some non-number values.
+    "8",
+    "Infinity",
+    "  Infinity  ",
+    {valueOf() { return 10; }},
+];
+
+for (let offset of invalidOffsets) {
+    for (let source of sources) {
+        assertThrowsInstanceOf(() => ta.set(source, offset), RangeError);
+    }
+}
+
+// Test when ToInteger(offset) is in [4, 5).
+for (let source of emptySources) {
+    ta.set(source, 4);
+    ta.set(source, 4.9);
+}
+for (let source of nonEmptySource) {
+    assertThrowsInstanceOf(() => ta.set(source, 4), RangeError);
+    assertThrowsInstanceOf(() => ta.set(source, 4.9), RangeError);
+}
+
+// ToInteger(symbol value) throws a TypeError.
+for (let source of sources) {
+    assertThrowsInstanceOf(() => ta.set(source, Symbol()), TypeError);
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedArray/set-toobject.js
@@ -0,0 +1,53 @@
+// Test ToObject in %TypedArray%.prototype.set(array|typedArray, offset).
+
+let ta = new Int32Array(4);
+
+for (let nullOrUndefined of [null, undefined]) {
+    // ToObject(array) throws a TypeError when |array| is null or undefined.
+    assertThrowsInstanceOf(() => ta.set(nullOrUndefined), TypeError);
+
+    // ToInteger(offset) is called before ToObject(array).
+    class ExpectedError extends Error {}
+    assertThrowsInstanceOf(() => ta.set(nullOrUndefined, {
+        valueOf() {
+            throw new ExpectedError();
+        }
+    }), ExpectedError);
+}
+
+// Ensure ta is still initialized with zeros.
+assertEqArray(ta, [0, 0, 0, 0]);
+
+// %TypedArray%.prototype.set can be called with a string primitive values.
+ta.set("");
+assertEqArray(ta, [0, 0, 0, 0]);
+
+ta.set("123");
+assertEqArray(ta, [1, 2, 3, 0]);
+
+// Throws a RangeError if the length is too large.
+assertThrowsInstanceOf(() => ta.set("456789"), RangeError);
+assertEqArray(ta, [1, 2, 3, 0]);
+
+// When called with other primitive values the typed array contents don't
+// change since ToObject(<primitive>).length is zero, i.e. the source object is
+// treated the same as an empty array.
+for (let value of [true, false, 0, NaN, 123, Infinity, Symbol()]) {
+    ta.set(value);
+    assertEqArray(ta, [1, 2, 3, 0]);
+}
+
+// Repeat test from above when the primitive wrapper prototype has been changed
+// to include "length" and an indexed property.
+Number.prototype.length = 4;
+Number.prototype[3] = -1;
+try {
+    ta.set(456);
+    assertEqArray(ta, [0, 0, 0, -1]);
+} finally {
+    delete Number.prototype.length;
+    delete Number.prototype[3];
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/tests/js1_8_5/extensions/typedarray-set-neutering.js
+++ b/js/src/tests/js1_8_5/extensions/typedarray-set-neutering.js
@@ -30,17 +30,17 @@ var src = [ 10, 20, 30, 40,
 Object.defineProperty(src, 4, {
   get: function () {
     detachArrayBuffer(ab);
     gc();
     return 200;
   }
 });
 
-a.set(src);
+assertThrowsInstanceOf(() => a.set(src), TypeError);
 
 // Not really needed
 Array.reverse(a_2);
 
 /******************************************************************************/
 
 if (typeof reportCompare === "function")
   reportCompare(true, true);
--- a/js/src/tests/jstests.list
+++ b/js/src/tests/jstests.list
@@ -432,16 +432,17 @@ skip script test262/language/expressions
 skip script test262/intl402/Collator/10.1.1_1.js
 skip script test262/intl402/NumberFormat/11.1.1_1.js
 skip script test262/intl402/DateTimeFormat/12.1.1_1.js
 
 # Waiting on https://github.com/tc39/ecma402/issues/122
 skip script test262/intl402/DateTimeFormat/prototype/12.3_a.js
 skip script test262/intl402/Collator/prototype/10.3_a.js
 skip script test262/intl402/NumberFormat/prototype/11.3_a.js
+skip script test262/intl402/PluralRules/prototype/prototype.js
 
 # https://bugzilla.mozilla.org/show_bug.cgi?id=1317378
 skip script test262/language/statements/do-while/cptn-abrupt-empty.js
 skip script test262/language/statements/do-while/cptn-normal.js
 skip script test262/language/statements/for-in/cptn-decl-abrupt-empty.js
 skip script test262/language/statements/for-in/cptn-decl-itr.js
 skip script test262/language/statements/for-in/cptn-decl-skip-itr.js
 skip script test262/language/statements/for-in/cptn-decl-zero-itr.js
@@ -543,37 +544,16 @@ skip script test262/language/expressions
 skip script test262/language/expressions/async-generators/expression-await-as-yield-operand.js
 skip script test262/language/expressions/async-generators/expression-yield-newline.js
 skip script test262/language/expressions/async-generators/expression-await-promise-as-yield-operand.js
 skip script test262/language/expressions/async-generators/expression-yield-star-before-newline.js
 
 # https://bugzilla.mozilla.org/show_bug.cgi?id=1050755
 skip script test262/built-ins/Date/UTC/return-value.js
 
-# PluralRules.
-skip script test262/intl402/PluralRules/builtin.js
-skip script test262/intl402/PluralRules/this-not-ignored.js
-skip script test262/intl402/PluralRules/name.js
-skip script test262/intl402/PluralRules/length.js
-skip script test262/intl402/PluralRules/supportedLocalesOf/main.js
-skip script test262/intl402/PluralRules/can-be-subclassed.js
-skip script test262/intl402/PluralRules/internals.js
-skip script test262/intl402/PluralRules/prototype/properties.js
-skip script test262/intl402/PluralRules/supportedLocalesOf/supportedLocalesOf.js
-skip script test262/intl402/PluralRules/supportedLocalesOf/name.js
-skip script test262/intl402/PluralRules/supportedLocalesOf/arguments.js
-skip script test262/intl402/PluralRules/prototype/builtins.js
-skip script test262/intl402/PluralRules/prototype/prototype.js
-skip script test262/intl402/PluralRules/prototype/bind.js
-skip script test262/intl402/PluralRules/prototype/resolvedOptions/name.js
-skip script test262/intl402/PluralRules/prototype/resolvedOptions/properties.js
-skip script test262/intl402/PluralRules/prototype/resolvedOptions/builtins.js
-skip script test262/intl402/PluralRules/prototype/constructor/main.js
-skip script test262/intl402/PluralRules/prototype/select/name.js
-
 # SIMD.
 skip script test262/built-ins/Simd/check.js
 skip script test262/built-ins/Simd/from.js
 skip script test262/built-ins/Simd/operators.js
 skip script test262/built-ins/Simd/replace_lane.js
 skip script test262/built-ins/Simd/shuffle.js
 skip script test262/built-ins/Simd/swizzle.js
 
new file mode 100644
--- /dev/null
+++ b/js/src/tests/test262-intl-extras.js
@@ -0,0 +1,7 @@
+// 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/.
+
+// Call the shell helper to add experimental features to the Intl object.
+if (typeof addIntlExtras === "function")
+    addIntlExtras(Intl);
--- a/js/src/tests/test262-update.py
+++ b/js/src/tests/test262-update.py
@@ -47,18 +47,18 @@ def tryParseTestFile(test262parser, sour
     error occured.
 
     See <https://github.com/tc39/test262/blob/master/INTERPRETING.md> for an
     overview of the returned test attributes.
     """
     try:
         return test262parser.parseTestRecord(source, testName)
     except Exception as err:
-        # TODO: Report erroneous files to test262 Github repository.
-        # print("Error '%s' in file: %s" % (err, testName), file=sys.stderr)
+        print("Error '%s' in file: %s" % (err, testName), file=sys.stderr)
+        print("Please report this error to the test262 GitHub repository!")
         return None
 
 def makeRefTestLine(refTest):
     """
     Creates the |reftest| entry from the input list. Or None if no reftest
     entry is required.
     """
 
@@ -104,17 +104,17 @@ def writeTestFile(test262OutDir, testFil
 
     with io.open(os.path.join(test262OutDir, testFileName), "wb") as output:
         output.write(source)
 
 def addSuffixToFileName(fileName, suffix):
     (filePath, ext) = os.path.splitext(fileName)
     return filePath + suffix + ext
 
-def writeShellAndBrowserFiles(test262OutDir, harnessDir, includesMap, relPath, helperFiles=[]):
+def writeShellAndBrowserFiles(test262OutDir, harnessDir, includesMap, localIncludesMap, relPath):
     """
     Generate the shell.js and browser.js files for the test harness.
     """
 
     # Find all includes from parent directories.
     def findParentIncludes(relPath, includesMap):
         parentIncludes = set()
         current = relPath
@@ -132,23 +132,25 @@ def writeShellAndBrowserFiles(test262Out
         for include in includeSet:
             if include not in parentIncludes:
                 yield include
 
     def readIncludeFile(filePath):
         with io.open(filePath, "rb") as includeFile:
             return "// file: %s\n%s" % (os.path.basename(filePath), includeFile.read())
 
+    localIncludes = localIncludesMap[relPath] if relPath in localIncludesMap else []
+
     # Concatenate all includes files.
     includeSource = "\n".join(imap(readIncludeFile, chain(
         # The requested include files.
         imap(partial(os.path.join, harnessDir), findIncludes(includesMap, relPath)),
 
-        # And additional local helper files.
-        imap(partial(os.path.join, os.getcwd()), helperFiles)
+        # And additional local include files.
+        imap(partial(os.path.join, os.getcwd()), localIncludes)
     )))
 
     # Write the concatenated include sources to shell.js.
     with io.open(os.path.join(test262OutDir, relPath, "shell.js"), "wb") as shellFile:
         shellFile.write(includeSource)
 
     # The browser.js file is always empty for test262 tests.
     with io.open(os.path.join(test262OutDir, relPath, "browser.js"), "wb") as browserFile:
@@ -187,29 +189,27 @@ def convertTestFile(test262parser, testS
     noStrict = "noStrict" in testRec
 
     # The "raw" attribute is used in the default test262 runner to prevent
     # prepending additional content (use-strict directive, harness files)
     # before the actual test source code. We can probably ignore it.
     raw = "raw" in testRec
     assert not (raw and (onlyStrict or noStrict))
 
-    # Most async tests are marked with the "async" attribute. But fallback to
-    # detect async tests when $DONE is present in the test source code.
-    async = "async" in testRec or "$DONE" in testSource
+    # Async tests are marked with the "async" attribute. It is an error for a
+    # test to use the $DONE function without specifying the "async" attribute.
+    async = "async" in testRec
+    assert "$DONE" not in testSource or async
 
-    # Most negative tests use {type=<error name>, phase=early|runtime}, except
-    # those which weren't update. These are still using negative=<error name>.
-    if "negative" in testRec:
-        negative = testRec["negative"]
-        # errorType is currently ignored. :-(
-        errorType = negative["type"] if type(negative) == dict else negative
-        isNegative = True
-    else:
-        isNegative = False
+    # Negative tests have additional meta-data to specify the error type and
+    # when the error is issued (runtime error or early parse error). We're
+    # currently ignoring this additional meta-data.
+    # testRec["negative"] == {type=<error name>, phase=early|runtime}
+    isNegative = "negative" in testRec
+    assert not isNegative or type(testRec["negative"]) == dict
 
     # Skip non-test files.
     isSupportFile = fileNameEndsWith(testName, "FIXTURE")
     if isSupportFile:
         refTestSkip.append("not a test file")
 
     # Skip all module tests.
     if "module" in testRec or pathStartsWith(testName, "language", "module-code"):
@@ -231,16 +231,20 @@ def convertTestFile(test262parser, testS
     if "includes" in testRec:
         assert not raw, "Raw test with includes: %s" % testName
         includeSet.update(testRec["includes"])
 
     # Skip intl402 tests when Intl isn't available.
     if pathStartsWith(testName, "intl402"):
         refTestSkipIf.append(("!this.hasOwnProperty('Intl')", "needs Intl"))
 
+    # Skip Intl.PluralRules tests when the addIntlExtras helper isn't available.
+    if pathStartsWith(testName, "intl402", "PluralRules"):
+        refTestSkipIf.append(("!this.hasOwnProperty('addIntlExtras')", "needs addIntlExtras"))
+
     # Skip built-ins/Simd tests when SIMD isn't available.
     if pathStartsWith(testName, "built-ins", "Simd"):
         refTestSkipIf.append(("!this.hasOwnProperty('SIMD')", "needs SIMD"))
 
     # Add reportCompare() after all positive, synchronous tests.
     if not isNegative and not async:
         testEpilogue = """
 reportCompare(0, 0);
@@ -270,38 +274,50 @@ def process_test262(test262Dir, test262O
     """
     Process all test262 files and converts them into jstests compatible tests.
     """
 
     harnessDir = os.path.join(test262Dir, "harness")
     testDir = os.path.join(test262Dir, "test")
     test262parser = loadTest262Parser(test262Dir)
 
+    # Map of test262 subdirectories to the set of include files required for
+    # tests in that subdirectory. The includes for all tests in a subdirectory
+    # are merged into a single shell.js.
     # map<dirname, set<includeFiles>>
     includesMap = {}
 
-    # Root directory contains required harness files.
+    # Additional local includes keyed by test262 directory names. The include
+    # files in this map must be located in the js/src/tests directory.
+    # map<dirname, list<includeFiles>>
+    localIncludesMap = {}
+
+    # The root directory contains required harness files and test262-host.js.
     includesMap[""] = set(["sta.js", "assert.js"])
+    localIncludesMap[""] = ["test262-host.js"]
 
     # Also add files known to be used by many tests to the root shell.js file.
     includesMap[""].update(["propertyHelper.js", "compareArray.js"])
 
-    # Add "test262-host.js" to the root shell.js file.
-    writeShellAndBrowserFiles(test262OutDir, harnessDir, includesMap, "", ["test262-host.js"])
+    # Write the root shell.js file.
+    writeShellAndBrowserFiles(test262OutDir, harnessDir, includesMap, localIncludesMap, "")
 
     # Additional explicit includes inserted at well-chosen locations to reduce
     # code duplication in shell.js files.
     explicitIncludes = {}
     explicitIncludes["intl402"] = ["testBuiltInObject.js"]
     explicitIncludes[os.path.join("built-ins", "DataView")] = ["byteConversionValues.js"]
     explicitIncludes[os.path.join("built-ins", "Promise")] = ["PromiseHelper.js"]
     explicitIncludes[os.path.join("built-ins", "TypedArray")] = ["byteConversionValues.js",
         "detachArrayBuffer.js", "nans.js"]
     explicitIncludes[os.path.join("built-ins", "TypedArrays")] = ["detachArrayBuffer.js"]
 
+    # Intl.PluralRules isn't yet enabled by default.
+    localIncludesMap[os.path.join("intl402", "PluralRules")] = ["test262-intl-extras.js"]
+
     # Process all test directories recursively.
     for (dirPath, dirNames, fileNames) in os.walk(testDir):
         relPath = os.path.relpath(dirPath, testDir)
         if relPath == ".":
             continue
         os.makedirs(os.path.join(test262OutDir, relPath))
 
         includeSet = set()
@@ -318,17 +334,17 @@ def process_test262(test262Dir, test262O
             # Read the original test source and preprocess it for the jstests harness.
             with io.open(filePath, "rb") as testFile:
                 testSource = testFile.read()
 
             for (newFileName, newSource) in convertTestFile(test262parser, testSource, testName, includeSet, strictTests):
                 writeTestFile(test262OutDir, newFileName, newSource)
 
         # Add shell.js and browers.js files for the current directory.
-        writeShellAndBrowserFiles(test262OutDir, harnessDir, includesMap, relPath)
+        writeShellAndBrowserFiles(test262OutDir, harnessDir, includesMap, localIncludesMap, relPath)
 
 def update_test262(args):
     import subprocess
 
     url = args.url
     branch = args.branch
     revision = args.revision
     outDir = args.out
--- a/js/src/tests/test262/intl402/PluralRules/builtin.js
+++ b/js/src/tests/test262/intl402/PluralRules/builtin.js
@@ -1,9 +1,9 @@
-// |reftest| skip-if(!this.hasOwnProperty('Intl')) -- needs Intl
+// |reftest| skip-if(!this.hasOwnProperty('Intl')||!this.hasOwnProperty('addIntlExtras')) -- needs Intl, needs addIntlExtras
 // Copyright 2016 Mozilla Corporation. All rights reserved.
 // This code is governed by the license found in the LICENSE file.
 
 /*---
 esid: sec-Intl.PluralRules
 description: >
     Tests that Intl.PluralRules meets the requirements for
     built-in objects defined by the introduction of chapter 17 of the
--- a/js/src/tests/test262/intl402/PluralRules/can-be-subclassed.js
+++ b/js/src/tests/test262/intl402/PluralRules/can-be-subclassed.js
@@ -1,9 +1,9 @@
-// |reftest| skip-if(!this.hasOwnProperty('Intl')) -- needs Intl
+// |reftest| skip-if(!this.hasOwnProperty('Intl')||!this.hasOwnProperty('addIntlExtras')) -- needs Intl, needs addIntlExtras
 // Copyright 2016 Mozilla Corporation. All rights reserved.
 // This code is governed by the license found in the LICENSE file.
 
 /*---
 esid: sec-intl-pluralrules-constructor
 description: Tests that Intl.PluralRules can be subclassed.
 author: Zibi Braniecki
 includes: [testIntl.js]
--- a/js/src/tests/test262/intl402/PluralRules/internals.js
+++ b/js/src/tests/test262/intl402/PluralRules/internals.js
@@ -1,9 +1,9 @@
-// |reftest| skip-if(!this.hasOwnProperty('Intl')) -- needs Intl
+// |reftest| skip-if(!this.hasOwnProperty('Intl')||!this.hasOwnProperty('addIntlExtras')) -- needs Intl, needs addIntlExtras
 // Copyright 2016 Mozilla Corporation. All rights reserved.
 // This code is governed by the license found in the LICENSE file.
 
 /*---
 esid: sec-intl-pluralrules-constructor
 description: >
     Tests that objects constructed by Intl.PluralRules have the specified
     internal properties.
--- a/js/src/tests/test262/intl402/PluralRules/length.js
+++ b/js/src/tests/test262/intl402/PluralRules/length.js
@@ -1,9 +1,9 @@
-// |reftest| skip-if(!this.hasOwnProperty('Intl')) -- needs Intl
+// |reftest| skip-if(!this.hasOwnProperty('Intl')||!this.hasOwnProperty('addIntlExtras')) -- needs Intl, needs addIntlExtras
 // Copyright 2016 Mozilla Corporation. All rights reserved.
 // This code is governed by the license found in the LICENSE file.
 
 /*---
 esid: sec-Intl.PluralRules