Merge inbound to central, a=merge central
authorWes Kocher <wkocher@mozilla.com>
Fri, 24 Mar 2017 17:17:27 -0700
changeset 388493 65b0ac174753b22c01156d72fb42d2abd3176dd1
parent 388492 f9acfdca68a45c8cecf54f5a1bfc3b2a4baa52a3 (current diff)
parent 388370 d56fb9484300ffbcb770709f14697faa314b29e6 (diff)
child 388494 3c112ab6ab3042291701e01d1cfe57de82c4b3e8
child 388520 2ecf610d3185de940cb7b4ed2c214dc9e2367cee
child 388530 4fb935383ebf8a568076c621c16f764a12021460
push idunknown
push userunknown
push dateunknown
reviewersmerge
milestone55.0a1
Merge inbound to central, a=merge MozReview-Commit-ID: JGfQoBJy2jt
browser/base/content/tabbrowser.xml
browser/components/sessionstore/test/browser.ini
taskcluster/ci/test/tests.yml
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -693,22 +693,18 @@ var ClickEventHandler = {
 };
 ClickEventHandler.init();
 
 ContentLinkHandler.init(this);
 
 // TODO: Load this lazily so the JSM is run only if a relevant event/message fires.
 var pluginContent = new PluginContent(global);
 
-addEventListener("DOMWebNotificationClicked", function(event) {
-  sendAsyncMessage("DOMWebNotificationClicked", {});
-}, false);
-
-addEventListener("DOMServiceWorkerFocusClient", function(event) {
-  sendAsyncMessage("DOMServiceWorkerFocusClient", {});
+addEventListener("DOMWindowFocus", function(event) {
+  sendAsyncMessage("DOMWindowFocus", {});
 }, false);
 
 ContentWebRTC.init();
 addMessageListener("rtcpeer:Allow", ContentWebRTC);
 addMessageListener("rtcpeer:Deny", ContentWebRTC);
 addMessageListener("webrtc:Allow", ContentWebRTC);
 addMessageListener("webrtc:Deny", ContentWebRTC);
 addMessageListener("webrtc:StopSharing", ContentWebRTC);
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -2700,17 +2700,17 @@
               // than closing the window right away. If the caller opts in, take
               // the fast path.
               if (closeWindow &&
                   aCloseWindowFastpath &&
                   this._removingTabs.length == 0) {
                 // This call actually closes the window, unless the user
                 // cancels the operation.  We are finished here in both cases.
                 this._windowIsClosing = window.closeWindow(true, window.warnAboutClosingWindow);
-                return null;
+                return false;
               }
 
               newTab = true;
             }
 
             aTab.closing = true;
             this._removingTabs.push(aTab);
             this._visibleTabs = null; // invalidate cache
@@ -5002,18 +5002,17 @@
                                           parentAllowsMixedContent: data.parentAllowsMixedContent,
                                           userContextId: data.userContextId,
                                         };
               let popup = browser.ownerDocument.getElementById("contentAreaContextMenu");
               let event = gContextMenuContentData.event;
               popup.openPopupAtScreen(event.screenX, event.screenY, true);
               break;
             }
-            case "DOMServiceWorkerFocusClient":
-            case "DOMWebNotificationClicked": {
+            case "DOMWindowFocus": {
               let tab = this.getTabForBrowser(browser);
               if (!tab)
                 return undefined;
               this.selectedTab = tab;
               window.focus();
               break;
             }
             case "Browser:Init": {
@@ -5235,18 +5234,17 @@
 
             // If this window has remote tabs, switch to our tabpanels fork
             // which does asynchronous tab switching.
             this.mPanelContainer.classList.add("tabbrowser-tabpanels");
           } else {
             this._outerWindowIDBrowserMap.set(this.mCurrentBrowser.outerWindowID,
                                               this.mCurrentBrowser);
           }
-          messageManager.addMessageListener("DOMWebNotificationClicked", this);
-          messageManager.addMessageListener("DOMServiceWorkerFocusClient", this);
+          messageManager.addMessageListener("DOMWindowFocus", this);
           messageManager.addMessageListener("RefreshBlocker:Blocked", this);
           messageManager.addMessageListener("Browser:WindowCreated", this);
 
           // To correctly handle keypresses for potential FindAsYouType, while
           // the tab's find bar is not yet initialized.
           this._findAsYouType = Services.prefs.getBoolPref("accessibility.typeaheadfind");
           Services.prefs.addObserver("accessibility.typeaheadfind", this, false);
           messageManager.addMessageListener("Findbar:Keypress", this);
--- a/browser/components/sessionstore/test/browser.ini
+++ b/browser/components/sessionstore/test/browser.ini
@@ -68,16 +68,17 @@ support-files =
 [browser_aboutPrivateBrowsing.js]
 [browser_aboutSessionRestore.js]
 [browser_async_duplicate_tab.js]
 [browser_async_flushes.js]
 run-if = e10s && crashreporter
 skip-if = debug # bug 1167933
 [browser_async_remove_tab.js]
 run-if = e10s
+skip-if = debug # bug 1211084
 [browser_attributes.js]
 [browser_backup_recovery.js]
 [browser_broadcast.js]
 [browser_capabilities.js]
 [browser_cleaner.js]
 [browser_cookies.js]
 [browser_crashedTabs.js]
 skip-if = !e10s || !crashreporter
--- 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.367
+Current extension version is: 1.7.381
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -3662,18 +3662,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.367';
-exports.build = 'f0c45f03';
+exports.version = '1.7.381';
+exports.build = '68f2bf3b';
 exports.getDocument = getDocument;
 exports.PDFDataRangeTransport = PDFDataRangeTransport;
 exports.PDFWorker = PDFWorker;
 exports.PDFDocumentProxy = PDFDocumentProxy;
 exports.PDFPageProxy = PDFPageProxy;
 exports._UnsupportedManager = _UnsupportedManager;
 
 /***/ }),
@@ -4680,18 +4680,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.367';
-PDFJS.build = 'f0c45f03';
+PDFJS.version = '1.7.381';
+PDFJS.build = '68f2bf3b';
 PDFJS.pdfBug = false;
 if (PDFJS.verbosity !== undefined) {
  sharedUtil.setVerbosityLevel(PDFJS.verbosity);
 }
 delete PDFJS.verbosity;
 Object.defineProperty(PDFJS, 'verbosity', {
  get: function () {
   return sharedUtil.getVerbosityLevel();
@@ -7181,18 +7181,18 @@ exports.TilingPattern = TilingPattern;
 
 
 /***/ }),
 /* 14 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
-var pdfjsVersion = '1.7.367';
-var pdfjsBuild = 'f0c45f03';
+var pdfjsVersion = '1.7.381';
+var pdfjsBuild = '68f2bf3b';
 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
@@ -26011,17 +26011,17 @@ var PartialEvaluator = function PartialE
       }
       var xobj = xobjs.get(name);
       if (!xobj) {
        break;
       }
       assert(isStream(xobj), 'XObject should be a stream');
       var type = xobj.dict.get('Subtype');
       assert(isName(type), 'XObject should have a Name subtype');
-      if ('Form' !== type.name) {
+      if (type.name !== 'Form') {
        xobjsCache.key = name;
        xobjsCache.texts = null;
        break;
       }
       stateManager.save();
       var matrix = xobj.dict.getArray('Matrix');
       if (isArray(matrix) && matrix.length === 6) {
        stateManager.transform(matrix);
@@ -27772,17 +27772,17 @@ var JpxImage = function JpxImageClosure(
      } else if (method === 2) {
       info('ICC profile not supported');
      }
      break;
     case 0x6A703263:
      this.parseCodestream(data, position, position + dataLength);
      break;
     case 0x6A502020:
-     if (0x0d0a870a !== readUint32(data, position)) {
+     if (readUint32(data, position) !== 0x0d0a870a) {
       warn('Invalid JP2 signature');
      }
      break;
     case 0x6A501A1A:
     case 0x66747970:
     case 0x72726571:
     case 0x72657320:
     case 0x69686472:
@@ -29879,16 +29879,19 @@ var Catalog = function CatalogClosure() 
    var metadata;
    if (stream && isDict(stream.dict)) {
     var type = stream.dict.get('Type');
     var subtype = stream.dict.get('Subtype');
     if (isName(type, 'Metadata') && isName(subtype, 'XML')) {
      try {
       metadata = stringToUTF8String(bytesToString(stream.getBytes()));
      } catch (e) {
+      if (e instanceof MissingDataException) {
+       throw e;
+      }
       info('Skipping invalid metadata.');
      }
     }
    }
    return shadow(this, 'metadata', metadata);
   },
   get toplevelPagesDict() {
    var pagesObj = this.catDict.get('Pages');
@@ -37751,16 +37754,19 @@ var PDFDocument = function PDFDocumentCl
     if (this.acroForm) {
      this.xfa = this.acroForm.get('XFA');
      var fields = this.acroForm.get('Fields');
      if ((!fields || !isArray(fields) || fields.length === 0) && !this.xfa) {
       this.acroForm = null;
      }
     }
    } catch (ex) {
+    if (ex instanceof MissingDataException) {
+     throw ex;
+    }
     info('Something wrong with AcroForm entry');
     this.acroForm = null;
    }
   },
   get linearization() {
    var linearization = null;
    if (this.stream.length) {
     try {
@@ -37864,16 +37870,19 @@ var PDFDocument = function PDFDocumentCl
     PDFFormatVersion: this.pdfFormatVersion,
     IsAcroFormPresent: !!this.acroForm,
     IsXFAPresent: !!this.xfa
    };
    var infoDict;
    try {
     infoDict = this.xref.trailer.get('Info');
    } catch (err) {
+    if (err instanceof MissingDataException) {
+     throw err;
+    }
     info('The document information dictionary is invalid.');
    }
    if (infoDict) {
     var validEntries = DocumentInfoValidators.entries;
     for (var key in validEntries) {
      if (infoDict.has(key)) {
       var value = infoDict.get(key);
       if (validEntries[key](value)) {
@@ -43365,16 +43374,17 @@ exports.Jbig2Image = Jbig2Image;
 
 /***/ }),
 /* 28 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 var sharedUtil = __w_pdfjs_require__(0);
+var warn = sharedUtil.warn;
 var error = sharedUtil.error;
 var JpegImage = function JpegImageClosure() {
  var dctZigZag = new Uint8Array([
   0,
   1,
   8,
   16,
   9,
@@ -43687,33 +43697,31 @@ var JpegImage = function JpegImageClosur
   }
   var mcu = 0, marker;
   var mcuExpected;
   if (componentsLength === 1) {
    mcuExpected = components[0].blocksPerLine * components[0].blocksPerColumn;
   } else {
    mcuExpected = mcusPerLine * frame.mcusPerColumn;
   }
-  if (!resetInterval) {
-   resetInterval = mcuExpected;
-  }
   var h, v;
   while (mcu < mcuExpected) {
+   var mcuToRead = resetInterval ? Math.min(mcuExpected - mcu, resetInterval) : mcuExpected;
    for (i = 0; i < componentsLength; i++) {
     components[i].pred = 0;
    }
    eobrun = 0;
    if (componentsLength === 1) {
     component = components[0];
-    for (n = 0; n < resetInterval; n++) {
+    for (n = 0; n < mcuToRead; n++) {
      decodeBlock(component, decodeFn, mcu);
      mcu++;
     }
    } else {
-    for (n = 0; n < resetInterval; n++) {
+    for (n = 0; n < mcuToRead; n++) {
      for (i = 0; i < componentsLength; i++) {
       component = components[i];
       h = component.h;
       v = component.v;
       for (j = 0; j < v; j++) {
        for (k = 0; k < h; k++) {
         decodeMcu(component, decodeFn, mcu, j, k);
        }
@@ -43905,18 +43913,33 @@ var JpegImage = function JpegImageClosur
  JpegImage.prototype = {
   parse: function parse(data) {
    function readUint16() {
     var value = data[offset] << 8 | data[offset + 1];
     offset += 2;
     return value;
    }
    function readDataBlock() {
+    function isValidMarkerAt(pos) {
+     if (pos < data.length - 1) {
+      return data[pos] === 0xFF && data[pos + 1] >= 0xC0 && data[pos + 1] <= 0xFE;
+     }
+     return true;
+    }
     var length = readUint16();
-    var array = data.subarray(offset, offset + length - 2);
+    var endOffset = offset + length - 2;
+    if (!isValidMarkerAt(endOffset)) {
+     warn('readDataBlock - incorrect length, next marker is: ' + (data[endOffset] << 8 | data[endOffset + 1]).toString('16'));
+     var pos = offset;
+     while (!isValidMarkerAt(pos)) {
+      pos++;
+     }
+     endOffset = pos;
+    }
+    var array = data.subarray(offset, endOffset);
     offset += array.length;
     return array;
    }
    function prepareComponents(frame) {
     var mcusPerLine = Math.ceil(frame.samplesPerLine / 8 / frame.maxH);
     var mcusPerColumn = Math.ceil(frame.scanLines / 8 / frame.maxV);
     for (var i = 0; i < frame.components.length; i++) {
      component = frame.components[i];
@@ -49127,17 +49150,17 @@ exports.Type1Parser = Type1Parser;
 
 
 /***/ }),
 /* 36 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
-var pdfjsVersion = '1.7.367';
-var pdfjsBuild = 'f0c45f03';
+var pdfjsVersion = '1.7.381';
+var pdfjsBuild = '68f2bf3b';
 var pdfjsCoreWorker = __w_pdfjs_require__(17);
 ;
 exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
 
 /***/ })
 /******/ ]);
 });
\ No newline at end of file
--- a/devtools/client/locales/en-US/debugger.properties
+++ b/devtools/client/locales/en-US/debugger.properties
@@ -384,21 +384,16 @@ watchExpressions.refreshButton=Refresh
 # search prompt. e.g. cmd+p to search for files. On windows, it's ctrl, on
 # a mac we use the unicode character.
 welcome.search=%S to search for sources
 
 # LOCALIZATION NOTE (sourceSearch.search): The center pane Source Search
 # prompt for searching for files.
 sourceSearch.search=Search Sources…
 
-# LOCALIZATION NOTE (sourceSearch.search): The center pane Source Search
-# prompt for searching for files.
-# Used in the old debugger fronted
-sourceSearch.search=Search…
-
 # LOCALIZATION NOTE (sourceSearch.noResults): The center pane Source Search
 # message when the query did not match any of the sources.
 sourceSearch.noResults=No files matching %S found
 
 # LOCALIZATION NOTE (sourceFooter.debugBtnTooltip): Tooltip text associated
 # with the pretty-print button
 sourceFooter.debugBtnTooltip=Prettify Source
 
--- a/devtools/client/netmonitor/test/browser_net_curl-utils.js
+++ b/devtools/client/netmonitor/test/browser_net_curl-utils.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 /**
  * Tests Curl Utils functionality.
  */
 
-const { CurlUtils } = require("devtools/client/shared/curl");
+const { Curl, CurlUtils } = require("devtools/client/shared/curl");
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(CURL_UTILS_URL);
   info("Starting test... ");
 
   let { gStore, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
   let { getSortedRequests } = windowRequire("devtools/client/netmonitor/selectors/index");
@@ -34,16 +34,17 @@ add_task(function* () {
   };
 
   let data = yield createCurlData(requests.get, getLongString);
   testFindHeader(data);
 
   data = yield createCurlData(requests.post, getLongString);
   testIsUrlEncodedRequest(data);
   testWritePostDataTextParams(data);
+  testDataArgumentOnGeneratedCommand(data);
 
   data = yield createCurlData(requests.multipart, getLongString);
   testIsMultipartRequest(data);
   testGetMultipartBoundary(data);
   testMultiPartHeaders(data);
   testRemoveBinaryDataFromMultipartText(data);
 
   data = yield createCurlData(requests.multipartForm, getLongString);
@@ -95,16 +96,22 @@ function testMultiPartHeaders(data) {
 }
 
 function testWritePostDataTextParams(data) {
   let params = CurlUtils.writePostDataTextParams(data.postDataText);
   is(params, "param1=value1&param2=value2&param3=value3",
     "Should return a serialized representation of the request parameters");
 }
 
+function testDataArgumentOnGeneratedCommand(data) {
+  let curlCommand = Curl.generateCommand(data);
+  ok(curlCommand.includes("--data"),
+    "Should return a curl command with --data");
+}
+
 function testGetMultipartBoundary(data) {
   let boundary = CurlUtils.getMultipartBoundary(data);
   ok(/-{3,}\w+/.test(boundary),
     "A boundary string should be found in a multipart request.");
 }
 
 function testRemoveBinaryDataFromMultipartText(data) {
   let generatedBoundary = CurlUtils.getMultipartBoundary(data);
--- a/devtools/client/shared/curl.js
+++ b/devtools/client/shared/curl.js
@@ -71,17 +71,18 @@ const Curl = {
     // Add URL.
     command.push(escapeString(data.url));
 
     let postDataText = null;
     let multipartRequest = utils.isMultipartRequest(data);
 
     // Create post data.
     let postData = [];
-    if (utils.isUrlEncodedRequest(data) || data.method == "PUT") {
+    if (utils.isUrlEncodedRequest(data) ||
+          ["PUT", "POST"].includes(data.method)) {
       postDataText = data.postDataText;
       postData.push("--data");
       postData.push(escapeString(utils.writePostDataTextParams(postDataText)));
       ignoredHeaders.add("Content-Length");
     } else if (multipartRequest) {
       postDataText = data.postDataText;
       postData.push("--data-binary");
       let boundary = utils.getMultipartBoundary(data);
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -10194,16 +10194,29 @@ nsDocShell::InternalLoad(nsIURI* aURI,
         // helper-app style protocol (ie. mailto://)
         //
         rv = NS_OK;
       } else if (isNewWindow) {
         // XXX: Once new windows are created hidden, the new
         //      window will need to be made visible...  For now,
         //      do nothing.
       }
+
+      // Switch to target tab if we're currently focused window.
+      // Take loadDivertedInBackground into account so the behavior would be
+      // the same as how the tab first opened.
+      bool isTargetActive = false;
+      targetDocShell->GetIsActive(&isTargetActive);
+      if (mIsActive && !isTargetActive &&
+          !Preferences::GetBool("browser.tabs.loadDivertedInBackground", false)) {
+        if (NS_FAILED(nsContentUtils::DispatchFocusChromeEvent(
+            targetDocShell->GetWindow()))) {
+          return NS_ERROR_FAILURE;
+        }
+      }
     }
 
     // Else we ran out of memory, or were a popup and got blocked,
     // or something.
 
     return rv;
   }
 
--- a/dom/base/WebSocket.h
+++ b/dom/base/WebSocket.h
@@ -7,16 +7,17 @@
 #ifndef WebSocket_h__
 #define WebSocket_h__
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/dom/WebSocketBinding.h" // for BinaryType
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/ErrorResult.h"
+#include "mozilla/Mutex.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsISupports.h"
 #include "nsISupportsUtils.h"
 #include "nsString.h"
 #include "nsWrapperCache.h"
 
 #define DEFAULT_WS_SCHEME_PORT  80
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -4122,17 +4122,17 @@ nsContentUtils::DispatchFocusChromeEvent
   MOZ_ASSERT(aWindow);
 
   nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
   if (!doc) {
     return NS_ERROR_FAILURE;
   }
 
   return DispatchChromeEvent(doc, aWindow,
-                             NS_LITERAL_STRING("DOMServiceWorkerFocusClient"),
+                             NS_LITERAL_STRING("DOMWindowFocus"),
                              true, true);
 }
 
 nsresult
 nsContentUtils::DispatchEventOnlyToChrome(nsIDocument* aDoc,
                                           nsISupports* aTarget,
                                           const nsAString& aEventName,
                                           bool aCanBubble, bool aCancelable,
@@ -10075,8 +10075,41 @@ nsContentUtils::HtmlObjectContentTypeFor
   if (pluginHost &&
       pluginHost->HavePluginForType(aMIMEType, nsPluginHost::eExcludeNone)) {
     // ShouldPlay will handle checking for disabled plugins
     return nsIObjectLoadingContent::TYPE_PLUGIN;
   }
 
   return nsIObjectLoadingContent::TYPE_NULL;
 }
+
+/* static */ already_AddRefed<Dispatcher>
+nsContentUtils::GetDispatcherByLoadInfo(nsILoadInfo* aLoadInfo)
+{
+  if (NS_WARN_IF(!aLoadInfo)) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIDOMDocument> domDoc;
+  aLoadInfo->GetLoadingDocument(getter_AddRefs(domDoc));
+  nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
+  RefPtr<Dispatcher> dispatcher;
+  if (doc) {
+    dispatcher = doc->GetDocGroup();
+  } else {
+    // There's no document yet, but this might be a top-level load where we can
+    // find a TabGroup.
+    uint64_t outerWindowId;
+    if (NS_FAILED(aLoadInfo->GetOuterWindowID(&outerWindowId))) {
+      // No window. This might be an add-on XHR, a service worker request, or
+      // something else.
+      return nullptr;
+    }
+    RefPtr<nsGlobalWindow> window = nsGlobalWindow::GetOuterWindowWithId(outerWindowId);
+    if (!window) {
+      return nullptr;
+    }
+
+    dispatcher = window->TabGroup();
+  }
+
+  return dispatcher.forget();
+}
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -67,16 +67,17 @@ class nsIDOMNode;
 class nsIDragSession;
 class nsIEditor;
 class nsIFragmentContentSink;
 class nsIFrame;
 class nsIImageLoadingContent;
 class nsIInterfaceRequestor;
 class nsIIOService;
 class nsILineBreaker;
+class nsILoadInfo;
 class nsILoadGroup;
 class nsIMessageBroadcaster;
 class nsNameSpaceManager;
 class nsIObserver;
 class nsIParser;
 class nsIParserService;
 class nsIPresShell;
 class nsIPrincipal;
@@ -110,16 +111,17 @@ class nsIWindowProvider;
 struct JSRuntime;
 
 template<class E> class nsCOMArray;
 template<class K, class V> class nsDataHashtable;
 template<class K, class V> class nsRefPtrHashtable;
 template<class T> class nsReadingIterator;
 
 namespace mozilla {
+class Dispatcher;
 class ErrorResult;
 class EventListenerManager;
 
 namespace dom {
 struct CustomElementDefinition;
 class DocumentFragment;
 class Element;
 class EventTarget;
@@ -1251,17 +1253,17 @@ public:
   static nsresult DispatchChromeEvent(nsIDocument* aDoc,
                                       nsISupports* aTarget,
                                       const nsAString& aEventName,
                                       bool aCanBubble,
                                       bool aCancelable,
                                       bool *aDefaultAction = nullptr);
 
   /**
-   * Helper function for dispatching a "DOMServiceWorkerFocusClient" event to
+   * Helper function for dispatching a "DOMWindowFocus" event to
    * the chrome event handler of the given DOM Window. This has the effect
    * of focusing the corresponding tab and bringing the browser window
    * to the foreground.
    */
   static nsresult DispatchFocusChromeEvent(nsPIDOMWindowOuter* aWindow);
 
   /**
    * This method creates and dispatches a trusted event.
@@ -2810,16 +2812,19 @@ public:
    * @param aContent The nsIContent object which is performing the load. May be
    *                 nullptr in which case the docshell's plugin permissions
    *                 will not be checked.
    */
   static uint32_t
   HtmlObjectContentTypeForMIMEType(const nsCString& aMIMEType,
                                    nsIContent* aContent);
 
+  static already_AddRefed<mozilla::Dispatcher>
+  GetDispatcherByLoadInfo(nsILoadInfo* aLoadInfo);
+
 private:
   static bool InitializeEventTable();
 
   static nsresult EnsureStringBundle(PropertiesFile aFile);
 
   static bool CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
                                 nsIPrincipal* aPrincipal);
 
--- a/dom/base/test/browser.ini
+++ b/dom/base/test/browser.ini
@@ -1,16 +1,19 @@
 [DEFAULT]
 support-files =
   audio.ogg
   empty.html
   file_audioLoop.html
   file_audioLoopInIframe.html
   file_bug1011748_redirect.sjs
   file_bug1011748_OK.sjs
+  file_bug1303838.html
+  file_bug1303838_target.html
+  file_bug1303838_with_iframe.html
   file_messagemanager_unload.html
   file_pluginAudio.html
   file_use_counter_outer.html
   file_use_counter_svg_getElementById.svg
   file_use_counter_svg_currentScale.svg
   file_use_counter_svg_fill_pattern_definition.svg
   file_use_counter_svg_fill_pattern.svg
   file_use_counter_svg_fill_pattern_internal.svg
@@ -28,8 +31,9 @@ tags = mcb
 [browser_messagemanager_unload.js]
 [browser_pagehide_on_tab_close.js]
 skip-if = e10s # this tests non-e10s behavior. it's not expected to work in e10s.
 [browser_state_notifications.js]
 skip-if = true # Bug 1271028
 [browser_use_counters.js]
 [browser_bug1307747.js]
 [browser_timeout_throttling_with_audio_playback.js]
+[browser_bug1303838.js]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/browser_bug1303838.js
@@ -0,0 +1,162 @@
+/* -*- Mode: JavaScript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Test for bug 1303838.
+ * Load a tab with some links, emulate link clicks and check if the
+ * browser would switch to the existing target tab opened by previous
+ * link click if loadDivertedInBackground is set to true.
+ */
+
+"use strict";
+
+const BASE_URL = "http://mochi.test:8888/browser/dom/base/test/";
+
+add_task(function*() {
+  yield* testLinkClick(false, false);
+  yield* testLinkClick(false, true);
+  yield* testLinkClick(true, false);
+  yield* testLinkClick(true, true);
+});
+
+function* testLinkClick(withFrame, loadDivertedInBackground) {
+  yield SpecialPowers.pushPrefEnv({"set": [["browser.tabs.loadDivertedInBackground", loadDivertedInBackground]]});
+
+  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser,
+    BASE_URL + (withFrame ? "file_bug1303838_with_iframe.html" : "file_bug1303838.html"));
+  is(gBrowser.tabs.length, 2, "check tabs.length");
+  is(gBrowser.selectedTab, tab, "check selectedTab");
+
+  info("Test normal links with loadDivertedInBackground=" + loadDivertedInBackground + ", withFrame=" + withFrame);
+
+  let [testTab] = yield clickLink(withFrame, "#link-1", tab.linkedBrowser);
+  is(gBrowser.tabs.length, 3, "check tabs.length");
+  is(gBrowser.selectedTab, loadDivertedInBackground ? tab : testTab, "check selectedTab");
+
+  if (!loadDivertedInBackground) {
+    yield BrowserTestUtils.switchTab(gBrowser, tab);
+  }
+  yield clickLink(withFrame, "#link-2", tab.linkedBrowser, testTab.linkedBrowser, !loadDivertedInBackground);
+  is(gBrowser.tabs.length, 3, "check tabs.length");
+  is(gBrowser.selectedTab, loadDivertedInBackground ? tab : testTab, "check selectedTab");
+
+  if (!loadDivertedInBackground) {
+    yield BrowserTestUtils.switchTab(gBrowser, tab);
+  }
+  yield clickLink(withFrame, "#link-3", tab.linkedBrowser, testTab.linkedBrowser, !loadDivertedInBackground);
+  is(gBrowser.tabs.length, 3, "check tabs.length");
+  is(gBrowser.selectedTab, loadDivertedInBackground ? tab : testTab, "check selectedTab");
+
+  if (!loadDivertedInBackground) {
+    yield BrowserTestUtils.switchTab(gBrowser, tab);
+  }
+  yield clickLink(withFrame, "#link-4", tab.linkedBrowser, testTab.linkedBrowser, !loadDivertedInBackground, 2);
+  is(gBrowser.tabs.length, 3, "check tabs.length");
+  is(gBrowser.selectedTab, loadDivertedInBackground ? tab : testTab, "check selectedTab");
+
+  info("Test anchor links with loadDivertedInBackground=" + loadDivertedInBackground + ", withFrame=" + withFrame);
+
+  if (!loadDivertedInBackground) {
+    yield BrowserTestUtils.switchTab(gBrowser, tab);
+  }
+  yield clickLink(withFrame, "#anchor-link-1", tab.linkedBrowser, testTab.linkedBrowser, !loadDivertedInBackground);
+  is(gBrowser.tabs.length, 3, "check tabs.length");
+  is(gBrowser.selectedTab, loadDivertedInBackground ? tab : testTab, "check selectedTab");
+
+  if (!loadDivertedInBackground) {
+    yield BrowserTestUtils.switchTab(gBrowser, tab);
+  }
+  yield clickLink(withFrame, "#anchor-link-2", tab.linkedBrowser, testTab.linkedBrowser, !loadDivertedInBackground);
+  is(gBrowser.tabs.length, 3, "check tabs.length");
+  is(gBrowser.selectedTab, loadDivertedInBackground ? tab : testTab, "check selectedTab");
+
+  if (!loadDivertedInBackground) {
+    yield BrowserTestUtils.switchTab(gBrowser, tab);
+  }
+  yield clickLink(withFrame, "#anchor-link-3", tab.linkedBrowser, testTab.linkedBrowser, !loadDivertedInBackground);
+  is(gBrowser.tabs.length, 3, "check tabs.length");
+  is(gBrowser.selectedTab, loadDivertedInBackground ? tab : testTab, "check selectedTab");
+
+  info("Test iframe links with loadDivertedInBackground=" + loadDivertedInBackground + ", withFrame=" + withFrame);
+
+  if (!loadDivertedInBackground) {
+    yield BrowserTestUtils.switchTab(gBrowser, tab);
+  }
+  yield clickLink(withFrame, "#frame-link-1", tab.linkedBrowser, testTab.linkedBrowser, !loadDivertedInBackground);
+  is(gBrowser.tabs.length, 3, "check tabs.length");
+  is(gBrowser.selectedTab, loadDivertedInBackground ? tab : testTab, "check selectedTab");
+
+  if (!loadDivertedInBackground) {
+    yield BrowserTestUtils.switchTab(gBrowser, tab);
+  }
+  yield clickLink(withFrame, "#frame-link-2", tab.linkedBrowser, testTab.linkedBrowser, !loadDivertedInBackground);
+  is(gBrowser.tabs.length, 3, "check tabs.length");
+  is(gBrowser.selectedTab, loadDivertedInBackground ? tab : testTab, "check selectedTab");
+
+  if (!loadDivertedInBackground) {
+    yield BrowserTestUtils.switchTab(gBrowser, tab);
+  }
+  yield clickLink(withFrame, "#frame-link-3", tab.linkedBrowser, testTab.linkedBrowser, !loadDivertedInBackground);
+  is(gBrowser.tabs.length, 3, "check tabs.length");
+  is(gBrowser.selectedTab, loadDivertedInBackground ? tab : testTab, "check selectedTab");
+
+  yield BrowserTestUtils.removeTab(testTab);
+  yield BrowserTestUtils.removeTab(tab);
+}
+
+function clickLink(isFrame, linkId, browser, testBrowser, awaitTabSwitch = false, locationChangeNum = 1) {
+  let promises = [];
+  if (awaitTabSwitch) {
+    promises.push(waitForTabSwitch(gBrowser));
+  }
+  promises.push(testBrowser ?
+                waitForLocationChange(testBrowser, locationChangeNum) :
+                BrowserTestUtils.waitForNewTab(gBrowser));
+  promises.push(ContentTask.spawn(browser, [isFrame, linkId],
+                                  ([contentIsFrame, contentLinkId]) => {
+    let doc = content.document;
+    if (contentIsFrame) {
+      let frame = content.document.getElementById("frame");
+      doc = frame.contentDocument;
+    }
+    info("Clicking " + contentLinkId);
+    doc.querySelector(contentLinkId).click();
+  }));
+  return Promise.all(promises);
+}
+
+function waitForTabSwitch(tabbrowser) {
+  info("Waiting for TabSwitch");
+  return new Promise(resolve => {
+    tabbrowser.addEventListener("TabSwitchDone", function onSwitch() {
+      info("TabSwitch done");
+      tabbrowser.removeEventListener("TabSwitchDone", onSwitch);
+      resolve(tabbrowser.selectedTab);
+    });
+  });
+}
+
+// We need a longer lifetime reference to ensure the listener is alive when
+// location change occurs.
+let locationChangeListener;
+function waitForLocationChange(browser, locationChangeNum) {
+  info("Waiting for " + locationChangeNum + " LocationChange");
+  return new Promise(resolve => {
+    let seen = 0;
+    locationChangeListener = {
+      onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
+        info("LocationChange: " + aLocation.spec);
+        if (++seen == locationChangeNum) {
+          browser.removeProgressListener(this);
+          resolve();
+        }
+      },
+      QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
+                                             Ci.nsISupportsWeakReference])
+    };
+    browser.addProgressListener(locationChangeListener);
+  });
+}
new file mode 100644
--- /dev/null
+++ b/dom/base/test/file_bug1303838.html
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests for tab switching on link clicks.
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Tests for tab switching on link clicks.</title>
+</head>
+<body>
+  <a id="link-1" target="testTab" href="data:text/html;charset=utf-8,foo">Link 1</a><br>
+  <a id="link-2" target="testTab" href="data:text/html;charset=utf-8,bar">Link 2</a><br>
+  <a id="link-3" target="testTab" href="data:text/html;charset=utf-8,baz">Link 3</a><br>
+  <a id="link-4" target="testTab" href="file_bug1303838_target.html">Link 4</a><br>
+  <a id="anchor-link-1" target="testTab" href="file_bug1303838_target.html#foo">Anchor Link 1</a><br>
+  <a id="anchor-link-2" target="testTab" href="file_bug1303838_target.html#bar">Anchor Link 2</a><br>
+  <a id="anchor-link-3" target="testTab" href="file_bug1303838_target.html#baz">Anchor Link 3</a><br>
+  <a id="frame-link-1" target="testFrame" href="data:text/html;charset=utf-8,ifoo">Frame Link 1</a><br>
+  <a id="frame-link-2" target="testFrame" href="data:text/html;charset=utf-8,ibar">Frame Link 2</a><br>
+  <a id="frame-link-3" target="testFrame" href="data:text/html;charset=utf-8,ibaz">Frame Link 3</a><br>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/file_bug1303838_target.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests for tab switching on link clicks.
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Tests for tab switching on link clicks.</title>
+</head>
+<body onload="setTimeout(loadTestFrame, 0);">
+  <div id="foo">Foo</div>
+  <div id="bar">Bar</div>
+  <div id="baz">Baz</div>
+  <iframe name="testFrame"></iframe>
+  <script>
+    function loadTestFrame() {
+      window.open("data:text/html;charset=utf-8,testFrame", "testFrame");
+    }
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/file_bug1303838_with_iframe.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests for tab switching on link clicks.
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Tests for tab switching on link clicks.</title>
+</head>
+<body>
+  <iframe id="frame" src="file_bug1303838.html"></iframe>
+</body>
+</html>
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -20,17 +20,16 @@
 #include "mozilla/dom/CallbackObject.h"
 #include "mozilla/dom/DOMJSClass.h"
 #include "mozilla/dom/DOMJSProxyHandler.h"
 #include "mozilla/dom/Exceptions.h"
 #include "mozilla/dom/NonRefcountedDOMObject.h"
 #include "mozilla/dom/Nullable.h"
 #include "mozilla/dom/RootedDictionary.h"
 #include "mozilla/SegmentedVector.h"
-#include "mozilla/dom/workers/Workers.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MemoryReporting.h"
 #include "nsAutoPtr.h"
 #include "nsIDocument.h"
 #include "nsIGlobalObject.h"
 #include "nsIXPConnect.h"
 #include "nsJSUtils.h"
--- a/dom/canvas/DocumentRendererChild.cpp
+++ b/dom/canvas/DocumentRendererChild.cpp
@@ -69,17 +69,17 @@ DocumentRendererChild::RenderDocument(ns
     if (!nsRuleNode::ComputeColor(bgColorValue, presContext, nullptr, bgColor)) {
         return false;
     }
 
     // Draw directly into the output array.
     data.SetLength(renderSize.width * renderSize.height * 4);
 
     RefPtr<DrawTarget> dt =
-        Factory::CreateDrawTargetForData(BackendType::CAIRO,
+        Factory::CreateDrawTargetForData(gfxPlatform::GetPlatform()->GetSoftwareBackend(),
                                          reinterpret_cast<uint8_t*>(data.BeginWriting()),
                                          IntSize(renderSize.width, renderSize.height),
                                          4 * renderSize.width,
                                          SurfaceFormat::B8G8R8A8);
     if (!dt || !dt->IsValid()) {
         gfxWarning() << "DocumentRendererChild::RenderDocument failed to Factory::CreateDrawTargetForData";
         return false;
     }
--- a/dom/canvas/ImageBitmapRenderingContext.cpp
+++ b/dom/canvas/ImageBitmapRenderingContext.cpp
@@ -105,17 +105,17 @@ ImageBitmapRenderingContext::MatchWithIn
   }
 
   DataSourceSurface::ScopedMap map(temp, DataSourceSurface::READ_WRITE);
   if (!map.IsMapped()) {
     return nullptr;
   }
 
   RefPtr<DrawTarget> dt =
-    Factory::CreateDrawTargetForData(BackendType::CAIRO,
+    Factory::CreateDrawTargetForData(gfxPlatform::GetPlatform()->GetSoftwareBackend(),
                                      map.GetData(),
                                      temp->GetSize(),
                                      map.GetStride(),
                                      temp->GetFormat());
   if (!dt || !dt->IsValid()) {
     gfxWarning() << "ImageBitmapRenderingContext::MatchWithIntrinsicSize failed";
     return nullptr;
   }
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -1976,17 +1976,17 @@ WebGLContext::GetSurfaceSnapshot(bool* o
         if (out_premultAlpha) {
             *out_premultAlpha = false;
         } else if(hasAlpha) {
             gfxUtils::PremultiplyDataSurface(surf, surf);
         }
     }
 
     RefPtr<DrawTarget> dt =
-        Factory::CreateDrawTarget(BackendType::CAIRO,
+        Factory::CreateDrawTarget(gfxPlatform::GetPlatform()->GetSoftwareBackend(),
                                   IntSize(mWidth, mHeight),
                                   SurfaceFormat::B8G8R8A8);
 
     if (!dt) {
         return nullptr;
     }
 
     dt->SetTransform(Matrix::Translation(0.0, mHeight).PreScale(1.0, -1.0));
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -240,34 +240,21 @@ Event::WrapObjectInternal(JSContext* aCx
 {
   return EventBinding::Wrap(aCx, this, aGivenProto);
 }
 
 // nsIDOMEventInterface
 NS_IMETHODIMP
 Event::GetType(nsAString& aType)
 {
-  if (!mIsMainThreadEvent || !mEvent->mSpecifiedEventTypeString.IsEmpty()) {
+  if (!mIsMainThreadEvent) {
     aType = mEvent->mSpecifiedEventTypeString;
     return NS_OK;
   }
-  const char* name = GetEventName(mEvent->mMessage);
-
-  if (name) {
-    CopyASCIItoUTF16(name, aType);
-    return NS_OK;
-  } else if (mEvent->mMessage == eUnidentifiedEvent &&
-             mEvent->mSpecifiedEventType) {
-    // Remove "on"
-    aType = Substring(nsDependentAtomString(mEvent->mSpecifiedEventType), 2);
-    mEvent->mSpecifiedEventTypeString = aType;
-    return NS_OK;
-  }
-
-  aType.Truncate();
+  GetWidgetEventType(mEvent, aType);
   return NS_OK;
 }
 
 EventTarget*
 Event::GetTarget() const
 {
   return mEvent->GetDOMEventTarget();
 }
@@ -1286,16 +1273,40 @@ Event::GetShadowRelatedTarget(nsIContent
     }
 
     relatedTarget = ancestorShadow->GetHost();
   }
 
   return nullptr;
 }
 
+void
+Event::GetWidgetEventType(WidgetEvent* aEvent, nsAString& aType)
+{
+  if (!aEvent->mSpecifiedEventTypeString.IsEmpty()) {
+    aType = aEvent->mSpecifiedEventTypeString;
+    return;
+  }
+
+  const char* name = GetEventName(aEvent->mMessage);
+
+  if (name) {
+    CopyASCIItoUTF16(name, aType);
+    return;
+  } else if (aEvent->mMessage == eUnidentifiedEvent &&
+             aEvent->mSpecifiedEventType) {
+    // Remove "on"
+    aType = Substring(nsDependentAtomString(aEvent->mSpecifiedEventType), 2);
+    aEvent->mSpecifiedEventTypeString = aType;
+    return;
+  }
+
+  aType.Truncate();
+}
+
 NS_IMETHODIMP
 Event::GetCancelBubble(bool* aCancelBubble)
 {
   NS_ENSURE_ARG_POINTER(aCancelBubble);
   *aCancelBubble = CancelBubble();
   return NS_OK;
 }
 
--- a/dom/events/Event.h
+++ b/dom/events/Event.h
@@ -260,16 +260,24 @@ public:
 
   void MarkUninitialized()
   {
     mEvent->mMessage = eVoidEvent;
     mEvent->mSpecifiedEventTypeString.Truncate();
     mEvent->mSpecifiedEventType = nullptr;
   }
 
+  /**
+   * For WidgetEvent, return it's type in string.
+   *
+   * @param aEvent is a WidgetEvent to get its type.
+   * @param aType is a string where to return the type.
+   */
+  static void GetWidgetEventType(WidgetEvent* aEvent, nsAString& aType);
+
 protected:
 
   // Internal helper functions
   void SetEventType(const nsAString& aEventTypeArg);
   already_AddRefed<nsIContent> GetTargetFromFrame();
 
   friend class EventMessageAutoOverride;
   friend class WantsPopupControlCheck;
--- a/dom/events/EventDispatcher.cpp
+++ b/dom/events/EventDispatcher.cpp
@@ -59,16 +59,17 @@
 #include "mozilla/Telemetry.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TouchEvents.h"
 #include "mozilla/Unused.h"
 
 #ifdef MOZ_TASK_TRACER
 #include "GeckoTaskTracer.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/Likely.h"
 using namespace mozilla::tasktracer;
 #endif
 
 namespace mozilla {
 
 using namespace dom;
 
 class ELMCreationDetector
@@ -601,33 +602,37 @@ EventDispatcher::Dispatch(nsISupports* a
 
   // If we're dispatching an already created DOMEvent object, make
   // sure it is initialized!
   // If aTargets is non-null, the event isn't going to be dispatched.
   NS_ENSURE_TRUE(aEvent->mMessage || !aDOMEvent || aTargets,
                  NS_ERROR_DOM_INVALID_STATE_ERR);
 
 #ifdef MOZ_TASK_TRACER
-  {
+  if (MOZ_UNLIKELY(mozilla::tasktracer::IsStartLogging())) {
+    nsAutoCString eventType;
+    nsAutoString eventTypeU16;
     if (aDOMEvent) {
-      nsAutoString eventType;
-      aDOMEvent->GetType(eventType);
+      aDOMEvent->GetType(eventTypeU16);
+    } else {
+      Event::GetWidgetEventType(aEvent, eventTypeU16);
+    }
+    eventType = NS_ConvertUTF16toUTF8(eventTypeU16);
 
-      nsCOMPtr<Element> element = do_QueryInterface(aTarget);
-      nsAutoString elementId;
-      nsAutoString elementTagName;
-      if (element) {
-        element->GetId(elementId);
-        element->GetTagName(elementTagName);
-      }
-      AddLabel("Event [%s] dispatched at target [id:%s tag:%s]",
-               NS_ConvertUTF16toUTF8(eventType).get(),
-               NS_ConvertUTF16toUTF8(elementId).get(),
-               NS_ConvertUTF16toUTF8(elementTagName).get());
+    nsCOMPtr<Element> element = do_QueryInterface(aTarget);
+    nsAutoString elementId;
+    nsAutoString elementTagName;
+    if (element) {
+      element->GetId(elementId);
+      element->GetTagName(elementTagName);
     }
+    AddLabel("Event [%s] dispatched at target [id:%s tag:%s]",
+             eventType.get(),
+             NS_ConvertUTF16toUTF8(elementId).get(),
+             NS_ConvertUTF16toUTF8(elementTagName).get());
   }
 #endif
 
   nsCOMPtr<EventTarget> target = do_QueryInterface(aTarget);
 
   bool retargeted = false;
 
   if (aEvent->mFlags.mRetargetToNonNativeAnonymous) {
--- a/dom/media/systemservices/DeviceChangeCallback.h
+++ b/dom/media/systemservices/DeviceChangeCallback.h
@@ -2,16 +2,18 @@
 /* vim: set sw=2 ts=8 et ft=cpp : */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_DeviceChangeCallback_h
 #define mozilla_DeviceChangeCallback_h
 
+#include "mozilla/Mutex.h"
+
 namespace mozilla {
 
 class DeviceChangeCallback
 {
 public:
   virtual void OnDeviceChange()
   {
     MutexAutoLock lock(mCallbackMutex);
--- a/dom/network/Connection.h
+++ b/dom/network/Connection.h
@@ -9,25 +9,26 @@
 
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/NetworkInformationBinding.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsINetworkProperties.h"
 
 namespace mozilla {
 
-namespace workers {
-class WorkerPrivate;
-} // namespace workers
-
 namespace hal {
 class NetworkInformation;
 } // namespace hal
 
 namespace dom {
+
+namespace workers {
+class WorkerPrivate;
+} // namespace workers
+
 namespace network {
 
 class Connection : public DOMEventTargetHelper
                  , public nsINetworkProperties
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSINETWORKPROPERTIES
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -317,24 +317,19 @@ public:
   Run() override
   {
     AssertIsOnMainThread();
     if (!mWindow->IsCurrentInnerWindow()) {
       // Window has been closed, this observer is not valid anymore
       return NS_OK;
     }
 
-    nsIDocument* doc = mWindow->GetExtantDoc();
-    if (doc) {
-      // Browser UI may use DOMWebNotificationClicked to focus the tab
-      // from which the event was dispatched.
-      nsContentUtils::DispatchChromeEvent(doc, mWindow->GetOuterWindow(),
-                                          NS_LITERAL_STRING("DOMWebNotificationClicked"),
-                                          true, true);
-    }
+    // Browser UI may use DOMWindowFocus to focus the tab
+    // from which the event was dispatched.
+    nsContentUtils::DispatchFocusChromeEvent(mWindow->GetOuterWindow());
 
     return NS_OK;
   }
 };
 
 nsresult
 CheckScope(nsIPrincipal* aPrincipal, const nsACString& aScope)
 {
@@ -1495,24 +1490,19 @@ MainThreadNotificationObserver::Observe(
     nsCOMPtr<nsPIDOMWindowInner> window = notification->GetOwner();
     if (NS_WARN_IF(!window || !window->IsCurrentInnerWindow())) {
       // Window has been closed, this observer is not valid anymore
       return NS_ERROR_FAILURE;
     }
 
     bool doDefaultAction = notification->DispatchClickEvent();
     if (doDefaultAction) {
-      nsIDocument* doc = window ? window->GetExtantDoc() : nullptr;
-      if (doc) {
-        // Browser UI may use DOMWebNotificationClicked to focus the tab
-        // from which the event was dispatched.
-        nsContentUtils::DispatchChromeEvent(doc, window->GetOuterWindow(),
-                                            NS_LITERAL_STRING("DOMWebNotificationClicked"),
-                                            true, true);
-      }
+      // Browser UI may use DOMWindowFocus to focus the tab
+      // from which the event was dispatched.
+      nsContentUtils::DispatchFocusChromeEvent(window->GetOuterWindow());
     }
   } else if (!strcmp("alertfinished", aTopic)) {
     notification->UnpersistNotification();
     notification->mIsClosed = true;
     notification->DispatchTrustedEvent(NS_LITERAL_STRING("close"));
   } else if (!strcmp("alertshow", aTopic)) {
     notification->DispatchTrustedEvent(NS_LITERAL_STRING("show"));
   }
--- a/dom/offline/nsDOMOfflineResourceList.cpp
+++ b/dom/offline/nsDOMOfflineResourceList.cpp
@@ -12,16 +12,17 @@
 #include "nsIPrefetchService.h"
 #include "nsCPrefetchService.h"
 #include "nsNetUtil.h"
 #include "nsNetCID.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIOfflineCacheUpdate.h"
 #include "nsContentUtils.h"
+#include "nsILoadContext.h"
 #include "nsIObserverService.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIWebNavigation.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/OfflineResourceListBinding.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/BasePrincipal.h"
--- a/dom/webbrowserpersist/nsWebBrowserPersist.cpp
+++ b/dom/webbrowserpersist/nsWebBrowserPersist.cpp
@@ -9,16 +9,17 @@
 
 #include "nsIFileStreams.h"       // New Necko file streams
 #include <algorithm>
 
 #include "nsAutoPtr.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsIInterfaceRequestorUtils.h"
+#include "nsILoadContext.h"
 #include "nsIPrivateBrowsingChannel.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIComponentRegistrar.h"
 #include "nsIStorageStream.h"
 #include "nsISeekableStream.h"
 #include "nsIHttpChannel.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsIEncodedChannel.h"
--- a/dom/workers/ServiceWorkerInfo.h
+++ b/dom/workers/ServiceWorkerInfo.h
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_workers_serviceworkerinfo_h
 #define mozilla_dom_workers_serviceworkerinfo_h
 
 #include "mozilla/dom/ServiceWorkerBinding.h" // For ServiceWorkerState
+#include "mozilla/dom/workers/Workers.h"
 #include "nsIServiceWorkerManager.h"
 
 namespace mozilla {
 namespace dom {
 namespace workers {
 
 class ServiceWorker;
 class ServiceWorkerPrivate;
--- a/dom/workers/Workers.h
+++ b/dom/workers/Workers.h
@@ -379,24 +379,16 @@ public:
 };
 
 WorkerCrossThreadDispatcher*
 GetWorkerCrossThreadDispatcher(JSContext* aCx, const JS::Value& aWorker);
 
 // Random unique constant to facilitate JSPrincipal debugging
 const uint32_t kJSPrincipalsDebugToken = 0x7e2df9d2;
 
-namespace exceptions {
-
-// Implemented in Exceptions.cpp
-void
-ThrowDOMExceptionForNSResult(JSContext* aCx, nsresult aNSResult);
-
-} // namespace exceptions
-
 bool
 IsWorkerGlobal(JSObject* global);
 
 bool
 IsDebuggerGlobal(JSObject* global);
 
 bool
 IsDebuggerSandbox(JSObject* object);
--- a/dom/xhr/XMLHttpRequestString.cpp
+++ b/dom/xhr/XMLHttpRequestString.cpp
@@ -1,16 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "XMLHttpRequestString.h"
-#include "mozilla/Mutex.h"
 #include "nsISupportsImpl.h"
 #include "mozilla/dom/DOMString.h"
 
 namespace mozilla {
 namespace dom {
 
 class XMLHttpRequestStringBuffer final
 {
--- a/dom/xhr/XMLHttpRequestString.h
+++ b/dom/xhr/XMLHttpRequestString.h
@@ -2,22 +2,20 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_XMLHttpRequestString_h
 #define mozilla_dom_XMLHttpRequestString_h
 
+#include "mozilla/Mutex.h"
 #include "nsString.h"
 
 namespace mozilla {
-
-class Mutex;
-
 namespace dom {
 
 class DOMString;
 class XMLHttpRequestStringBuffer;
 class XMLHttpRequestStringSnapshot;
 class XMLHttpRequestStringWriterHelper;
 class XMLHttpRequestStringSnapshotReaderHelper;
 
--- a/editor/reftests/reftest-stylo.list
+++ b/editor/reftests/reftest-stylo.list
@@ -1,9 +1,8 @@
-# DO NOT EDIT! This is a auto-generated temporary list for Stylo testing
 # include the XUL reftests
 include xul/reftest-stylo.list
 
 fails == newline-1.html newline-1.html
 fails == newline-2.html newline-2.html
 fails == newline-3.html newline-3.html
 fails == newline-4.html newline-4.html
 fails == dynamic-1.html dynamic-1.html
@@ -78,17 +77,17 @@ fails == spellcheck-url-valid.html spell
 fails needs-focus == spellcheck-non-latin-arabic.html spellcheck-non-latin-arabic.html
 fails needs-focus == spellcheck-non-latin-chinese-simplified.html spellcheck-non-latin-chinese-simplified.html
 fails needs-focus == spellcheck-non-latin-chinese-traditional.html spellcheck-non-latin-chinese-traditional.html
 fails needs-focus == spellcheck-non-latin-hebrew.html spellcheck-non-latin-hebrew.html
 fails needs-focus == spellcheck-non-latin-japanese.html spellcheck-non-latin-japanese.html
 fails needs-focus == spellcheck-non-latin-korean.html spellcheck-non-latin-korean.html
 fails == unneeded_scroll.html unneeded_scroll.html
 == caret_on_presshell_reinit.html caret_on_presshell_reinit.html
-== caret_on_presshell_reinit-2.html caret_on_presshell_reinit-2.html
+asserts-if(stylo&&isDebugBuild,0-2) fuzzy-if(stylo,255,3) == caret_on_presshell_reinit-2.html caret_on_presshell_reinit-2.html
 fails == 642800.html 642800.html
 fails == selection_visibility_after_reframe.html selection_visibility_after_reframe.html
 fails == selection_visibility_after_reframe-2.html selection_visibility_after_reframe-2.html
 fails == selection_visibility_after_reframe-3.html selection_visibility_after_reframe-3.html
 == 672709.html 672709.html
 fails == 338427-1.html 338427-1.html
 fails == 674212-spellcheck.html 674212-spellcheck.html
 fails == 338427-2.html 338427-2.html
--- a/ipc/chromium/src/base/message_loop.cc
+++ b/ipc/chromium/src/base/message_loop.cc
@@ -291,18 +291,21 @@ void MessageLoop::PostTask_Helper(alread
     } else {
       rv = target->Dispatch(Move(task), 0);
     }
     MOZ_ALWAYS_SUCCEEDS(rv);
     return;
   }
 
 #ifdef MOZ_TASK_TRACER
-  RefPtr<Runnable> tracedTask = mozilla::tasktracer::CreateTracedRunnable(Move(task));
-  (static_cast<mozilla::tasktracer::TracedRunnable*>(tracedTask.get()))->DispatchTask();
+  RefPtr<Runnable> tracedTask = task;
+  if (mozilla::tasktracer::IsStartLogging()) {
+    tracedTask = mozilla::tasktracer::CreateTracedRunnable(Move(task));
+    (static_cast<mozilla::tasktracer::TracedRunnable*>(tracedTask.get()))->DispatchTask();
+  }
   PendingTask pending_task(tracedTask.forget(), true);
 #else
   PendingTask pending_task(Move(task), true);
 #endif
 
   if (delay_ms > 0) {
     pending_task.delayed_run_time =
         TimeTicks::Now() + TimeDelta::FromMilliseconds(delay_ms);
--- a/ipc/chromium/src/chrome/common/ipc_message.cc
+++ b/ipc/chromium/src/chrome/common/ipc_message.cc
@@ -16,17 +16,17 @@
 #include "GeckoTaskTracerImpl.h"
 #endif
 
 #include "mozilla/Move.h"
 
 #ifdef MOZ_TASK_TRACER
 using namespace mozilla::tasktracer;
 
-#define MSG_HEADER_SZ (GetOrCreateTraceInfo() == nullptr ?              \
+#define MSG_HEADER_SZ (IsStartLogging() && GetOrCreateTraceInfo() == nullptr ? \
                        sizeof(Header) : sizeof(HeaderTaskTracer))
 #else
 #define MSG_HEADER_SZ sizeof(Header)
 #endif
 
 namespace IPC {
 
 //------------------------------------------------------------------------------
--- a/js/ductwork/debugger/moz.build
+++ b/js/ductwork/debugger/moz.build
@@ -1,14 +1,17 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+with Files("**"):
+    BUG_COMPONENT = ("Core", "JavaScript Engine")
+
 XPIDL_SOURCES += [
     'IJSDebugger.idl',
 ]
 
 XPIDL_MODULE = 'jsdebugger'
 
 XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell.ini']
 
new file mode 100644
--- /dev/null
+++ b/js/moz.build
@@ -0,0 +1,13 @@
+component_engine = ('Core', 'JavaScript Engine')
+component_gc     = ('Core', 'JavaScript: GC')
+component_jit    = ('Core', 'JavaScript Engine: JIT')
+
+with Files("**"):
+    BUG_COMPONENT = component_engine
+
+for header in ('GCAnnotations.h', 'GCAPI.h', 'HeapAPI.h', 'RootingAPI.h', 'SliceBudget.h', 'SweepingAPI.h', 'TraceKind.h', 'TracingAPI.h', 'WeakMapPtr.h', 'GCHashTable.h', 'GCPolicyAPI.h', 'GCVariant.h', 'GCVector.h'):
+    with Files('public/' + header):
+        BUG_COMPONENT = component_gc
+
+with Files('public/TrackedOptimizationInfo.h'):
+    BUG_COMPONENT = component_jit
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/baseline/bug1349298.js
@@ -0,0 +1,2 @@
+for (var i=0; i<40; i++)
+    assertEq(typeof objectEmulatingUndefined(), "undefined");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/timeout/debug-noprofiling.js
@@ -0,0 +1,32 @@
+// |jit-test| exitstatus: 6;
+
+// Don't include wasm.js in timeout tests: when wasm isn't supported, it will
+// quit(0) which will cause the test to fail.
+if (!wasmIsSupported())
+    quit(6);
+
+newGlobal().Debugger().addDebuggee(this);
+
+var t = new WebAssembly.Table({
+    initial: 1,
+    element: "anyfunc"
+});
+
+new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(`
+(module
+    (func $iloop loop $top br $top end)
+    (import "imports" "t" (table1 anyfunc))
+    (elem (i32.const0) $iloop))
+`)), { imports: { t } });
+
+outer = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(`
+(module
+    (import "imports" "t" (table1 anyfunc))
+    (type $v2v (func))
+    (func (export "run")
+        i32.const0
+        call_indirect $v2v)
+    )`)), { imports: { t } });
+
+timeout(1);
+outer.exports.run();
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -4323,16 +4323,19 @@ DoTypeOfFallback(JSContext* cx, Baseline
                  MutableHandleValue res)
 {
     FallbackICSpew(cx, stub, "TypeOf");
     JSType type = js::TypeOfValue(val);
     RootedString string(cx, TypeName(type, cx->names()));
 
     res.setString(string);
 
+    if (stub->numOptimizedStubs() >= ICTypeOf_Fallback::MAX_OPTIMIZED_STUBS)
+        return true;
+
     MOZ_ASSERT(type != JSTYPE_NULL);
     if (type != JSTYPE_OBJECT && type != JSTYPE_FUNCTION) {
         // Create a new TypeOf stub.
         JitSpew(JitSpew_BaselineIC, "  Generating TypeOf stub for JSType (%d)", (int) type);
         ICTypeOf_Typed::Compiler compiler(cx, type, string);
         ICStub* typeOfStub = compiler.getStub(compiler.getStubSpace(frame->script()));
         if (!typeOfStub)
             return false;
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -1529,16 +1529,18 @@ class ICTypeOf_Fallback : public ICFallb
 {
     friend class ICStubSpace;
 
     explicit ICTypeOf_Fallback(JitCode* stubCode)
       : ICFallbackStub(ICStub::TypeOf_Fallback, stubCode)
     { }
 
   public:
+    static const uint32_t MAX_OPTIMIZED_STUBS = 6;
+
     class Compiler : public ICStubCompiler {
       protected:
         MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
 
       public:
         explicit Compiler(JSContext* cx)
           : ICStubCompiler(cx, ICStub::TypeOf_Fallback, Engine::Baseline)
         { }
--- a/js/src/jit/arm/Simulator-arm.cpp
+++ b/js/src/jit/arm/Simulator-arm.cpp
@@ -1556,17 +1556,18 @@ Simulator::exclusiveMonitorClear()
 bool
 Simulator::handleWasmFault(int32_t addr, unsigned numBytes)
 {
     WasmActivation* act = cx_->wasmActivationStack();
     if (!act)
         return false;
 
     void* pc = reinterpret_cast<void*>(get_pc());
-    wasm::Instance* instance = act->compartment()->wasm.lookupInstanceDeprecated(pc);
+    void* fp = reinterpret_cast<void*>(get_register(r11));
+    wasm::Instance* instance = wasm::LookupFaultingInstance(act, pc, fp);
     if (!instance || !instance->memoryAccessInGuardRegion((uint8_t*)addr, numBytes))
         return false;
 
     const wasm::MemoryAccess* memoryAccess = instance->code().lookupMemoryAccess(pc);
     if (!memoryAccess) {
         set_pc(int32_t(instance->codeSegment().outOfBoundsCode()));
         return true;
     }
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -8,18 +8,16 @@
 component_engine = ('Core', 'JavaScript Engine')
 component_gc     = ('Core', 'JavaScript: GC')
 component_intl   = ('Core', 'JavaScript: Internationalization API')
 component_jit    = ('Core', 'JavaScript Engine: JIT')
 component_stl    = ('Core', 'JavaScript: Standard Library')
 
 FILES_PER_UNIFIED_FILE = 6
 
-with Files('../public/**'):
-    BUG_COMPONENT = component_engine
 with Files('*'):
     BUG_COMPONENT = component_engine
 
 with Files('wasm/**'):
     BUG_COMPONENT = component_jit
 with Files('builtin/**'):
     BUG_COMPONENT = component_stl
 with Files('ctypes/**'):
@@ -28,32 +26,26 @@ with Files('gc/**'):
     BUG_COMPONENT = component_gc
 with Files('jit/**'):
     BUG_COMPONENT = component_jit
 
 # File-specific metadata
 for gcfile in ['jsgc*', 'devtools/rootAnalysis', 'devtools/gc-ubench', 'devtools/gctrace']:
     with Files(gcfile):
         BUG_COMPONENT = component_gc
-for header in ('GCAnnotations.h', 'GCAPI.h', 'HeapAPI.h', 'RootingAPI.h', 'SliceBudget.h', 'SweepingAPI.h', 'TraceKind.h', 'TracingAPI.h', 'WeakMapPtr.h'):
-    with Files('../public/' + header):
-        BUG_COMPONENT = component_gc
 
 for stlfile in ['jsarray.*', 'jsbool*', 'jsdate.*', 'jsnum.*', 'json.*', 'jsstr.*']:
     with Files(stlfile):
         BUG_COMPONENT = component_stl
 
 with Files('builtin/Intl*'):
     BUG_COMPONENT = component_intl
 with Files('builtin/make_intl_data.py'):
     BUG_COMPONENT = component_intl
 
-with Files('../public/TrackedOptimizationInfo.h'):
-    BUG_COMPONENT = component_jit
-
 
 if CONFIG['JS_BUNDLED_EDITLINE']:
     DIRS += ['editline']
 
 if not CONFIG['JS_DISABLE_SHELL']:
     DIRS += ['shell']
 
 TEST_DIRS += ['jsapi-tests', 'tests', 'gdb']
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -7725,16 +7725,19 @@ SetContextOptions(JSContext* cx, const O
                              .setWasm(enableWasm)
                              .setWasmAlwaysBaseline(enableWasmAlwaysBaseline)
                              .setNativeRegExp(enableNativeRegExp)
                              .setUnboxedArrays(enableUnboxedArrays);
 
     if (op.getBoolOption("wasm-check-bce"))
         jit::JitOptions.wasmAlwaysCheckBounds = true;
 
+    if (op.getBoolOption("wasm-test-mode"))
+        jit::JitOptions.wasmTestMode = true;
+
     if (op.getBoolOption("no-unboxed-objects"))
         jit::JitOptions.disableUnboxedObjects = true;
 
     if (const char* str = op.getStringOption("cache-ir-stubs")) {
         if (strcmp(str, "on") == 0)
             jit::JitOptions.disableCacheIR = false;
         else if (strcmp(str, "off") == 0)
             jit::JitOptions.disableCacheIR = true;
@@ -8198,16 +8201,18 @@ main(int argc, char** argv, char** envp)
         || !op.addBoolOption('\0', "no-ion", "Disable IonMonkey")
         || !op.addBoolOption('\0', "no-asmjs", "Disable asm.js compilation")
         || !op.addBoolOption('\0', "no-wasm", "Disable WebAssembly compilation")
         || !op.addBoolOption('\0', "no-native-regexp", "Disable native regexp compilation")
         || !op.addBoolOption('\0', "no-unboxed-objects", "Disable creating unboxed plain objects")
         || !op.addBoolOption('\0', "unboxed-arrays", "Allow creating unboxed arrays")
         || !op.addBoolOption('\0', "wasm-always-baseline", "Enable wasm baseline compiler when possible")
         || !op.addBoolOption('\0', "wasm-check-bce", "Always generate wasm bounds check, even redundant ones.")
+        || !op.addBoolOption('\0', "wasm-test-mode", "Enable wasm testing mode, creating synthetic "
+                                   "objects for non-canonical NaNs and i64 returned from wasm.")
 #ifdef ENABLE_SHARED_ARRAY_BUFFER
         || !op.addStringOption('\0', "shared-memory", "on/off",
                                "SharedArrayBuffer and Atomics "
 #  if SHARED_MEMORY_DEFAULT
                                "(default: on, off to disable)"
 #  else
                                "(default: off, on to enable)"
 #  endif
@@ -8307,17 +8312,17 @@ main(int argc, char** argv, char** envp)
                             "The maximum pc relative OFFSET permitted in pool reference instructions.", 1024)
 #endif
 #if defined(JS_SIMULATOR_ARM)
         || !op.addBoolOption('\0', "arm-sim-icache-checks", "Enable icache flush checks in the ARM "
                              "simulator.")
         || !op.addIntOption('\0', "arm-sim-stop-at", "NUMBER", "Stop the ARM simulator after the given "
                             "NUMBER of instructions.", -1)
 #elif defined(JS_SIMULATOR_MIPS32) || defined(JS_SIMULATOR_MIPS64)
-	|| !op.addBoolOption('\0', "mips-sim-icache-checks", "Enable icache flush checks in the MIPS "
+        || !op.addBoolOption('\0', "mips-sim-icache-checks", "Enable icache flush checks in the MIPS "
                              "simulator.")
         || !op.addIntOption('\0', "mips-sim-stop-at", "NUMBER", "Stop the MIPS simulator after the given "
                             "NUMBER of instructions.", -1)
 #endif
         || !op.addIntOption('\0', "nursery-size", "SIZE-MB", "Set the maximum nursery size in MB", 16)
 #ifdef JS_GC_ZEAL
         || !op.addStringOption('z', "gc-zeal", "LEVEL(;LEVEL)*[,N]", gc::ZealModeHelpText)
 #endif
--- a/js/src/tests/jstests.list
+++ b/js/src/tests/jstests.list
@@ -821,31 +821,33 @@ skip script test262/language/expressions
 skip script test262/language/expressions/class/dstr-gen-meth-static-dflt-obj-ptrn-rest-obj-own-property.js
 skip script test262/language/expressions/class/dstr-meth-static-obj-ptrn-rest-val-obj.js
 skip script test262/language/expressions/class/dstr-meth-dflt-obj-ptrn-rest-obj-nested-rest.js
 skip script test262/language/expressions/class/dstr-gen-meth-static-obj-ptrn-rest-skip-non-enumerable.js
 skip script test262/language/expressions/class/gen-method-yield-spread-obj.js
 skip script test262/language/expressions/class/dstr-meth-static-dflt-obj-ptrn-rest-val-obj.js
 skip script test262/language/expressions/class/dstr-gen-meth-static-dflt-obj-ptrn-rest-getter.js
 
-# https://bugzilla.mozilla.org/show_bug.cgi?id=1346068
-skip script test262/built-ins/Atomics/wait/negative-timeout.js
-skip script test262/built-ins/Atomics/wait/was-woken.js
-skip script test262/built-ins/Atomics/wait/did-timeout.js
-skip script test262/built-ins/Atomics/wait/good-views.js
-skip script test262/built-ins/Atomics/wait/no-spurious-wakeup.js
-skip script test262/built-ins/Atomics/wait/nan-timeout.js
-skip script test262/built-ins/Atomics/wake/wake-all.js
-skip script test262/built-ins/Atomics/wake/wake-zero.js
-skip script test262/built-ins/Atomics/wake/wake-negative.js
-skip script test262/built-ins/Atomics/wake/wake-nan.js
-skip script test262/built-ins/Atomics/wake/wake-two.js
-skip script test262/built-ins/Atomics/wake/wake-in-order.js
-skip script test262/built-ins/Atomics/wake/wake-one.js
-skip script test262/built-ins/Atomics/wake/wake-all-on-loc.js
+# Dependent on evalInWorker, setSharedArrayBuffer, and
+# getSharedArrayBuffer, plus the test cases can't actually run in the
+# browser even if that were fixed, https://bugzil.la/1349863
+skip-if(!xulRuntime.shell) script test262/built-ins/Atomics/wait/negative-timeout.js
+skip-if(!xulRuntime.shell) script test262/built-ins/Atomics/wait/was-woken.js
+skip-if(!xulRuntime.shell) script test262/built-ins/Atomics/wait/did-timeout.js
+skip-if(!xulRuntime.shell) script test262/built-ins/Atomics/wait/good-views.js
+skip-if(!xulRuntime.shell) script test262/built-ins/Atomics/wait/no-spurious-wakeup.js
+skip-if(!xulRuntime.shell) script test262/built-ins/Atomics/wait/nan-timeout.js
+skip-if(!xulRuntime.shell) script test262/built-ins/Atomics/wake/wake-all.js
+skip-if(!xulRuntime.shell) script test262/built-ins/Atomics/wake/wake-zero.js
+skip-if(!xulRuntime.shell) script test262/built-ins/Atomics/wake/wake-negative.js
+skip-if(!xulRuntime.shell) script test262/built-ins/Atomics/wake/wake-nan.js
+skip-if(!xulRuntime.shell) script test262/built-ins/Atomics/wake/wake-two.js
+skip-if(!xulRuntime.shell) script test262/built-ins/Atomics/wake/wake-in-order.js
+skip-if(!xulRuntime.shell) script test262/built-ins/Atomics/wake/wake-one.js
+skip-if(!xulRuntime.shell) script test262/built-ins/Atomics/wake/wake-all-on-loc.js
 
 # SharedArrayBuffer API pedantry
 # https://bugzilla.mozilla.org/show_bug.cgi?id=1346071
 skip script test262/built-ins/TypedArrays/buffer-arg-proto-from-ctor-realm-sab.js
 skip script test262/built-ins/SharedArrayBuffer/proto-from-ctor-realm.js
 skip script test262/built-ins/DataView/proto-from-ctor-realm-sab.js
 # https://bugzilla.mozilla.org/show_bug.cgi?id=1346073
 skip script test262/built-ins/TypedArrays/buffer-arg-byteoffset-is-negative-throws-sab.js
--- a/js/src/tests/test262-host.js
+++ b/js/src/tests/test262-host.js
@@ -2,28 +2,189 @@
 // 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/.
 
 // https://github.com/tc39/test262/blob/master/INTERPRETING.md#host-defined-functions
 ;(function createHostObject(global) {
     var FunctionToString = global.Function.prototype.toString;
     var ReflectApply = global.Reflect.apply;
     var NewGlobal = global.newGlobal;
+    var Atomics = global.Atomics;
+    var SharedArrayBuffer = global.SharedArrayBuffer;
+    var Int32Array = global.Int32Array;
+    var setSharedArrayBuffer = global.setSharedArrayBuffer;
+    var getSharedArrayBuffer = global.getSharedArrayBuffer;
+    var evalInWorker = global.evalInWorker;
+    var hasThreads = ("helperThreadCount" in global ? global.helperThreadCount() > 0 : true);
+    var hasMailbox = typeof setSharedArrayBuffer == "function" && typeof getSharedArrayBuffer == "function";
+    var hasEvalInWorker = typeof evalInWorker == "function";
+
+    // The $262.agent framework is not appropriate for browsers yet, and some
+    // test cases can't work in browsers (they block the main thread).
+
+    var shellCode = hasMailbox && hasEvalInWorker;
+    var sabTestable = Atomics && SharedArrayBuffer && hasThreads && shellCode;
 
     global.$262 = {
         __proto__: null,
         createRealm() {
             var newGlobalObject = NewGlobal();
             var createHostObjectFn = ReflectApply(FunctionToString, createHostObject, []);
             newGlobalObject.Function(`${createHostObjectFn} createHostObject(this);`)();
             return newGlobalObject.$262;
         },
         detachArrayBuffer: global.detachArrayBuffer,
         evalScript: global.evaluateScript || global.evaluate,
         global,
+        agent: (function () {
+
+            // SpiderMonkey complication: With run-time argument --no-threads
+            // our test runner will not properly filter test cases that can't be
+            // run because agents can't be started, and so we do a little
+            // filtering here: We will quietly succeed and exit if an agent test
+            // should not have been run because threads cannot be started.
+            //
+            // Firefox complication: The test cases that use $262.agent can't
+            // currently work in the browser, so for now we rely on them not
+            // being run at all.
+
+            if (!sabTestable) {
+                return {
+                    _notAvailable() {
+                        // See comment above.
+                        if (!hasThreads && shellCode) {
+                            global.reportCompare(0,0);
+                            global.quit(0);
+                        }
+                        throw new Error("Agents not available");
+                    },
+                    start(script) { this._notAvailable() },
+                    broadcast(sab, id) { this._notAvailable() },
+                    getReport() { this._notAvailable() },
+                    sleep(s) { this._notAvailable() }
+                }
+            }
+
+            // The SpiderMonkey implementation uses a designated shared buffer _ia
+            // for coordination, and spinlocks for everything except sleeping.
+
+            var _MSG_LOC = 0;           // Low bit set: broadcast available; High bits: seq #
+            var _ID_LOC = 1;            // ID sent with broadcast
+            var _ACK_LOC = 2;           // Worker increments this to ack that broadcast was received
+            var _RDY_LOC = 3;           // Worker increments this to ack that worker is up and running
+            var _LOCKTXT_LOC = 4;       // Writer lock for the text buffer: 0=open, 1=closed
+            var _NUMTXT_LOC = 5;        // Count of messages in text buffer
+            var _NEXT_LOC = 6;          // First free location in the buffer
+            var _SLEEP_LOC = 7;         // Used for sleeping
+
+            var _FIRST = 10;            // First location of first message
+
+            var _ia = new Int32Array(new SharedArrayBuffer(65536));
+            _ia[_NEXT_LOC] = _FIRST;
+
+            var _worker_prefix =
+// BEGIN WORKER PREFIX
+`if (typeof $262 == 'undefined')
+    $262 = {};
+$262.agent = (function () {
+    var _ia = new Int32Array(getSharedArrayBuffer());
+    var agent = {
+        receiveBroadcast(receiver) {
+            var k;
+            while (((k = Atomics.load(_ia, ${_MSG_LOC})) & 1) == 0)
+                ;
+            var received_sab = getSharedArrayBuffer();
+            var received_id = Atomics.load(_ia, ${_ID_LOC});
+            Atomics.add(_ia, ${_ACK_LOC}, 1);
+            while (Atomics.load(_ia, ${_MSG_LOC}) == k)
+                ;
+            receiver(received_sab, received_id);
+        },
+
+        report(msg) {
+            while (Atomics.compareExchange(_ia, ${_LOCKTXT_LOC}, 0, 1) == 1)
+                ;
+            msg = "" + msg;
+            var i = _ia[${_NEXT_LOC}];
+            _ia[i++] = msg.length;
+            for ( let j=0 ; j < msg.length ; j++ )
+                _ia[i++] = msg.charCodeAt(j);
+            _ia[${_NEXT_LOC}] = i;
+            Atomics.add(_ia, ${_NUMTXT_LOC}, 1);
+            Atomics.store(_ia, ${_LOCKTXT_LOC}, 0);
+        },
+
+        sleep(s) {
+            Atomics.wait(_ia, ${_SLEEP_LOC}, 0, s);
+        },
+
+        leaving() {}
+    };
+    Atomics.add(_ia, ${_RDY_LOC}, 1);
+    return agent;
+})();`;
+// END WORKER PREFIX
+
+            return {
+                _numWorkers: 0,
+                _numReports: 0,
+                _reportPtr: _FIRST,
+
+                _bailIfNotAvailable() {
+                    if (!sabTestable) {
+                        // See comment above.
+                        if (!hasThreads && shellCode) {
+                            global.reportCompare(0,0);
+                            global.quit(0);
+                        }
+                        throw new Error("Agents not available");
+                    }
+                },
+
+                start(script) {
+                    this._bailIfNotAvailable();
+                    setSharedArrayBuffer(_ia.buffer);
+                    var oldrdy = Atomics.load(_ia, _RDY_LOC);
+                    evalInWorker(_worker_prefix + script);
+                    while (Atomics.load(_ia, _RDY_LOC) == oldrdy)
+                        ;
+                    this._numWorkers++;
+                },
+
+                broadcast(sab, id) {
+                    this._bailIfNotAvailable();
+                    setSharedArrayBuffer(sab);
+                    Atomics.store(_ia, _ID_LOC, id);
+                    Atomics.store(_ia, _ACK_LOC, 0);
+                    Atomics.add(_ia, _MSG_LOC, 1);
+                    while (Atomics.load(_ia, _ACK_LOC) < this._numWorkers)
+                        ;
+                    Atomics.add(_ia, _MSG_LOC, 1);
+                },
+
+                getReport() {
+                    this._bailIfNotAvailable();
+                    if (this._numReports == Atomics.load(_ia, _NUMTXT_LOC))
+                        return null;
+                    var s = "";
+                    var i = this._reportPtr;
+                    var len = _ia[i++];
+                    for ( let j=0 ; j < len ; j++ )
+                        s += String.fromCharCode(_ia[i++]);
+                    this._reportPtr = i;
+                    this._numReports++;
+                    return s;
+                },
+
+                sleep(s) {
+                    this._bailIfNotAvailable();
+                    Atomics.wait(_ia, _SLEEP_LOC, 0, s);
+                },
+            };
+        })()
     };
 })(this);
 
 var $mozAsyncTestDone = false;
 function $DONE(failure) {
     // This function is generally called from within a Promise handler, so any
     // exception thrown by this method will be swallowed and most likely
     // ignored by the Promise machinery.
--- a/js/src/tests/test262/shell.js
+++ b/js/src/tests/test262/shell.js
@@ -238,28 +238,189 @@ function testFailed(message) {
 // 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/.
 
 // https://github.com/tc39/test262/blob/master/INTERPRETING.md#host-defined-functions
 ;(function createHostObject(global) {
     var FunctionToString = global.Function.prototype.toString;
     var ReflectApply = global.Reflect.apply;
     var NewGlobal = global.newGlobal;
+    var Atomics = global.Atomics;
+    var SharedArrayBuffer = global.SharedArrayBuffer;
+    var Int32Array = global.Int32Array;
+    var setSharedArrayBuffer = global.setSharedArrayBuffer;
+    var getSharedArrayBuffer = global.getSharedArrayBuffer;
+    var evalInWorker = global.evalInWorker;
+    var hasThreads = ("helperThreadCount" in global ? global.helperThreadCount() > 0 : true);
+    var hasMailbox = typeof setSharedArrayBuffer == "function" && typeof getSharedArrayBuffer == "function";
+    var hasEvalInWorker = typeof evalInWorker == "function";
+
+    // The $262.agent framework is not appropriate for browsers yet, and some
+    // test cases can't work in browsers (they block the main thread).
+
+    var shellCode = hasMailbox && hasEvalInWorker;
+    var sabTestable = Atomics && SharedArrayBuffer && hasThreads && shellCode;
 
     global.$262 = {
         __proto__: null,
         createRealm() {
             var newGlobalObject = NewGlobal();
             var createHostObjectFn = ReflectApply(FunctionToString, createHostObject, []);
             newGlobalObject.Function(`${createHostObjectFn} createHostObject(this);`)();
             return newGlobalObject.$262;
         },
         detachArrayBuffer: global.detachArrayBuffer,
         evalScript: global.evaluateScript || global.evaluate,
         global,
+        agent: (function () {
+
+            // SpiderMonkey complication: With run-time argument --no-threads
+            // our test runner will not properly filter test cases that can't be
+            // run because agents can't be started, and so we do a little
+            // filtering here: We will quietly succeed and exit if an agent test
+            // should not have been run because threads cannot be started.
+            //
+            // Firefox complication: The test cases that use $262.agent can't
+            // currently work in the browser, so for now we rely on them not
+            // being run at all.
+
+            if (!sabTestable) {
+                return {
+                    _notAvailable() {
+                        // See comment above.
+                        if (!hasThreads && shellCode) {
+                            global.reportCompare(0,0);
+                            global.quit(0);
+                        }
+                        throw new Error("Agents not available");
+                    },
+                    start(script) { this._notAvailable() },
+                    broadcast(sab, id) { this._notAvailable() },
+                    getReport() { this._notAvailable() },
+                    sleep(s) { this._notAvailable() }
+                }
+            }
+
+            // The SpiderMonkey implementation uses a designated shared buffer _ia
+            // for coordination, and spinlocks for everything except sleeping.
+
+            var _MSG_LOC = 0;           // Low bit set: broadcast available; High bits: seq #
+            var _ID_LOC = 1;            // ID sent with broadcast
+            var _ACK_LOC = 2;           // Worker increments this to ack that broadcast was received
+            var _RDY_LOC = 3;           // Worker increments this to ack that worker is up and running
+            var _LOCKTXT_LOC = 4;       // Writer lock for the text buffer: 0=open, 1=closed
+            var _NUMTXT_LOC = 5;        // Count of messages in text buffer
+            var _NEXT_LOC = 6;          // First free location in the buffer
+            var _SLEEP_LOC = 7;         // Used for sleeping
+
+            var _FIRST = 10;            // First location of first message
+
+            var _ia = new Int32Array(new SharedArrayBuffer(65536));
+            _ia[_NEXT_LOC] = _FIRST;
+
+            var _worker_prefix =
+// BEGIN WORKER PREFIX
+`if (typeof $262 == 'undefined')
+    $262 = {};
+$262.agent = (function () {
+    var _ia = new Int32Array(getSharedArrayBuffer());
+    var agent = {
+        receiveBroadcast(receiver) {
+            var k;
+            while (((k = Atomics.load(_ia, ${_MSG_LOC})) & 1) == 0)
+                ;
+            var received_sab = getSharedArrayBuffer();
+            var received_id = Atomics.load(_ia, ${_ID_LOC});
+            Atomics.add(_ia, ${_ACK_LOC}, 1);
+            while (Atomics.load(_ia, ${_MSG_LOC}) == k)
+                ;
+            receiver(received_sab, received_id);
+        },
+
+        report(msg) {
+            while (Atomics.compareExchange(_ia, ${_LOCKTXT_LOC}, 0, 1) == 1)
+                ;
+            msg = "" + msg;
+            var i = _ia[${_NEXT_LOC}];
+            _ia[i++] = msg.length;
+            for ( let j=0 ; j < msg.length ; j++ )
+                _ia[i++] = msg.charCodeAt(j);
+            _ia[${_NEXT_LOC}] = i;
+            Atomics.add(_ia, ${_NUMTXT_LOC}, 1);
+            Atomics.store(_ia, ${_LOCKTXT_LOC}, 0);
+        },
+
+        sleep(s) {
+            Atomics.wait(_ia, ${_SLEEP_LOC}, 0, s);
+        },
+
+        leaving() {}
+    };
+    Atomics.add(_ia, ${_RDY_LOC}, 1);
+    return agent;
+})();`;
+// END WORKER PREFIX
+
+            return {
+                _numWorkers: 0,
+                _numReports: 0,
+                _reportPtr: _FIRST,
+
+                _bailIfNotAvailable() {
+                    if (!sabTestable) {
+                        // See comment above.
+                        if (!hasThreads && shellCode) {
+                            global.reportCompare(0,0);
+                            global.quit(0);
+                        }
+                        throw new Error("Agents not available");
+                    }
+                },
+
+                start(script) {
+                    this._bailIfNotAvailable();
+                    setSharedArrayBuffer(_ia.buffer);
+                    var oldrdy = Atomics.load(_ia, _RDY_LOC);
+                    evalInWorker(_worker_prefix + script);
+                    while (Atomics.load(_ia, _RDY_LOC) == oldrdy)
+                        ;
+                    this._numWorkers++;
+                },
+
+                broadcast(sab, id) {
+                    this._bailIfNotAvailable();
+                    setSharedArrayBuffer(sab);
+                    Atomics.store(_ia, _ID_LOC, id);
+                    Atomics.store(_ia, _ACK_LOC, 0);
+                    Atomics.add(_ia, _MSG_LOC, 1);
+                    while (Atomics.load(_ia, _ACK_LOC) < this._numWorkers)
+                        ;
+                    Atomics.add(_ia, _MSG_LOC, 1);
+                },
+
+                getReport() {
+                    this._bailIfNotAvailable();
+                    if (this._numReports == Atomics.load(_ia, _NUMTXT_LOC))
+                        return null;
+                    var s = "";
+                    var i = this._reportPtr;
+                    var len = _ia[i++];
+                    for ( let j=0 ; j < len ; j++ )
+                        s += String.fromCharCode(_ia[i++]);
+                    this._reportPtr = i;
+                    this._numReports++;
+                    return s;
+                },
+
+                sleep(s) {
+                    this._bailIfNotAvailable();
+                    Atomics.wait(_ia, _SLEEP_LOC, 0, s);
+                },
+            };
+        })()
     };
 })(this);
 
 var $mozAsyncTestDone = false;
 function $DONE(failure) {
     // This function is generally called from within a Promise handler, so any
     // exception thrown by this method will be swallowed and most likely
     // ignored by the Promise machinery.
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -1130,36 +1130,37 @@ CallAddPropertyHookDense(JSContext* cx, 
         if (!CallJSAddPropertyOp(cx, addProperty, obj, id, value)) {
             obj->setDenseElementHole(cx, index);
             return false;
         }
     }
     return true;
 }
 
-static bool
-UpdateShapeTypeAndValue(JSContext* cx, HandleNativeObject obj, HandleShape shape, const Value& value)
+static MOZ_ALWAYS_INLINE void
+UpdateShapeTypeAndValue(JSContext* cx, NativeObject* obj, Shape* shape, jsid id,
+                        const Value& value)
 {
-    jsid id = shape->propid();
+    MOZ_ASSERT(id == shape->propid());
+
     if (shape->hasSlot()) {
         obj->setSlotWithType(cx, shape, value, /* overwriting = */ false);
 
         // Per the acquired properties analysis, when the shape of a partially
         // initialized object is changed to its fully initialized shape, its
         // group can be updated as well.
         if (TypeNewScript* newScript = obj->groupRaw()->newScript()) {
             if (newScript->initializedShape() == shape)
                 obj->setGroup(newScript->initializedGroup());
         }
     }
     if (!shape->hasSlot() || !shape->hasDefaultGetter() || !shape->hasDefaultSetter())
         MarkTypePropertyNonData(cx, obj, id);
     if (!shape->writable())
         MarkTypePropertyNonWritable(cx, obj, id);
-    return true;
 }
 
 static bool
 PurgeProtoChain(JSContext* cx, JSObject* objArg, HandleId id)
 {
     /* Root locally so we can re-assign. */
     RootedObject obj(cx, objArg);
 
@@ -1256,18 +1257,17 @@ AddOrChangeProperty(JSContext* cx, Handl
         }
     }
 
     RootedShape shape(cx, NativeObject::putProperty(cx, obj, id, desc.getter(), desc.setter(),
                                                     SHAPE_INVALID_SLOT, desc.attributes(), 0));
     if (!shape)
         return false;
 
-    if (!UpdateShapeTypeAndValue(cx, obj, shape, desc.value()))
-        return false;
+    UpdateShapeTypeAndValue(cx, obj, shape, id, desc.value());
 
     // Clear any existing dense index after adding a sparse indexed property,
     // and investigate converting the object to dense indexes.
     if (JSID_IS_INT(id)) {
         if (!obj->maybeCopyElementsForWrite(cx))
             return false;
 
         uint32_t index = JSID_TO_INT(id);
@@ -1493,21 +1493,18 @@ js::NativeDefineProperty(JSContext* cx, 
     bool redundant;
     if (!DefinePropertyIsRedundant(cx, obj, id, prop, shapeAttrs, desc, &redundant))
         return false;
     if (redundant) {
         // In cases involving JSOP_NEWOBJECT and JSOP_INITPROP, obj can have a
         // type for this property that doesn't match the value in the slot.
         // Update the type here, even though this DefineProperty call is
         // otherwise a no-op. (See bug 1125624 comment 13.)
-        if (!prop.isDenseOrTypedArrayElement() && desc.hasValue()) {
-            RootedShape shape(cx, prop.shape());
-            if (!UpdateShapeTypeAndValue(cx, obj, shape, desc.value()))
-                return false;
-        }
+        if (!prop.isDenseOrTypedArrayElement() && desc.hasValue())
+            UpdateShapeTypeAndValue(cx, obj, prop.shape(), id, desc.value());
         return result.succeed();
     }
 
     // Non-standard hack: Allow redefining non-configurable properties if
     // JSPROP_REDEFINE_NONCONFIGURABLE is set _and_ the object is a non-DOM
     // global. The idea is that a DOM object can never have such a thing on
     // its proto chain directly on the web, so we should be OK optimizing
     // access to accessors found on such an object. Bug 1105518 contemplates
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -425,25 +425,17 @@ js::NativeObject::toDictionaryMode(JSCon
 /* static */ Shape*
 NativeObject::addProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
                           GetterOp getter, SetterOp setter, uint32_t slot, unsigned attrs,
                           unsigned flags, bool allowDictionary)
 {
     MOZ_ASSERT(!JSID_IS_VOID(id));
     MOZ_ASSERT(getter != JS_PropertyStub);
     MOZ_ASSERT(setter != JS_StrictPropertyStub);
-
-    bool extensible;
-    if (!IsExtensible(cx, obj, &extensible))
-        return nullptr;
-    if (!extensible) {
-        if (!cx->helperThread())
-            JSObject::reportNotExtensible(cx, obj);
-        return nullptr;
-    }
+    MOZ_ASSERT(obj->nonProxyIsExtensible());
 
     AutoKeepShapeTables keep(cx);
     ShapeTable::Entry* entry = nullptr;
     if (obj->inDictionaryMode()) {
         ShapeTable* table = obj->lastProperty()->ensureTableForDictionary(cx, keep);
         if (!table)
             return nullptr;
         entry = &table->search<MaybeAdding::Adding>(id, keep);
@@ -675,26 +667,17 @@ NativeObject::putProperty(JSContext* cx,
         return nullptr;
     }
 
     if (!shape) {
         /*
          * You can't add properties to a non-extensible object, but you can change
          * attributes of properties in such objects.
          */
-        bool extensible;
-
-        if (!IsExtensible(cx, obj, &extensible))
-            return nullptr;
-
-        if (!extensible) {
-            if (!cx->helperThread())
-                JSObject::reportNotExtensible(cx, obj);
-            return nullptr;
-        }
+        MOZ_ASSERT(obj->nonProxyIsExtensible());
 
         return addPropertyInternal(cx, obj, id, getter, setter, slot, attrs, flags,
                                    entry, true, keep);
     }
 
     /* Property exists: search must have returned a valid entry. */
     MOZ_ASSERT_IF(entry, !entry->isRemoved());
 
--- a/js/src/wasm/WasmCompartment.cpp
+++ b/js/src/wasm/WasmCompartment.cpp
@@ -42,18 +42,23 @@ Compartment::~Compartment()
 struct InstanceComparator
 {
     const Instance& target;
     explicit InstanceComparator(const Instance& target) : target(target) {}
 
     int operator()(const Instance* instance) const {
         if (instance == &target)
             return 0;
-        MOZ_ASSERT(!target.codeSegment().containsCodePC(instance->codeBase()));
-        MOZ_ASSERT(!instance->codeSegment().containsCodePC(target.codeBase()));
+
+        // Instances can share code, so the segments can be equal (though they
+        // can't partially overlap).  If the codeBases are equal, we sort by
+        // Instance address.  Thus a Code may map to many instances.
+        if (instance->codeBase() == target.codeBase())
+            return instance < &target ? -1 : 1;
+
         return target.codeBase() < instance->codeBase() ? -1 : 1;
     }
 };
 
 void
 Compartment::trace(JSTracer* trc)
 {
     // A WasmInstanceObject that was initially reachable when called can become
@@ -115,35 +120,28 @@ struct PCComparator
             return 0;
         return pc < instance->codeBase() ? -1 : 1;
     }
 };
 
 Code*
 Compartment::lookupCode(const void* pc) const
 {
-    Instance* instance = lookupInstanceDeprecated(pc);
-    return instance ? &instance->code() : nullptr;
-}
-
-Instance*
-Compartment::lookupInstanceDeprecated(const void* pc) const
-{
     // lookupInstanceDeprecated can be called asynchronously from the interrupt
     // signal handler. In that case, the signal handler is just asking whether
     // the pc is in wasm code. If instances_ is being mutated then we can't be
     // executing wasm code so returning nullptr is fine.
     if (mutatingInstances_)
         return nullptr;
 
     size_t index;
     if (!BinarySearchIf(instances_, 0, instances_.length(), PCComparator(pc), &index))
         return nullptr;
 
-    return instances_[index];
+    return &instances_[index]->code();
 }
 
 void
 Compartment::setInterrupted(bool interrupted)
 {
     if (interrupted) {
         interruptedCount_++;
     } else {
--- a/js/src/wasm/WasmCompartment.h
+++ b/js/src/wasm/WasmCompartment.h
@@ -76,23 +76,16 @@ class Compartment
 
     const InstanceVector& instances() const { return instances_; }
 
     // This methods returns the wasm::Code containing the given pc, if any
     // exists in the compartment.
 
     Code* lookupCode(const void* pc) const;
 
-    // Currently, there is one Code per Instance so it is also possible to
-    // lookup a Instance given a pc. However, the goal is to share one Code
-    // between multiple Instances at which point in time this method will be
-    // removed.
-
-    Instance* lookupInstanceDeprecated(const void* pc) const;
-
     // The wasm::Compartment must be notified when execution is interrupted
     // while executing in wasm code in this compartment.
 
     void setInterrupted(bool interrupted);
 
     // Ensure all Instances in this JSCompartment have profiling labels created.
 
     void ensureProfilingLabels(bool profilingEnabled);
--- a/js/src/wasm/WasmFrameIterator.cpp
+++ b/js/src/wasm/WasmFrameIterator.cpp
@@ -221,25 +221,29 @@ FrameIterator::instance() const
     return FrameToDebugFrame(fp_)->instance();
 }
 
 bool
 FrameIterator::debugEnabled() const
 {
     MOZ_ASSERT(!done() && code_);
     MOZ_ASSERT_IF(!missingFrameMessage_, codeRange_->kind() == CodeRange::Function);
+    MOZ_ASSERT_IF(missingFrameMessage_, !codeRange_ && !fp_);
     // Only non-imported functions can have debug frames.
     return code_->metadata().debugEnabled &&
+           fp_ &&
+           !missingFrameMessage_ &&
            codeRange_->funcIndex() >= code_->metadata().funcImports.length();
 }
 
 DebugFrame*
 FrameIterator::debugFrame() const
 {
     MOZ_ASSERT(!done() && debugEnabled());
+    MOZ_ASSERT(fp_);
     return FrameToDebugFrame(fp_);
 }
 
 const CallSite*
 FrameIterator::debugTrapCallsite() const
 {
     MOZ_ASSERT(!done() && debugEnabled());
     MOZ_ASSERT(callsite_->kind() == CallSite::EnterFrame || callsite_->kind() == CallSite::LeaveFrame ||
@@ -250,47 +254,53 @@ FrameIterator::debugTrapCallsite() const
 /*****************************************************************************/
 // Prologue/epilogue code generation
 
 // These constants reflect statically-determined offsets in the
 // prologue/epilogue. The offsets are dynamically asserted during code
 // generation.
 #if defined(JS_CODEGEN_X64)
 static const unsigned PushedRetAddr = 0;
-static const unsigned PushedFP = 1;
-static const unsigned PushedTLS = 3;
-static const unsigned PoppedTLS = 1;
+static const unsigned PushedTLS = 2;
+static const unsigned PushedFP = 3;
+static const unsigned SetFP = 6;
+static const unsigned PoppedFP = 2;
 #elif defined(JS_CODEGEN_X86)
 static const unsigned PushedRetAddr = 0;
-static const unsigned PushedFP = 1;
-static const unsigned PushedTLS = 2;
-static const unsigned PoppedTLS = 1;
+static const unsigned PushedTLS = 1;
+static const unsigned PushedFP = 2;
+static const unsigned SetFP = 4;
+static const unsigned PoppedFP = 1;
 #elif defined(JS_CODEGEN_ARM)
 static const unsigned BeforePushRetAddr = 0;
 static const unsigned PushedRetAddr = 4;
-static const unsigned PushedFP = 8;
-static const unsigned PushedTLS = 12;
-static const unsigned PoppedTLS = 4;
+static const unsigned PushedTLS = 8;
+static const unsigned PushedFP = 12;
+static const unsigned SetFP = 16;
+static const unsigned PoppedFP = 4;
 #elif defined(JS_CODEGEN_ARM64)
 static const unsigned BeforePushRetAddr = 0;
 static const unsigned PushedRetAddr = 0;
+static const unsigned PushedTLS = 0;
 static const unsigned PushedFP = 0;
-static const unsigned PushedTLS = 0;
-static const unsigned PoppedTLS = 0;
+static const unsigned SetFP = 0;
+static const unsigned PoppedFP = 0;
 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
 static const unsigned BeforePushRetAddr = 0;
 static const unsigned PushedRetAddr = 4;
-static const unsigned PushedFP = 8;
-static const unsigned PushedTLS = 12;
-static const unsigned PoppedTLS = 4;
+static const unsigned PushedTLS = 8;
+static const unsigned PushedFP = 12;
+static const unsigned SetFP = 16;
+static const unsigned PoppedFP = 4;
 #elif defined(JS_CODEGEN_NONE)
 static const unsigned PushedRetAddr = 0;
+static const unsigned PushedTLS = 0;
 static const unsigned PushedFP = 0;
-static const unsigned PushedTLS = 0;
-static const unsigned PoppedTLS = 0;
+static const unsigned SetFP = 0;
+static const unsigned PoppedFP = 0;
 #else
 # error "Unknown architecture!"
 #endif
 
 static void
 PushRetAddr(MacroAssembler& masm, unsigned entry)
 {
 #if defined(JS_CODEGEN_ARM)
@@ -317,21 +327,22 @@ GenerateCallablePrologue(MacroAssembler&
 #if defined(JS_CODEGEN_ARM)
         AutoForbidPools afp(&masm, /* number of instructions in scope = */ 8);
 #endif
 
         *entry = masm.currentOffset();
 
         PushRetAddr(masm, *entry);
         MOZ_ASSERT_IF(!masm.oom(), PushedRetAddr == masm.currentOffset() - *entry);
+        masm.push(WasmTlsReg);
+        MOZ_ASSERT_IF(!masm.oom(), PushedTLS == masm.currentOffset() - *entry);
         masm.push(FramePointer);
         MOZ_ASSERT_IF(!masm.oom(), PushedFP == masm.currentOffset() - *entry);
-        masm.push(WasmTlsReg);
-        MOZ_ASSERT_IF(!masm.oom(), PushedTLS == masm.currentOffset() - *entry);
         masm.moveStackPtrTo(FramePointer);
+        MOZ_ASSERT_IF(!masm.oom(), SetFP == masm.currentOffset() - *entry);
     }
 
     if (reason != ExitReason::None) {
         Register scratch = ABINonArgReg0;
         masm.loadWasmActivationFromTls(scratch);
         masm.wasmAssertNonExitInvariants(scratch);
         Address exitReason(scratch, WasmActivation::offsetOfExitReason());
         masm.store32(Imm32(int32_t(reason)), exitReason);
@@ -360,22 +371,30 @@ GenerateCallableEpilogue(MacroAssembler&
         masm.store32(Imm32(int32_t(ExitReason::None)), exitReason);
     }
 
     // Forbid pools for the same reason as described in GenerateCallablePrologue.
 #if defined(JS_CODEGEN_ARM)
     AutoForbidPools afp(&masm, /* number of instructions in scope = */ 3);
 #endif
 
+    // There is an important ordering constraint here: fp must be repointed to
+    // the caller's frame before any field of the frame currently pointed to by
+    // fp is popped: asynchronous signal handlers (which use stack space
+    // starting at sp) could otherwise clobber these fields while they are still
+    // accessible via fp (fp fields are read during frame iteration which is
+    // *also* done asynchronously).
+
+    masm.pop(FramePointer);
+    DebugOnly<uint32_t> poppedFP = masm.currentOffset();
     masm.pop(WasmTlsReg);
-    DebugOnly<uint32_t> poppedTLS = masm.currentOffset();
-    masm.pop(FramePointer);
     *ret = masm.currentOffset();
     masm.ret();
-    MOZ_ASSERT_IF(!masm.oom(), PoppedTLS == *ret - poppedTLS);
+
+    MOZ_ASSERT_IF(!masm.oom(), PoppedFP == *ret - poppedFP);
 }
 
 void
 wasm::GenerateFunctionPrologue(MacroAssembler& masm, unsigned framePushed, const SigIdDesc& sigId,
                                FuncOffsets* offsets)
 {
 #if defined(JS_CODEGEN_ARM)
     // Flush pending pools so they do not get dumped between the 'begin' and
@@ -613,32 +632,32 @@ ProfilingFrameIterator::ProfilingFrameIt
         } else
 #endif
         if (offsetFromEntry == PushedRetAddr || codeRange->isThunk()) {
             // The return address has been pushed on the stack but fp still
             // points to the caller's fp.
             callerPC_ = sp[0];
             callerFP_ = fp;
             AssertMatchesCallSite(*activation_, callerPC_, callerFP_);
-        } else if (offsetFromEntry == PushedFP) {
-            // The return address and caller's fp have been pushed on the stack; fp
+        } else if (offsetFromEntry == PushedTLS) {
+            // The return address and caller's TLS have been pushed on the stack; fp
             // is still the caller's fp.
             callerPC_ = sp[1];
-            callerFP_ = sp[0];
+            callerFP_ = fp;
             AssertMatchesCallSite(*activation_, callerPC_, callerFP_);
-        } else if (offsetFromEntry == PushedTLS) {
+        } else if (offsetFromEntry == PushedFP) {
             // The full Frame has been pushed; fp is still the caller's fp.
             MOZ_ASSERT(fp == CallerFPFromFP(sp));
             callerPC_ = ReturnAddressFromFP(sp);
             callerFP_ = fp;
             AssertMatchesCallSite(*activation_, callerPC_, callerFP_);
-        } else if (offsetInModule == codeRange->ret() - PoppedTLS) {
-            // The TLS field of the Frame has been popped.
+        } else if (offsetInModule == codeRange->ret() - PoppedFP) {
+            // The callerFP field of the Frame has been popped into fp.
             callerPC_ = sp[1];
-            callerFP_ = sp[0];
+            callerFP_ = fp;
         } else if (offsetInModule == codeRange->ret()) {
             // Both the TLS and callerFP fields have been popped and fp now
             // points to the caller's frame.
             callerPC_ = sp[0];
             callerFP_ = fp;
             AssertMatchesCallSite(*activation_, callerPC_, callerFP_);
         } else {
             // Not in the prologue/epilogue.
@@ -774,8 +793,36 @@ wasm::TraceActivations(JSContext* cx, co
         if (iter.activation()->isWasm()) {
             for (FrameIterator fi(iter.activation()->asWasm()); !fi.done(); ++fi) {
                 if (fi.hasInstance())
                     fi.instance()->trace(trc);
             }
         }
     }
 }
+
+Instance*
+wasm::LookupFaultingInstance(WasmActivation* activation, void* pc, void* fp)
+{
+    // Assume bug-caused faults can be raised at any PC and apply the logic of
+    // ProfilingFrameIterator to reject any pc outside the (post-prologue,
+    // pre-epilogue) body of a wasm function. This is exhaustively tested by the
+    // simulators which call this function at every load/store before even
+    // knowing whether there is a fault.
+
+    Code* code = activation->compartment()->wasm.lookupCode(pc);
+    if (!code)
+        return nullptr;
+
+    const CodeRange* codeRange = code->lookupRange(pc);
+    if (!codeRange || !codeRange->isFunction())
+        return nullptr;
+
+    size_t offsetInModule = ((uint8_t*)pc) - code->segment().base();
+    if (offsetInModule < codeRange->funcNormalEntry() + SetFP)
+        return nullptr;
+    if (offsetInModule >= codeRange->ret() - PoppedFP)
+        return nullptr;
+
+    Instance* instance = reinterpret_cast<Frame*>(fp)->tls->instance;
+    MOZ_RELEASE_ASSERT(&instance->code() == code);
+    return instance;
+}
--- a/js/src/wasm/WasmFrameIterator.h
+++ b/js/src/wasm/WasmFrameIterator.h
@@ -133,12 +133,18 @@ GenerateFunctionPrologue(jit::MacroAssem
 void
 GenerateFunctionEpilogue(jit::MacroAssembler& masm, unsigned framePushed, FuncOffsets* offsets);
 
 // Mark all instance objects live on the stack.
 
 void
 TraceActivations(JSContext* cx, const CooperatingContext& target, JSTracer* trc);
 
+// Given a fault at pc with register fp, return the faulting instance if there
+// is such a plausible instance, and otherwise null.
+
+Instance*
+LookupFaultingInstance(WasmActivation* activation, void* pc, void* fp);
+
 } // namespace wasm
 } // namespace js
 
 #endif // wasm_frame_iterator_h
--- a/js/src/wasm/WasmSignalHandlers.cpp
+++ b/js/src/wasm/WasmSignalHandlers.cpp
@@ -64,16 +64,17 @@ class AutoSetHandlingSegFault
         MOZ_ASSERT(cx->handlingSegFault);
         cx->handlingSegFault = false;
     }
 };
 
 #if defined(XP_WIN)
 # define XMM_sig(p,i) ((p)->Xmm##i)
 # define EIP_sig(p) ((p)->Eip)
+# define EBP_sig(p) ((p)->Ebp)
 # define RIP_sig(p) ((p)->Rip)
 # define RAX_sig(p) ((p)->Rax)
 # define RCX_sig(p) ((p)->Rcx)
 # define RDX_sig(p) ((p)->Rdx)
 # define RBX_sig(p) ((p)->Rbx)
 # define RSP_sig(p) ((p)->Rsp)
 # define RBP_sig(p) ((p)->Rbp)
 # define RSI_sig(p) ((p)->Rsi)
@@ -84,16 +85,17 @@ class AutoSetHandlingSegFault
 # define R11_sig(p) ((p)->R11)
 # define R12_sig(p) ((p)->R12)
 # define R13_sig(p) ((p)->R13)
 # define R14_sig(p) ((p)->R14)
 # define R15_sig(p) ((p)->R15)
 #elif defined(__OpenBSD__)
 # define XMM_sig(p,i) ((p)->sc_fpstate->fx_xmm[i])
 # define EIP_sig(p) ((p)->sc_eip)
+# define EBP_sig(p) ((p)->sc_ebp)
 # define RIP_sig(p) ((p)->sc_rip)
 # define RAX_sig(p) ((p)->sc_rax)
 # define RCX_sig(p) ((p)->sc_rcx)
 # define RDX_sig(p) ((p)->sc_rdx)
 # define RBX_sig(p) ((p)->sc_rbx)
 # define RSP_sig(p) ((p)->sc_rsp)
 # define RBP_sig(p) ((p)->sc_rbp)
 # define RSI_sig(p) ((p)->sc_rsi)
@@ -105,52 +107,56 @@ class AutoSetHandlingSegFault
 # define R12_sig(p) ((p)->sc_r12)
 # define R13_sig(p) ((p)->sc_r13)
 # define R14_sig(p) ((p)->sc_r14)
 # define R15_sig(p) ((p)->sc_r15)
 #elif defined(__linux__) || defined(SOLARIS)
 # if defined(__linux__)
 #  define XMM_sig(p,i) ((p)->uc_mcontext.fpregs->_xmm[i])
 #  define EIP_sig(p) ((p)->uc_mcontext.gregs[REG_EIP])
+#  define EBP_sig(p) ((p)->uc_mcontext.gregs[REG_EBP])
 # else
 #  define XMM_sig(p,i) ((p)->uc_mcontext.fpregs.fp_reg_set.fpchip_state.xmm[i])
 #  define EIP_sig(p) ((p)->uc_mcontext.gregs[REG_PC])
+#  define EBP_sig(p) ((p)->uc_mcontext.gregs[REG_EBP])
 # endif
 # define RIP_sig(p) ((p)->uc_mcontext.gregs[REG_RIP])
 # define RAX_sig(p) ((p)->uc_mcontext.gregs[REG_RAX])
 # define RCX_sig(p) ((p)->uc_mcontext.gregs[REG_RCX])
 # define RDX_sig(p) ((p)->uc_mcontext.gregs[REG_RDX])
 # define RBX_sig(p) ((p)->uc_mcontext.gregs[REG_RBX])
 # define RSP_sig(p) ((p)->uc_mcontext.gregs[REG_RSP])
 # define RBP_sig(p) ((p)->uc_mcontext.gregs[REG_RBP])
 # define RSI_sig(p) ((p)->uc_mcontext.gregs[REG_RSI])
 # define RDI_sig(p) ((p)->uc_mcontext.gregs[REG_RDI])
 # define R8_sig(p) ((p)->uc_mcontext.gregs[REG_R8])
 # define R9_sig(p) ((p)->uc_mcontext.gregs[REG_R9])
 # define R10_sig(p) ((p)->uc_mcontext.gregs[REG_R10])
-# define R11_sig(p) ((p)->uc_mcontext.gregs[REG_R11])
 # define R12_sig(p) ((p)->uc_mcontext.gregs[REG_R12])
 # define R13_sig(p) ((p)->uc_mcontext.gregs[REG_R13])
 # define R14_sig(p) ((p)->uc_mcontext.gregs[REG_R14])
 # if defined(__linux__) && defined(__arm__)
+#  define R11_sig(p) ((p)->uc_mcontext.arm_fp)
 #  define R15_sig(p) ((p)->uc_mcontext.arm_pc)
 # else
+#  define R11_sig(p) ((p)->uc_mcontext.gregs[REG_R11])
 #  define R15_sig(p) ((p)->uc_mcontext.gregs[REG_R15])
 # endif
 # if defined(__linux__) && defined(__aarch64__)
 #  define EPC_sig(p) ((p)->uc_mcontext.pc)
 # endif
 # if defined(__linux__) && defined(__mips__)
 #  define EPC_sig(p) ((p)->uc_mcontext.pc)
 #  define RSP_sig(p) ((p)->uc_mcontext.gregs[29])
 #  define RFP_sig(p) ((p)->uc_mcontext.gregs[30])
 # endif
 #elif defined(__NetBSD__)
 # define XMM_sig(p,i) (((struct fxsave64*)(p)->uc_mcontext.__fpregs)->fx_xmm[i])
 # define EIP_sig(p) ((p)->uc_mcontext.__gregs[_REG_EIP])
+# define EBP_sig(p) ((p)->uc_mcontext.__gregs[_REG_EBP])
 # define RIP_sig(p) ((p)->uc_mcontext.__gregs[_REG_RIP])
 # define RAX_sig(p) ((p)->uc_mcontext.__gregs[_REG_RAX])
 # define RCX_sig(p) ((p)->uc_mcontext.__gregs[_REG_RCX])
 # define RDX_sig(p) ((p)->uc_mcontext.__gregs[_REG_RDX])
 # define RBX_sig(p) ((p)->uc_mcontext.__gregs[_REG_RBX])
 # define RSP_sig(p) ((p)->uc_mcontext.__gregs[_REG_RSP])
 # define RBP_sig(p) ((p)->uc_mcontext.__gregs[_REG_RBP])
 # define RSI_sig(p) ((p)->uc_mcontext.__gregs[_REG_RSI])
@@ -165,16 +171,17 @@ class AutoSetHandlingSegFault
 # define R15_sig(p) ((p)->uc_mcontext.__gregs[_REG_R15])
 #elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
 # if defined(__DragonFly__)
 #  define XMM_sig(p,i) (((union savefpu*)(p)->uc_mcontext.mc_fpregs)->sv_xmm.sv_xmm[i])
 # else
 #  define XMM_sig(p,i) (((struct savefpu*)(p)->uc_mcontext.mc_fpstate)->sv_xmm[i])
 # endif
 # define EIP_sig(p) ((p)->uc_mcontext.mc_eip)
+# define EBP_sig(p) ((p)->uc_mcontext.mc_ebp)
 # define RIP_sig(p) ((p)->uc_mcontext.mc_rip)
 # define RAX_sig(p) ((p)->uc_mcontext.mc_rax)
 # define RCX_sig(p) ((p)->uc_mcontext.mc_rcx)
 # define RDX_sig(p) ((p)->uc_mcontext.mc_rdx)
 # define RBX_sig(p) ((p)->uc_mcontext.mc_rbx)
 # define RSP_sig(p) ((p)->uc_mcontext.mc_rsp)
 # define RBP_sig(p) ((p)->uc_mcontext.mc_rbp)
 # define RSI_sig(p) ((p)->uc_mcontext.mc_rsi)
@@ -188,17 +195,19 @@ class AutoSetHandlingSegFault
 # define R14_sig(p) ((p)->uc_mcontext.mc_r14)
 # if defined(__FreeBSD__) && defined(__arm__)
 #  define R15_sig(p) ((p)->uc_mcontext.__gregs[_REG_R15])
 # else
 #  define R15_sig(p) ((p)->uc_mcontext.mc_r15)
 # endif
 #elif defined(XP_DARWIN)
 # define EIP_sig(p) ((p)->uc_mcontext->__ss.__eip)
+# define EBP_sig(p) ((p)->uc_mcontext->__ss.__ebp)
 # define RIP_sig(p) ((p)->uc_mcontext->__ss.__rip)
+# define RBP_sig(p) ((p)->uc_mcontext->__ss.__rbp)
 # define R15_sig(p) ((p)->uc_mcontext->__ss.__pc)
 #else
 # error "Don't know how to read/write to the thread state via the mcontext_t."
 #endif
 
 #if defined(XP_WIN)
 # include "jswin.h"
 #else
@@ -330,36 +339,51 @@ struct macos_arm_context {
 #  error Unsupported architecture
 # endif
 #else
 # define EMULATOR_CONTEXT CONTEXT
 #endif
 
 #if defined(_M_X64) || defined(__x86_64__)
 # define PC_sig(p) RIP_sig(p)
+# define FP_sig(p) RBP_sig(p)
 #elif defined(_M_IX86) || defined(__i386__)
 # define PC_sig(p) EIP_sig(p)
+# define FP_sig(p) EBP_sig(p)
 #elif defined(__arm__)
 # define PC_sig(p) R15_sig(p)
+# define FP_sig(p) R11_sig(p)
 #elif defined(__aarch64__)
 # define PC_sig(p) EPC_sig(p)
+# define FP_sig(p) RFP_sig(p)
 #elif defined(__mips__)
 # define PC_sig(p) EPC_sig(p)
+# define FP_sig(p) RFP_sig(p)
 #endif
 
 static uint8_t**
 ContextToPC(CONTEXT* context)
 {
 #ifdef JS_CODEGEN_NONE
     MOZ_CRASH();
 #else
     return reinterpret_cast<uint8_t**>(&PC_sig(context));
 #endif
 }
 
+uint8_t*
+ContextToFP(CONTEXT* context)
+{
+#ifdef JS_CODEGEN_NONE
+    MOZ_CRASH();
+#else
+    return reinterpret_cast<uint8_t*>(FP_sig(context));
+#endif
+}
+
 #if defined(WASM_HUGE_MEMORY)
 MOZ_COLD static void
 SetFPRegToNaN(size_t size, void* fp_reg)
 {
     MOZ_RELEASE_ASSERT(size <= Simd128DataSize);
     memset(fp_reg, 0, Simd128DataSize);
     switch (size) {
       case 4: *static_cast<float*>(fp_reg) = GenericNaN(); break;
@@ -785,41 +809,45 @@ HandleFault(PEXCEPTION_POINTERS exceptio
     if (!cx || cx->handlingSegFault)
         return false;
     AutoSetHandlingSegFault handling(cx);
 
     WasmActivation* activation = cx->wasmActivationStack();
     if (!activation)
         return false;
 
-    const Instance* instance = activation->compartment()->wasm.lookupInstanceDeprecated(pc);
+    Code* code = activation->compartment()->wasm.lookupCode(pc);
+    if (!code)
+        return false;
+
+    if (!code->segment().containsFunctionPC(pc)) {
+        // On Windows, it is possible for InterruptRunningCode to execute
+        // between a faulting heap access and the handling of the fault due
+        // to InterruptRunningCode's use of SuspendThread. When this happens,
+        // after ResumeThread, the exception handler is called with pc equal to
+        // CodeSegment.interrupt, which is logically wrong. The Right Thing would
+        // be for the OS to make fault-handling atomic (so that CONTEXT.pc was
+        // always the logically-faulting pc). Fortunately, we can detect this
+        // case and silence the exception ourselves (the exception will
+        // retrigger after the interrupt jumps back to resumePC).
+        return pc == code->segment().interruptCode() &&
+               code->segment().containsFunctionPC(activation->resumePC());
+    }
+
+    const Instance* instance = LookupFaultingInstance(activation, pc, ContextToFP(context));
     if (!instance)
         return false;
 
     uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(record->ExceptionInformation[1]);
 
     // This check isn't necessary, but, since we can, check anyway to make
     // sure we aren't covering up a real bug.
     if (!IsHeapAccessAddress(*instance, faultingAddress))
         return false;
 
-    if (!instance->codeSegment().containsFunctionPC(pc)) {
-        // On Windows, it is possible for InterruptRunningCode to execute
-        // between a faulting heap access and the handling of the fault due
-        // to InterruptRunningCode's use of SuspendThread. When this happens,
-        // after ResumeThread, the exception handler is called with pc equal to
-        // instance.interrupt, which is logically wrong. The Right Thing would
-        // be for the OS to make fault-handling atomic (so that CONTEXT.pc was
-        // always the logically-faulting pc). Fortunately, we can detect this
-        // case and silence the exception ourselves (the exception will
-        // retrigger after the interrupt jumps back to resumePC).
-        return pc == instance->codeSegment().interruptCode() &&
-               instance->codeSegment().containsFunctionPC(activation->resumePC());
-    }
-
     HandleMemoryAccess(context, pc, faultingAddress, *instance, ppc);
     return true;
 }
 
 static LONG WINAPI
 WasmFaultHandler(LPEXCEPTION_POINTERS exception)
 {
     if (HandleFault(exception))
@@ -847,16 +875,30 @@ ContextToPC(EMULATOR_CONTEXT* context)
     static_assert(sizeof(context->thread.__pc) == sizeof(void*),
                   "stored IP should be compile-time pointer-sized");
     return reinterpret_cast<uint8_t**>(&context->thread.__pc);
 # else
 #  error Unsupported architecture
 # endif
 }
 
+static void*
+ContextToFP(EMULATOR_CONTEXT* context)
+{
+# if defined(__x86_64__)
+    return (void*)context->thread.__rbp;
+# elif defined(__i386__)
+    return (void*)context->thread.uts.ts32.__ebp;
+# elif defined(__arm__)
+    return (void*)context->thread.__fp;
+# else
+#  error Unsupported architecture
+# endif
+}
+
 // This definition was generated by mig (the Mach Interface Generator) for the
 // routine 'exception_raise' (exc.defs).
 #pragma pack(4)
 typedef struct {
     mach_msg_header_t Head;
     /* start of the kernel processed data */
     mach_msg_body_t msgh_body;
     mach_msg_port_descriptor_t thread;
@@ -922,17 +964,17 @@ HandleMachException(JSContext* cx, const
 
     if (request.body.exception != EXC_BAD_ACCESS || request.body.codeCnt != 2)
         return false;
 
     WasmActivation* activation = cx->wasmActivationStack();
     if (!activation)
         return false;
 
-    const Instance* instance = activation->compartment()->wasm.lookupInstanceDeprecated(pc);
+    const Instance* instance = LookupFaultingInstance(activation, pc, ContextToFP(&context));
     if (!instance || !instance->codeSegment().containsFunctionPC(pc))
         return false;
 
     uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(request.body.code[1]);
 
     // This check isn't necessary, but, since we can, check anyway to make
     // sure we aren't covering up a real bug.
     if (!IsHeapAccessAddress(*instance, faultingAddress))
@@ -1129,17 +1171,17 @@ HandleFault(int signum, siginfo_t* info,
     if (!cx || cx->handlingSegFault)
         return false;
     AutoSetHandlingSegFault handling(cx);
 
     WasmActivation* activation = cx->wasmActivationStack();
     if (!activation)
         return false;
 
-    const Instance* instance = activation->compartment()->wasm.lookupInstanceDeprecated(pc);
+    const Instance* instance = LookupFaultingInstance(activation, pc, ContextToFP(context));
     if (!instance || !instance->codeSegment().containsFunctionPC(pc))
         return false;
 
     uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(info->si_addr);
 
     // Although it's not strictly necessary, to make sure we're not covering up
     // any real bugs, check that the faulting address is indeed in the
     // instance's memory.
@@ -1240,27 +1282,27 @@ RedirectJitCodeToInterruptCheck(JSContex
     RedirectIonBackedgesToInterruptCheck(cx);
 
     if (WasmActivation* activation = cx->wasmActivationStack()) {
 #ifdef JS_SIMULATOR
         (void)ContextToPC(context);  // silence static 'unused' errors
 
         void* pc = cx->simulator()->get_pc_as<void*>();
 
-        const Instance* instance = activation->compartment()->wasm.lookupInstanceDeprecated(pc);
-        if (instance && instance->codeSegment().containsFunctionPC(pc))
-            cx->simulator()->set_resume_pc(instance->codeSegment().interruptCode());
+        const Code* code = activation->compartment()->wasm.lookupCode(pc);
+        if (code && code->segment().containsFunctionPC(pc))
+            cx->simulator()->set_resume_pc(code->segment().interruptCode());
 #else
         uint8_t** ppc = ContextToPC(context);
         uint8_t* pc = *ppc;
 
-        const Instance* instance = activation->compartment()->wasm.lookupInstanceDeprecated(pc);
-        if (instance && instance->codeSegment().containsFunctionPC(pc)) {
+        const Code* code = activation->compartment()->wasm.lookupCode(pc);
+        if (code && code->segment().containsFunctionPC(pc)) {
             activation->setResumePC(pc);
-            *ppc = instance->codeSegment().interruptCode();
+            *ppc = code->segment().interruptCode();
             return true;
         }
 #endif
     }
 
     return false;
 }
 
@@ -1482,10 +1524,10 @@ js::wasm::IsPCInWasmCode(void *pc)
         return false;
 
     MOZ_RELEASE_ASSERT(!cx->handlingSegFault);
 
     WasmActivation* activation = cx->wasmActivationStack();
     if (!activation)
         return false;
 
-    return !!activation->compartment()->wasm.lookupInstanceDeprecated(pc);
+    return !!activation->compartment()->wasm.lookupCode(pc);
 }
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -1441,31 +1441,38 @@ class MemoryAccess
         insnOffset_ += delta;
         if (hasTrapOutOfLineCode())
             trapOutOfLineOffset_ += delta;
     }
 };
 
 WASM_DECLARE_POD_VECTOR(MemoryAccess, MemoryAccessVector)
 
-// As an invariant across architectures, within wasm code:
-//   $sp % WasmStackAlignment = (sizeof(wasm::Frame) + masm.framePushed) % WasmStackAlignment
-// Thus, wasm::Frame represents the bytes pushed after the call (which occurred
-// with a WasmStackAlignment-aligned StackPointer) that are not included in
-// masm.framePushed.
+// wasm::Frame represents the bytes pushed by the call instruction and the fixed
+// prologue generated by wasm::GenerateCallablePrologue.
+//
+// Across all architectures it is assumed that, before the call instruction, the
+// stack pointer is WasmStackAlignment-aligned. Thus after the prologue, and
+// before the function has made its stack reservation, the stack alignment is
+// sizeof(Frame) % WasmStackAlignment.
+//
+// During MacroAssembler code generation, the bytes pushed after the wasm::Frame
+// are counted by masm.framePushed. Thus, the stack alignment at any point in
+// time is (sizeof(wasm::Frame) + masm.framePushed) % WasmStackAlignment.
 
 struct Frame
 {
+    // The caller's Frame*. See GenerateCallableEpilogue for why this must be
+    // the first field of wasm::Frame (in a downward-growing stack).
+    uint8_t* callerFP;
+
     // The saved value of WasmTlsReg on entry to the function. This is
     // effectively the callee's instance.
     TlsData* tls;
 
-    // The caller's Frame*.
-    uint8_t* callerFP;
-
     // The return address pushed by the call (in the case of ARM/MIPS the return
     // address is pushed by the first instruction of the prologue).
     void* returnAddress;
 
     // Helper functions:
 
     Instance* instance() const { return tls->instance; }
 };
--- a/js/xpconnect/loader/ChromeScriptLoader.cpp
+++ b/js/xpconnect/loader/ChromeScriptLoader.cpp
@@ -164,18 +164,21 @@ AsyncScriptCompiler::Run()
 
     return NS_OK;
 }
 
 void
 AsyncScriptCompiler::FinishCompile(JSContext* aCx)
 {
     Rooted<JSScript*> script(aCx, JS::FinishOffThreadScript(aCx, mToken));
-
-    Finish(aCx, script);
+    if (script) {
+        Finish(aCx, script);
+    } else {
+        Reject(aCx);
+    }
 }
 
 
 void
 AsyncScriptCompiler::Finish(JSContext* aCx, Handle<JSScript*> aScript)
 {
     RefPtr<PrecompiledScript> result = new PrecompiledScript(mGlobalObject, aScript, mOptions);
 
--- a/js/xpconnect/tests/unit/test_compileScript.js
+++ b/js/xpconnect/tests/unit/test_compileScript.js
@@ -48,8 +48,25 @@ add_task(function*() {
   equal(sandbox2.bar, null);
 
   obj = script2.executeInGlobal(sandbox2);
   equal(obj, undefined, "No-return script has no return value");
 
   equal(Cu.getObjectPrincipal(sandbox2.bar).origin, "http://example.org", "Object value origin is correct");
   equal(sandbox2.bar.foo, "\u00ae", "Object value has the correct charset");
 });
+
+add_task(function* test_syntaxError() {
+  // Generate an artificially large script to force off-main-thread
+  // compilation.
+  let scriptUrl = `data:,${";".repeat(1024 * 1024)}(`;
+
+  yield Assert.rejects(
+    ChromeUtils.compileScript(scriptUrl),
+    SyntaxError);
+
+  // Generate a small script to force main thread compilation.
+  scriptUrl = `data:,;(`;
+
+  yield Assert.rejects(
+    ChromeUtils.compileScript(scriptUrl),
+    SyntaxError);
+});
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -7180,27 +7180,31 @@ PresShell::CanDispatchEvent(const Widget
 nsresult
 PresShell::HandleEvent(nsIFrame* aFrame,
                        WidgetGUIEvent* aEvent,
                        bool aDontRetargetEvents,
                        nsEventStatus* aEventStatus,
                        nsIContent** aTargetContent)
 {
 #ifdef MOZ_TASK_TRACER
-  // Make touch events, mouse events and hardware key events to be the source
-  // events of TaskTracer, and originate the rest correlation tasks from here.
-  SourceEventType type = SourceEventType::Unknown;
-  if (aEvent->AsTouchEvent()) {
-    type = SourceEventType::Touch;
-  } else if (aEvent->AsMouseEvent()) {
-    type = SourceEventType::Mouse;
-  } else if (aEvent->AsKeyboardEvent()) {
-    type = SourceEventType::Key;
-  }
-  AutoSourceEvent taskTracerEvent(type);
+  Maybe<AutoSourceEvent> taskTracerEvent;
+  if (MOZ_UNLIKELY(IsStartLogging())) {
+    // Make touch events, mouse events and hardware key events to be
+    // the source events of TaskTracer, and originate the rest
+    // correlation tasks from here.
+    SourceEventType type = SourceEventType::Unknown;
+    if (aEvent->AsTouchEvent()) {
+      type = SourceEventType::Touch;
+    } else if (aEvent->AsMouseEvent()) {
+      type = SourceEventType::Mouse;
+    } else if (aEvent->AsKeyboardEvent()) {
+      type = SourceEventType::Key;
+    }
+    taskTracerEvent.emplace(type);
+  }
 #endif
 
   NS_ASSERTION(aFrame, "aFrame should be not null");
 
   if (sPointerEventEnabled) {
     AutoWeakFrame weakFrame(aFrame);
     nsCOMPtr<nsIContent> targetContent;
     DispatchPointerFromMouseOrTouch(this, aFrame, aEvent, aDontRetargetEvents,
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -629,18 +629,18 @@ load text-overflow-bug666751-2.html
 load text-overflow-bug670564.xhtml
 load text-overflow-bug671796.xhtml
 load text-overflow-bug713610.html
 load text-overflow-form-elements.html
 load text-overflow-iframe.html
 asserts-if(Android,2-4) asserts-if(!Android,4) asserts-if(stylo,0) load 1225005.html # bug 682647 and bug 448083
 load 1233191.html
 asserts-if(stylo,0-15) load 1271765.html # bug 1324684
-asserts(2) load 1272983-1.html # bug 1324654 # bug 586628
-asserts(2) load 1272983-2.html # bug 1324654 # bug 586628
+asserts(2) load 1272983-1.html # bug 586628
+asserts(2) load 1272983-2.html # bug 586628
 load 1275059.html
 load 1278007.html
 load 1278080.html
 load 1279814.html
 load large-border-radius-dashed.html
 load large-border-radius-dashed2.html
 load large-border-radius-dotted.html
 load large-border-radius-dotted2.html
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -8035,18 +8035,17 @@ bool nsDisplayMask::TryMerge(nsDisplayIt
     return false;
   }
   if (aItem->GetClipChain() != GetClipChain()) {
     return false;
   }
 
   // Do not merge if mFrame has mask. Continuation frames should apply mask
   // independently(just like nsDisplayBackgroundImage).
-  const nsStyleSVGReset *style = mFrame->StyleSVGReset();
-  if (style->mMask.HasLayerWithImage()) {
+  if (mFrame->StyleSVGReset()->HasMask()) {
     return false;
   }
 
   nsDisplayMask* other = static_cast<nsDisplayMask*>(aItem);
   MergeFromTrackingMergedFrames(other);
   mEffectsBounds.UnionRect(mEffectsBounds,
     other->mEffectsBounds + other->mFrame->GetOffsetTo(mFrame));
 
--- a/layout/reftests/async-scrolling/reftest-stylo.list
+++ b/layout/reftests/async-scrolling/reftest-stylo.list
@@ -1,9 +1,8 @@
-# DO NOT EDIT! This is a auto-generated temporary list for Stylo testing
 == bg-fixed-1.html bg-fixed-1.html
 == bg-fixed-cover-1.html bg-fixed-cover-1.html
 == bg-fixed-cover-2.html bg-fixed-cover-2.html
 skip-if(!asyncPan) == bg-fixed-cover-3.html bg-fixed-cover-3.html
 skip-if(!asyncPan) == bg-fixed-child.html bg-fixed-child.html
 == bg-fixed-child-clip-1.html bg-fixed-child-clip-1.html
 == bg-fixed-child-clip-2.html bg-fixed-child-clip-2.html
 fuzzy(1,246) fuzzy-if(skiaContent,2,160) fuzzy-if(browserIsRemote&&d2d,53,185) skip-if(!asyncPan) == bg-fixed-child-mask.html bg-fixed-child-mask.html
@@ -27,31 +26,31 @@ fails == position-fixed-transformed-1.ht
 == split-opacity-layers-1.html split-opacity-layers-1.html
 == sticky-pos-scrollable-1.html sticky-pos-scrollable-1.html
 == sticky-pos-scrollable-2.html sticky-pos-scrollable-2.html
 skip-if(!asyncPan) == sticky-pos-scrollable-3.html sticky-pos-scrollable-3.html
 fails == fixed-pos-scrollable-1.html fixed-pos-scrollable-1.html
 == culling-1.html culling-1.html
 == position-fixed-iframe-1.html position-fixed-iframe-1.html
 == position-fixed-iframe-2.html position-fixed-iframe-2.html
-fuzzy-if(skiaContent,1,11300) skip-if(!asyncPan) == position-fixed-in-scroll-container.html position-fixed-in-scroll-container.html
+fuzzy-if(skiaContent,1,11300) skip-if(!asyncPan) fails-if(stylo&&browserIsRemote) == position-fixed-in-scroll-container.html position-fixed-in-scroll-container.html
 == position-fixed-inside-sticky-1.html position-fixed-inside-sticky-1.html
 == position-fixed-inside-sticky-2.html position-fixed-inside-sticky-2.html
 == group-opacity-surface-size-1.html group-opacity-surface-size-1.html
 skip-if(!asyncPan) == position-sticky-transformed.html position-sticky-transformed.html
 == offscreen-prerendered-active-opacity.html offscreen-prerendered-active-opacity.html
 fails == offscreen-clipped-blendmode-1.html offscreen-clipped-blendmode-1.html
 fails == offscreen-clipped-blendmode-2.html offscreen-clipped-blendmode-2.html
 fuzzy-if(Android,6,4) skip == offscreen-clipped-blendmode-3.html offscreen-clipped-blendmode-3.html
-fuzzy-if(Android,6,4) skip-if(!asyncPan) == offscreen-clipped-blendmode-4.html offscreen-clipped-blendmode-4.html
+fuzzy-if(Android,6,4) skip-if(!asyncPan) fails-if(stylo&&browserIsRemote) == offscreen-clipped-blendmode-4.html offscreen-clipped-blendmode-4.html
 fails == perspective-scrolling-1.html perspective-scrolling-1.html
 fails == perspective-scrolling-2.html perspective-scrolling-2.html
-fuzzy-if(Android,7,4) skip-if(!asyncPan) == perspective-scrolling-3.html perspective-scrolling-3.html
-fuzzy-if(Android,7,4) skip-if(!asyncPan) == perspective-scrolling-4.html perspective-scrolling-4.html
-pref(apz.disable_for_scroll_linked_effects,true) skip-if(!asyncPan) == disable-apz-for-sle-pages.html disable-apz-for-sle-pages.html
+fuzzy-if(Android,7,4) skip-if(!asyncPan) fails-if(stylo&&browserIsRemote) == perspective-scrolling-3.html perspective-scrolling-3.html
+fuzzy-if(Android,7,4) skip-if(!asyncPan) fails-if(stylo&&browserIsRemote) == perspective-scrolling-4.html perspective-scrolling-4.html
+pref(apz.disable_for_scroll_linked_effects,true) skip-if(!asyncPan) fails-if(stylo&&browserIsRemote) == disable-apz-for-sle-pages.html disable-apz-for-sle-pages.html
 == background-blend-mode-1.html background-blend-mode-1.html
 fails == opaque-fractional-displayport-1.html opaque-fractional-displayport-1.html
 fails == opaque-fractional-displayport-2.html opaque-fractional-displayport-2.html
 
 # for the following tests, we want to disable the low-precision buffer
 # as it will expand the displayport beyond what the test specifies in
 # its reftest-displayport attributes, and interfere with where we expect
 # checkerboarding to occur
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -966,17 +966,17 @@ fails == 413027-3.html 413027-3-ref.html
 == 413286-2a.html 413286-2-ref.html
 == 413286-2b.html 413286-2-ref.html
 == 413286-2c.html 413286-2-ref.html
 == 413286-3.html 413286-3-ref.html
 == 413286-4a.html 413286-4-ref.html
 == 413286-4b.html 413286-4-ref.html
 == 413286-5.html 413286-5-ref.html
 == 413286-6.html 413286-6-ref.html
-skip-if(cocoaWidget) == 413292-1.html 413292-1-ref.html # disabling due to failure loading on some mac tinderboxes. See bug 432954
+== 413292-1.html 413292-1-ref.html
 fuzzy-if(Android,11,17) == 413361-1.html 413361-1-ref.html # bug 1128229
 == 413840-background-unchanged.html 413840-background-unchanged-ref.html
 == 413840-ltr-offsets.html 413840-ltr-offsets-ref.html
 == 413840-rtl-offsets.html 413840-rtl-offsets-ref.html
 == 413840-pushed-line-bullet.html 413840-pushed-line-bullet-ref.html
 == 413840-bullet-first-line.html 413840-bullet-first-line-ref.html
 == 413982.html 413982-ref.html
 == 414123.xhtml 414123-ref.xhtml
--- a/layout/reftests/invalidation/reftest-stylo.list
+++ b/layout/reftests/invalidation/reftest-stylo.list
@@ -1,16 +1,15 @@
-# DO NOT EDIT! This is a auto-generated temporary list for Stylo testing
 == table-repaint-a.html table-repaint-a.html
 == table-repaint-b.html table-repaint-b.html
 == table-repaint-c.html table-repaint-c.html
 == table-repaint-d.html table-repaint-d.html
 == 540247-1.xul 540247-1.xul
 fails == 543681-1.html 543681-1.html
-fails == 1243409-1.html 1243409-1.html
+fails-if(stylo&&!browserIsRemote) == 1243409-1.html 1243409-1.html
 fails == test-image-layers.html test-image-layers.html
 fails == test-image-layers-multiple-displayitem.html test-image-layers-multiple-displayitem.html
 pref(layout.animated-image-layers.enabled,true) skip-if(Android||gtkWidget) == test-animated-image-layers.html test-animated-image-layers.html
 skip-if(stylo) == test-animated-image-layers-background.html test-animated-image-layers-background.html
 == box-shadow-border-radius.html box-shadow-border-radius.html
 == filter-userspace-offset.svg?offsetContainer=rect filter-userspace-offset.svg?offsetContainer=rect
 == filter-userspace-offset.svg?offsetContainer=use filter-userspace-offset.svg?offsetContainer=use
 == filter-userspace-offset.svg?offsetContainer=innerSVG filter-userspace-offset.svg?offsetContainer=innerSVG
--- a/layout/reftests/layers/reftest-stylo.list
+++ b/layout/reftests/layers/reftest-stylo.list
@@ -1,25 +1,24 @@
-# DO NOT EDIT! This is a auto-generated temporary list for Stylo testing
 == move-to-background-1.html move-to-background-1.html
 == component-alpha-exit-1.html component-alpha-exit-1.html
 fails == pull-background-1.html pull-background-1.html
 fails == pull-background-2.html pull-background-2.html
 fails == pull-background-3.html pull-background-3.html
 fails == pull-background-4.html pull-background-4.html
 fails == pull-background-5.html pull-background-5.html
 fails == pull-background-6.html pull-background-6.html
 # The animated-position tests are disabled for intermittent failures / passes, bug 1150941
 fails == pull-background-animated-position-1.html pull-background-animated-position-1.html
 fails == pull-background-animated-position-2.html pull-background-animated-position-2.html
 fails == pull-background-animated-position-3.html pull-background-animated-position-3.html
 fails == pull-background-animated-position-4.html pull-background-animated-position-4.html
 fails == pull-background-animated-position-5.html pull-background-animated-position-5.html
 fails == pull-background-displayport-1.html pull-background-displayport-1.html
 fails == pull-background-displayport-2.html pull-background-displayport-2.html
-skip-if(!asyncPan) == pull-background-displayport-3.html pull-background-displayport-3.html
-skip-if(!asyncPan) == pull-background-displayport-4.html pull-background-displayport-4.html
-skip-if(!asyncPan) == pull-background-displayport-5.html pull-background-displayport-5.html
-skip-if(!asyncPan) == pull-background-displayport-6.html pull-background-displayport-6.html
+skip-if(!asyncPan) fails-if(stylo&&browserIsRemote) == pull-background-displayport-3.html pull-background-displayport-3.html
+skip-if(!asyncPan) fails-if(stylo&&browserIsRemote) == pull-background-displayport-4.html pull-background-displayport-4.html
+skip-if(!asyncPan) fails-if(stylo&&browserIsRemote) == pull-background-displayport-5.html pull-background-displayport-5.html
+skip-if(!asyncPan) fails-if(stylo&&browserIsRemote) == pull-background-displayport-6.html pull-background-displayport-6.html
 fuzzy(2,30150) == opacity-blending.html opacity-blending.html
 fuzzy(16,5) == mask-layer-transform.html mask-layer-transform.html
 fails == forced-bg-color-outside-visible-region.html forced-bg-color-outside-visible-region.html
 fails == layerize-over-fixed-bg-1.html layerize-over-fixed-bg-1.html
--- a/layout/reftests/ogg-video/reftest-stylo.list
+++ b/layout/reftests/ogg-video/reftest-stylo.list
@@ -1,11 +1,11 @@
 # DO NOT EDIT! This is a auto-generated temporary list for Stylo testing
 # NOTE: bug 1084564 covers "fails"/"skip" annotations for android below:
-== 444-1.html 444-1.html
+fuzzy-if(stylo,2377,28) == 444-1.html 444-1.html
 == aspect-ratio-1a.xhtml aspect-ratio-1a.xhtml
 == aspect-ratio-1b.xhtml aspect-ratio-1b.xhtml
 fails-if(Android) skip-if(gtkWidget) HTTP(..) == aspect-ratio-2a.xhtml aspect-ratio-2a.xhtml
 fails-if(Android) skip-if(gtkWidget) HTTP(..) == aspect-ratio-2b.xhtml aspect-ratio-2b.xhtml
 HTTP(..) == aspect-ratio-3a.xhtml aspect-ratio-3a.xhtml
 HTTP(..) == aspect-ratio-3b.xhtml aspect-ratio-3b.xhtml
 == encoded-aspect-ratio-1.html encoded-aspect-ratio-1.html
 == basic-1.xhtml basic-1.xhtml
--- a/layout/reftests/position-sticky/reftest-stylo.list
+++ b/layout/reftests/position-sticky/reftest-stylo.list
@@ -36,17 +36,17 @@ fails == scrollframe-auto-1.html scrollf
 == left-right-3.html left-right-3.html
 == containing-block-1.html containing-block-1.html
 == overconstrained-1.html overconstrained-1.html
 == overconstrained-2.html overconstrained-2.html
 == overconstrained-3.html overconstrained-3.html
 fails == inline-1.html inline-1.html
 fails == inline-2.html inline-2.html
 fails == inline-3.html inline-3.html
-skip-if(!asyncPan) == inline-4.html inline-4.html
+skip-if(!asyncPan) fails-if(stylo&&browserIsRemote) == inline-4.html inline-4.html
 == column-contain-1a.html column-contain-1a.html
 == column-contain-1b.html column-contain-1b.html
 == column-contain-2.html column-contain-2.html
 fails == block-in-inline-1.html block-in-inline-1.html
 fails == block-in-inline-2.html block-in-inline-2.html
 fails == block-in-inline-3.html block-in-inline-3.html
 fails == block-in-inline-continuations.html block-in-inline-continuations.html
 == inner-table-1.html inner-table-1.html
--- a/layout/reftests/reftest-sanity/reftest-stylo.list
+++ b/layout/reftests/reftest-sanity/reftest-stylo.list
@@ -1,9 +1,8 @@
-# DO NOT EDIT! This is a auto-generated temporary list for Stylo testing
 == data:text/html,<body> data:text/html,<body>
 == data:text/plain, data:text/plain,
 fails == data:text/plain,HELLO data:text/plain,HELLO # Bug 1341637
 
 # these tests make sure async reftests work:
 == test-async.xul test-async.xul
 == test-async.html test-async.html
 
@@ -95,17 +94,17 @@ needs-focus == data:text/plain, data:tex
 fails == 647192-1.html 647192-1.html
 fails == 656041-1.html 656041-1.html
 pref(dom.meta-viewport.enabled,true) skip-if(!browserIsRemote||layersOMTC) == test-displayport-bg.html test-displayport-bg.html
 
 # IPC Position-fixed frames/layers test
 # Fixed layers are temporarily disabled (bug 656167).
 pref(dom.meta-viewport.enabled,true) skip-if(!browserIsRemote) == test-pos-fixed.html test-pos-fixed.html
 pref(dom.meta-viewport.enabled,true) skip-if(!browserIsRemote) == test-bg-attachment-fixed.html test-bg-attachment-fixed.html
-pref(dom.meta-viewport.enabled,true) skip-if(!browserIsRemote) == test-pos-fixed-transform.html test-pos-fixed-transform.html
+pref(dom.meta-viewport.enabled,true) skip-if(stylo) == test-pos-fixed-transform.html test-pos-fixed-transform.html # Bug 1348754
 
 # reftest syntax: require-or
 fails require-or(unrecognizedCondition,skip) script scripttest-fail.html
 fails require-or(true&&unrecognizedCondition,skip) script scripttest-fail.html
 fails require-or(unrecognizedCondition&&true,skip) script scripttest-fail.html
 fails require-or(unrecognizedCondition,fails) script scripttest-fail.html
 require-or(true,fails) script scripttest-pass.html
 require-or(true&&true,fails) script scripttest-pass.html
--- a/layout/reftests/w3c-css/received/reftest-stylo.list
+++ b/layout/reftests/w3c-css/received/reftest-stylo.list
@@ -231,17 +231,17 @@ fails == css-values-3/ch-unit-001.html c
 == css-values-3/vh-em-inherit.html css-values-3/vh-em-inherit.html
 == css-values-3/vh-inherit.html css-values-3/vh-inherit.html
 == css-values-3/vh-interpolate-pct.html css-values-3/vh-interpolate-pct.html
 == css-values-3/vh-interpolate-px.html css-values-3/vh-interpolate-px.html
 == css-values-3/vh-interpolate-vh.html css-values-3/vh-interpolate-vh.html
 fails == css-values-3/vh-support-atviewport.html css-values-3/vh-support-atviewport.html
 == css-values-3/vh-support-margin.html css-values-3/vh-support-margin.html
 == css-values-3/vh-support-transform-origin.html css-values-3/vh-support-transform-origin.html
-== css-values-3/vh-support-transform-translate.html css-values-3/vh-support-transform-translate.html
+fuzzy-if(stylo,160000,251) == css-values-3/vh-support-transform-translate.html css-values-3/vh-support-transform-translate.html
 == css-values-3/vh-support.html css-values-3/vh-support.html
 == css-values-3/vh-zero-support.html css-values-3/vh-zero-support.html
 skip-if(stylo) == css-values-3/vh_not_refreshing_on_chrome.html css-values-3/vh_not_refreshing_on_chrome.html # bug 1346829
 skip-if(stylo) == css-values-3/vh_not_refreshing_on_chrome_iframe.html css-values-3/vh_not_refreshing_on_chrome_iframe.html # Why does this fail to load?
 fails needs-focus == selectors-4/focus-within-001.html selectors-4/focus-within-001.html
 fails needs-focus == selectors-4/focus-within-002.html selectors-4/focus-within-002.html
 fails needs-focus == selectors-4/focus-within-003.html selectors-4/focus-within-003.html
 fails needs-focus == selectors-4/focus-within-004.html selectors-4/focus-within-004.html
--- a/layout/reftests/w3c-css/submitted/ui3/reftest-stylo.list
+++ b/layout/reftests/w3c-css/submitted/ui3/reftest-stylo.list
@@ -1,11 +1,10 @@
-# DO NOT EDIT! This is a auto-generated temporary list for Stylo testing
 == box-sizing-border-box-001.xht box-sizing-border-box-001.xht
 == box-sizing-border-box-002.xht box-sizing-border-box-002.xht
 == box-sizing-border-box-003.xht box-sizing-border-box-003.xht
 == box-sizing-border-box-004.xht box-sizing-border-box-004.xht
 == box-sizing-content-box-001.xht box-sizing-content-box-001.xht
 == box-sizing-content-box-002.xht box-sizing-content-box-002.xht
 == box-sizing-content-box-003.xht box-sizing-content-box-003.xht
 == box-sizing-replaced-001.xht box-sizing-replaced-001.xht
 == box-sizing-replaced-002.xht box-sizing-replaced-002.xht
-== box-sizing-replaced-003.xht box-sizing-replaced-003.xht
+random-if(stylo&&browserIsRemote) == box-sizing-replaced-003.xht box-sizing-replaced-003.xht
--- a/layout/style/FontFaceSet.cpp
+++ b/layout/style/FontFaceSet.cpp
@@ -25,16 +25,17 @@
 #include "nsCSSParser.h"
 #include "nsDeviceContext.h"
 #include "nsFontFaceLoader.h"
 #include "nsIConsoleService.h"
 #include "nsIContentPolicy.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIDocShell.h"
 #include "nsIDocument.h"
+#include "nsILoadContext.h"
 #include "nsINetworkPredictor.h"
 #include "nsIPresShell.h"
 #include "nsIPrincipal.h"
 #include "nsISupportsPriority.h"
 #include "nsIWebNavigation.h"
 #include "nsNetUtil.h"
 #include "nsIProtocolHandler.h"
 #include "nsIInputStream.h"
--- a/layout/style/StyleAnimationValue.h
+++ b/layout/style/StyleAnimationValue.h
@@ -252,19 +252,16 @@ public:
    * The overload that takes an rvalue StyleAnimationValue reference
    * transfers ownership for some resources such that the |aComputedValue|
    * does not depend on the lifetime of |aSpecifiedValue|.
    *
    * @param aProperty      The property whose value we're uncomputing.
    * @param aComputedValue The computed value to be converted.
    * @param [out] aSpecifiedValue The resulting specified value.
    * @return true on success, false on failure.
-   *
-   * These functions are not MOZ_MUST_USE because failing to check the return
-   * value is common and reasonable.
    */
   static MOZ_MUST_USE bool
   UncomputeValue(nsCSSPropertyID aProperty,
                  const StyleAnimationValue& aComputedValue,
                  nsCSSValue& aSpecifiedValue);
   static MOZ_MUST_USE bool
   UncomputeValue(nsCSSPropertyID aProperty,
                  StyleAnimationValue&& aComputedValue,
--- a/layout/style/nsStyleContext.cpp
+++ b/layout/style/nsStyleContext.cpp
@@ -1175,18 +1175,18 @@ nsStyleContext::CalcStyleDifferenceInter
 
   if (hint & nsChangeHint_UpdateContainingBlock) {
     // If a struct returned nsChangeHint_UpdateContainingBlock, that
     // means that one property's influence on whether we're a containing
     // block for abs-pos or fixed-pos elements has changed.  However, we
     // only need to return the hint if the overall computation of
     // whether we establish a containing block has changed.
 
-    // This depends on data in nsStyleDisplay and nsStyleEffects, so we
-    // do it here.
+    // This depends on data in nsStyleDisplay, nsStyleEffects and
+    // nsStyleSVGReset, so we do it here.
 
     // Note that it's perhaps good for this test to be last because it
     // doesn't use Peek* functions to get the structs on the old
     // context.  But this isn't a big concern because these struct
     // getters should be called during frame construction anyway.
     if (ThreadsafeStyleDisplay()->IsAbsPosContainingBlockForAppropriateFrame(this) ==
         aNewContext->ThreadsafeStyleDisplay()->
           IsAbsPosContainingBlockForAppropriateFrame(aNewContext) &&
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -1216,22 +1216,46 @@ nsStyleSVGReset::CalcDifference(const ns
              mFloodColor    != aNewData.mFloodColor    ||
              mLightingColor != aNewData.mLightingColor ||
              mStopOpacity   != aNewData.mStopOpacity   ||
              mFloodOpacity  != aNewData.mFloodOpacity  ||
              mMaskType      != aNewData.mMaskType) {
     hint |= nsChangeHint_RepaintFrame;
   }
 
+  if (HasMask() != aNewData.HasMask()) {
+    // A change from/to being a containing block for position:fixed.
+    hint |= nsChangeHint_UpdateContainingBlock;
+  }
+
   hint |= mMask.CalcDifference(aNewData.mMask,
                                nsStyleImageLayers::LayerType::Mask);
 
   return hint;
 }
 
+bool
+nsStyleSVGReset::HasMask() const
+{
+  for (uint32_t i = 0; i < mMask.mImageCount; i++) {
+    // mMask.mLayers[i].mSourceURI can be nullptr if mask-image prop value is
+    // <element-reference> or <gradient>.
+    // mMask.mLayers[i].mImage can be empty if mask-image prop value is a
+    // reference to SVG mask element.
+    //
+    // So we need to test both mSourceURI and mImage.
+    if ((mMask.mLayers[i].mSourceURI && mMask.mLayers[i].mSourceURI->GetURI()) ||
+        !mMask.mLayers[i].mImage.IsEmpty()) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
 // nsStyleSVGPaint implementation
 nsStyleSVGPaint::nsStyleSVGPaint(nsStyleSVGPaintType aType)
   : mType(aType)
   , mFallbackColor(NS_RGB(0, 0, 0))
 {
   MOZ_ASSERT(aType == nsStyleSVGPaintType(0) ||
              aType == eStyleSVGPaintType_None ||
              aType == eStyleSVGPaintType_Color);
@@ -2642,34 +2666,16 @@ nsStyleImageLayers::CalcDifference(const
       mPositionYCount != aNewLayers.mPositionYCount ||
       mSizeCount != aNewLayers.mSizeCount) {
     hint |= nsChangeHint_NeutralChange;
   }
 
   return hint;
 }
 
-bool
-nsStyleImageLayers::HasLayerWithImage() const
-{
-  for (uint32_t i = 0; i < mImageCount; i++) {
-    // mLayers[i].mSourceURI can be nullptr if mask-image prop value is
-    // <element-reference> or <gradient>
-    // mLayers[i].mImage can be empty if mask-image prop value is a reference
-    // to SVG mask element.
-    // So we need to test both mSourceURI and mImage.
-    if ((mLayers[i].mSourceURI && mLayers[i].mSourceURI->GetURI()) ||
-        !mLayers[i].mImage.IsEmpty()) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
 nsStyleImageLayers&
 nsStyleImageLayers::operator=(const nsStyleImageLayers& aOther)
 {
   mAttachmentCount = aOther.mAttachmentCount;
   mClipCount = aOther.mClipCount;
   mOriginCount = aOther.mOriginCount;
   mRepeatCount = aOther.mRepeatCount;
   mPositionXCount = aOther.mPositionXCount;
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -823,17 +823,16 @@ struct nsStyleImageLayers {
     for (uint32_t i = 0; i < mImageCount; ++i) {
       mLayers[i].ResolveImage(aContext);
     }
   }
 
   nsChangeHint CalcDifference(const nsStyleImageLayers& aNewLayers,
                               nsStyleImageLayers::LayerType aType) const;
 
-  bool HasLayerWithImage() const;
   nsStyleImageLayers& operator=(const nsStyleImageLayers& aOther);
   nsStyleImageLayers& operator=(nsStyleImageLayers&& aOther);
   bool operator==(const nsStyleImageLayers& aOther) const;
 
   static const nsCSSPropertyID kBackgroundLayerTable[];
   static const nsCSSPropertyID kMaskLayerTable[];
 
   #define NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(var_, layers_) \
@@ -3621,16 +3620,18 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   void Destroy(nsPresContext* aContext);
 
   nsChangeHint CalcDifference(const nsStyleSVGReset& aNewData) const;
 
   bool HasClipPath() const {
     return mClipPath.GetType() != mozilla::StyleShapeSourceType::None;
   }
 
+  bool HasMask() const;
+
   bool HasNonScalingStroke() const {
     return mVectorEffect == NS_STYLE_VECTOR_EFFECT_NON_SCALING_STROKE;
   }
 
   nsStyleImageLayers    mMask;
   mozilla::StyleShapeSource mClipPath;// [reset]
   nscolor          mStopColor;        // [reset]
   nscolor          mFloodColor;       // [reset]
--- a/layout/svg/nsSVGEffects.cpp
+++ b/layout/svg/nsSVGEffects.cpp
@@ -584,17 +584,17 @@ nsSVGEffects::GetEffectProperties(nsIFra
     nsCOMPtr<nsIURI> pathURI = nsSVGEffects::GetClipPathURI(aFrame);
     result.mClipPath =
       GetPaintingProperty(pathURI, aFrame, ClipPathProperty());
   } else {
     result.mClipPath = nullptr;
   }
 
   MOZ_ASSERT(style->mMask.mImageCount > 0);
-  result.mMask = style->mMask.HasLayerWithImage()
+  result.mMask = style->HasMask()
                  ? GetOrCreateMaskProperty(aFrame) : nullptr;
 
   return result;
 }
 
 nsSVGPaintServerFrame *
 nsSVGEffects::GetPaintServer(nsIFrame* aTargetFrame,
                              nsStyleSVGPaint nsStyleSVG::* aPaint,
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -132,26 +132,24 @@ bool
 nsSVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame* aFrame)
 {
   // Even when SVG display lists are disabled, returning true for SVG frames
   // does not adversely affect any of our callers. Therefore we don't bother
   // checking the SDL prefs here, since we don't know if we're being called for
   // painting or hit-testing anyway.
   const nsStyleSVGReset *style = aFrame->StyleSVGReset();
   return aFrame->StyleEffects()->HasFilters() ||
-         style->HasClipPath() ||
-         style->mMask.HasLayerWithImage();
+         style->HasClipPath() || style->HasMask();
 }
 
 bool
 nsSVGIntegrationUtils::UsingMaskOrClipPathForFrame(const nsIFrame* aFrame)
 {
   const nsStyleSVGReset *style = aFrame->StyleSVGReset();
-  return style->HasClipPath() ||
-         style->mMask.HasLayerWithImage();
+  return style->HasClipPath() || style->HasMask();
 }
 
 nsPoint
 nsSVGIntegrationUtils::GetOffsetToBoundingBox(nsIFrame* aFrame)
 {
   if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
     // Do NOT call GetAllInFlowRectsUnion for SVG - it will get the
     // covered region relative to the nsSVGOuterSVGFrame, which is absolutely
@@ -1133,17 +1131,17 @@ bool
 PaintFrameCallback::operator()(gfxContext* aContext,
                                const gfxRect& aFillRect,
                                const SamplingFilter aSamplingFilter,
                                const gfxMatrix& aTransform)
 {
   if (mFrame->GetStateBits() & NS_FRAME_DRAWING_AS_PAINTSERVER)
     return false;
 
-  mFrame->AddStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
+  AutoSetRestorePaintServerState paintServer(mFrame);
 
   aContext->Save();
 
   // Clip to aFillRect so that we don't paint outside.
   aContext->NewPath();
   aContext->Rectangle(aFillRect);
   aContext->Clip();
 
@@ -1201,18 +1199,16 @@ PaintFrameCallback::operator()(gfxContex
                               nsDisplayListBuilderMode::PAINTING,
                               flags);
 
     aContext->Restore();
   }
 
   aContext->Restore();
 
-  mFrame->RemoveStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
-
   return true;
 }
 
 /* static */ already_AddRefed<gfxDrawable>
 nsSVGIntegrationUtils::DrawableFromPaintServer(nsIFrame*         aFrame,
                                                nsIFrame*         aTarget,
                                                const nsSize&     aPaintServerSize,
                                                const IntSize& aRenderSize,
--- a/layout/svg/nsSVGPaintServerFrame.h
+++ b/layout/svg/nsSVGPaintServerFrame.h
@@ -21,16 +21,42 @@ class DrawTarget;
 } // namespace mozilla
 
 class gfxContext;
 class gfxPattern;
 class nsStyleContext;
 
 struct gfxRect;
 
+/**
+ * RAII class used to temporarily set and remove the
+ * NS_FRAME_DRAWING_AS_PAINTSERVER frame state bit while a frame is being
+ * drawn as a paint server.
+ */
+class MOZ_RAII AutoSetRestorePaintServerState
+{
+public:
+  explicit AutoSetRestorePaintServerState(
+             nsIFrame* aFrame
+             MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
+    mFrame(aFrame)
+  {
+    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+    mFrame->AddStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
+  }
+  ~AutoSetRestorePaintServerState()
+  {
+    mFrame->RemoveStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
+  }
+
+private:
+  nsIFrame* mFrame;
+  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
 class nsSVGPaintServerFrame : public nsSVGContainerFrame
 {
 protected:
   typedef mozilla::gfx::DrawTarget DrawTarget;
 
   explicit nsSVGPaintServerFrame(nsStyleContext* aContext)
     : nsSVGContainerFrame(aContext)
   {
--- a/layout/svg/nsSVGPatternFrame.cpp
+++ b/layout/svg/nsSVGPatternFrame.cpp
@@ -375,17 +375,17 @@ nsSVGPatternFrame::PaintPattern(const Dr
   if (aSource->IsFrameOfType(nsIFrame::eSVGGeometry)) {
     // Set the geometrical parent of the pattern we are rendering
     patternWithChildren->mSource = static_cast<SVGGeometryFrame*>(aSource);
   }
 
   // Delay checking NS_FRAME_DRAWING_AS_PAINTSERVER bit until here so we can
   // give back a clear surface if there's a loop
   if (!(patternWithChildren->GetStateBits() & NS_FRAME_DRAWING_AS_PAINTSERVER)) {
-    patternWithChildren->AddStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
+    AutoSetRestorePaintServerState paintServer(patternWithChildren);
     for (nsIFrame* kid = firstKid; kid;
          kid = kid->GetNextSibling()) {
       // The CTM of each frame referencing us can be different
       nsSVGDisplayableFrame* SVGFrame = do_QueryFrame(kid);
       if (SVGFrame) {
         SVGFrame->NotifySVGChanged(nsSVGDisplayableFrame::TRANSFORM_CHANGED);
       }
       gfxMatrix tm = *(patternWithChildren->mCTM);
@@ -393,17 +393,16 @@ nsSVGPatternFrame::PaintPattern(const Dr
         tm = static_cast<nsSVGElement*>(kid->GetContent())->
                PrependLocalTransformsTo(tm, eUserSpaceToParent);
       }
       DrawResult result = nsSVGUtils::PaintFrameWithEffects(kid, *ctx, tm);
       if (result != DrawResult::SUCCESS) {
         return nullptr;
       }
     }
-    patternWithChildren->RemoveStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
   }
 
   patternWithChildren->mSource = nullptr;
 
   if (aGraphicOpacity != 1.0f) {
     ctx->PopGroupAndBlend();
     ctx->Restore();
   }
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -13,16 +13,17 @@
 #include "timecard.h"
 
 #include "jsapi.h"
 #include "nspr.h"
 #include "nss.h"
 #include "pk11pub.h"
 
 #include "nsNetCID.h"
+#include "nsILoadContext.h"
 #include "nsIProperty.h"
 #include "nsIPropertyBag2.h"
 #include "nsIServiceManager.h"
 #include "nsISimpleEnumerator.h"
 #include "nsServiceManagerUtils.h"
 #include "nsISocketTransportService.h"
 #include "nsIConsoleService.h"
 #include "nsThreadUtils.h"
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -3624,17 +3624,17 @@ Tab.prototype = {
     this.browser.addEventListener("DOMAudioPlaybackStopped", this, true);
     this.browser.addEventListener("DOMWindowClose", this, true);
     this.browser.addEventListener("DOMWillOpenModalDialog", this, true);
     this.browser.addEventListener("DOMAutoComplete", this, true);
     this.browser.addEventListener("blur", this, true);
     this.browser.addEventListener("pageshow", this, true);
     this.browser.addEventListener("MozApplicationManifest", this, true);
     this.browser.addEventListener("TabPreZombify", this, true);
-    this.browser.addEventListener("DOMServiceWorkerFocusClient", this, true);
+    this.browser.addEventListener("DOMWindowFocus", this, true);
 
     // Note that the XBL binding is untrusted
     this.browser.addEventListener("PluginBindingAttached", this, true, true);
     this.browser.addEventListener("VideoBindingAttached", this, true, true);
     this.browser.addEventListener("VideoBindingCast", this, true, true);
 
     Services.obs.addObserver(this, "before-first-paint", false);
     Services.obs.addObserver(this, "media-playback", false);
@@ -3740,17 +3740,17 @@ Tab.prototype = {
     this.browser.removeEventListener("DOMAudioPlaybackStopped", this, true);
     this.browser.removeEventListener("DOMWindowClose", this, true);
     this.browser.removeEventListener("DOMWillOpenModalDialog", this, true);
     this.browser.removeEventListener("DOMAutoComplete", this, true);
     this.browser.removeEventListener("blur", this, true);
     this.browser.removeEventListener("pageshow", this, true);
     this.browser.removeEventListener("MozApplicationManifest", this, true);
     this.browser.removeEventListener("TabPreZombify", this, true);
-    this.browser.removeEventListener("DOMServiceWorkerFocusClient", this, true);
+    this.browser.removeEventListener("DOMWindowFocus", this, true);
 
     this.browser.removeEventListener("PluginBindingAttached", this, true, true);
     this.browser.removeEventListener("VideoBindingAttached", this, true, true);
     this.browser.removeEventListener("VideoBindingCast", this, true, true);
 
     Services.obs.removeObserver(this, "before-first-paint");
     Services.obs.removeObserver(this, "media-playback");
     Services.obs.removeObserver(this, "media-playback-resumed");
@@ -4263,17 +4263,17 @@ Tab.prototype = {
         break;
       }
 
       case "MozApplicationManifest": {
         OfflineApps.offlineAppRequested(aEvent.originalTarget.defaultView);
         break;
       }
 
-      case "DOMServiceWorkerFocusClient": {
+      case "DOMWindowFocus": {
         GlobalEventDispatcher.sendRequest({
           type: "Tab:Select",
           tabID: this.id,
           foreground: true,
         });
         break;
       }
 
--- a/netwerk/cache2/CacheIOThread.cpp
+++ b/netwerk/cache2/CacheIOThread.cpp
@@ -322,18 +322,20 @@ nsresult CacheIOThread::DispatchAfterPen
   return DispatchInternal(do_AddRef(aRunnable), OPEN_PRIORITY);
 }
 
 nsresult CacheIOThread::DispatchInternal(already_AddRefed<nsIRunnable> aRunnable,
 					 uint32_t aLevel)
 {
   nsCOMPtr<nsIRunnable> runnable(aRunnable);
 #ifdef MOZ_TASK_TRACER
-  runnable = tasktracer::CreateTracedRunnable(runnable.forget());
-  (static_cast<tasktracer::TracedRunnable*>(runnable.get()))->DispatchTask();
+  if (tasktracer::IsStartLogging()) {
+      runnable = tasktracer::CreateTracedRunnable(runnable.forget());
+      (static_cast<tasktracer::TracedRunnable*>(runnable.get()))->DispatchTask();
+  }
 #endif
 
   if (NS_WARN_IF(!runnable))
     return NS_ERROR_NULL_POINTER;
 
   mMonitor.AssertCurrentThreadOwns();
 
   ++mQueueLength[aLevel];
--- a/netwerk/dns/ChildDNSService.cpp
+++ b/netwerk/dns/ChildDNSService.cpp
@@ -6,16 +6,17 @@
 #include "nsIDNSListener.h"
 #include "nsIIOService.h"
 #include "nsIThread.h"
 #include "nsThreadUtils.h"
 #include "nsIXPConnect.h"
 #include "nsIPrefService.h"
 #include "nsIProtocolProxyService.h"
 #include "nsNetCID.h"
+#include "mozilla/SystemGroup.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/net/DNSListenerProxy.h"
 #include "nsServiceManagerUtils.h"
 
 namespace mozilla {
 namespace net {
 
 //-----------------------------------------------------------------------------
@@ -168,19 +169,17 @@ ChildDNSService::AsyncResolveExtendedNat
 
   // We need original listener for the pending requests hash.
   nsIDNSListener *originalListener = listener;
 
   // make sure JS callers get notification on the main thread
   nsCOMPtr<nsIEventTarget> target = target_;
   nsCOMPtr<nsIXPConnectWrappedJS> wrappedListener = do_QueryInterface(listener);
   if (wrappedListener && !target) {
-    nsCOMPtr<nsIThread> mainThread;
-    NS_GetMainThread(getter_AddRefs(mainThread));
-    target = do_QueryInterface(mainThread);
+    target = SystemGroup::EventTargetFor(TaskCategory::Network);
   }
   if (target) {
     // Guarantee listener freed on main thread.  Not sure we need this in child
     // (or in parent in nsDNSService.cpp) but doesn't hurt.
     listener = new DNSListenerProxy(listener, target);
   }
 
   RefPtr<DNSRequestChild> childReq =
--- a/netwerk/dns/DNSRequestChild.cpp
+++ b/netwerk/dns/DNSRequestChild.cpp
@@ -2,16 +2,17 @@
 /* vim: set sw=2 ts=8 et tw=80 : */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/net/ChildDNSService.h"
 #include "mozilla/net/DNSRequestChild.h"
 #include "mozilla/net/NeckoChild.h"
+#include "mozilla/SystemGroup.h"
 #include "mozilla/Unused.h"
 #include "nsIDNSRecord.h"
 #include "nsHostResolver.h"
 #include "nsTArray.h"
 #include "nsNetAddr.h"
 #include "nsIThread.h"
 #include "nsThreadUtils.h"
 
@@ -205,21 +206,28 @@ DNSRequestChild::DNSRequestChild(const n
 {
 }
 
 void
 DNSRequestChild::StartRequest()
 {
   // we can only do IPDL on the main thread
   if (!NS_IsMainThread()) {
-    NS_DispatchToMainThread(
+    SystemGroup::Dispatch(
+      "StartDNSRequestChild",
+      TaskCategory::Other,
       NewRunnableMethod(this, &DNSRequestChild::StartRequest));
     return;
   }
 
+  nsCOMPtr<nsIEventTarget> systemGroupEventTarget
+    = SystemGroup::EventTargetFor(TaskCategory::Other);
+
+  gNeckoChild->SetEventTargetForActor(this, systemGroupEventTarget);
+
   // Send request to Parent process.
   gNeckoChild->SendPDNSRequestConstructor(this, mHost, mOriginAttributes,
                                           mFlags, mNetworkInterface);
   mIPCOpen = true;
 
   // IPDL holds a reference until IPDL channel gets destroyed
   AddIPDLReference();
 }
@@ -301,17 +309,19 @@ NS_IMPL_ISUPPORTS(DNSRequestChild,
 // DNSRequestChild::nsICancelable
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 DNSRequestChild::Cancel(nsresult reason)
 {
   if(mIPCOpen) {
     // We can only do IPDL on the main thread
-    NS_DispatchToMainThread(
-      new CancelDNSRequestEvent(this, reason));
+    nsCOMPtr<nsIRunnable> runnable = new CancelDNSRequestEvent(this, reason);
+    SystemGroup::Dispatch("CancelDNSRequest",
+                          TaskCategory::Other,
+                          runnable.forget());
   }
   return NS_OK;
 }
 
 //------------------------------------------------------------------------------
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/ftp/FTPChannelChild.cpp
+++ b/netwerk/protocol/ftp/FTPChannelChild.cpp
@@ -1,20 +1,23 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set sw=2 ts=8 et tw=80 : */
 
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "mozilla/SystemGroup.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/net/ChannelDiverterChild.h"
 #include "mozilla/net/FTPChannelChild.h"
 #include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/TabChild.h"
+#include "nsContentUtils.h"
 #include "nsFtpProtocolHandler.h"
 #include "nsITabChild.h"
 #include "nsStringStream.h"
 #include "nsNetUtil.h"
 #include "base/compiler_specific.h"
 #include "mozilla/ipc/IPCStreamUtils.h"
 #include "mozilla/ipc/URIUtils.h"
 #include "SerializedLoadContext.h"
@@ -193,16 +196,19 @@ FTPChannelChild::AsyncOpen(::nsIStreamLi
   openArgs.entityID() = mEntityID;
   openArgs.uploadStream() = autoStream.TakeOptionalValue();
 
   nsCOMPtr<nsILoadInfo> loadInfo;
   GetLoadInfo(getter_AddRefs(loadInfo));
   rv = mozilla::ipc::LoadInfoToLoadInfoArgs(loadInfo, &openArgs.loadInfo());
   NS_ENSURE_SUCCESS(rv, rv);
 
+  // This must happen before the constructor message is sent.
+  EnsureDispatcher();
+
   gNeckoChild->
     SendPFTPChannelConstructor(this, tabChild, IPC::SerializedLoadContext(this),
                                openArgs);
 
   // The socket transport layer in the chrome process now has a logical ref to
   // us until OnStopRequest is called.
   AddIPDLReference();
 
@@ -603,17 +609,27 @@ FTPChannelChild::DoOnStopRequest(const n
         nsCOMPtr<nsIRunnable> alertEvent;
         if (aUseUTF8) {
           alertEvent = new nsFtpChildAsyncAlert(prompter,
                              NS_ConvertUTF8toUTF16(aErrorMsg));
         } else {
           alertEvent = new nsFtpChildAsyncAlert(prompter,
                              NS_ConvertASCIItoUTF16(aErrorMsg));
         }
-        NS_DispatchToMainThread(alertEvent);
+
+        if (mDispatcher) {
+          mDispatcher->Dispatch("FTPAlertEvent",
+                                TaskCategory::Other,
+                                alertEvent.forget());
+        } else {
+          // In case |mDispatcher| is null, dispatch by SystemGroup.
+          SystemGroup::Dispatch("FTPAlertEvent",
+                                TaskCategory::Other,
+                                alertEvent.forget());
+        }
       }
     }
 
     mListener = nullptr;
     mListenerContext = nullptr;
 
     if (mLoadGroup)
       mLoadGroup->RemoveRequest(this, nullptr, aChannelStatus);
@@ -815,16 +831,19 @@ FTPChannelChild::ConnectParent(uint32_t 
   NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
                                 NS_GET_IID(nsITabChild),
                                 getter_AddRefs(iTabChild));
   GetCallback(iTabChild);
   if (iTabChild) {
     tabChild = static_cast<mozilla::dom::TabChild*>(iTabChild.get());
   }
 
+  // This must happen before the constructor message is sent.
+  EnsureDispatcher();
+
   // The socket transport in the chrome process now holds a logical ref to us
   // until OnStopRequest, or we do a redirect, or we hit an IPDL error.
   AddIPDLReference();
 
   FTPChannelConnectArgs connectArgs(id);
 
   if (!gNeckoChild->SendPFTPChannelConstructor(this, tabChild,
                                                IPC::SerializedLoadContext(this),
@@ -919,11 +938,32 @@ FTPChannelChild::UnknownDecoderInvolvedO
 NS_IMETHODIMP
 FTPChannelChild::GetDivertingToParent(bool* aDiverting)
 {
   NS_ENSURE_ARG_POINTER(aDiverting);
   *aDiverting = mDivertingToParent;
   return NS_OK;
 }
 
+void
+FTPChannelChild::EnsureDispatcher()
+{
+  if (mDispatcher) {
+    return;
+  }
+
+  nsCOMPtr<nsILoadInfo> loadInfo;
+  GetLoadInfo(getter_AddRefs(loadInfo));
+
+  mDispatcher = nsContentUtils::GetDispatcherByLoadInfo(loadInfo);
+  if (!mDispatcher) {
+    return;
+  }
+
+  nsCOMPtr<nsIEventTarget> target =
+    mDispatcher->EventTargetFor(TaskCategory::Network);
+  gNeckoChild->SetEventTargetForActor(this, target);
+  mEventQ->RetargetDeliveryTo(target);
+}
+
 } // namespace net
 } // namespace mozilla
 
--- a/netwerk/protocol/ftp/FTPChannelChild.h
+++ b/netwerk/protocol/ftp/FTPChannelChild.h
@@ -18,16 +18,19 @@
 #include "nsIResumableChannel.h"
 #include "nsIChildChannel.h"
 #include "nsIDivertableChannel.h"
 
 #include "nsIStreamListener.h"
 #include "PrivateBrowsingChannel.h"
 
 namespace mozilla {
+
+class Dispatcher;
+
 namespace net {
 
 // This class inherits logic from nsBaseChannel that is not needed for an
 // e10s child channel, but it works.  At some point we could slice up
 // nsBaseChannel and have a new class that has only the common logic for
 // nsFTPChannel/FTPChannelChild.
 
 class FTPChannelChild final : public PFTPChannelChild
@@ -145,16 +148,20 @@ private:
   // Once set, OnData and possibly OnStop will be diverted to the parent.
   bool mDivertingToParent;
   // Once set, no OnStart/OnData/OnStop callbacks should be received from the
   // parent channel, nor dequeued from the ChannelEventQueue.
   bool mFlushedForDiversion;
   // Set if SendSuspend is called. Determines if SendResume is needed when
   // diverting callbacks to parent.
   bool mSuspendSent;
+
+  RefPtr<Dispatcher> mDispatcher;
+
+  void EnsureDispatcher();
 };
 
 inline bool
 FTPChannelChild::IsSuspended()
 {
   return mSuspendCount != 0;
 }
 
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1973,21 +1973,23 @@ HttpChannelChild::AsyncOpen(nsIStreamLis
 
   NS_ENSURE_TRUE(gNeckoChild != nullptr, NS_ERROR_FAILURE);
   NS_ENSURE_ARG_POINTER(listener);
   NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
   NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
 
   mAsyncOpenTime = TimeStamp::Now();
 #ifdef MOZ_TASK_TRACER
-  nsCOMPtr<nsIURI> uri;
-  GetURI(getter_AddRefs(uri));
-  nsAutoCString urispec;
-  uri->GetSpec(urispec);
-  tasktracer::AddLabel("HttpChannelChild::AsyncOpen %s", urispec.get());
+  if (tasktracer::IsStartLogging()) {
+    nsCOMPtr<nsIURI> uri;
+    GetURI(getter_AddRefs(uri));
+    nsAutoCString urispec;
+    uri->GetSpec(urispec);
+    tasktracer::AddLabel("HttpChannelChild::AsyncOpen %s", urispec.get());
+  }
 #endif
   
 
   // Port checked in parent, but duplicate here so we can return with error
   // immediately
   nsresult rv;
   rv = NS_CheckPortSafety(mURI);
   if (NS_FAILED(rv))
@@ -2065,56 +2067,37 @@ HttpChannelChild::AsyncOpen2(nsIStreamLi
 
 // Assigns an nsIEventTarget to our IPDL actor so that IPC messages are sent to
 // the correct DocGroup/TabGroup.
 void
 HttpChannelChild::SetEventTarget()
 {
   nsCOMPtr<nsILoadInfo> loadInfo;
   GetLoadInfo(getter_AddRefs(loadInfo));
-  if (!loadInfo) {
+
+  RefPtr<Dispatcher> dispatcher =
+    nsContentUtils::GetDispatcherByLoadInfo(loadInfo);
+
+  if (!dispatcher) {
     return;
   }
 
-  nsCOMPtr<nsIDOMDocument> domDoc;
-  loadInfo->GetLoadingDocument(getter_AddRefs(domDoc));
-  nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
-  RefPtr<Dispatcher> dispatcher;
-  if (doc) {
-    dispatcher = doc->GetDocGroup();
-  } else {
-    // There's no document yet, but this might be a top-level load where we can
-    // find a TabGroup.
-    uint64_t outerWindowId;
-    if (NS_FAILED(loadInfo->GetOuterWindowID(&outerWindowId))) {
-      // No window. This might be an add-on XHR, a service worker request, or
-      // something else.
-      return;
-    }
-    RefPtr<nsGlobalWindow> window = nsGlobalWindow::GetOuterWindowWithId(outerWindowId);
-    if (!window) {
-      return;
-    }
-
 #ifdef DEBUG
+  if (dispatcher->AsTabGroup()) {
     // We have a TabGroup. This must be a top-level load.
     bool isMainDocumentChannel;
     GetIsMainDocumentChannel(&isMainDocumentChannel);
     MOZ_ASSERT(isMainDocumentChannel);
+  }
 #endif
 
-    dispatcher = window->TabGroup();
-  }
-
-  if (dispatcher) {
-    nsCOMPtr<nsIEventTarget> target =
-      dispatcher->EventTargetFor(TaskCategory::Network);
-    gNeckoChild->SetEventTargetForActor(this, target);
-    mEventQ->RetargetDeliveryTo(target);
-  }
+  nsCOMPtr<nsIEventTarget> target =
+    dispatcher->EventTargetFor(TaskCategory::Network);
+  gNeckoChild->SetEventTargetForActor(this, target);
+  mEventQ->RetargetDeliveryTo(target);
 }
 
 nsresult
 HttpChannelChild::ContinueAsyncOpen()
 {
   nsCString appCacheClientId;
   if (mInheritApplicationCache) {
     // Pick up an application cache from the notification
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -5769,17 +5769,17 @@ nsHttpChannel::AsyncOpen(nsIStreamListen
                mLoadInfo->GetInitialSecurityCheckDone() ||
                (mLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
                 nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())),
                "security flags in loadInfo but asyncOpen2() not called");
 
     LOG(("nsHttpChannel::AsyncOpen [this=%p]\n", this));
 
 #ifdef MOZ_TASK_TRACER
-    {
+    if (tasktracer::IsStartLogging()) {
         uint64_t sourceEventId, parentTaskId;
         tasktracer::SourceEventType sourceEventType;
         GetCurTraceInfo(&sourceEventId, &parentTaskId, &sourceEventType);
         nsCOMPtr<nsIURI> uri;
         GetURI(getter_AddRefs(uri));
         nsAutoCString urispec;
         uri->GetSpec(urispec);
         tasktracer::AddLabel("nsHttpChannel::AsyncOpen %s", urispec.get());
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -1918,17 +1918,17 @@ nsHttpHandler::NewProxiedChannel2(nsIURI
                                   nsIChannel** result)
 {
     RefPtr<HttpBaseChannel> httpChannel;
 
     LOG(("nsHttpHandler::NewProxiedChannel [proxyInfo=%p]\n",
         givenProxyInfo));
 
 #ifdef MOZ_TASK_TRACER
-    {
+    if (tasktracer::IsStartLogging()) {
         nsAutoCString urispec;
         uri->GetSpec(urispec);
         tasktracer::AddLabel("nsHttpHandler::NewProxiedChannel2 %s", urispec.get());
     }
 #endif
 
     nsCOMPtr<nsProxyInfo> proxyInfo;
     if (givenProxyInfo) {
--- a/nsprpub/TAG-INFO
+++ b/nsprpub/TAG-INFO
@@ -1,1 +1,1 @@
-NSPR_4_14_BETA2
+NSPR_4_15_BETA1
--- a/nsprpub/configure
+++ b/nsprpub/configure
@@ -2483,17 +2483,17 @@ case $target_os in *\ *) target_os=`echo
 # The aliases save the names the user supplied, while $host etc.
 # will get canonicalized.
 test -n "$target_alias" &&
   test "$program_prefix$program_suffix$program_transform_name" = \
     NONENONEs,x,x, &&
   program_prefix=${target_alias}-
 
 MOD_MAJOR_VERSION=4
-MOD_MINOR_VERSION=14
+MOD_MINOR_VERSION=15
 MOD_PATCH_VERSION=0
 NSPR_MODNAME=nspr20
 _HAVE_PTHREADS=
 USE_PTHREADS=
 USE_USER_PTHREADS=
 USE_NSPR_THREADS=
 USE_N32=
 USE_X32=
@@ -5419,16 +5419,50 @@ rm -f core conftest.err conftest.$ac_obj
     { $as_echo "$as_me:${as_lineno-$LINENO}: result: $_res" >&5
 $as_echo "$_res" >&6; }
 else
     { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
 fi
 
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pragma diagnostic" >&5
+$as_echo_n "checking for pragma diagnostic... " >&6; }
+if test "$GNU_CC" = "1"; then
+    cat >dummy-hello.c <<EOF
+#ifdef _PR_HAS_PRAGMA_DIAGNOSTIC
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
+#endif
+int main() {
+    char *dummy = "";
+    return 0;
+}
+EOF
+    ${CC} -Werror=unused-but-set-variable -S dummy-hello.c -o dummy-hello.s 2>&5
+    if test $? != 0; then
+        ${CC} -Werror=unused-but-set-variable -D_PR_HAS_PRAGMA_DIAGNOSTIC -S dummy-hello.c -o dummy-hello.s 2>&5
+        if test $? = 0; then
+            CFLAGS="$CFLAGS -D_PR_HAS_PRAGMA_DIAGNOSTIC=1"
+            _res=yes
+        else
+            _res=no
+        fi
+    else
+        _res=no
+    fi
+    rm -f dummy-hello.c dummy-hello.s
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $_res" >&5
+$as_echo "$_res" >&6; }
+else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
 _SAVE_CFLAGS="$CFLAGS"
 CFLAGS="$CFLAGS -fprofile-generate -fprofile-correction"
 
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler supports -fprofile-generate" >&5
 $as_echo_n "checking whether C compiler supports -fprofile-generate... " >&6; }
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -6519,16 +6553,18 @@ fi
     $as_echo "#define XP_UNIX 1" >>confdefs.h
 
     $as_echo "#define DARWIN 1" >>confdefs.h
 
     $as_echo "#define HAVE_BSD_FLOCK 1" >>confdefs.h
 
     $as_echo "#define HAVE_SOCKLEN_T 1" >>confdefs.h
 
+    $as_echo "#define HAVE_POINTER_LOCALTIME_R 1" >>confdefs.h
+
     AS='$(CC) -x assembler-with-cpp'
     CFLAGS="$CFLAGS -Wall -fno-common"
     case "${target_cpu}" in
         arm*)
             CPU_ARCH=arm
             ;;
         i*86*|x86_64)
             if test -n "$USE_64"; then
@@ -6970,16 +7006,18 @@ tools are selected during the Xcode/Deve
         IMPL_STRATEGY=_PTH
     fi
     $as_echo "#define XP_UNIX 1" >>confdefs.h
 
     $as_echo "#define _GNU_SOURCE 1" >>confdefs.h
 
     $as_echo "#define HAVE_FCNTL_FILE_LOCKING 1" >>confdefs.h
 
+    $as_echo "#define HAVE_POINTER_LOCALTIME_R 1" >>confdefs.h
+
     case "${target}" in
     *-android*|*-linuxandroid*)
         OS_TARGET=Android
         $as_echo "#define LINUX 1" >>confdefs.h
 
         ;;
     *-linux*)
         $as_echo "#define LINUX 1" >>confdefs.h
--- a/nsprpub/configure.in
+++ b/nsprpub/configure.in
@@ -10,17 +10,17 @@ AC_CONFIG_SRCDIR([pr/include/nspr.h])
 
 AC_CONFIG_AUX_DIR(${srcdir}/build/autoconf)
 AC_CANONICAL_TARGET
 
 dnl ========================================================
 dnl = Defaults
 dnl ========================================================
 MOD_MAJOR_VERSION=4
-MOD_MINOR_VERSION=14
+MOD_MINOR_VERSION=15
 MOD_PATCH_VERSION=0
 NSPR_MODNAME=nspr20
 _HAVE_PTHREADS=
 USE_PTHREADS=
 USE_USER_PTHREADS=
 USE_NSPR_THREADS=
 USE_N32=
 USE_X32=
@@ -711,16 +711,50 @@ if test -n "$GNU_CC" && test -n "$GNU_CX
     fi
     rm -f dummy-hello.c dummy-hello.s dummy-hello.S dummy-hello a.out
     AC_MSG_RESULT([$_res])
 else
     AC_MSG_RESULT([no])
 fi
 
 dnl ========================================================
+dnl Check for pragma diagnostic
+dnl ========================================================
+
+AC_MSG_CHECKING([for pragma diagnostic])
+if test "$GNU_CC" = "1"; then
+    cat >dummy-hello.c <<EOF
+#ifdef _PR_HAS_PRAGMA_DIAGNOSTIC
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
+#endif
+int main() {
+    char *dummy = "";
+    return 0;
+}
+EOF
+    ${CC} -Werror=unused-but-set-variable -S dummy-hello.c -o dummy-hello.s 2>&5
+    if test $? != 0; then
+        ${CC} -Werror=unused-but-set-variable -D_PR_HAS_PRAGMA_DIAGNOSTIC -S dummy-hello.c -o dummy-hello.s 2>&5
+        if test $? = 0; then
+            CFLAGS="$CFLAGS -D_PR_HAS_PRAGMA_DIAGNOSTIC=1"
+            _res=yes
+        else
+            _res=no
+        fi
+    else
+        _res=no
+    fi
+    rm -f dummy-hello.c dummy-hello.s
+    AC_MSG_RESULT([$_res])
+else
+    AC_MSG_RESULT([no])
+fi
+
+dnl ========================================================
 dnl Profile guided optimization
 dnl ========================================================
 dnl Test for profiling options
 dnl Under gcc 3.4+, use -fprofile-generate/-fprofile-use
 
 _SAVE_CFLAGS="$CFLAGS"
 CFLAGS="$CFLAGS -fprofile-generate -fprofile-correction"
 
@@ -1344,16 +1378,17 @@ case "$target" in
 
     ;;
 
 *-darwin*)
     AC_DEFINE(XP_UNIX)
     AC_DEFINE(DARWIN)
     AC_DEFINE(HAVE_BSD_FLOCK)
     AC_DEFINE(HAVE_SOCKLEN_T)
+    AC_DEFINE(HAVE_POINTER_LOCALTIME_R)
     AS='$(CC) -x assembler-with-cpp'
     CFLAGS="$CFLAGS -Wall -fno-common"
     case "${target_cpu}" in
         arm*)
             CPU_ARCH=arm
             ;;
         i*86*|x86_64)
             if test -n "$USE_64"; then
@@ -1784,16 +1819,17 @@ tools are selected during the Xcode/Deve
 *-linux*|*-gnu*|*-k*bsd*-gnu|*-android*|*-linuxandroid*)
     if test -z "$USE_NSPR_THREADS"; then
         USE_PTHREADS=1
         IMPL_STRATEGY=_PTH
     fi
     AC_DEFINE(XP_UNIX)
     AC_DEFINE(_GNU_SOURCE)
     AC_DEFINE(HAVE_FCNTL_FILE_LOCKING)
+    AC_DEFINE(HAVE_POINTER_LOCALTIME_R)
     case "${target}" in
     *-android*|*-linuxandroid*)
         OS_TARGET=Android
         AC_DEFINE(LINUX)
         ;;
     *-linux*)
         AC_DEFINE(LINUX)
         ;;
--- a/nsprpub/lib/ds/plvrsion.c
+++ b/nsprpub/lib/ds/plvrsion.c
@@ -69,25 +69,32 @@ PRVersionDescription VERSION_DESC_NAME =
  */
 static char rcsid[] = "$Header: NSPR " PR_VERSION _DEBUG_STRING
         "  " _BUILD_STRING " $";
 static char sccsid[] = "@(#)NSPR " PR_VERSION _DEBUG_STRING
         "  " _BUILD_STRING;
 
 #endif /* XP_UNIX */
 
+#ifdef _PR_HAS_PRAGMA_DIAGNOSTIC
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
+#endif
 PR_IMPLEMENT(const PRVersionDescription*) libVersionPoint()
 {
 #ifdef XP_UNIX
     /*
      * Add dummy references to rcsid and sccsid to prevent them
      * from being optimized away as unused variables.
      */
     const char *dummy;
     
     dummy = rcsid;
     dummy = sccsid;
 #endif
     return &VERSION_DESC_NAME;
 }  /* versionEntryPointType */
+#ifdef _PR_HAS_PRAGMA_DIAGNOSTIC
+#pragma GCC diagnostic pop
+#endif
 
 /* plvrsion.c */
 
--- a/nsprpub/lib/libc/src/plvrsion.c
+++ b/nsprpub/lib/libc/src/plvrsion.c
@@ -69,25 +69,32 @@ PRVersionDescription VERSION_DESC_NAME =
  */
 static char rcsid[] = "$Header: NSPR " PR_VERSION _DEBUG_STRING
         "  " _BUILD_STRING " $";
 static char sccsid[] = "@(#)NSPR " PR_VERSION _DEBUG_STRING
         "  " _BUILD_STRING;
 
 #endif /* XP_UNIX */
 
+#ifdef _PR_HAS_PRAGMA_DIAGNOSTIC
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
+#endif
 PR_IMPLEMENT(const PRVersionDescription*) libVersionPoint()
 {
 #ifdef XP_UNIX
     /*
      * Add dummy references to rcsid and sccsid to prevent them
      * from being optimized away as unused variables.
      */
     const char *dummy;
     
     dummy = rcsid;
     dummy = sccsid;
 #endif
     return &VERSION_DESC_NAME;
 }  /* versionEntryPointType */
+#ifdef _PR_HAS_PRAGMA_DIAGNOSTIC
+#pragma GCC diagnostic pop
+#endif
 
 /* plvrsion.c */
 
--- a/nsprpub/lib/prstreams/plvrsion.c
+++ b/nsprpub/lib/prstreams/plvrsion.c
@@ -69,25 +69,32 @@ PRVersionDescription VERSION_DESC_NAME =
  */
 static char rcsid[] = "$Header: NSPR " PR_VERSION _DEBUG_STRING
         "  " _BUILD_STRING " $";
 static char sccsid[] = "@(#)NSPR " PR_VERSION _DEBUG_STRING
         "  " _BUILD_STRING;
 
 #endif /* XP_UNIX */
 
+#ifdef _PR_HAS_PRAGMA_DIAGNOSTIC
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
+#endif
 PR_IMPLEMENT(const PRVersionDescription*) libVersionPoint()
 {
 #ifdef XP_UNIX
     /*
      * Add dummy references to rcsid and sccsid to prevent them
      * from being optimized away as unused variables.
      */
     const char *dummy;
     
     dummy = rcsid;
     dummy = sccsid;
 #endif
     return &VERSION_DESC_NAME;
 }  /* versionEntryPointType */
+#ifdef _PR_HAS_PRAGMA_DIAGNOSTIC
+#pragma GCC diagnostic pop
+#endif
 
 /* plvrsion.c */
 
--- a/nsprpub/pr/include/prinit.h
+++ b/nsprpub/pr/include/prinit.h
@@ -26,19 +26,19 @@ PR_BEGIN_EXTERN_C
 /*
 ** NSPR's version is used to determine the likelihood that the version you
 ** used to build your component is anywhere close to being compatible with
 ** what is in the underlying library.
 **
 ** The format of the version string is
 **     "<major version>.<minor version>[.<patch level>] [<Beta>]"
 */
-#define PR_VERSION  "4.14 Beta"
+#define PR_VERSION  "4.15 Beta"
 #define PR_VMAJOR   4
-#define PR_VMINOR   14
+#define PR_VMINOR   15
 #define PR_VPATCH   0
 #define PR_BETA     PR_TRUE
 
 /*
 ** PRVersionCheck
 **
 ** The basic signature of the function that is called to provide version
 ** checking. The result will be a boolean that indicates the likelihood
--- a/nsprpub/pr/src/io/prlayer.c
+++ b/nsprpub/pr/src/io/prlayer.c
@@ -647,19 +647,21 @@ retry:
               identity_cache.ident < identity_cache.length);
     identity = identity_cache.ident + 1;
     if (identity >= identity_cache.length)  /* there's no room */
     {
         /* we have to do something - hopefully it's already done */
         if ((NULL != names) && (identity < length))
         {
             /* what we did is still okay */
-            memcpy(
-                names, identity_cache.name,
-                identity_cache.length * sizeof(char*));
+            if (identity_cache.length != 0) {
+                memcpy(
+                    names, identity_cache.name,
+                    identity_cache.length * sizeof(char*));
+            }
             old = identity_cache.name;
             identity_cache.name = names;
             identity_cache.length = length;
             names = NULL;
         }
         else
         {
             PR_Unlock(identity_cache.ml);
--- a/nsprpub/pr/src/misc/praton.c
+++ b/nsprpub/pr/src/misc/praton.c
@@ -172,27 +172,29 @@ pr_inet_aton(const char *cp, PRUint32 *a
     n = pp - parts + 1;
     switch (n) {
     case 1:                /*%< a -- 32 bits */
         break;
 
     case 2:                /*%< a.b -- 8.24 bits */
         if (val > 0xffffffU)
             return (0);
-        val |= parts[0] << 24;
+        val |= (unsigned int)parts[0] << 24;
         break;
 
     case 3:                /*%< a.b.c -- 8.8.16 bits */
         if (val > 0xffffU)
             return (0);
-        val |= (parts[0] << 24) | (parts[1] << 16);
+        val |= ((unsigned int)parts[0] << 24) | ((unsigned int)parts[1] << 16);
         break;
 
     case 4:                /*%< a.b.c.d -- 8.8.8.8 bits */
         if (val > 0xffU)
             return (0);
-        val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
+        val |= ((unsigned int)parts[0] << 24) |
+               ((unsigned int)parts[1] << 16) |
+               ((unsigned int)parts[2] << 8);
         break;
     }
     *addr = PR_htonl(val);
     return (1);
 }
 
--- a/nsprpub/pr/src/misc/prnetdb.c
+++ b/nsprpub/pr/src/misc/prnetdb.c
@@ -1400,17 +1400,17 @@ PR_IMPLEMENT(PRIntn) PR_EnumerateHostEnt
 }  /* PR_EnumerateHostEnt */
 
 PR_IMPLEMENT(PRStatus) PR_InitializeNetAddr(
     PRNetAddrValue val, PRUint16 port, PRNetAddr *addr)
 {
     PRStatus rv = PR_SUCCESS;
     if (!_pr_initialized) _PR_ImplicitInitialization();
 
-	if (val != PR_IpAddrNull) memset(addr, 0, sizeof(addr->inet));
+	if (val != PR_IpAddrNull) memset(addr, 0, sizeof(*addr));
 	addr->inet.family = AF_INET;
 	addr->inet.port = htons(port);
 	switch (val)
 	{
 	case PR_IpAddrNull:
 		break;  /* don't overwrite the address */
 	case PR_IpAddrAny:
 		addr->inet.ip = htonl(INADDR_ANY);
@@ -1772,48 +1772,36 @@ PR_IMPLEMENT(PRUint16) PR_ntohs(PRUint16
 PR_IMPLEMENT(PRUint32) PR_ntohl(PRUint32 n) { return ntohl(n); }
 PR_IMPLEMENT(PRUint16) PR_htons(PRUint16 n) { return htons(n); }
 PR_IMPLEMENT(PRUint32) PR_htonl(PRUint32 n) { return htonl(n); }
 PR_IMPLEMENT(PRUint64) PR_ntohll(PRUint64 n)
 {
 #ifdef IS_BIG_ENDIAN
     return n;
 #else
-    PRUint64 tmp;
     PRUint32 hi, lo;
-    LL_L2UI(lo, n);
-    LL_SHR(tmp, n, 32);
-    LL_L2UI(hi, tmp);
+    lo = (PRUint32)n;
+    hi = (PRUint32)(n >> 32);
     hi = PR_ntohl(hi);
     lo = PR_ntohl(lo);
-    LL_UI2L(n, lo);
-    LL_SHL(n, n, 32);
-    LL_UI2L(tmp, hi);
-    LL_ADD(n, n, tmp);
-    return n;
+    return ((PRUint64)lo << 32) + (PRUint64)hi;
 #endif
 }  /* ntohll */
 
 PR_IMPLEMENT(PRUint64) PR_htonll(PRUint64 n)
 {
 #ifdef IS_BIG_ENDIAN
     return n;
 #else
-    PRUint64 tmp;
     PRUint32 hi, lo;
-    LL_L2UI(lo, n);
-    LL_SHR(tmp, n, 32);
-    LL_L2UI(hi, tmp);
+    lo = (PRUint32)n;
+    hi = (PRUint32)(n >> 32);
     hi = htonl(hi);
     lo = htonl(lo);
-    LL_UI2L(n, lo);
-    LL_SHL(n, n, 32);
-    LL_UI2L(tmp, hi);
-    LL_ADD(n, n, tmp);
-    return n;
+    return ((PRUint64)lo << 32) + (PRUint64)hi;
 #endif
 }  /* htonll */
 
 
 /*
  * Implementation of PR_GetAddrInfoByName and friends
  *
  * Compile-time options:
--- a/nsprpub/pr/src/misc/prtime.c
+++ b/nsprpub/pr/src/misc/prtime.c
@@ -490,16 +490,30 @@ PR_NormalizeTime(PRExplodedTime *time, P
 
 #define MT_safe_localtime(timer, result) \
         (localtime_r(timer, result) == -1 ? NULL: result)
 
 #elif defined(HAVE_POINTER_LOCALTIME_R)
 
 #define MT_safe_localtime localtime_r
 
+#elif defined(_MSC_VER)
+
+/* Visual C++ has had localtime_s() since Visual C++ 2005. */
+
+static struct tm *MT_safe_localtime(const time_t *clock, struct tm *result)
+{
+    errno_t err = localtime_s(result, clock);
+    if (err != 0) {
+        errno = err;
+        return NULL;
+    }
+    return result;
+}
+
 #else
 
 #define HAVE_LOCALTIME_MONITOR 1  /* We use 'monitor' to serialize our calls
                                    * to localtime(). */
 static PRLock *monitor = NULL;
 
 static struct tm *MT_safe_localtime(const time_t *clock, struct tm *result)
 {
@@ -575,16 +589,17 @@ void _PR_CleanupTime(void)
 #if defined(XP_UNIX) || defined(XP_PC) || defined(XP_BEOS)
 
 PR_IMPLEMENT(PRTimeParameters)
 PR_LocalTimeParameters(const PRExplodedTime *gmt)
 {
 
     PRTimeParameters retVal;
     struct tm localTime;
+    struct tm *localTimeResult;
     time_t secs;
     PRTime secs64;
     PRInt64 usecPerSec;
     PRInt64 usecPerSec_1;
     PRInt64 maxInt32;
     PRInt64 minInt32;
     PRInt32 dayOffset;
     PRInt32 offset2Jan1970;
@@ -601,17 +616,22 @@ PR_LocalTimeParameters(const PRExplodedT
      * Caveat: the validity of this calculation depends on two
      * assumptions:
      * 1. Daylight saving time was not in effect on Jan. 2, 1970.
      * 2. The time zone of the geographic location has not changed
      *    since Jan. 2, 1970.
      */
 
     secs = 86400L;
-    (void) MT_safe_localtime(&secs, &localTime);
+    localTimeResult = MT_safe_localtime(&secs, &localTime);
+    PR_ASSERT(localTimeResult != NULL);
+    if (localTimeResult == NULL) {
+        /* Shouldn't happen. Use safe fallback for optimized builds. */
+        return PR_GMTParameters(gmt);
+    }
 
     /* GMT is 00:00:00, 2nd of Jan. */
 
     offset2Jan1970 = (PRInt32)localTime.tm_sec 
             + 60L * (PRInt32)localTime.tm_min
             + 3600L * (PRInt32)localTime.tm_hour
             + 86400L * (PRInt32)((PRInt32)localTime.tm_mday - 2L);
 
@@ -952,16 +972,17 @@ PR_ParseTimeStringToExplodedTime(
   TIME_TOKEN zone = TT_UNKNOWN;
   int zone_offset = -1;
   int dst_offset = 0;
   int date = -1;
   PRInt32 year = -1;
   int hour = -1;
   int min = -1;
   int sec = -1;
+  struct tm *localTimeResult;
 
   const char *rest = string;
 
   int iterations = 0;
 
   PR_ASSERT(string && result);
   if (!string || !result) return PR_FAILURE;
 
@@ -1613,17 +1634,21 @@ PR_ParseTimeStringToExplodedTime(
                       return PR_SUCCESS;
                     }
                 }
                 
                 /* So mktime() can't handle this case.  We assume the
                    zone_offset for the date we are parsing is the same as
                    the zone offset on 00:00:00 2 Jan 1970 GMT. */
                 secs = 86400;
-                (void) MT_safe_localtime(&secs, &localTime);
+                localTimeResult = MT_safe_localtime(&secs, &localTime);
+                PR_ASSERT(localTimeResult != NULL);
+                if (localTimeResult == NULL) {
+                    return PR_FAILURE;
+                }
                 zone_offset = localTime.tm_min
                               + 60 * localTime.tm_hour
                               + 1440 * (localTime.tm_mday - 2);
         }
 
   result->tm_params.tp_gmt_offset = zone_offset * 60;
   result->tm_params.tp_dst_offset = dst_offset * 60;
 
--- a/nsprpub/pr/src/prvrsion.c
+++ b/nsprpub/pr/src/prvrsion.c
@@ -69,25 +69,32 @@ PRVersionDescription VERSION_DESC_NAME =
  */
 static char rcsid[] = "$Header: NSPR " PR_VERSION _DEBUG_STRING
         "  " _BUILD_STRING " $";
 static char sccsid[] = "@(#)NSPR " PR_VERSION _DEBUG_STRING
         "  " _BUILD_STRING;
 
 #endif /* XP_UNIX */
 
+#ifdef _PR_HAS_PRAGMA_DIAGNOSTIC
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
+#endif
 PR_IMPLEMENT(const PRVersionDescription*) libVersionPoint(void)
 {
 #ifdef XP_UNIX
     /*
      * Add dummy references to rcsid and sccsid to prevent them
      * from being optimized away as unused variables.
      */ 
     const char *dummy;
 
     dummy = rcsid;
     dummy = sccsid;
 #endif
     return &VERSION_DESC_NAME;
 }  /* versionEntryPointType */
+#ifdef _PR_HAS_PRAGMA_DIAGNOSTIC
+#pragma GCC diagnostic pop
+#endif
 
 /* prvrsion.c */
 
--- a/nsprpub/pr/src/pthreads/ptio.c
+++ b/nsprpub/pr/src/pthreads/ptio.c
@@ -1537,33 +1537,36 @@ static PRStatus pt_Fsync(PRFileDesc *fd)
 
 static PRStatus pt_Connect(
     PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout)
 {
     PRIntn rv = -1, syserrno;
     pt_SockLen addr_len;
 	const PRNetAddr *addrp = addr;
 #if defined(_PR_HAVE_SOCKADDR_LEN) || defined(_PR_INET6)
-	PRUint16 md_af = addr->raw.family;
     PRNetAddr addrCopy;
 #endif
+#ifdef _PR_HAVE_SOCKADDR_LEN
+    PRUint16 md_af = addr->raw.family;
+#endif
 
     if (pt_TestAbort()) return PR_FAILURE;
 
     PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
     addr_len = PR_NETADDR_SIZE(addr);
-#if defined(_PR_INET6)
-	if (addr->raw.family == PR_AF_INET6) {
-		md_af = AF_INET6;
-#ifndef _PR_HAVE_SOCKADDR_LEN
-		addrCopy = *addr;
-		addrCopy.raw.family = AF_INET6;
-		addrp = &addrCopy;
+#ifdef _PR_INET6
+    if (addr->raw.family == PR_AF_INET6) {
+#ifdef _PR_HAVE_SOCKADDR_LEN
+        md_af = AF_INET6;
+#else
+        addrCopy = *addr;
+        addrCopy.raw.family = AF_INET6;
+        addrp = &addrCopy;
 #endif
-	}
+    }
 #endif
 
 #ifdef _PR_HAVE_SOCKADDR_LEN
     addrCopy = *addr;
     ((struct sockaddr*)&addrCopy)->sa_len = addr_len;
     ((struct sockaddr*)&addrCopy)->sa_family = md_af;
     addrp = &addrCopy;
 #endif
@@ -1727,42 +1730,45 @@ failed:
 }  /* pt_Accept */
 
 static PRStatus pt_Bind(PRFileDesc *fd, const PRNetAddr *addr)
 {
     PRIntn rv;
     pt_SockLen addr_len;
 	const PRNetAddr *addrp = addr;
 #if defined(_PR_HAVE_SOCKADDR_LEN) || defined(_PR_INET6)
-	PRUint16 md_af = addr->raw.family;
     PRNetAddr addrCopy;
 #endif
+#ifdef _PR_HAVE_SOCKADDR_LEN
+    PRUint16 md_af = addr->raw.family;
+#endif
 
     if (pt_TestAbort()) return PR_FAILURE;
 
     PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
     if (addr->raw.family == AF_UNIX)
     {
         /* Disallow relative pathnames */
         if (addr->local.path[0] != '/')
         {
             PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
             return PR_FAILURE;
         }
     }
 
-#if defined(_PR_INET6)
-	if (addr->raw.family == PR_AF_INET6) {
-		md_af = AF_INET6;
-#ifndef _PR_HAVE_SOCKADDR_LEN
-		addrCopy = *addr;
-		addrCopy.raw.family = AF_INET6;
-		addrp = &addrCopy;
+#ifdef _PR_INET6
+    if (addr->raw.family == PR_AF_INET6) {
+#ifdef _PR_HAVE_SOCKADDR_LEN
+        md_af = AF_INET6;
+#else
+        addrCopy = *addr;
+        addrCopy.raw.family = AF_INET6;
+        addrp = &addrCopy;
 #endif
-	}
+    }
 #endif
 
     addr_len = PR_NETADDR_SIZE(addr);
 #ifdef _PR_HAVE_SOCKADDR_LEN
     addrCopy = *addr;
     ((struct sockaddr*)&addrCopy)->sa_len = addr_len;
     ((struct sockaddr*)&addrCopy)->sa_family = md_af;
     addrp = &addrCopy;
@@ -1984,32 +1990,35 @@ static PRInt32 pt_SendTo(
     PRInt32 amount, PRIntn flags, const PRNetAddr *addr,
     PRIntervalTime timeout)
 {
     PRInt32 syserrno, bytes = -1;
     PRBool fNeedContinue = PR_FALSE;
     pt_SockLen addr_len;
 	const PRNetAddr *addrp = addr;
 #if defined(_PR_HAVE_SOCKADDR_LEN) || defined(_PR_INET6)
-	PRUint16 md_af = addr->raw.family;
     PRNetAddr addrCopy;
 #endif
+#ifdef _PR_HAVE_SOCKADDR_LEN
+    PRUint16 md_af = addr->raw.family;
+#endif
 
     if (pt_TestAbort()) return bytes;
 
     PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
-#if defined(_PR_INET6)
-	if (addr->raw.family == PR_AF_INET6) {
-		md_af = AF_INET6;
-#ifndef _PR_HAVE_SOCKADDR_LEN
-		addrCopy = *addr;
-		addrCopy.raw.family = AF_INET6;
-		addrp = &addrCopy;
+#ifdef _PR_INET6
+    if (addr->raw.family == PR_AF_INET6) {
+#ifdef _PR_HAVE_SOCKADDR_LEN
+        md_af = AF_INET6;
+#else
+        addrCopy = *addr;
+        addrCopy.raw.family = AF_INET6;
+        addrp = &addrCopy;
 #endif
-	}
+    }
 #endif
 
     addr_len = PR_NETADDR_SIZE(addr);
 #ifdef _PR_HAVE_SOCKADDR_LEN
     addrCopy = *addr;
     ((struct sockaddr*)&addrCopy)->sa_len = addr_len;
     ((struct sockaddr*)&addrCopy)->sa_family = md_af;
     addrp = &addrCopy;
--- a/nsprpub/pr/tests/vercheck.c
+++ b/nsprpub/pr/tests/vercheck.c
@@ -34,34 +34,34 @@ static char *compatible_version[] = {
     "4.7", "4.7.1", "4.7.2", "4.7.3", "4.7.4", "4.7.5",
     "4.7.6",
     "4.8", "4.8.1", "4.8.2", "4.8.3", "4.8.4", "4.8.5",
     "4.8.6", "4.8.7", "4.8.8", "4.8.9",
     "4.9", "4.9.1", "4.9.2", "4.9.3", "4.9.4", "4.9.5",
     "4.9.6",
     "4.10", "4.10.1", "4.10.2", "4.10.3", "4.10.4",
     "4.10.5", "4.10.6", "4.10.7", "4.10.8", "4.10.9",
-    "4.10.10", "4.11", "4.12", "4.13",
+    "4.10.10", "4.11", "4.12", "4.13", "4.14"
     PR_VERSION
 };
 
 /*
  * This release is not backward compatible with the old
  * NSPR 2.1 and 3.x releases.
  *
  * Any release is incompatible with future releases and
  * patches.
  */
 static char *incompatible_version[] = {
     "2.1 19980529",
     "3.0", "3.0.1",
     "3.1", "3.1.1", "3.1.2", "3.1.3",
     "3.5", "3.5.1",
-    "4.14.1",
-    "4.15", "4.15.1",
+    "4.15.1",
+    "4.16", "4.16.1",
     "10.0", "11.1", "12.14.20"
 };
 
 int main(int argc, char **argv)
 {
     int idx;
     int num_compatible = sizeof(compatible_version) / sizeof(char *);
     int num_incompatible = sizeof(incompatible_version) / sizeof(char *);
--- a/old-configure.in
+++ b/old-configure.in
@@ -43,17 +43,17 @@ dnl ====================================
 _SUBDIR_HOST_LDFLAGS="$HOST_LDFLAGS"
 _SUBDIR_CONFIG_ARGS="$ac_configure_args"
 
 dnl Set the version number of the libs included with mozilla
 dnl ========================================================
 MOZJPEG=62
 MOZPNG=10629
 NSPR_VERSION=4
-NSPR_MINVER=4.13.1
+NSPR_MINVER=4.14
 NSS_VERSION=3
 
 dnl Set the minimum version of toolkit libs used by mozilla
 dnl ========================================================
 GLIB_VERSION=2.22
 # 2_26 is the earliest version we can set GLIB_VERSION_MIN_REQUIRED.
 # The macro won't be used when compiling with earlier versions anyway.
 GLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_26
--- a/probes/moz.build
+++ b/probes/moz.build
@@ -1,14 +1,17 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+with Files('**'):
+    BUG_COMPONENT = ('Core', 'JavaScript Engine')
+
 if CONFIG['HAVE_DTRACE']:
     EXPORTS += [
         '!mozilla-trace.h',
     ]
 
     GENERATED_FILES += [
         'mozilla-trace.h',
     ]
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-d621b1e53054
+06158d335df0
new file mode 100644
--- /dev/null
+++ b/security/nss/automation/taskcluster/docker-clang-3.9/Dockerfile
@@ -0,0 +1,30 @@
+FROM ubuntu:16.04
+MAINTAINER Tim Taubert <ttaubert@mozilla.com>
+
+RUN useradd -d /home/worker -s /bin/bash -m worker
+WORKDIR /home/worker
+
+# Add build and test scripts.
+ADD bin /home/worker/bin
+RUN chmod +x /home/worker/bin/*
+
+# Install dependencies.
+ADD setup.sh /tmp/setup.sh
+RUN bash /tmp/setup.sh
+
+# Change user.
+USER worker
+
+# Env variables.
+ENV HOME /home/worker
+ENV SHELL /bin/bash
+ENV USER worker
+ENV LOGNAME worker
+ENV HOSTNAME taskcluster-worker
+ENV LANG en_US.UTF-8
+ENV LC_ALL en_US.UTF-8
+ENV HOST localhost
+ENV DOMSUF localdomain
+
+# Set a default command for debugging.
+CMD ["/bin/bash", "--login"]
new file mode 100644
--- /dev/null
+++ b/security/nss/automation/taskcluster/docker-clang-3.9/bin/checkout.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+
+set -v -e -x
+
+if [ $(id -u) = 0 ]; then
+    # Drop privileges by re-running this script.
+    exec su worker $0
+fi
+
+# Default values for testing.
+REVISION=${NSS_HEAD_REVISION:-default}
+REPOSITORY=${NSS_HEAD_REPOSITORY:-https://hg.mozilla.org/projects/nss}
+
+# Clone NSS.
+for i in 0 2 5; do
+    sleep $i
+    hg clone -r $REVISION $REPOSITORY nss && exit 0
+    rm -rf nss
+done
+exit 1
new file mode 100644
--- /dev/null
+++ b/security/nss/automation/taskcluster/docker-clang-3.9/setup.sh
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+
+set -v -e -x
+
+# Update packages.
+export DEBIAN_FRONTEND=noninteractive
+apt-get -y update && apt-get -y upgrade
+
+# Need this to add keys for PPAs below.
+apt-get install -y --no-install-recommends apt-utils
+
+apt_packages=()
+apt_packages+=('ca-certificates')
+apt_packages+=('curl')
+apt_packages+=('xz-utils')
+
+# Latest Mercurial.
+apt_packages+=('mercurial')
+apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 41BD8711B1F0EC2B0D85B91CF59CE3A8323293EE
+echo "deb http://ppa.launchpad.net/mercurial-ppa/releases/ubuntu xenial main" > /etc/apt/sources.list.d/mercurial.list
+
+# Install packages.
+apt-get -y update
+apt-get install -y --no-install-recommends ${apt_packages[@]}
+
+# Download clang.
+curl -LO http://releases.llvm.org/3.9.1/clang+llvm-3.9.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz
+curl -LO http://releases.llvm.org/3.9.1/clang+llvm-3.9.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz.sig
+# Verify the signature.
+gpg --keyserver pool.sks-keyservers.net --recv-keys B6C8F98282B944E3B0D5C2530FC3042E345AD05D
+gpg --verify *.tar.xz.sig
+# Install into /usr/local/.
+tar xJvf *.tar.xz -C /usr/local --strip-components=1
+# Cleanup.
+rm *.tar.xz*
+
+locale-gen en_US.UTF-8
+dpkg-reconfigure locales
+
+# Cleanup.
+rm -rf ~/.ccache ~/.cache
+apt-get autoremove -y
+apt-get clean
+apt-get autoclean
+rm $0
--- a/security/nss/automation/taskcluster/docker/setup.sh
+++ b/security/nss/automation/taskcluster/docker/setup.sh
@@ -44,19 +44,26 @@ echo "deb http://ppa.launchpad.net/ubunt
 
 # Install packages.
 apt-get -y update
 apt-get install -y --no-install-recommends ${apt_packages[@]}
 
 # 32-bit builds
 ln -s /usr/include/x86_64-linux-gnu/zconf.h /usr/include
 
-# Install clang-3.9 into /usr/local/.
-# FIXME: verify signature
-curl -L http://releases.llvm.org/3.9.1/clang+llvm-3.9.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz | tar xJv -C /usr/local --strip-components=1
+# Download clang.
+curl -LO http://releases.llvm.org/4.0.0/clang+llvm-4.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz
+curl -LO http://releases.llvm.org/4.0.0/clang+llvm-4.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz.sig
+# Verify the signature.
+gpg --keyserver pool.sks-keyservers.net --recv-keys B6C8F98282B944E3B0D5C2530FC3042E345AD05D
+gpg --verify *.tar.xz.sig
+# Install into /usr/local/.
+tar xJvf *.tar.xz -C /usr/local --strip-components=1
+# Cleanup.
+rm *.tar.xz*
 
 # Install latest Rust (stable).
 su worker -c "curl https://sh.rustup.rs -sSf | sh -s -- -y"
 
 locale-gen en_US.UTF-8
 dpkg-reconfigure locales
 
 # Cleanup.
--- a/security/nss/automation/taskcluster/graph/src/extend.js
+++ b/security/nss/automation/taskcluster/graph/src/extend.js
@@ -1,17 +1,29 @@
 /* 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 merge from "./merge";
 import * as queue from "./queue";
 
-const LINUX_IMAGE = {name: "linux", path: "automation/taskcluster/docker"};
-const FUZZ_IMAGE = {name: "fuzz", path: "automation/taskcluster/docker-fuzz"};
+const LINUX_IMAGE = {
+  name: "linux",
+  path: "automation/taskcluster/docker"
+};
+
+const LINUX_CLANG39_IMAGE = {
+  name: "linux-clang-3.9",
+  path: "automation/taskcluster/docker-clang-3.9"
+};
+
+const FUZZ_IMAGE = {
+  name: "fuzz",
+  path: "automation/taskcluster/docker-fuzz"
+};
 
 const WINDOWS_CHECKOUT_CMD =
   "bash -c \"hg clone -r $NSS_HEAD_REVISION $NSS_HEAD_REPOSITORY nss || " +
     "(sleep 2; hg clone -r $NSS_HEAD_REVISION $NSS_HEAD_REPOSITORY nss) || " +
     "(sleep 5; hg clone -r $NSS_HEAD_REVISION $NSS_HEAD_REPOSITORY nss)\"";
 
 /*****************************************************************************/
 
@@ -120,17 +132,18 @@ export default async function main() {
       UBSAN_OPTIONS: "print_stacktrace=1",
       NSS_DISABLE_ARENA_FREE_LIST: "1",
       NSS_DISABLE_UNLOAD: "1",
       CC: "clang",
       CCC: "clang++",
     },
     platform: "linux64",
     collection: "asan",
-    image: LINUX_IMAGE
+    image: LINUX_IMAGE,
+    features: ["allowPtrace"],
   });
 
   await scheduleWindows("Windows 2012 64 (opt)", {
     env: {BUILD_OPT: "1"}
   });
 
   await scheduleWindows("Windows 2012 64 (debug)", {
     collection: "debug"
@@ -216,22 +229,22 @@ async function scheduleLinux(name, base)
       "-c",
       "bin/checkout.sh && nss/automation/taskcluster/scripts/run_tests.sh"
     ]
   }));
 
   // Extra builds.
   let extra_base = merge({group: "Builds"}, build_base);
   queue.scheduleTask(merge(extra_base, {
-    name: `${name} w/ clang-3.9`,
+    name: `${name} w/ clang-4.0`,
     env: {
       CC: "clang",
       CCC: "clang++",
     },
-    symbol: "clang-3.9"
+    symbol: "clang-4.0"
   }));
 
   queue.scheduleTask(merge(extra_base, {
     name: `${name} w/ gcc-4.8`,
     env: {
       CC: "gcc-4.8",
       CCC: "g++-4.8"
     },
@@ -279,17 +292,17 @@ function scheduleFuzzingRun(base, name, 
     ],
     symbol: symbol || name
   }));
 }
 
 async function scheduleFuzzing() {
   let base = {
     env: {
-      ASAN_OPTIONS: "allocator_may_return_null=1",
+      ASAN_OPTIONS: "allocator_may_return_null=1:detect_stack_use_after_return=1",
       UBSAN_OPTIONS: "print_stacktrace=1",
       NSS_DISABLE_ARENA_FREE_LIST: "1",
       NSS_DISABLE_UNLOAD: "1",
       CC: "clang",
       CCC: "clang++"
     },
     features: ["allowPtrace"],
     platform: "linux64",
@@ -558,34 +571,35 @@ function scheduleTests(task_build, task_
     name: "SSL tests (upgradedb)", symbol: "upgradedb", cycle: "upgradedb"
   }));
 }
 
 /*****************************************************************************/
 
 async function scheduleTools() {
   let base = {
-    image: LINUX_IMAGE,
     platform: "nss-tools",
     kind: "test"
   };
 
   queue.scheduleTask(merge(base, {
     symbol: "clang-format-3.9",
     name: "clang-format-3.9",
+    image: LINUX_CLANG39_IMAGE,
     command: [
       "/bin/bash",
       "-c",
       "bin/checkout.sh && nss/automation/taskcluster/scripts/run_clang_format.sh"
     ]
   }));
 
   queue.scheduleTask(merge(base, {
-    symbol: "scan-build-3.9",
-    name: "scan-build-3.9",
+    symbol: "scan-build-4.0",
+    name: "scan-build-4.0",
+    image: LINUX_IMAGE,
     env: {
       USE_64: "1",
       CC: "clang",
       CCC: "clang++",
     },
     artifacts: {
       public: {
         expires: 24 * 7,
--- a/security/nss/cmd/ecperf/ecperf.c
+++ b/security/nss/cmd/ecperf/ecperf.c
@@ -477,17 +477,17 @@ ectest_curve_freebl(ECCurveName curve, i
     PORT_Memset(digestData, 0xa5, sizeof(digestData));
     digest.data = digestData;
     digest.len = sizeof(digestData);
     sig.data = sigData;
     sig.len = sizeof(sigData);
 
     rv = EC_NewKey(&ecParams, &ecPriv);
     if (rv != SECSuccess) {
-        return SECFailure;
+        goto cleanup;
     }
     ecPub.ecParams = ecParams;
     ecPub.publicValue = ecPriv->publicValue;
 
     if (ecCurve_map[curve]->usage & KU_KEY_AGREEMENT) {
         rv = M_TimeOperation(genericThread, (op_func)ECDH_DeriveWrap, "ECDH_Derive",
                              ecPriv, &ecPub, NULL, iterations, numThreads, 0, 0, 0, &deriveRate);
         if (rv != SECSuccess) {
@@ -510,17 +510,19 @@ ectest_curve_freebl(ECCurveName curve, i
         if (rv != SECSuccess) {
             goto cleanup;
         }
     }
 
 cleanup:
     SECITEM_FreeItem(&ecEncodedParams, PR_FALSE);
     PORT_FreeArena(arena, PR_FALSE);
-    PORT_FreeArena(ecPriv->ecParams.arena, PR_FALSE);
+    if (ecPriv) {
+        PORT_FreeArena(ecPriv->ecParams.arena, PR_FALSE);
+    }
     return rv;
 }
 
 /* Prints help information. */
 void
 printUsage(char *prog)
 {
     printf("Usage: %s [-i iterations] [-t threads ] [-ans] [-fp] [-Al]\n"
--- a/security/nss/cmd/selfserv/selfserv.c
+++ b/security/nss/cmd/selfserv/selfserv.c
@@ -164,17 +164,18 @@ PrintUsageHeader(const char *progName)
             "         [-f password_file] [-L [seconds]] [-M maxProcs] [-P dbprefix]\n"
             "         [-V [min-version]:[max-version]] [-a sni_name]\n"
             "         [ T <good|revoked|unknown|badsig|corrupted|none|ocsp>] [-A ca]\n"
             "         [-C SSLCacheEntries] [-S dsa_nickname] -Q [-I groups]"
 #ifndef NSS_DISABLE_ECC
             " [-e ec_nickname]"
 #endif /* NSS_DISABLE_ECC */
             "\n"
-            "         -U [0|1] -H [0|1|2] -W [0|1]\n",
+            "         -U [0|1] -H [0|1|2] -W [0|1]\n"
+            "\n",
             progName);
 }
 
 static void
 PrintParameterUsage()
 {
     fputs(
         "-V [min]:[max] restricts the set of enabled SSL/TLS protocol versions.\n"
@@ -214,17 +215,17 @@ PrintParameterUsage()
         "   badsig: use a good status but with an invalid signature\n"
         "   corrupted: stapled cert status is an invalid block of data\n"
         "   random: each connection uses a random status from this list:\n"
         "           good, revoked, unknown, failure, badsig, corrupted\n"
         "   ocsp: fetch from external OCSP server using AIA, or none\n"
         "-A <ca> Nickname of a CA used to sign a stapled cert status\n"
         "-U override default ECDHE ephemeral key reuse, 0: refresh, 1: reuse\n"
         "-H override default DHE server support, 0: disable, 1: enable, "
-        "   2: require DH named groups\n"
+        "   2: require DH named groups [RFC7919]\n"
         "-W override default DHE server weak parameters support, 0: disable, 1: enable\n"
         "-c Restrict ciphers\n"
         "-Y prints cipher values allowed for parameter -c and exits\n"
         "-G enables the extended master secret extension [RFC7627]\n"
         "-Q enables ALPN for HTTP/1.1 [RFC7301]\n"
         "-I comma separated list of enabled groups for TLS key exchange.\n"
         "   The following values are valid:\n"
         "   P256, P384, P521, x25519, FF2048, FF3072, FF4096, FF6144, FF8192\n"
--- a/security/nss/cmd/tstclnt/tstclnt.c
+++ b/security/nss/cmd/tstclnt/tstclnt.c
@@ -173,17 +173,18 @@ static void
 PrintUsageHeader(const char *progName)
 {
     fprintf(stderr,
             "Usage:  %s -h host [-a 1st_hs_name ] [-a 2nd_hs_name ] [-p port]\n"
             "[-D | -d certdir] [-C] [-b | -R root-module] \n"
             "[-n nickname] [-Bafosvx] [-c ciphers] [-Y] [-Z]\n"
             "[-V [min-version]:[max-version]] [-K] [-T] [-U]\n"
             "[-r N] [-w passwd] [-W pwfile] [-q [-t seconds]] [-I groups]\n"
-            "[-A requestfile] [-L totalconnections]",
+            "[-A requestfile] [-L totalconnections]\n"
+            "\n",
             progName);
 }
 
 static void
 PrintParameterUsage(void)
 {
     fprintf(stderr, "%-20s Send different SNI name. 1st_hs_name - at first\n"
                     "%-20s handshake, 2nd_hs_name - at second handshake.\n"
@@ -237,19 +238,17 @@ PrintParameterUsage(void)
             "-F", "", "", "", "", "", "", "", "", "");
     fprintf(stderr, "%-20s Test -F allows 0=any (default), 1=only OCSP, 2=only CRL\n", "-M");
     fprintf(stderr, "%-20s Restrict ciphers\n", "-c ciphers");
     fprintf(stderr, "%-20s Print cipher values allowed for parameter -c and exit\n", "-Y");
     fprintf(stderr, "%-20s Enforce using an IPv4 destination address\n", "-4");
     fprintf(stderr, "%-20s Enforce using an IPv6 destination address\n", "-6");
     fprintf(stderr, "%-20s (Options -4 and -6 cannot be combined.)\n", "");
     fprintf(stderr, "%-20s Enable the extended master secret extension [RFC7627]\n", "-G");
-    fprintf(stderr, "%-20s Require the use of FFDHE supported groups "
-                    "[I-D.ietf-tls-negotiated-ff-dhe]\n",
-            "-H");
+    fprintf(stderr, "%-20s Require the use of FFDHE supported groups [RFC7919]\n", "-H");
     fprintf(stderr, "%-20s Read from a file instead of stdin\n", "-A");
     fprintf(stderr, "%-20s Allow 0-RTT data (TLS 1.3 only)\n", "-Z");
     fprintf(stderr, "%-20s Disconnect and reconnect up to N times total\n", "-L");
     fprintf(stderr, "%-20s Comma separated list of enabled groups for TLS key exchange.\n"
                     "%-20s The following values are valid:\n"
                     "%-20s P256, P384, P521, x25519, FF2048, FF3072, FF4096, FF6144, FF8192\n",
             "-I", "", "");
 }
--- a/security/nss/coreconf/config.gypi
+++ b/security/nss/coreconf/config.gypi
@@ -218,24 +218,16 @@
             'ldflags': [
               '-Wl,--gc-sections',
             ],
             'conditions': [
               ['no_zdefs==0', {
                 'ldflags': [
                   '-Wl,-z,defs',
                 ],
-               'conditions': [
-                 ['OS=="dragonfly" or OS=="freebsd" or OS=="netbsd" or OS=="openbsd"', {
-                   # Bug 1321317 - unix_rand.c:880: undefined reference to `environ'
-                   'ldflags': [
-                     '-Wl,--warn-unresolved-symbols',
-                   ],
-                 }],
-               ],
               }],
             ],
           }],
         ],
         'xcode_settings': {
           'DYLIB_INSTALL_NAME_BASE': '@executable_path',
           'DYLIB_COMPATIBILITY_VERSION': '1',
           'DYLIB_CURRENT_VERSION': '1',
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,9 +5,8 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
-
--- a/security/nss/coreconf/sanitizers.py
+++ b/security/nss/coreconf/sanitizers.py
@@ -9,17 +9,17 @@ def main():
 
     sanitizer = sys.argv[1]
     if sanitizer == "ubsan":
         if len(sys.argv) < 3:
             raise Exception('ubsan requires another argument.')
         print('-fsanitize='+sys.argv[2]+' -fno-sanitize-recover=undefined ', end='')
         return
     if sanitizer == "asan":
-        print('-fsanitize=address ', end='')
+        print('-fsanitize=address -fsanitize-address-use-after-scope ', end='')
         print('-fno-omit-frame-pointer -fno-optimize-sibling-calls ', end='')
         return
     if sanitizer == "msan":
         print('-fsanitize=memory -fsanitize-memory-track-origins ', end='')
         print('-fno-omit-frame-pointer -fno-optimize-sibling-calls ', end='')
         return
     if sanitizer == "sancov":
         if len(sys.argv) < 3:
--- a/security/nss/fuzz/tls_client_target.cc
+++ b/security/nss/fuzz/tls_client_target.cc
@@ -76,17 +76,17 @@ extern "C" int LLVMFuzzerTestOneInput(co
   EnableAllProtocolVersions();
   std::unique_ptr<ClientConfig> config(new ClientConfig(data, len));
 
   // Clear the cache. We never want to resume as we couldn't reproduce that.
   SSL_ClearSessionCache();
 
 #ifdef UNSAFE_FUZZER_MODE
   // Reset the RNG state.
-  assert(RNG_ResetForFuzzing() == SECSuccess);
+  assert(RNG_RandomUpdate(NULL, 0) == SECSuccess);
 #endif
 
   // Create and import dummy socket.
   std::unique_ptr<DummyPrSocket> socket(new DummyPrSocket(data, len));
   static PRDescIdentity id = PR_GetUniqueIdentity("fuzz-client");
   ScopedPRFileDesc fd(DummyIOLayerMethods::CreateFD(id, socket.get()));
   PRFileDesc* ssl_fd = SSL_ImportFD(nullptr, fd.get());
   assert(ssl_fd == fd.get());
--- a/security/nss/fuzz/tls_server_target.cc
+++ b/security/nss/fuzz/tls_server_target.cc
@@ -80,17 +80,17 @@ extern "C" int LLVMFuzzerTestOneInput(co
 
   std::unique_ptr<ServerConfig> config(new ServerConfig(data, len));
 
   // Clear the cache. We never want to resume as we couldn't reproduce that.
   SSL_ClearSessionCache();
 
 #ifdef UNSAFE_FUZZER_MODE
   // Reset the RNG state.
-  assert(RNG_ResetForFuzzing() == SECSuccess);
+  assert(RNG_RandomUpdate(NULL, 0) == SECSuccess);
 #endif
 
   // Create model socket.
   static ScopedPRFileDesc model(SSL_ImportFD(nullptr, PR_NewTCPSocket()));
   assert(model);
 
   // Initialize the model socket once.
   static PRCallOnceType initModelOnce;
--- a/security/nss/gtests/common/gtest.gypi
+++ b/security/nss/gtests/common/gtest.gypi
@@ -22,17 +22,17 @@
           '-lws2_32',
         ],
       }],
       ['OS=="android"', {
         'libraries': [
           '-lstdc++',
         ],
       }],
-      [ 'fuzz_tls==1', {
+      [ 'fuzz==1', {
         'defines': [
           'UNSAFE_FUZZER_MODE',
         ],
       }],
     ],
     'msvs_settings': {
       'VCCLCompilerTool': {
         'ExceptionHandling': 1,
--- a/security/nss/gtests/der_gtest/der_gtest.gyp
+++ b/security/nss/gtests/der_gtest/der_gtest.gyp
@@ -8,16 +8,17 @@
   ],
   'targets': [
     {
       'target_name': 'der_gtest',
       'type': 'executable',
       'sources': [
         'der_getint_unittest.cc',
         'der_private_key_import_unittest.cc',
+        'der_quickder_unittest.cc',
         '<(DEPTH)/gtests/common/gtests.cc'
       ],
       'dependencies': [
         '<(DEPTH)/exports.gyp:nss_exports',
         '<(DEPTH)/gtests/google_test/google_test.gyp:gtest',
         '<(DEPTH)/lib/util/util.gyp:nssutil3',
         '<(DEPTH)/lib/ssl/ssl.gyp:ssl3',
         '<(DEPTH)/lib/nss/nss.gyp:nss3',
new file mode 100644
--- /dev/null
+++ b/security/nss/gtests/der_gtest/der_quickder_unittest.cc
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdint.h>
+
+#include "gtest/gtest.h"
+#include "scoped_ptrs.h"
+
+#include "nss.h"
+#include "prerror.h"
+#include "secasn1.h"
+#include "secerr.h"
+#include "secitem.h"
+
+const SEC_ASN1Template mySEC_NullTemplate[] = {
+    {SEC_ASN1_NULL, 0, NULL, sizeof(SECItem)}};
+
+namespace nss_test {
+
+class QuickDERTest : public ::testing::Test,
+                     public ::testing::WithParamInterface<SECItem> {};
+
+static const uint8_t kNullTag = 0x05;
+static const uint8_t kLongLength = 0x80;
+
+// Length of zero wrongly encoded as 0x80 instead of 0x00.
+static uint8_t kOverlongLength_0_0[] = {kNullTag, kLongLength | 0};
+
+// Length of zero wrongly encoded as { 0x81, 0x00 } instead of 0x00.
+static uint8_t kOverlongLength_1_0[] = {kNullTag, kLongLength | 1, 0x00};
+
+// Length of zero wrongly encoded as:
+//
+//     { 0x90, <arbitrary junk of 12 bytes>,
+//       0x00, 0x00, 0x00, 0x00 }
+//
+// instead of 0x00. Note in particular that if there is an integer overflow
+// then the arbitrary junk is likely get left-shifted away, as long as there
+// are at least sizeof(length) bytes following it. This would be a good way to
+// smuggle arbitrary input into DER-encoded data in a way that an non-careful
+// parser would ignore.
+static uint8_t kOverlongLength_16_0[] = {kNullTag, kLongLength | 0x10,
+                                         0x11,     0x22,
+                                         0x33,     0x44,
+                                         0x55,     0x66,
+                                         0x77,     0x88,
+                                         0x99,     0xAA,
+                                         0xBB,     0xCC,
+                                         0x00,     0x00,
+                                         0x00,     0x00};
+
+static const SECItem kInvalidDER[] = {
+    {siBuffer, kOverlongLength_0_0, sizeof(kOverlongLength_0_0)},
+    {siBuffer, kOverlongLength_1_0, sizeof(kOverlongLength_1_0)},
+    {siBuffer, kOverlongLength_16_0, sizeof(kOverlongLength_16_0)},
+};
+
+TEST_P(QuickDERTest, InvalidLengths) {
+  const SECItem& original_input(GetParam());
+
+  ScopedSECItem copy_of_input(SECITEM_AllocItem(nullptr, nullptr, 0U));
+  ASSERT_TRUE(copy_of_input);
+  ASSERT_EQ(SECSuccess,
+            SECITEM_CopyItem(nullptr, copy_of_input.get(), &original_input));
+
+  PORTCheapArenaPool pool;
+  PORT_InitCheapArena(&pool, DER_DEFAULT_CHUNKSIZE);
+  ScopedSECItem parsed_value(SECITEM_AllocItem(nullptr, nullptr, 0U));
+  ASSERT_TRUE(parsed_value);
+  ASSERT_EQ(SECFailure,
+            SEC_QuickDERDecodeItem(&pool.arena, parsed_value.get(),
+                                   mySEC_NullTemplate, copy_of_input.get()));
+  ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
+  PORT_DestroyCheapArena(&pool);
+}
+
+INSTANTIATE_TEST_CASE_P(QuickderTestsInvalidLengths, QuickDERTest,
+                        testing::ValuesIn(kInvalidDER));
+
+}  // namespace nss_test
--- a/security/nss/gtests/der_gtest/manifest.mn
+++ b/security/nss/gtests/der_gtest/manifest.mn
@@ -4,16 +4,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 CORE_DEPTH = ../..
 DEPTH      = ../..
 MODULE = nss
 
 CPPSRCS = \
       der_getint_unittest.cc \
       der_private_key_import_unittest.cc \
+      der_quickder_unittest.cc \
       $(NULL)
 
 INCLUDES += -I$(CORE_DEPTH)/gtests/google_test/gtest/include \
             -I$(CORE_DEPTH)/gtests/common \
             -I$(CORE_DEPTH)/cpputil
 
 REQUIRES = nspr nss libdbm gtest
 
--- a/security/nss/gtests/pk11_gtest/pk11_prng_unittest.cc
+++ b/security/nss/gtests/pk11_gtest/pk11_prng_unittest.cc
@@ -31,43 +31,86 @@ TEST_F(PK11PrngTest, Fuzz_DetPRNG) {
 }
 
 // Test that two consecutive calls to the RNG return two equal values
 // when the RNG's internal state is reset before each call.
 TEST_F(PK11PrngTest, Fuzz_DetPRNG_Reset) {
   std::vector<uint8_t> rnd1(2048, 0);
   std::vector<uint8_t> rnd2(2048, 0);
 
-  RNG_ResetForFuzzing();
+  EXPECT_EQ(SECSuccess, RNG_RandomUpdate(NULL, 0));
 
   SECStatus rv = PK11_GenerateRandom(rnd1.data(), rnd1.size());
   EXPECT_EQ(rv, SECSuccess);
 
-  RNG_ResetForFuzzing();
+  EXPECT_EQ(SECSuccess, RNG_RandomUpdate(NULL, 0));
 
   rv = PK11_GenerateRandom(rnd2.data(), rnd2.size());
   EXPECT_EQ(rv, SECSuccess);
 
   EXPECT_EQ(rnd1, rnd2);
 }
 
 // Test that the RNG's internal state progresses in a consistent manner.
 TEST_F(PK11PrngTest, Fuzz_DetPRNG_StatefulReset) {
   std::vector<uint8_t> rnd1(2048, 0);
   std::vector<uint8_t> rnd2(2048, 0);
 
-  RNG_ResetForFuzzing();
+  EXPECT_EQ(SECSuccess, RNG_RandomUpdate(NULL, 0));
 
   SECStatus rv = PK11_GenerateRandom(rnd1.data(), rnd1.size() - 1024);
   EXPECT_EQ(rv, SECSuccess);
 
   rv = PK11_GenerateRandom(rnd1.data() + 1024, rnd1.size() - 1024);
   EXPECT_EQ(rv, SECSuccess);
 
-  RNG_ResetForFuzzing();
+  EXPECT_EQ(SECSuccess, RNG_RandomUpdate(NULL, 0));
+
+  rv = PK11_GenerateRandom(rnd2.data(), rnd2.size() - 1024);
+  EXPECT_EQ(rv, SECSuccess);
+
+  rv = PK11_GenerateRandom(rnd2.data() + 1024, rnd2.size() - 1024);
+  EXPECT_EQ(rv, SECSuccess);
+
+  EXPECT_EQ(rnd1, rnd2);
+}
+
+TEST_F(PK11PrngTest, Fuzz_DetPRNG_Seed) {
+  std::vector<uint8_t> rnd1(2048, 0);
+  std::vector<uint8_t> rnd2(2048, 0);
+  std::vector<uint8_t> seed = {0x01, 0x22, 0xAA, 0x45};
+
+  SECStatus rv = PK11_RandomUpdate(seed.data(), seed.size());
+  EXPECT_EQ(rv, SECSuccess);
+
+  rv = PK11_GenerateRandom(rnd1.data(), rnd1.size());
+  EXPECT_EQ(rv, SECSuccess);
+
+  rv = PK11_GenerateRandom(rnd2.data(), rnd2.size());
+  EXPECT_EQ(rv, SECSuccess);
+
+  EXPECT_NE(rnd1, rnd2);
+}
+
+TEST_F(PK11PrngTest, Fuzz_DetPRNG_StatefulReset_Seed) {
+  std::vector<uint8_t> rnd1(2048, 0);
+  std::vector<uint8_t> rnd2(2048, 0);
+  std::vector<uint8_t> seed = {0x01, 0x22, 0xAA, 0x45};
+
+  SECStatus rv = PK11_RandomUpdate(seed.data(), seed.size());
+  EXPECT_EQ(rv, SECSuccess);
+
+  rv = PK11_GenerateRandom(rnd1.data(), rnd1.size() - 1024);
+  EXPECT_EQ(rv, SECSuccess);
+
+  rv = PK11_GenerateRandom(rnd1.data() + 1024, rnd1.size() - 1024);
+  EXPECT_EQ(rv, SECSuccess);
+
+  rv = PK11_RandomUpdate(seed.data(), seed.size());
+  EXPECT_EQ(rv, SECSuccess);
 
   rv = PK11_GenerateRandom(rnd2.data(), rnd2.size() - 1024);
   EXPECT_EQ(rv, SECSuccess);
 
   rv = PK11_GenerateRandom(rnd2.data() + 1024, rnd2.size() - 1024);
   EXPECT_EQ(rv, SECSuccess);
 
   EXPECT_EQ(rnd1, rnd2);
--- a/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc
@@ -19,18 +19,16 @@ extern "C" {
 #include "tls_connect.h"
 #include "tls_filter.h"
 #include "tls_parser.h"
 
 namespace nss_test {
 
 TEST_P(TlsConnectTls13, ZeroRtt) {
   SetupForZeroRtt();
-  client_->SetExpectedAlertSentCount(1);
-  server_->SetExpectedAlertReceivedCount(1);
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
   ExpectResumption(RESUME_TICKET);
   ZeroRttSendReceive(true, true);
   Handshake();
   ExpectEarlyDataAccepted(true);
   CheckConnected();
   SendReceive();
@@ -100,18 +98,16 @@ TEST_P(TlsConnectTls13, ZeroRttServerOnl
   SendReceive();
   CheckKeys();
 }
 
 TEST_P(TlsConnectTls13, TestTls13ZeroRttAlpn) {
   EnableAlpn();
   SetupForZeroRtt();
   EnableAlpn();
-  client_->SetExpectedAlertSentCount(1);
-  server_->SetExpectedAlertReceivedCount(1);
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
   ExpectResumption(RESUME_TICKET);
   ExpectEarlyDataAccepted(true);
   ZeroRttSendReceive(true, true, [this]() {
     client_->CheckAlpn(SSL_NEXT_PROTO_EARLY_VALUE, "a");
     return true;
   });
@@ -154,16 +150,17 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRtt
   server_->Set0RttEnabled(true);
   EnableAlpn();
   ExpectResumption(RESUME_TICKET);
   ZeroRttSendReceive(true, true, [this]() {
     PRUint8 b[] = {'b'};
     client_->CheckAlpn(SSL_NEXT_PROTO_EARLY_VALUE, "a");
     EXPECT_EQ(SECSuccess, SSLInt_Set0RttAlpn(client_->ssl_fd(), b, sizeof(b)));
     client_->CheckAlpn(SSL_NEXT_PROTO_EARLY_VALUE, "b");
+    ExpectAlert(client_, kTlsAlertIllegalParameter);
     return true;
   });
   Handshake();
   client_->CheckErrorCode(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
   server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
 }
 
 // Set up with no ALPN and then set the client so it thinks it has ALPN.
@@ -173,16 +170,17 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRtt
   SetupForZeroRtt();
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
   ExpectResumption(RESUME_TICKET);
   ZeroRttSendReceive(true, true, [this]() {
     PRUint8 b[] = {'b'};
     EXPECT_EQ(SECSuccess, SSLInt_Set0RttAlpn(client_->ssl_fd(), b, 1));
     client_->CheckAlpn(SSL_NEXT_PROTO_EARLY_VALUE, "b");
+    ExpectAlert(client_, kTlsAlertIllegalParameter);
     return true;
   });
   Handshake();
   client_->CheckErrorCode(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
   server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
 }
 
 // Remove the old ALPN value and so the client will not offer early data.
@@ -223,16 +221,20 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRtt
   client_->StartConnect();
   server_->StartConnect();
 
   // We will send the early data xtn without sending actual early data. Thus
   // a 1.2 server shouldn't fail until the client sends an alert because the
   // client sends end_of_early_data only after reading the server's flight.
   client_->Set0RttEnabled(true);
 
+  client_->ExpectSendAlert(kTlsAlertIllegalParameter);
+  if (mode_ == STREAM) {
+    server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
+  }
   client_->Handshake();
   server_->Handshake();
   ASSERT_TRUE_WAIT(
       (client_->error_code() == SSL_ERROR_DOWNGRADE_WITH_EARLY_DATA), 2000);
 
   // DTLS will timeout as we bump the epoch when installing the early app data
   // cipher suite. Thus the encrypted alert will be ignored.
   if (mode_ == STREAM) {
@@ -260,17 +262,23 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRtt
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
                            SSL_LIBRARY_VERSION_TLS_1_2);
   client_->StartConnect();
   server_->StartConnect();
 
   // Send the early data xtn in the CH, followed by early app data. The server
   // will fail right after sending its flight, when receiving the early data.
   client_->Set0RttEnabled(true);
-  ZeroRttSendReceive(true, false);
+  ZeroRttSendReceive(true, false, [this]() {
+    client_->ExpectSendAlert(kTlsAlertIllegalParameter);
+    if (mode_ == STREAM) {
+      server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
+    }
+    return true;
+  });
 
   client_->Handshake();
   server_->Handshake();
   ASSERT_TRUE_WAIT(
       (client_->error_code() == SSL_ERROR_DOWNGRADE_WITH_EARLY_DATA), 2000);
 
   // DTLS will timeout as we bump the epoch when installing the early app data
   // cipher suite. Thus the encrypted alert will be ignored.
@@ -296,19 +304,18 @@ TEST_P(TlsConnectTls13, SendTooMuchEarly
   const size_t short_size = strlen(big_message) - 1;
   const PRInt32 short_length = static_cast<PRInt32>(short_size);
   SSLInt_SetMaxEarlyDataSize(static_cast<PRUint32>(short_size));
   SetupForZeroRtt();
 
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
   ExpectResumption(RESUME_TICKET);
-  client_->SetExpectedAlertSentCount(1);
-  server_->SetExpectedAlertReceivedCount(1);
 
+  ExpectAlert(client_, kTlsAlertEndOfEarlyData);
   client_->Handshake();
   CheckEarlyDataLimit(client_, short_size);
 
   PRInt32 sent;
   // Writing more than the limit will succeed in TLS, but fail in DTLS.
   if (mode_ == STREAM) {
     sent = PR_Write(client_->ssl_fd(), big_message,
                     static_cast<PRInt32>(strlen(big_message)));
@@ -352,37 +359,41 @@ TEST_P(TlsConnectTls13, ReceiveTooMuchEa
   const size_t limit = 5;
   SSLInt_SetMaxEarlyDataSize(limit);
   SetupForZeroRtt();
 
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
   ExpectResumption(RESUME_TICKET);
 
+  client_->ExpectSendAlert(kTlsAlertEndOfEarlyData);
   client_->Handshake();  // Send ClientHello
   CheckEarlyDataLimit(client_, limit);
 
   // Lift the limit on the client.
   EXPECT_EQ(SECSuccess,
             SSLInt_SetSocketMaxEarlyDataSize(client_->ssl_fd(), 1000));
 
   // Send message
   const char* message = "0123456789abcdef";
   const PRInt32 message_len = static_cast<PRInt32>(strlen(message));
   EXPECT_EQ(message_len, PR_Write(client_->ssl_fd(), message, message_len));
 
+  if (mode_ == STREAM) {
+    // This error isn't fatal for DTLS.
+    ExpectAlert(server_, kTlsAlertUnexpectedMessage);
+  }
   server_->Handshake();  // Process ClientHello, send server flight.
   server_->Handshake();  // Just to make sure that we don't read ahead.
   CheckEarlyDataLimit(server_, limit);
 
   // Attempt to read early data.
   std::vector<uint8_t> buf(strlen(message) + 1);
   EXPECT_GT(0, PR_Read(server_->ssl_fd(), buf.data(), buf.capacity()));
   if (mode_ == STREAM) {
-    // This error isn't fatal for DTLS.
     server_->CheckErrorCode(SSL_ERROR_TOO_MUCH_EARLY_DATA);
   }
 
   client_->Handshake();  // Process the handshake.
   client_->Handshake();  // Process the alert.
   if (mode_ == STREAM) {
     client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
   }
--- a/security/nss/gtests/ssl_gtest/ssl_agent_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_agent_unittest.cc
@@ -51,23 +51,25 @@ const static uint8_t kCannedTls13ServerH
     0x23, 0x17, 0x64, 0x23, 0x03, 0xf0, 0xfb, 0x45, 0x98, 0x26, 0xd1, 0x65,
     0x24, 0xa1, 0x6c, 0xa9, 0x80, 0x8f, 0x2c, 0xac, 0x0a, 0xea, 0x53, 0x3a,
     0xcb, 0xe3, 0x08, 0x84, 0xae, 0x19};
 static const char *k0RttData = "ABCDEF";
 
 TEST_P(TlsAgentTest, EarlyFinished) {
   DataBuffer buffer;
   MakeTrivialHandshakeRecord(kTlsHandshakeFinished, 0, &buffer);
+  ExpectAlert(kTlsAlertUnexpectedMessage);
   ProcessMessage(buffer, TlsAgent::STATE_ERROR,
                  SSL_ERROR_RX_UNEXPECTED_FINISHED);
 }
 
 TEST_P(TlsAgentTest, EarlyCertificateVerify) {
   DataBuffer buffer;
   MakeTrivialHandshakeRecord(kTlsHandshakeCertificateVerify, 0, &buffer);
+  ExpectAlert(kTlsAlertUnexpectedMessage);
   ProcessMessage(buffer, TlsAgent::STATE_ERROR,
                  SSL_ERROR_RX_UNEXPECTED_CERT_VERIFY);
 }
 
 TEST_P(TlsAgentTestClient13, CannedHello) {
   DataBuffer buffer;
   EnsureInit();
   DataBuffer server_hello;
@@ -85,16 +87,17 @@ TEST_P(TlsAgentTestClient13, EncryptedEx
   DataBuffer encrypted_extensions;
   MakeHandshakeMessage(kTlsHandshakeEncryptedExtensions, nullptr, 0,
                        &encrypted_extensions, 1);
   server_hello.Append(encrypted_extensions);
   DataBuffer buffer;
   MakeRecord(kTlsHandshakeType, SSL_LIBRARY_VERSION_TLS_1_3,
              server_hello.data(), server_hello.len(), &buffer);
   EnsureInit();
+  ExpectAlert(kTlsAlertUnexpectedMessage);
   ProcessMessage(buffer, TlsAgent::STATE_ERROR,
                  SSL_ERROR_RX_UNEXPECTED_HANDSHAKE);
 }
 
 TEST_F(TlsAgentStreamTestClient, EncryptedExtensionsInClearTwoPieces) {
   DataBuffer server_hello;
   MakeHandshakeMessage(kTlsHandshakeServerHello, kCannedTls13ServerHello,
                        sizeof(kCannedTls13ServerHello), &server_hello);
@@ -109,16 +112,17 @@ TEST_F(TlsAgentStreamTestClient, Encrypt
   DataBuffer buffer2;
   MakeRecord(kTlsHandshakeType, SSL_LIBRARY_VERSION_TLS_1_3,
              server_hello.data() + 20, server_hello.len() - 20, &buffer2);
 
   EnsureInit();
   agent_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_3,
                           SSL_LIBRARY_VERSION_TLS_1_3);
   ProcessMessage(buffer, TlsAgent::STATE_CONNECTING);
+  ExpectAlert(kTlsAlertUnexpectedMessage);
   ProcessMessage(buffer2, TlsAgent::STATE_ERROR,
                  SSL_ERROR_RX_UNEXPECTED_HANDSHAKE);
 }
 
 TEST_F(TlsAgentDgramTestClient, EncryptedExtensionsInClearTwoPieces) {
   DataBuffer server_hello_frag1;
   MakeHandshakeMessageFragment(
       kTlsHandshakeServerHello, kCannedTls13ServerHello,
@@ -139,16 +143,17 @@ TEST_F(TlsAgentDgramTestClient, Encrypte
   DataBuffer buffer2;
   MakeRecord(kTlsHandshakeType, SSL_LIBRARY_VERSION_TLS_1_3,
              server_hello_frag2.data(), server_hello_frag2.len(), &buffer2, 1);
 
   EnsureInit();
   agent_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_3,
                           SSL_LIBRARY_VERSION_TLS_1_3);
   ProcessMessage(buffer, TlsAgent::STATE_CONNECTING);
+  ExpectAlert(kTlsAlertUnexpectedMessage);
   ProcessMessage(buffer2, TlsAgent::STATE_ERROR,
                  SSL_ERROR_RX_UNEXPECTED_HANDSHAKE);
 }
 
 TEST_F(TlsAgentStreamTestClient, Set0RttOptionThenWrite) {
   EnsureInit();
   agent_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                           SSL_LIBRARY_VERSION_TLS_1_3);
@@ -169,16 +174,17 @@ TEST_F(TlsAgentStreamTestClient, Set0Rtt
   agent_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                           SSL_LIBRARY_VERSION_TLS_1_3);
   agent_->StartConnect();
   agent_->Set0RttEnabled(true);
   DataBuffer buffer;
   MakeRecord(kTlsApplicationDataType, SSL_LIBRARY_VERSION_TLS_1_3,
              reinterpret_cast<const uint8_t *>(k0RttData), strlen(k0RttData),
              &buffer);
+  ExpectAlert(kTlsAlertUnexpectedMessage);
   ProcessMessage(buffer, TlsAgent::STATE_ERROR,
                  SSL_ERROR_RX_UNEXPECTED_APPLICATION_DATA);
 }
 
 // The server is allowing 0-RTT but the client doesn't offer it,
 // so trial decryption isn't engaged and 0-RTT messages cause
 // an error.
 TEST_F(TlsAgentStreamTestServer, Set0RttOptionClientHelloThenRead) {
@@ -189,16 +195,17 @@ TEST_F(TlsAgentStreamTestServer, Set0Rtt
   agent_->Set0RttEnabled(true);
   DataBuffer buffer;
   MakeRecord(kTlsHandshakeType, SSL_LIBRARY_VERSION_TLS_1_3,
              kCannedTls13ClientHello, sizeof(kCannedTls13ClientHello), &buffer);
   ProcessMessage(buffer, TlsAgent::STATE_CONNECTING);
   MakeRecord(kTlsApplicationDataType, SSL_LIBRARY_VERSION_TLS_1_3,
              reinterpret_cast<const uint8_t *>(k0RttData), strlen(k0RttData),
              &buffer);
+  ExpectAlert(kTlsAlertBadRecordMac);
   ProcessMessage(buffer, TlsAgent::STATE_ERROR, SSL_ERROR_BAD_MAC_READ);
 }
 
 INSTANTIATE_TEST_CASE_P(AgentTests, TlsAgentTest,
                         ::testing::Combine(TlsAgentTestBase::kTlsRolesAll,
                                            TlsConnectTestBase::kTlsModesStream,
                                            TlsConnectTestBase::kTlsVAll));
 INSTANTIATE_TEST_CASE_P(ClientTests, TlsAgentTestClient,
--- a/security/nss/gtests/ssl_gtest/ssl_auth_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_auth_unittest.cc
@@ -193,17 +193,17 @@ TEST_P(TlsConnectTls12, ClientAuthNoSigA
   server_->SetPacketFilter(filter);
   auto capture_cert_verify =
       std::make_shared<TlsInspectorRecordHandshakeMessage>(
           kTlsHandshakeCertificateVerify);
   client_->SetPacketFilter(capture_cert_verify);
   client_->SetupClientAuth();
   server_->RequestClientAuth(true);
 
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertDecryptError);
 
   // We're expecting a bad signature here because we tampered with a handshake
   // message (CertReq). Previously, without the SHA-1 fallback, we would've
   // seen a malformed record alert.
   server_->CheckErrorCode(SEC_ERROR_BAD_SIGNATURE);
   client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT);
 
   CheckSigScheme(capture_cert_verify, 0, server_, ssl_sig_rsa_pkcs1_sha1, 1024);
@@ -279,17 +279,17 @@ TEST_P(TlsConnectTls12, SignatureSchemeC
   Connect();
 }
 
 // In TLS 1.3, curve and hash are coupled.
 TEST_P(TlsConnectTls13, SignatureSchemeCurveMismatch) {
   Reset(TlsAgent::kServerEcdsa256);
   client_->SetSignatureSchemes(SignatureSchemeEcdsaSha384,
                                PR_ARRAY_SIZE(SignatureSchemeEcdsaSha384));
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
   server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
   client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
 }
 
 // Configuring a P-256 cert with only SHA-384 signatures is OK in TLS 1.2.
 TEST_P(TlsConnectTls12, SignatureSchemeBadConfig) {
   Reset(TlsAgent::kServerEcdsa256);  // P-256 cert can't be used.
   server_->SetSignatureSchemes(SignatureSchemeEcdsaSha384,
@@ -297,17 +297,17 @@ TEST_P(TlsConnectTls12, SignatureSchemeB
   Connect();
 }
 
 // A P-256 certificate in TLS 1.3 needs a SHA-256 signature scheme.
 TEST_P(TlsConnectTls13, SignatureSchemeBadConfig) {
   Reset(TlsAgent::kServerEcdsa256);  // P-256 cert can't be used.
   server_->SetSignatureSchemes(SignatureSchemeEcdsaSha384,
                                PR_ARRAY_SIZE(SignatureSchemeEcdsaSha384));
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
   server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
   client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
 }
 
 // Where there is no overlap on signature schemes, we still connect successfully
 // if we aren't going to use a signature.
 TEST_P(TlsConnectGenericPre13, SignatureAlgorithmNoOverlapStaticRsa) {
   client_->SetSignatureSchemes(SignatureSchemeRsaSha384,
@@ -320,17 +320,17 @@ TEST_P(TlsConnectGenericPre13, Signature
 }
 
 TEST_P(TlsConnectTls12Plus, SignatureAlgorithmNoOverlapEcdsa) {
   Reset(TlsAgent::kServerEcdsa256);
   client_->SetSignatureSchemes(SignatureSchemeEcdsaSha384,
                                PR_ARRAY_SIZE(SignatureSchemeEcdsaSha384));
   server_->SetSignatureSchemes(SignatureSchemeEcdsaSha256,
                                PR_ARRAY_SIZE(SignatureSchemeEcdsaSha256));
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
   client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
   server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
 }
 
 // Pre 1.2, a mismatch on signature algorithms shouldn't affect anything.
 TEST_P(TlsConnectPre12, SignatureAlgorithmNoOverlapEcdsa) {
   Reset(TlsAgent::kServerEcdsa256);
   client_->SetSignatureSchemes(SignatureSchemeEcdsaSha384,
@@ -339,27 +339,27 @@ TEST_P(TlsConnectPre12, SignatureAlgorit
                                PR_ARRAY_SIZE(SignatureSchemeEcdsaSha256));
   Connect();
 }
 
 // The signature_algorithms extension is mandatory in TLS 1.3.
 TEST_P(TlsConnectTls13, SignatureAlgorithmDrop) {
   client_->SetPacketFilter(
       std::make_shared<TlsExtensionDropper>(ssl_signature_algorithms_xtn));
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertMissingExtension);
   client_->CheckErrorCode(SSL_ERROR_MISSING_EXTENSION_ALERT);
   server_->CheckErrorCode(SSL_ERROR_MISSING_SIGNATURE_ALGORITHMS_EXTENSION);
 }
 
 // TLS 1.2 has trouble detecting this sort of modification: it uses SHA1 and
 // only fails when the Finished is checked.
 TEST_P(TlsConnectTls12, SignatureAlgorithmDrop) {
   client_->SetPacketFilter(
       std::make_shared<TlsExtensionDropper>(ssl_signature_algorithms_xtn));
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertDecryptError);
   client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT);
   server_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
 }
 
 TEST_P(TlsConnectTls12Plus, RequestClientAuthWithSha384) {
   server_->SetSignatureSchemes(SignatureSchemeRsaSha384,
                                PR_ARRAY_SIZE(SignatureSchemeRsaSha384));
   server_->RequestClientAuth(false);
--- a/security/nss/gtests/ssl_gtest/ssl_cert_ext_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_cert_ext_unittest.cc
@@ -185,17 +185,17 @@ TEST_P(TlsConnectGenericPre13, OcspMangl
                                       SSL_ENABLE_OCSP_STAPLING, PR_TRUE));
   EXPECT_TRUE(
       server_->ConfigServerCert(TlsAgent::kServerRsa, true, &kOcspExtraData));
 
   static const uint8_t val[] = {1};
   auto replacer = std::make_shared<TlsExtensionReplacer>(
       ssl_cert_status_xtn, DataBuffer(val, sizeof(val)));
   server_->SetPacketFilter(replacer);
-  ConnectExpectFail();
+  ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
   client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
   server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
 }
 
 TEST_P(TlsConnectGeneric, OcspSuccess) {
   EnsureTlsSetup();
   EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
                                       SSL_ENABLE_OCSP_STAPLING, PR_TRUE));
--- a/security/nss/gtests/ssl_gtest/ssl_damage_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_damage_unittest.cc
@@ -28,32 +28,37 @@ TEST_F(TlsConnectTest, DamageSecretHandl
   client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_3);
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_3);
   server_->StartConnect();
   client_->StartConnect();
   client_->Handshake();
   server_->Handshake();
-  std::cerr << "Damaging HS secret\n";
+  std::cerr << "Damaging HS secret" << std::endl;
   SSLInt_DamageClientHsTrafficSecret(server_->ssl_fd());
   client_->Handshake();
-  server_->Handshake();
   // The client thinks it has connected.
   EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
+
+  ExpectAlert(server_, kTlsAlertDecryptError);
+  server_->Handshake();
   server_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
   client_->Handshake();
   client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT);
 }
 
 TEST_F(TlsConnectTest, DamageSecretHandleServerFinished) {
   client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_3);
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_3);
+  client_->ExpectSendAlert(kTlsAlertDecryptError);
+  // The server can't read the client's alert, so it also sends an alert.
+  server_->ExpectSendAlert(kTlsAlertBadRecordMac);
   server_->SetPacketFilter(std::make_shared<AfterRecordN>(
       server_, client_,
       0,  // ServerHello.
       [this]() { SSLInt_DamageServerHsTrafficSecret(client_->ssl_fd()); }));
   ConnectExpectFail();
   client_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
   server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
 }
--- a/security/nss/gtests/ssl_gtest/ssl_dhe_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_dhe_unittest.cc
@@ -92,17 +92,17 @@ TEST_P(TlsConnectGenericPre13, ConnectFf
   EnableOnlyDheCiphers();
   EXPECT_EQ(SECSuccess, SSL_OptionSet(server_->ssl_fd(),
                                       SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE));
 
   if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
     Connect();
     CheckKeys(ssl_kea_dh, ssl_auth_rsa_sign);
   } else {
-    ConnectExpectFail();
+    ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
     client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
     server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
   }
 }
 
 class TlsDheServerKeyExchangeDamager : public TlsHandshakeFilter {
  public:
   TlsDheServerKeyExchangeDamager() {}
@@ -125,17 +125,17 @@ class TlsDheServerKeyExchangeDamager : p
 // invalidate the signature over the ServerKeyShare. That's ok, NSS won't check
 // the signature until everything else has been checked.
 TEST_P(TlsConnectGenericPre13, DamageServerKeyShare) {
   EnableOnlyDheCiphers();
   EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
                                       SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE));
   server_->SetPacketFilter(std::make_shared<TlsDheServerKeyExchangeDamager>());
 
-  ConnectExpectFail();
+  ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
 
   client_->CheckErrorCode(SSL_ERROR_WEAK_SERVER_EPHEMERAL_DH_KEY);
   server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
 }
 
 class TlsDheSkeChangeY : public TlsHandshakeFilter {
  public:
   enum ChangeYTo {
@@ -290,16 +290,21 @@ TEST_P(TlsDamageDHYTest, DamageServerY) 
   if (std::get<3>(GetParam())) {
     EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
                                         SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE));
   }
   TlsDheSkeChangeY::ChangeYTo change = std::get<2>(GetParam());
   server_->SetPacketFilter(
       std::make_shared<TlsDheSkeChangeYServer>(change, true));
 
+  if (change == TlsDheSkeChangeY::kYZeroPad) {
+    ExpectAlert(client_, kTlsAlertDecryptError);
+  } else {
+    ExpectAlert(client_, kTlsAlertIllegalParameter);
+  }
   ConnectExpectFail();
   if (change == TlsDheSkeChangeY::kYZeroPad) {
     // Zero padding Y only manifests in a signature failure.
     // In TLS 1.0 and 1.1, the client reports a device error.
     if (version_ < SSL_LIBRARY_VERSION_TLS_1_2) {
       client_->CheckErrorCode(SEC_ERROR_PKCS11_DEVICE_ERROR);
     } else {
       client_->CheckErrorCode(SEC_ERROR_BAD_SIGNATURE);
@@ -322,16 +327,21 @@ TEST_P(TlsDamageDHYTest, DamageClientY) 
       std::make_shared<TlsDheSkeChangeYServer>(TlsDheSkeChangeY::kYZero, false);
   server_->SetPacketFilter(server_filter);
 
   // The client filter does the damage.
   TlsDheSkeChangeY::ChangeYTo change = std::get<2>(GetParam());
   client_->SetPacketFilter(
       std::make_shared<TlsDheSkeChangeYClient>(change, server_filter));
 
+  if (change == TlsDheSkeChangeY::kYZeroPad) {
+    ExpectAlert(server_, kTlsAlertDecryptError);
+  } else {
+    ExpectAlert(server_, kTlsAlertHandshakeFailure);
+  }
   ConnectExpectFail();
   if (change == TlsDheSkeChangeY::kYZeroPad) {
     // Zero padding Y only manifests in a finished error.
     client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT);
     server_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
   } else {
     client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_FAILURE_ALERT);
     server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_DHE_KEY_SHARE);
@@ -380,17 +390,17 @@ class TlsDheSkeMakePEven : public TlsHan
   }
 };
 
 // Even without requiring named groups, an even value for p is bad news.
 TEST_P(TlsConnectGenericPre13, MakeDhePEven) {
   EnableOnlyDheCiphers();
   server_->SetPacketFilter(std::make_shared<TlsDheSkeMakePEven>());
 
-  ConnectExpectFail();
+  ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
 
   client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_DHE_KEY_SHARE);
   server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
 }
 
 class TlsDheSkeZeroPadP : public TlsHandshakeFilter {
  public:
   virtual PacketFilter::Action FilterHandshake(
@@ -411,17 +421,17 @@ class TlsDheSkeZeroPadP : public TlsHand
   }
 };
 
 // Zero padding only causes signature failure.
 TEST_P(TlsConnectGenericPre13, PadDheP) {
   EnableOnlyDheCiphers();
   server_->SetPacketFilter(std::make_shared<TlsDheSkeZeroPadP>());
 
-  ConnectExpectFail();
+  ConnectExpectAlert(client_, kTlsAlertDecryptError);
 
   // In TLS 1.0 and 1.1, the client reports a device error.
   if (version_ < SSL_LIBRARY_VERSION_TLS_1_2) {
     client_->CheckErrorCode(SEC_ERROR_PKCS11_DEVICE_ERROR);
   } else {
     client_->CheckErrorCode(SEC_ERROR_BAD_SIGNATURE);
   }
   server_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT);
@@ -470,17 +480,17 @@ TEST_P(TlsConnectGenericPre13, NamedGrou
 TEST_P(TlsConnectTls13, NamedGroupMismatch13) {
   EnableOnlyDheCiphers();
   static const std::vector<SSLNamedGroup> server_groups = {ssl_grp_ffdhe_3072};
   static const std::vector<SSLNamedGroup> client_groups = {
       ssl_grp_ec_secp256r1};
   server_->ConfigNamedGroups(server_groups);
   client_->ConfigNamedGroups(client_groups);
 
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
   server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
   client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
 }
 
 // Even though the client doesn't have DHE groups enabled the server assumes it
 // does. The client requires named groups and thus does not accept FF3072 as
 // custom group in contrast to the previous test.
 TEST_P(TlsConnectGenericPre13, RequireNamedGroupsMismatchPre13) {
@@ -488,17 +498,17 @@ TEST_P(TlsConnectGenericPre13, RequireNa
   EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
                                       SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE));
   static const std::vector<SSLNamedGroup> server_groups = {ssl_grp_ffdhe_3072};
   static const std::vector<SSLNamedGroup> client_groups = {ssl_grp_ec_secp256r1,
                                                            ssl_grp_ffdhe_2048};
   server_->ConfigNamedGroups(server_groups);
   client_->ConfigNamedGroups(client_groups);
 
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
   server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
   client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
 }
 
 TEST_P(TlsConnectGenericPre13, PreferredFfdhe) {
   EnableOnlyDheCiphers();
   static const SSLDHEGroupType groups[] = {ssl_ff_dhe_3072_group,
                                            ssl_ff_dhe_2048_group};
@@ -518,17 +528,17 @@ TEST_P(TlsConnectGenericPre13, MismatchD
                                       SSL_REQUIRE_DH_NAMED_GROUPS, PR_TRUE));
   static const SSLDHEGroupType serverGroups[] = {ssl_ff_dhe_3072_group};
   EXPECT_EQ(SECSuccess, SSL_DHEGroupPrefSet(server_->ssl_fd(), serverGroups,
                                             PR_ARRAY_SIZE(serverGroups)));
   static const SSLDHEGroupType clientGroups[] = {ssl_ff_dhe_2048_group};
   EXPECT_EQ(SECSuccess, SSL_DHEGroupPrefSet(client_->ssl_fd(), clientGroups,
                                             PR_ARRAY_SIZE(clientGroups)));
 
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
   server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
   client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
 }
 
 TEST_P(TlsConnectTls13, ResumeFfdhe) {
   EnableOnlyDheCiphers();
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   Connect();
@@ -602,13 +612,13 @@ TEST_P(TlsConnectGenericPre13, InvalidDE
   Reset(TlsAgent::kServerDsa);
 
   const std::vector<SSLNamedGroup> client_groups = {ssl_grp_ffdhe_2048};
   client_->ConfigNamedGroups(client_groups);
 
   server_->SetPacketFilter(std::make_shared<TlsDheSkeChangeSignature>(
       version_, kBogusDheSignature, sizeof(kBogusDheSignature)));
 
-  ConnectExpectFail();
+  ConnectExpectAlert(client_, kTlsAlertDecryptError);
   client_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
 }
 
 }  // namespace nss_test
--- a/security/nss/gtests/ssl_gtest/ssl_ecdh_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_ecdh_unittest.cc
@@ -223,29 +223,29 @@ class TlsKeyExchangeGroupCapture : publi
 // P-256 is supported by the client (<= 1.2 only).
 TEST_P(TlsConnectGenericPre13, DropSupportedGroupExtensionP256) {
   EnsureTlsSetup();
   client_->SetPacketFilter(
       std::make_shared<TlsExtensionDropper>(ssl_supported_groups_xtn));
   auto group_capture = std::make_shared<TlsKeyExchangeGroupCapture>();
   server_->SetPacketFilter(group_capture);
 
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertDecryptError);
   client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT);
   server_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
 
   EXPECT_EQ(ssl_grp_ec_secp256r1, group_capture->group());
 }
 
 // Supported groups is mandatory in TLS 1.3.
 TEST_P(TlsConnectTls13, DropSupportedGroupExtension) {
   EnsureTlsSetup();
   client_->SetPacketFilter(
       std::make_shared<TlsExtensionDropper>(ssl_supported_groups_xtn));
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertMissingExtension);
   client_->CheckErrorCode(SSL_ERROR_MISSING_EXTENSION_ALERT);
   server_->CheckErrorCode(SSL_ERROR_MISSING_SUPPORTED_GROUPS_EXTENSION);
 }
 
 // If we only have a lame group, we fall back to static RSA.
 TEST_P(TlsConnectGenericPre13, UseLameGroup) {
   const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp192r1};
   client_->ConfigNamedGroups(groups);
@@ -480,17 +480,17 @@ TEST_P(TlsConnectGeneric, P256ClientAndC
 
   // The client sends a P256 key share while the server prefers 25519.
   const std::vector<SSLNamedGroup> client_groups = {ssl_grp_ec_secp256r1};
   const std::vector<SSLNamedGroup> server_groups = {ssl_grp_ec_curve25519};
 
   client_->ConfigNamedGroups(client_groups);
   server_->ConfigNamedGroups(server_groups);
 
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
   client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
   server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
 }
 
 TEST_P(TlsKeyExchangeTest13, MultipleClientShares) {
   EnsureKeyShareSetup();
 
   // The client sends 25519 and P256 key shares. The server prefers P256,
@@ -557,24 +557,24 @@ class ECCServerKEXFilter : public TlsHan
     output->Write(3, 0U, 1);                // point length 0
     return CHANGE;
   }
 };
 
 TEST_P(TlsConnectGenericPre13, ConnectECDHEmptyServerPoint) {
   // add packet filter
   server_->SetPacketFilter(std::make_shared<ECCServerKEXFilter>());
-  ConnectExpectFail();
+  ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
   client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_SERVER_KEY_EXCH);
 }
 
 TEST_P(TlsConnectGenericPre13, ConnectECDHEmptyClientPoint) {
   // add packet filter
   client_->SetPacketFilter(std::make_shared<ECCClientKEXFilter>());
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
   server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_KEY_EXCH);
 }
 
 INSTANTIATE_TEST_CASE_P(KeyExchangeTest, TlsKeyExchangeTest,
                         ::testing::Combine(TlsConnectTestBase::kTlsModesAll,
                                            TlsConnectTestBase::kTlsV11Plus));
 
 #ifndef NSS_DISABLE_TLS_1_3
--- a/security/nss/gtests/ssl_gtest/ssl_ems_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_ems_unittest.cc
@@ -74,21 +74,17 @@ TEST_P(TlsConnectGenericPre13, ConnectEx
 }
 
 TEST_P(TlsConnectGenericPre13, ConnectExtendedMasterSecretResumeWithout) {
   EnableExtendedMasterSecret();
   Connect();
 
   Reset();
   server_->EnableExtendedMasterSecret();
-  auto alert_recorder = std::make_shared<TlsAlertRecorder>();
-  server_->SetPacketFilter(alert_recorder);
-  ConnectExpectFail();
-  EXPECT_EQ(kTlsAlertFatal, alert_recorder->level());
-  EXPECT_EQ(kTlsAlertHandshakeFailure, alert_recorder->description());
+  ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
 }
 
 TEST_P(TlsConnectGenericPre13, ConnectNormalResumeWithExtendedMasterSecret) {
   ConfigureSessionCache(RESUME_SESSIONID, RESUME_SESSIONID);
   ExpectExtendedMasterSecret(false);
   Connect();
 
   Reset();
--- a/security/nss/gtests/ssl_gtest/ssl_exporter_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_exporter_unittest.cc
@@ -86,18 +86,17 @@ int32_t RegularExporterShouldFail(TlsAge
                             strlen(kExporterLabel), PR_TRUE, kExporterContext,
                             sizeof(kExporterContext), val, sizeof(val)))
       << "regular exporter should fail";
   return 0;
 }
 
 TEST_P(TlsConnectTls13, EarlyExporter) {
   SetupForZeroRtt();
-  client_->SetExpectedAlertSentCount(1);
-  server_->SetExpectedAlertReceivedCount(1);
+  ExpectAlert(client_, kTlsAlertEndOfEarlyData);
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
   ExpectResumption(RESUME_TICKET);
 
   client_->Handshake();  // Send ClientHello.
   uint8_t client_value[10] = {0};
   RegularExporterShouldFail(client_.get(), nullptr, 0);
   EXPECT_EQ(SECSuccess,
--- a/security/nss/gtests/ssl_gtest/ssl_extension_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_extension_unittest.cc
@@ -163,74 +163,24 @@ class TlsExtensionTestBase : public TlsC
  protected:
   TlsExtensionTestBase(Mode mode, uint16_t version)
       : TlsConnectTestBase(mode, version) {}
   TlsExtensionTestBase(const std::string& mode, uint16_t version)
       : TlsConnectTestBase(mode, version) {}
 
   void ClientHelloErrorTest(std::shared_ptr<PacketFilter> filter,
                             uint8_t desc = kTlsAlertDecodeError) {
-    SSLAlert alert;
-
-    auto alert_recorder = std::make_shared<TlsAlertRecorder>();
-    server_->SetPacketFilter(alert_recorder);
     client_->SetPacketFilter(filter);
-    ConnectExpectFail();
-
-    EXPECT_EQ(kTlsAlertFatal, alert_recorder->level());
-    EXPECT_EQ(desc, alert_recorder->description());
-
-    // verify no alerts received by the server
-    EXPECT_EQ(0U, server_->alert_received_count());
-
-    // verify the alert sent by the server
-    EXPECT_EQ(1U, server_->alert_sent_count());
-    EXPECT_TRUE(server_->GetLastAlertSent(&alert));
-    EXPECT_EQ(kTlsAlertFatal, alert.level);
-    EXPECT_EQ(desc, alert.description);
-
-    // verify the alert received by the client
-    EXPECT_EQ(1U, client_->alert_received_count());
-    EXPECT_TRUE(client_->GetLastAlertReceived(&alert));
-    EXPECT_EQ(kTlsAlertFatal, alert.level);
-    EXPECT_EQ(desc, alert.description);
-
-    // verify no alerts sent by the client
-    EXPECT_EQ(0U, client_->alert_sent_count());
+    ConnectExpectAlert(server_, desc);
   }
 
   void ServerHelloErrorTest(std::shared_ptr<PacketFilter> filter,
                             uint8_t desc = kTlsAlertDecodeError) {
-    SSLAlert alert;
-
-    auto alert_recorder = std::make_shared<TlsAlertRecorder>();
-    client_->SetPacketFilter(alert_recorder);
     server_->SetPacketFilter(filter);
-    ConnectExpectFail();
-
-    EXPECT_EQ(kTlsAlertFatal, alert_recorder->level());
-    EXPECT_EQ(desc, alert_recorder->description());
-
-    // verify no alerts received by the client
-    EXPECT_EQ(0U, client_->alert_received_count());
-
-    // verify the alert sent by the client
-    EXPECT_EQ(1U, client_->alert_sent_count());
-    EXPECT_TRUE(client_->GetLastAlertSent(&alert));
-    EXPECT_EQ(kTlsAlertFatal, alert.level);
-    EXPECT_EQ(desc, alert.description);
-
-    // verify the alert received by the server
-    EXPECT_EQ(1U, server_->alert_received_count());
-    EXPECT_TRUE(server_->GetLastAlertReceived(&alert));
-    EXPECT_EQ(kTlsAlertFatal, alert.level);
-    EXPECT_EQ(desc, alert.description);
-
-    // verify no alerts sent by the server
-    EXPECT_EQ(0U, server_->alert_sent_count());
+    ConnectExpectAlert(client_, desc);
   }
 
   static void InitSimpleSni(DataBuffer* extension) {
     const char* name = "host.name";
     const size_t namelen = PL_strlen(name);
     extension->Allocate(namelen + 5);
     extension->Write(0, namelen + 3, 2);
     extension->Write(2, static_cast<uint32_t>(0), 1);  // 0 == hostname
@@ -287,17 +237,17 @@ class TlsExtensionTest13 : public TlsExt
  public:
   TlsExtensionTest13()
       : TlsExtensionTestBase(GetParam(), SSL_LIBRARY_VERSION_TLS_1_3) {}
 
   void ConnectWithBogusVersionList(const uint8_t* buf, size_t len) {
     DataBuffer versions_buf(buf, len);
     client_->SetPacketFilter(std::make_shared<TlsExtensionReplacer>(
         ssl_tls13_supported_versions_xtn, versions_buf));
-    ConnectExpectFail();
+    ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
     client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
     server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
   }
 
   void ConnectWithReplacementVersionList(uint16_t version) {
     DataBuffer versions_buf;
 
     size_t index = versions_buf.Write(0, 2, 1);
@@ -632,16 +582,18 @@ TEST_P(TlsExtensionTest13, EmptyClientKe
 // These tests only work in stream mode because the client sends a
 // cleartext alert which causes a MAC error on the server. With
 // stream this causes handshake failure but with datagram, the
 // packet gets dropped.
 TEST_F(TlsExtensionTest13Stream, DropServerKeyShare) {
   EnsureTlsSetup();
   server_->SetPacketFilter(
       std::make_shared<TlsExtensionDropper>(ssl_tls13_key_share_xtn));
+  client_->ExpectSendAlert(kTlsAlertMissingExtension);
+  server_->ExpectSendAlert(kTlsAlertBadRecordMac);
   ConnectExpectFail();
   EXPECT_EQ(SSL_ERROR_MISSING_KEY_SHARE, client_->error_code());
   EXPECT_EQ(SSL_ERROR_BAD_MAC_READ, server_->error_code());
 }
 
 TEST_F(TlsExtensionTest13Stream, WrongServerKeyShare) {
   const uint16_t wrong_group = ssl_grp_ec_secp384r1;
 
@@ -651,16 +603,18 @@ TEST_F(TlsExtensionTest13Stream, WrongSe
       0x00,
       0x02,  // length = 2
       0x01,
       0x02};
   DataBuffer buf(key_share, sizeof(key_share));
   EnsureTlsSetup();
   server_->SetPacketFilter(
       std::make_shared<TlsExtensionReplacer>(ssl_tls13_key_share_xtn, buf));
+  client_->ExpectSendAlert(kTlsAlertIllegalParameter);
+  server_->ExpectSendAlert(kTlsAlertBadRecordMac);
   ConnectExpectFail();
   EXPECT_EQ(SSL_ERROR_RX_MALFORMED_KEY_SHARE, client_->error_code());
   EXPECT_EQ(SSL_ERROR_BAD_MAC_READ, server_->error_code());
 }
 
 // TODO(ekr@rtfm.com): This is the wrong error code. See bug 1307269.
 TEST_F(TlsExtensionTest13Stream, UnknownServerKeyShare) {
   const uint16_t wrong_group = 0xffff;
@@ -671,26 +625,30 @@ TEST_F(TlsExtensionTest13Stream, Unknown
       0x00,
       0x02,  // length = 2
       0x01,
       0x02};
   DataBuffer buf(key_share, sizeof(key_share));
   EnsureTlsSetup();
   server_->SetPacketFilter(
       std::make_shared<TlsExtensionReplacer>(ssl_tls13_key_share_xtn, buf));
+  client_->ExpectSendAlert(kTlsAlertMissingExtension);
+  server_->ExpectSendAlert(kTlsAlertBadRecordMac);
   ConnectExpectFail();
   EXPECT_EQ(SSL_ERROR_MISSING_KEY_SHARE, client_->error_code());
   EXPECT_EQ(SSL_ERROR_BAD_MAC_READ, server_->error_code());
 }
 
 TEST_F(TlsExtensionTest13Stream, AddServerSignatureAlgorithmsOnResumption) {
   SetupForResume();
   DataBuffer empty;
   server_->SetPacketFilter(std::make_shared<TlsExtensionInjector>(
       ssl_signature_algorithms_xtn, empty));
+  client_->ExpectSendAlert(kTlsAlertUnsupportedExtension);
+  server_->ExpectSendAlert(kTlsAlertBadRecordMac);
   ConnectExpectFail();
   EXPECT_EQ(SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION, client_->error_code());
   EXPECT_EQ(SSL_ERROR_BAD_MAC_READ, server_->error_code());
 }
 
 struct PskIdentity {
   DataBuffer identity;
   uint32_t obfuscated_ticket_age;
@@ -815,132 +773,134 @@ class TlsPreSharedKeyReplacer : public T
   TlsPreSharedKeyReplacerFunc function_;
 };
 
 TEST_F(TlsExtensionTest13Stream, ResumeEmptyPskLabel) {
   SetupForResume();
 
   client_->SetPacketFilter(std::make_shared<TlsPreSharedKeyReplacer>([](
       TlsPreSharedKeyReplacer* r) { r->identities_[0].identity.Truncate(0); }));
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
   client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
   server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
 }
 
 // Flip the first byte of the binder.
 TEST_F(TlsExtensionTest13Stream, ResumeIncorrectBinderValue) {
   SetupForResume();
 
   client_->SetPacketFilter(
       std::make_shared<TlsPreSharedKeyReplacer>([](TlsPreSharedKeyReplacer* r) {
         r->binders_[0].Write(0, r->binders_[0].data()[0] ^ 0xff, 1);
       }));
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertDecryptError);
   client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT);
   server_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
 }
 
 // Extend the binder by one.
 TEST_F(TlsExtensionTest13Stream, ResumeIncorrectBinderLength) {
   SetupForResume();
 
   client_->SetPacketFilter(
       std::make_shared<TlsPreSharedKeyReplacer>([](TlsPreSharedKeyReplacer* r) {
         r->binders_[0].Write(r->binders_[0].len(), 0xff, 1);
       }));
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
   client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
   server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
 }
 
 // Binders must be at least 32 bytes.
 TEST_F(TlsExtensionTest13Stream, ResumeBinderTooShort) {
   SetupForResume();
 
   client_->SetPacketFilter(std::make_shared<TlsPreSharedKeyReplacer>(
       [](TlsPreSharedKeyReplacer* r) { r->binders_[0].Truncate(31); }));
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
   client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
   server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
 }
 
 // Duplicate the identity and binder. This will fail with an error
 // processing the binder (because we extended the identity list.)
 TEST_F(TlsExtensionTest13Stream, ResumeTwoPsks) {
   SetupForResume();
 
   client_->SetPacketFilter(
       std::make_shared<TlsPreSharedKeyReplacer>([](TlsPreSharedKeyReplacer* r) {
         r->identities_.push_back(r->identities_[0]);
         r->binders_.push_back(r->binders_[0]);
       }));
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertDecryptError);
   client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT);
   server_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
 }
 
 // The next two tests have mismatches in the number of identities
 // and binders. This generates an illegal parameter alert.
 TEST_F(TlsExtensionTest13Stream, ResumeTwoIdentitiesOneBinder) {
   SetupForResume();
 
   client_->SetPacketFilter(
       std::make_shared<TlsPreSharedKeyReplacer>([](TlsPreSharedKeyReplacer* r) {
         r->identities_.push_back(r->identities_[0]);
       }));
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
   client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
   server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
 }
 
 TEST_F(TlsExtensionTest13Stream, ResumeOneIdentityTwoBinders) {
   SetupForResume();
 
   client_->SetPacketFilter(std::make_shared<TlsPreSharedKeyReplacer>([](
       TlsPreSharedKeyReplacer* r) { r->binders_.push_back(r->binders_[0]); }));
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
   client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
   server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
 }
 
 TEST_F(TlsExtensionTest13Stream, ResumePskExtensionNotLast) {
   SetupForResume();
 
   const uint8_t empty_buf[] = {0};
   DataBuffer empty(empty_buf, 0);
   client_->SetPacketFilter(
       // Inject an unused extension.
       std::make_shared<TlsExtensionAppender>(0xffff, empty));
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
   client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
   server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
 }
 
 TEST_F(TlsExtensionTest13Stream, ResumeNoKeModes) {
   SetupForResume();
 
   DataBuffer empty;
   client_->SetPacketFilter(std::make_shared<TlsExtensionDropper>(
       ssl_tls13_psk_key_exchange_modes_xtn));
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertMissingExtension);
   client_->CheckErrorCode(SSL_ERROR_MISSING_EXTENSION_ALERT);
   server_->CheckErrorCode(SSL_ERROR_MISSING_PSK_KEY_EXCHANGE_MODES);
 }
 
 // The following test contains valid but unacceptable PreSharedKey
 // modes and therefore produces non-resumption followed by MAC
 // errors.
 TEST_F(TlsExtensionTest13Stream, ResumeBogusKeModes) {
   SetupForResume();
   const static uint8_t ke_modes[] = {1,  // Length
                                      kTls13PskKe};
 
   DataBuffer modes(ke_modes, sizeof(ke_modes));
   client_->SetPacketFilter(std::make_shared<TlsExtensionReplacer>(
       ssl_tls13_psk_key_exchange_modes_xtn, modes));
+  client_->ExpectSendAlert(kTlsAlertBadRecordMac);
+  server_->ExpectSendAlert(kTlsAlertBadRecordMac);
   ConnectExpectFail();
   client_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
   server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
 }
 
 TEST_P(TlsExtensionTest13, NoKeModesIfResumptionOff) {
   ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
   auto capture = std::make_shared<TlsExtensionCapture>(
@@ -950,61 +910,71 @@ TEST_P(TlsExtensionTest13, NoKeModesIfRe
   EXPECT_FALSE(capture->captured());
 }
 
 // In these tests, we downgrade to TLS 1.2, causing the
 // server to negotiate TLS 1.2.
 // 1. Both sides only support TLS 1.3, so we get a cipher version
 //    error.
 TEST_P(TlsExtensionTest13, RemoveTls13FromVersionList) {
+  ExpectAlert(server_, kTlsAlertProtocolVersion);
   ConnectWithReplacementVersionList(SSL_LIBRARY_VERSION_TLS_1_2);
   client_->CheckErrorCode(SSL_ERROR_PROTOCOL_VERSION_ALERT);
   server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_VERSION);
 }
 
 // 2. Server supports 1.2 and 1.3, client supports 1.2, so we
 //    can't negotiate any ciphers.
 TEST_P(TlsExtensionTest13, RemoveTls13FromVersionListServerV12) {
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
                            SSL_LIBRARY_VERSION_TLS_1_3);
+  ExpectAlert(server_, kTlsAlertHandshakeFailure);
   ConnectWithReplacementVersionList(SSL_LIBRARY_VERSION_TLS_1_2);
   client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
   server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
 }
 
 // 3. Server supports 1.2 and 1.3, client supports 1.2 and 1.3
 // but advertises 1.2 (because we changed things).
 TEST_P(TlsExtensionTest13, RemoveTls13FromVersionListBothV12) {
   client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
                            SSL_LIBRARY_VERSION_TLS_1_3);
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
                            SSL_LIBRARY_VERSION_TLS_1_3);
+#ifndef TLS_1_3_DRAFT_VERSION
+  ExpectAlert(server_, kTlsAlertIllegalParameter);
+#else
+  ExpectAlert(server_, kTlsAlertDecryptError);
+#endif
   ConnectWithReplacementVersionList(SSL_LIBRARY_VERSION_TLS_1_2);
 #ifndef TLS_1_3_DRAFT_VERSION
   client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
   server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
 #else
   client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT);
   server_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
 #endif
 }
 
 TEST_P(TlsExtensionTest13, HrrThenRemoveSignatureAlgorithms) {
+  ExpectAlert(server_, kTlsAlertMissingExtension);
   HrrThenRemoveExtensionsTest(ssl_signature_algorithms_xtn,
                               SSL_ERROR_MISSING_EXTENSION_ALERT,
                               SSL_ERROR_MISSING_SIGNATURE_ALGORITHMS_EXTENSION);
 }
 
 TEST_P(TlsExtensionTest13, HrrThenRemoveKeyShare) {
+  ExpectAlert(server_, kTlsAlertIllegalParameter);
   HrrThenRemoveExtensionsTest(ssl_tls13_key_share_xtn,
                               SSL_ERROR_ILLEGAL_PARAMETER_ALERT,
                               SSL_ERROR_BAD_2ND_CLIENT_HELLO);
 }
 
 TEST_P(TlsExtensionTest13, HrrThenRemoveSupportedGroups) {
+  ExpectAlert(server_, kTlsAlertMissingExtension);
   HrrThenRemoveExtensionsTest(ssl_supported_groups_xtn,
                               SSL_ERROR_MISSING_EXTENSION_ALERT,
                               SSL_ERROR_MISSING_SUPPORTED_GROUPS_EXTENSION);
 }
 
 TEST_P(TlsExtensionTest13, EmptyVersionList) {
   static const uint8_t ext[] = {0x00, 0x00};
   ConnectWithBogusVersionList(ext, sizeof(ext));
--- a/security/nss/gtests/ssl_gtest/ssl_fuzz_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_fuzz_unittest.cc
@@ -12,19 +12,16 @@
 namespace nss_test {
 
 #ifdef UNSAFE_FUZZER_MODE
 #define FUZZ_F(c, f) TEST_F(c, Fuzz_##f)
 #define FUZZ_P(c, f) TEST_P(c, Fuzz_##f)
 #else
 #define FUZZ_F(c, f) TEST_F(c, DISABLED_Fuzz_##f)
 #define FUZZ_P(c, f) TEST_P(c, DISABLED_Fuzz_##f)
-// RNG_ResetForFuzzing() isn't exported from the shared libraries, rather than
-// fail to link to it, make it fail (we're not running it anyway).
-#define RNG_ResetForFuzzing() SECFailure
 #endif
 
 const uint8_t kShortEmptyFinished[8] = {0};
 const uint8_t kLongEmptyFinished[128] = {0};
 
 class TlsFuzzTest : public ::testing::Test {};
 
 // Record the application data stream.
@@ -86,31 +83,31 @@ FUZZ_P(TlsConnectGeneric, DeterministicE
   // Make sure we have RSA blinding params.
   Connect();
 
   Reset();
   ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
   DisableECDHEServerKeyReuse();
 
   // Reset the RNG state.
-  EXPECT_EQ(SECSuccess, RNG_ResetForFuzzing());
+  EXPECT_EQ(SECSuccess, RNG_RandomUpdate(NULL, 0));
   Connect();
 
   // Export a key derived from the MS and nonces.
   SECStatus rv =
       SSL_ExportKeyingMaterial(client_->ssl_fd(), kLabel, strlen(kLabel), false,
                                NULL, 0, out1.data(), out1.size());
   EXPECT_EQ(SECSuccess, rv);
 
   Reset();
   ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
   DisableECDHEServerKeyReuse();
 
   // Reset the RNG state.
-  EXPECT_EQ(SECSuccess, RNG_ResetForFuzzing());
+  EXPECT_EQ(SECSuccess, RNG_RandomUpdate(NULL, 0));
   Connect();
 
   // Export another key derived from the MS and nonces.
   rv = SSL_ExportKeyingMaterial(client_->ssl_fd(), kLabel, strlen(kLabel),
                                 false, NULL, 0, out2.data(), out2.size());
   EXPECT_EQ(SECSuccess, rv);
 
   // The two exported keys should be the same.
@@ -130,17 +127,17 @@ FUZZ_P(TlsConnectGeneric, DeterministicT
     ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
     DisableECDHEServerKeyReuse();
 
     DataBuffer buffer;
     client_->SetPacketFilter(std::make_shared<TlsConversationRecorder>(buffer));
     server_->SetPacketFilter(std::make_shared<TlsConversationRecorder>(buffer));
 
     // Reset the RNG state.
-    EXPECT_EQ(SECSuccess, RNG_ResetForFuzzing());
+    EXPECT_EQ(SECSuccess, RNG_RandomUpdate(NULL, 0));
     Connect();
 
     // Ensure the filters go away before |buffer| does.
     client_->DeletePacketFilter();
     server_->DeletePacketFilter();
 
     if (last.len() > 0) {
       EXPECT_EQ(last, buffer);
--- a/security/nss/gtests/ssl_gtest/ssl_gather_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_gather_unittest.cc
@@ -10,28 +10,22 @@
 namespace nss_test {
 
 class GatherV2ClientHelloTest : public TlsConnectTestBase {
  public:
   GatherV2ClientHelloTest() : TlsConnectTestBase(STREAM, 0) {}
 
   void ConnectExpectMalformedClientHello(const DataBuffer &data) {
     EnsureTlsSetup();
-
-    auto alert_recorder = std::make_shared<TlsAlertRecorder>();
-    server_->SetPacketFilter(alert_recorder);
-
+    server_->ExpectSendAlert(kTlsAlertIllegalParameter);
     client_->SendDirect(data);
     server_->StartConnect();
     server_->Handshake();
     ASSERT_TRUE_WAIT(
         (server_->error_code() == SSL_ERROR_RX_MALFORMED_CLIENT_HELLO), 2000);
-
-    EXPECT_EQ(kTlsAlertFatal, alert_recorder->level());
-    EXPECT_EQ(illegal_parameter, alert_recorder->description());
   }
 };
 
 // Gather a 5-byte v3 record, with a zero fragment length. The empty handshake
 // message should be ignored, and the connection will succeed afterwards.
 TEST_F(TlsConnectTest, GatherEmptyV3Record) {
   DataBuffer buffer;
 
@@ -50,26 +44,22 @@ TEST_F(TlsConnectTest, GatherExcessiveV3
   DataBuffer buffer;
 
   size_t idx = 0;
   idx = buffer.Write(idx, 0x16, 1);                            // handshake
   idx = buffer.Write(idx, 0x0301, 2);                          // record_version
   (void)buffer.Write(idx, MAX_FRAGMENT_LENGTH + 2048 + 1, 2);  // length=max+1
 
   EnsureTlsSetup();
-  auto alert_recorder = std::make_shared<TlsAlertRecorder>();
-  server_->SetPacketFilter(alert_recorder);
+  server_->ExpectSendAlert(kTlsAlertRecordOverflow);
   client_->SendDirect(buffer);
   server_->StartConnect();
   server_->Handshake();
   ASSERT_TRUE_WAIT((server_->error_code() == SSL_ERROR_RX_RECORD_TOO_LONG),
                    2000);
-
-  EXPECT_EQ(kTlsAlertFatal, alert_recorder->level());
-  EXPECT_EQ(record_overflow, alert_recorder->description());
 }
 
 // Gather a 3-byte v2 header, with a fragment length of 2.
 TEST_F(GatherV2ClientHelloTest, GatherV2RecordLongHeader) {
   DataBuffer buffer;
 
   size_t idx = 0;
   idx = buffer.Write(idx, 0x0002, 2);  // length=2 (long header)
--- a/security/nss/gtests/ssl_gtest/ssl_hrr_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_hrr_unittest.cc
@@ -139,16 +139,17 @@ TEST_P(TlsConnectTls13, SecondClientHell
   client_->Handshake();
 
   // Send 0-RTT data.
   const char* k0RttData = "ABCDEF";
   const PRInt32 k0RttDataLen = static_cast<PRInt32>(strlen(k0RttData));
   PRInt32 rv = PR_Write(client_->ssl_fd(), k0RttData, k0RttDataLen);
   EXPECT_EQ(k0RttDataLen, rv);
 
+  ExpectAlert(server_, kTlsAlertUnsupportedExtension);
   Handshake();
   client_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_EXTENSION_ALERT);
 }
 
 class KeyShareReplayer : public TlsExtensionFilter {
  public:
   KeyShareReplayer() {}
 
@@ -176,17 +177,17 @@ class KeyShareReplayer : public TlsExten
 // the second ClientHello is modified so that it omits the requested share.  The
 // server should reject this.
 TEST_P(TlsConnectTls13, RetryWithSameKeyShare) {
   EnsureTlsSetup();
   client_->SetPacketFilter(std::make_shared<KeyShareReplayer>());
   static const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1,
                                                     ssl_grp_ec_secp521r1};
   server_->ConfigNamedGroups(groups);
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
   EXPECT_EQ(SSL_ERROR_BAD_2ND_CLIENT_HELLO, server_->error_code());
   EXPECT_EQ(SSL_ERROR_ILLEGAL_PARAMETER_ALERT, client_->error_code());
 }
 
 // This tests that the second attempt at sending a ClientHello (after receiving
 // a HelloRetryRequest) is correctly retransmitted.
 TEST_F(TlsConnectDatagram13, DropClientSecondFlightWithHelloRetry) {
   static const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1,
@@ -253,16 +254,17 @@ TEST_F(TlsConnectTest, Select12AfterHell
   // Here we replace the TLS server with one that does TLS 1.2 only.
   // This will happily send the client a TLS 1.2 ServerHello.
   server_.reset(new TlsAgent(server_->name(), TlsAgent::SERVER, mode_));
   client_->SetPeer(server_);
   server_->SetPeer(client_);
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
                            SSL_LIBRARY_VERSION_TLS_1_2);
   server_->StartConnect();
+  ExpectAlert(client_, kTlsAlertIllegalParameter);
   Handshake();
   EXPECT_EQ(SSL_ERROR_ILLEGAL_PARAMETER_ALERT, server_->error_code());
   EXPECT_EQ(SSL_ERROR_RX_MALFORMED_SERVER_HELLO, client_->error_code());
 }
 
 class HelloRetryRequestAgentTest : public TlsAgentTestClient {
  protected:
   void SetUp() override {
@@ -304,32 +306,35 @@ class HelloRetryRequestAgentTest : publi
 // Send two HelloRetryRequest messages in response to the ClientHello.  The are
 // constructed to appear legitimate by asking for a new share in each, so that
 // the client has to count to work out that the server is being unreasonable.
 TEST_P(HelloRetryRequestAgentTest, SendSecondHelloRetryRequest) {
   DataBuffer hrr;
   MakeGroupHrr(ssl_grp_ec_secp384r1, &hrr, 0);
   ProcessMessage(hrr, TlsAgent::STATE_CONNECTING);
   MakeGroupHrr(ssl_grp_ec_secp521r1, &hrr, 1);
+  ExpectAlert(kTlsAlertUnexpectedMessage);
   ProcessMessage(hrr, TlsAgent::STATE_ERROR,
                  SSL_ERROR_RX_UNEXPECTED_HELLO_RETRY_REQUEST);
 }
 
 // Here the client receives a HelloRetryRequest with a group that they already
 // provided a share for.
 TEST_P(HelloRetryRequestAgentTest, HandleBogusHelloRetryRequest) {
   DataBuffer hrr;
   MakeGroupHrr(ssl_grp_ec_curve25519, &hrr);
+  ExpectAlert(kTlsAlertIllegalParameter);
   ProcessMessage(hrr, TlsAgent::STATE_ERROR,
                  SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
 }
 
 TEST_P(HelloRetryRequestAgentTest, HandleNoopHelloRetryRequest) {
   DataBuffer hrr;
   MakeCannedHrr(nullptr, 0U, &hrr);
+  ExpectAlert(kTlsAlertDecodeError);
   ProcessMessage(hrr, TlsAgent::STATE_ERROR,
                  SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
 }
 
 TEST_P(HelloRetryRequestAgentTest, HandleHelloRetryRequestCookie) {
   const uint8_t canned_cookie_hrr[] = {
       static_cast<uint8_t>(ssl_tls13_cookie_xtn >> 8),
       static_cast<uint8_t>(ssl_tls13_cookie_xtn),
--- a/security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc
@@ -34,30 +34,116 @@ TEST_P(TlsConnectGeneric, Connect) {
 
 TEST_P(TlsConnectGeneric, ConnectEcdsa) {
   SetExpectedVersion(std::get<1>(GetParam()));
   Reset(TlsAgent::kServerEcdsa256);
   Connect();
   CheckKeys(ssl_kea_ecdh, ssl_auth_ecdsa);
 }
 
-TEST_P(TlsConnectGenericPre13, CipherSuiteMismatch) {
+TEST_P(TlsConnectGeneric, CipherSuiteMismatch) {
   EnsureTlsSetup();
   if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
     client_->EnableSingleCipher(TLS_AES_128_GCM_SHA256);
     server_->EnableSingleCipher(TLS_AES_256_GCM_SHA384);
   } else {
     client_->EnableSingleCipher(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA);
     server_->EnableSingleCipher(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA);
   }
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
   client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
   server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
 }
 
+class TlsAlertRecorder : public TlsRecordFilter {
+ public:
+  TlsAlertRecorder() : level_(255), description_(255) {}
+
+  PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
+                                    const DataBuffer& input,
+                                    DataBuffer* output) override {
+    if (level_ != 255) {  // Already captured.
+      return KEEP;
+    }
+    if (header.content_type() != kTlsAlertType) {
+      return KEEP;
+    }
+
+    std::cerr << "Alert: " << input << std::endl;
+
+    TlsParser parser(input);
+    EXPECT_TRUE(parser.Read(&level_));
+    EXPECT_TRUE(parser.Read(&description_));
+    return KEEP;
+  }
+
+  uint8_t level() const { return level_; }
+  uint8_t description() const { return description_; }
+
+ private:
+  uint8_t level_;
+  uint8_t description_;
+};
+
+class HelloTruncator : public TlsHandshakeFilter {
+  PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
+                                       const DataBuffer& input,
+                                       DataBuffer* output) override {
+    if (header.handshake_type() != kTlsHandshakeClientHello &&
+        header.handshake_type() != kTlsHandshakeServerHello) {
+      return KEEP;
+    }
+    output->Assign(input.data(), input.len() - 1);
+    return CHANGE;
+  }
+};
+
+// Verify that when NSS reports that an alert is sent, it is actually sent.
+TEST_P(TlsConnectGeneric, CaptureAlertServer) {
+  client_->SetPacketFilter(std::make_shared<HelloTruncator>());
+  auto alert_recorder = std::make_shared<TlsAlertRecorder>();
+  server_->SetPacketFilter(alert_recorder);
+
+  ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
+  EXPECT_EQ(kTlsAlertFatal, alert_recorder->level());
+  EXPECT_EQ(kTlsAlertIllegalParameter, alert_recorder->description());
+}
+
+TEST_P(TlsConnectGenericPre13, CaptureAlertClient) {
+  server_->SetPacketFilter(std::make_shared<HelloTruncator>());
+  auto alert_recorder = std::make_shared<TlsAlertRecorder>();
+  client_->SetPacketFilter(alert_recorder);
+
+  ConnectExpectAlert(client_, kTlsAlertDecodeError);
+  EXPECT_EQ(kTlsAlertFatal, alert_recorder->level());
+  EXPECT_EQ(kTlsAlertDecodeError, alert_recorder->description());
+}
+
+// In TLS 1.3, the server can't read the client alert.
+TEST_P(TlsConnectTls13, CaptureAlertClient) {
+  server_->SetPacketFilter(std::make_shared<HelloTruncator>());
+  auto alert_recorder = std::make_shared<TlsAlertRecorder>();
+  client_->SetPacketFilter(alert_recorder);
+
+  server_->StartConnect();
+  client_->StartConnect();
+
+  client_->Handshake();
+  client_->ExpectSendAlert(kTlsAlertDecodeError);
+  server_->Handshake();
+  client_->Handshake();
+  if (mode_ == STREAM) {
+    // DTLS just drops the alert it can't decrypt.
+    server_->ExpectSendAlert(kTlsAlertBadRecordMac);
+  }
+  server_->Handshake();
+  EXPECT_EQ(kTlsAlertFatal, alert_recorder->level());
+  EXPECT_EQ(kTlsAlertDecodeError, alert_recorder->description());
+}
+
 TEST_P(TlsConnectGenericPre13, ConnectFalseStart) {
   client_->EnableFalseStart();
   Connect();
   SendReceive();
 }
 
 TEST_P(TlsConnectGeneric, ConnectAlpn) {
   EnableAlpn();
@@ -173,41 +259,47 @@ class TlsPreCCSHeaderInjector : public T
     *offset = nhdr.Write(output, *offset, hhdr_buf);
     *offset = record_header.Write(output, *offset, input);
     return CHANGE;
   }
 };
 
 TEST_P(TlsConnectStreamPre13, ClientFinishedHeaderBeforeCCS) {
   client_->SetPacketFilter(std::make_shared<TlsPreCCSHeaderInjector>());
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertUnexpectedMessage);
   client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
   server_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_CHANGE_CIPHER);
 }
 
 TEST_P(TlsConnectStreamPre13, ServerFinishedHeaderBeforeCCS) {
   server_->SetPacketFilter(std::make_shared<TlsPreCCSHeaderInjector>());
   client_->StartConnect();
   server_->StartConnect();
+  ExpectAlert(client_, kTlsAlertUnexpectedMessage);
   Handshake();
   EXPECT_EQ(TlsAgent::STATE_ERROR, client_->state());
   client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_CHANGE_CIPHER);
   EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state());
+  server_->Handshake();  // Make sure alert is consumed.
 }
 
 TEST_P(TlsConnectTls13, UnknownAlert) {
   Connect();
+  server_->ExpectSendAlert(0xff, kTlsAlertWarning);
+  client_->ExpectReceiveAlert(0xff, kTlsAlertWarning);
   SSLInt_SendAlert(server_->ssl_fd(), kTlsAlertWarning,
                    0xff);  // Unknown value.
   client_->ExpectReadWriteError();
   client_->WaitForErrorCode(SSL_ERROR_RX_UNKNOWN_ALERT, 2000);
 }
 
 TEST_P(TlsConnectTls13, AlertWrongLevel) {
   Connect();
+  server_->ExpectSendAlert(kTlsAlertUnexpectedMessage, kTlsAlertWarning);
+  client_->ExpectReceiveAlert(kTlsAlertUnexpectedMessage, kTlsAlertWarning);
   SSLInt_SendAlert(server_->ssl_fd(), kTlsAlertWarning,
                    kTlsAlertUnexpectedMessage);
   client_->ExpectReadWriteError();
   client_->WaitForErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT, 2000);
 }
 
 TEST_F(TlsConnectStreamTls13, Tls13FailedWriteSecondFlight) {
   EnsureTlsSetup();
--- a/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc
@@ -476,16 +476,22 @@ TEST_P(TlsConnectStream, TestResumptionO
   SendReceive();
   CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign);
 
   Reset();
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   server_->SetPacketFilter(std::make_shared<SelectedCipherSuiteReplacer>(
       ChooseAnotherCipher(version_)));
 
+  if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
+    client_->ExpectSendAlert(kTlsAlertIllegalParameter);
+    server_->ExpectSendAlert(kTlsAlertBadRecordMac);
+  } else {
+    ExpectAlert(client_, kTlsAlertHandshakeFailure);
+  }
   ConnectExpectFail();
   client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
   if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
     // The reason this test is stream only: the server is unable to decrypt
     // the alert that the client sends, see bug 1304603.
     server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
   } else {
     server_->CheckErrorCode(SSL_ERROR_HANDSHAKE_FAILURE_ALERT);
@@ -548,17 +554,17 @@ TEST_P(TlsConnectGenericPre13, TestResum
   Reset();
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   // Enable the lower version on the client.
   client_->SetVersionRange(version_ - 1, version_);
   server_->EnableSingleCipher(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA);
   server_->SetPacketFilter(
       std::make_shared<SelectedVersionReplacer>(override_version));
 
-  ConnectExpectFail();
+  ConnectExpectAlert(client_, kTlsAlertHandshakeFailure);
   client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
   server_->CheckErrorCode(SSL_ERROR_HANDSHAKE_FAILURE_ALERT);
 }
 
 // Test that two TLS resumptions work and produce the same ticket.
 // This will change after bug 1257047 is fixed.
 TEST_F(TlsConnectTest, TestTls13ResumptionTwice) {
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
@@ -686,14 +692,16 @@ TEST_F(TlsConnectTest, TestTls13Resumpti
   // We will eventually fail the (sid.version == SH.version) check.
   std::vector<std::shared_ptr<PacketFilter>> filters;
   filters.push_back(std::make_shared<SelectedCipherSuiteReplacer>(
       TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256));
   filters.push_back(
       std::make_shared<SelectedVersionReplacer>(SSL_LIBRARY_VERSION_TLS_1_2));
   server_->SetPacketFilter(std::make_shared<ChainedPacketFilter>(filters));
 
+  client_->ExpectSendAlert(kTlsAlertDecodeError);
+  server_->ExpectSendAlert(kTlsAlertBadRecordMac);  // Server can't read
   ConnectExpectFail();
   client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
   server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
 }
 
 }  // namespace nss_test
--- a/security/nss/gtests/ssl_gtest/ssl_skip_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_skip_unittest.cc
@@ -82,56 +82,57 @@ class TlsSkipTest
     : public TlsConnectTestBase,
       public ::testing::WithParamInterface<std::tuple<std::string, uint16_t>> {
  protected:
   TlsSkipTest()
       : TlsConnectTestBase(std::get<0>(GetParam()), std::get<1>(GetParam())) {}
 
   void ServerSkipTest(std::shared_ptr<PacketFilter> filter,
                       uint8_t alert = kTlsAlertUnexpectedMessage) {
-    auto alert_recorder = std::make_shared<TlsAlertRecorder>();
-    client_->SetPacketFilter(alert_recorder);
     server_->SetPacketFilter(filter);
-    ConnectExpectFail();
-    EXPECT_EQ(kTlsAlertFatal, alert_recorder->level());
-    EXPECT_EQ(alert, alert_recorder->description());
+    ConnectExpectAlert(client_, alert);
   }
 };
 
 class Tls13SkipTest : public TlsConnectTestBase,
                       public ::testing::WithParamInterface<std::string> {
  protected:
   Tls13SkipTest()
       : TlsConnectTestBase(GetParam(), SSL_LIBRARY_VERSION_TLS_1_3) {}
 
   void ServerSkipTest(std::shared_ptr<TlsRecordFilter> filter, int32_t error) {
     EnsureTlsSetup();
     server_->SetTlsRecordFilter(filter);
     filter->EnableDecryption();
+    client_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
     if (mode_ == STREAM) {
+      server_->ExpectSendAlert(kTlsAlertBadRecordMac);
       ConnectExpectFail();
     } else {
       ConnectExpectFailOneSide(TlsAgent::CLIENT);
     }
     client_->CheckErrorCode(error);
     if (mode_ == STREAM) {
       server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
     } else {
       ASSERT_EQ(TlsAgent::STATE_CONNECTING, server_->state());
     }
   }
 
   void ClientSkipTest(std::shared_ptr<TlsRecordFilter> filter, int32_t error) {
     EnsureTlsSetup();
     client_->SetTlsRecordFilter(filter);
     filter->EnableDecryption();
+    server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
     ConnectExpectFailOneSide(TlsAgent::SERVER);
 
     server_->CheckErrorCode(error);
     ASSERT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
+
+    client_->Handshake();  // Make sure to consume the alert the server sends.
   }
 };
 
 TEST_P(TlsSkipTest, SkipCertificateRsa) {
   EnableOnlyStaticRsaCiphers();
   ServerSkipTest(
       std::make_shared<TlsHandshakeSkipFilter>(kTlsHandshakeCertificate));
   client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_HELLO_DONE);
@@ -206,24 +207,26 @@ TEST_P(Tls13SkipTest, SkipServerCertific
   ServerSkipTest(
       std::make_shared<TlsHandshakeSkipFilter>(kTlsHandshakeCertificateVerify),
       SSL_ERROR_RX_UNEXPECTED_FINISHED);
 }
 
 TEST_P(Tls13SkipTest, SkipClientCertificate) {
   client_->SetupClientAuth();
   server_->RequestClientAuth(true);
+  client_->ExpectReceiveAlert(kTlsAlertUnexpectedMessage);
   ClientSkipTest(
       std::make_shared<TlsHandshakeSkipFilter>(kTlsHandshakeCertificate),
       SSL_ERROR_RX_UNEXPECTED_CERT_VERIFY);
 }
 
 TEST_P(Tls13SkipTest, SkipClientCertificateVerify) {
   client_->SetupClientAuth();
   server_->RequestClientAuth(true);
+  client_->ExpectReceiveAlert(kTlsAlertUnexpectedMessage);
   ClientSkipTest(
       std::make_shared<TlsHandshakeSkipFilter>(kTlsHandshakeCertificateVerify),
       SSL_ERROR_RX_UNEXPECTED_FINISHED);
 }
 
 INSTANTIATE_TEST_CASE_P(SkipTls10, TlsSkipTest,
                         ::testing::Combine(TlsConnectTestBase::kTlsModesStream,
                                            TlsConnectTestBase::kTlsV10));
--- a/security/nss/gtests/ssl_gtest/ssl_staticrsa_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_staticrsa_unittest.cc
@@ -47,34 +47,26 @@ TEST_P(TlsConnectGenericPre13, ConnectSt
 // Test that a totally bogus EPMS is handled correctly.
 // This test is stream so we can catch the bad_record_mac alert.
 TEST_P(TlsConnectStreamPre13, ConnectStaticRSABogusCKE) {
   EnableOnlyStaticRsaCiphers();
   auto i1 = std::make_shared<TlsInspectorReplaceHandshakeMessage>(
       kTlsHandshakeClientKeyExchange,
       DataBuffer(kBogusClientKeyExchange, sizeof(kBogusClientKeyExchange)));
   client_->SetPacketFilter(i1);
-  auto alert_recorder = std::make_shared<TlsAlertRecorder>();
-  server_->SetPacketFilter(alert_recorder);
-  ConnectExpectFail();
-  EXPECT_EQ(kTlsAlertFatal, alert_recorder->level());
-  EXPECT_EQ(kTlsAlertBadRecordMac, alert_recorder->description());
+  ConnectExpectAlert(server_, kTlsAlertBadRecordMac);
 }
 
 // Test that a PMS with a bogus version number is handled correctly.
 // This test is stream so we can catch the bad_record_mac alert.
 TEST_P(TlsConnectStreamPre13, ConnectStaticRSABogusPMSVersionDetect) {
   EnableOnlyStaticRsaCiphers();
   client_->SetPacketFilter(
       std::make_shared<TlsInspectorClientHelloVersionChanger>(server_));
-  auto alert_recorder = std::make_shared<TlsAlertRecorder>();
-  server_->SetPacketFilter(alert_recorder);
-  ConnectExpectFail();
-  EXPECT_EQ(kTlsAlertFatal, alert_recorder->level());
-  EXPECT_EQ(kTlsAlertBadRecordMac, alert_recorder->description());
+  ConnectExpectAlert(server_, kTlsAlertBadRecordMac);
 }
 
 // Test that a PMS with a bogus version number is ignored when
 // rollback detection is disabled. This is a positive control for
 // ConnectStaticRSABogusPMSVersionDetect.
 TEST_P(TlsConnectGenericPre13, ConnectStaticRSABogusPMSVersionIgnore) {
   EnableOnlyStaticRsaCiphers();
   client_->SetPacketFilter(
@@ -86,35 +78,27 @@ TEST_P(TlsConnectGenericPre13, ConnectSt
 // This test is stream so we can catch the bad_record_mac alert.
 TEST_P(TlsConnectStreamPre13, ConnectExtendedMasterSecretStaticRSABogusCKE) {
   EnableOnlyStaticRsaCiphers();
   EnableExtendedMasterSecret();
   auto inspect = std::make_shared<TlsInspectorReplaceHandshakeMessage>(
       kTlsHandshakeClientKeyExchange,
       DataBuffer(kBogusClientKeyExchange, sizeof(kBogusClientKeyExchange)));
   client_->SetPacketFilter(inspect);
-  auto alert_recorder = std::make_shared<TlsAlertRecorder>();
-  server_->SetPacketFilter(alert_recorder);
-  ConnectExpectFail();
-  EXPECT_EQ(kTlsAlertFatal, alert_recorder->level());
-  EXPECT_EQ(kTlsAlertBadRecordMac, alert_recorder->description());
+  ConnectExpectAlert(server_, kTlsAlertBadRecordMac);
 }
 
 // This test is stream so we can catch the bad_record_mac alert.
 TEST_P(TlsConnectStreamPre13,
        ConnectExtendedMasterSecretStaticRSABogusPMSVersionDetect) {
   EnableOnlyStaticRsaCiphers();
   EnableExtendedMasterSecret();
   client_->SetPacketFilter(
       std::make_shared<TlsInspectorClientHelloVersionChanger>(server_));
-  auto alert_recorder = std::make_shared<TlsAlertRecorder>();
-  server_->SetPacketFilter(alert_recorder);
-  ConnectExpectFail();
-  EXPECT_EQ(kTlsAlertFatal, alert_recorder->level());
-  EXPECT_EQ(kTlsAlertBadRecordMac, alert_recorder->description());
+  ConnectExpectAlert(server_, kTlsAlertBadRecordMac);
 }
 
 TEST_P(TlsConnectStreamPre13,
        ConnectExtendedMasterSecretStaticRSABogusPMSVersionIgnore) {
   EnableOnlyStaticRsaCiphers();
   EnableExtendedMasterSecret();
   client_->SetPacketFilter(
       std::make_shared<TlsInspectorClientHelloVersionChanger>(server_));
--- a/security/nss/gtests/ssl_gtest/ssl_v2_client_hello_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_v2_client_hello_unittest.cc
@@ -215,30 +215,30 @@ TEST_P(SSLv2ClientHelloTest, ConnectAfte
   EnsureTlsSetup();
   client_->SendDirect(buffer);
 
   // Need padding so the connection doesn't just time out. With a v2
   // ClientHello parsed as a v3 record we will use the record version
   // as the record length.
   SetPadding(255);
 
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
   EXPECT_EQ(SSL_ERROR_BAD_CLIENT, server_->error_code());
 }
 
 // Test negotiating TLS 1.3.
 TEST_F(SSLv2ClientHelloTestF, Connect13) {
   EnsureTlsSetup();
   SetExpectedVersion(SSL_LIBRARY_VERSION_TLS_1_3);
   ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
 
   std::vector<uint16_t> cipher_suites = {TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256};
   SetAvailableCipherSuites(cipher_suites);
 
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
   EXPECT_EQ(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, server_->error_code());
 }
 
 // Test negotiating an EC suite.
 TEST_P(SSLv2ClientHelloTest, NegotiateECSuite) {
   SetAvailableCipherSuite(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA);
   Connect();
 }
@@ -255,49 +255,49 @@ TEST_P(SSLv2ClientHelloTest, SendSecurit
   SetAvailableCipherSuite(TLS_DHE_RSA_WITH_AES_128_CBC_SHA);
 
   // Send a security escape.
   SetSendEscape(true);
 
   // Set a big padding so that the server fails instead of timing out.
   SetPadding(255);
 
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
 }
 
 // Invalid SSLv2 client hello padding must fail the handshake.
 TEST_P(SSLv2ClientHelloTest, AddErroneousPadding) {
   SetAvailableCipherSuite(TLS_DHE_RSA_WITH_AES_128_CBC_SHA);
 
   // Append 5 bytes of padding but say it's only 4.
   SetPadding(5, 4);
 
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
   EXPECT_EQ(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, server_->error_code());
 }
 
 // Invalid SSLv2 client hello padding must fail the handshake.
 TEST_P(SSLv2ClientHelloTest, AddErroneousPadding2) {
   SetAvailableCipherSuite(TLS_DHE_RSA_WITH_AES_128_CBC_SHA);
 
   // Append 5 bytes of padding but say it's 6.
   SetPadding(5, 6);
 
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
   EXPECT_EQ(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, server_->error_code());
 }
 
 // Wrong amount of bytes for the ClientRandom must fail the handshake.
 TEST_P(SSLv2ClientHelloTest, SmallClientRandom) {
   SetAvailableCipherSuite(TLS_DHE_RSA_WITH_AES_128_CBC_SHA);
 
   // Send a ClientRandom that's too small.
   SetClientRandomLength(15);
 
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
   EXPECT_EQ(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, server_->error_code());
 }
 
 // Test sending the maximum accepted number of ClientRandom bytes.
 TEST_P(SSLv2ClientHelloTest, MaxClientRandom) {
   SetAvailableCipherSuite(TLS_DHE_RSA_WITH_AES_128_CBC_SHA);
   SetClientRandomLength(32);
   Connect();
@@ -305,26 +305,26 @@ TEST_P(SSLv2ClientHelloTest, MaxClientRa
 
 // Wrong amount of bytes for the ClientRandom must fail the handshake.
 TEST_P(SSLv2ClientHelloTest, BigClientRandom) {
   SetAvailableCipherSuite(TLS_DHE_RSA_WITH_AES_128_CBC_SHA);
 
   // Send a ClientRandom that's too big.
   SetClientRandomLength(33);
 
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
   EXPECT_EQ(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, server_->error_code());
 }
 
 // Connection must fail if we require safe renegotiation but the client doesn't
 // include TLS_EMPTY_RENEGOTIATION_INFO_SCSV in the list of cipher suites.
 TEST_P(SSLv2ClientHelloTest, RequireSafeRenegotiation) {
   RequireSafeRenegotiation();
   SetAvailableCipherSuite(TLS_DHE_RSA_WITH_AES_128_CBC_SHA);
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
   EXPECT_EQ(SSL_ERROR_UNSAFE_NEGOTIATION, server_->error_code());
 }
 
 // Connection must succeed when requiring safe renegotiation and the client
 // includes TLS_EMPTY_RENEGOTIATION_INFO_SCSV in the list of cipher suites.
 TEST_P(SSLv2ClientHelloTest, RequireSafeRenegotiationWithSCSV) {
   RequireSafeRenegotiation();
   std::vector<uint16_t> cipher_suites = {TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
@@ -356,17 +356,17 @@ TEST_F(SSLv2ClientHelloTestF, Inappropri
                            SSL_LIBRARY_VERSION_TLS_1_1);
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_2);
 
   std::vector<uint16_t> cipher_suites = {TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
                                          TLS_FALLBACK_SCSV};
   SetAvailableCipherSuites(cipher_suites);
 
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertInappropriateFallback);
   EXPECT_EQ(SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT, server_->error_code());
 }
 
 INSTANTIATE_TEST_CASE_P(VersionsStream10Pre13, SSLv2ClientHelloTest,
                         TlsConnectTestBase::kTlsV10);
 INSTANTIATE_TEST_CASE_P(VersionsStreamPre13, SSLv2ClientHelloTest,
                         TlsConnectTestBase::kTlsV11V12);
 
--- a/security/nss/gtests/ssl_gtest/ssl_version_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_version_unittest.cc
@@ -130,17 +130,17 @@ TEST_F(TlsConnectTest, TestFallbackFromT
 TEST_P(TlsConnectGeneric, TestFallbackSCSVVersionMatch) {
   client_->SetFallbackSCSVEnabled(true);
   Connect();
 }
 
 TEST_P(TlsConnectGenericPre13, TestFallbackSCSVVersionMismatch) {
   client_->SetFallbackSCSVEnabled(true);
   server_->SetVersionRange(version_, version_ + 1);
-  ConnectExpectFail();
+  ConnectExpectAlert(server_, kTlsAlertInappropriateFallback);
   client_->CheckErrorCode(SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT);
 }
 
 // The TLS v1.3 spec section C.4 states that 'Implementations MUST NOT send or
 // accept any records with a version less than { 3, 0 }'. Thus we will not
 // allow version ranges including both SSL v3 and TLS v1.3.
 TEST_F(TlsConnectTest, DisallowSSLv3HelloWithTLSv13Enabled) {
   SECStatus rv;
@@ -172,16 +172,23 @@ TEST_P(TlsConnectStream, ConnectTls10And
   // Now renegotiate, with the server being set to do
   // |version_|.
   client_->PrepareForRenegotiate();
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0, test_version);
   // Reset version and cipher suite so that the preinfo callback
   // doesn't fail.
   server_->ResetPreliminaryInfo();
   server_->StartRenegotiate();
+
+  if (test_version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+    ExpectAlert(server_, kTlsAlertUnexpectedMessage);
+  } else {
+    ExpectAlert(client_, kTlsAlertIllegalParameter);
+  }
+
   Handshake();
   if (test_version >= SSL_LIBRARY_VERSION_TLS_1_3) {
     // In TLS 1.3, the server detects this problem.
     client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
     server_->CheckErrorCode(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED);
   } else {
     client_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_VERSION);
     server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
@@ -205,16 +212,21 @@ TEST_P(TlsConnectStream, ConnectTls10And
   // Now renegotiate, with the server being set to do
   // |version_|.
   server_->PrepareForRenegotiate();
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0, test_version);
   // Reset version and cipher suite so that the preinfo callback
   // doesn't fail.
   server_->ResetPreliminaryInfo();
   client_->StartRenegotiate();
+  if (test_version >= SSL_LIBRARY_VERSION_TLS_1_3) {
+    ExpectAlert(server_, kTlsAlertUnexpectedMessage);
+  } else {
+    ExpectAlert(client_, kTlsAlertIllegalParameter);
+  }
   Handshake();
   if (test_version >= SSL_LIBRARY_VERSION_TLS_1_3) {
     // In TLS 1.3, the server detects this problem.
     client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
     server_->CheckErrorCode(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED);
   } else {
     client_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_VERSION);
     server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
@@ -236,17 +248,17 @@ TEST_F(TlsConnectTest, Tls13RejectsRehan
   Connect();
   SECStatus rv = SSL_ReHandshake(server_->ssl_fd(), PR_TRUE);
   EXPECT_EQ(SECFailure, rv);
   EXPECT_EQ(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED, PORT_GetError());
 }
 
 TEST_P(TlsConnectGeneric, AlertBeforeServerHello) {
   EnsureTlsSetup();
-  client_->SetExpectedAlertReceivedCount(1);
+  client_->ExpectReceiveAlert(kTlsAlertUnrecognizedName, kTlsAlertWarning);
   client_->StartConnect();
   server_->StartConnect();
   client_->Handshake();  // Send ClientHello.
   static const uint8_t kWarningAlert[] = {kTlsAlertWarning,
                                           kTlsAlertUnrecognizedName};
   DataBuffer alert;
   TlsAgentTestBase::MakeRecord(mode_, kTlsAlertType,
                                SSL_LIBRARY_VERSION_TLS_1_0, kWarningAlert,
@@ -263,17 +275,17 @@ class Tls13NoSupportedVersions : public 
                              SSL_LIBRARY_VERSION_TLS_1_2);
     server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2, max_server_version);
     client_->SetPacketFilter(
         std::make_shared<TlsInspectorClientHelloVersionSetter>(
             overwritten_client_version));
     auto capture = std::make_shared<TlsInspectorRecordHandshakeMessage>(
         kTlsHandshakeServerHello);
     server_->SetPacketFilter(capture);
-    ConnectExpectFail();
+    ConnectExpectAlert(server_, kTlsAlertDecryptError);
     client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT);
     server_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
     const DataBuffer& server_hello = capture->buffer();
     ASSERT_GT(server_hello.len(), 2U);
     uint32_t ver;
     ASSERT_TRUE(server_hello.Read(0, 2, &ver));
     ASSERT_EQ(static_cast<uint32_t>(SSL_LIBRARY_VERSION_TLS_1_2), ver);
   }
@@ -300,16 +312,18 @@ TEST_F(Tls13NoSupportedVersions,
 // causes a bad MAC error when we read EncryptedExtensions.
 TEST_F(TlsConnectStreamTls13, Tls14ClientHelloWithSupportedVersions) {
   client_->SetPacketFilter(
       std::make_shared<TlsInspectorClientHelloVersionSetter>(
           SSL_LIBRARY_VERSION_TLS_1_3 + 1));
   auto capture = std::make_shared<TlsInspectorRecordHandshakeMessage>(
       kTlsHandshakeServerHello);
   server_->SetPacketFilter(capture);
+  client_->ExpectSendAlert(kTlsAlertBadRecordMac);
+  server_->ExpectSendAlert(kTlsAlertBadRecordMac);
   ConnectExpectFail();
   client_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
   server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
   const DataBuffer& server_hello = capture->buffer();
   ASSERT_GT(server_hello.len(), 2U);
   uint32_t ver;
   ASSERT_TRUE(server_hello.Read(0, 2, &ver));
   // This way we don't need to change with new draft version.
--- a/security/nss/gtests/ssl_gtest/tls_agent.cc
+++ b/security/nss/gtests/ssl_gtest/tls_agent.cc
@@ -55,22 +55,20 @@ TlsAgent::TlsAgent(const std::string& na
       falsestart_enabled_(false),
       expected_version_(0),
       expected_cipher_suite_(0),
       expect_resumption_(false),
       expect_client_auth_(false),
       can_falsestart_hook_called_(false),
       sni_hook_called_(false),
       auth_certificate_hook_called_(false),
-      alert_received_count_(0),
-      expected_alert_received_count_(0),
-      last_alert_received_({0, 0}),
-      alert_sent_count_(0),
-      expected_alert_sent_count_(0),
-      last_alert_sent_({0, 0}),
+      expected_received_alert_(kTlsAlertCloseNotify),
+      expected_received_alert_level_(kTlsAlertWarning),
+      expected_sent_alert_(kTlsAlertCloseNotify),
+      expected_sent_alert_level_(kTlsAlertWarning),
       handshake_callback_called_(false),
       error_code_(0),
       send_ctr_(0),
       recv_ctr_(0),
       expect_readwrite_error_(false),
       handshake_callback_(),
       auth_certificate_callback_(),
       sni_callback_(),
@@ -85,16 +83,21 @@ TlsAgent::TlsAgent(const std::string& na
 TlsAgent::~TlsAgent() {
   if (timer_handle_) {
     timer_handle_->Cancel();
   }
 
   if (adapter_) {
     Poller::Instance()->Cancel(READABLE_EVENT, adapter_);
   }
+
+  EXPECT_EQ(expected_received_alert_, kTlsAlertCloseNotify);
+  EXPECT_EQ(expected_received_alert_level_, kTlsAlertWarning);
+  EXPECT_EQ(expected_sent_alert_, kTlsAlertCloseNotify);
+  EXPECT_EQ(expected_sent_alert_level_, kTlsAlertWarning);
 }
 
 void TlsAgent::SetState(State state) {
   if (state_ == state) return;
 
   LOG("Changing state from " << state_ << " to " << state);
   state_ = state;
 }
@@ -602,19 +605,63 @@ void TlsAgent::CheckSrtp() const {
 
 void TlsAgent::CheckErrorCode(int32_t expected) const {
   EXPECT_EQ(STATE_ERROR, state_);
   EXPECT_EQ(expected, error_code_)
       << "Got error code " << PORT_ErrorToName(error_code_) << " expecting "
       << PORT_ErrorToName(expected) << std::endl;
 }
 
-void TlsAgent::CheckAlerts() const {
-  EXPECT_EQ(expected_alert_received_count_, alert_received_count_);
-  EXPECT_EQ(expected_alert_sent_count_, alert_sent_count_);
+static uint8_t GetExpectedAlertLevel(uint8_t alert) {
+  switch (alert) {
+    case kTlsAlertCloseNotify:
+    case kTlsAlertEndOfEarlyData:
+      return kTlsAlertWarning;
+    default:
+      break;
+  }
+  return kTlsAlertFatal;
+}
+
+void TlsAgent::ExpectReceiveAlert(uint8_t alert, uint8_t level) {
+  expected_received_alert_ = alert;
+  if (level == 0) {
+    expected_received_alert_level_ = GetExpectedAlertLevel(alert);
+  } else {
+    expected_received_alert_level_ = level;
+  }
+}
+
+void TlsAgent::ExpectSendAlert(uint8_t alert, uint8_t level) {
+  expected_sent_alert_ = alert;
+  if (level == 0) {
+    expected_sent_alert_level_ = GetExpectedAlertLevel(alert);
+  } else {
+    expected_sent_alert_level_ = level;
+  }
+}
+
+void TlsAgent::CheckAlert(bool sent, const SSLAlert* alert) {
+  LOG(((alert->level == kTlsAlertWarning) ? "Warning" : "Fatal")
+      << " alert " << (sent ? "sent" : "received") << ": "
+      << static_cast<int>(alert->description));
+
+  auto& expected = sent ? expected_sent_alert_ : expected_received_alert_;
+  auto& expected_level =
+      sent ? expected_sent_alert_level_ : expected_received_alert_level_;
+  /* Silently pass close_notify in case the test has already ended. */
+  if (expected == kTlsAlertCloseNotify && expected_level == kTlsAlertWarning &&
+      alert->description == expected && alert->level == expected_level) {
+    return;
+  }
+
+  EXPECT_EQ(expected, alert->description);
+  EXPECT_EQ(expected_level, alert->level);
+  expected = kTlsAlertCloseNotify;
+  expected_level = kTlsAlertWarning;
 }
 
 void TlsAgent::WaitForErrorCode(int32_t expected, uint32_t delay) const {
   ASSERT_EQ(0, error_code_);
   WAIT_(error_code_ != 0, delay);
   EXPECT_EQ(expected, error_code_)
       << "Got error code " << PORT_ErrorToName(error_code_) << " expecting "
       << PORT_ErrorToName(expected) << std::endl;
@@ -941,16 +988,21 @@ void TlsAgentTestBase::EnsureInit() {
     Reset();
   }
   const std::vector<SSLNamedGroup> groups = {
       ssl_grp_ec_curve25519, ssl_grp_ec_secp256r1, ssl_grp_ec_secp384r1,
       ssl_grp_ffdhe_2048};
   agent_->ConfigNamedGroups(groups);
 }
 
+void TlsAgentTestBase::ExpectAlert(uint8_t alert) {
+  EnsureInit();
+  agent_->ExpectSendAlert(alert);
+}
+
 void TlsAgentTestBase::ProcessMessage(const DataBuffer& buffer,
                                       TlsAgent::State expected_state,
                                       int32_t error_code) {
   std::cerr << "Process message: " << buffer << std::endl;
   EnsureInit();
   agent_->adapter()->PacketReceived(buffer);
   agent_->Handshake();
 
--- a/security/nss/gtests/ssl_gtest/tls_agent.h
+++ b/security/nss/gtests/ssl_gtest/tls_agent.h
@@ -139,17 +139,16 @@ class TlsAgent : public PollTarget {
   void ExpectShortHeaders();
   void SetSignatureSchemes(const SSLSignatureScheme* schemes, size_t count);
   void EnableAlpn(const uint8_t* val, size_t len);
   void CheckAlpn(SSLNextProtoState expected_state,
                  const std::string& expected = "") const;
   void EnableSrtp();
   void CheckSrtp() const;
   void CheckErrorCode(int32_t expected) const;
-  void CheckAlerts() const;
   void WaitForErrorCode(int32_t expected, uint32_t delay) const;
   // Send data on the socket, encrypting it.
   void SendData(size_t bytes, size_t blocksize = 1024);
   void SendBuffer(const DataBuffer& buf);
   // Send data directly to the underlying socket, skipping the TLS layer.
   void SendDirect(const DataBuffer& buf);
   void ReadBytes();
   void ResetSentBytes();  // Hack to test drops.
@@ -241,43 +240,18 @@ class TlsAgent : public PollTarget {
       AuthCertificateCallbackFunction auth_certificate_callback) {
     auth_certificate_callback_ = auth_certificate_callback;
   }
 
   void SetSniCallback(SniCallbackFunction sni_callback) {
     sni_callback_ = sni_callback;
   }
 
-  size_t alert_received_count() const { return alert_received_count_; }
-
-  void SetExpectedAlertReceivedCount(size_t count) {
-    expected_alert_received_count_ = count;
-  }
-
-  bool GetLastAlertReceived(SSLAlert* alert) const {
-    if (!alert_received_count_) {
-      return false;
-    }
-    *alert = last_alert_received_;
-    return true;
-  }
-
-  size_t alert_sent_count() const { return alert_sent_count_; }
-
-  void SetExpectedAlertSentCount(size_t count) {
-    expected_alert_sent_count_ = count;
-  }
-
-  bool GetLastAlertSent(SSLAlert* alert) const {
-    if (!alert_sent_count_) {
-      return false;
-    }
-    *alert = last_alert_sent_;
-    return true;
-  }
+  void ExpectReceiveAlert(uint8_t alert, uint8_t level = 0);
+  void ExpectSendAlert(uint8_t alert, uint8_t level = 0);
 
  private:
   const static char* states[];
 
   void SetState(State state);
 
   // Dummy auth certificate hook.
   static SECStatus AuthCertificateHook(void* arg, PRFileDesc* fd,
@@ -350,38 +324,26 @@ class TlsAgent : public PollTarget {
     agent->CheckPreliminaryInfo();
     EXPECT_TRUE(agent->falsestart_enabled_);
     EXPECT_FALSE(agent->can_falsestart_hook_called_);
     agent->can_falsestart_hook_called_ = true;
     *canFalseStart = true;
     return SECSuccess;
   }
 
+  void CheckAlert(bool sent, const SSLAlert* alert);
+
   static void AlertReceivedCallback(const PRFileDesc* fd, void* arg,
                                     const SSLAlert* alert) {
-    TlsAgent* agent = reinterpret_cast<TlsAgent*>(arg);
-
-    std::cerr << agent->role_str()
-              << ": Alert received: level=" << static_cast<int>(alert->level)
-              << " desc=" << static_cast<int>(alert->description) << std::endl;
-
-    ++agent->alert_received_count_;
-    agent->last_alert_received_ = *alert;
+    reinterpret_cast<TlsAgent*>(arg)->CheckAlert(false, alert);
   }
 
   static void AlertSentCallback(const PRFileDesc* fd, void* arg,
                                 const SSLAlert* alert) {
-    TlsAgent* agent = reinterpret_cast<TlsAgent*>(arg);
-
-    std::cerr << agent->role_str()
-              << ": Alert sent: level=" << static_cast<int>(alert->level)
-              << " desc=" << static_cast<int>(alert->description) << std::endl;
-
-    ++agent->alert_sent_count_;
-    agent->last_alert_sent_ = *alert;
+    reinterpret_cast<TlsAgent*>(arg)->CheckAlert(true, alert);
   }
 
   static void HandshakeCallback(PRFileDesc* fd, void* arg) {
     TlsAgent* agent = reinterpret_cast<TlsAgent*>(arg);
     agent->handshake_callback_called_ = true;
     agent->Connected();
     if (agent->handshake_callback_) {
       agent->handshake_callback_(agent);
@@ -405,22 +367,20 @@ class TlsAgent : public PollTarget {
   bool falsestart_enabled_;
   uint16_t expected_version_;
   uint16_t expected_cipher_suite_;
   bool expect_resumption_;
   bool expect_client_auth_;
   bool can_falsestart_hook_called_;
   bool sni_hook_called_;
   bool auth_certificate_hook_called_;
-  size_t alert_received_count_;
-  size_t expected_alert_received_count_;
-  SSLAlert last_alert_received_;
-  size_t alert_sent_count_;
-  size_t expected_alert_sent_count_;
-  SSLAlert last_alert_sent_;
+  uint8_t expected_received_alert_;
+  uint8_t expected_received_alert_level_;
+  uint8_t expected_sent_alert_;
+  uint8_t expected_sent_alert_level_;
   bool handshake_callback_called_;
   SSLChannelInfo info_;
   SSLCipherSuiteInfo csinfo_;
   SSLVersionRange vrange_;
   PRErrorCode error_code_;
   size_t send_ctr_;
   size_t recv_ctr_;
   bool expect_readwrite_error_;
@@ -445,16 +405,18 @@ class TlsAgentTestBase : public ::testin
         mode_(mode),
         version_(version),
         sink_adapter_(new DummyPrSocket("sink", mode)) {}
   virtual ~TlsAgentTestBase() {}
 
   void SetUp();
   void TearDown();
 
+  void ExpectAlert(uint8_t alert);
+
   static void MakeRecord(Mode mode, uint8_t type, uint16_t version,
                          const uint8_t* buf, size_t len, DataBuffer* out,
                          uint64_t seq_num = 0);
   void MakeRecord(uint8_t type, uint16_t version, const uint8_t* buf,
                   size_t len, DataBuffer* out, uint64_t seq_num = 0) const;
   void MakeHandshakeMessage(uint8_t hs_type, const uint8_t* data, size_t hs_len,
                             DataBuffer* out, uint64_t seq_num = 0) const;
   void MakeHandshakeMessageFragment(uint8_t hs_type, const uint8_t* data,
--- a/security/nss/gtests/ssl_gtest/tls_connect.cc
+++ b/security/nss/gtests/ssl_gtest/tls_connect.cc
@@ -296,19 +296,16 @@ void TlsConnectTestBase::CheckConnected(
     session_ids_.push_back(sid_c1);
   }
 
   CheckExtendedMasterSecret();
   CheckEarlyDataAccepted();
   CheckResumption(expected_resumption_mode_);
   client_->CheckSecretsDestroyed();
   server_->CheckSecretsDestroyed();
-
-  client_->CheckAlerts();
-  server_->CheckAlerts();
 }
 
 void TlsConnectTestBase::CheckKeys(SSLKEAType kea_type, SSLNamedGroup kea_group,
                                    SSLAuthType auth_type,
                                    SSLSignatureScheme sig_scheme) const {
   client_->CheckKEA(kea_type, kea_group);
   server_->CheckKEA(kea_type, kea_group);
   client_->CheckAuthType(auth_type, sig_scheme);
@@ -370,16 +367,30 @@ void TlsConnectTestBase::CheckKeys() con
 void TlsConnectTestBase::ConnectExpectFail() {
   server_->StartConnect();
   client_->StartConnect();
   Handshake();
   ASSERT_EQ(TlsAgent::STATE_ERROR, client_->state());
   ASSERT_EQ(TlsAgent::STATE_ERROR, server_->state());
 }
 
+void TlsConnectTestBase::ExpectAlert(std::shared_ptr<TlsAgent>& sender,
+                                     uint8_t alert) {
+  EnsureTlsSetup();
+  auto receiver = (sender == client_) ? server_ : client_;
+  sender->ExpectSendAlert(alert);
+  receiver->ExpectReceiveAlert(alert);
+}
+
+void TlsConnectTestBase::ConnectExpectAlert(std::shared_ptr<TlsAgent>& sender,
+                                            uint8_t alert) {
+  ExpectAlert(sender, alert);
+  ConnectExpectFail();
+}
+
 void TlsConnectTestBase::ConnectExpectFailOneSide(TlsAgent::Role failing_side) {
   server_->StartConnect();
   client_->StartConnect();
   client_->SetServerKeyBits(server_->server_key_bits());
   client_->Handshake();
   server_->Handshake();
 
   auto failing_agent = server_;
@@ -556,16 +567,20 @@ void TlsConnectTestBase::SetupForResume(
 }
 
 void TlsConnectTestBase::ZeroRttSendReceive(
     bool expect_writable, bool expect_readable,
     std::function<bool()> post_clienthello_check) {
   const char* k0RttData = "ABCDEF";
   const PRInt32 k0RttDataLen = static_cast<PRInt32>(strlen(k0RttData));
 
+  if (expect_writable && expect_readable) {
+    ExpectAlert(client_, kTlsAlertEndOfEarlyData);
+  }
+
   client_->Handshake();  // Send ClientHello.
   if (post_clienthello_check) {
     if (!post_clienthello_check()) return;
   }
   PRInt32 rv =
       PR_Write(client_->ssl_fd(), k0RttData, k0RttDataLen);  // 0-RTT write.
   if (expect_writable) {
     EXPECT_EQ(k0RttDataLen, rv);
--- a/security/nss/gtests/ssl_gtest/tls_connect.h
+++ b/security/nss/gtests/ssl_gtest/tls_connect.h
@@ -63,16 +63,18 @@ class TlsConnectTestBase : public ::test
   // Run the handshake.
   void Handshake();
   // Connect and check that it works.
   void Connect();
   // Check that the connection was successfully established.
   void CheckConnected();
   // Connect and expect it to fail.
   void ConnectExpectFail();
+  void ExpectAlert(std::shared_ptr<TlsAgent>& sender, uint8_t alert);
+  void ConnectExpectAlert(std::shared_ptr<TlsAgent>& sender, uint8_t alert);
   void ConnectExpectFailOneSide(TlsAgent::Role failingSide);
   void ConnectWithCipherSuite(uint16_t cipher_suite);
   // Check that the keys used in the handshake match expectations.
   void CheckKeys(SSLKEAType kea_type, SSLNamedGroup kea_group,
                  SSLAuthType auth_type, SSLSignatureScheme sig_scheme) const;
   // This version guesses some of the values.
   void CheckKeys(SSLKEAType kea_type, SSLAuthType auth_type) const;
   // This version assumes defaults.
--- a/security/nss/gtests/ssl_gtest/tls_filter.cc
+++ b/security/nss/gtests/ssl_gtest/tls_filter.cc
@@ -364,41 +364,16 @@ PacketFilter::Action TlsInspectorReplace
 
 PacketFilter::Action TlsConversationRecorder::FilterRecord(
     const TlsRecordHeader& header, const DataBuffer& input,
     DataBuffer* output) {
   buffer_.Append(input);
   return KEEP;
 }
 
-PacketFilter::Action TlsAlertRecorder::FilterRecord(
-    const TlsRecordHeader& header, const DataBuffer& input,
-    DataBuffer* output) {
-  if (level_ == kTlsAlertFatal) {  // already fatal
-    return KEEP;
-  }
-  if (header.content_type() != kTlsAlertType) {
-    return KEEP;
-  }
-
-  std::cerr << "Alert: " << input << std::endl;
-
-  TlsParser parser(input);
-  uint8_t lvl;
-  if (!parser.Read(&lvl)) {
-    return KEEP;
-  }
-  if (lvl == kTlsAlertWarning) {  // not strong enough
-    return KEEP;
-  }
-  level_ = lvl;
-  (void)parser.Read(&description_);
-  return KEEP;
-}
-
 PacketFilter::Action ChainedPacketFilter::Filter(const DataBuffer& input,
                                                  DataBuffer* output) {
   DataBuffer in(input);
   bool changed = false;
   for (auto it = filters_.begin(); it != filters_.end(); ++it) {
     PacketFilter::Action action = (*it)->Filter(in, output);
     if (action == DROP) {
       return DROP;
--- a/security/nss/gtests/ssl_gtest/tls_filter.h
+++ b/security/nss/gtests/ssl_gtest/tls_filter.h
@@ -227,34 +227,16 @@ class TlsConversationRecorder : public T
   virtual PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
                                             const DataBuffer& input,
                                             DataBuffer* output);
 
  private:
   DataBuffer& buffer_;
 };
 
-// Records an alert.  If an alert has already been recorded, it won't save the
-// new alert unless the old alert is a warning and the new one is fatal.
-class TlsAlertRecorder : public TlsRecordFilter {
- public:
-  TlsAlertRecorder() : level_(255), description_(255) {}
-
-  virtual PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
-                                            const DataBuffer& input,
-                                            DataBuffer* output);
-
-  uint8_t level() const { return level_; }
-  uint8_t description() const { return description_; }
-
- private:
-  uint8_t level_;
-  uint8_t description_;
-};
-
 // Runs multiple packet filters in series.
 class ChainedPacketFilter : public PacketFilter {
  public:
   ChainedPacketFilter() {}
   ChainedPacketFilter(const std::vector<std::shared_ptr<PacketFilter>> filters)
       : filters_(filters.begin(), filters.end()) {}
   virtual ~ChainedPacketFilter() {}
 
--- a/security/nss/gtests/ssl_gtest/tls_parser.h
+++ b/security/nss/gtests/ssl_gtest/tls_parser.h
@@ -34,22 +34,27 @@ const uint8_t kTlsHandshakeServerKeyExch
 const uint8_t kTlsHandshakeCertificateRequest = 13;
 const uint8_t kTlsHandshakeCertificateVerify = 15;
 const uint8_t kTlsHandshakeClientKeyExchange = 16;
 const uint8_t kTlsHandshakeFinished = 20;
 
 const uint8_t kTlsAlertWarning = 1;
 const uint8_t kTlsAlertFatal = 2;
 
+const uint8_t kTlsAlertCloseNotify = 0;
+const uint8_t kTlsAlertEndOfEarlyData = 1;
 const uint8_t kTlsAlertUnexpectedMessage = 10;
 const uint8_t kTlsAlertBadRecordMac = 20;
+const uint8_t kTlsAlertRecordOverflow = 22;
 const uint8_t kTlsAlertHandshakeFailure = 40;
 const uint8_t kTlsAlertIllegalParameter = 47;
 const uint8_t kTlsAlertDecodeError = 50;
 const uint8_t kTlsAlertDecryptError = 51;
+const uint8_t kTlsAlertProtocolVersion = 70;
+const uint8_t kTlsAlertInappropriateFallback = 86;
 const uint8_t kTlsAlertMissingExtension = 109;
 const uint8_t kTlsAlertUnsupportedExtension = 110;
 const uint8_t kTlsAlertUnrecognizedName = 112;
 const uint8_t kTlsAlertNoApplicationProtocol = 120;
 
 const uint8_t kTlsFakeChangeCipherSpec[] = {
     kTlsChangeCipherSpecType,  // Type
     0xfe,
--- a/security/nss/gtests/util_gtest/manifest.mn
+++ b/security/nss/gtests/util_gtest/manifest.mn
@@ -3,16 +3,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 CORE_DEPTH = ../..
 DEPTH      = ../..
 MODULE = nss
 
 CPPSRCS = \
 	util_utf8_unittest.cc \
+	util_b64_unittest.cc \
 	$(NULL)
 
 INCLUDES += \
 	-I$(CORE_DEPTH)/gtests/google_test/gtest/include \
 	-I$(CORE_DEPTH)/gtests/common \
 	-I$(CORE_DEPTH)/cpputil \
 	$(NULL)
 
new file mode 100644
--- /dev/null
+++ b/security/nss/gtests/util_gtest/util_b64_unittest.cc
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <climits>
+#include <memory>
+#include "nssb64.h"
+
+#include "gtest/gtest.h"
+#include "scoped_ptrs.h"
+
+namespace nss_test {
+
+class B64EncodeDecodeTest : public ::testing::Test {
+ public:
+  void TestDecodeStr(const std::string &str) {
+    ScopedSECItem tmp(
+        NSSBase64_DecodeBuffer(nullptr, nullptr, str.c_str(), str.size()));
+    ASSERT_TRUE(tmp);
+    char *out = NSSBase64_EncodeItem(nullptr, nullptr, 0, tmp.get());
+    ASSERT_TRUE(out);
+    ASSERT_EQ(std::string(out), str);
+    PORT_Free(out);
+  }
+  bool TestEncodeItem(SECItem *item) {
+    bool rv = true;
+    char *out = NSSBase64_EncodeItem(nullptr, nullptr, 0, item);
+    rv = !!out;
+    if (out) {
+      ScopedSECItem tmp(
+          NSSBase64_DecodeBuffer(nullptr, nullptr, out, strlen(out)));
+      EXPECT_TRUE(tmp);
+      EXPECT_EQ(SECEqual, SECITEM_CompareItem(item, tmp.get()));
+      PORT_Free(out);
+    }
+    return rv;
+  }
+  bool TestFakeDecode(size_t str_len) {
+    std::string str(str_len, 'A');
+    ScopedSECItem tmp(
+        NSSBase64_DecodeBuffer(nullptr, nullptr, str.c_str(), str.size()));
+    return !!tmp;
+  }
+  bool TestFakeEncode(size_t len) {
+    std::vector<uint8_t> data(len, 0x30);
+    SECItem tmp = {siBuffer, data.data(),
+                   static_cast<unsigned int>(data.size())};
+    return TestEncodeItem(&tmp);
+  }
+
+ protected:
+};
+
+TEST_F(B64EncodeDecodeTest, DecEncTest) { TestDecodeStr("VGhpcyBpcyBOU1Mh"); }
+
+TEST_F(B64EncodeDecodeTest, EncDecTest) {
+  uint8_t data[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09};
+  SECItem tmp = {siBuffer, data, sizeof(data)};
+  TestEncodeItem(&tmp);
+}
+
+TEST_F(B64EncodeDecodeTest, FakeDecTest) { EXPECT_TRUE(TestFakeDecode(100)); }
+
+TEST_F(B64EncodeDecodeTest, FakeEncDecTest) {
+  EXPECT_TRUE(TestFakeEncode(100));
+}
+
+// These takes a while ...
+TEST_F(B64EncodeDecodeTest, LongFakeDecTest1) {
+  EXPECT_TRUE(TestFakeDecode(0x66666666));
+}
+TEST_F(B64EncodeDecodeTest, LongFakeEncDecTest1) { TestFakeEncode(0x3fffffff); }
+TEST_F(B64EncodeDecodeTest, LongFakeEncDecTest2) {
+  EXPECT_FALSE(TestFakeEncode(0x40000000));
+}
+
+}  // namespace nss_test
--- a/security/nss/gtests/util_gtest/util_gtest.gyp
+++ b/security/nss/gtests/util_gtest/util_gtest.gyp
@@ -7,17 +7,18 @@
     '../common/gtest.gypi',
   ],
   'targets': [
     {
       'target_name': 'util_gtest',
       'type': 'executable',
       'sources': [
         'util_utf8_unittest.cc',
-        '<(DEPTH)/gtests/common/gtests.cc'
+        'util_b64_unittest.cc',
+        '<(DEPTH)/gtests/common/gtests.cc',
       ],
       'dependencies': [
         '<(DEPTH)/exports.gyp:nss_exports',
         '<(DEPTH)/gtests/google_test/google_test.gyp:gtest',
         '<(DEPTH)/lib/util/util.gyp:nssutil',
         '<(DEPTH)/lib/nss/nss.gyp:nss_static',
         '<(DEPTH)/lib/pk11wrap/pk11wrap.gyp:pk11wrap_static',
         '<(DEPTH)/lib/cryptohi/cryptohi.gyp:cryptohi',
--- a/security/nss/lib/certdb/cert.h
+++ b/security/nss/lib/certdb/cert.h
@@ -1400,34 +1400,21 @@ void CERT_SetStatusConfig(CERTCertDBHand
  * Acquire the cert reference count lock
  * There is currently one global lock for all certs, but I'm putting a cert
  * arg here so that it will be easy to make it per-cert in the future if
  * that turns out to be necessary.
  */
 void CERT_LockCertRefCount(CERTCertificate *cert);
 
 /*
- * Free the cert reference count lock
+ * Release the cert reference count lock
  */
 void CERT_UnlockCertRefCount(CERTCertificate *cert);
 
 /*
- * Acquire the cert trust lock
- * There is currently one global lock for all certs, but I'm putting a cert
- * arg here so that it will be easy to make it per-cert in the future if
- * that turns out to be necessary.
- */
-void CERT_LockCertTrust(const CERTCertificate *cert);
-
-/*
- * Free the cert trust lock
- */
-void CERT_UnlockCertTrust(const CERTCertificate *cert);
-
-/*
  * Digest the cert's subject public key using the specified algorithm.
  * NOTE: this digests the value of the BIT STRING subjectPublicKey (excluding
  * the tag, length, and number of unused bits) rather than the whole
  * subjectPublicKeyInfo field.
  *
  * The necessary storage for the digest data is allocated.  If "fill" is
  * non-null, the data is put there, otherwise a SECItem is allocated.
  * Allocation from "arena" if it is non-null, heap otherwise.  Any problem
@@ -1574,11 +1561,17 @@ extern CERTRevocationFlags *CERT_AllocCE
     PRUint32 number_chain_methods, PRUint32 number_chain_pref_methods);
 
 /*
  * Destroy the arrays inside flags,
  * and destroy the object pointed to by flags, too.
  */
 extern void CERT_DestroyCERTRevocationFlags(CERTRevocationFlags *flags);
 
+/*
+ * Get istemp and isperm fields from a cert in a thread safe way.
+ */
+extern SECStatus CERT_GetCertIsTemp(const CERTCertificate *cert, PRBool *istemp);
+extern SECStatus CERT_GetCertIsPerm(const CERTCertificate *cert, PRBool *isperm);
+
 SEC_END_PROTOS
 
 #endif /* _CERT_H_ */
--- a/security/nss/lib/certdb/certdb.c
+++ b/security/nss/lib/certdb/certdb.c
@@ -2860,17 +2860,28 @@ static PZLock *certTrustLock = NULL;
  * arg here so that it will be easy to make it per-cert in the future if
  * that turns out to be necessary.
  */
 void
 CERT_LockCertTrust(const CERTCertificate *cert)
 {
     PORT_Assert(certTrustLock != NULL);
     PZ_Lock(certTrustLock);
-    return;
+}
+
+static PZLock *certTempPermLock = NULL;
+
+/*
+ * Acquire the cert temp/perm lock
+ */
+void
+CERT_LockCertTempPerm(const CERTCertificate *cert)
+{
+    PORT_Assert(certTempPermLock != NULL);
+    PZ_Lock(certTempPermLock);
 }
 
 SECStatus
 cert_InitLocks(void)
 {
     if (certRefCountLock == NULL) {
         certRefCountLock = PZ_NewLock(nssILockRefLock);
         PORT_Assert(certRefCountLock != NULL);
@@ -2884,16 +2895,28 @@ cert_InitLocks(void)
         PORT_Assert(certTrustLock != NULL);
         if (!certTrustLock) {
             PZ_DestroyLock(certRefCountLock);
             certRefCountLock = NULL;
             return SECFailure;
         }
     }
 
+    if (certTempPermLock == NULL) {
+        certTempPermLock = PZ_NewLock(nssILockCertDB);
+        PORT_Assert(certTempPermLock != NULL);
+        if (!certTempPermLock) {
+            PZ_DestroyLock(certTrustLock);
+            PZ_DestroyLock(certRefCountLock);
+            certRefCountLock = NULL;
+            certTrustLock = NULL;
+            return SECFailure;
+        }
+    }
+
     return SECSuccess;
 }
 
 SECStatus
 cert_DestroyLocks(void)
 {
     SECStatus rv = SECSuccess;
 
@@ -2907,16 +2930,24 @@ cert_DestroyLocks(void)
 
     PORT_Assert(certTrustLock != NULL);
     if (certTrustLock) {
         PZ_DestroyLock(certTrustLock);
         certTrustLock = NULL;
     } else {
         rv = SECFailure;
     }
+
+    PORT_Assert(certTempPermLock != NULL);
+    if (certTempPermLock) {
+        PZ_DestroyLock(certTempPermLock);
+        certTempPermLock = NULL;
+    } else {
+        rv = SECFailure;
+    }
     return rv;
 }
 
 /*
  * Free the cert trust lock
  */
 void
 CERT_UnlockCertTrust(const CERTCertificate *cert)
@@ -2929,16 +2960,33 @@ CERT_UnlockCertTrust(const CERTCertifica
         PORT_Assert(prstat == PR_SUCCESS);
     }
 #else
     PZ_Unlock(certTrustLock);
 #endif
 }
 
 /*
+ * Free the temp/perm lock
+ */
+void
+CERT_UnlockCertTempPerm(const CERTCertificate *cert)
+{
+    PORT_Assert(certTempPermLock != NULL);
+#ifdef DEBUG
+    {
+        PRStatus prstat = PZ_Unlock(certTempPermLock);
+        PORT_Assert(prstat == PR_SUCCESS);
+    }
+#else
+    (void)PZ_Unlock(certTempPermLock);
+#endif
+}
+
+/*
  * Get the StatusConfig data for this handle
  */
 CERTStatusConfig *
 CERT_GetStatusConfig(CERTCertDBHandle *handle)
 {
     return handle->statusConfig;
 }
 
--- a/security/nss/lib/certdb/certi.h
+++ b/security/nss/lib/certdb/certi.h
@@ -373,9 +373,32 @@ PRUint32 cert_CountDNSPatterns(CERTGener
  * failedFlags will be set to the trust bit value that lead to the failure.
  * If the leaf is trusted, isTrusted is set to true and the function returns
  * SECSuccess. This function does not check if the cert is fit for a
  * particular usage.
  */
 SECStatus cert_CheckLeafTrust(CERTCertificate* cert, SECCertUsage usage,
                               unsigned int* failedFlags, PRBool* isTrusted);
 
+/*
+ * Acquire the cert temp/perm lock
+ */
+void CERT_LockCertTempPerm(const CERTCertificate* cert);
+
+/*
+ * Release the temp/perm lock
+ */
+void CERT_UnlockCertTempPerm(const CERTCertificate* cert);
+
+/*
+ * Acquire the cert trust lock
+ * There is currently one global lock for all certs, but I'm putting a cert
+ * arg here so that it will be easy to make it per-cert in the future if
+ * that turns out to be necessary.
+ */
+void CERT_LockCertTrust(const CERTCertificate* cert);
+
+/*
+ * Release the cert trust lock
+ */
+void CERT_UnlockCertTrust(const CERTCertificate* cert);
+
 #endif /* _CERTI_H_ */
--- a/security/nss/lib/certdb/stanpcertdb.c
+++ b/security/nss/lib/certdb/stanpcertdb.c
@@ -86,17 +86,17 @@ SEC_DeletePermCertificate(CERTCertificat
     return (nssrv == PR_SUCCESS) ? SECSuccess : SECFailure;
 }
 
 SECStatus
 CERT_GetCertTrust(const CERTCertificate *cert, CERTCertTrust *trust)
 {
     SECStatus rv;
     CERT_LockCertTrust(cert);
-    if (cert->trust == NULL) {
+    if (!cert || cert->trust == NULL) {
         rv = SECFailure;
     } else {
         *trust = *cert->trust;
         rv = SECSuccess;
     }
     CERT_UnlockCertTrust(cert);
     return (rv);
 }
@@ -299,18 +299,20 @@ SECStatus
     nssTrustDomain_AddCertsToCache(STAN_GetDefaultTrustDomain(), &c, 1);
     /* reset the CERTCertificate fields */
     cert->nssCertificate = NULL;
     cert = STAN_GetCERTCertificateOrRelease(c); /* should return same pointer */
     if (!cert) {
         CERT_MapStanError();
         return SECFailure;
     }
+    CERT_LockCertTempPerm(cert);
     cert->istemp = PR_FALSE;
     cert->isperm = PR_TRUE;
+    CERT_UnlockCertTempPerm(cert);
     if (!trust) {
         return SECSuccess;
     }
     ret = STAN_ChangeCertTrust(cert, trust);
     rv = SECSuccess;
     if (ret != PR_SUCCESS) {
         rv = SECFailure;
         CERT_MapStanError();
@@ -431,18 +433,20 @@ CERT_NewTempCertificate(CERTCertDBHandle
     c = tempCert;
     cc = STAN_GetCERTCertificateOrRelease(c);
     if (!cc) {
         /* STAN_GetCERTCertificateOrRelease destroys c on failure. */
         CERT_MapStanError();
         return NULL;
     }
 
+    CERT_LockCertTempPerm(cc);
     cc->istemp = PR_TRUE;
     cc->isperm = PR_FALSE;
+    CERT_UnlockCertTempPerm(cc);
     return cc;
 loser:
     /* Perhaps this should be nssCertificate_Destroy(c) */
     nssPKIObject_Destroy(&c->object);
     return NULL;
 }
 
 /* This symbol is exported for backward compatibility. */
@@ -906,16 +910,17 @@ loser:
  */
 
 SECStatus
 CERT_SaveSMimeProfile(CERTCertificate *cert, SECItem *emailProfile,
                       SECItem *profileTime)
 {
     const char *emailAddr;
     SECStatus rv;
+    PRBool isperm = PR_FALSE;
 
     if (!cert) {
         return SECFailure;
     }
 
     if (cert->slot && !PK11_IsInternal(cert->slot)) {
         /* this cert comes from an external source, we need to add it
         to the cert db before creating an S/MIME profile */
@@ -927,17 +932,21 @@ CERT_SaveSMimeProfile(CERTCertificate *c
                              PR_FALSE);
 
         PK11_FreeSlot(internalslot);
         if (rv != SECSuccess) {
             return SECFailure;
         }
     }
 
-    if (cert->slot && cert->isperm && CERT_IsUserCert(cert) &&
+    rv = CERT_GetCertIsPerm(cert, &isperm);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+    if (cert->slot && isperm && CERT_IsUserCert(cert) &&
         (!emailProfile || !emailProfile->len)) {
         /* Don't clobber emailProfile for user certs. */
         return SECSuccess;
     }
 
     for (emailAddr = CERT_GetFirstEmailAddress(cert); emailAddr != NULL;
          emailAddr = CERT_GetNextEmailAddress(cert, emailAddr)) {
         rv = certdb_SaveSingleProfile(cert, emailAddr, emailProfile,
@@ -981,16 +990,42 @@ CERT_FindSMimeProfile(CERTCertificate *c
     rvItem =
         PK11_FindSMimeProfile(&slot, cert->emailAddr, &cert->derSubject, NULL);
     if (slot) {
         PK11_FreeSlot(slot);
     }
     return rvItem;
 }
 
+SECStatus
+CERT_GetCertIsPerm(const CERTCertificate *cert, PRBool *isperm)
+{
+    if (cert == NULL) {
+        return SECFailure;
+    }
+
+    CERT_LockCertTempPerm(cert);
+    *isperm = cert->isperm;
+    CERT_UnlockCertTempPerm(cert);
+    return SECSuccess;
+}
+
+SECStatus
+CERT_GetCertIsTemp(const CERTCertificate *cert, PRBool *istemp)
+{
+    if (cert == NULL) {
+        return SECFailure;
+    }
+
+    CERT_LockCertTempPerm(cert);
+    *istemp = cert->istemp;
+    CERT_UnlockCertTempPerm(cert);
+    return SECSuccess;
+}
+
 /*
  * deprecated functions that are now just stubs.
  */
 /*
  * Close the database
  */
 void
 __CERT_ClosePermCertDB(CERTCertDBHandle *handle)
--- a/security/nss/lib/certhigh/certhigh.c
+++ b/security/nss/lib/certhigh/certhigh.c
@@ -6,16 +6,17 @@
 #include "secasn1.h"
 #include "seccomon.h"
 #include "pk11func.h"
 #include "certdb.h"
 #include "certt.h"
 #include "cert.h"
 #include "certxutl.h"
 
+#include "certi.h"
 #include "nsspki.h"
 #include "pki.h"
 #include "pkit.h"
 #include "pkitm.h"
 #include "pki3hack.h"
 
 PRBool
 CERT_MatchNickname(char *name1, char *name2)
@@ -867,16 +868,17 @@ cert_ImportCAChain(SECItem *certs, int n
     SECItem *derCert;
     CERTCertificate *cert = NULL;
     CERTCertificate *newcert = NULL;
     CERTCertDBHandle *handle;
     CERTCertTrust trust;
     PRBool isca;
     char *nickname;
     unsigned int certtype;
+    PRBool istemp = PR_FALSE;
 
     handle = CERT_GetDefaultCertDB();
 
     while (numcerts--) {
         derCert = certs;
         certs++;
 
         /* decode my certificate */
@@ -944,33 +946,34 @@ cert_ImportCAChain(SECItem *certs, int n
 
         cert = CERT_NewTempCertificate(handle, derCert, NULL,
                                        PR_FALSE, PR_FALSE);
         if (cert == NULL) {
             goto loser;
         }
 
         /* if the cert is temp, make it perm; otherwise we're done */
-        if (cert->istemp) {
+        rv = CERT_GetCertIsTemp(cert, &istemp);
+        if (rv != SECSuccess) {
+            goto loser;
+        }
+        if (istemp) {
             /* get a default nickname for it */
             nickname = CERT_MakeCANickname(cert);
 
             rv = CERT_AddTempCertToPerm(cert, nickname, &trust);
 
             /* free the nickname */
             if (nickname) {
                 PORT_Free(nickname);
             }
         } else {
             rv = SECSuccess;
         }
 
-        CERT_DestroyCertificate(cert);
-        cert = NULL;
-
         if (rv != SECSuccess) {
             goto loser;
         }
 
     endloop:
         if (newcert) {
             CERT_DestroyCertificate(newcert);
             newcert = NULL;
--- a/security/nss/lib/freebl/blapi.h
+++ b/security/nss/lib/freebl/blapi.h
@@ -1424,18 +1424,16 @@ extern SECStatus RNG_RNGInit(void);
 extern SECStatus RNG_RandomUpdate(const void *data, size_t bytes);
 
 /*
 ** Generate some random bytes, using the global random number generator
 ** object.
 */
 extern SECStatus RNG_GenerateGlobalRandomBytes(void *dest, size_t len);
 
-extern SECStatus RNG_ResetForFuzzing(void);
-
 /* Destroy the global RNG context.  After a call to RNG_RNGShutdown()
 ** a call to RNG_RNGInit() is required in order to use the generator again,
 ** along with seed data (see the comment above RNG_RNGInit()).
 */
 extern void RNG_RNGShutdown(void);
 
 extern void RNG_SystemInfoForRNG(void);
 
--- a/security/nss/lib/freebl/det_rng.c
+++ b/security/nss/lib/freebl/det_rng.c
@@ -4,41 +4,67 @@
 
 #include "blapi.h"
 #include "blapit.h"
 #include "chacha20.h"
 #include "nssilock.h"
 #include "seccomon.h"
 #include "secerr.h"
 
+#define GLOBAL_BYTES_SIZE 100
+static PRUint8 globalBytes[GLOBAL_BYTES_SIZE];
 static unsigned long globalNumCalls = 0;
+static PZLock *rng_lock = NULL;
 
 SECStatus
-prng_ResetForFuzzing(PZLock *rng_lock)
+RNG_RNGInit(void)
+{
+    rng_lock = PZ_NewLock(nssILockOther);
+    if (!rng_lock) {
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    /* --- LOCKED --- */
+    PZ_Lock(rng_lock);
+    memset(globalBytes, 0, GLOBAL_BYTES_SIZE);
+    PZ_Unlock(rng_lock);
+    /* --- UNLOCKED --- */
+
+    return SECSuccess;
+}
+
+/* Take min(size, GLOBAL_BYTES_SIZE) bytes from data and use as seed and reset
+ * the rng state. */
+SECStatus
+RNG_RandomUpdate(const void *data, size_t bytes)
 {
     /* Check for a valid RNG lock. */
     PORT_Assert(rng_lock != NULL);
     if (rng_lock == NULL) {
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return SECFailure;
     }
 
     /* --- LOCKED --- */
     PZ_Lock(rng_lock);
+    memset(globalBytes, 0, GLOBAL_BYTES_SIZE);
     globalNumCalls = 0;
+    if (data) {
+        memcpy(globalBytes, (PRUint8 *)data, PR_MIN(bytes, GLOBAL_BYTES_SIZE));
+    }
     PZ_Unlock(rng_lock);
     /* --- UNLOCKED --- */
 
     return SECSuccess;
 }
 
 SECStatus
-prng_GenerateDeterministicRandomBytes(PZLock *rng_lock, void *dest, size_t len)
+RNG_GenerateGlobalRandomBytes(void *dest, size_t len)
 {
-    static const uint8_t key[32];
+    static const uint8_t key[32] = { 0 };
     uint8_t nonce[12] = { 0 };
 
     /* Check for a valid RNG lock. */
     PORT_Assert(rng_lock != NULL);
     if (rng_lock == NULL) {
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return SECFailure;
     }
@@ -53,15 +79,65 @@ prng_GenerateDeterministicRandomBytes(PZ
         ChaCha20Poly1305_CreateContext(key, sizeof(key), 16);
     if (!cx) {
         PORT_SetError(SEC_ERROR_NO_MEMORY);
         PZ_Unlock(rng_lock);
         return SECFailure;
     }
 
     memset(dest, 0, len);
+    memcpy(dest, globalBytes, PR_MIN(len, GLOBAL_BYTES_SIZE));
     ChaCha20XOR(dest, dest, len, key, nonce, 0);
     ChaCha20Poly1305_DestroyContext(cx, PR_TRUE);
 
     PZ_Unlock(rng_lock);
     /* --- UNLOCKED --- */
+
     return SECSuccess;
 }
+
+void
+RNG_RNGShutdown(void)
+{
+    PZ_DestroyLock(rng_lock);
+    rng_lock = NULL;
+}
+
+/* Test functions are not implemented! */
+SECStatus
+PRNGTEST_Instantiate(const PRUint8 *entropy, unsigned int entropy_len,
+                     const PRUint8 *nonce, unsigned int nonce_len,
+                     const PRUint8 *personal_string, unsigned int ps_len)
+{
+    return SECFailure;
+}
+
+SECStatus
+PRNGTEST_Reseed(const PRUint8 *entropy, unsigned int entropy_len,
+                const PRUint8 *additional, unsigned int additional_len)
+{
+    return SECFailure;
+}
+
+SECStatus
+PRNGTEST_Generate(PRUint8 *bytes, unsigned int bytes_len,
+                  const PRUint8 *additional, unsigned int additional_len)
+{
+    return SECFailure;
+}
+
+SECStatus
+PRNGTEST_Uninstantiate()
+{
+    return SECFailure;
+}
+
+SECStatus
+PRNGTEST_RunHealthTests()
+{
+    return SECFailure;
+}
+
+SECStatus
+PRNGTEST_Instantiate_Kat()
+{
+    return SECFailure;
+}
--- a/security/nss/lib/freebl/drbg.c
+++ b/security/nss/lib/freebl/drbg.c
@@ -15,20 +15,16 @@
 #include "blapii.h"
 #include "nssilock.h"
 #include "secitem.h"
 #include "sha_fast.h"
 #include "sha256.h"
 #include "secrng.h" /* for RNG_SystemRNG() */
 #include "secmpi.h"
 
-#ifdef UNSAFE_FUZZER_MODE
-#include "det_rng.h"
-#endif
-
 /* PRNG_SEEDLEN defined in NIST SP 800-90 section 10.1
  * for SHA-1, SHA-224, and SHA-256 it's 440 bits.
  * for SHA-384 and SHA-512 it's 888 bits */
 #define PRNG_SEEDLEN (440 / PR_BITS_PER_BYTE)
 #define PRNG_MAX_ADDITIONAL_BYTES PR_INT64(0x100000000)
 /* 2^35 bits or 2^32 bytes */
 #define PRNG_MAX_REQUEST_SIZE 0x10000             /* 2^19 bits or 2^16 bytes */
 #define PRNG_ADDITONAL_DATA_CACHE_SIZE (8 * 1024) /* must be less than          \
@@ -396,37 +392,34 @@ prng_generateNewBytes(RNGContext *rng,
  * threads, creating a race condition.
  */
 static const PRCallOnceType pristineCallOnce;
 static PRCallOnceType coRNGInit;
 static PRStatus
 rng_init(void)
 {
     PRUint8 bytes[PRNG_SEEDLEN * 2]; /* entropy + nonce */
-#ifndef UNSAFE_RNG_NO_URANDOM_SEED
     unsigned int numBytes;
     SECStatus rv = SECSuccess;
-#endif
 
     if (globalrng == NULL) {
         /* bytes needs to have enough space to hold
      * a SHA256 hash value. Blow up at compile time if this isn't true */
         PR_STATIC_ASSERT(sizeof(bytes) >= SHA256_LENGTH);
         /* create a new global RNG context */
         globalrng = &theGlobalRng;
         PORT_Assert(NULL == globalrng->lock);
         /* create a lock for it */
         globalrng->lock = PZ_NewLock(nssILockOther);
         if (globalrng->lock == NULL) {
             globalrng = NULL;
             PORT_SetError(PR_OUT_OF_MEMORY_ERROR);
             return PR_FAILURE;
         }
 
-#ifndef UNSAFE_RNG_NO_URANDOM_SEED
         /* Try to get some seed data for the RNG */
         numBytes = (unsigned int)RNG_SystemRNG(bytes, sizeof bytes);
         PORT_Assert(numBytes == 0 || numBytes == sizeof bytes);
         if (numBytes != 0) {
             /* if this is our first call,  instantiate, otherwise reseed
              * prng_instantiate gets a new clean state, we want to mix
              * any previous entropy we may have collected */
             if (V(globalrng)[0] == 0) {
@@ -439,17 +432,16 @@ rng_init(void)
             PZ_DestroyLock(globalrng->lock);
             globalrng->lock = NULL;
             globalrng = NULL;
             return PR_FAILURE;
         }
         if (rv != SECSuccess) {
             return PR_FAILURE;
         }
-#endif
 
         /* the RNG is in a valid state */
         globalrng->isValid = PR_TRUE;
         globalrng->isKatTest = PR_FALSE;
 
         /* fetch one random value so that we can populate rng->oldV for our
          * continous random number test. */
         prng_generateNewBytes(globalrng, bytes, SHA256_LENGTH, NULL, 0);
@@ -657,31 +649,17 @@ prng_GenerateGlobalRandomBytes(RNGContex
 
 /*
 ** Generate some random bytes, using the global random number generator
 ** object.
 */
 SECStatus
 RNG_GenerateGlobalRandomBytes(void *dest, size_t len)
 {
-#ifdef UNSAFE_FUZZER_MODE
-    return prng_GenerateDeterministicRandomBytes(globalrng->lock, dest, len);
-#else
     return prng_GenerateGlobalRandomBytes(globalrng, dest, len);
-#endif
-}
-
-SECStatus
-RNG_ResetForFuzzing(void)
-{
-#ifdef UNSAFE_FUZZER_MODE
-    return prng_ResetForFuzzing(globalrng->lock);
-#else
-    return SECFailure;
-#endif
 }
 
 void
 RNG_RNGShutdown(void)
 {
     /* check for a valid global RNG context */
     PORT_Assert(globalrng != NULL);
     if (globalrng == NULL) {
--- a/security/nss/lib/freebl/ecl/ecl.c
+++ b/security/nss/lib/freebl/ecl/ecl.c
@@ -262,41 +262,28 @@ ec_GetNamedCurveParams(const ECCurveName
         return ecCurve_map[name];
     }
 }
 
 /* Construct ECGroup from named parameters. */
 ECGroup *
 ECGroup_fromName(const ECCurveName name)
 {
-    ECGroup *group = NULL;
     const ECCurveBytes *params = NULL;
-    mp_err res = MP_OKAY;
 
     /* This doesn't work with Curve25519 but it's not necessary to. */
     PORT_Assert(name != ECCurve25519);
 
     params = ec_GetNamedCurveParams(name);
     if (params == NULL) {
-        res = MP_UNDEF;
-        goto CLEANUP;
+        return NULL;
     }
 
     /* construct actual group */
-    group = ecgroup_fromName(name, params);
-    if (group == NULL) {
-        res = MP_UNDEF;
-    }
-
-CLEANUP:
-    if (group && res != MP_OKAY) {
-        ECGroup_free(group);
-        return NULL;
-    }
-    return group;
+    return ecgroup_fromName(name, params);
 }
 
 /* Validates an EC public key as described in Section 5.2.2 of X9.62. */
 mp_err
 ECPoint_validate(const ECGroup *group, const mp_int *px, const mp_int *py)
 {
     /* 1: Verify that publicValue is not the point at infinity */
     /* 2: Verify that the coordinates of publicValue are elements
--- a/security/nss/lib/freebl/freebl_base.gypi
+++ b/security/nss/lib/freebl/freebl_base.gypi
@@ -153,25 +153,21 @@
           # not x64
           'sources': [
             'chacha20.c',
             'poly1305.c',
           ],
         }],
       ],
     }],
-    [ 'fuzz_oss==1', {
-      'defines': [
-        'UNSAFE_RNG_NO_URANDOM_SEED',
-      ],
+    [ 'fuzz==1', {
+      'sources!': [ 'drbg.c' ],
+      'sources': [ 'det_rng.c' ],
     }],
     [ 'fuzz_tls==1', {
-      'sources': [
-        'det_rng.c',
-      ],
       'defines': [
         'UNSAFE_FUZZER_MODE',
       ],
     }],
     [ 'ct_verif==1', {
       'defines': [
         'CT_VERIF',
       ],
--- a/security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocsprequest.c
+++ b/security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocsprequest.c
@@ -84,18 +84,18 @@ pkix_pl_OcspRequest_Hashcode(
 
         if (ocspRq->addServiceLocator == PKIX_TRUE) {
                 extensionHash = 0xff;
         }
 
         PKIX_HASHCODE(ocspRq->signerCert, &signerHash, plContext,
                 PKIX_CERTHASHCODEFAILED);
 
-        *pHashcode = (((((extensionHash << 8) || certHash) << 8) ||
-                dateHash) << 8) || signerHash;
+        *pHashcode = (((((extensionHash << 8) | certHash) << 8) |
+                dateHash) << 8) | signerHash;
 
 cleanup:
 
         PKIX_RETURN(OCSPREQUEST);
 
 }
 
 /*
--- a/security/nss/lib/nss/nss.def
+++ b/security/nss/lib/nss/nss.def
@@ -1099,8 +1099,15 @@ PK11_VerifyWithMechanism;
 ;+};
 ;+NSS_3.30 { 	# NSS 3.30 release
 ;+    global:
 CERT_CompareAVA;
 PK11_HasAttributeSet;
 ;+    local:
 ;+       *;
 ;+};
+;+NSS_3.31 { 	# NSS 3.31 release
+;+    global:
+CERT_GetCertIsPerm;
+CERT_GetCertIsTemp;
+;+    local:
+;+       *;
+;+};
--- a/security/nss/lib/nss/nss.h
+++ b/security/nss/lib/nss/nss.h
@@ -17,17 +17,17 @@
 
 /*
  * NSS's major version, minor version, patch level, build number, and whether
  * this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
  */
-#define NSS_VERSION "3.30" _NSS_CUSTOMIZED " Beta"
+#define NSS_VERSION "3.31" _NSS_CUSTOMIZED " Beta"
 #define NSS_VMAJOR 3
 #define NSS_VMINOR 31
 #define NSS_VPATCH 0
 #define NSS_VBUILD 0
 #define NSS_BETA PR_TRUE
 
 #ifndef RC_INVOKED
 
--- a/security/nss/lib/pk11wrap/pk11cert.c
+++ b/security/nss/lib/pk11wrap/pk11cert.c
@@ -969,18 +969,20 @@ PK11_ImportCert(PK11SlotInfo *slot, CERT
 
     if (c->object.cryptoContext) {
         /* Delete the temp instance */
         NSSCryptoContext *cc = c->object.cryptoContext;
         nssCertificateStore_Lock(cc->certStore, &lockTrace);
         nssCertificateStore_RemoveCertLOCKED(cc->certStore, c);
         nssCertificateStore_Unlock(cc->certStore, &lockTrace, &unlockTrace);
         c->object.cryptoContext = NULL;
+        CERT_LockCertTempPerm(cert);
         cert->istemp = PR_FALSE;
         cert->isperm = PR_TRUE;
+        CERT_UnlockCertTempPerm(cert);
     }
 
     /* add the new instance to the cert, force an update of the
      * CERTCertificate, and finish
      */
     nssPKIObject_AddInstance(&c->object, certobj);
     /* nssTrustDomain_AddCertsToCache may release a reference to 'c' and
      * replace 'c' with a different value. So we add a reference to 'c' to
--- a/security/nss/lib/pki/pki3hack.c
+++ b/security/nss/lib/pki/pki3hack.c
@@ -826,18 +826,20 @@ fill_CERTCertificateFields(NSSCertificat
     }
     if (instance) {
         nssCryptokiObject_Destroy(instance);
     }
     /* database handle is now the trust domain */
     cc->dbhandle = c->object.trustDomain;
     /* subjectList ? */
     /* istemp and isperm are supported in NSS 3.4 */
+    CERT_LockCertTempPerm(cc);
     cc->istemp = PR_FALSE; /* CERT_NewTemp will override this */
     cc->isperm = PR_TRUE;  /* by default */
+    CERT_UnlockCertTempPerm(cc);
     /* pointer back */
     cc->nssCertificate = c;
     if (trust) {
         /* force the cert type to be recomputed to include trust info */
         PRUint32 nsCertType = cert_ComputeCertType(cc);
 
         /* Assert that it is safe to cast &cc->nsCertType to "PRInt32 *" */
         PORT_Assert(sizeof(cc->nsCertType) == sizeof(PRInt32));
--- a/security/nss/lib/softoken/legacydb/pcertdb.c
+++ b/security/nss/lib/softoken/legacydb/pcertdb.c
@@ -728,26 +728,37 @@ DecodeDBCertEntry(certDBEntryCert *entry
         if (lenoff < 0 || (lenoff & 0xffff) != 0) {
             PORT_SetError(SEC_ERROR_BAD_DATABASE);
             goto loser;
         }
         /* The cert size exceeded 64KB.  Reconstruct the correct length. */
         entry->derCert.len += lenoff;
     }
 
+    /* Is data long enough? */
+    if (dbentry->len < headerlen + entry->derCert.len) {
+        PORT_SetError(SEC_ERROR_BAD_DATABASE);
+        goto loser;
+    }
+
     /* copy the dercert */
     entry->derCert.data = pkcs11_copyStaticData(&dbentry->data[headerlen],
                                                 entry->derCert.len, entry->derCertSpace, sizeof(entry->derCertSpace));
     if (entry->derCert.data == NULL) {
         PORT_SetError(SEC_ERROR_NO_MEMORY);
         goto loser;
     }
 
     /* copy the nickname */
     if (nnlen > 1) {
+        /* Is data long enough? */
+        if (dbentry->len < headerlen + entry->derCert.len + nnlen) {
+            PORT_SetError(SEC_ERROR_BAD_DATABASE);
+            goto loser;
+        }
         entry->nickname = (char *)pkcs11_copyStaticData(
             &dbentry->data[headerlen + entry->derCert.len], nnlen,
             (unsigned char *)entry->nicknameSpace,
             sizeof(entry->nicknameSpace));
         if (entry->nickname == NULL) {
             PORT_SetError(SEC_ERROR_NO_MEMORY);
             goto loser;
         }
--- a/security/nss/lib/softoken/softkver.h
+++ b/security/nss/lib/softoken/softkver.h
@@ -16,16 +16,16 @@
 
 /*
  * Softoken's major version, minor version, patch level, build number,
  * and whether this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
  */
-#define SOFTOKEN_VERSION "3.30" SOFTOKEN_ECC_STRING " Beta"
+#define SOFTOKEN_VERSION "3.31" SOFTOKEN_ECC_STRING " Beta"
 #define SOFTOKEN_VMAJOR 3
 #define SOFTOKEN_VMINOR 31
 #define SOFTOKEN_VPATCH 0
 #define SOFTOKEN_VBUILD 0
 #define SOFTOKEN_BETA PR_TRUE
 
 #endif /* _SOFTKVER_H_ */
--- a/security/nss/lib/softoken/softoken.h
+++ b/security/nss/lib/softoken/softoken.h
@@ -178,17 +178,17 @@ extern PRBool sftk_fatalError;
  */
 
 #ifdef SOLARIS
 
 /* Solaris 8, s9 use PID checks, s10 uses pthread_atfork */
 
 #define CHECK_FORK_MIXED
 
-#elif defined(LINUX) || defined(__GLIBC__)
+#elif defined(LINUX) || defined(__GLIBC__) || defined(FREEBSD) || defined(OPENBSD)
 
 #define CHECK_FORK_PTHREAD
 
 #else
 
 /* Other Unix platforms use only PID checks. Even if pthread_atfork is
  * available, the behavior of dlclose isn't guaranteed by POSIX to
  * unregister the fork handler. */
--- a/security/nss/lib/ssl/ssl3con.c
+++ b/security/nss/lib/ssl/ssl3con.c
@@ -1764,39 +1764,16 @@ ssl3_InitCompressionContext(ssl3CipherSp
             PORT_Assert(0);
             PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
             return SECFailure;
     }
 
     return SECSuccess;
 }
 
-/* This function should probably be moved to pk11wrap and be named
- * PK11_ParamFromIVAndEffectiveKeyBits
- */
-static SECItem *
-ssl3_ParamFromIV(CK_MECHANISM_TYPE mtype, SECItem *iv, CK_ULONG ulEffectiveBits)
-{
-    SECItem *param = PK11_ParamFromIV(mtype, iv);
-    if (param && param->data && param->len >= sizeof(CK_RC2_PARAMS)) {
-        switch (mtype) {
-            case CKM_RC2_KEY_GEN:
-            case CKM_RC2_ECB:
-            case CKM_RC2_CBC:
-            case CKM_RC2_MAC:
-            case CKM_RC2_MAC_GENERAL:
-            case CKM_RC2_CBC_PAD:
-                *(CK_RC2_PARAMS *)param->data = ulEffectiveBits;
-            default:
-                break;
-        }
-    }
-    return param;
-}
-
 /* ssl3_BuildRecordPseudoHeader writes the SSL/TLS pseudo-header (the data
  * which is included in the MAC or AEAD additional data) to |out| and returns
  * its length. See https://tools.ietf.org/html/rfc5246#section-6.2.3.3 for the
  * definition of the AEAD additional data.
  *
  * TLS pseudo-header includes the record's version field, SSL's doesn't. Which
  * pseudo-header defintiion to use should be decided based on the version of
  * the protocol that was negotiated when the cipher spec became current, NOT
@@ -1968,17 +1945,16 @@ ssl3_InitPendingContexts(sslSocket *ss)
     ssl3CipherSpec *pwSpec;
     const ssl3BulkCipherDef *cipher_def;
     PK11Context *serverContext = NULL;
     PK11Context *clientContext = NULL;
     SECItem *param;
     CK_MECHANISM_TYPE mechanism;
     CK_MECHANISM_TYPE mac_mech;
     CK_ULONG macLength;
-    CK_ULONG effKeyBits;
     SECItem iv;
     SECItem mac_param;
     SSLCipherAlgorithm calg;
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSpecWriteLock(ss));
     PORT_Assert(ss->ssl3.prSpec == ss->ssl3.pwSpec);
 
@@ -2038,24 +2014,23 @@ ssl3_InitPendingContexts(sslSocket *ss)
     */
 
     if (calg == calg_null) {
         pwSpec->encode = Null_Cipher;
         pwSpec->decode = Null_Cipher;
         return SECSuccess;
     }
     mechanism = ssl3_Alg2Mech(calg);
-    effKeyBits = cipher_def->key_size * BPB;
 
     /*
      * build the server context
      */
     iv.data = pwSpec->server.write_iv;
     iv.len = cipher_def->iv_size;
-    param = ssl3_ParamFromIV(mechanism, &iv, effKeyBits);
+    param = PK11_ParamFromIV(mechanism, &iv);
     if (param == NULL) {
         ssl_MapLowLevelError(SSL_ERROR_IV_PARAM_FAILURE);
         goto fail;
     }
     serverContext = PK11_CreateContextBySymKey(mechanism,
                                                (ss->sec.isServer ? CKA_ENCRYPT
                                                                  : CKA_DECRYPT),
                                                pwSpec->server.write_key, param);
@@ -2069,17 +2044,17 @@ ssl3_InitPendingContexts(sslSocket *ss)
     }
 
     /*
      * build the client context
      */
     iv.data = pwSpec->client.write_iv;
     iv.len = cipher_def->iv_size;
 
-    param = ssl3_ParamFromIV(mechanism, &iv, effKeyBits);
+    param = PK11_ParamFromIV(mechanism, &iv);
     if (param == NULL) {
         ssl_MapLowLevelError(SSL_ERROR_IV_PARAM_FAILURE);
         goto fail;
     }
     clientContext = PK11_CreateContextBySymKey(mechanism,
                                                (ss->sec.isServer ? CKA_DECRYPT
                                                                  : CKA_ENCRYPT),
                                                pwSpec->client.write_key, param);
--- a/security/nss/lib/ssl/ssl3ecc.c
+++ b/security/nss/lib/ssl/ssl3ecc.c
@@ -252,26 +252,16 @@ ssl3_SendECDHClientKeyExchange(sslSocket
 loser:
     if (pms)
         PK11_FreeSymKey(pms);
     if (keyPair)
         ssl_FreeEphemeralKeyPair(keyPair);
     return SECFailure;
 }
 
-/* This function returns the size of the key_exchange field in
- * the KeyShareEntry structure, i.e.:
- *     opaque point <1..2^8-1>; */
-unsigned int
-tls13_SizeOfECDHEKeyShareKEX(const SECKEYPublicKey *pubKey)
-{
-    PORT_Assert(pubKey->keyType == ecKey);
-    return pubKey->u.ec.publicValue.len;
-}
-
 /* This function encodes the key_exchange field in
  * the KeyShareEntry structure. */
 SECStatus
 tls13_EncodeECDHEKeyShareKEX(const sslSocket *ss, const SECKEYPublicKey *pubKey)
 {
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
     PORT_Assert(pubKey->keyType == ecKey);
--- a/security/nss/lib/ssl/sslimpl.h
+++ b/security/nss/lib/ssl/sslimpl.h
@@ -1639,17 +1639,16 @@ extern SECStatus ssl3_HandleECDHServerKe
                                                   SSL3Opaque *b, PRUint32 length);
 extern SECStatus ssl3_HandleECDHClientKeyExchange(sslSocket *ss,
                                                   SSL3Opaque *b, PRUint32 length,
                                                   sslKeyPair *serverKeys);
 extern SECStatus ssl3_SendECDHServerKeyExchange(sslSocket *ss);
 extern SECStatus ssl_ImportECDHKeyShare(
     sslSocket *ss, SECKEYPublicKey *peerKey,
     SSL3Opaque *b, PRUint32 length, const sslNamedGroupDef *curve);
-unsigned int tls13_SizeOfECDHEKeyShareKEX(const SECKEYPublicKey *pubKey);
 SECStatus tls13_EncodeECDHEKeyShareKEX(const sslSocket *ss,
                                        const SECKEYPublicKey *pubKey);
 
 extern SECStatus ssl3_ComputeCommonKeyHash(SSLHashType hashAlg,
                                            PRUint8 *hashBuf,
                                            unsigned int bufLen,
                                            SSL3Hashes *hashes);
 extern void ssl3_DestroyCipherSpec(ssl3CipherSpec *spec, PRBool freeSrvName);
--- a/security/nss/lib/util/nssb64d.c
+++ b/security/nss/lib/util/nssb64d.c
@@ -365,17 +365,17 @@ pl_base64_decode_flush(PLBase64Decoder *
 
 /*
  * The maximum space needed to hold the output of the decoder given
  * input data of length "size".
  */
 static PRUint32
 PL_Base64MaxDecodedLength(PRUint32 size)
 {
-    return ((size * 3) / 4);
+    return size * 0.75;
 }
 
 /*
  * A distinct internal creation function for the buffer version to use.
  * (It does not want to specify an output_fn, and we want the normal
  * Create function to require that.)  If more common initialization
  * of the decoding context needs to be done, it should be done *here*.
  */
--- a/security/nss/lib/util/nssb64e.c
+++ b/security/nss/lib/util/nssb64e.c
@@ -277,30 +277,38 @@ pl_base64_encode_flush(PLBase64Encoder *
  * line_length bytes (we will add it at nearest lower multiple of 4).
  * There is no trailing CRLF.
  */
 static PRUint32
 PL_Base64MaxEncodedLength(PRUint32 size, PRUint32 line_length)
 {
     PRUint32 tokens, tokens_per_line, full_lines, line_break_chars, remainder;
 
+    /* This is the maximum length we support. */
+    if (size > 0x3fffffff) {
+        return 0;
+    }
+
     tokens = (size + 2) / 3;
 
-    if (line_length == 0)
+    if (line_length == 0) {
         return tokens * 4;
+    }
 
-    if (line_length < 4) /* too small! */
+    if (line_length < 4) { /* too small! */
         line_length = 4;
+    }
 
     tokens_per_line = line_length / 4;
     full_lines = tokens / tokens_per_line;
     remainder = (tokens - (full_lines * tokens_per_line)) * 4;
     line_break_chars = full_lines * 2;
-    if (remainder == 0)
+    if (remainder == 0) {
         line_break_chars -= 2;
+    }
 
     return (full_lines * tokens_per_line * 4) + line_break_chars + remainder;
 }
 
 /*
  * A distinct internal creation function for the buffer version to use.
  * (It does not want to specify an output_fn, and we want the normal
  * Create function to require that.)  All common initialization of the
@@ -442,23 +450,28 @@ PL_Base64EncodeBuffer(const unsigned cha
                       PRUint32 line_length, char *dest, PRUint32 maxdestlen,
                       PRUint32 *output_destlen)
 {
     PRUint32 need_length;
     PLBase64Encoder *data = NULL;
     PRStatus status;
 
     PR_ASSERT(srclen > 0);
-    if (srclen == 0)
+    if (srclen == 0) {
         return dest;
+    }
 
     /*
      * How much space could we possibly need for encoding this input?
      */
     need_length = PL_Base64MaxEncodedLength(srclen, line_length);
+    if (need_length == 0) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return NULL;
+    }
 
     /*
      * Make sure we have at least that much, if output buffer provided.
      */
     if (dest != NULL) {
         PR_ASSERT(maxdestlen >= need_length);
         if (maxdestlen < need_length) {
             PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0);
@@ -626,16 +639,20 @@ NSSBase64_EncodeItem(PLArenaPool *arenaO
 
     PORT_Assert(inItem != NULL && inItem->data != NULL && inItem->len != 0);
     if (inItem == NULL || inItem->data == NULL || inItem->len == 0) {
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return NULL;
     }
 
     max_out_len = PL_Base64MaxEncodedLength(inItem->len, 64);
+    if (max_out_len == 0) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return NULL;
+    }
 
     if (arenaOpt != NULL)
         mark = PORT_ArenaMark(arenaOpt);
 
     if (out_string == NULL) {
         if (arenaOpt != NULL)
             out_string = PORT_ArenaAlloc(arenaOpt, max_out_len + 1);
         else
--- a/security/nss/lib/util/nssutil.h
+++ b/security/nss/lib/util/nssutil.h
@@ -14,17 +14,17 @@
 
 /*
  * NSS utilities's major version, minor version, patch level, build number,
  * and whether this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <Beta>]"
  */
-#define NSSUTIL_VERSION "3.30 Beta"
+#define NSSUTIL_VERSION "3.31 Beta"
 #define NSSUTIL_VMAJOR 3
 #define NSSUTIL_VMINOR 31
 #define NSSUTIL_VPATCH 0
 #define NSSUTIL_VBUILD 0
 #define NSSUTIL_BETA PR_TRUE
 
 SEC_BEGIN_PROTOS
 
--- a/security/nss/tests/all.sh
+++ b/security/nss/tests/all.sh
@@ -268,17 +268,21 @@ run_cycles()
     done
 }
 
 ############################## main code ###############################
 
 cycles="standard pkix upgradedb sharedb"
 CYCLES=${NSS_CYCLES:-$cycles}
 
-tests="cipher lowhash libpkix cert dbtests tools fips sdr crmf smime ssl ocsp merge pkits chains ec gtests ssl_gtests"
+tests="cipher lowhash libpkix cert dbtests tools fips sdr crmf smime ssl ocsp merge pkits ec gtests ssl_gtests"
+# Don't run chains tests when we have a gyp build.
+if [ "$OBJDIR" != "Debug" -a "$OBJDIR" != "Release" ]; then
+  tests="$tests chains"
+fi
 TESTS=${NSS_TESTS:-$tests}
 
 ALL_TESTS=${TESTS}
 
 nss_ssl_tests="crl fips_normal normal_fips iopr policy"
 NSS_SSL_TESTS="${NSS_SSL_TESTS:-$nss_ssl_tests}"
 
 nss_ssl_run="cov auth stapling stress"
--- a/taskcluster/ci/test/tests.yml
+++ b/taskcluster/ci/test/tests.yml
@@ -1053,24 +1053,24 @@ reftest-stylo:
         # run only e10s tests for this platform see bug 1343301
         by-test-platform:
             linux64-stylo/opt:
                 # no opt on inbound see bug 1339604
                 by-project:
                     stylo: true
                     autoland: true
                     mozilla-central: true
-                    default: false
+                    default: true
             linux64-stylo/debug:
                 by-project:
-                    mozilla-inbound: true
                     stylo: true
                     autoland: true
                     mozilla-central: true
-                    default: false
+                    mozilla-inbound: true
+                    default: true
     mozharness:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         config:
             - unittests/linux_unittest.py
             - remove_executables.py
         extra-options:
             - --reftest-suite=reftest-stylo
--- a/taskcluster/taskgraph/optimize.py
+++ b/taskcluster/taskgraph/optimize.py
@@ -223,14 +223,18 @@ def opt_seta(task, params):
         # Always optimize away low-value tasks
         return True, None
     else:
         return False, None
 
 
 @optimization('files-changed')
 def opt_files_changed(task, params, file_patterns):
+    # pushlog_id == -1 - this is the case when run from a cron.yml job
+    if params.get('pushlog_id') == -1:
+        return True, None
+
     changed = files_changed.check(params, file_patterns)
     if not changed:
         logger.debug('no files found matching a pattern in `when.files-changed` for ' +
                      task.label)
         return True, None
     return False, None
--- a/testing/mozharness/configs/unittests/linux_unittest.py
+++ b/testing/mozharness/configs/unittests/linux_unittest.py
@@ -223,17 +223,16 @@ config = {
             "tests": ["tests/reftest/tests/layout/reftests/reftest.list"]
         },
         "reftest-no-accel": {
             "options": ["--suite=reftest",
                         "--setpref=layers.acceleration.force-enabled=disabled"],
             "tests": ["tests/reftest/tests/layout/reftests/reftest.list"]},
         "reftest-stylo": {
             "options": ["--suite=reftest",
-                        "--disable-e10s",
                         "--setpref=reftest.compareStyloToGecko=true"],
             "tests": ["tests/reftest/tests/layout/reftests/reftest-stylo.list"],
         },
     },
     "all_xpcshell_suites": {
         "xpcshell": {
             "options": ["--xpcshell=%(abs_app_dir)s/" + XPCSHELL_NAME,
                         "--manifest=tests/xpcshell/tests/xpcshell.ini"],
--- a/toolkit/components/telemetry/TelemetryController.jsm
+++ b/toolkit/components/telemetry/TelemetryController.jsm
@@ -714,37 +714,40 @@ var Impl = {
     }
 
     this._attachObservers();
 
     // Perform a lightweight, early initialization for the component, just registering
     // a few observers and initializing the session.
     TelemetrySession.earlyInit(this._testMode);
 
+    // Annotate crash reports so that we get pings for startup crashes
+    TelemetrySend.earlyInit();
+
     // For very short session durations, we may never load the client
     // id from disk.
     // We try to cache it in prefs to avoid this, even though this may
     // lead to some stale client ids.
     this._clientID = ClientID.getCachedClientID();
 
     // Delay full telemetry initialization to give the browser time to
     // run various late initializers. Otherwise our gathered memory
     // footprint and other numbers would be too optimistic.
     this._delayedInitTaskDeferred = Promise.defer();
     this._delayedInitTask = new DeferredTask(function* () {
       try {
         // TODO: This should probably happen after all the delayed init here.
         this._initialized = true;
         TelemetryEnvironment.delayedInit();
 
-        yield TelemetrySend.setup(this._testMode);
-
         // Load the ClientID.
         this._clientID = yield ClientID.getClientID();
 
+        yield TelemetrySend.setup(this._testMode);
+
         // Perform TelemetrySession delayed init.
         yield TelemetrySession.delayedInit();
         // Purge the pings archive by removing outdated pings. We don't wait for
         // this task to complete, but TelemetryStorage blocks on it during
         // shutdown.
         TelemetryStorage.runCleanPingArchiveTask();
 
         // Now that FHR/healthreporter is gone, make sure to remove FHR's DB from
--- a/toolkit/components/telemetry/TelemetrySend.jsm
+++ b/toolkit/components/telemetry/TelemetrySend.jsm
@@ -14,16 +14,17 @@
 this.EXPORTED_SYMBOLS = [
   "TelemetrySend",
 ];
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 Cu.import("resource://gre/modules/Task.jsm", this);
+Cu.import("resource://gre/modules/ClientID.jsm");
 Cu.import("resource://gre/modules/Log.jsm", this);
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/PromiseUtils.jsm");
 Cu.import("resource://gre/modules/ServiceRequest.jsm", this);
 Cu.import("resource://gre/modules/Services.jsm", this);
 Cu.import("resource://gre/modules/TelemetryUtils.jsm", this);
 Cu.import("resource://gre/modules/Timer.jsm", this);
 
@@ -40,16 +41,17 @@ XPCOMUtils.defineLazyServiceGetter(this,
 const Utils = TelemetryUtils;
 
 const LOGGER_NAME = "Toolkit.Telemetry";
 const LOGGER_PREFIX = "TelemetrySend::";
 
 const PREF_BRANCH = "toolkit.telemetry.";
 const PREF_SERVER = PREF_BRANCH + "server";
 const PREF_UNIFIED = PREF_BRANCH + "unified";
+const PREF_ENABLED = PREF_BRANCH + "enabled";
 const PREF_FHR_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled";
 const PREF_OVERRIDE_OFFICIAL_CHECK = PREF_BRANCH + "send.overrideOfficialCheck";
 
 const TOPIC_IDLE_DAILY = "idle-daily";
 const TOPIC_QUIT_APPLICATION = "quit-application";
 
 // Whether the FHR/Telemetry unification features are enabled.
 // Changing this pref requires a restart.
@@ -172,16 +174,24 @@ this.TelemetrySend = {
     return OVERDUE_PING_FILE_AGE;
   },
 
   get pendingPingCount() {
     return TelemetrySendImpl.pendingPingCount;
   },
 
   /**
+   * Partial setup that runs immediately at startup. This currently triggers
+   * the crash report annotations.
+   */
+  earlyInit() {
+    TelemetrySendImpl.earlyInit();
+  },
+
+  /**
    * Initializes this module.
    *
    * @param {Boolean} testing Whether this is run in a test. This changes some behavior
    * to enable proper testing.
    * @return {Promise} Resolved when setup is finished.
    */
   setup(testing = false) {
     return TelemetrySendImpl.setup(testing);
@@ -544,24 +554,30 @@ var TelemetrySendImpl = {
   // This is true when running in the test infrastructure.
   _testMode: false,
   // This holds pings that we currently try and haven't persisted yet.
   _currentPings: new Map(),
 
   // Count of pending pings that were overdue.
   _overduePingCount: 0,
 
-  // Whether sending pings has been overridden. We have it here instead
-  // of the top of the file to ease testing.
-  _overrideOfficialCheck: false,
-
   OBSERVER_TOPICS: [
     TOPIC_IDLE_DAILY,
   ],
 
+  OBSERVED_PREFERENCES: [
+    PREF_ENABLED,
+    PREF_FHR_UPLOAD_ENABLED,
+  ],
+
+  // Whether sending pings has been overridden.
+  get _overrideOfficialCheck() {
+    return Preferences.get(PREF_OVERRIDE_OFFICIAL_CHECK, false);
+  },
+
   get _log() {
     if (!this._logger) {
       this._logger = Log.repository.getLoggerWithMessagePrefix(LOGGER_NAME, LOGGER_PREFIX);
     }
 
     return this._logger;
   },
 
@@ -576,43 +592,82 @@ var TelemetrySendImpl = {
   get pendingPingCount() {
     return TelemetryStorage.getPendingPingList().length + this._currentPings.size;
   },
 
   setTestModeEnabled(testing) {
     this._testMode = testing;
   },
 
+  earlyInit() {
+    this._annotateCrashReport();
+  },
+
   async setup(testing) {
     this._log.trace("setup");
 
     this._testMode = testing;
     this._sendingEnabled = true;
-    this._overrideOfficialCheck = Preferences.get(PREF_OVERRIDE_OFFICIAL_CHECK, false);
 
     Services.obs.addObserver(this, TOPIC_IDLE_DAILY, false);
 
     this._server = Preferences.get(PREF_SERVER, undefined);
 
+    // Annotate crash reports so that crash pings are sent correctly and listen
+    // to pref changes to adjust the annotations accordingly.
+    for (let pref of this.OBSERVED_PREFERENCES) {
+      Preferences.observe(pref, this._annotateCrashReport, this);
+    }
+    this._annotateCrashReport();
+
     // Check the pending pings on disk now.
     try {
       await this._checkPendingPings();
     } catch (ex) {
       this._log.error("setup - _checkPendingPings rejected", ex);
     }
 
     // Enforce the pending pings storage quota. It could take a while so don't
     // block on it.
     TelemetryStorage.runEnforcePendingPingsQuotaTask();
 
     // Start sending pings, but don't block on this.
     SendScheduler.triggerSendingPings(true);
   },
 
   /**
+   * Triggers the crash report annotations depending on the current
+   * configuration. This communicates to the crash reporter if it can send a
+   * crash ping or not. This method can be called safely before setup() has
+   * been called.
+   */
+  _annotateCrashReport() {
+    try {
+      const cr = Cc["@mozilla.org/toolkit/crash-reporter;1"];
+      if (cr) {
+        const crs = cr.getService(Ci.nsICrashReporter);
+
+        let clientId = ClientID.getCachedClientID();
+        let server = this._server || Preferences.get(PREF_SERVER, undefined);
+
+        if (!this.sendingEnabled() || !TelemetryReportingPolicy.canUpload()) {
+          // If we cannot send pings then clear the crash annotations
+          crs.annotateCrashReport("TelemetryClientId", "");
+          crs.annotateCrashReport("TelemetryServerURL", "");
+        } else {
+          crs.annotateCrashReport("TelemetryClientId", clientId);
+          crs.annotateCrashReport("TelemetryServerURL", server);
+        }
+      }
+    } catch (e) {
+      // Ignore errors when crash reporting is disabled
+    }
+  },
+
+  /**
    * Discard old pings from the pending pings and detect overdue ones.
    * @return {Boolean} True if we have overdue pings, false otherwise.
    */
   async _checkPendingPings() {
     // Scan the pending pings - that gives us a list sorted by last modified, descending.
     let infos = await TelemetryStorage.loadPendingPingList();
     this._log.info("_checkPendingPings - pending ping count: " + infos.length);
     if (infos.length == 0) {
@@ -633,16 +688,23 @@ var TelemetrySendImpl = {
         Utils.millisecondsToDays(Math.abs(now.getTime() - pingInfo.lastModificationDate));
       Telemetry.getHistogramById("TELEMETRY_PENDING_PINGS_AGE").add(ageInDays);
     }
    },
 
   async shutdown() {
     this._shutdown = true;
 
+    for (let pref of this.OBSERVED_PREFERENCES) {
+      // FIXME: When running tests this causes errors to be printed out if
+      // TelemetrySend.shutdown() is called twice in a row without calling
+      // TelemetrySend.setup() in-between.
+      Preferences.ignore(pref, this._annotateCrashReport, this);
+    }
+
     for (let topic of this.OBSERVER_TOPICS) {
       try {
         Services.obs.removeObserver(this, topic);
       } catch (ex) {
         this._log.error("shutdown - failed to remove observer for " + topic, ex);
       }
     }
 
@@ -663,35 +725,37 @@ var TelemetrySendImpl = {
   },
 
   reset() {
     this._log.trace("reset");
 
     this._shutdown = false;
     this._currentPings = new Map();
     this._overduePingCount = 0;
-    this._overrideOfficialCheck = Preferences.get(PREF_OVERRIDE_OFFICIAL_CHECK, false);
 
     const histograms = [
       "TELEMETRY_SUCCESS",
       "TELEMETRY_SEND_SUCCESS",
       "TELEMETRY_SEND_FAILURE",
     ];
 
     histograms.forEach(h => Telemetry.getHistogramById(h).clear());
 
     return SendScheduler.reset();
   },
 
   /**
    * Notify that we can start submitting data to the servers.
    */
   notifyCanUpload() {
-    // Let the scheduler trigger sending pings if possible.
+    // Let the scheduler trigger sending pings if possible, also inform the
+    // crash reporter that it can send crash pings if appropriate.
     SendScheduler.triggerSendingPings(true);
+    this._annotateCrashReport();
+
     return this.promisePendingPingActivity();
   },
 
   observe(subject, topic, data) {
     switch (topic) {
     case TOPIC_IDLE_DAILY:
       SendScheduler.triggerSendingPings(true);
       break;
--- a/toolkit/components/telemetry/TelemetrySession.jsm
+++ b/toolkit/components/telemetry/TelemetrySession.jsm
@@ -16,17 +16,16 @@ Cu.import("resource://gre/modules/Servic
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 Cu.import("resource://gre/modules/Promise.jsm", this);
 Cu.import("resource://gre/modules/DeferredTask.jsm", this);
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Timer.jsm");
 Cu.import("resource://gre/modules/TelemetrySend.jsm", this);
 Cu.import("resource://gre/modules/TelemetryUtils.jsm", this);
 Cu.import("resource://gre/modules/AppConstants.jsm");
-Cu.import("resource://gre/modules/ClientID.jsm");
 
 const Utils = TelemetryUtils;
 
 const myScope = this;
 
 // When modifying the payload in incompatible ways, please bump this version number
 const PAYLOAD_VERSION = 4;
 const PING_TYPE_MAIN = "main";
@@ -61,17 +60,16 @@ const MIN_SUBSESSION_LENGTH_MS = Prefere
 const LOGGER_NAME = "Toolkit.Telemetry";
 const LOGGER_PREFIX = "TelemetrySession" + (Utils.isContentProcess ? "#content::" : "::");
 
 const PREF_BRANCH = "toolkit.telemetry.";
 const PREF_PREVIOUS_BUILDID = PREF_BRANCH + "previousBuildID";
 const PREF_FHR_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled";
 const PREF_ASYNC_PLUGIN_INIT = "dom.ipc.plugins.asyncInit.enabled";
 const PREF_UNIFIED = PREF_BRANCH + "unified";
-const PREF_SERVER = PREF_BRANCH + "server";
 
 const MESSAGE_TELEMETRY_PAYLOAD = "Telemetry:Payload";
 const MESSAGE_TELEMETRY_THREAD_HANGS = "Telemetry:ChildThreadHangs";
 const MESSAGE_TELEMETRY_GET_CHILD_THREAD_HANGS = "Telemetry:GetChildThreadHangs";
 const MESSAGE_TELEMETRY_USS = "Telemetry:USS";
 const MESSAGE_TELEMETRY_GET_CHILD_USS = "Telemetry:GetChildUSS";
 
 const DATAREPORTING_DIRECTORY = "datareporting";
@@ -194,35 +192,22 @@ function getPingType(aPayload) {
   }
 
   return PING_TYPE_MAIN;
 }
 
 /**
  * Annotate the current session ID with the crash reporter to map potential
  * crash pings with the related main ping.
- *
- * @param sessionId {String} The telemetry session ID
- * @param clientId {String} The telemetry client ID
- * @param telemetryServer {String} The URL of the telemetry server
  */
-function annotateCrashReport(sessionId, clientId, telemetryServer) {
+function annotateCrashReport(sessionId) {
   try {
     const cr = Cc["@mozilla.org/toolkit/crash-reporter;1"];
     if (cr) {
-      const crs = cr.getService(Ci.nsICrashReporter);
-
-      crs.setTelemetrySessionId(sessionId);
-
-      // We do not annotate the crash if telemetry is disabled to prevent the
-      // crashreporter client from sending the crash ping.
-      if (Utils.isTelemetryEnabled) {
-        crs.annotateCrashReport("TelemetryClientId", clientId);
-        crs.annotateCrashReport("TelemetryServerURL", telemetryServer);
-      }
+      cr.getService(Ci.nsICrashReporter).setTelemetrySessionId(sessionId);
     }
   } catch (e) {
     // Ignore errors when crash reporting is disabled
   }
 }
 
 /**
  * Read current process I/O counters.
@@ -1483,20 +1468,17 @@ var Impl = {
     // Generate a unique id once per session so the server can cope with duplicate
     // submissions, orphaning and other oddities. The id is shared across subsessions.
     this._sessionId = Policy.generateSessionUUID();
     this.startNewSubsession();
     // startNewSubsession sets |_subsessionStartDate| to the current date/time. Use
     // the very same value for |_sessionStartDate|.
     this._sessionStartDate = this._subsessionStartDate;
 
-    // Annotate crash reports using the cached client ID which can be accessed
-    // synchronously. If it is not available yet, we'll update it later.
-    annotateCrashReport(this._sessionId, ClientID.getCachedClientID(),
-                        Preferences.get(PREF_SERVER, undefined));
+    annotateCrashReport(this._sessionId);
 
     // Initialize some probes that are kept in their own modules
     this._thirdPartyCookies = new ThirdPartyCookieProbe();
     this._thirdPartyCookies.init();
 
     // Record old value and update build ID preference if this is the first
     // run with a new build ID.
     let previousBuildId = Preferences.get(PREF_PREVIOUS_BUILDID, null);
@@ -1541,20 +1523,16 @@ var Impl = {
         this.gatherMemory();
 
         if (Telemetry.canRecordExtended) {
           GCTelemetry.init();
         }
 
         Telemetry.asyncFetchTelemetryData(function() {});
 
-        // Update the crash annotation with the proper client ID.
-        annotateCrashReport(this._sessionId, await ClientID.getClientID(),
-                            Preferences.get(PREF_SERVER, undefined));
-
         if (IS_UNIFIED_TELEMETRY) {
           // Check for a previously written aborted session ping.
           await TelemetryController.checkAbortedSessionPing();
 
           // Write the first aborted-session ping as early as possible. Just do that
           // if we are not testing, since calling Telemetry.reset() will make a previous
           // aborted ping a pending ping.
           if (!this._testing) {
--- a/toolkit/content/tests/chrome/test_arrowpanel.xul
+++ b/toolkit/content/tests/chrome/test_arrowpanel.xul
@@ -178,29 +178,25 @@ function* nextTest()
     $("topright").removeAttribute("right");
     $("bottomleft").removeAttribute("left");
     $("bottomright").removeAttribute("right");
   }
 
   // Test that a transition occurs when opening or closing the popup. The transition is
   // disabled on Linux.
   if (navigator.platform.indexOf("Linux") == -1) {
-    var transitions = 0;
     function transitionEnded(event) {
-      transitions++;
-      // Two properties transition so continue on the second one finishing.
-      if (!(transitions % 2)) {
+      if ($("animatepanel").state != "open") {
+        is($("animatepanel").state, "showing", "state is showing during transitionend");
+        ok(!animatedPopupShown, "popupshown not fired yet")
+      } else {
         is($("animatepanel").state, "open", "state is open after transitionend");
         ok(animatedPopupShown, "popupshown now fired")
         SimpleTest.executeSoon(() => runNextTest.next());
       }
-      else {
-        is($("animatepanel").state, "showing", "state is showing during transitionend");
-        ok(!animatedPopupShown, "popupshown not fired yet")
-      }
     }
 
     // Check that the transition occurs for an arrow panel with animate="true"
     window.addEventListener("transitionend", transitionEnded, false);
     $("animatepanel").openPopup($("topleft"), "after_start", 0, 0, false, false, null, "start");
     is($("animatepanel").state, "showing", "state is showing");
     yield;
     window.removeEventListener("transitionend", transitionEnded, false);
--- a/toolkit/crashreporter/test/unit/test_crashreporter_crash.js
+++ b/toolkit/crashreporter/test/unit/test_crashreporter_crash.js
@@ -27,28 +27,104 @@ function run_test() {
                });
              }
              if (is_win7_or_newer)
                do_check_true(CrashTestUtils.dumpHasStream(mdump.path, CrashTestUtils.MD_MEMORY_INFO_LIST_STREAM));
            });
 
   // check setting some basic data
   do_crash(function() {
+             // Add various annotations
              crashReporter.annotateCrashReport("TestKey", "TestValue");
              crashReporter.annotateCrashReport("\u2665", "\u{1F4A9}");
              crashReporter.appendAppNotesToCrashReport("Junk");
              crashReporter.appendAppNotesToCrashReport("MoreJunk");
+
              // TelemetrySession setup will trigger the session annotation
              let scope = {};
              Components.utils.import("resource://gre/modules/TelemetryController.jsm", scope);
              scope.TelemetryController.testSetup();
            },
            function(mdump, extra) {
              do_check_eq(extra.TestKey, "TestValue");
              do_check_eq(extra["\u2665"], "\u{1F4A9}");
              do_check_eq(extra.Notes, "JunkMoreJunk");
              const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
              Assert.ok("TelemetrySessionId" in extra,
                        "The TelemetrySessionId field is present in the extra file");
              Assert.ok(UUID_REGEX.test(extra.TelemetrySessionId),
                        "The TelemetrySessionId is a UUID");
+             Assert.ok(!("TelemetryClientId" in extra),
+                       "The TelemetryClientId field is omitted by default");
+             Assert.ok(!("TelemetryServerURL" in extra),
+                       "The TelemetryServerURL field is omitted by default");
            });
+
+  do_crash(function() {
+    // Enable the FHR, official policy bypass (since we're in a test) and
+    // specify a telemetry server & client ID.
+    let prefs = Components.classes["@mozilla.org/preferences-service;1"]
+                          .getService(Components.interfaces.nsIPrefBranch);
+    prefs.setBoolPref("datareporting.policy.dataSubmissionPolicyBypassNotification", true);
+    prefs.setBoolPref("datareporting.healthreport.uploadEnabled", true);
+    prefs.setCharPref("toolkit.telemetry.server", "http://a.telemetry.server");
+    prefs.setCharPref("toolkit.telemetry.cachedClientID",
+                      "f3582dee-22b9-4d73-96d1-79ef5bf2fc24");
+
+    // TelemetrySession setup will trigger the session annotation
+    let scope = {};
+    Components.utils.import("resource://gre/modules/TelemetryController.jsm", scope);
+    Components.utils.import("resource://gre/modules/TelemetrySend.jsm", scope);
+    scope.TelemetrySend.setTestModeEnabled(true);
+    scope.TelemetryController.testSetup();
+  }, function(mdump, extra) {
+    Assert.ok("TelemetryClientId" in extra,
+              "The TelemetryClientId field is present when the FHR is on");
+    Assert.equal(extra.TelemetryClientId,
+                 "f3582dee-22b9-4d73-96d1-79ef5bf2fc24",
+                 "The TelemetryClientId matches the expected value");
+    Assert.ok("TelemetryServerURL" in extra,
+              "The TelemetryServerURL field is present when the FHR is on");
+    Assert.equal(extra.TelemetryServerURL, "http://a.telemetry.server",
+                 "The TelemetryServerURL matches the expected value");
+  });
+
+  do_crash(function() {
+    // Disable the FHR upload, no telemetry annotations should be present.
+    let prefs = Components.classes["@mozilla.org/preferences-service;1"]
+                          .getService(Components.interfaces.nsIPrefBranch);
+    prefs.setBoolPref("datareporting.policy.dataSubmissionPolicyBypassNotification", true);
+    prefs.setBoolPref("datareporting.healthreport.uploadEnabled", false);
+
+    // TelemetrySession setup will trigger the session annotation
+    let scope = {};
+    Components.utils.import("resource://gre/modules/TelemetryController.jsm", scope);
+    Components.utils.import("resource://gre/modules/TelemetrySend.jsm", scope);
+    scope.TelemetrySend.setTestModeEnabled(true);
+    scope.TelemetryController.testSetup();
+  }, function(mdump, extra) {
+    Assert.ok(!("TelemetryClientId" in extra),
+              "The TelemetryClientId field is omitted when FHR upload is disabled");
+    Assert.ok(!("TelemetryServerURL" in extra),
+              "The TelemetryServerURL field is omitted when FHR upload is disabled");
+  });
+
+  do_crash(function() {
+    // No telemetry annotations should be present if the user has not been
+    // notified yet
+    let prefs = Components.classes["@mozilla.org/preferences-service;1"]
+                          .getService(Components.interfaces.nsIPrefBranch);
+    prefs.setBoolPref("datareporting.policy.dataSubmissionPolicyBypassNotification", false);
+    prefs.setBoolPref("datareporting.healthreport.uploadEnabled", true);
+
+    // TelemetrySession setup will trigger the session annotation
+    let scope = {};
+    Components.utils.import("resource://gre/modules/TelemetryController.jsm", scope);
+    Components.utils.import("resource://gre/modules/TelemetrySend.jsm", scope);
+    scope.TelemetrySend.setTestModeEnabled(true);
+    scope.TelemetryController.testSetup();
+  }, function(mdump, extra) {
+    Assert.ok(!("TelemetryClientId" in extra),
+              "The TelemetryClientId field is omitted when FHR upload is disabled");
+    Assert.ok(!("TelemetryServerURL" in extra),
+              "The TelemetryServerURL field is omitted when FHR upload is disabled");
+  });
 }
--- a/tools/profiler/tasktracer/GeckoTaskTracer.cpp
+++ b/tools/profiler/tasktracer/GeckoTaskTracer.cpp
@@ -52,17 +52,17 @@ namespace tasktracer {
 #include "SourceEventTypeMap.h"
 #undef SOURCE_EVENT_NAME
 
 static MOZ_THREAD_LOCAL(TraceInfo*) sTraceInfoTLS;
 static mozilla::StaticMutex sMutex;
 
 // The generation of TraceInfo. It will be > 0 if the Task Tracer is started and
 // <= 0 if stopped.
-static mozilla::Atomic<bool> sStarted(false);
+bool gStarted(false);
 static nsTArray<UniquePtr<TraceInfo>>* sTraceInfos = nullptr;
 static PRTime sStartTime;
 
 static const char sJSLabelPrefix[] = "#tt#";
 
 namespace {
 
 static PRTime
@@ -134,29 +134,23 @@ inline static void
 ObsoleteCurrentTraceInfos()
 {
   MOZ_ASSERT(sTraceInfos);
   for (uint32_t i = 0; i < sTraceInfos->Length(); ++i) {
     (*sTraceInfos)[i]->mObsolete = true;
   }
 }
 
-inline static bool
-IsStartLogging()
-{
-  return sStarted;
-}
-
 static void
 SetLogStarted(bool aIsStartLogging)
 {
-  MOZ_ASSERT(aIsStartLogging != sStarted);
+  MOZ_ASSERT(aIsStartLogging != gStarted);
   StaticMutexAutoLock lock(sMutex);
 
-  sStarted = aIsStartLogging;
+  gStarted = aIsStartLogging;
 
   if (aIsStartLogging && sTraceInfos == nullptr) {
     sTraceInfos = new nsTArray<UniquePtr<TraceInfo>>();
   }
 
   if (!aIsStartLogging && sTraceInfos) {
     ObsoleteCurrentTraceInfos();
   }
@@ -267,22 +261,22 @@ GenNewUniqueTaskId()
   TraceInfoHolder info = GetOrCreateTraceInfo();
   ENSURE_TRUE(info, 0);
 
   Thread::tid_t tid = Thread::GetCurrentId();
   uint64_t taskid = ((uint64_t)tid << 32) | ++info->mLastUniqueTaskId;
   return taskid;
 }
 
-AutoSaveCurTraceInfo::AutoSaveCurTraceInfo()
+AutoSaveCurTraceInfoImpl::AutoSaveCurTraceInfoImpl()
 {
   GetCurTraceInfo(&mSavedSourceEventId, &mSavedTaskId, &mSavedSourceEventType);
 }
 
-AutoSaveCurTraceInfo::~AutoSaveCurTraceInfo()
+AutoSaveCurTraceInfoImpl::~AutoSaveCurTraceInfoImpl()
 {
   SetCurTraceInfo(mSavedSourceEventId, mSavedTaskId, mSavedSourceEventType);
 }
 
 void
 SetCurTraceInfo(uint64_t aSourceEventId, uint64_t aParentTaskId,
                 SourceEventType aSourceEventType)
 {
@@ -386,64 +380,50 @@ LogVirtualTablePtr(uint64_t aTaskId, uin
     // non-function addresses, we use the first entry of vtable as the symbol
     // to solve. We should find a better solution later.
     log->mVPtr.mType = ACTION_GET_VTABLE;
     log->mVPtr.mTaskId = aTaskId;
     log->mVPtr.mVPtr = reinterpret_cast<uintptr_t>(aVptr);
   }
 }
 
-AutoSourceEvent::AutoSourceEvent(SourceEventType aType)
-  : AutoSaveCurTraceInfo()
+void
+AutoSourceEvent::StartScope(SourceEventType aType)
 {
   CreateSourceEvent(aType);
 }
 
-AutoSourceEvent::~AutoSourceEvent()
+void
+AutoSourceEvent::StopScope()
 {
   DestroySourceEvent();
 }
 
-AutoScopedLabel::AutoScopedLabel(const char* aFormat, ...)
-  : mLabel(nullptr)
+void
+AutoScopedLabel::Init(const char* aFormat, va_list& aArgs)
 {
-  if (IsStartLogging()) {
-    // Optimization for when it is disabled.
-    nsCString label;
-    va_list args;
-    va_start(args, aFormat);
-    label.AppendPrintf(aFormat, args);
-    va_end(args);
-    mLabel = strdup(label.get());
-    AddLabel("Begin %s", mLabel);
-  }
+  nsCString label;
+  va_list& args = aArgs;
+  label.AppendPrintf(aFormat, args);
+  mLabel = strdup(label.get());
+  AddLabel("Begin %s", mLabel);
 }
 
-AutoScopedLabel::~AutoScopedLabel()
-{
-  if (mLabel) {
-    AddLabel("End %s", mLabel);
-    free(mLabel);
-  }
-}
-
-void AddLabel(const char* aFormat, ...)
+void DoAddLabel(const char* aFormat, va_list& aArgs)
 {
   TraceInfoHolder info = GetOrCreateTraceInfo();
   ENSURE_TRUE_VOID(info);
 
   // Log format:
   // [3 taskId "label"]
   TraceInfoLogType* log = info->AppendLog();
   if (log) {
-    va_list args;
-    va_start(args, aFormat);
+    va_list& args = aArgs;
     nsCString &buffer = *info->mStrs.AppendElement();
     buffer.AppendPrintf(aFormat, args);
-    va_end(args);
 
     log->mLabel.mType = ACTION_ADD_LABEL;
     log->mLabel.mTaskId = info->mCurTaskId;
     log->mLabel.mTime = GetTimestamp();
     log->mLabel.mStrIdx = info->mStrs.Length() - 1;
   }
 }
 
--- a/tools/profiler/tasktracer/GeckoTaskTracer.h
+++ b/tools/profiler/tasktracer/GeckoTaskTracer.h
@@ -2,17 +2,21 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GECKO_TASK_TRACER_H
 #define GECKO_TASK_TRACER_H
 
+#include <stdarg.h>
+
 #include "mozilla/UniquePtr.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Maybe.h"
 #include "nsCOMPtr.h"
 #include "nsTArrayForwardDeclare.h"
 
 /**
  * TaskTracer provides a way to trace the correlation between different tasks
  * across threads and processes. Unlike sampling based profilers, TaskTracer can
  * tell you where a task is dispatched from, what its original source was, how
  * long it waited in the event queue, and how long it took to execute.
@@ -30,49 +34,111 @@ class nsCString;
 
 namespace mozilla {
 
 class TimeStamp;
 class Runnable;
 
 namespace tasktracer {
 
+extern bool gStarted;
+
+/**
+ * Check if the TaskTracer has been started.
+ */
+inline bool IsStartLogging()
+{
+  // |gStarted| is not an atomic variable, but it is fine for it is a
+  // boolean value and will be changed under the protection of
+  // |sMutex|.
+  //
+  // There is a latency between the change of the value and the
+  // observation of threads.  |gStarted| would be checked again with
+  // the protection of mutex in logging functions; for example,
+  // |AddLabel()|, so all false positive would be blocked with the
+  // double checks.  For false negative, it is fine to lose some
+  // records at begin of logging.
+  return gStarted;
+}
+
 enum {
   FORKED_AFTER_NUWA = 1 << 0
 };
 
 enum SourceEventType {
 #define SOURCE_EVENT_NAME(x) x,
 #include "SourceEventTypeMap.h"
 #undef SOURCE_EVENT_NAME
 };
 
-class AutoSaveCurTraceInfo
+class AutoSaveCurTraceInfoImpl
 {
   uint64_t mSavedTaskId;
   uint64_t mSavedSourceEventId;