Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Fri, 02 Dec 2016 13:06:38 -0800
changeset 325136 633e960e72dbd3006a2d0325416e50a19a7e1838
parent 325115 919596f62a27dbc3275aea5e04572a9ab026c1da (current diff)
parent 325135 d277102b35f954c38255d2fc683b7bbb2a2a3024 (diff)
child 325137 bfa85d23df57c8a1db17c99b267667becc1c4afd
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewersmerge
milestone53.0a1
Merge inbound to central, a=merge
layout/reftests/forms/button/focus-area-percent-units-1-ref.html
layout/reftests/forms/button/focus-area-percent-units-1.html
layout/reftests/forms/button/focus-area-percent-units-2-ref.html
layout/reftests/forms/button/focus-area-percent-units-2.html
layout/reftests/forms/button/percent-height-child-1-ref.html
layout/reftests/forms/button/percent-height-child-1.html
layout/reftests/forms/button/percent-height-child-2-ref.html
layout/reftests/forms/button/percent-height-child-2.html
layout/reftests/forms/button/percent-width-child-1-ref.html
layout/reftests/forms/button/percent-width-child-1.html
layout/reftests/forms/button/percent-width-child-2-ref.html
layout/reftests/forms/button/percent-width-child-2.html
layout/reftests/forms/button/width-erode-all-focuspadding-ltr-ref.html
layout/reftests/forms/button/width-erode-all-focuspadding-ltr.html
layout/reftests/forms/button/width-erode-overflow-focuspadding-ltr-ref.html
layout/reftests/forms/button/width-erode-overflow-focuspadding-ltr.html
layout/reftests/forms/button/width-erode-overflow-focuspadding-rtl-ref.html
layout/reftests/forms/button/width-erode-overflow-focuspadding-rtl.html
layout/reftests/forms/button/width-erode-part-focuspadding-ltr-ref.html
layout/reftests/forms/button/width-erode-part-focuspadding-ltr.html
layout/reftests/forms/button/width-erode-part-focuspadding-rtl-ref.html
layout/reftests/forms/button/width-erode-part-focuspadding-rtl.html
security/nss/fuzz/libFuzzer/libFuzzer.gyp
testing/web-platform/meta/FileAPI/reading-data-section/filereader_abort.html.ini
--- a/browser/modules/BrowserUsageTelemetry.jsm
+++ b/browser/modules/BrowserUsageTelemetry.jsm
@@ -288,88 +288,91 @@ let BrowserUsageTelemetry = {
     } else {
       if (!KNOWN_SEARCH_SOURCES.includes(source)) {
         throw new Error("Unknown source for search: " + source);
       }
       Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS").add(countId);
     }
 
     // Dispatch the search signal to other handlers.
-    this._handleSearchAction(source, details);
+    this._handleSearchAction(engine, source, details);
   },
 
-  _handleSearchAction(source, details) {
+  _recordSearch(engine, source, action = null) {
+    let scalarKey = action ? "search_" + action : "search";
+    Services.telemetry.keyedScalarAdd("browser.engagement.navigation." + source,
+                                      scalarKey, 1);
+    Services.telemetry.recordEvent("navigation", "search", source, action,
+                                   { engine: getSearchEngineId(engine) });
+  },
+
+  _handleSearchAction(engine, source, details) {
     switch (source) {
       case "urlbar":
       case "oneoff-urlbar":
       case "searchbar":
       case "oneoff-searchbar":
       case "unknown": // Edge case: this is the searchbar (see bug 1195733 comment 7).
-        this._handleSearchAndUrlbar(source, details);
+        this._handleSearchAndUrlbar(engine, source, details);
         break;
       case "abouthome":
-        Services.telemetry.keyedScalarAdd("browser.engagement.navigation.about_home",
-                                          "search_enter", 1);
+        this._recordSearch(engine, "about_home", "enter");
         break;
       case "newtab":
-        Services.telemetry.keyedScalarAdd("browser.engagement.navigation.about_newtab",
-                                          "search_enter", 1);
+        this._recordSearch(engine, "about_newtab", "enter");
         break;
       case "contextmenu":
-        Services.telemetry.keyedScalarAdd("browser.engagement.navigation.contextmenu",
-                                          "search", 1);
+        this._recordSearch(engine, "contextmenu");
         break;
     }
   },
 
   /**
    * This function handles the "urlbar", "urlbar-oneoff", "searchbar" and
    * "searchbar-oneoff" sources.
    */
-  _handleSearchAndUrlbar(source, details) {
+  _handleSearchAndUrlbar(engine, source, details) {
     // We want "urlbar" and "urlbar-oneoff" (and similar cases) to go in the same
     // scalar, but in a different key.
 
     // When using one-offs in the searchbar we get an "unknown" source. See bug
     // 1195733 comment 7 for the context. Fix-up the label here.
-    const plainSourceName =
+    const sourceName =
       (source === "unknown") ? "searchbar" : source.replace("oneoff-", "");
-    const scalarName = "browser.engagement.navigation." + plainSourceName;
 
     const isOneOff = !!details.isOneOff;
     if (isOneOff) {
       // We will receive a signal from the "urlbar"/"searchbar" even when the
       // search came from "oneoff-urlbar". That's because both signals
       // are propagated from search.xml. Skip it if that's the case.
       // Moreover, we skip the "unknown" source that comes from the searchbar
       // when performing searches from the default search engine. See bug 1195733
       // comment 7 for context.
       if (["urlbar", "searchbar", "unknown"].includes(source)) {
         return;
       }
 
-      // If that's a legit one-off search signal, increment the scalar using the
-      // relative key.
-      Services.telemetry.keyedScalarAdd(scalarName, "search_oneoff", 1);
+      // If that's a legit one-off search signal, record it using the relative key.
+      this._recordSearch(engine, sourceName, "oneoff");
       return;
     }
 
     // The search was not a one-off. It was a search with the default search engine.
     if (details.isSuggestion) {
       // It came from a suggested search, so count it as such.
-      Services.telemetry.keyedScalarAdd(scalarName, "search_suggestion", 1);
+      this._recordSearch(engine, sourceName, "suggestion");
       return;
     } else if (details.isAlias) {
       // This one came from a search that used an alias.
-      Services.telemetry.keyedScalarAdd(scalarName, "search_alias", 1);
+      this._recordSearch(engine, sourceName, "alias");
       return;
     }
 
     // The search signal was generated by typing something and pressing enter.
-    Services.telemetry.keyedScalarAdd(scalarName, "search_enter", 1);
+    this._recordSearch(engine, sourceName, "enter");
   },
 
   /**
    * This gets called shortly after the SessionStore has finished restoring
    * windows and tabs. It counts the open tabs and adds listeners to all the
    * windows.
    */
   _setupAfterRestore() {
--- a/browser/modules/test/browser_UsageTelemetry_content.js
+++ b/browser/modules/test/browser_UsageTelemetry_content.js
@@ -32,18 +32,19 @@ add_task(function* setup() {
   registerCleanupFunction(function* () {
     Services.search.currentEngine = originalEngine;
     Services.search.removeEngine(engineDefault);
     Services.search.removeEngine(engineOneOff);
   });
 });
 
 add_task(function* test_context_menu() {
-  // Let's reset the counts.
+  // Let's reset the Telemetry data.
   Services.telemetry.clearScalars();
+  Services.telemetry.clearEvents();
   let search_hist = getSearchCountsHistogram();
 
   // Open a new tab with a page containing some text.
   let tab =
     yield BrowserTestUtils.openNewForegroundTab(gBrowser, "data:text/plain;charset=utf8,test%20search");
 
   info("Select all the text in the page.");
   yield ContentTask.spawn(tab.linkedBrowser, "", function*() {
@@ -59,34 +60,40 @@ add_task(function* test_context_menu() {
   BrowserTestUtils.synthesizeMouseAtCenter("body", { type: "contextmenu", button: 2 },
                                            gBrowser.selectedBrowser);
   yield popupPromise;
 
   info("Click on search.");
   let searchItem = contextMenu.getElementsByAttribute("id", "context-searchselect")[0];
   searchItem.click();
 
-  info("Validate the search counts.");
+  info("Validate the search metrics.");
   const scalars =
     Services.telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
   checkKeyedScalar(scalars, SCALAR_CONTEXT_MENU, "search", 1);
   Assert.equal(Object.keys(scalars[SCALAR_CONTEXT_MENU]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   checkKeyedHistogram(search_hist, 'other-MozSearch.contextmenu', 1);
 
+  // Also check events.
+  let events = Services.telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
+  events = events.filter(e => e[1] == "navigation" && e[2] == "search");
+  checkEvents(events, [["navigation", "search", "contextmenu", null, {engine: "other-MozSearch"}]]);
+
   contextMenu.hidePopup();
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
   yield BrowserTestUtils.removeTab(tab);
 });
 
 add_task(function* test_about_newtab() {
   // Let's reset the counts.
   Services.telemetry.clearScalars();
+  Services.telemetry.clearEvents();
   let search_hist = getSearchCountsHistogram();
 
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:newtab", false);
   yield ContentTask.spawn(tab.linkedBrowser, null, function* () {
     yield ContentTaskUtils.waitForCondition(() => !content.document.hidden);
   });
 
   info("Trigger a simple serch, just text + enter.");
@@ -100,10 +107,15 @@ add_task(function* test_about_newtab() {
     Services.telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
   checkKeyedScalar(scalars, SCALAR_ABOUT_NEWTAB, "search_enter", 1);
   Assert.equal(Object.keys(scalars[SCALAR_ABOUT_NEWTAB]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   checkKeyedHistogram(search_hist, 'other-MozSearch.newtab', 1);
 
+  // Also check events.
+  let events = Services.telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
+  events = events.filter(e => e[1] == "navigation" && e[2] == "search");
+  checkEvents(events, [["navigation", "search", "about_newtab", "enter", {engine: "other-MozSearch"}]]);
+
   yield BrowserTestUtils.removeTab(tab);
 });
--- a/browser/modules/test/browser_UsageTelemetry_content_aboutHome.js
+++ b/browser/modules/test/browser_UsageTelemetry_content_aboutHome.js
@@ -36,16 +36,17 @@ add_task(function* setup() {
     Services.search.removeEngine(engineDefault);
     Services.search.removeEngine(engineOneOff);
   });
 });
 
 add_task(function* test_abouthome_simpleQuery() {
   // Let's reset the counts.
   Services.telemetry.clearScalars();
+  Services.telemetry.clearEvents();
   let search_hist = getSearchCountsHistogram();
 
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser);
 
   info("Setup waiting for AboutHomeLoadSnippetsCompleted.");
   let promiseAboutHomeLoaded = new Promise(resolve => {
     tab.linkedBrowser.addEventListener("AboutHomeLoadSnippetsCompleted", function loadListener(event) {
       tab.linkedBrowser.removeEventListener("AboutHomeLoadSnippetsCompleted", loadListener, true);
@@ -69,10 +70,15 @@ add_task(function* test_abouthome_simple
     Services.telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
   checkKeyedScalar(scalars, SCALAR_ABOUT_HOME, "search_enter", 1);
   Assert.equal(Object.keys(scalars[SCALAR_ABOUT_HOME]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   checkKeyedHistogram(search_hist, 'other-MozSearch.abouthome', 1);
 
+  // Also check events.
+  let events = Services.telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
+  events = events.filter(e => e[1] == "navigation" && e[2] == "search");
+  checkEvents(events, [["navigation", "search", "about_home", "enter", {engine: "other-MozSearch"}]]);
+
   yield BrowserTestUtils.removeTab(tab);
 });
--- a/browser/modules/test/browser_UsageTelemetry_searchbar.js
+++ b/browser/modules/test/browser_UsageTelemetry_searchbar.js
@@ -76,16 +76,17 @@ add_task(function* setup() {
     Services.search.removeEngine(engineDefault);
     Services.search.removeEngine(engineOneOff);
   });
 });
 
 add_task(function* test_plainQuery() {
   // Let's reset the counts.
   Services.telemetry.clearScalars();
+  Services.telemetry.clearEvents();
   let search_hist = getSearchCountsHistogram();
 
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
 
   info("Simulate entering a simple search.");
   let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
   yield searchInSearchbar("simple query");
   EventUtils.sendKey("return");
@@ -96,22 +97,28 @@ add_task(function* test_plainQuery() {
     Services.telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
   checkKeyedScalar(scalars, SCALAR_SEARCHBAR, "search_enter", 1);
   Assert.equal(Object.keys(scalars[SCALAR_SEARCHBAR]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   checkKeyedHistogram(search_hist, 'other-MozSearch.searchbar', 1);
 
+  // Also check events.
+  let events = Services.telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
+  events = events.filter(e => e[1] == "navigation" && e[2] == "search");
+  checkEvents(events, [["navigation", "search", "searchbar", "enter", {engine: "other-MozSearch"}]]);
+
   yield BrowserTestUtils.removeTab(tab);
 });
 
 add_task(function* test_oneOff() {
   // Let's reset the counts.
   Services.telemetry.clearScalars();
+  Services.telemetry.clearEvents();
   let search_hist = getSearchCountsHistogram();
 
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
 
   info("Perform a one-off search using the first engine.");
   let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
   yield searchInSearchbar("query");
 
@@ -125,22 +132,28 @@ add_task(function* test_oneOff() {
     Services.telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
   checkKeyedScalar(scalars, SCALAR_SEARCHBAR, "search_oneoff", 1);
   Assert.equal(Object.keys(scalars[SCALAR_SEARCHBAR]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   checkKeyedHistogram(search_hist, 'other-MozSearch2.searchbar', 1);
 
+  // Also check events.
+  let events = Services.telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
+  events = events.filter(e => e[1] == "navigation" && e[2] == "search");
+  checkEvents(events, [["navigation", "search", "searchbar", "oneoff", {engine: "other-MozSearch2"}]]);
+
   yield BrowserTestUtils.removeTab(tab);
 });
 
 add_task(function* test_suggestion() {
   // Let's reset the counts.
   Services.telemetry.clearScalars();
+  Services.telemetry.clearEvents();
   let search_hist = getSearchCountsHistogram();
 
   // Create an engine to generate search suggestions and add it as default
   // for this test.
   const url = getRootDirectory(gTestPath) + "usageTelemetrySearchSuggestions.xml";
   let suggestionEngine = yield new Promise((resolve, reject) => {
     Services.search.addEngine(url, null, "", false, {
       onSuccess(engine) { resolve(engine) },
@@ -163,14 +176,20 @@ add_task(function* test_suggestion() {
   // Check if the scalars contain the expected values.
   const scalars =
     Services.telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
   checkKeyedScalar(scalars, SCALAR_SEARCHBAR, "search_suggestion", 1);
   Assert.equal(Object.keys(scalars[SCALAR_SEARCHBAR]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
-  checkKeyedHistogram(search_hist, 'other-' + suggestionEngine.name + '.searchbar', 1);
+  let searchEngineId = 'other-' + suggestionEngine.name;
+  checkKeyedHistogram(search_hist, searchEngineId + '.searchbar', 1);
+
+  // Also check events.
+  let events = Services.telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
+  events = events.filter(e => e[1] == "navigation" && e[2] == "search");
+  checkEvents(events, [["navigation", "search", "searchbar", "suggestion", {engine: searchEngineId}]]);
 
   Services.search.currentEngine = previousEngine;
   Services.search.removeEngine(suggestionEngine);
   yield BrowserTestUtils.removeTab(tab);
 });
--- a/browser/modules/test/browser_UsageTelemetry_urlbar.js
+++ b/browser/modules/test/browser_UsageTelemetry_urlbar.js
@@ -69,16 +69,17 @@ add_task(function* setup() {
     Services.prefs.clearUserPref(SUGGEST_URLBAR_PREF, true);
     Services.prefs.clearUserPref(ONEOFF_URLBAR_PREF);
   });
 });
 
 add_task(function* test_simpleQuery() {
   // Let's reset the counts.
   Services.telemetry.clearScalars();
+  Services.telemetry.clearEvents();
   let search_hist = getSearchCountsHistogram();
 
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
 
   info("Simulate entering a simple search.");
   let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
   yield searchInAwesomebar("simple query");
   EventUtils.sendKey("return");
@@ -89,22 +90,28 @@ add_task(function* test_simpleQuery() {
     Services.telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
   checkKeyedScalar(scalars, SCALAR_URLBAR, "search_enter", 1);
   Assert.equal(Object.keys(scalars[SCALAR_URLBAR]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   checkKeyedHistogram(search_hist, 'other-MozSearch.urlbar', 1);
 
+  // Also check events.
+  let events = Services.telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
+  events = events.filter(e => e[1] == "navigation" && e[2] == "search");
+  checkEvents(events, [["navigation", "search", "urlbar", "enter", {engine: "other-MozSearch"}]]);
+
   yield BrowserTestUtils.removeTab(tab);
 });
 
 add_task(function* test_searchAlias() {
   // Let's reset the counts.
   Services.telemetry.clearScalars();
+  Services.telemetry.clearEvents();
   let search_hist = getSearchCountsHistogram();
 
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
 
   info("Search using a search alias.");
   let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
   yield searchInAwesomebar("mozalias query");
   EventUtils.sendKey("return");
@@ -115,22 +122,28 @@ add_task(function* test_searchAlias() {
     Services.telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
   checkKeyedScalar(scalars, SCALAR_URLBAR, "search_alias", 1);
   Assert.equal(Object.keys(scalars[SCALAR_URLBAR]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   checkKeyedHistogram(search_hist, 'other-MozSearch.urlbar', 1);
 
+  // Also check events.
+  let events = Services.telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
+  events = events.filter(e => e[1] == "navigation" && e[2] == "search");
+  checkEvents(events, [["navigation", "search", "urlbar", "alias", {engine: "other-MozSearch"}]]);
+
   yield BrowserTestUtils.removeTab(tab);
 });
 
 add_task(function* test_oneOff() {
   // Let's reset the counts.
   Services.telemetry.clearScalars();
+  Services.telemetry.clearEvents();
   let search_hist = getSearchCountsHistogram();
 
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
 
   info("Perform a one-off search using the first engine.");
   let p = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
   yield searchInAwesomebar("query");
 
@@ -144,22 +157,28 @@ add_task(function* test_oneOff() {
     Services.telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
   checkKeyedScalar(scalars, SCALAR_URLBAR, "search_oneoff", 1);
   Assert.equal(Object.keys(scalars[SCALAR_URLBAR]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   checkKeyedHistogram(search_hist, 'other-MozSearch.urlbar', 1);
 
+  // Also check events.
+  let events = Services.telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
+  events = events.filter(e => e[1] == "navigation" && e[2] == "search");
+  checkEvents(events, [["navigation", "search", "urlbar", "oneoff", {engine: "other-MozSearch"}]]);
+
   yield BrowserTestUtils.removeTab(tab);
 });
 
 add_task(function* test_suggestion() {
   // Let's reset the counts.
   Services.telemetry.clearScalars();
+  Services.telemetry.clearEvents();
   let search_hist = getSearchCountsHistogram();
 
   // Create an engine to generate search suggestions and add it as default
   // for this test.
   const url = getRootDirectory(gTestPath) + "usageTelemetrySearchSuggestions.xml";
   let suggestionEngine = yield new Promise((resolve, reject) => {
     Services.search.addEngine(url, null, "", false, {
       onSuccess(engine) { resolve(engine) },
@@ -182,14 +201,20 @@ add_task(function* test_suggestion() {
   // Check if the scalars contain the expected values.
   const scalars =
     Services.telemetry.snapshotKeyedScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
   checkKeyedScalar(scalars, SCALAR_URLBAR, "search_suggestion", 1);
   Assert.equal(Object.keys(scalars[SCALAR_URLBAR]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
-  checkKeyedHistogram(search_hist, 'other-' + suggestionEngine.name + '.urlbar', 1);
+  let searchEngineId = 'other-' + suggestionEngine.name;
+  checkKeyedHistogram(search_hist, searchEngineId + '.urlbar', 1);
+
+  // Also check events.
+  let events = Services.telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
+  events = events.filter(e => e[1] == "navigation" && e[2] == "search");
+  checkEvents(events, [["navigation", "search", "urlbar", "suggestion", {engine: searchEngineId}]]);
 
   Services.search.currentEngine = previousEngine;
   Services.search.removeEngine(suggestionEngine);
   yield BrowserTestUtils.removeTab(tab);
 });
--- a/browser/modules/test/head.js
+++ b/browser/modules/test/head.js
@@ -90,8 +90,24 @@ function getSearchCountsHistogram() {
 /**
  * Check that the keyed histogram contains the right value.
  */
 function checkKeyedHistogram(h, key, expectedValue) {
   const snapshot = h.snapshot();
   Assert.ok(key in snapshot, `The histogram must contain ${key}.`);
   Assert.equal(snapshot[key].sum, expectedValue, `The key ${key} must contain ${expectedValue}.`);
 }
+
+function checkEvents(events, expectedEvents) {
+  if (!Services.telemetry.canRecordExtended) {
+    // Currently we only collect the tested events when extended Telemetry is enabled.
+    return;
+  }
+
+  Assert.equal(events.length, expectedEvents.length, "Should have matching amount of events.");
+
+  // Strip timestamps from the events for easier comparison.
+  events = events.map(e => e.slice(1));
+
+  for (let i = 0; i < events.length; ++i) {
+    Assert.deepEqual(events[i], expectedEvents[i], "Events should match.");
+  }
+}
--- a/build/autoconf/clang-plugin.m4
+++ b/build/autoconf/clang-plugin.m4
@@ -51,17 +51,17 @@ if test -n "$ENABLE_CLANG_PLUGIN"; then
         dnl binary. In order to do this, we need to pass -flat_namespace and
         dnl -undefined suppress to the linker. This makes sure that we link the
         dnl symbols into the flat namespace provided by clang, and thus get
         dnl access to all of the symbols which are undefined in our dylib as we
         dnl are building it right now, and also that we don't fail the build
         dnl due to undefined symbols (which will be provided by clang).
         CLANG_LDFLAGS="-Wl,-flat_namespace -Wl,-undefined,suppress -lclangASTMatchers"
     elif test "${HOST_OS_ARCH}" = "WINNT"; then
-        CLANG_LDFLAGS="clangASTMatchers.lib"
+        CLANG_LDFLAGS="clang.lib"
     else
         CLANG_LDFLAGS="-lclangASTMatchers"
     fi
 
     if test -n "$CLANG_CL"; then
         dnl The llvm-config coming with clang-cl may give us arguments in the
         dnl /ARG form, which in msys will be interpreted as a path name.  So we
         dnl need to split the args and convert the leading slashes that we find
--- a/build/build-clang/build-clang.py
+++ b/build/build-clang/build-clang.py
@@ -200,16 +200,18 @@ def build_one_stage(cc, cxx, src_dir, st
                   "-DCMAKE_BUILD_TYPE=%s" % build_type,
                   "-DLLVM_TARGETS_TO_BUILD=X86;ARM",
                   "-DLLVM_ENABLE_ASSERTIONS=%s" % ("ON" if assertions else "OFF"),
                   "-DPYTHON_EXECUTABLE=%s" % slashify_path(python_path),
                   "-DCMAKE_INSTALL_PREFIX=%s" % inst_dir,
                   "-DLLVM_TOOL_LIBCXX_BUILD=%s" % ("ON" if build_libcxx else "OFF"),
                   "-DLIBCXX_LIBCPPABI_VERSION=\"\"",
                   src_dir];
+    if is_windows():
+        cmake_args.insert(-1, "-DLLVM_EXPORT_SYMBOLS_FOR_PLUGINS=ON")
     build_package(build_dir, run_cmake, cmake_args)
 
     if is_linux():
         install_libgcc(gcc_dir, inst_dir)
 
 
 def get_compiler(config, key):
     if key not in config:
--- a/build/clang-plugin/clang-plugin.cpp
+++ b/build/clang-plugin/clang-plugin.cpp
@@ -2232,12 +2232,8 @@ public:
                  const std::vector<std::string> &Args) override {
     return true;
   }
 };
 }
 
 static FrontendPluginRegistry::Add<MozCheckAction> X("moz-check",
                                                      "check moz action");
-// Export the registry on Windows.
-#ifdef LLVM_EXPORT_REGISTRY
-LLVM_EXPORT_REGISTRY(FrontendPluginRegistry)
-#endif
--- a/build/clang-plugin/tests/Makefile.in
+++ b/build/clang-plugin/tests/Makefile.in
@@ -1,16 +1,16 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # Build without any warning flags, and with clang verify flag for a
 # syntax-only build (no codegen), without a limit on the number of errors.
-OS_CFLAGS := $(filter-out -W%,$(OS_CFLAGS)) -fsyntax-only -Xclang -verify -ferror-limit=0 -std=c11
-OS_CXXFLAGS := $(filter-out -W%,$(OS_CXXFLAGS)) -fsyntax-only -Xclang -verify -ferror-limit=0
+OS_CFLAGS := $(filter-out -W%,$(OS_CFLAGS)) -fsyntax-only -Xclang -verify -ferror-limit=0 -std=c11 -Wno-invalid-noreturn
+OS_CXXFLAGS := $(filter-out -W%,$(OS_CXXFLAGS)) -fsyntax-only -Xclang -verify -ferror-limit=0 -Wno-invalid-noreturn
 
 include $(topsrcdir)/config/rules.mk
 
 target:: $(OBJS)
 
 # We don't actually build anything.
 .PHONY: $(OBJS)
 
--- a/build/clang-plugin/tests/TestCustomHeap.cpp
+++ b/build/clang-plugin/tests/TestCustomHeap.cpp
@@ -16,13 +16,12 @@ void *operator new(size_t x, int qual) M
 }
 
 template <typename T>
 T *customAlloc() MOZ_HEAP_ALLOCATOR {
   T *arg =  static_cast<T*>(malloc(sizeof(T)));
   return new (arg) T();
 }
 
-template <typename T>
-void misuseX(T q) {
+void misuseX() {
   X *foo = customAlloc<X>(); // expected-error {{variable of type 'X' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}}
   X *foo2 = new (100) X(); // expected-error {{variable of type 'X' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}}
 }
--- a/config/static-checking-config.mk
+++ b/config/static-checking-config.mk
@@ -1,12 +1,20 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # The entire tree should be subject to static analysis using the XPCOM
 # script. Additional scripts may be added by specific subdirectories.
 
 ifdef ENABLE_CLANG_PLUGIN
+# Replace "clang-cl.exe" to "clang.exe --driver-mode=cl" to avoid loading the
+# module clang.exe again when load the plugin dll, which links to the import
+# library of clang.exe.
+ifeq ($(OS_ARCH),WINNT)
+CC := $(subst clang-cl.exe,clang.exe --driver-mode=cl,$(CC:.EXE=.exe))
+CXX := $(subst clang-cl.exe,clang.exe --driver-mode=cl,$(CXX:.EXE=.exe))
+endif
+
 CLANG_PLUGIN := $(topobjdir)/build/clang-plugin/$(DLL_PREFIX)clang-plugin$(DLL_SUFFIX)
 OS_CXXFLAGS += -Xclang -load -Xclang $(CLANG_PLUGIN) -Xclang -add-plugin -Xclang moz-check
 OS_CFLAGS += -Xclang -load -Xclang $(CLANG_PLUGIN) -Xclang -add-plugin -Xclang moz-check
 endif
--- a/dom/events/test/mochitest.ini
+++ b/dom/events/test/mochitest.ini
@@ -119,17 +119,16 @@ skip-if = toolkit == 'android' #CRASH_DU
 [test_bug864040.html]
 [test_bug924087.html]
 [test_bug930374-content.html]
 [test_bug944011.html]
 [test_bug944847.html]
 [test_bug946632.html]
 skip-if = (e10s && os == "mac") # bug 1252273
 [test_bug967796.html]
-skip-if = e10s # bug 1251659
 [test_bug985988.html]
 [test_bug998809.html]
 [test_bug1003432.html]
 support-files = test_bug1003432.js
 [test_bug1013412.html]
 [test_bug1017086_disable.html]
 support-files = bug1017086_inner.html
 [test_bug1017086_enable.html]
--- a/dom/events/test/test_bug967796.html
+++ b/dom/events/test/test_bug967796.html
@@ -14,21 +14,25 @@ https://bugzilla.mozilla.org/show_bug.cg
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 967796 **/
-
-SpecialPowers.setBoolPref("dom.w3c_pointer_events.enabled", true);      // Enable Pointer Events
-
 SimpleTest.waitForExplicitFinish();
-SimpleTest.waitForFocus(runTests);
+SimpleTest.waitForFocus(() => {
+  // Enable Pointer Events
+  SpecialPowers.pushPrefEnv({
+    "set": [
+      ["dom.w3c_pointer_events.enabled", true]
+    ]
+  }, runTests);
+});
 var outer;
 var middle;
 var inner;
 var outside;
 var container;
 var file;
 var iframe;
 var checkRelatedTarget = false;
@@ -49,17 +53,24 @@ var expectedPointerLeaveTargets = [];
 
 function runTests() {
   outer = document.getElementById("outertest");
   middle = document.getElementById("middletest");
   inner = document.getElementById("innertest");
   outside = document.getElementById("outside");
   container = document.getElementById("container");
   file = document.getElementById("file");
-  iframe = document.getElementById("iframe");
+  iframe = document.createElement('iframe');
+  iframe.width=50;
+  iframe.height=50;
+  container.appendChild(iframe);
+  iframe.addEventListener("pointerenter", penter, false);
+  iframe.addEventListener("pointerleave", pleave, false);
+  iframe.addEventListener("pointerout", pout, false);
+  iframe.addEventListener("pointerover", pover, false);
 
   // Make sure ESM thinks pointer is outside the test elements.
   sendPointerEvent("pointermove", outside);
 
   pointerentercount = 0;
   pointerleavecount = 0;
   pointerovercount = 0;
   pointeroutcount = 0;
@@ -121,22 +132,26 @@ function runTests() {
   iframe.contentDocument.body.lastChild.onpointerenter = penter;
   iframe.contentDocument.body.lastChild.onpointerleave = pleave;
   r = iframe.getBoundingClientRect();
   expectedRelatedEnter = outside;
   expectedRelatedLeave = iframe;
   // Move pointer inside the iframe.
   synthesizePointer(iframe.contentDocument.body, r.width / 2, r.height / 4, {type: "pointermove"},
                     iframe.contentWindow);
+  is(pointerentercount, 6, "Unexpected pointerenter event count!");
+  is(pointerleavecount, 4, "Unexpected pointerleave event count!");
   synthesizePointer(iframe.contentDocument.body, r.width / 2, r.height - (r.height / 4), {type: "pointermove"},
                     iframe.contentWindow);
   is(pointerentercount, 7, "Unexpected pointerenter event count!");
+  is(pointerleavecount, 5, "Unexpected pointerleave event count!");
   expectedRelatedEnter = iframe;
   expectedRelatedLeave = outside;
   sendPointerEvent("pointermove", outside);
+  is(pointerentercount, 7, "Unexpected pointerenter event count!");
   is(pointerleavecount, 7, "Unexpected pointerleave event count!");
 
   // pointerdown must produce pointerenter event
   expectedRelatedEnter = outside;
   expectedRelatedLeave = iframe;
   // Move pointer inside the iframe.
   synthesizePointer(iframe.contentDocument.body, r.width / 2, r.height / 4, {type: "pointerdown"},
                     iframe.contentWindow);
@@ -156,18 +171,16 @@ function runTests() {
   Array.from(document.querySelectorAll('*'))
     .concat([iframe.contentDocument.body.firstChild, iframe.contentDocument.body.lastChild])
     .forEach((elt) => {
       elt.onpointerenter = null;
       elt.onpointerleave = null;
       elt.onpointerenter = null;
       elt.onpointerleave = null;
     });
-  SpecialPowers.clearUserPref("dom.w3c_pointer_events.enabled");      // Disable Pointer Events
-
   SimpleTest.finish();
 }
 
 function penter(evt) {
   ++pointerentercount;
   evt.stopPropagation();
   if (expectedPointerEnterTargets.length) {
     var t = expectedPointerEnterTargets.shift();
@@ -222,14 +235,11 @@ function pout(evt) {
       <div id="innertest" onpointerenter="penter(event)" onpointerleave="pleave(event)"
                           onpointerout="pout(event)" onpointerover="pover(event)">foo</div>
     </div>
   </div>
   <input type="file" id="file"
          onpointerenter="penter(event)" onpointerleave="pleave(event)"
          onpointerout="pout(event)" onpointerover="pover(event)">
   <br>
-  <iframe id="iframe" width="50px" height="50px"
-          onpointerenter="penter(event)" onpointerleave="pleave(event)"
-          onpointerout="pout(event)" onpointerover="pover(event)"></iframe>
 </div>
 </body>
 </html>
--- a/dom/file/FileReader.cpp
+++ b/dom/file/FileReader.cpp
@@ -659,24 +659,24 @@ FileReader::OnLoadEnd(nsresult aStatus)
   // Dispatch event to signify end of a successful operation
   DispatchProgressEvent(successEvent);
   DispatchProgressEvent(termEvent);
 
   return NS_OK;
 }
 
 void
-FileReader::Abort(ErrorResult& aRv)
+FileReader::Abort()
 {
-  if (mReadyState != LOADING) {
-    // XXX The spec doesn't say this
-    aRv.Throw(NS_ERROR_DOM_FILE_ABORT_ERR);
+  if (mReadyState == EMPTY || mReadyState == DONE) {
     return;
   }
 
+  MOZ_ASSERT(mReadyState == LOADING);
+
   ClearProgressEventTimer();
 
   mReadyState = DONE;
 
   // XXX The spec doesn't say this
   mError = new DOMError(GetOwner(), NS_LITERAL_STRING("AbortError"));
 
   // Revert status and result attributes
--- a/dom/file/FileReader.h
+++ b/dom/file/FileReader.h
@@ -74,17 +74,17 @@ public:
     ReadFileContent(aBlob, aLabel, FILE_AS_TEXT, aRv);
   }
 
   void ReadAsDataURL(Blob& aBlob, ErrorResult& aRv)
   {
     ReadFileContent(aBlob, EmptyString(), FILE_AS_DATAURL, aRv);
   }
 
-  void Abort(ErrorResult& aRv);
+  void Abort();
 
   uint16_t ReadyState() const
   {
     return static_cast<uint16_t>(mReadyState);
   }
 
   DOMError* GetError() const
   {
--- a/dom/file/tests/test_fileapi.html
+++ b/dom/file/tests/test_fileapi.html
@@ -334,17 +334,17 @@ function onFilesOpened(message) {
   r.onerror = function() { ok(false, "error should not fire for aborted reads") };
   r.onprogress = function() { ok(false, "progress should not fire for aborted reads") };
   var abortThrew = false;
   try {
     r.abort();
   } catch(e) {
     abortThrew = true;
   }
-  is(abortThrew, true, "abort() must throw if not loading");
+  is(abortThrew, false, "abort() doesn't throw");
   is(abortHasRun, false, "abort() is a no-op unless loading");
   r.readAsText(asciiFile);
   r.abort();
   is(abortHasRun, true, "abort should fire sync");
   is(loadEndHasRun, true, "loadend should fire sync");
 
   // Test calling readAsX to cause abort()
   var reuseAbortHasRun = false;
@@ -359,17 +359,17 @@ function onFilesOpened(message) {
   r.onload = function() { ok(false, "load should fire for nested reads"); };
 
   var abortThrew = false;
   try {
     r.abort();
   } catch(e) {
     abortThrew = true;
   }
-  is(abortThrew, true, "abort() must throw if not loading");
+  is(abortThrew, false, "abort() should not throw");
   is(reuseAbortHasRun, false, "abort() is a no-op unless loading");
   r.readAsText(asciiFile);
 
   var readThrew = false;
   try {
     r.readAsText(asciiFile);
   } catch(e) {
     readThrew = true;
--- a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp
@@ -305,16 +305,23 @@ FFmpegVideoDecoder<LIBAV_VER>::DoDecode(
     switch (mLib->av_frame_get_colorspace(mFrame)) {
       case AVCOL_SPC_BT709:
         b.mYUVColorSpace = YUVColorSpace::BT709;
         break;
       case AVCOL_SPC_SMPTE170M:
       case AVCOL_SPC_BT470BG:
         b.mYUVColorSpace = YUVColorSpace::BT601;
         break;
+      case AVCOL_SPC_UNSPECIFIED:
+#if LIBAVCODEC_VERSION_MAJOR >= 55
+        if (mCodecContext->codec_id == AV_CODEC_ID_VP9) {
+          b.mYUVColorSpace = YUVColorSpace::BT709;
+        }
+#endif
+        break;
       default:
         break;
     }
   }
   RefPtr<VideoData> v =
     VideoData::CreateAndCopyData(mInfo,
                                   mImageContainer,
                                   aSample->mOffset,
--- a/dom/webidl/FileReader.webidl
+++ b/dom/webidl/FileReader.webidl
@@ -16,17 +16,16 @@ interface FileReader : EventTarget {
   // async read methods
   [Throws]
   void readAsArrayBuffer(Blob blob);
   [Throws]
   void readAsText(Blob blob, optional DOMString label = "");
   [Throws]
   void readAsDataURL(Blob blob);
 
-  [Throws]
   void abort();
 
   // states
   const unsigned short EMPTY = 0;
   const unsigned short LOADING = 1;
   const unsigned short DONE = 2;
 
 
--- a/dom/workers/test/worker_fileReader.js
+++ b/dom/workers/test/worker_fileReader.js
@@ -295,17 +295,17 @@ onmessage = function(message) {
   r.onerror = function() { ok(false, "error should not fire for aborted reads") };
   r.onprogress = function() { ok(false, "progress should not fire for aborted reads") };
   var abortThrew = false;
   try {
     r.abort();
   } catch(e) {
     abortThrew = true;
   }
-  is(abortThrew, true, "abort() must throw if not loading");
+  is(abortThrew, false, "abort() never throws");
   is(abortHasRun, false, "abort() is a no-op unless loading");
   r.readAsText(asciiFile);
   r.abort();
   is(abortHasRun, true, "1 abort should fire sync");
   is(loadEndHasRun, true, "loadend should fire sync");
 
   // Test calling readAsX to cause abort()
   var reuseAbortHasRun = false;
@@ -313,17 +313,17 @@ onmessage = function(message) {
   r.onabort = function (event) { reuseAbortHasRun = true; }
   r.onload = function() { ok(true, "load should fire for aborted reads") };
   var abortThrew = false;
   try {
     r.abort();
   } catch(e) {
     abortThrew = true;
   }
-  is(abortThrew, true, "abort() must throw if not loading");
+  is(abortThrew, false, "abort() never throws");
   is(reuseAbortHasRun, false, "abort() is a no-op unless loading");
   r.readAsText(asciiFile);
 
   var readThrew = false;
   try {
   r.readAsText(asciiFile);
   } catch(e) {
     readThrew = true;
--- a/layout/forms/nsButtonFrameRenderer.cpp
+++ b/layout/forms/nsButtonFrameRenderer.cpp
@@ -31,19 +31,16 @@ nsButtonFrameRenderer::nsButtonFrameRend
 nsButtonFrameRenderer::~nsButtonFrameRenderer()
 {
   MOZ_COUNT_DTOR(nsButtonFrameRenderer);
 
 #ifdef DEBUG
   if (mInnerFocusStyle) {
     mInnerFocusStyle->FrameRelease();
   }
-  if (mOuterFocusStyle) {
-    mOuterFocusStyle->FrameRelease();
-  }
 #endif
 }
 
 void
 nsButtonFrameRenderer::SetFrame(nsFrame* aFrame, nsPresContext* aPresContext)
 {
   mFrame = aFrame;
   ReResolveStyles(aPresContext);
@@ -239,20 +236,19 @@ void nsDisplayButtonForeground::Paint(ns
                                       nsRenderingContext* aCtx)
 {
   nsPresContext *presContext = mFrame->PresContext();
   const nsStyleDisplay *disp = mFrame->StyleDisplay();
   if (!mFrame->IsThemed(disp) ||
       !presContext->GetTheme()->ThemeDrawsFocusForWidget(disp->mAppearance)) {
     nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize());
 
-    // Draw the focus and outline borders.
+    // Draw the -moz-focus-inner border
     DrawResult result =
-      mBFR->PaintOutlineAndFocusBorders(aBuilder, presContext, *aCtx,
-                                        mVisibleRect, r);
+      mBFR->PaintInnerFocusBorder(aBuilder, presContext, *aCtx, mVisibleRect, r);
 
     nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
   }
 }
 
 nsresult
 nsButtonFrameRenderer::DisplayButton(nsDisplayListBuilder* aBuilder,
                                      nsDisplayList* aBackground,
@@ -269,60 +265,56 @@ nsButtonFrameRenderer::DisplayButton(nsD
   nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
     aBuilder, mFrame, buttonRect, aBackground);
 
   aBackground->AppendNewToTop(new (aBuilder)
     nsDisplayButtonBorder(aBuilder, this));
 
   // Only display focus rings if we actually have them. Since at most one
   // button would normally display a focus ring, most buttons won't have them.
-  if ((mOuterFocusStyle && mOuterFocusStyle->StyleBorder()->HasBorder()) ||
-      (mInnerFocusStyle && mInnerFocusStyle->StyleBorder()->HasBorder())) {
+  if (mInnerFocusStyle && mInnerFocusStyle->StyleBorder()->HasBorder()) {
     aForeground->AppendNewToTop(new (aBuilder)
       nsDisplayButtonForeground(aBuilder, this));
   }
   return NS_OK;
 }
 
+void
+nsButtonFrameRenderer::GetButtonInnerFocusRect(const nsRect& aRect, nsRect& aResult)
+{
+  GetButtonRect(aRect, aResult);
+  aResult.Deflate(mFrame->GetUsedBorderAndPadding());
+
+  nsMargin innerFocusPadding(0,0,0,0);
+  if (mInnerFocusStyle) {
+    mInnerFocusStyle->StylePadding()->GetPadding(innerFocusPadding);
+  }
+  aResult.Inflate(innerFocusPadding);
+}
+
 DrawResult
-nsButtonFrameRenderer::PaintOutlineAndFocusBorders(
+nsButtonFrameRenderer::PaintInnerFocusBorder(
   nsDisplayListBuilder* aBuilder,
   nsPresContext* aPresContext,
   nsRenderingContext& aRenderingContext,
   const nsRect& aDirtyRect,
   const nsRect& aRect)
 {
-  // once we have all that we'll draw the focus if we have it. We will
-  // need to draw 2 focuses, the inner and the outer. This is so we
-  // can do any kind of look and feel. Some buttons have focus on the
-  // outside like mac and motif. While others like windows have it
-  // inside (dotted line).  Usually only one will be specifed. But I
-  // guess you could have both if you wanted to.
+  // we draw the -moz-focus-inner border just inside the button's
+  // normal border and padding, to match Windows themes.
 
   nsRect rect;
 
   PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages()
                          ? PaintBorderFlags::SYNC_DECODE_IMAGES
                          : PaintBorderFlags();
 
   DrawResult result = DrawResult::SUCCESS;
 
-  if (mOuterFocusStyle) {
-    // ---------- paint the outer focus border -------------
-
-    GetButtonOuterFocusRect(aRect, rect);
-
-    result &=
-      nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame,
-                                  aDirtyRect, rect, mOuterFocusStyle, flags);
-  }
-
   if (mInnerFocusStyle) { 
-    // ---------- paint the inner focus border -------------
-
     GetButtonInnerFocusRect(aRect, rect);
 
     result &=
       nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame,
                                   aDirtyRect, rect, mInnerFocusStyle, flags);
   }
 
   return result;
@@ -353,142 +345,57 @@ nsButtonFrameRenderer::PaintBorder(
     nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame,
                                 aDirtyRect, buttonRect, context, borderFlags);
 
   return result;
 }
 
 
 void
-nsButtonFrameRenderer::GetButtonOuterFocusRect(const nsRect& aRect, nsRect& focusRect)
-{
-  focusRect = aRect;
-}
-
-void
 nsButtonFrameRenderer::GetButtonRect(const nsRect& aRect, nsRect& r)
 {
   r = aRect;
-  r.Deflate(GetButtonOuterFocusBorderAndPadding());
-}
-
-
-void
-nsButtonFrameRenderer::GetButtonInnerFocusRect(const nsRect& aRect, nsRect& focusRect)
-{
-  GetButtonRect(aRect, focusRect);
-  focusRect.Deflate(GetButtonBorderAndPadding());
-  focusRect.Deflate(GetButtonInnerFocusMargin());
-}
-
-
-nsMargin
-nsButtonFrameRenderer::GetButtonOuterFocusBorderAndPadding()
-{
-  nsMargin result(0,0,0,0);
-
-  if (mOuterFocusStyle) {
-    mOuterFocusStyle->StylePadding()->GetPadding(result);
-    result += mOuterFocusStyle->StyleBorder()->GetComputedBorder();
-  }
-
-  return result;
-}
-
-nsMargin
-nsButtonFrameRenderer::GetButtonBorderAndPadding()
-{
-  return mFrame->GetUsedBorderAndPadding();
 }
 
-/**
- * Gets the size of the buttons border this is the union of the normal and disabled borders.
- */
-nsMargin
-nsButtonFrameRenderer::GetButtonInnerFocusMargin()
-{
-  nsMargin innerFocusMargin(0,0,0,0);
-
-  if (mInnerFocusStyle) {
-    const nsStyleMargin* margin = mInnerFocusStyle->StyleMargin();
-    margin->GetMargin(innerFocusMargin);
-  }
-
-  return innerFocusMargin;
-}
-
-nsMargin
-nsButtonFrameRenderer::GetButtonInnerFocusBorderAndPadding()
-{
-  nsMargin result(0,0,0,0);
-
-  if (mInnerFocusStyle) {
-    mInnerFocusStyle->StylePadding()->GetPadding(result);
-    result += mInnerFocusStyle->StyleBorder()->GetComputedBorder();
-  }
-
-  return result;
-}
-
-// gets all the focus borders and padding that will be added to the regular border
-nsMargin
-nsButtonFrameRenderer::GetAddedButtonBorderAndPadding()
-{
-  return GetButtonOuterFocusBorderAndPadding() + GetButtonInnerFocusMargin() + GetButtonInnerFocusBorderAndPadding();
-}
 
 /**
  * Call this when styles change
  */
 void
 nsButtonFrameRenderer::ReResolveStyles(nsPresContext* aPresContext)
 {
   // get all the styles
   nsStyleContext* context = mFrame->StyleContext();
   StyleSetHandle styleSet = aPresContext->StyleSet();
 
 #ifdef DEBUG
   if (mInnerFocusStyle) {
     mInnerFocusStyle->FrameRelease();
   }
-  if (mOuterFocusStyle) {
-    mOuterFocusStyle->FrameRelease();
-  }
 #endif
 
-  // style for the inner such as a dotted line (Windows)
+  // get styles assigned to -moz-inner-focus (ie dotted border on Windows)
   mInnerFocusStyle =
     styleSet->ProbePseudoElementStyle(mFrame->GetContent()->AsElement(),
                                       CSSPseudoElementType::mozFocusInner,
                                       context);
 
-  // style for outer focus like a ridged border (MAC).
-  mOuterFocusStyle =
-    styleSet->ProbePseudoElementStyle(mFrame->GetContent()->AsElement(),
-                                      CSSPseudoElementType::mozFocusOuter,
-                                      context);
-
 #ifdef DEBUG
   if (mInnerFocusStyle) {
     mInnerFocusStyle->FrameAddRef();
   }
-  if (mOuterFocusStyle) {
-    mOuterFocusStyle->FrameAddRef();
-  }
 #endif
 }
 
 nsStyleContext*
 nsButtonFrameRenderer::GetStyleContext(int32_t aIndex) const
 {
   switch (aIndex) {
   case NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX:
     return mInnerFocusStyle;
-  case NS_BUTTON_RENDERER_FOCUS_OUTER_CONTEXT_INDEX:
-    return mOuterFocusStyle;
   default:
     return nullptr;
   }
 }
 
 void 
 nsButtonFrameRenderer::SetStyleContext(int32_t aIndex, nsStyleContext* aStyleContext)
 {
@@ -496,21 +403,13 @@ nsButtonFrameRenderer::SetStyleContext(i
   case NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX:
 #ifdef DEBUG
     if (mInnerFocusStyle) {
       mInnerFocusStyle->FrameRelease();
     }
 #endif
     mInnerFocusStyle = aStyleContext;
     break;
-  case NS_BUTTON_RENDERER_FOCUS_OUTER_CONTEXT_INDEX:
-#ifdef DEBUG
-    if (mOuterFocusStyle) {
-      mOuterFocusStyle->FrameRelease();
-    }
-#endif
-    mOuterFocusStyle = aStyleContext;
-    break;
   }
 #ifdef DEBUG
   aStyleContext->FrameAddRef();
 #endif
 }
--- a/layout/forms/nsButtonFrameRenderer.h
+++ b/layout/forms/nsButtonFrameRenderer.h
@@ -15,74 +15,66 @@ class nsDisplayList;
 class nsDisplayListBuilder;
 class nsPresContext;
 class nsRenderingContext;
 struct nsRect;
 class nsStyleContext;
 
 
 #define NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX  0
-#define NS_BUTTON_RENDERER_FOCUS_OUTER_CONTEXT_INDEX  1
-#define NS_BUTTON_RENDERER_LAST_CONTEXT_INDEX   NS_BUTTON_RENDERER_FOCUS_OUTER_CONTEXT_INDEX
+#define NS_BUTTON_RENDERER_LAST_CONTEXT_INDEX   NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX
 
 class nsButtonFrameRenderer {
   typedef mozilla::image::DrawResult DrawResult;
 
 public:
 
   nsButtonFrameRenderer();
   ~nsButtonFrameRenderer();
 
   /**
    * Create display list items for the button
    */
   nsresult DisplayButton(nsDisplayListBuilder* aBuilder,
                          nsDisplayList* aBackground, nsDisplayList* aForeground);
 
 
-  DrawResult PaintOutlineAndFocusBorders(nsDisplayListBuilder* aBuilder,
-                                         nsPresContext* aPresContext,
-                                         nsRenderingContext& aRenderingContext,
-                                         const nsRect& aDirtyRect,
-                                         const nsRect& aRect);
+  DrawResult PaintInnerFocusBorder(nsDisplayListBuilder* aBuilder,
+                                   nsPresContext* aPresContext,
+                                   nsRenderingContext& aRenderingContext,
+                                   const nsRect& aDirtyRect,
+                                   const nsRect& aRect);
 
   DrawResult PaintBorder(nsDisplayListBuilder* aBuilder,
                          nsPresContext* aPresContext,
                          nsRenderingContext& aRenderingContext,
                          const nsRect& aDirtyRect,
                          const nsRect& aRect);
 
   void SetFrame(nsFrame* aFrame, nsPresContext* aPresContext);
  
   void SetDisabled(bool aDisabled, bool notify);
 
   bool isActive();
   bool isDisabled();
 
-  void GetButtonOuterFocusRect(const nsRect& aRect, nsRect& aResult);
   void GetButtonRect(const nsRect& aRect, nsRect& aResult);
   void GetButtonInnerFocusRect(const nsRect& aRect, nsRect& aResult);
-  nsMargin GetButtonOuterFocusBorderAndPadding();
-  nsMargin GetButtonBorderAndPadding();
-  nsMargin GetButtonInnerFocusMargin();
-  nsMargin GetButtonInnerFocusBorderAndPadding();
-  nsMargin GetAddedButtonBorderAndPadding();
 
   nsStyleContext* GetStyleContext(int32_t aIndex) const;
   void SetStyleContext(int32_t aIndex, nsStyleContext* aStyleContext);
   void ReResolveStyles(nsPresContext* aPresContext);
 
   nsIFrame* GetFrame();
 
 protected:
 
 private:
 
-  // cached styles for focus and outline.
+  // cached style for optional inner focus outline (used on Windows).
   RefPtr<nsStyleContext> mInnerFocusStyle;
-  RefPtr<nsStyleContext> mOuterFocusStyle;
 
   nsFrame* mFrame;
 };
 
 
 #endif
 
--- a/layout/forms/nsHTMLButtonControlFrame.cpp
+++ b/layout/forms/nsHTMLButtonControlFrame.cpp
@@ -152,54 +152,43 @@ nsHTMLButtonControlFrame::GetMinISize(ns
   nscoord result;
   DISPLAY_MIN_WIDTH(this, result);
 
   nsIFrame* kid = mFrames.FirstChild();
   result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
                                                 kid,
                                                 nsLayoutUtils::MIN_ISIZE);
 
-  result += GetWritingMode().IsVertical()
-    ? mRenderer.GetAddedButtonBorderAndPadding().TopBottom()
-    : mRenderer.GetAddedButtonBorderAndPadding().LeftRight();
-
   return result;
 }
 
 nscoord
 nsHTMLButtonControlFrame::GetPrefISize(nsRenderingContext* aRenderingContext)
 {
   nscoord result;
   DISPLAY_PREF_WIDTH(this, result);
   
   nsIFrame* kid = mFrames.FirstChild();
   result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
                                                 kid,
                                                 nsLayoutUtils::PREF_ISIZE);
 
-  result += GetWritingMode().IsVertical()
-    ? mRenderer.GetAddedButtonBorderAndPadding().TopBottom()
-    : mRenderer.GetAddedButtonBorderAndPadding().LeftRight();
-
   return result;
 }
 
 void
 nsHTMLButtonControlFrame::Reflow(nsPresContext* aPresContext,
                                  ReflowOutput& aDesiredSize,
                                  const ReflowInput& aReflowInput,
                                  nsReflowStatus& aStatus)
 {
   MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsHTMLButtonControlFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
 
-  NS_PRECONDITION(aReflowInput.ComputedISize() != NS_INTRINSICSIZE,
-                  "Should have real computed inline-size by now");
-
   if (mState & NS_FRAME_FIRST_REFLOW) {
     nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), true);
   }
 
   // Reflow the child
   nsIFrame* firstKid = mFrames.FirstChild();
 
   MOZ_ASSERT(firstKid, "Button should have a child frame for its contents");
@@ -232,101 +221,35 @@ nsHTMLButtonControlFrame::Reflow(nsPresC
   // We're always complete and we don't support overflow containers
   // so we shouldn't have a next-in-flow ever.
   aStatus = NS_FRAME_COMPLETE;
   MOZ_ASSERT(!GetNextInFlow());
 
   NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
 }
 
-// Helper-function that lets us clone the button's reflow state, but with its
-// ComputedWidth and ComputedHeight reduced by the amount of renderer-specific
-// focus border and padding that we're using. (This lets us provide a more
-// appropriate content-box size for descendents' percent sizes to resolve
-// against.)
-static ReflowInput
-CloneReflowInputWithReducedContentBox(
-  const ReflowInput& aButtonReflowInput,
-  const LogicalMargin& aFocusPadding)
-{
-  auto wm = aButtonReflowInput.GetWritingMode();
-  nscoord adjustedISize = aButtonReflowInput.ComputedISize();
-  adjustedISize -= aFocusPadding.IStartEnd(wm);
-  adjustedISize = std::max(0, adjustedISize);
-
-  // (Only adjust the block-size if it's an actual length.)
-  nscoord adjustedBSize = aButtonReflowInput.ComputedBSize();
-  if (adjustedBSize != NS_INTRINSICSIZE) {
-    adjustedBSize -= aFocusPadding.BStartEnd(wm);
-    adjustedBSize = std::max(0, adjustedBSize);
-  }
-
-  ReflowInput clone(aButtonReflowInput);
-  clone.SetComputedISize(adjustedISize);
-  clone.SetComputedBSize(adjustedBSize);
-
-  return clone;
-}
-
 void
 nsHTMLButtonControlFrame::ReflowButtonContents(nsPresContext* aPresContext,
                                                ReflowOutput& aButtonDesiredSize,
                                                const ReflowInput& aButtonReflowInput,
                                                nsIFrame* aFirstKid)
 {
   WritingMode wm = GetWritingMode();
   LogicalSize availSize = aButtonReflowInput.ComputedSize(wm);
   availSize.BSize(wm) = NS_INTRINSICSIZE;
 
-  // Buttons have some bonus renderer-determined border/padding,
-  // which occupies part of the button's content-box area:
-  LogicalMargin focusPadding =
-    LogicalMargin(wm, mRenderer.GetAddedButtonBorderAndPadding());
-
-  // See whether out availSize's inline-size is big enough.  If it's
-  // smaller than our intrinsic min iSize, that means that the kid
-  // wouldn't really fit.  In that case, we overflow into our internal
-  // focuspadding (which other browsers don't have) so that there's a
-  // little more space for it.
-  // Note that GetMinISize includes the focusPadding.
-  nscoord IOverflow = GetMinISize(aButtonReflowInput.mRenderingContext) -
-                      aButtonReflowInput.ComputedISize();
-  nscoord IFocusPadding = focusPadding.IStartEnd(wm);
-  nscoord focusPaddingReduction = std::min(IFocusPadding,
-                                           std::max(IOverflow, 0));
-  if (focusPaddingReduction > 0) {
-    nscoord startReduction = focusPadding.IStart(wm);
-    if (focusPaddingReduction != IFocusPadding) {
-      startReduction = NSToCoordRound(startReduction *
-                                      (float(focusPaddingReduction) /
-                                       float(IFocusPadding)));
-    }
-    focusPadding.IStart(wm) -= startReduction;
-    focusPadding.IEnd(wm) -= focusPaddingReduction - startReduction;
-  }
-
   // shorthand for a value we need to use in a bunch of places
   const LogicalMargin& clbp = aButtonReflowInput.ComputedLogicalBorderPadding();
 
-  // Indent the child inside us by the focus border. We must do this separate
-  // from the regular border.
-  availSize.ISize(wm) -= focusPadding.IStartEnd(wm);
-
   LogicalPoint childPos(wm);
-  childPos.I(wm) = focusPadding.IStart(wm) + clbp.IStart(wm);
+  childPos.I(wm) = clbp.IStart(wm);
   availSize.ISize(wm) = std::max(availSize.ISize(wm), 0);
 
-  // Give child a clone of the button's reflow state, with height/width reduced
-  // by focusPadding, so that descendants with height:100% don't protrude.
-  ReflowInput adjustedButtonReflowInput =
-    CloneReflowInputWithReducedContentBox(aButtonReflowInput, focusPadding);
-
-  ReflowInput contentsReflowInput(aPresContext,
-                                        adjustedButtonReflowInput,
-                                        aFirstKid, availSize);
+  ReflowInput contentsReflowInput(aPresContext, aButtonReflowInput,
+                                  aFirstKid, availSize);
 
   nsReflowStatus contentsReflowStatus;
   ReflowOutput contentsDesiredSize(aButtonReflowInput);
   childPos.B(wm) = 0; // This will be set properly later, after reflowing the
                       // child to determine its size.
 
   // We just pass a dummy containerSize here, as the child will be
   // repositioned later by FinishReflowChild.
@@ -340,52 +263,48 @@ nsHTMLButtonControlFrame::ReflowButtonCo
 
   // Compute the button's content-box size:
   LogicalSize buttonContentBox(wm);
   if (aButtonReflowInput.ComputedBSize() != NS_INTRINSICSIZE) {
     // Button has a fixed block-size -- that's its content-box bSize.
     buttonContentBox.BSize(wm) = aButtonReflowInput.ComputedBSize();
   } else {
     // Button is intrinsically sized -- it should shrinkwrap the
-    // button-contents' bSize, plus any focus-padding space:
-    buttonContentBox.BSize(wm) =
-      contentsDesiredSize.BSize(wm) + focusPadding.BStartEnd(wm);
+    // button-contents' bSize:
+    buttonContentBox.BSize(wm) = contentsDesiredSize.BSize(wm);
 
     // Make sure we obey min/max-bSize in the case when we're doing intrinsic
     // sizing (we get it for free when we have a non-intrinsic
     // aButtonReflowInput.ComputedBSize()).  Note that we do this before
     // adjusting for borderpadding, since mComputedMaxBSize and
     // mComputedMinBSize are content bSizes.
     buttonContentBox.BSize(wm) =
       NS_CSS_MINMAX(buttonContentBox.BSize(wm),
                     aButtonReflowInput.ComputedMinBSize(),
                     aButtonReflowInput.ComputedMaxBSize());
   }
   if (aButtonReflowInput.ComputedISize() != NS_INTRINSICSIZE) {
     buttonContentBox.ISize(wm) = aButtonReflowInput.ComputedISize();
   } else {
-    buttonContentBox.ISize(wm) =
-      contentsDesiredSize.ISize(wm) + focusPadding.IStartEnd(wm);
+    buttonContentBox.ISize(wm) = contentsDesiredSize.ISize(wm);
     buttonContentBox.ISize(wm) =
       NS_CSS_MINMAX(buttonContentBox.ISize(wm),
                     aButtonReflowInput.ComputedMinISize(),
                     aButtonReflowInput.ComputedMaxISize());
   }
 
   // Center child in the block-direction in the button
   // (technically, inside of the button's focus-padding area)
-  nscoord extraSpace =
-    buttonContentBox.BSize(wm) - focusPadding.BStartEnd(wm) -
-    contentsDesiredSize.BSize(wm);
+  nscoord extraSpace = buttonContentBox.BSize(wm) -
+                       contentsDesiredSize.BSize(wm);
 
   childPos.B(wm) = std::max(0, extraSpace / 2);
 
-  // Adjust childPos.B() to be in terms of the button's frame-rect, instead of
-  // its focus-padding rect:
-  childPos.B(wm) += focusPadding.BStart(wm) + clbp.BStart(wm);
+  // Adjust childPos.B() to be in terms of the button's frame-rect:
+  childPos.B(wm) += clbp.BStart(wm);
 
   nsSize containerSize =
     (buttonContentBox + clbp.Size(wm)).GetPhysicalSize(wm);
 
   // Place the child
   FinishReflowChild(aFirstKid, aPresContext,
                     contentsDesiredSize, &contentsReflowInput,
                     wm, childPos, containerSize, 0);
--- a/layout/reftests/bugs/491180-1-ref.html
+++ b/layout/reftests/bugs/491180-1-ref.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
 <html>
-  <div style="padding: 0; margin: 0; border: none; background: transparent; -moz-appearance: none; width: 0; height: 0; font: inherit; padding: 1px 0">
+  <div style="padding: 0; margin: 0; border: none; background: transparent; -moz-appearance: none; width: 0; height: 0; font: inherit; padding: 0">
     Some text <div style="background: green; width: 100px; height: 100px"></div>
   </div>
--- a/layout/reftests/bugs/491180-2-ref.html
+++ b/layout/reftests/bugs/491180-2-ref.html
@@ -1,4 +1,4 @@
 <!DOCTYPE html>
 <html>
-  <div style="padding: 0; margin: 0; border: none; background: transparent; -moz-appearance: none; width: 20px; font: inherit; padding: 1px 0; white-space: pre; overflow: hidden">Some text</div>
+  <div style="padding: 0; margin: 0; border: none; background: transparent; -moz-appearance: none; width: 20px; font: inherit; padding: 0; white-space: pre; overflow: hidden">Some text</div>
   </div>
--- a/layout/reftests/css-grid/grid-item-intrinsic-size-normal-001-ref.html
+++ b/layout/reftests/css-grid/grid-item-intrinsic-size-normal-001-ref.html
@@ -142,89 +142,89 @@ button {
 <div class="grid"><button class="as h20 mxw2"></button></div>
 <div class="grid"><button class="an h20"></button></div>
 <div class="grid"><button class="an h20 mxw10"></button></div>
 
 <script>
 var buttonSizes =
 [
   ['32px', '4px'],
-  ['8px', '4px'],
-  ['8px', '4px'],
-  ['8px', '4px'],
-  ['8px', '4px'],
-  ['2px', '4px'],
+  ['0px', '0px'],
+  ['0px', '4px'],
+  ['0px', '4px'],
+  ['0px', '4px'],
+  ['0px', '4px'],
   ['32px', '20px'],
   ['10px', '20px'],
-  ['8px', '20px'],
-  ['8px', '20px'],
-  ['8px', '20px'],
-  ['8px', '20px'],
-  ['8px', '20px'],
-  ['8px', '20px'],
+  ['0px', '20px'],
+  ['0px', '20px'],
+  ['0px', '20px'],
+  ['0px', '20px'],
+  ['0px', '20px'],
+  ['0px', '20px'],
   ['32px', '4px'],
   ['10px', '4px'],
-  ['8px', '4px'],
-  ['8px', '4px'],
-  ['8px', '4px'],
-  ['8px', '4px'],
-  ['8px', '4px'],
-  ['2px', '4px'],
+  ['0px', '0px'],
+  ['0px', '0px'],
+  ['0px', '4px'],
+  ['0px', '4px'],
+  ['0px', '4px'],
+  ['0px', '4px'],
   ['4px', '32px'],
   ['2px', '32px'],
-  ['4px', '4px'],
-  ['2px', '4px'],
-  ['4px', '32px'],
-  ['2px', '32px'],
-  ['4px', '32px'],
-  ['2px', '32px'],
+  ['0px', '0px'],
+  ['2px', '0px'],
+  ['0px', '32px'],
+  ['0px', '32px'],
+  ['0px', '32px'],
+  ['0px', '32px'],
   ['20px', '32px'],
   ['20px', '10px'],
-  ['20px', '4px'],
-  ['20px', '4px'],
+  ['20px', '0px'],
+  ['20px', '0px'],
   ['20px', '32px'],
   ['20px', '10px'],
   ['20px', '32px'],
   ['20px', '10px'],
   ['4px', '32px'],
   ['4px', '10px'],
-  ['8px', '4px'],
-  ['8px', '4px'],
-  ['8px', '32px'],
-  ['8px', '10px'],
-  ['8px', '32px'],
-  ['8px', '10px'],
+  ['0px', '0px'],
+  ['0px', '0px'],
+  ['0px', '32px'],
+  ['0px', '10px'],
+  ['0px', '32px'],
+  ['0px', '10px'],
   ['20px', '4px'],
   ['20px', '4px'],
-  ['20px', '4px'],
-  ['20px', '4px'],
+  ['20px', '0px'],
+  ['20px', '0px'],
   ['20px', '4px'],
   ['20px', '2px'],
   ['20px', '4px'],
   ['20px', '2px'],
-  ['20px', '4px'],
-  ['20px', '4px'],
-  ['20px', '4px'],
-  ['20px', '4px'],
+  ['20px', '0px'],
+  ['20px', '0px'],
+  ['20px', '0px'],
+  ['20px', '0px'],
   ['20px', '32px'],
   ['20px', '10px'],
   ['20px', '32px'],
   ['20px', '10px'],
   ['32px', '20px'],
   ['10px', '20px'],
-  ['8px', '20px'],
-  ['8px', '20px'],
+  ['0px', '20px'],
+  ['0px', '20px'],
   ['32px', '20px'],
   ['10px', '20px'],
   ['32px', '20px'],
   ['10px', '20px'],
   ['4px', '20px'],
   ['2px', '20px'],
-  ['4px', '20px'],
-  ['4px', '20px'],
+  ['0px', '20px'],
+  ['0px', '20px'],
   ['4px', '20px'],
   ['2px', '20px'],
   ['4px', '20px'],
   ['4px', '20px'],
 ];
 var buttons = document.querySelectorAll('button');
 for (var i = 0; i < buttons.length; ++i) {
   var button = buttons[i];
--- a/layout/reftests/css-sizing/min-intrinsic-with-percents-across-elements-ref.html
+++ b/layout/reftests/css-sizing/min-intrinsic-with-percents-across-elements-ref.html
@@ -84,24 +84,24 @@ canvas {
 </tr></table>
 
 <table><tr>
   <td><input type="button"></td>
   <td>empty input type="button", unstyled</td>
 </tr></table>
 
 <table><tr>
-  <!-- 2px border, 2px padding, 4px ::-moz-focus-inner padding, 2px ::-moz-focus-inner border -->
-  <td><div style="width: 10px"><input type="button" style="width: 5px"></div></td>
+  <!-- 2px border, 2px padding -->
+  <td><div style="width: 4px"><input type="button" style="width: 2px"></div></td>
   <td>empty input type="button", width: 50%</td>
 </tr></table>
 
 <table><tr>
-  <!-- 2px border, 2px padding, 4px ::-moz-focus-inner padding, 2px ::-moz-focus-inner border -->
-  <td><div style="width: 10px"><input type="button" style="width: 5px"></div></td>
+  <!-- 2px border, 2px padding -->
+  <td><div style="width: 4px"><input type="button" style="width: 2px"></div></td>
   <td>empty input type="button", max-width: 50%</td>
 </tr></table>
 
 <table><tr>
   <td><input type="button" value="Button"></td>
   <td>nonempty input type="button", unstyled</td>
 </tr></table>
 
deleted file mode 100644
--- a/layout/reftests/forms/button/focus-area-percent-units-1-ref.html
+++ /dev/null
@@ -1,52 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-<html>
-<head>
-  <title>Reference case for bug 1272983</title>
-  <style>
-    /* Set explicit font size so that em units are predictable: */
-    body, button { font: 10px sans-serif; }
-
-    /* Set margin to 0 for all cases. In the first 6, that's how we expect
-       the testcase to render; and in the 7th and 8th, our reference margin
-       will be applied via a child div instead of via the pseudo-element. */
-    button.mfi1::-moz-focus-inner,
-    button.mfi2::-moz-focus-inner,
-    button.mfi3::-moz-focus-inner,
-    button.mfi4::-moz-focus-inner,
-    button.mfi5::-moz-focus-inner,
-    button.mfi6::-moz-focus-inner,
-    button.mfi7::-moz-focus-inner,
-    button.mfi8::-moz-focus-inner { margin: 0; }
-
-    /* Use an explicit div instead of pseudo-element, for reference case's
-       version of margin values that we actually expect to take effect: */
-    button.mfi7 > div { margin: 10px; }
-    button.mfi8 > div { margin: 20px; /* = 2em * 20px/em */ }
-  </style>
-</head>
-<body>
-  <button class="mfi1">mfi1</button>
-  <button class="mfi2">mfi2</button>
-  <button class="mfi3">mfi3</button>
-  <button class="mfi4">mfi4</button>
-  <br>
-  <button class="mfi5">mfi5</button>
-  <button class="mfi6">mfi6</button>
-  <button class="mfi7"><div>mfi7</div></button>
-  <button class="mfi8"><div>mfi8</div></button>
-  <br>
-
-  <button class="mfo1">mfo1</button>
-  <button class="mfo2">mfo2</button>
-  <button class="mfo3">mfo3</button>
-  <button class="mfo4">mfo4</button>
-  <br>
-  <button class="mfo5">mfo5</button>
-  <button class="mfo6">mfo6</button>
-  <button class="mfo7">mfo7</button>
-  <button class="mfo8">mfo8</button>
-  <br>
-</body>
-</html>
deleted file mode 100644
--- a/layout/reftests/forms/button/focus-area-percent-units-1.html
+++ /dev/null
@@ -1,62 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-<html>
-<!-- The ::-moz-focus-inner & ::-moz-focus-outer button pseudo-elements only
-     support CSS "margin" values that contain absolute lengths.  Any percent or
-     "auto" margin values will simply make the margin collapse to zero.
-     This test verifies that this is indeed what happens (not anything worse).
--->
-<head>
-  <title>Testcase for bug 1272983</title>
-  <style>
-    /* Set explicit font size so that em units are predictable: */
-    body, button { font: 10px sans-serif; }
-
-    /* Testing percent and auto margin values on "-moz-focus-inner": */
-    button.mfi1::-moz-focus-inner { margin: 50%; }
-    button.mfi2::-moz-focus-inner { margin: 50% 10px; }
-    button.mfi3::-moz-focus-inner { margin: 10px 50%; }
-    button.mfi4::-moz-focus-inner { margin: auto; }
-    button.mfi5::-moz-focus-inner { margin: auto 10px; }
-    button.mfi6::-moz-focus-inner { margin: 10px auto; }
-    button.mfi7::-moz-focus-inner { margin: 10px; }
-    button.mfi8::-moz-focus-inner { margin: 2em; }
-
-    /* Testing percent and auto margin values on "-moz-focus-outer":
-       (just for completeness -- really, 'margin' has no effect on
-       the behavior of -moz-focus-outer) */
-    button.mfo1::-moz-focus-outer { margin: 50%; }
-    button.mfo2::-moz-focus-outer { margin: 50% 10px; }
-    button.mfo3::-moz-focus-outer { margin: 10px 50%; }
-    button.mfo4::-moz-focus-outer { margin: auto; }
-    button.mfo5::-moz-focus-outer { margin: auto 10px; }
-    button.mfo6::-moz-focus-outer { margin: 10px auto; }
-    button.mfo7::-moz-focus-outer { margin: 10px; }
-    button.mfo8::-moz-focus-outer { margin: 2em; }
-  </style>
-</head>
-<body>
-  <button class="mfi1">mfi1</button>
-  <button class="mfi2">mfi2</button>
-  <button class="mfi3">mfi3</button>
-  <button class="mfi4">mfi4</button>
-  <br>
-  <button class="mfi5">mfi5</button>
-  <button class="mfi6">mfi6</button>
-  <button class="mfi7">mfi7</button>
-  <button class="mfi8">mfi8</button>
-  <br>
-
-  <button class="mfo1">mfo1</button>
-  <button class="mfo2">mfo2</button>
-  <button class="mfo3">mfo3</button>
-  <button class="mfo4">mfo4</button>
-  <br>
-  <button class="mfo5">mfo5</button>
-  <button class="mfo6">mfo6</button>
-  <button class="mfo7">mfo7</button>
-  <button class="mfo8">mfo8</button>
-  <br>
-</body>
-</html>
deleted file mode 100644
--- a/layout/reftests/forms/button/focus-area-percent-units-2-ref.html
+++ /dev/null
@@ -1,68 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-<html>
-<head>
-  <title>Reference case for bug 1272983</title>
-  <style>
-    /* Set explicit font size so that em units are predictable: */
-    body, button { font: 10px sans-serif; }
-
-    /* Add outline to help visualize the padding outside of buttons: */
-    button { outline: 1px solid black; }
-
-    /* Set padding to 0 for all cases. In the first 3, that's how we expect
-       the testcase to render; and in the 4th and 5th, our reference padding
-       will be applied via a child div instead of via the pseudo-element. */
-    button.mfi1::-moz-focus-inner,
-    button.mfi2::-moz-focus-inner,
-    button.mfi3::-moz-focus-inner,
-    button.mfi4::-moz-focus-inner,
-    button.mfi5::-moz-focus-inner { padding: 0; }
-
-    /* Use an explicit div instead of pseudo-element, for reference case's
-       version of padding values that we actually expect to take effect: */
-    button.mfi4 > div { padding: 10px; }
-    button.mfi5 > div { padding: 2em; }
-
-    /* As above, set padding to 0 for all cases: */
-    button.mfo1::-moz-focus-outer,
-    button.mfo2::-moz-focus-outer,
-    button.mfo3::-moz-focus-outer,
-    button.mfo4::-moz-focus-outer,
-    button.mfo5::-moz-focus-outer { padding: 0; }
-
-    /* To make reference for -moz-focus-outer padding that we expect to
-       take effect, we'll put the padding on a wrapper-div (and bump the
-       button's outline to that div). */
-    div.mfo4-wrapper { padding: 10px; }
-    div.mfo5-wrapper { padding: 20px; /* = 2em * 10px/em */ }
-
-    button.mfo4,
-    button.mfo5 { outline: none; }
-    div.mfo4-wrapper,
-    div.mfo5-wrapper { display: inline-block; outline: 1px solid black; }
-  </style>
-</head>
-<body>
-  <button class="mfi1">mfi1</button>
-  <button class="mfi2">mfi2</button>
-  <button class="mfi3">mfi3</button>
-  <br>
-  <button class="mfi4"><div>mfi4</div></button>
-  <button class="mfi5"><div>mfi5</div></button>
-  <br>
-
-  <button class="mfo1">mfo1</button>
-  <button class="mfo2">mfo2</button>
-  <button class="mfo3">mfo3</button>
-  <br>
-  <div class="mfo4-wrapper">
-    <button class="mfo4">mfo4</button>
-  </div>
-  <div class="mfo5-wrapper">
-    <button class="mfo5">mfo5</button>
-  </div>
-  <br>
-</body>
-</html>
deleted file mode 100644
--- a/layout/reftests/forms/button/focus-area-percent-units-2.html
+++ /dev/null
@@ -1,51 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-<html>
-<!-- The ::-moz-focus-inner & ::-moz-focus-outer button pseudo-elements only
-     support CSS "padding" values that contain absolute lengths.  Any percent
-     padding values will simply make the padding collapse to zero.
-     This test verifies that this is indeed what happens (not anything worse).
--->
-<head>
-  <title>Testcase for bug 1272983</title>
-  <style>
-    /* Set explicit font size so that em units are predictable: */
-    body, button { font: 10px sans-serif; }
-
-    /* Add outline to help visualize the padding outside of buttons: */
-    button { outline: 1px solid black; }
-
-    /* Testing percent and auto padding values on "-moz-focus-inner": */
-    button.mfi1::-moz-focus-inner { padding: 50%; }
-    button.mfi2::-moz-focus-inner { padding: 50% 10px; }
-    button.mfi3::-moz-focus-inner { padding: 10px 50%; }
-    button.mfi4::-moz-focus-inner { padding: 10px; }
-    button.mfi5::-moz-focus-inner { padding: 2em; }
-
-    /* Testing percent and auto padding values on "-moz-focus-outer": */
-    button.mfo1::-moz-focus-outer { padding: 50%; }
-    button.mfo2::-moz-focus-outer { padding: 50% 10px; }
-    button.mfo3::-moz-focus-outer { padding: 10px 50%; }
-    button.mfo4::-moz-focus-outer { padding: 10px; }
-    button.mfo5::-moz-focus-outer { padding: 2em; }
-  </style>
-</head>
-<body>
-  <button class="mfi1">mfi1</button>
-  <button class="mfi2">mfi2</button>
-  <button class="mfi3">mfi3</button>
-  <br>
-  <button class="mfi4">mfi4</button>
-  <button class="mfi5">mfi5</button>
-  <br>
-
-  <button class="mfo1">mfo1</button>
-  <button class="mfo2">mfo2</button>
-  <button class="mfo3">mfo3</button>
-  <br>
-  <button class="mfo4">mfo4</button>
-  <button class="mfo5">mfo5</button>
-  <br>
-</body>
-</html>
deleted file mode 100644
--- a/layout/reftests/forms/button/percent-height-child-2-ref.html
+++ /dev/null
@@ -1,54 +0,0 @@
-<!DOCTYPE html>
-<!--
-     Any copyright is dedicated to the Public Domain.
-     http://creativecommons.org/publicdomain/zero/1.0/
--->
-<html>
-<head>
-<style>
-div.button {
-    display: inline-block;
-    width: 78px;
-    border: 0;
-
-    /* Padding to match the -moz-focus-inner border/padding in testcase: */
-    padding: 7px 1px 3px;
-
-    font: 10px sans-serif;
-    text-align: center;
-    vertical-align: top;
-
-    color: black;
-    background: gray;
-}
-
-div.p80 {
-    background: pink;
-}
-
-div.p100 {
-    background: yellow;
-}
-</style>
-</head>
-<body>
-<!--Button has explicit height for us to resolve against: -->
-<div class="button" style="height: 100px">
-  <div class="p80" style="margin-top: 10px; height: 80px">abc</div>
-</div>
-
-<div class="button" style="height: 100px">
-  <div class="p100" style="height: 100px">abc</div>
-</div>
-
-<!--Button is using intrinsic height: -->
-<div class="button">
-  <div class="p80">abc</div>
-</div>
-
-<div class="button">
-  <div class="p100">abc</div>
-</div>
-
-</body>
-</html>
deleted file mode 100644
--- a/layout/reftests/forms/button/percent-height-child-2.html
+++ /dev/null
@@ -1,63 +0,0 @@
-<!DOCTYPE html>
-<!--
-     Any copyright is dedicated to the Public Domain.
-     http://creativecommons.org/publicdomain/zero/1.0/
--->
-<!-- This test verifies that we honor percent heights on content inside of
-     a <button> element (resolving the percent against the <button>).
-     (In this testcase, the button has some focus-border/padding, which
-     occupies part of the button's content-box and which we'll have to
-     subtract out from the button's size before using it to resolve
-     percentages sizes.) -->
-<html>
-<head>
-<style>
-button {
-    width: 80px;
-    border: 0;
-    padding: 0;
-
-    font: 10px sans-serif;
-    vertical-align: top;
-
-    color: black;
-    background: gray;
-    -moz-appearance: none;
-}
-button::-moz-focus-inner {
-    padding: 6px 0 2px;
-    border: 1px dotted transparent;
-}
-
-div.p80 {
-    height: 80%;
-    background: pink;
-}
-
-div.p100 {
-    height: 100%;
-    background: yellow;
-}
-</style>
-</head>
-<body>
-<!--Button has explicit height for us to resolve against: -->
-<button style="height: 110px">
-  <div class="p80">abc</div>
-</button>
-
-<button style="height: 110px">
-  <div class="p100">abc</div>
-</button>
-
-<!--Button is using intrinsic height: -->
-<button>
-  <div class="p80">abc</div>
-</button>
-
-<button>
-  <div class="p100">abc</div>
-</button>
-
-</body>
-</html>
rename from layout/reftests/forms/button/percent-height-child-1-ref.html
rename to layout/reftests/forms/button/percent-height-child-ref.html
rename from layout/reftests/forms/button/percent-height-child-1.html
rename to layout/reftests/forms/button/percent-height-child.html
deleted file mode 100644
--- a/layout/reftests/forms/button/percent-width-child-2-ref.html
+++ /dev/null
@@ -1,55 +0,0 @@
-<!DOCTYPE html>
-<!--
-     Any copyright is dedicated to the Public Domain.
-     http://creativecommons.org/publicdomain/zero/1.0/
--->
-<html>
-<head>
-<style>
-div.button {
-    display: inline-block;
-    border: 0;
-
-    /* Padding to match the -moz-focus-inner border/padding in testcase: */
-    padding: 1px 7px 1px 3px;
-
-    font: 10px sans-serif;
-    text-align: center;
-    vertical-align: top;
-
-    color: black;
-    background: gray;
-}
-
-div.p80 {
-    width: 80%;
-    background: pink;
-}
-
-div.p100 {
-    width: 100%;
-    background: yellow;
-}
-</style>
-</head>
-<body>
-<!--Button has explicit width for us to resolve against: -->
-<div class="button" style="width: 100px">
-  <div class="p80">abc</div>
-</div>
-
-<div class="button" style="width: 100px">
-  <div class="p100">abc</div>
-</div>
-
-<!--Button is using intrinsic width: -->
-<div class="button">
-  <div class="p80">abc</div>
-</div>
-
-<div class="button">
-  <div class="p100">abc</div>
-</div>
-
-</body>
-</html>
deleted file mode 100644
--- a/layout/reftests/forms/button/percent-width-child-2.html
+++ /dev/null
@@ -1,62 +0,0 @@
-<!DOCTYPE html>
-<!--
-     Any copyright is dedicated to the Public Domain.
-     http://creativecommons.org/publicdomain/zero/1.0/
--->
-<!-- This test verifies that we honor percent widths on content inside of
-     a <button> element (resolving the percent against the <button>).
-     (In this testcase, the button has some focus-border/padding, which
-     occupies part of the button's content-box and which we'll have to
-     subtract out from the button's size before using it to resolve
-     percentages sizes.) -->
-<html>
-<head>
-<style>
-button {
-    border: 0;
-    padding: 0;
-
-    font: 10px sans-serif;
-    vertical-align: top;
-
-    color: black;
-    background: gray;
-    -moz-appearance: none;
-}
-button::-moz-focus-inner {
-    padding: 0 6px 0 2px;
-    border: 1px dotted transparent;
-}
-
-div.p80 {
-    width: 80%;
-    background: pink;
-}
-
-div.p100 {
-    width: 100%;
-    background: yellow;
-}
-</style>
-</head>
-<body>
-<!--Button has explicit width for us to resolve against: -->
-<button style="width: 110px">
-  <div class="p80">abc</div>
-</button>
-
-<button style="width: 110px">
-  <div class="p100">abc</div>
-</button>
-
-<!--Button is using intrinsic width: -->
-<button>
-  <div class="p80">abc</div>
-</button>
-
-<button>
-  <div class="p100">abc</div>
-</button>
-
-</body>
-</html>
rename from layout/reftests/forms/button/percent-width-child-1-ref.html
rename to layout/reftests/forms/button/percent-width-child-ref.html
rename from layout/reftests/forms/button/percent-width-child-1.html
rename to layout/reftests/forms/button/percent-width-child.html
--- a/layout/reftests/forms/button/reftest-stylo.list
+++ b/layout/reftests/forms/button/reftest-stylo.list
@@ -1,29 +1,22 @@
 # DO NOT EDIT! This is a auto-generated temporary list for Stylo testing
 == first-letter-1.html first-letter-1.html
 == first-letter-1.html first-letter-1.html
 
-== focus-area-percent-units-1.html focus-area-percent-units-1.html
-== focus-area-percent-units-2.html focus-area-percent-units-2.html
-
 == max-height.html max-height.html
 == min-height.html min-height.html
 == overflow-areas-1.html overflow-areas-1.html
 
 # The buttons in these tests have some fancy shading applied to their corners
 # on B2G, despite their "-moz-appearance: none; background: gray", so they
 # don't quite match the reference case's normal <div>. That's why they're fuzzy.
-fuzzy-if(B2G||Mulet||Android,125,20) == percent-height-child-1.html percent-height-child-1.html
-# Initial mulet triage: parity with B2G/B2G Desktop
-pref(browser.display.focus_ring_width,1) fuzzy-if(B2G||Mulet||Android,125,80) == percent-height-child-2.html percent-height-child-2.html
+fuzzy-if(B2G||Mulet||Android,125,20) == percent-height-child.html percent-height-child.html
 # Initial mulet triage: parity with B2G/B2G Desktop
-fuzzy-if(B2G||Mulet||Android,125,20) == percent-width-child-1.html percent-width-child-1.html
-# Initial mulet triage: parity with B2G/B2G Desktop
-pref(browser.display.focus_ring_width,1) fuzzy-if(B2G||Mulet||Android,125,80) == percent-width-child-2.html percent-width-child-2.html
+fuzzy-if(B2G||Mulet||Android,125,20) == percent-width-child.html percent-width-child.html
 # Initial mulet triage: parity with B2G/B2G Desktop
 
 == vertical-centering.html vertical-centering.html
 
 == line-height-button-0.5.html line-height-button-0.5.html
 == line-height-button-1.5.html line-height-button-1.5.html
 == line-height-input-0.5.html line-height-input-0.5.html
 == line-height-input-1.5.html line-height-input-1.5.html
@@ -41,21 +34,15 @@ fails-if(Android||B2G||Mulet) == disable
 
 fails-if(B2G||Mulet) == width-auto-size-em-ltr.html width-auto-size-em-ltr.html
 # Bug 1145672
 # Bug 1150486
 fails-if(B2G||Mulet) == width-auto-size-ltr.html width-auto-size-ltr.html
 # Bug 1145672
 # Bug 1150486
 == width-exact-fit-ltr.html width-exact-fit-ltr.html
-== width-erode-part-focuspadding-ltr.html width-erode-part-focuspadding-ltr.html
-== width-erode-all-focuspadding-ltr.html width-erode-all-focuspadding-ltr.html
-== width-erode-overflow-focuspadding-ltr.html width-erode-overflow-focuspadding-ltr.html
 fails-if(B2G||Mulet) == width-auto-size-em-rtl.html width-auto-size-em-rtl.html
 # Bug 1145672
 # Bug 1150486
 fails-if(B2G||Mulet) == width-auto-size-rtl.html width-auto-size-rtl.html
 # Bug 1145672
 # Bug 1150486
 == width-exact-fit-rtl.html width-exact-fit-rtl.html
-== width-erode-part-focuspadding-rtl.html width-erode-part-focuspadding-rtl.html
-== width-erode-all-focuspadding-rtl.html width-erode-all-focuspadding-rtl.html
-== width-erode-overflow-focuspadding-rtl.html width-erode-overflow-focuspadding-rtl.html
--- a/layout/reftests/forms/button/reftest.list
+++ b/layout/reftests/forms/button/reftest.list
@@ -1,25 +1,23 @@
 == first-letter-1.html first-letter-1-ref.html
 != first-letter-1.html first-letter-1-noref.html
 
-== focus-area-percent-units-1.html focus-area-percent-units-1-ref.html
-== focus-area-percent-units-2.html focus-area-percent-units-2-ref.html
-
 == max-height.html max-height-ref.html
 == min-height.html min-height-ref.html
-== overflow-areas-1.html overflow-areas-1-ref.html
+
+# Android is off ever-so-slightly on the points where the text
+# runs into the border on this text, so a little fuzz is needed.
+fuzzy-if(Android,1,16) == overflow-areas-1.html overflow-areas-1-ref.html
 
 # The buttons in these tests have some fancy shading applied to their corners
 # on Android, despite their "-moz-appearance: none; background: gray", so they
 # don't quite match the reference case's normal <div>. That's why they're fuzzy.
-fuzzy-if(Android,125,20) == percent-height-child-1.html percent-height-child-1-ref.html
-pref(browser.display.focus_ring_width,1) fuzzy-if(Android,125,80) == percent-height-child-2.html percent-height-child-2-ref.html
-fuzzy-if(Android,125,20) == percent-width-child-1.html  percent-width-child-1-ref.html
-pref(browser.display.focus_ring_width,1) fuzzy-if(Android,125,80) == percent-width-child-2.html  percent-width-child-2-ref.html
+fuzzy-if(Android,125,20) == percent-height-child.html percent-height-child-ref.html
+fuzzy-if(Android,125,20) == percent-width-child.html  percent-width-child-ref.html
 
 == vertical-centering.html vertical-centering-ref.html
 
 != line-height-button-0.5.html line-height-button-1.0.html
 != line-height-button-1.5.html line-height-button-1.0.html
 != line-height-input-0.5.html line-height-input-1.0.html
 != line-height-input-1.5.html line-height-input-1.0.html
 
@@ -31,19 +29,13 @@ fails-if(Android) == disabled-1.html dis
 != disabled-3.html disabled-3-notref.html
 != disabled-4.html disabled-4-notref.html
 != disabled-5.html disabled-5-notref.html
 != disabled-6.html disabled-6-notref.html
 
 == width-auto-size-em-ltr.html width-auto-size-em-ltr-ref.html
 == width-auto-size-ltr.html width-auto-size-ltr-ref.html
 == width-exact-fit-ltr.html width-auto-size-ltr-ref.html
-== width-erode-part-focuspadding-ltr.html width-erode-part-focuspadding-ltr-ref.html
-== width-erode-all-focuspadding-ltr.html width-erode-all-focuspadding-ltr-ref.html
-== width-erode-overflow-focuspadding-ltr.html width-erode-overflow-focuspadding-ltr-ref.html
 == width-auto-size-em-rtl.html width-auto-size-em-rtl-ref.html
 == width-auto-size-rtl.html width-auto-size-rtl-ref.html
 == width-exact-fit-rtl.html width-auto-size-rtl-ref.html
-== width-erode-part-focuspadding-rtl.html width-erode-part-focuspadding-rtl-ref.html
-== width-erode-all-focuspadding-rtl.html width-erode-all-focuspadding-rtl-ref.html
-== width-erode-overflow-focuspadding-rtl.html width-erode-overflow-focuspadding-rtl-ref.html
 == display-grid-flex-columnset.html display-grid-flex-columnset-ref.html
 == 1317351.html 1317351-ref.html
--- a/layout/reftests/forms/button/width-auto-size-em-ltr-ref.html
+++ b/layout/reftests/forms/button/width-auto-size-em-ltr-ref.html
@@ -6,14 +6,13 @@ div.button {
   background: yellow;
   border: 2px solid fuchsia;
   padding: 2px;
 }
 
 span {
   display: inline-block; vertical-align: middle;
   height: 2em; width: 5em; border: 2px solid blue;
-  margin: 1px 3px; /* for implicit focuspadding */
 }
 
 </style>
 
 <div class=button><span></span></div>
--- a/layout/reftests/forms/button/width-auto-size-em-rtl-ref.html
+++ b/layout/reftests/forms/button/width-auto-size-em-rtl-ref.html
@@ -7,14 +7,13 @@ div.button {
   background: yellow;
   border: 2px solid fuchsia;
   padding: 2px;
 }
 
 span {
   display: inline-block; vertical-align: middle;
   height: 2em; width: 5em; border: 2px solid blue;
-  margin: 1px 3px; /* for implicit focuspadding */
 }
 
 </style>
 
 <div class=button><span></span></div>
--- a/layout/reftests/forms/button/width-auto-size-ltr-ref.html
+++ b/layout/reftests/forms/button/width-auto-size-ltr-ref.html
@@ -7,14 +7,13 @@ div.button {
   border: 2px solid fuchsia;
   padding: 2px;
   font-size: 10px;
 }
 
 span {
   display: inline-block; vertical-align: middle;
   height: 20px; width: 50px; border: 2px solid blue;
-  margin: 1px 3px; /* for implicit focuspadding */
 }
 
 </style>
 
 <div class=button><span></span></div>
--- a/layout/reftests/forms/button/width-auto-size-rtl-ref.html
+++ b/layout/reftests/forms/button/width-auto-size-rtl-ref.html
@@ -8,14 +8,13 @@ div.button {
   border: 2px solid fuchsia;
   padding: 2px;
   font-size: 10px;
 }
 
 span {
   display: inline-block; vertical-align: middle;
   height: 20px; width: 50px; border: 2px solid blue;
-  margin: 1px 3px; /* for implicit focuspadding */
 }
 
 </style>
 
 <div class=button><span></span></div>
deleted file mode 100644
--- a/layout/reftests/forms/button/width-erode-all-focuspadding-ltr-ref.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE HTML>
-<style>
-
-div.button {
-  display: inline-block;
-  background: yellow;
-  border: 2px solid fuchsia;
-  padding: 2px;
-  font-size: 10px;
-}
-
-span {
-  display: inline-block; vertical-align: middle;
-  height: 20px; width: 50px; border: 2px solid blue;
-  margin: 1px 0px; /* for implicit focuspadding, fully eroded */
-}
-
-</style>
-
-<div class=button><span></span></div>
deleted file mode 100644
--- a/layout/reftests/forms/button/width-erode-all-focuspadding-ltr.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<!DOCTYPE HTML>
-<style>
-
-button {
-  -moz-appearance: none;
-  background: yellow;
-  border: 2px solid fuchsia;
-  padding: 2px;
-  font-size: 10px;
-  width: 62px;
-  border-radius: 0; /* override mobile/android/themes/core/content.css */
-}
-
-span {
-  display: inline-block; vertical-align: middle;
-  height: 20px; width: 50px; border: 2px solid blue;
-}
-
-</style>
-
-<button><span></span></button>
deleted file mode 100644
--- a/layout/reftests/forms/button/width-erode-overflow-focuspadding-ltr-ref.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE HTML>
-<style>
-
-div.button {
-  display: inline-block;
-  background: yellow;
-  border: 2px solid fuchsia;
-  padding: 2px;
-  font-size: 10px;
-}
-
-span {
-  display: inline-block; vertical-align: middle;
-  height: 20px; width: 50px; border: 2px solid blue;
-  margin: 1px -2px 1px 0; /* for implicit focuspadding, fully eroded and overflowed */
-}
-
-</style>
-
-<div class=button><span></span></div>
deleted file mode 100644
--- a/layout/reftests/forms/button/width-erode-overflow-focuspadding-ltr.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<!DOCTYPE HTML>
-<style>
-
-button {
-  -moz-appearance: none;
-  background: yellow;
-  border: 2px solid fuchsia;
-  padding: 2px;
-  font-size: 10px;
-  width: 60px;
-  border-radius: 0; /* override mobile/android/themes/core/content.css */
-}
-
-span {
-  display: inline-block; vertical-align: middle;
-  height: 20px; width: 50px; border: 2px solid blue;
-}
-
-</style>
-
-<button><span></span></button>
deleted file mode 100644
--- a/layout/reftests/forms/button/width-erode-overflow-focuspadding-rtl-ref.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<!DOCTYPE HTML>
-<html dir=rtl>
-<style>
-
-div.button {
-  display: inline-block;
-  background: yellow;
-  border: 2px solid fuchsia;
-  padding: 2px;
-  font-size: 10px;
-}
-
-span {
-  display: inline-block; vertical-align: middle;
-  height: 20px; width: 50px; border: 2px solid blue;
-  margin: 1px 0 1px -2px; /* for implicit focuspadding, fully eroded and overflowed */
-}
-
-</style>
-
-<div class=button><span></span></div>
deleted file mode 100644
--- a/layout/reftests/forms/button/width-erode-overflow-focuspadding-rtl.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE HTML>
-<html dir=rtl>
-<style>
-
-button {
-  -moz-appearance: none;
-  background: yellow;
-  border: 2px solid fuchsia;
-  padding: 2px;
-  font-size: 10px;
-  width: 60px;
-  border-radius: 0; /* override mobile/android/themes/core/content.css */
-}
-
-span {
-  display: inline-block; vertical-align: middle;
-  height: 20px; width: 50px; border: 2px solid blue;
-}
-
-</style>
-
-<button><span></span></button>
deleted file mode 100644
--- a/layout/reftests/forms/button/width-erode-part-focuspadding-ltr-ref.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE HTML>
-<style>
-
-div.button {
-  display: inline-block;
-  background: yellow;
-  border: 2px solid fuchsia;
-  padding: 2px;
-  font-size: 10px;
-}
-
-span {
-  display: inline-block; vertical-align: middle;
-  height: 20px; width: 50px; border: 2px solid blue;
-  margin: 1px 2px; /* for implicit focuspadding, partly eroded */
-}
-
-</style>
-
-<div class=button><span></span></div>
deleted file mode 100644
--- a/layout/reftests/forms/button/width-erode-part-focuspadding-ltr.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<!DOCTYPE HTML>
-<style>
-
-button {
-  -moz-appearance: none;
-  background: yellow;
-  border: 2px solid fuchsia;
-  padding: 2px;
-  font-size: 10px;
-  width: 66px;
-  border-radius: 0; /* override mobile/android/themes/core/content.css */
-}
-
-span {
-  display: inline-block; vertical-align: middle;
-  height: 20px; width: 50px; border: 2px solid blue;
-}
-
-</style>
-
-<button><span></span></button>
deleted file mode 100644
--- a/layout/reftests/forms/button/width-erode-part-focuspadding-rtl-ref.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<!DOCTYPE HTML>
-<html dir=rtl>
-<style>
-
-div.button {
-  display: inline-block;
-  background: yellow;
-  border: 2px solid fuchsia;
-  padding: 2px;
-  font-size: 10px;
-}
-
-span {
-  display: inline-block; vertical-align: middle;
-  height: 20px; width: 50px; border: 2px solid blue;
-  margin: 1px 2px; /* for implicit focuspadding, partly eroded */
-}
-
-</style>
-
-<div class=button><span></span></div>
deleted file mode 100644
--- a/layout/reftests/forms/button/width-erode-part-focuspadding-rtl.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE HTML>
-<html dir=rtl>
-<style>
-
-button {
-  -moz-appearance: none;
-  background: yellow;
-  border: 2px solid fuchsia;
-  padding: 2px;
-  font-size: 10px;
-  width: 66px;
-  border-radius: 0; /* override mobile/android/themes/core/content.css */
-}
-
-span {
-  display: inline-block; vertical-align: middle;
-  height: 20px; width: 50px; border: 2px solid blue;
-}
-
-</style>
-
-<button><span></span></button>
--- a/layout/reftests/forms/button/width-exact-fit-ltr.html
+++ b/layout/reftests/forms/button/width-exact-fit-ltr.html
@@ -2,17 +2,17 @@
 <style>
 
 button {
   -moz-appearance: none;
   background: yellow;
   border: 2px solid fuchsia;
   padding: 2px;
   font-size: 10px;
-  width: 68px;
+  width: 62px;
   border-radius: 0; /* override mobile/android/themes/core/content.css */
 }
 
 span {
   display: inline-block; vertical-align: middle;
   height: 20px; width: 50px; border: 2px solid blue;
 }
 
--- a/layout/reftests/forms/button/width-exact-fit-rtl.html
+++ b/layout/reftests/forms/button/width-exact-fit-rtl.html
@@ -3,17 +3,17 @@
 <style>
 
 button {
   -moz-appearance: none;
   background: yellow;
   border: 2px solid fuchsia;
   padding: 2px;
   font-size: 10px;
-  width: 68px;
+  width: 62px;
   border-radius: 0; /* override mobile/android/themes/core/content.css */
 }
 
 span {
   display: inline-block; vertical-align: middle;
   height: 20px; width: 50px; border: 2px solid blue;
 }
 
index 701341fc2a3a5def1749c3523daa566abc6aadf9..89cf30e15226e57c70685ac30b2ccb50f694ce39
GIT binary patch
literal 565
zc$|}Ry}y`|0Sx4w+8P<1z60^6Mi!^fjT}x*E#;|6xlZkkOirzhOcKEj=@6xi{{($!
zmpJJ7?(Pm=-6HC_GA(#b<5S<=JwTpV$hz#{HBF4l5Uxz{ZZP-Z8>lU-7={c0c?{}m
zBdzNjoB5p_d>eXw63fy|&Gk&p^b8FQ!W~ihE}b0?{SII)pn!ntc3KBzSYB^rEI!)E
zIH!?8Y3rQk(!7+m*0A`n0L$h^M&-xNE!MM6F+6~(<=xOIu&a?{RwGl$rbZTd=WV4>
ziInG!42$nKGB7kO@@#feV31?rJHXD;+zgarVPN17`2YW3&6?%^|9^YbRsa9v`L6nZ
zlYxx?|E`}?`2YX^-%WLn|NsBo-C_U#|J6v4$WuuW`REc*_}|Kf>_Admf&mn7Q2PXT
V_it`w*xm(986TS%3qLe60{|y@mOlUh
index 8b6b363138fecfdaf33959bd28e8b0966604fdc5..3f6105a02e35bc64ca5e230c51c0e32c7e07d4bf
GIT binary patch
literal 559
zc$|}Ry}y`|0Sx4w+8P<1z60^6Mi!^fjT}x*E#;|6xlZkkOirzhOcKEj=@6xiKLmYe
zmpJJ7?(Pm=-6HC_GA(#b<5S<=JwTpV$hz#{HBF4l5Uxz{ZZP-u8>lU-7={c0c?{}m
zBdzNjoB5p_d>eXw63fy|&Gk&p^b8FQ!W~ihE}b0?{SII)pn!ntc3KBzSYB^rEI!)E
zIH!?8Y3rQk(!7+m*0A`n0L$h^M&-xNE!MM6F+6~(<=xQ8v8z#FRwGl$rbZTd=WV4>
ziMZ#D42y3zGB7kO@@#feVBlkrJHXD;+zgarVPN3y`2YX^eC_)G|Lz3j0-1#l^8eo*
zk*@#$|L<0Yx-NU5(A}0`Ao(HYlKlVw|9HQ@SPbO-5z_>czj`tlKrsfjNpN@n=0=9?
PUBEQ)v5B$pLnAW)rtOeP
--- a/layout/reftests/writing-mode/ua-style-sheet-button-1a-ref.html
+++ b/layout/reftests/writing-mode/ua-style-sheet-button-1a-ref.html
@@ -5,22 +5,22 @@
 .v-rl { writing-mode: vertical-rl; }
 .ltr, .rtl, .v-rl { border: 1px solid blue; }
 .a { -moz-appearance: none; }
 
 .ltr button,
 .rtl button,
 .ltr input[type="button"],
 .rtl input[type="button"] {
-  padding: 0px 6px;
+  padding: 0px 8px;
 }
 
 .v-rl button,
 .v-rl input[type="button"] {
-  padding: 6px 0px;
+  padding: 8px 0px;
 }
 </style>
 <div class=ltr>
   <button>one</button><br>
   <button class=a>two</button><br>
   <button disabled>three</button><br>
   <button class=a disabled>four</button><br>
   <input type="button" value="five"><br>
--- a/layout/reftests/writing-mode/ua-style-sheet-input-color-1-ref.html
+++ b/layout/reftests/writing-mode/ua-style-sheet-input-color-1-ref.html
@@ -5,23 +5,23 @@
 .v-rl { writing-mode: vertical-rl; }
 .ltr, .rtl, .v-rl { border: 1px solid blue; }
 .a { -moz-appearance: none; }
 
 .ltr input[type="color"]:-moz-system-metric(color-picker-available),
 .rtl input[type="color"]:-moz-system-metric(color-picker-available) {
   width: 64px;
   height: 23px;
-  padding: 0px 6px;
+  padding: 0px 8px;
 }
 
 .v-rl input[type="color"]:-moz-system-metric(color-picker-available) {
   height: 64px;
   width: 23px;
-  padding: 6px 0px;
+  padding: 8px 0px;
 }
 </style>
 <div class=ltr>
   <input type=color><br>
   <input type=color class=a><br>
 </div>
 
 <div class=rtl dir=rtl>
--- a/layout/style/nsLayoutStylesheetCache.cpp
+++ b/layout/style/nsLayoutStylesheetCache.cpp
@@ -909,17 +909,16 @@ nsLayoutStylesheetCache::BuildPreference
   if ((focusRingWidth != 1 && focusRingWidth <= 4) || focusRingOnAnything) {
     if (focusRingWidth != 1) {
       // If the focus ring width is different from the default, fix buttons
       // with rings.
       sheetText.AppendPrintf(
           "button::-moz-focus-inner, input[type=\"reset\"]::-moz-focus-inner, "
           "input[type=\"button\"]::-moz-focus-inner, "
           "input[type=\"submit\"]::-moz-focus-inner { "
-          "padding: 1px 2px 1px 2px; "
           "border: %dpx %s transparent !important; }\n",
           focusRingWidth,
           focusRingStyle == 0 ? "solid" : "dotted");
 
       sheetText.AppendLiteral(
           "button:focus::-moz-focus-inner, "
           "input[type=\"reset\"]:focus::-moz-focus-inner, "
           "input[type=\"button\"]:focus::-moz-focus-inner, "
--- a/layout/style/res/forms.css
+++ b/layout/style/res/forms.css
@@ -655,22 +655,22 @@ input[type="search"] {
    input[type="color"] */
 button,
 input[type="color"]:-moz-system-metric(color-picker-available),
 input[type="reset"],
 input[type="button"],
 input[type="submit"] {
   -moz-appearance: button;
   /* The sum of border and padding on block-start and block-end
-     must be the same here, for text inputs, and for <select>.  For
-     buttons, make sure to include the -moz-focus-inner border/padding. */
+     must be the same here, for text inputs, and for <select>.
+     Note -moz-focus-inner padding does not affect button size. */
   padding-block-start: 0px;
-  padding-inline-end: 6px;
+  padding-inline-end: 8px;
   padding-block-end: 0px;
-  padding-inline-start: 6px;
+  padding-inline-start: 8px;
   border: 2px outset ThreeDLightShadow;
   background-color: ButtonFace;
   cursor: default;
   box-sizing: border-box;
   -moz-user-select: none;
   -moz-binding: none;
 }
 
@@ -753,19 +753,19 @@ input[type="submit"]:hover {
 
 button:active:hover,
 input[type="color"]:-moz-system-metric(color-picker-available):active:hover,
 input[type="reset"]:active:hover,
 input[type="button"]:active:hover,
 input[type="submit"]:active:hover {
 %ifndef XP_MACOSX
   padding-block-start: 0px;
-  padding-inline-end: 5px;
+  padding-inline-end: 7px;
   padding-block-end: 0px;
-  padding-inline-start: 7px;
+  padding-inline-start: 9px;
 %endif
   border-style: inset;
   background-color: ButtonFace;
 }
 
 button:active:hover,
 input[type="reset"]:active:hover,
 input[type="button"]:active:hover,
@@ -774,16 +774,17 @@ input[type="submit"]:active:hover {
 }
 
 button::-moz-focus-inner,
 input[type="color"]:-moz-system-metric(color-picker-available)::-moz-focus-inner,
 input[type="reset"]::-moz-focus-inner,
 input[type="button"]::-moz-focus-inner,
 input[type="submit"]::-moz-focus-inner,
 input[type="file"] > button[type="button"]::-moz-focus-inner {
+  /* Note this padding only affects the -moz-focus-inner ring, not the button itself */
   padding-block-start: 0px;
   padding-inline-end: 2px;
   padding-block-end: 0px;
   padding-inline-start: 2px;
   border: 1px dotted transparent;
 }
 
 button:-moz-focusring::-moz-focus-inner,
@@ -804,19 +805,19 @@ input[type="button"]:disabled:active,
 input[type="button"]:disabled,
 select:disabled > button,
 select:disabled > button,
 input[type="submit"]:disabled:active,
 input[type="submit"]:disabled {
   /* The sum of border and padding on block-start and block-end
      must be the same here and for text inputs */
   padding-block-start: 0px;
-  padding-inline-end: 6px;
+  padding-inline-end: 8px;
   padding-block-end: 0px;
-  padding-inline-start: 6px;
+  padding-inline-start: 8px;
   border: 2px outset ThreeDLightShadow;
   cursor: inherit;
 }
 
 button:disabled:active, button:disabled,
 input[type="reset"]:disabled:active,
 input[type="reset"]:disabled,
 input[type="button"]:disabled:active,
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-0ce009e2303a
+5f2db99c258f
--- a/security/nss/automation/taskcluster/graph/src/extend.js
+++ b/security/nss/automation/taskcluster/graph/src/extend.js
@@ -274,17 +274,17 @@ async function scheduleFuzzing() {
   };
 
   // Build base definition.
   let build_base = merge({
     command: [
       "/bin/bash",
       "-c",
       "bin/checkout.sh && " +
-      "nss/automation/taskcluster/scripts/build_gyp.sh -g -v --fuzz"
+      "nss/automation/taskcluster/scripts/build_gyp.sh -g -v --fuzz --ubsan"
     ],
     artifacts: {
       public: {
         expires: 24 * 7,
         type: "directory",
         path: "/home/worker/artifacts"
       }
     },
--- a/security/nss/build.sh
+++ b/security/nss/build.sh
@@ -8,17 +8,17 @@ set -e
 
 source $(dirname $0)/coreconf/nspr.sh
 
 # Usage info
 show_help() {
 cat << EOF
 
 Usage: ${0##*/} [-hcgv] [-j <n>] [--test] [--fuzz] [--scan-build[=output]]
-                [-m32] [--opt|-o] [--asan] [--ubsan] [--sancov[=edge|bb|func]]
+                [-m32] [--opt|-o] [--asan] [--ubsan] [--sancov[=edge|bb|func|...]]
                 [--pprof] [--msan]
 
 This script builds NSS with gyp and ninja.
 
 This build system is still under development.  It does not yet support all
 the features or platforms that NSS supports.
 
 NSS build tool options:
@@ -49,16 +49,17 @@ fi
 
 opt_build=0
 build_64=0
 clean=0
 rebuild_gyp=0
 target=Debug
 verbose=0
 fuzz=0
+sancov_default=edge,indirect-calls,8bit-counters
 
 # parse parameters to store in config
 params=$(echo "$*" | perl -pe 's/-c|-v|-g|-j [0-9]*|-h//g' | perl -pe 's/^\s*(.*?)\s*$/\1/')
 params=$(echo "$params $CC $CCC" | tr " " "\n" | perl -pe '/^\s*$/d')
 params=$(echo "${params[*]}" | sort)
 
 cwd=$(cd $(dirname $0); pwd -P)
 dist_dir="$cwd/../dist"
@@ -72,21 +73,19 @@ fi
 gyp_params=()
 ninja_params=()
 scanbuild=()
 
 enable_fuzz()
 {
     fuzz=1
     nspr_sanitizer asan
-    nspr_sanitizer ubsan
-    nspr_sanitizer sancov edge
+    nspr_sanitizer sancov $sancov_default
     gyp_params+=(-Duse_asan=1)
-    gyp_params+=(-Duse_ubsan=1)
-    gyp_params+=(-Duse_sancov=edge)
+    gyp_params+=(-Duse_sancov=$sancov_default)
 
     # Adding debug symbols even for opt builds.
     nspr_opt+=(--enable-debug-symbols)
 }
 
 # parse command line arguments
 while [ $# -gt 0 ]; do
     case $1 in
@@ -97,17 +96,17 @@ while [ $# -gt 0 ]; do
         --test) gyp_params+=(-Dtest_build=1) ;;
         --fuzz) gyp_params+=(-Dtest_build=1 -Dfuzz=1); enable_fuzz ;;
         --scan-build) scanbuild=(scan-build) ;;
         --scan-build=?*) scanbuild=(scan-build -o "${1#*=}") ;;
         --opt|-o) opt_build=1 ;;
         -m32|--m32) build_64=0 ;;
         --asan) gyp_params+=(-Duse_asan=1); nspr_sanitizer asan ;;
         --ubsan) gyp_params+=(-Duse_ubsan=1); nspr_sanitizer ubsan ;;
-        --sancov) gyp_params+=(-Duse_sancov=edge); nspr_sanitizer sancov edge ;;
+        --sancov) gyp_params+=(-Duse_sancov=$sancov_default); nspr_sanitizer sancov $sancov_default ;;
         --sancov=?*) gyp_params+=(-Duse_sancov="${1#*=}"); nspr_sanitizer sancov "${1#*=}" ;;
         --pprof) gyp_params+=(-Duse_pprof=1) ;;
         --msan) gyp_params+=(-Duse_msan=1); nspr_sanitizer msan ;;
         *) show_help; exit ;;
     esac
     shift
 done
 
@@ -116,17 +115,16 @@ if [ "$opt_build" = "1" ]; then
     nspr_opt+=(--disable-debug --enable-optimize)
 else
     target=Debug
 fi
 if [ "$build_64" == "1" ]; then
     nspr_opt+=(--enable-64bit)
 else
     gyp_params+=(-Dtarget_arch=ia32)
-    nspr_opt+=(--enable-x32)
 fi
 
 # clone fuzzing stuff
 if [ "$fuzz" = "1" ]; then
     [ $verbose = 0 ] && exec 3>/dev/null || exec 3>&1
 
     echo "[1/2] Cloning libFuzzer files ..."
     $cwd/fuzz/clone_libfuzzer.sh 1>&3 2>&3
--- a/security/nss/cmd/tstclnt/tstclnt.c
+++ b/security/nss/cmd/tstclnt/tstclnt.c
@@ -164,30 +164,16 @@ printSecurityInfo(PRFileDesc *fd)
     scts = SSL_PeerSignedCertTimestamps(fd);
     if (scts && scts->len) {
         fprintf(stderr, "Received a Signed Certificate Timestamp of length"
                         " %u\n",
                 scts->len);
     }
 }
 
-void
-handshakeCallback(PRFileDesc *fd, void *client_data)
-{
-    const char *secondHandshakeName = (char *)client_data;
-    if (secondHandshakeName) {
-        SSL_SetURL(fd, secondHandshakeName);
-    }
-    printSecurityInfo(fd);
-    if (renegotiationsDone < renegotiationsToDo) {
-        SSL_ReHandshake(fd, (renegotiationsToDo < 2));
-        ++renegotiationsDone;
-    }
-}
-
 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"
@@ -918,23 +904,29 @@ PRBool clientSpeaksFirst = PR_FALSE;
 PRBool skipProtoHeader = PR_FALSE;
 ServerCertAuth serverCertAuth;
 char *hs1SniHostName = NULL;
 char *hs2SniHostName = NULL;
 PRUint16 portno = 443;
 int override = 0;
 char *requestString = NULL;
 PRInt32 requestStringLen = 0;
+PRBool requestSent = PR_FALSE;
 PRBool enableZeroRtt = PR_FALSE;
 
 static int
-writeBytesToServer(PRFileDesc *s, PRPollDesc *pollset, const char *buf, int nb)
+writeBytesToServer(PRFileDesc *s, const char *buf, int nb)
 {
     SECStatus rv;
     const char *bufp = buf;
+    PRPollDesc pollDesc;
+
+    pollDesc.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
+    pollDesc.out_flags = 0;
+    pollDesc.fd = s;
 
     FPRINTF(stderr, "%s: Writing %d bytes to server\n",
             progName, nb);
     do {
         PRInt32 cc = PR_Send(s, bufp, nb, 0, maxInterval);
         if (cc < 0) {
             PRErrorCode err = PR_GetError();
             if (err != PR_WOULD_BLOCK_ERROR) {
@@ -951,49 +943,80 @@ writeBytesToServer(PRFileDesc *s, PRPoll
 
         rv = restartHandshakeAfterServerCertIfNeeded(s,
                                                      &serverCertAuth, override);
         if (rv != SECSuccess) {
             SECU_PrintError(progName, "authentication of server cert failed");
             return EXIT_CODE_HANDSHAKE_FAILED;
         }
 
-        pollset[SSOCK_FD].in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
-        pollset[SSOCK_FD].out_flags = 0;
+        pollDesc.in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
+        pollDesc.out_flags = 0;
         FPRINTF(stderr,
                 "%s: about to call PR_Poll on writable socket !\n",
                 progName);
-        cc = PR_Poll(pollset, 1, PR_INTERVAL_NO_TIMEOUT);
+        cc = PR_Poll(&pollDesc, 1, PR_INTERVAL_NO_TIMEOUT);
         if (cc < 0) {
             SECU_PrintError(progName,
                             "PR_Poll failed");
             return -1;
         }
         FPRINTF(stderr,
                 "%s: PR_Poll returned with writable socket !\n",
                 progName);
     } while (1);
 
     return 0;
 }
 
+void
+handshakeCallback(PRFileDesc *fd, void *client_data)
+{
+    const char *secondHandshakeName = (char *)client_data;
+    if (secondHandshakeName) {
+        SSL_SetURL(fd, secondHandshakeName);
+    }
+    printSecurityInfo(fd);
+    if (renegotiationsDone < renegotiationsToDo) {
+        SSL_ReHandshake(fd, (renegotiationsToDo < 2));
+        ++renegotiationsDone;
+    }
+    if (requestString && requestSent) {
+        /* This data was sent in 0-RTT. */
+        SSLChannelInfo info;
+        SECStatus rv;
+
+        rv = SSL_GetChannelInfo(fd, &info, sizeof(info));
+        if (rv != SECSuccess)
+            return;
+
+        if (!info.earlyDataAccepted) {
+            FPRINTF(stderr, "Early data rejected. Re-sending\n");
+            writeBytesToServer(fd, requestString, requestStringLen);
+        }
+    }
+}
+
+#define REQUEST_WAITING (requestString && !requestSent)
+
 static int
 run_client(void)
 {
     int headerSeparatorPtrnId = 0;
     int error = 0;
     SECStatus rv;
     PRStatus status;
     PRInt32 filesReady;
     int npds;
     PRFileDesc *s = NULL;
     PRFileDesc *std_out;
     PRPollDesc pollset[2];
     PRBool wrStarted = PR_FALSE;
-    char *requestStringInt = requestString;
+
+    requestSent = PR_FALSE;
 
     /* Create socket */
     s = PR_OpenTCPSocket(addr.raw.family);
     if (s == NULL) {
         SECU_PrintError(progName, "error creating socket");
         error = 1;
         goto done;
     }
@@ -1240,17 +1263,17 @@ run_client(void)
             goto done;
         }
     }
 
     pollset[SSOCK_FD].fd = s;
     pollset[SSOCK_FD].in_flags = PR_POLL_EXCEPT |
                                  (clientSpeaksFirst ? 0 : PR_POLL_READ);
     pollset[STDIN_FD].fd = PR_GetSpecialFD(PR_StandardInput);
-    if (!requestStringInt) {
+    if (!REQUEST_WAITING) {
         pollset[STDIN_FD].in_flags = PR_POLL_READ;
         npds = 2;
     } else {
         npds = 1;
     }
     std_out = PR_GetSpecialFD(PR_StandardOutput);
 
 #if defined(WIN32) || defined(OS2)
@@ -1290,17 +1313,17 @@ run_client(void)
     }
 
     /*
     ** Select on stdin and on the socket. Write data from stdin to
     ** socket, read data from socket and write to stdout.
     */
     FPRINTF(stderr, "%s: ready...\n", progName);
     while ((pollset[SSOCK_FD].in_flags | pollset[STDIN_FD].in_flags) ||
-           requestStringInt) {
+           REQUEST_WAITING) {
         char buf[4000]; /* buffer for stdin */
         int nb;         /* num bytes read from stdin. */
 
         rv = restartHandshakeAfterServerCertIfNeeded(s, &serverCertAuth,
                                                      override);
         if (rv != SECSuccess) {
             error = EXIT_CODE_HANDSHAKE_FAILED;
             SECU_PrintError(progName, "authentication of server cert failed");
@@ -1328,40 +1351,39 @@ run_client(void)
                     "%s: PR_Poll returned 0x%02x for stdin out_flags.\n",
                     progName, pollset[STDIN_FD].out_flags);
         }
         if (pollset[SSOCK_FD].in_flags) {
             FPRINTF(stderr,
                     "%s: PR_Poll returned 0x%02x for socket out_flags.\n",
                     progName, pollset[SSOCK_FD].out_flags);
         }
-        if (requestStringInt) {
-            error = writeBytesToServer(s, pollset,
-                                       requestStringInt, requestStringLen);
+        if (REQUEST_WAITING) {
+            error = writeBytesToServer(s, requestString, requestStringLen);
             if (error) {
                 goto done;
             }
-            requestStringInt = NULL;
+            requestSent = PR_TRUE;
             pollset[SSOCK_FD].in_flags = PR_POLL_READ;
         }
         if (pollset[STDIN_FD].out_flags & PR_POLL_READ) {
             /* Read from stdin and write to socket */
             nb = PR_Read(pollset[STDIN_FD].fd, buf, sizeof(buf));
             FPRINTF(stderr, "%s: stdin read %d bytes\n", progName, nb);
             if (nb < 0) {
                 if (PR_GetError() != PR_WOULD_BLOCK_ERROR) {
                     SECU_PrintError(progName, "read from stdin failed");
                     error = 1;
                     break;
                 }
             } else if (nb == 0) {
                 /* EOF on stdin, stop polling stdin for read. */
                 pollset[STDIN_FD].in_flags = 0;
             } else {
-                error = writeBytesToServer(s, pollset, buf, nb);
+                error = writeBytesToServer(s, buf, nb);
                 if (error) {
                     goto done;
                 }
                 pollset[SSOCK_FD].in_flags = PR_POLL_READ;
             }
         }
 
         if (pollset[SSOCK_FD].in_flags) {
--- a/security/nss/coreconf/config.gypi
+++ b/security/nss/coreconf/config.gypi
@@ -268,32 +268,40 @@
               [ 'target_arch=="ia32"', {
                 'cflags': ['-m32'],
                 'ldflags': ['-m32'],
               }],
               [ 'target_arch=="x64"', {
                 'cflags': ['-m64'],
                 'ldflags': ['-m64'],
               }],
-              [ 'use_pprof==1' , {
-                'ldflags': [ '-lprofiler' ],
-              }],
+            ],
+          }],
+          [ 'use_pprof==1 and OS=="linux"', {
+            'ldflags': [ '-lprofiler' ],
+          }],
+          [ 'use_pprof==1 and OS=="mac"', {
+            'xcode_settings': {
+              'OTHER_LDFLAGS': [ '-lprofiler' ],
+            },
+            'library_dirs': [
+              '/usr/local/lib/',
             ],
           }],
           [ 'disable_werror==0 and (OS=="linux" or OS=="mac")', {
             'cflags': [
               '<!@(<(python) <(DEPTH)/coreconf/werror.py)',
             ],
           }],
           [ 'fuzz==1', {
             'cflags': [
               '-Wno-unused-function',
             ]
           }],
-          [ 'use_asan==1 or use_ubsan==1', {
+          [ 'fuzz==1 or use_asan==1 or use_ubsan==1', {
             'cflags': ['-O1'],
             'xcode_settings': {
               'GCC_OPTIMIZATION_LEVEL': '1', # -O1
             }
           }],
           [ 'use_asan==1', {
             'variables': {
               'asan_flags': '<!(<(python) <(DEPTH)/coreconf/sanitizers.py asan)',
--- 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/detect_host_arch.py
+++ b/security/nss/coreconf/detect_host_arch.py
@@ -9,17 +9,17 @@ from __future__ import print_function
 import fnmatch
 import platform
 
 def main():
     host_arch = platform.machine().lower()
     if host_arch in ('amd64', 'x86_64'):
         host_arch = 'x64'
     elif fnmatch.fnmatch(host_arch, 'i?86') or host_arch == 'i86pc':
-        host_arch = 'x64'
+        host_arch = 'ia32'
     elif host_arch.startswith('arm'):
         host_arch = 'arm'
     elif host_arch.startswith('mips'):
         host_arch = 'mips'
     print(host_arch)
 
 if __name__ == '__main__':
     main()
--- a/security/nss/coreconf/werror.py
+++ b/security/nss/coreconf/werror.py
@@ -20,16 +20,17 @@ def main():
         if not warning_supported('all'):
             return False
 
         # If we aren't clang, make sure we have gcc 4.8 at least
         if not cc_is_clang:
             try:
                 v = subprocess.check_output([cc, '-dumpversion'], stderr=sink)
                 v = v.strip(' \r\n').split('.')
+                v = list(map(int, v))
                 if v[0] < 4 or (v[0] == 4 and v[1] < 8):
                     # gcc 4.8 minimum
                     return False
             except OSError:
                 return False
         return True
 
     if not can_enable():
new file mode 100644
--- /dev/null
+++ b/security/nss/fuzz/asn1_mutators.cc
@@ -0,0 +1,117 @@
+/* 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 <assert.h>
+#include <string.h>
+#include <tuple>
+
+#include "FuzzerRandom.h"
+#include "asn1_mutators.h"
+
+using namespace std;
+
+static tuple<uint8_t *, size_t> ParseItem(uint8_t *Data, size_t MaxLength) {
+  // Short form. Bit 8 has value "0" and bits 7-1 give the length.
+  if ((Data[1] & 0x80) == 0) {
+    size_t length = min(static_cast<size_t>(Data[1]), MaxLength - 2);
+    return make_tuple(&Data[2], length);
+  }
+
+  // Constructed, indefinite length. Read until {0x00, 0x00}.
+  if (Data[1] == 0x80) {
+    void *offset = memmem(&Data[2], MaxLength - 2, "\0", 2);
+    size_t length = offset ? (static_cast<uint8_t *>(offset) - &Data[2]) + 2
+                           : MaxLength - 2;
+    return make_tuple(&Data[2], length);
+  }
+
+  // Long form. Two to 127 octets. Bit 8 of first octet has value "1"
+  // and bits 7-1 give the number of additional length octets.
+  size_t octets = min(static_cast<size_t>(Data[1] & 0x7f), MaxLength - 2);
+
+  // Handle lengths bigger than 32 bits.
+  if (octets > 4) {
+    // Ignore any further children, assign remaining length.
+    return make_tuple(&Data[2] + octets, MaxLength - 2 - octets);
+  }
+
+  // Parse the length.
+  size_t length = 0;
+  for (size_t j = 0; j < octets; j++) {
+    length = (length << 8) | Data[2 + j];
+  }
+
+  length = min(length, MaxLength - 2 - octets);
+  return make_tuple(&Data[2] + octets, length);
+}
+
+static vector<uint8_t *> ParseItems(uint8_t *Data, size_t Size) {
+  vector<uint8_t *> items;
+  vector<size_t> lengths;
+
+  // The first item is always the whole corpus.
+  items.push_back(Data);
+  lengths.push_back(Size);
+
+  // Can't use iterators here because the `items` vector is modified inside the
+  // loop. That's safe as long as we always check `items.size()` before every
+  // iteration, and only call `.push_back()` to append new items we found.
+  // Items are accessed through `items.at()`, we hold no references.
+  for (size_t i = 0; i < items.size(); i++) {
+    uint8_t *item = items.at(i);
+    size_t remaining = lengths.at(i);
+
+    // Empty or primitive items have no children.
+    if (remaining == 0 || (0x20 & item[0]) == 0) {
+      continue;
+    }
+
+    while (remaining > 2) {
+      uint8_t *content;
+      size_t length;
+
+      tie(content, length) = ParseItem(item, remaining);
+
+      if (length > 0) {
+        // Record the item.
+        items.push_back(content);
+
+        // Record the length for further parsing.
+        lengths.push_back(length);
+      }
+
+      // Reduce number of bytes left in current item.
+      remaining -= length + (content - item);
+
+      // Skip the item we just parsed.
+      item = content + length;
+    }
+  }
+
+  return items;
+}
+
+size_t ASN1MutatorFlipConstructed(uint8_t *Data, size_t Size, size_t MaxSize,
+                                  unsigned int Seed) {
+  fuzzer::Random R(Seed);
+  auto items = ParseItems(Data, Size);
+  uint8_t *item = items.at(R(items.size()));
+
+  // Flip "constructed" type bit.
+  item[0] ^= 0x20;
+
+  return Size;
+}
+
+size_t ASN1MutatorChangeType(uint8_t *Data, size_t Size, size_t MaxSize,
+                             unsigned int Seed) {
+  fuzzer::Random R(Seed);
+  auto items = ParseItems(Data, Size);
+  uint8_t *item = items.at(R(items.size()));
+
+  // Change type to a random int [0, 31).
+  item[0] = R(31);
+
+  return Size;
+}
new file mode 100644
--- /dev/null
+++ b/security/nss/fuzz/asn1_mutators.h
@@ -0,0 +1,16 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef asn1_mutators_h__
+#define asn1_mutators_h__
+
+#include <stdint.h>
+#include <cstddef>
+
+size_t ASN1MutatorFlipConstructed(uint8_t *Data, size_t Size, size_t MaxSize,
+                                  unsigned int Seed);
+size_t ASN1MutatorChangeType(uint8_t *Data, size_t Size, size_t MaxSize,
+                             unsigned int Seed);
+
+#endif  // asn1_mutators_h__
--- a/security/nss/fuzz/clone_corpus.sh
+++ b/security/nss/fuzz/clone_corpus.sh
@@ -1,11 +1,4 @@
 #!/bin/sh
 
-cd $(dirname $0)
-
-mkdir tmp/
-git clone --no-checkout --depth 1 https://github.com/mozilla/nss-fuzzing-corpus tmp/
-(cd tmp && git reset --hard master)
-
-mkdir -p corpus
-cp -r tmp/* corpus
-rm -Rf tmp/
+d=$(dirname $0)
+$d/git-copy.sh https://github.com/mozilla/nss-fuzzing-corpus master $d/corpus
--- a/security/nss/fuzz/clone_libfuzzer.sh
+++ b/security/nss/fuzz/clone_libfuzzer.sh
@@ -1,11 +1,22 @@
 #!/bin/sh
 
-cd $(dirname $0)
+d=$(dirname $0)
+$d/git-copy.sh https://chromium.googlesource.com/chromium/llvm-project/llvm/lib/Fuzzer 1b543d6e5073b56be214394890c9193979a3d7e1 $d/libFuzzer
 
-mkdir tmp/
-git clone --no-checkout --depth 1 https://chromium.googlesource.com/chromium/llvm-project/llvm/lib/Fuzzer tmp/
-(cd tmp && git reset --hard 1b543d6e5073b56be214394890c9193979a3d7e1)
+cat <<EOF | patch -p0 -d $d
+diff --git libFuzzer/FuzzerMutate.cpp libFuzzer/FuzzerMutate.cpp
+--- libFuzzer/FuzzerMutate.cpp
++++ libFuzzer/FuzzerMutate.cpp
+@@ -53,10 +53,9 @@
+     DefaultMutators.push_back(
+         {&MutationDispatcher::Mutate_AddWordFromTORC, "CMP"});
 
-mkdir -p libFuzzer
-cp tmp/*.cpp tmp/*.h tmp/*.def libFuzzer
-rm -Rf tmp/
++  Mutators = DefaultMutators;
+   if (EF->LLVMFuzzerCustomMutator)
+     Mutators.push_back({&MutationDispatcher::Mutate_Custom, "Custom"});
+-  else
+-    Mutators = DefaultMutators;
+
+   if (EF->LLVMFuzzerCustomCrossOver)
+     Mutators.push_back(
+EOF
--- a/security/nss/fuzz/fuzz.gyp
+++ b/security/nss/fuzz/fuzz.gyp
@@ -3,27 +3,77 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 {
   'includes': [
     '../coreconf/config.gypi',
     '../cmd/platlibs.gypi'
   ],
   'targets': [
     {
+      'target_name': 'libFuzzer',
+      'type': 'static_library',
+      'sources': [
+        'libFuzzer/FuzzerCrossOver.cpp',
+        'libFuzzer/FuzzerDriver.cpp',
+        'libFuzzer/FuzzerExtFunctionsDlsym.cpp',
+        'libFuzzer/FuzzerExtFunctionsWeak.cpp',
+        'libFuzzer/FuzzerIO.cpp',
+        'libFuzzer/FuzzerLoop.cpp',
+        'libFuzzer/FuzzerMutate.cpp',
+        'libFuzzer/FuzzerSHA1.cpp',
+        'libFuzzer/FuzzerTracePC.cpp',
+        'libFuzzer/FuzzerTraceState.cpp',
+        'libFuzzer/FuzzerUtil.cpp',
+        'libFuzzer/FuzzerUtilDarwin.cpp',
+        'libFuzzer/FuzzerUtilLinux.cpp',
+      ],
+      'cflags': [
+        '-O2',
+      ],
+      'cflags!': [
+        '-O1',
+      ],
+      'cflags/': [
+        ['exclude', '-fsanitize'],
+      ],
+      'xcode_settings': {
+        'GCC_OPTIMIZATION_LEVEL': '2', # -O2
+        'OTHER_CFLAGS/': [
+          ['exclude', '-fsanitize'],
+        ],
+      },
+    },
+    {
       'target_name': 'nssfuzz',
       'type': 'executable',
       'sources': [
+        'asn1_mutators.cc',
         'nssfuzz.cc',
         'pkcs8_target.cc',
         'quickder_targets.cc',
       ],
       'dependencies': [
         '<(DEPTH)/exports.gyp:nss_exports',
-        '<(DEPTH)/fuzz/libFuzzer/libFuzzer.gyp:libFuzzer'
-      ]
+        'libFuzzer',
+      ],
+      'cflags': [
+        '-O2',
+      ],
+      'cflags!': [
+        '-O1',
+      ],
+      'cflags/': [
+        ['exclude', '-fsanitize-coverage'],
+      ],
+      'xcode_settings': {
+        'GCC_OPTIMIZATION_LEVEL': '2', # -O2
+        'OTHER_CFLAGS/': [
+          ['exclude', '-fsanitize-coverage'],
+        ],
+      },
     }
   ],
   'target_defaults': {
     'include_dirs': [
       'libFuzzer',
     ],
   },
   'variables': {
new file mode 100755
--- /dev/null
+++ b/security/nss/fuzz/git-copy.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+set -e
+
+if [ $# -lt 3 ]; then
+  echo "Usage: $0 <repo> <branch> <directory>" 1>&2
+  exit 2
+fi
+
+REPO=$1
+COMMIT=$2
+DIR=$3
+
+echo "Copy '$COMMIT' from '$REPO' to '$DIR'"
+if [ -f $DIR/.git-copy ]; then
+  CURRENT=$(cat $DIR/.git-copy)
+  if [ $(echo -n $COMMIT | wc -c) != "40" ]; then
+    ACTUAL=$(git ls-remote $REPO $COMMIT | cut -c 1-40 -)
+  else
+    ACTUAL=$COMMIT
+  fi
+  if [ CURRENT = ACTUAL ]; then
+    echo "Up to date."
+  fi
+fi
+
+mkdir -p $DIR
+git -C $DIR init -q
+git -C $DIR fetch -q --depth=1 $REPO $COMMIT:git-copy-tmp
+git -C $DIR reset --hard git-copy-tmp
+git -C $DIR show-ref HEAD | cut -c 1-40 - > $DIR/.git-copy
+rm -rf $DIR/.git
deleted file mode 100644
--- a/security/nss/fuzz/libFuzzer/libFuzzer.gyp
+++ /dev/null
@@ -1,43 +0,0 @@
-# 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/.
-{
-  'includes': [
-    '../../coreconf/config.gypi'
-  ],
-  'targets': [
-    {
-      'target_name': 'libFuzzer',
-      'type': 'static_library',
-      'sources': [
-        'FuzzerCrossOver.cpp',
-        'FuzzerDriver.cpp',
-        'FuzzerExtFunctionsDlsym.cpp',
-        'FuzzerExtFunctionsWeak.cpp',
-        'FuzzerIO.cpp',
-        'FuzzerLoop.cpp',
-        'FuzzerMutate.cpp',
-        'FuzzerSHA1.cpp',
-        'FuzzerTracePC.cpp',
-        'FuzzerTraceState.cpp',
-        'FuzzerUtil.cpp',
-        'FuzzerUtilDarwin.cpp',
-        'FuzzerUtilLinux.cpp',
-      ],
-      'cflags': [
-        '-O2',
-      ],
-      'cflags/': [
-        ['exclude', '-fsanitize='],
-        ['exclude', '-fsanitize-'],
-      ],
-      'xcode_settings': {
-        'GCC_OPTIMIZATION_LEVEL': '2', # -O2
-        'OTHER_CFLAGS/': [
-          ['exclude', '-fsanitize='],
-          ['exclude', '-fsanitize-'],
-        ],
-      },
-    }
-  ],
-}
--- a/security/nss/fuzz/nssfuzz.cc
+++ b/security/nss/fuzz/nssfuzz.cc
@@ -1,27 +1,27 @@
 /* -*- 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 <iomanip>
 #include <iostream>
-#include <memory>
-
-#include "keyhi.h"
-#include "pk11pub.h"
 
 #include "FuzzerInternal.h"
+#include "FuzzerMutate.h"
+#include "FuzzerRandom.h"
 #include "registry.h"
 #include "shared.h"
 
 using namespace std;
 
+static vector<Mutator> gMutators;
+
 class Args {
  public:
   Args(int argc, char **argv) : args_(argv, argv + argc) {}
 
   string &operator[](const int idx) { return args_[idx]; }
 
   bool Has(const string &arg) {
     return any_of(args_.begin(), args_.end(),
@@ -122,16 +122,20 @@ int main(int argc, char **argv) {
 
   if (args.count() < 2 || !Registry::Has(args[1])) {
     printUsage(args);
     return 1;
   }
 
   string targetName(args[1]);
 
+  // Add target mutators.
+  auto mutators = Registry::Mutators(targetName);
+  gMutators.insert(gMutators.end(), mutators.begin(), mutators.end());
+
   // Remove the target argument when -workers=x or -jobs=y is NOT given.
   // If both are given, libFuzzer will spawn multiple processes for the target.
   if (!args.Has("-workers=") || !args.Has("-jobs=")) {
     args.Remove(1);
   }
 
   // Set default max_len arg, if none given and we're not merging.
   if (!args.Has("-max_len=") && !args.Has("-merge=1")) {
@@ -141,8 +145,19 @@ int main(int argc, char **argv) {
 
   // Hand control to the libFuzzer driver.
   vector<char *> args_new(args.argv());
   argc = args_new.size();
   argv = args_new.data();
 
   return fuzzer::FuzzerDriver(&argc, &argv, Registry::Func(targetName));
 }
+
+extern "C" size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
+                                          size_t MaxSize, unsigned int Seed) {
+  if (gMutators.empty()) {
+    return 0;
+  }
+
+  // Forward to a pseudorandom mutator.
+  fuzzer::Random R(Seed);
+  return gMutators.at(R(gMutators.size()))(Data, Size, MaxSize, Seed);
+}
--- a/security/nss/fuzz/pkcs8_target.cc
+++ b/security/nss/fuzz/pkcs8_target.cc
@@ -29,9 +29,10 @@ extern "C" int pkcs8_fuzzing_target(cons
                                                nullptr) == SECSuccess) {
     SECKEY_DestroyPrivateKey(key);
   }
 
   PK11_FreeSlot(slot);
   return 0;
 }
 
-REGISTER_FUZZING_TARGET("pkcs8", pkcs8_fuzzing_target, 2048, "PKCS#8 Import")
+REGISTER_FUZZING_TARGET("pkcs8", pkcs8_fuzzing_target, 2048, "PKCS#8 Import",
+                        {})
--- a/security/nss/fuzz/quickder_targets.cc
+++ b/security/nss/fuzz/quickder_targets.cc
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <stdint.h>
 
+#include "asn1_mutators.h"
 #include "cert.h"
-
 #include "registry.h"
 
 void QuickDERDecode(void *dst, const SEC_ASN1Template *tpl, const uint8_t *buf,
                     size_t len) {
   PORTCheapArenaPool pool;
   SECItem data = {siBuffer, const_cast<unsigned char *>(buf),
                   static_cast<unsigned int>(len)};
 
@@ -20,17 +20,19 @@ void QuickDERDecode(void *dst, const SEC
 }
 
 extern "C" int cert_fuzzing_target(const uint8_t *Data, size_t Size) {
   CERTCertificate cert;
   QuickDERDecode(&cert, SEC_SignedCertificateTemplate, Data, Size);
   return 0;
 }
 
-REGISTER_FUZZING_TARGET("cert", cert_fuzzing_target, 3072, "Certificate Import")
+REGISTER_FUZZING_TARGET("cert", cert_fuzzing_target, 3072, "Certificate Import",
+                        {&ASN1MutatorFlipConstructed, &ASN1MutatorChangeType})
 
 extern "C" int spki_fuzzing_target(const uint8_t *Data, size_t Size) {
   CERTSubjectPublicKeyInfo spki;
   QuickDERDecode(&spki, CERT_SubjectPublicKeyInfoTemplate, Data, Size);
   return 0;
 }
 
-REGISTER_FUZZING_TARGET("spki", spki_fuzzing_target, 1024, "SPKI Import")
+REGISTER_FUZZING_TARGET("spki", spki_fuzzing_target, 1024, "SPKI Import",
+                        {&ASN1MutatorFlipConstructed, &ASN1MutatorChangeType})
--- a/security/nss/fuzz/registry.h
+++ b/security/nss/fuzz/registry.h
@@ -6,66 +6,74 @@
 
 #ifndef registry_h__
 #define registry_h__
 
 #include <map>
 #include "FuzzerInternal.h"
 #include "nss.h"
 
+using namespace fuzzer;
+using namespace std;
+
+typedef decltype(LLVMFuzzerCustomMutator)* Mutator;
+
 class Registry {
  public:
-  static void Add(std::string name, fuzzer::UserCallback func, uint16_t max_len,
-                  std::string desc) {
+  static void Add(string name, UserCallback func, uint16_t max_len, string desc,
+                  vector<Mutator> mutators = {}) {
     assert(!Has(name));
-    GetInstance().targets_[name] = TargetData(func, max_len, desc);
+    GetInstance().targets_[name] = TargetData(func, max_len, desc, mutators);
   }
 
-  static bool Has(std::string name) {
+  static bool Has(string name) {
     return GetInstance().targets_.count(name) > 0;
   }
 
-  static fuzzer::UserCallback Func(std::string name) {
+  static UserCallback Func(string name) {
     assert(Has(name));
-    return std::get<0>(Get(name));
+    return get<0>(Get(name));
+  }
+
+  static uint16_t MaxLen(string name) {
+    assert(Has(name));
+    return get<1>(Get(name));
   }
 
-  static uint16_t MaxLen(std::string name) {
+  static string& Desc(string name) {
     assert(Has(name));
-    return std::get<1>(Get(name));
+    return get<2>(Get(name));
   }
 
-  static std::string& Desc(std::string name) {
+  static vector<Mutator>& Mutators(string name) {
     assert(Has(name));
-    return std::get<2>(Get(name));
+    return get<3>(Get(name));
   }
 
-  static std::vector<std::string> Names() {
-    std::vector<std::string> names;
+  static vector<string> Names() {
+    vector<string> names;
     for (auto& it : GetInstance().targets_) {
       names.push_back(it.first);
     }
     return names;
   }
 
  private:
-  typedef std::tuple<fuzzer::UserCallback, uint16_t, std::string> TargetData;
+  typedef tuple<UserCallback, uint16_t, string, vector<Mutator>> TargetData;
 
   static Registry& GetInstance() {
     static Registry registry;
     return registry;
   }
 
-  static TargetData& Get(std::string name) {
-    return GetInstance().targets_[name];
-  }
+  static TargetData& Get(string name) { return GetInstance().targets_[name]; }
 
   Registry() {}
 
-  std::map<std::string, TargetData> targets_;
+  map<string, TargetData> targets_;
 };
 
-#define REGISTER_FUZZING_TARGET(name, func, max_len, desc)     \
-  static void __attribute__((constructor)) Register_##func() { \
-    Registry::Add(name, func, max_len, desc);                  \
+#define REGISTER_FUZZING_TARGET(name, func, max_len, desc, ...) \
+  static void __attribute__((constructor)) Register_##func() {  \
+    Registry::Add(name, func, max_len, desc, __VA_ARGS__);      \
   }
 
 #endif  // registry_h__
--- a/security/nss/gtests/nss_bogo_shim/nss_bogo_shim.cc
+++ b/security/nss/gtests/nss_bogo_shim/nss_bogo_shim.cc
@@ -10,16 +10,17 @@
 #include <memory>
 #include "nspr.h"
 #include "nss.h"
 #include "prio.h"
 #include "prnetdb.h"
 #include "ssl.h"
 #include "sslerr.h"
 #include "sslproto.h"
+#include "ssl3prot.h"
 
 #include "nsskeys.h"
 
 static const char* kVersionDisableFlags[] = {
   "no-ssl3",
   "no-tls1",
   "no-tls11",
   "no-tls12",
@@ -147,43 +148,76 @@ class TestAgent {
         rv = SSL_GetClientAuthDataHook(ssl_fd_, GetClientAuthDataHook, this);
         if (rv != SECSuccess) return false;
       }
     }
 
     return true;
   }
 
+  static bool ConvertFromWireVersion(SSLProtocolVariant variant,
+                                     int wire_version,
+                                     uint16_t* lib_version) {
+    // These default values are used when {min,max}-version isn't given.
+    if (wire_version == 0 || wire_version == 0xffff) {
+      *lib_version = static_cast<uint16_t>(wire_version);
+      return true;
+    }
+
+#ifdef TLS_1_3_DRAFT_VERSION
+    if (wire_version == (0x7f00 | TLS_1_3_DRAFT_VERSION)) {
+      // N.B. SSL_LIBRARY_VERSION_DTLS_1_3_WIRE == SSL_LIBRARY_VERSION_TLS_1_3
+      wire_version = SSL_LIBRARY_VERSION_TLS_1_3;
+    }
+#endif
+
+    if (variant == ssl_variant_datagram) {
+      switch (wire_version) {
+      case SSL_LIBRARY_VERSION_DTLS_1_0_WIRE:
+        *lib_version = SSL_LIBRARY_VERSION_DTLS_1_0;
+        break;
+      case SSL_LIBRARY_VERSION_DTLS_1_2_WIRE:
+        *lib_version = SSL_LIBRARY_VERSION_DTLS_1_2;
+        break;
+      case SSL_LIBRARY_VERSION_DTLS_1_3_WIRE:
+        *lib_version = SSL_LIBRARY_VERSION_DTLS_1_3;
+        break;
+      default:
+        std::cerr << "Unrecognized DTLS version " << wire_version << ".\n";
+        return false;
+      }
+    } else {
+      if (wire_version < SSL_LIBRARY_VERSION_3_0 ||
+          wire_version > SSL_LIBRARY_VERSION_TLS_1_3) {
+        std::cerr << "Unrecognized TLS version " << wire_version << ".\n";
+        return false;
+      }
+      *lib_version = static_cast<uint16_t>(wire_version);
+    }
+    return true;
+  }
+
   bool GetVersionRange(SSLVersionRange* range_out, SSLProtocolVariant variant) {
     SSLVersionRange supported;
     if (SSL_VersionRangeGetSupported(variant, &supported) != SECSuccess) {
       return false;
     }
 
-    auto max_allowed = static_cast<uint16_t>(cfg_.get<int>("max-version"));
-    if (variant == ssl_variant_datagram) {
-      // For DTLS this is the wire version; adjust if needed.
-      switch (max_allowed) {
-      case SSL_LIBRARY_VERSION_DTLS_1_0_WIRE:
-        max_allowed = SSL_LIBRARY_VERSION_DTLS_1_0;
-        break;
-      case SSL_LIBRARY_VERSION_DTLS_1_2_WIRE:
-        max_allowed = SSL_LIBRARY_VERSION_DTLS_1_2;
-        break;
-      case SSL_LIBRARY_VERSION_DTLS_1_3_WIRE:
-        max_allowed = SSL_LIBRARY_VERSION_DTLS_1_3;
-        break;
-      case 0xffff: // No maximum specified.
-        break;
-      default:
-        // Unrecognized DTLS version.
-        return false;
-      }
+    uint16_t min_allowed;
+    uint16_t max_allowed;
+    if (!ConvertFromWireVersion(variant, cfg_.get<int>("min-version"),
+                                &min_allowed)) {
+      return false;
+    }
+    if (!ConvertFromWireVersion(variant, cfg_.get<int>("max-version"),
+                                &max_allowed)) {
+      return false;
     }
 
+    min_allowed = std::max(min_allowed, supported.min);
     max_allowed = std::min(max_allowed, supported.max);
 
     bool found_min = false;
     bool found_max = false;
     // Ignore -no-ssl3, because SSLv3 is never supported.
     for (size_t i = 1; i < PR_ARRAY_SIZE(kVersionDisableFlags); ++i) {
       auto version =
         static_cast<uint16_t>(SSL_LIBRARY_VERSION_TLS_1_0 + (i - 1));
@@ -194,17 +228,17 @@ class TestAgent {
           // DTLS 1.1 doesn't exist.
           continue;
         }
         if (version == SSL_LIBRARY_VERSION_TLS_1_0) {
           version = SSL_LIBRARY_VERSION_DTLS_1_0;
         }
       }
 
-      if (version < supported.min) {
+      if (version < min_allowed) {
         continue;
       }
       if (version > max_allowed) {
         break;
       }
 
       const bool allowed = !cfg_.get<bool>(kVersionDisableFlags[i]);
 
@@ -215,22 +249,24 @@ class TestAgent {
       if (found_min && !found_max) {
         if (allowed) {
           range_out->max = version;
         } else {
           found_max = true;
         }
       }
       if (found_max && allowed) {
-        // Discontiguous range.
+        std::cerr << "Discontiguous version range.\n";
         return false;
       }
     }
 
-    // Iff found_min is still false, no usable version was found.
+    if (!found_min) {
+      std::cerr << "All versions disabled.\n";
+    }
     return found_min;
   }
 
   bool SetupOptions() {
     SECStatus rv = SSL_OptionSet(ssl_fd_, SSL_ENABLE_SESSION_TICKETS, PR_TRUE);
     if (rv != SECSuccess) return false;
 
     SSLVersionRange vrange;
@@ -349,16 +385,17 @@ class TestAgent {
 std::unique_ptr<const Config> ReadConfig(int argc, char** argv) {
   std::unique_ptr<Config> cfg(new Config());
 
   cfg->AddEntry<int>("port", 0);
   cfg->AddEntry<bool>("server", false);
   cfg->AddEntry<int>("resume-count", 0);
   cfg->AddEntry<std::string>("key-file", "");
   cfg->AddEntry<std::string>("cert-file", "");
+  cfg->AddEntry<int>("min-version", 0);
   cfg->AddEntry<int>("max-version", 0xffff);
   for (auto flag : kVersionDisableFlags) {
     cfg->AddEntry<bool>(flag, false);
   }
 
   auto rv = cfg->ParseArgs(argc, argv);
   switch (rv) {
     case Config::kOK:
--- a/security/nss/gtests/ssl_gtest/libssl_internals.c
+++ b/security/nss/gtests/ssl_gtest/libssl_internals.c
@@ -5,18 +5,16 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* This file contains functions for frobbing the internals of libssl */
 #include "libssl_internals.h"
 
 #include "nss.h"
 #include "pk11pub.h"
 #include "seccomon.h"
-#include "ssl.h"
-#include "sslimpl.h"
 
 SECStatus SSLInt_IncrementClientHandshakeVersion(PRFileDesc *fd) {
   sslSocket *ss = ssl_FindSocket(fd);
   if (!ss) {
     return SECFailure;
   }
 
   ++ss->clientHelloVersion;
@@ -308,8 +306,42 @@ SECStatus SSLInt_AdvanceWriteSeqByAWindo
 }
 
 SSLKEAType SSLInt_GetKEAType(SSLNamedGroup group) {
   const sslNamedGroupDef *groupDef = ssl_LookupNamedGroup(group);
   if (!groupDef) return ssl_kea_null;
 
   return groupDef->keaType;
 }
+
+SECStatus SSLInt_SetCipherSpecChangeFunc(PRFileDesc *fd,
+                                         sslCipherSpecChangedFunc func,
+                                         void *arg) {
+  sslSocket *ss;
+
+  ss = ssl_FindSocket(fd);
+  if (!ss) {
+    return SECFailure;
+  }
+
+  ss->ssl3.changedCipherSpecFunc = func;
+  ss->ssl3.changedCipherSpecArg = arg;
+
+  return SECSuccess;
+}
+
+static ssl3KeyMaterial *GetKeyingMaterial(PRBool isServer,
+                                          ssl3CipherSpec *spec) {
+  return isServer ? &spec->server : &spec->client;
+}
+
+PK11SymKey *SSLInt_CipherSpecToKey(PRBool isServer, ssl3CipherSpec *spec) {
+  return GetKeyingMaterial(isServer, spec)->write_key;
+}
+
+SSLCipherAlgorithm SSLInt_CipherSpecToAlgorithm(PRBool isServer,
+                                                ssl3CipherSpec *spec) {
+  return spec->cipher_def->calg;
+}
+
+unsigned char *SSLInt_CipherSpecToIv(PRBool isServer, ssl3CipherSpec *spec) {
+  return GetKeyingMaterial(isServer, spec)->write_iv;
+}
--- a/security/nss/gtests/ssl_gtest/libssl_internals.h
+++ b/security/nss/gtests/ssl_gtest/libssl_internals.h
@@ -6,16 +6,18 @@
 
 #ifndef libssl_internals_h_
 #define libssl_internals_h_
 
 #include <stdint.h>
 
 #include "prio.h"
 #include "seccomon.h"
+#include "ssl.h"
+#include "sslimpl.h"
 #include "sslt.h"
 
 SECStatus SSLInt_IncrementClientHandshakeVersion(PRFileDesc *fd);
 
 SECStatus SSLInt_UpdateSSLv2ClientRandom(PRFileDesc *fd, uint8_t *rnd,
                                          size_t rnd_len, uint8_t *msg,
                                          size_t msg_len);
 
@@ -33,9 +35,17 @@ SECStatus SSLInt_Set0RttAlpn(PRFileDesc 
 PRBool SSLInt_HasCertWithAuthType(PRFileDesc *fd, SSLAuthType authType);
 PRBool SSLInt_SendAlert(PRFileDesc *fd, uint8_t level, uint8_t type);
 PRBool SSLInt_SendNewSessionTicket(PRFileDesc *fd);
 SECStatus SSLInt_AdvanceWriteSeqNum(PRFileDesc *fd, PRUint64 to);
 SECStatus SSLInt_AdvanceReadSeqNum(PRFileDesc *fd, PRUint64 to);
 SECStatus SSLInt_AdvanceWriteSeqByAWindow(PRFileDesc *fd, PRInt32 extra);
 SSLKEAType SSLInt_GetKEAType(SSLNamedGroup group);
 
+SECStatus SSLInt_SetCipherSpecChangeFunc(PRFileDesc *fd,
+                                         sslCipherSpecChangedFunc func,
+                                         void *arg);
+PK11SymKey *SSLInt_CipherSpecToKey(PRBool isServer, ssl3CipherSpec *spec);
+SSLCipherAlgorithm SSLInt_CipherSpecToAlgorithm(PRBool isServer,
+                                                ssl3CipherSpec *spec);
+unsigned char *SSLInt_CipherSpecToIv(PRBool isServer, ssl3CipherSpec *spec);
+
 #endif  // ndef libssl_internals_h_
--- a/security/nss/gtests/ssl_gtest/manifest.mn
+++ b/security/nss/gtests/ssl_gtest/manifest.mn
@@ -19,32 +19,34 @@ CPPSRCS = \
       ssl_ciphersuite_unittest.cc \
       ssl_damage_unittest.cc \
       ssl_dhe_unittest.cc \
       ssl_drop_unittest.cc \
       ssl_ecdh_unittest.cc \
       ssl_ems_unittest.cc \
       ssl_exporter_unittest.cc \
       ssl_extension_unittest.cc \
+      ssl_fragment_unittest.cc \
       ssl_fuzz_unittest.cc \
       ssl_gtest.cc \
       ssl_hrr_unittest.cc \
       ssl_loopback_unittest.cc \
       ssl_record_unittest.cc \
       ssl_resumption_unittest.cc \
       ssl_skip_unittest.cc \
       ssl_staticrsa_unittest.cc \
       ssl_v2_client_hello_unittest.cc \
       ssl_version_unittest.cc \
       test_io.cc \
       tls_agent.cc \
       tls_connect.cc \
       tls_hkdf_unittest.cc \
       tls_filter.cc \
       tls_parser.cc \
+      tls_protect.cc \
       $(NULL)
 
 INCLUDES += -I$(CORE_DEPTH)/gtests/google_test/gtest/include \
             -I$(CORE_DEPTH)/gtests/common
 
 REQUIRES = nspr nss libdbm gtest
 
 PROGRAM = ssl_gtest
--- a/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc
@@ -195,9 +195,87 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRtt
     return false;
   });
   Handshake();
   CheckConnected();
   SendReceive();
   CheckAlpn("b");
 }
 
+// The client should abort the connection when sending a 0-rtt handshake but
+// the servers responds with a TLS 1.2 ServerHello. (no app data sent)
+TEST_P(TlsConnectTls13, TestTls13ZeroRttDowngrade) {
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  server_->Set0RttEnabled(true);  // set ticket_allow_early_data
+  Connect();
+
+  SendReceive();  // Need to read so that we absorb the session tickets.
+  CheckKeys();
+
+  Reset();
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  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_2);
+  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_->Handshake();
+  server_->Handshake();
+  ASSERT_TRUE_WAIT(
+      (client_->error_code() == SSL_ERROR_RX_MALFORMED_SERVER_HELLO), 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) {
+    // The client sends an encrypted alert message.
+    ASSERT_TRUE_WAIT(
+        (server_->error_code() == SSL_ERROR_RX_UNEXPECTED_APPLICATION_DATA),
+        2000);
+  }
+}
+
+// The client should abort the connection when sending a 0-rtt handshake but
+// the servers responds with a TLS 1.2 ServerHello. (with app data)
+TEST_P(TlsConnectTls13, TestTls13ZeroRttDowngradeEarlyData) {
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  server_->Set0RttEnabled(true);  // set ticket_allow_early_data
+  Connect();
+
+  SendReceive();  // Need to read so that we absorb the session tickets.
+  CheckKeys();
+
+  Reset();
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  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_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);
+
+  client_->Handshake();
+  server_->Handshake();
+  ASSERT_TRUE_WAIT(
+      (client_->error_code() == SSL_ERROR_RX_MALFORMED_SERVER_HELLO), 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) {
+    // The server sends an alert when receiving the early app data record.
+    ASSERT_TRUE_WAIT(
+        (server_->error_code() == SSL_ERROR_RX_UNEXPECTED_APPLICATION_DATA),
+        2000);
+  }
+}
+
 }  // namespace nss_test
--- a/security/nss/gtests/ssl_gtest/ssl_auth_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_auth_unittest.cc
@@ -284,17 +284,17 @@ class BeforeFinished : public TlsRecordF
                  VoidFunction before_finished)
       : client_(client),
         server_(server),
         before_ccs_(before_ccs),
         before_finished_(before_finished),
         state_(BEFORE_CCS) {}
 
  protected:
-  virtual PacketFilter::Action FilterRecord(const RecordHeader& header,
+  virtual PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
                                             const DataBuffer& body,
                                             DataBuffer* out) {
     switch (state_) {
       case BEFORE_CCS:
         // Awaken when we see the CCS.
         if (header.content_type() == kTlsChangeCipherSpecType) {
           before_ccs_();
 
@@ -502,17 +502,17 @@ TEST_P(TlsConnectGenericPre13, AuthCompl
 
   // This should allow the handshake to complete now.
   EXPECT_EQ(SECSuccess, SSL_AuthCertificateComplete(client_->ssl_fd(), 0));
   client_->Handshake();  // Transition to connected
   EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
   EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state());
 
   // Remove this before closing or the close_notify alert will trigger it.
-  client_->SetPacketFilter(nullptr);
+  client_->DeletePacketFilter();
 }
 
 // TLS 1.3 handles a delayed AuthComplete callback differently since the
 // shape of the handshake is different.
 TEST_P(TlsConnectTls13, AuthCompleteDelayed) {
   client_->SetAuthCertificateCallback(AuthCompleteBlock);
 
   server_->StartConnect();
@@ -523,17 +523,17 @@ TEST_P(TlsConnectTls13, AuthCompleteDela
   EXPECT_EQ(TlsAgent::STATE_CONNECTING, server_->state());
 
   // The client will send nothing until AuthCertificateComplete is called.
   client_->SetPacketFilter(new EnforceNoActivity());
   client_->Handshake();
   EXPECT_EQ(TlsAgent::STATE_CONNECTING, client_->state());
 
   // This should allow the handshake to complete now.
-  client_->SetPacketFilter(nullptr);
+  client_->DeletePacketFilter();
   EXPECT_EQ(SECSuccess, SSL_AuthCertificateComplete(client_->ssl_fd(), 0));
   client_->Handshake();  // Send Finished
   server_->Handshake();  // Transition to connected and send NewSessionTicket
   EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
   EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state());
 }
 
 static const SSLExtraServerCertData ServerCertDataRsaPkcs1Decrypt = {
new file mode 100644
--- /dev/null
+++ b/security/nss/gtests/ssl_gtest/ssl_fragment_unittest.cc
@@ -0,0 +1,157 @@
+/* -*- 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 "secerr.h"
+#include "ssl.h"
+#include "sslerr.h"
+#include "sslproto.h"
+
+#include "gtest_utils.h"
+#include "scoped_ptrs.h"
+#include "tls_connect.h"
+#include "tls_filter.h"
+#include "tls_parser.h"
+
+namespace nss_test {
+
+// This class cuts every unencrypted handshake record into two parts.
+class RecordFragmenter : public PacketFilter {
+ public:
+  RecordFragmenter() : sequence_number_(0), splitting_(true) {}
+
+ private:
+  class HandshakeSplitter {
+   public:
+    HandshakeSplitter(const DataBuffer& input, DataBuffer* output,
+                      uint64_t* sequence_number)
+        : input_(input),
+          output_(output),
+          cursor_(0),
+          sequence_number_(sequence_number) {}
+
+   private:
+    void WriteRecord(TlsRecordHeader& record_header,
+                     DataBuffer& record_fragment) {
+      TlsRecordHeader fragment_header(record_header.version(),
+                                      record_header.content_type(),
+                                      *sequence_number_);
+      ++*sequence_number_;
+      if (::g_ssl_gtest_verbose) {
+        std::cerr << "Fragment: " << fragment_header << ' ' << record_fragment
+                  << std::endl;
+      }
+      cursor_ = fragment_header.Write(output_, cursor_, record_fragment);
+    }
+
+    bool SplitRecord(TlsRecordHeader& record_header, DataBuffer& record) {
+      TlsParser parser(record);
+      while (parser.remaining()) {
+        TlsHandshakeFilter::HandshakeHeader handshake_header;
+        DataBuffer handshake_body;
+        if (!handshake_header.Parse(&parser, record_header, &handshake_body)) {
+          ADD_FAILURE() << "couldn't parse handshake header";
+          return false;
+        }
+
+        DataBuffer record_fragment;
+        // We can't fragment handshake records that are too small.
+        if (handshake_body.len() < 2) {
+          handshake_header.Write(&record_fragment, 0U, handshake_body);
+          WriteRecord(record_header, record_fragment);
+          continue;
+        }
+
+        size_t cut = handshake_body.len() / 2;
+        handshake_header.WriteFragment(&record_fragment, 0U, handshake_body, 0U,
+                                       cut);
+        WriteRecord(record_header, record_fragment);
+
+        handshake_header.WriteFragment(&record_fragment, 0U, handshake_body,
+                                       cut, handshake_body.len() - cut);
+        WriteRecord(record_header, record_fragment);
+      }
+      return true;
+    }
+
+   public:
+    bool Split() {
+      TlsParser parser(input_);
+      while (parser.remaining()) {
+        TlsRecordHeader header;
+        DataBuffer record;
+        if (!header.Parse(&parser, &record)) {
+          ADD_FAILURE() << "bad record header";
+          return false;
+        }
+
+        if (::g_ssl_gtest_verbose) {
+          std::cerr << "Record: " << header << ' ' << record << std::endl;
+        }
+
+        // Don't touch packets from a non-zero epoch.  Leave these unmodified.
+        if ((header.sequence_number() >> 48) != 0ULL) {
+          cursor_ = header.Write(output_, cursor_, record);
+          continue;
+        }
+
+        // Just rewrite the sequence number (CCS only).
+        if (header.content_type() != kTlsHandshakeType) {
+          EXPECT_EQ(kTlsChangeCipherSpecType, header.content_type());
+          WriteRecord(header, record);
+          continue;
+        }
+
+        if (!SplitRecord(header, record)) {
+          return false;
+        }
+      }
+      return true;
+    }
+
+   private:
+    const DataBuffer& input_;
+    DataBuffer* output_;
+    size_t cursor_;
+    uint64_t* sequence_number_;
+  };
+
+ protected:
+  virtual PacketFilter::Action Filter(const DataBuffer& input,
+                                      DataBuffer* output) override {
+    if (!splitting_) {
+      return KEEP;
+    }
+
+    output->Allocate(input.len());
+    HandshakeSplitter splitter(input, output, &sequence_number_);
+    if (!splitter.Split()) {
+      // If splitting fails, we obviously reached encrypted packets.
+      // Stop splitting from that point onward.
+      splitting_ = false;
+      return KEEP;
+    }
+
+    return CHANGE;
+  }
+
+ private:
+  uint64_t sequence_number_;
+  bool splitting_;
+};
+
+TEST_P(TlsConnectDatagram, FragmentClientPackets) {
+  client_->SetPacketFilter(new RecordFragmenter());
+  Connect();
+  SendReceive();
+}
+
+TEST_P(TlsConnectDatagram, FragmentServerPackets) {
+  server_->SetPacketFilter(new RecordFragmenter());
+  Connect();
+  SendReceive();
+}
+
+}  // namespace nss_test
--- a/security/nss/gtests/ssl_gtest/ssl_fuzz_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_fuzz_unittest.cc
@@ -7,28 +7,37 @@
 #include "sslimpl.h"
 #include "tls_connect.h"
 
 #include "gtest/gtest.h"
 
 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.
 class TlsApplicationDataRecorder : public TlsRecordFilter {
  public:
   TlsApplicationDataRecorder() : buffer_() {}
 
-  virtual PacketFilter::Action FilterRecord(const RecordHeader& header,
+  virtual PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
                                             const DataBuffer& input,
                                             DataBuffer* output) {
     if (header.content_type() == kTlsApplicationDataType) {
       buffer_.Append(input);
     }
 
     return KEEP;
   }
@@ -56,102 +65,101 @@ class TlsSignatureDamager : public TlsHa
     output->data()[output->len() - 1]++;
     return CHANGE;
   }
 
  private:
   uint8_t type_;
 };
 
-void ResetState() {
-  // Clear the list of RSA blinding params.
-  BL_Cleanup();
-
-  // Reinit the list of RSA blinding params.
-  EXPECT_EQ(SECSuccess, BL_Init());
-
-  // Reset the RNG state.
-  EXPECT_EQ(SECSuccess, RNG_ResetForFuzzing());
-}
-
 // Ensure that ssl_Time() returns a constant value.
-TEST_F(TlsFuzzTest, Fuzz_SSL_Time_Constant) {
-  PRInt32 now = ssl_Time();
+FUZZ_F(TlsFuzzTest, SSL_Time_Constant) {
+  PRUint32 now = ssl_Time();
   PR_Sleep(PR_SecondsToInterval(2));
   EXPECT_EQ(ssl_Time(), now);
 }
 
 // Check that due to the deterministic PRNG we derive
 // the same master secret in two consecutive TLS sessions.
-TEST_P(TlsConnectGeneric, Fuzz_DeterministicExporter) {
+FUZZ_P(TlsConnectGeneric, DeterministicExporter) {
   const char kLabel[] = "label";
   std::vector<unsigned char> out1(32), out2(32);
 
+  // Make sure we have RSA blinding params.
+  Connect();
+
+  Reset();
   ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
   DisableECDHEServerKeyReuse();
 
-  ResetState();
+  // Reset the RNG state.
+  EXPECT_EQ(SECSuccess, RNG_ResetForFuzzing());
   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();
 
-  ResetState();
+  // Reset the RNG state.
+  EXPECT_EQ(SECSuccess, RNG_ResetForFuzzing());
   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.
   EXPECT_EQ(out1, out2);
 }
 
 // Check that due to the deterministic RNG two consecutive
 // TLS sessions will have the exact same transcript.
-TEST_P(TlsConnectGeneric, Fuzz_DeterministicTranscript) {
+FUZZ_P(TlsConnectGeneric, DeterministicTranscript) {
+  // Make sure we have RSA blinding params.
+  Connect();
+
   // Connect a few times and compare the transcripts byte-by-byte.
   DataBuffer last;
   for (size_t i = 0; i < 5; i++) {
     Reset();
     ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
     DisableECDHEServerKeyReuse();
 
     DataBuffer buffer;
     client_->SetPacketFilter(new TlsConversationRecorder(buffer));
     server_->SetPacketFilter(new TlsConversationRecorder(buffer));
 
-    ResetState();
+    // Reset the RNG state.
+    EXPECT_EQ(SECSuccess, RNG_ResetForFuzzing());
     Connect();
 
     // Ensure the filters go away before |buffer| does.
-    client_->SetPacketFilter(nullptr);
-    server_->SetPacketFilter(nullptr);
+    client_->DeletePacketFilter();
+    server_->DeletePacketFilter();
 
     if (last.len() > 0) {
       EXPECT_EQ(last, buffer);
     }
 
     last = buffer;
   }
 }
 
 // Check that we can establish and use a connection
 // with all supported TLS versions, STREAM and DGRAM.
 // Check that records are NOT encrypted.
 // Check that records don't have a MAC.
-TEST_P(TlsConnectGeneric, Fuzz_ConnectSendReceive_NullCipher) {
+FUZZ_P(TlsConnectGeneric, ConnectSendReceive_NullCipher) {
   EnsureTlsSetup();
 
   // Set up app data filters.
   auto client_recorder = new TlsApplicationDataRecorder();
   client_->SetPacketFilter(client_recorder);
   auto server_recorder = new TlsApplicationDataRecorder();
   server_->SetPacketFilter(server_recorder);
 
@@ -170,54 +178,52 @@ TEST_P(TlsConnectGeneric, Fuzz_ConnectSe
   Receive(buf.len());
 
   // Check for plaintext on the wire.
   EXPECT_EQ(buf, client_recorder->buffer());
   EXPECT_EQ(buf, server_recorder->buffer());
 }
 
 // Check that an invalid Finished message doesn't abort the connection.
-TEST_P(TlsConnectGeneric, Fuzz_BogusClientFinished) {
+FUZZ_P(TlsConnectGeneric, BogusClientFinished) {
   EnsureTlsSetup();
 
   auto i1 = new TlsInspectorReplaceHandshakeMessage(
       kTlsHandshakeFinished,
       DataBuffer(kShortEmptyFinished, sizeof(kShortEmptyFinished)));
   client_->SetPacketFilter(i1);
   Connect();
   SendReceive();
 }
 
 // Check that an invalid Finished message doesn't abort the connection.
-TEST_P(TlsConnectGeneric, Fuzz_BogusServerFinished) {
+FUZZ_P(TlsConnectGeneric, BogusServerFinished) {
   EnsureTlsSetup();
 
   auto i1 = new TlsInspectorReplaceHandshakeMessage(
       kTlsHandshakeFinished,
       DataBuffer(kLongEmptyFinished, sizeof(kLongEmptyFinished)));
   server_->SetPacketFilter(i1);
   Connect();
   SendReceive();
 }
 
 // Check that an invalid server auth signature doesn't abort the connection.
-TEST_P(TlsConnectGeneric, Fuzz_BogusServerAuthSignature) {
+FUZZ_P(TlsConnectGeneric, BogusServerAuthSignature) {
   EnsureTlsSetup();
   uint8_t msg_type = version_ == SSL_LIBRARY_VERSION_TLS_1_3
                          ? kTlsHandshakeCertificateVerify
                          : kTlsHandshakeServerKeyExchange;
   server_->SetPacketFilter(new TlsSignatureDamager(msg_type));
   Connect();
   SendReceive();
 }
 
 // Check that an invalid client auth signature doesn't abort the connection.
-TEST_P(TlsConnectGeneric, Fuzz_BogusClientAuthSignature) {
+FUZZ_P(TlsConnectGeneric, BogusClientAuthSignature) {
   EnsureTlsSetup();
   client_->SetupClientAuth();
   server_->RequestClientAuth(true);
   client_->SetPacketFilter(
       new TlsSignatureDamager(kTlsHandshakeCertificateVerify));
   Connect();
 }
-
-#endif
 }
--- a/security/nss/gtests/ssl_gtest/ssl_gtest.gyp
+++ b/security/nss/gtests/ssl_gtest/ssl_gtest.gyp
@@ -20,31 +20,33 @@
         'ssl_damage_unittest.cc',
         'ssl_dhe_unittest.cc',
         'ssl_drop_unittest.cc',
         'ssl_ecdh_unittest.cc',
         'ssl_ems_unittest.cc',
         'ssl_exporter_unittest.cc',
         'ssl_extension_unittest.cc',
         'ssl_fuzz_unittest.cc',
+        'ssl_fragment_unittest.cc',
         'ssl_gtest.cc',
         'ssl_hrr_unittest.cc',
         'ssl_loopback_unittest.cc',
         'ssl_record_unittest.cc',
         'ssl_resumption_unittest.cc',
         'ssl_skip_unittest.cc',
         'ssl_staticrsa_unittest.cc',
         'ssl_v2_client_hello_unittest.cc',
         'ssl_version_unittest.cc',
         'test_io.cc',
         'tls_agent.cc',
         'tls_connect.cc',
         'tls_filter.cc',
         'tls_hkdf_unittest.cc',
-        'tls_parser.cc'
+        'tls_parser.cc',
+        'tls_protect.cc'
       ],
       'dependencies': [
         '<(DEPTH)/exports.gyp:nss_exports',
         '<(DEPTH)/lib/util/util.gyp:nssutil3',
         '<(DEPTH)/lib/sqlite/sqlite.gyp:sqlite3',
         '<(DEPTH)/gtests/google_test/google_test.gyp:gtest',
         '<(DEPTH)/lib/softoken/softoken.gyp:softokn',
         '<(DEPTH)/lib/smime/smime.gyp:smime',
--- a/security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc
@@ -156,26 +156,25 @@ TEST_P(TlsConnectDatagram, TestDtlsHoldd
     // One for send, one for receive.
     EXPECT_EQ(2, SSLInt_CountTls13CipherSpecs(client_->ssl_fd()));
   }
 }
 
 class TlsPreCCSHeaderInjector : public TlsRecordFilter {
  public:
   TlsPreCCSHeaderInjector() {}
-  virtual PacketFilter::Action FilterRecord(const RecordHeader& record_header,
-                                            const DataBuffer& input,
-                                            size_t* offset,
-                                            DataBuffer* output) override {
+  virtual PacketFilter::Action FilterRecord(
+      const TlsRecordHeader& record_header, const DataBuffer& input,
+      size_t* offset, DataBuffer* output) override {
     if (record_header.content_type() != kTlsChangeCipherSpecType) return KEEP;
 
     std::cerr << "Injecting Finished header before CCS\n";
     const uint8_t hhdr[] = {kTlsHandshakeFinished, 0x00, 0x00, 0x0c};
     DataBuffer hhdr_buf(hhdr, sizeof(hhdr));
-    RecordHeader nhdr(record_header.version(), kTlsHandshakeType, 0);
+    TlsRecordHeader nhdr(record_header.version(), kTlsHandshakeType, 0);
     *offset = nhdr.Write(output, *offset, hhdr_buf);
     *offset = record_header.Write(output, *offset, input);
     return CHANGE;
   }
 };
 
 TEST_P(TlsConnectStreamPre13, ClientFinishedHeaderBeforeCCS) {
   client_->SetPacketFilter(new TlsPreCCSHeaderInjector());
--- a/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc
@@ -16,16 +16,17 @@ extern "C" {
 #include "libssl_internals.h"
 }
 
 #include "gtest_utils.h"
 #include "scoped_ptrs.h"
 #include "tls_connect.h"
 #include "tls_filter.h"
 #include "tls_parser.h"
+#include "tls_protect.h"
 
 namespace nss_test {
 
 class TlsServerKeyExchangeEcdhe {
  public:
   bool Parse(const DataBuffer& buffer) {
     TlsParser parser(buffer);
 
@@ -574,9 +575,68 @@ TEST_F(TlsConnectTest, TestTls13Resumpti
   Reset();
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
   ExpectResumption(RESUME_TICKET);
   Connect();
   SendReceive();
 }
 
+TEST_F(TlsConnectTest, TestTls13ResumptionDowngrade) {
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+  Connect();
+
+  SendReceive();  // Need to read so that we absorb the session tickets.
+  CheckKeys();
+
+  // Try resuming the connection. This will fail resuming the 1.3 session
+  // from before, but will successfully establish a 1.2 connection.
+  Reset();
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  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_2);
+  Connect();
+
+  // Renegotiate to ensure we don't carryover any state
+  // from the 1.3 resumption attempt.
+  client_->SetExpectedVersion(SSL_LIBRARY_VERSION_TLS_1_2);
+  client_->PrepareForRenegotiate();
+  server_->StartRenegotiate();
+  Handshake();
+
+  SendReceive();
+  CheckKeys();
+}
+
+TEST_F(TlsConnectTest, TestTls13ResumptionForcedDowngrade) {
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+  Connect();
+
+  SendReceive();  // Need to read so that we absorb the session tickets.
+  CheckKeys();
+
+  // Try resuming the connection.
+  Reset();
+  ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
+  ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
+  // Enable the lower version on the client.
+  client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+                           SSL_LIBRARY_VERSION_TLS_1_3);
+
+  // Add filters that set downgrade SH.version to 1.2 and the cipher suite
+  // to one that works with 1.2, so that we don't run into early sanity checks.
+  // We will eventually fail the (sid.version == SH.version) check.
+  std::vector<PacketFilter*> filters;
+  filters.push_back(new SelectedCipherSuiteReplacer(
+      TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256));
+  filters.push_back(new SelectedVersionReplacer(SSL_LIBRARY_VERSION_TLS_1_2));
+  server_->SetPacketFilter(new ChainedPacketFilter(filters));
+
+  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
@@ -23,19 +23,19 @@ class TlsHandshakeSkipFilter : public Tl
  public:
   // A TLS record filter that skips handshake messages of the identified type.
   TlsHandshakeSkipFilter(uint8_t handshake_type)
       : handshake_type_(handshake_type), skipped_(false) {}
 
  protected:
   // Takes a record; if it is a handshake record, it removes the first handshake
   // message that is of handshake_type_ type.
-  virtual PacketFilter::Action FilterRecord(const RecordHeader& record_header,
-                                            const DataBuffer& input,
-                                            DataBuffer* output) {
+  virtual PacketFilter::Action FilterRecord(
+      const TlsRecordHeader& record_header, const DataBuffer& input,
+      DataBuffer* output) {
     if (record_header.content_type() != kTlsHandshakeType) {
       return KEEP;
     }
 
     size_t output_offset = 0U;
     output->Allocate(input.len());
 
     TlsParser parser(input);
@@ -93,16 +93,50 @@ class TlsSkipTest
       server_->SetPacketFilter(filter);
     }
     ConnectExpectFail();
     EXPECT_EQ(kTlsAlertFatal, alert_recorder->level());
     EXPECT_EQ(alert, alert_recorder->description());
   }
 };
 
+class Tls13SkipTest : public TlsConnectTestBase,
+                      public ::testing::WithParamInterface<std::string> {
+ protected:
+  Tls13SkipTest()
+      : TlsConnectTestBase(GetParam(), SSL_LIBRARY_VERSION_TLS_1_3) {}
+
+  void ServerSkipTest(TlsRecordFilter* filter, int32_t error) {
+    EnsureTlsSetup();
+    server_->SetPacketFilter(filter);
+    filter->EnableDecryption();
+    if (mode_ == STREAM) {
+      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(TlsRecordFilter* filter, int32_t error) {
+    EnsureTlsSetup();
+    client_->SetPacketFilter(filter);
+    filter->EnableDecryption();
+    ConnectExpectFailOneSide(TlsAgent::SERVER);
+
+    server_->CheckErrorCode(error);
+    ASSERT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
+  }
+};
+
 TEST_P(TlsSkipTest, SkipCertificateRsa) {
   EnableOnlyStaticRsaCiphers();
   ServerSkipTest(new TlsHandshakeSkipFilter(kTlsHandshakeCertificate));
   client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_HELLO_DONE);
 }
 
 TEST_P(TlsSkipTest, SkipCertificateDhe) {
   ServerSkipTest(new TlsHandshakeSkipFilter(kTlsHandshakeCertificate));
@@ -143,16 +177,46 @@ TEST_P(TlsSkipTest, SkipCertAndKeyExchEc
   Reset(TlsAgent::kServerEcdsa256);
   auto chain = new ChainedPacketFilter();
   chain->Add(new TlsHandshakeSkipFilter(kTlsHandshakeCertificate));
   chain->Add(new TlsHandshakeSkipFilter(kTlsHandshakeServerKeyExchange));
   ServerSkipTest(chain);
   client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_HELLO_DONE);
 }
 
+TEST_P(Tls13SkipTest, SkipEncryptedExtensions) {
+  ServerSkipTest(new TlsHandshakeSkipFilter(kTlsHandshakeEncryptedExtensions),
+                 SSL_ERROR_RX_UNEXPECTED_CERTIFICATE);
+}
+
+TEST_P(Tls13SkipTest, SkipServerCertificate) {
+  ServerSkipTest(new TlsHandshakeSkipFilter(kTlsHandshakeCertificate),
+                 SSL_ERROR_RX_UNEXPECTED_CERT_VERIFY);
+}
+
+TEST_P(Tls13SkipTest, SkipServerCertificateVerify) {
+  ServerSkipTest(new TlsHandshakeSkipFilter(kTlsHandshakeCertificateVerify),
+                 SSL_ERROR_RX_UNEXPECTED_FINISHED);
+}
+
+TEST_P(Tls13SkipTest, SkipClientCertificate) {
+  client_->SetupClientAuth();
+  server_->RequestClientAuth(true);
+  ClientSkipTest(new TlsHandshakeSkipFilter(kTlsHandshakeCertificate),
+                 SSL_ERROR_RX_UNEXPECTED_CERT_VERIFY);
+}
+
+TEST_P(Tls13SkipTest, SkipClientCertificateVerify) {
+  client_->SetupClientAuth();
+  server_->RequestClientAuth(true);
+  ClientSkipTest(new TlsHandshakeSkipFilter(kTlsHandshakeCertificateVerify),
+                 SSL_ERROR_RX_UNEXPECTED_FINISHED);
+}
+
 INSTANTIATE_TEST_CASE_P(SkipTls10, TlsSkipTest,
                         ::testing::Combine(TlsConnectTestBase::kTlsModesStream,
                                            TlsConnectTestBase::kTlsV10));
 INSTANTIATE_TEST_CASE_P(SkipVariants, TlsSkipTest,
                         ::testing::Combine(TlsConnectTestBase::kTlsModesAll,
                                            TlsConnectTestBase::kTlsV11V12));
-
+INSTANTIATE_TEST_CASE_P(Skip13Variants, Tls13SkipTest,
+                        TlsConnectTestBase::kTlsModesAll);
 }  // namespace nss_test
--- a/security/nss/gtests/ssl_gtest/tls_agent.cc
+++ b/security/nss/gtests/ssl_gtest/tls_agent.cc
@@ -844,16 +844,17 @@ void TlsAgent::ConfigureSessionCache(Ses
   EXPECT_EQ(SECSuccess, rv);
 
   rv = SSL_OptionSet(ssl_fd_, SSL_ENABLE_SESSION_TICKETS,
                      mode & RESUME_TICKET ? PR_TRUE : PR_FALSE);
   EXPECT_EQ(SECSuccess, rv);
 }
 
 void TlsAgent::DisableECDHEServerKeyReuse() {
+  ASSERT_TRUE(EnsureTlsSetup());
   ASSERT_EQ(TlsAgent::SERVER, role_);
   SECStatus rv = SSL_OptionSet(ssl_fd_, SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE);
   EXPECT_EQ(SECSuccess, rv);
 }
 
 static const std::string kTlsRolesAllArr[] = {"CLIENT", "SERVER"};
 ::testing::internal::ParamGenerator<std::string>
     TlsAgentTestBase::kTlsRolesAll = ::testing::ValuesIn(kTlsRolesAllArr);
--- a/security/nss/gtests/ssl_gtest/tls_agent.h
+++ b/security/nss/gtests/ssl_gtest/tls_agent.h
@@ -9,16 +9,17 @@
 
 #include "prio.h"
 #include "ssl.h"
 
 #include <functional>
 #include <iostream>
 
 #include "test_io.h"
+#include "tls_filter.h"
 
 #define GTEST_HAS_RTTI 0
 #include "gtest/gtest.h"
 
 extern bool g_ssl_gtest_verbose;
 
 namespace nss_test {
 
@@ -80,20 +81,27 @@ class TlsAgent : public PollTarget {
     adapter_ = DummyPrSocket::GetAdapter(pr_fd_);
     if (!adapter_) return false;
 
     return true;
   }
 
   void SetPeer(TlsAgent* peer) { adapter_->SetPeer(peer->adapter_); }
 
+  void SetPacketFilter(TlsRecordFilter* filter) {
+    filter->SetAgent(this);
+    adapter_->SetPacketFilter(filter);
+  }
+
   void SetPacketFilter(PacketFilter* filter) {
     adapter_->SetPacketFilter(filter);
   }
 
+  void DeletePacketFilter() { adapter_->SetPacketFilter(nullptr); }
+
   void StartConnect(PRFileDesc* model = nullptr);
   void CheckKEA(SSLKEAType kea_type, SSLNamedGroup group,
                 size_t kea_size = 0) const;
   void CheckAuthType(SSLAuthType auth_type,
                      SSLSignatureScheme sig_scheme) const;
 
   void DisableAllCiphers();
   void EnableCiphersByAuthType(SSLAuthType authType);
@@ -166,17 +174,17 @@ class TlsAgent : public PollTarget {
   const CERTCertificate* peer_cert() const {
     return SSL_PeerCertificate(ssl_fd_);
   }
 
   const char* state_str() const { return state_str(state()); }
 
   static const char* state_str(State state) { return states[state]; }
 
-  PRFileDesc* ssl_fd() { return ssl_fd_; }
+  PRFileDesc* ssl_fd() const { return ssl_fd_; }
   DummyPrSocket* adapter() { return adapter_; }
 
   bool is_compressed() const {
     return info_.compressionMethod != ssl_compression_null;
   }
   uint16_t server_key_bits() const { return server_key_bits_; }
   uint16_t min_version() const { return vrange_.min; }
   uint16_t max_version() const { return vrange_.max; }
--- a/security/nss/gtests/ssl_gtest/tls_connect.cc
+++ b/security/nss/gtests/ssl_gtest/tls_connect.cc
@@ -368,16 +368,32 @@ 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::ConnectExpectFailOneSide(TlsAgent::Role failing_side) {
+  server_->StartConnect();
+  client_->StartConnect();
+  client_->SetServerKeyBits(server_->server_key_bits());
+  client_->Handshake();
+  server_->Handshake();
+  TlsAgent* fail_agent;
+
+  if (failing_side == TlsAgent::CLIENT) {
+    fail_agent = client_;
+  } else {
+    fail_agent = server_;
+  }
+  ASSERT_TRUE_WAIT(fail_agent->state() == TlsAgent::STATE_ERROR, 5000);
+}
+
 void TlsConnectTestBase::ConfigureVersion(uint16_t version) {
   client_->SetVersionRange(version, version);
   server_->SetVersionRange(version, version);
 }
 
 void TlsConnectTestBase::SetExpectedVersion(uint16_t version) {
   client_->SetExpectedVersion(version);
   server_->SetExpectedVersion(version);
--- a/security/nss/gtests/ssl_gtest/tls_connect.h
+++ b/security/nss/gtests/ssl_gtest/tls_connect.h
@@ -63,16 +63,17 @@ 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 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.
   void CheckKeys() const;
--- a/security/nss/gtests/ssl_gtest/tls_filter.cc
+++ b/security/nss/gtests/ssl_gtest/tls_filter.cc
@@ -10,30 +10,86 @@
 extern "C" {
 // This is not something that should make you happy.
 #include "libssl_internals.h"
 }
 
 #include <iostream>
 #include "gtest_utils.h"
 #include "tls_agent.h"
+#include "tls_filter.h"
+#include "tls_protect.h"
 
 namespace nss_test {
 
+void TlsVersioned::WriteStream(std::ostream& stream) const {
+  stream << (is_dtls() ? "DTLS " : "TLS ");
+  switch (version()) {
+    case 0:
+      stream << "(no version)";
+      break;
+    case SSL_LIBRARY_VERSION_TLS_1_0:
+      stream << "1.0";
+      break;
+    case SSL_LIBRARY_VERSION_DTLS_1_0_WIRE:
+    case SSL_LIBRARY_VERSION_TLS_1_1:
+      stream << (is_dtls() ? "1.0" : "1.1");
+      break;
+    case SSL_LIBRARY_VERSION_DTLS_1_2_WIRE:
+    case SSL_LIBRARY_VERSION_TLS_1_2:
+      stream << "1.2";
+      break;
+    case SSL_LIBRARY_VERSION_TLS_1_3:
+      stream << "1.3";
+      break;
+    default:
+      stream << "Invalid version: " << version();
+      break;
+  }
+}
+
+void TlsRecordFilter::EnableDecryption() {
+  SSLInt_SetCipherSpecChangeFunc(agent()->ssl_fd(), CipherSpecChanged,
+                                 (void*)this);
+}
+
+void TlsRecordFilter::CipherSpecChanged(void* arg, PRBool sending,
+                                        ssl3CipherSpec* newSpec) {
+  TlsRecordFilter* self = static_cast<TlsRecordFilter*>(arg);
+  PRBool isServer = self->agent()->role() == TlsAgent::SERVER;
+
+  if (g_ssl_gtest_verbose) {
+    std::cerr << "Cipher spec changed. Role="
+              << (isServer ? "server" : "client")
+              << " direction=" << (sending ? "send" : "receive") << std::endl;
+  }
+  if (!sending) return;
+
+  self->cipher_spec_.reset(new TlsCipherSpec());
+  bool ret =
+      self->cipher_spec_->Init(SSLInt_CipherSpecToAlgorithm(isServer, newSpec),
+                               SSLInt_CipherSpecToKey(isServer, newSpec),
+                               SSLInt_CipherSpecToIv(isServer, newSpec));
+  EXPECT_EQ(true, ret);
+}
+
 PacketFilter::Action TlsRecordFilter::Filter(const DataBuffer& input,
                                              DataBuffer* output) {
   bool changed = false;
   size_t offset = 0U;
   output->Allocate(input.len());
 
   TlsParser parser(input);
+
   while (parser.remaining()) {
-    RecordHeader header;
+    TlsRecordHeader header;
     DataBuffer record;
+
     if (!header.Parse(&parser, &record)) {
+      ADD_FAILURE() << "not a valid record";
       return KEEP;
     }
 
     if (FilterRecord(header, record, &offset, output) != KEEP) {
       changed = true;
     } else {
       offset = header.Write(output, offset, record);
     }
@@ -44,44 +100,55 @@ PacketFilter::Action TlsRecordFilter::Fi
   if (changed) {
     ++count_;
     return (offset == 0) ? DROP : CHANGE;
   }
 
   return KEEP;
 }
 
-PacketFilter::Action TlsRecordFilter::FilterRecord(const RecordHeader& header,
-                                                   const DataBuffer& record,
-                                                   size_t* offset,
-                                                   DataBuffer* output) {
+PacketFilter::Action TlsRecordFilter::FilterRecord(
+    const TlsRecordHeader& header, const DataBuffer& record, size_t* offset,
+    DataBuffer* output) {
   DataBuffer filtered;
-  PacketFilter::Action action = FilterRecord(header, record, &filtered);
+  uint8_t inner_content_type;
+  DataBuffer plaintext;
+
+  if (!Unprotect(header, record, &inner_content_type, &plaintext)) {
+    return KEEP;
+  }
+
+  TlsRecordHeader real_header = {header.version(), inner_content_type,
+                                 header.sequence_number()};
+
+  PacketFilter::Action action = FilterRecord(real_header, plaintext, &filtered);
   if (action == KEEP) {
     return KEEP;
   }
 
   if (action == DROP) {
     std::cerr << "record drop: " << record << std::endl;
     return DROP;
   }
 
-  const DataBuffer* source = &record;
-  if (action == CHANGE) {
-    EXPECT_GT(0x10000U, filtered.len());
-    std::cerr << "record old: " << record << std::endl;
-    std::cerr << "record new: " << filtered << std::endl;
-    source = &filtered;
+  EXPECT_GT(0x10000U, filtered.len());
+  std::cerr << "record old: " << plaintext << std::endl;
+  std::cerr << "record new: " << filtered << std::endl;
+
+  DataBuffer ciphertext;
+  bool rv = Protect(header, inner_content_type, filtered, &ciphertext);
+  EXPECT_TRUE(rv);
+  if (!rv) {
+    return KEEP;
   }
-
-  *offset = header.Write(output, *offset, *source);
+  *offset = header.Write(output, *offset, ciphertext);
   return CHANGE;
 }
 
-bool TlsRecordFilter::RecordHeader::Parse(TlsParser* parser, DataBuffer* body) {
+bool TlsRecordHeader::Parse(TlsParser* parser, DataBuffer* body) {
   if (!parser->Read(&content_type_)) {
     return false;
   }
 
   uint32_t version;
   if (!parser->Read(&version, 2)) {
     return false;
   }
@@ -97,32 +164,72 @@ bool TlsRecordFilter::RecordHeader::Pars
     if (!parser->Read(&tmp, 4)) {
       return false;
     }
     sequence_number_ |= static_cast<uint64_t>(tmp);
   }
   return parser->ReadVariable(body, 2);
 }
 
-size_t TlsRecordFilter::RecordHeader::Write(DataBuffer* buffer, size_t offset,
-                                            const DataBuffer& body) const {
+size_t TlsRecordHeader::Write(DataBuffer* buffer, size_t offset,
+                              const DataBuffer& body) const {
   offset = buffer->Write(offset, content_type_, 1);
   offset = buffer->Write(offset, version_, 2);
   if (is_dtls()) {
     // write epoch (2 octet), and seqnum (6 octet)
     offset = buffer->Write(offset, sequence_number_ >> 32, 4);
     offset = buffer->Write(offset, sequence_number_ & 0xffffffff, 4);
   }
   offset = buffer->Write(offset, body.len(), 2);
   offset = buffer->Write(offset, body);
   return offset;
 }
 
+bool TlsRecordFilter::Unprotect(const TlsRecordHeader& header,
+                                const DataBuffer& ciphertext,
+                                uint8_t* inner_content_type,
+                                DataBuffer* plaintext) {
+  if (!cipher_spec_ || header.content_type() != kTlsApplicationDataType) {
+    *inner_content_type = header.content_type();
+    *plaintext = ciphertext;
+    return true;
+  }
+
+  if (!cipher_spec_->Unprotect(header, ciphertext, plaintext)) return false;
+
+  size_t len = plaintext->len();
+  while (len > 0 && !plaintext->data()[len - 1]) {
+    --len;
+  }
+  if (!len) {
+    // Bogus padding.
+    return false;
+  }
+
+  *inner_content_type = plaintext->data()[len - 1];
+  plaintext->Truncate(len - 1);
+
+  return true;
+}
+
+bool TlsRecordFilter::Protect(const TlsRecordHeader& header,
+                              uint8_t inner_content_type,
+                              const DataBuffer& plaintext,
+                              DataBuffer* ciphertext) {
+  if (!cipher_spec_ || header.content_type() != kTlsApplicationDataType) {
+    *ciphertext = plaintext;
+    return true;
+  }
+  DataBuffer padded = plaintext;
+  padded.Write(padded.len(), inner_content_type, 1);
+  return cipher_spec_->Protect(header, padded, ciphertext);
+}
+
 PacketFilter::Action TlsHandshakeFilter::FilterRecord(
-    const RecordHeader& record_header, const DataBuffer& input,
+    const TlsRecordHeader& record_header, const DataBuffer& input,
     DataBuffer* output) {
   // Check that the first byte is as requested.
   if (record_header.content_type() != kTlsHandshakeType) {
     return KEEP;
   }
 
   bool changed = false;
   size_t offset = 0U;
@@ -154,19 +261,18 @@ PacketFilter::Action TlsHandshakeFilter:
     }
 
     offset = header.Write(output, offset, *source);
   }
   output->Truncate(offset);
   return changed ? (offset ? CHANGE : DROP) : KEEP;
 }
 
-bool TlsHandshakeFilter::HandshakeHeader::ReadLength(TlsParser* parser,
-                                                     const RecordHeader& header,
-                                                     uint32_t* length) {
+bool TlsHandshakeFilter::HandshakeHeader::ReadLength(
+    TlsParser* parser, const TlsRecordHeader& header, uint32_t* length) {
   if (!parser->Read(length, 3)) {
     return false;  // malformed
   }
 
   if (!header.is_dtls()) {
     return true;  // nothing left to do
   }
 
@@ -187,38 +293,51 @@ bool TlsHandshakeFilter::HandshakeHeader
     return false;
   }
 
   // All current tests where we are using this code don't fragment.
   return (fragment_offset == 0 && fragment_length == *length);
 }
 
 bool TlsHandshakeFilter::HandshakeHeader::Parse(
-    TlsParser* parser, const RecordHeader& record_header, DataBuffer* body) {
+    TlsParser* parser, const TlsRecordHeader& record_header, DataBuffer* body) {
   version_ = record_header.version();
   if (!parser->Read(&handshake_type_)) {
     return false;  // malformed
   }
   uint32_t length;
   if (!ReadLength(parser, record_header, &length)) {
     return false;
   }
 
   return parser->Read(body, length);
 }
 
-size_t TlsHandshakeFilter::HandshakeHeader::Write(
-    DataBuffer* buffer, size_t offset, const DataBuffer& body) const {
+size_t TlsHandshakeFilter::HandshakeHeader::WriteFragment(
+    DataBuffer* buffer, size_t offset, const DataBuffer& body,
+    size_t fragment_offset, size_t fragment_length) const {
+  EXPECT_TRUE(is_dtls());
+  EXPECT_GE(body.len(), fragment_offset + fragment_length);
   offset = buffer->Write(offset, handshake_type(), 1);
   offset = buffer->Write(offset, body.len(), 3);
+  offset = buffer->Write(offset, message_seq_, 2);
+  offset = buffer->Write(offset, fragment_offset, 3);
+  offset = buffer->Write(offset, fragment_length, 3);
+  offset =
+      buffer->Write(offset, body.data() + fragment_offset, fragment_length);
+  return offset;
+}
+
+size_t TlsHandshakeFilter::HandshakeHeader::Write(
+    DataBuffer* buffer, size_t offset, const DataBuffer& body) const {
   if (is_dtls()) {
-    offset = buffer->Write(offset, message_seq_, 2);
-    offset = buffer->Write(offset, 0U, 3);  // fragment_offset
-    offset = buffer->Write(offset, body.len(), 3);
+    return WriteFragment(buffer, offset, body, 0U, body.len());
   }
+  offset = buffer->Write(offset, handshake_type(), 1);
+  offset = buffer->Write(offset, body.len(), 3);
   offset = buffer->Write(offset, body);
   return offset;
 }
 
 PacketFilter::Action TlsInspectorRecordHandshakeMessage::FilterHandshake(
     const HandshakeHeader& header, const DataBuffer& input,
     DataBuffer* output) {
   // Only do this once.
@@ -239,24 +358,25 @@ PacketFilter::Action TlsInspectorReplace
     *output = buffer_;
     return CHANGE;
   }
 
   return KEEP;
 }
 
 PacketFilter::Action TlsConversationRecorder::FilterRecord(
-    const RecordHeader& header, const DataBuffer& input, DataBuffer* output) {
+    const TlsRecordHeader& header, const DataBuffer& input,
+    DataBuffer* output) {
   buffer_.Append(input);
   return KEEP;
 }
 
-PacketFilter::Action TlsAlertRecorder::FilterRecord(const RecordHeader& header,
-                                                    const DataBuffer& input,
-                                                    DataBuffer* output) {
+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;
@@ -313,17 +433,17 @@ PacketFilter::Action TlsExtensionFilter:
       return KEEP;
     }
     return FilterExtensions(&parser, input, output);
   }
   return KEEP;
 }
 
 bool TlsExtensionFilter::FindClientHelloExtensions(TlsParser* parser,
-                                                   const Versioned& header) {
+                                                   const TlsVersioned& header) {
   if (!parser->Skip(2 + 32)) {  // version + random
     return false;
   }
   if (!parser->SkipVariable(1)) {  // session ID
     return false;
   }
   if (header.is_dtls() && !parser->SkipVariable(1)) {  // DTLS cookie
     return false;
@@ -451,17 +571,17 @@ PacketFilter::Action TlsExtensionReplace
 PacketFilter::Action TlsExtensionDropper::FilterExtension(
     uint16_t extension_type, const DataBuffer& input, DataBuffer* output) {
   if (extension_type == extension_) {
     return DROP;
   }
   return KEEP;
 }
 
-PacketFilter::Action AfterRecordN::FilterRecord(const RecordHeader& header,
+PacketFilter::Action AfterRecordN::FilterRecord(const TlsRecordHeader& header,
                                                 const DataBuffer& body,
                                                 DataBuffer* out) {
   if (counter_++ == record_) {
     DataBuffer buf;
     header.Write(&buf, 0, body);
     src_->SendDirect(buf);
     dest_->Handshake();
     func_();
--- a/security/nss/gtests/ssl_gtest/tls_filter.h
+++ b/security/nss/gtests/ssl_gtest/tls_filter.h
@@ -8,123 +8,180 @@
 #define tls_filter_h_
 
 #include <functional>
 #include <memory>
 #include <vector>
 
 #include "test_io.h"
 #include "tls_parser.h"
+#include "tls_protect.h"
+
+extern "C" {
+#include "libssl_internals.h"
+}
 
 namespace nss_test {
 
+class TlsCipherSpec;
+class TlsAgent;
+
+class TlsVersioned {
+ public:
+  TlsVersioned() : version_(0) {}
+  explicit TlsVersioned(uint16_t version) : version_(version) {}
+
+  bool is_dtls() const { return IsDtls(version_); }
+  uint16_t version() const { return version_; }
+
+  void WriteStream(std::ostream& stream) const;
+
+ protected:
+  uint16_t version_;
+};
+
+class TlsRecordHeader : public TlsVersioned {
+ public:
+  TlsRecordHeader() : TlsVersioned(), content_type_(0), sequence_number_(0) {}
+  TlsRecordHeader(uint16_t version, uint8_t content_type,
+                  uint64_t sequence_number)
+      : TlsVersioned(version),
+        content_type_(content_type),
+        sequence_number_(sequence_number) {}
+
+  uint8_t content_type() const { return content_type_; }
+  uint64_t sequence_number() const { return sequence_number_; }
+  size_t header_length() const { return is_dtls() ? 11 : 3; }
+
+  // Parse the header; return true if successful; body in an outparam if OK.
+  bool Parse(TlsParser* parser, DataBuffer* body);
+  // Write the header and body to a buffer at the given offset.
+  // Return the offset of the end of the write.
+  size_t Write(DataBuffer* buffer, size_t offset, const DataBuffer& body) const;
+
+ private:
+  uint8_t content_type_;
+  uint64_t sequence_number_;
+};
+
 // Abstract filter that operates on entire (D)TLS records.
 class TlsRecordFilter : public PacketFilter {
  public:
-  TlsRecordFilter() : count_(0) {}
+  TlsRecordFilter() : agent_(nullptr), count_(0), cipher_spec_() {}
+
+  void SetAgent(const TlsAgent* agent) { agent_ = agent; }
+  const TlsAgent* agent() const { return agent_; }
 
   // External interface. Overrides PacketFilter.
   PacketFilter::Action Filter(const DataBuffer& input, DataBuffer* output);
 
   // Report how many packets were altered by the filter.
   size_t filtered_packets() const { return count_; }
 
-  class Versioned {
-   public:
-    Versioned() : version_(0) {}
-    explicit Versioned(uint16_t version) : version_(version) {}
-
-    bool is_dtls() const { return IsDtls(version_); }
-    uint16_t version() const { return version_; }
-
-   protected:
-    uint16_t version_;
-  };
-
-  class RecordHeader : public Versioned {
-   public:
-    RecordHeader() : Versioned(), content_type_(0), sequence_number_(0) {}
-    RecordHeader(uint16_t version, uint8_t content_type,
-                 uint64_t sequence_number)
-        : Versioned(version),
-          content_type_(content_type),
-          sequence_number_(sequence_number) {}
-
-    uint8_t content_type() const { return content_type_; }
-    uint64_t sequence_number() const { return sequence_number_; }
-    size_t header_length() const { return is_dtls() ? 11 : 3; }
-
-    // Parse the header; return true if successful; body in an outparam if OK.
-    bool Parse(TlsParser* parser, DataBuffer* body);
-    // Write the header and body to a buffer at the given offset.
-    // Return the offset of the end of the write.
-    size_t Write(DataBuffer* buffer, size_t offset,
-                 const DataBuffer& body) const;
-
-   private:
-    uint8_t content_type_;
-    uint64_t sequence_number_;
-  };
+  // Enable decryption. This only works properly for TLS 1.3 and above.
+  // Enabling it for lower version tests will cause undefined
+  // behavior.
+  void EnableDecryption();
+  bool Unprotect(const TlsRecordHeader& header, const DataBuffer& cipherText,
+                 uint8_t* inner_content_type, DataBuffer* plaintext);
+  bool Protect(const TlsRecordHeader& header, uint8_t inner_content_type,
+               const DataBuffer& plaintext, DataBuffer* ciphertext);
 
  protected:
   // There are two filter functions which can be overriden. Both are
   // called with the header and the record but the outer one is called
   // with a raw pointer to let you write into the buffer and lets you
   // do anything with this section of the stream. The inner one
   // just lets you change the record contents. By default, the
   // outer one calls the inner one, so if you override the outer
   // one, the inner one is never called unless you call it yourself.
-  virtual PacketFilter::Action FilterRecord(const RecordHeader& header,
+  virtual PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
                                             const DataBuffer& record,
                                             size_t* offset, DataBuffer* output);
 
   // The record filter receives the record contentType, version and DTLS
   // sequence number (which is zero for TLS), plus the existing record payload.
   // It returns an action (KEEP, CHANGE, DROP).  It writes to the `changed`
   // outparam with the new record contents if it chooses to CHANGE the record.
-  virtual PacketFilter::Action FilterRecord(const RecordHeader& header,
+  virtual PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
                                             const DataBuffer& data,
                                             DataBuffer* changed) {
     return KEEP;
   }
 
  private:
+  static void CipherSpecChanged(void* arg, PRBool sending,
+                                ssl3CipherSpec* newSpec);
+
+  const TlsAgent* agent_;
   size_t count_;
+  std::unique_ptr<TlsCipherSpec> cipher_spec_;
 };
 
+inline std::ostream& operator<<(std::ostream& stream, TlsVersioned v) {
+  v.WriteStream(stream);
+  return stream;
+}
+
+inline std::ostream& operator<<(std::ostream& stream, TlsRecordHeader& hdr) {
+  hdr.WriteStream(stream);
+  stream << ' ';
+  switch (hdr.content_type()) {
+    case kTlsChangeCipherSpecType:
+      stream << "CCS";
+      break;
+    case kTlsAlertType:
+      stream << "Alert";
+      break;
+    case kTlsHandshakeType:
+      stream << "Handshake";
+      break;
+    case kTlsApplicationDataType:
+      stream << "Data";
+      break;
+    default:
+      stream << '<' << hdr.content_type() << '>';
+      break;
+  }
+  return stream << ' ' << std::hex << hdr.sequence_number() << std::dec;
+}
+
 // Abstract filter that operates on handshake messages rather than records.
 // This assumes that the handshake messages are written in a block as entire
 // records and that they don't span records or anything crazy like that.
 class TlsHandshakeFilter : public TlsRecordFilter {
  public:
   TlsHandshakeFilter() {}
 
-  class HandshakeHeader : public Versioned {
+  class HandshakeHeader : public TlsVersioned {
    public:
-    HandshakeHeader() : Versioned(), handshake_type_(0), message_seq_(0) {}
+    HandshakeHeader() : TlsVersioned(), handshake_type_(0), message_seq_(0) {}
 
     uint8_t handshake_type() const { return handshake_type_; }
-    bool Parse(TlsParser* parser, const RecordHeader& record_header,
+    bool Parse(TlsParser* parser, const TlsRecordHeader& record_header,
                DataBuffer* body);
     size_t Write(DataBuffer* buffer, size_t offset,
                  const DataBuffer& body) const;
+    size_t WriteFragment(DataBuffer* buffer, size_t offset,
+                         const DataBuffer& body, size_t fragment_offset,
+                         size_t fragment_length) const;
 
    private:
     // Reads the length from the record header.
     // This also reads the DTLS fragment information and checks it.
-    bool ReadLength(TlsParser* parser, const RecordHeader& header,
+    bool ReadLength(TlsParser* parser, const TlsRecordHeader& header,
                     uint32_t* length);
 
     uint8_t handshake_type_;
     uint16_t message_seq_;
     // fragment_offset is always zero in these tests.
   };
 
  protected:
-  virtual PacketFilter::Action FilterRecord(const RecordHeader& header,
+  virtual PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
                                             const DataBuffer& input,
                                             DataBuffer* output);
   virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
                                                const DataBuffer& input,
                                                DataBuffer* output) = 0;
 
  private:
 };
@@ -162,31 +219,31 @@ class TlsInspectorReplaceHandshakeMessag
   DataBuffer buffer_;
 };
 
 // Make a copy of the complete conversation.
 class TlsConversationRecorder : public TlsRecordFilter {
  public:
   TlsConversationRecorder(DataBuffer& buffer) : buffer_(buffer) {}
 
-  virtual PacketFilter::Action FilterRecord(const RecordHeader& header,
+  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 RecordHeader& header,
+  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_;
@@ -218,17 +275,17 @@ class TlsExtensionFilter : public TlsHan
                                        DataBuffer* output) override;
 
   virtual PacketFilter::Action FilterExtension(uint16_t extension_type,
                                                const DataBuffer& input,
                                                DataBuffer* output) = 0;
 
  public:
   static bool FindClientHelloExtensions(TlsParser* parser,
-                                        const Versioned& header);
+                                        const TlsVersioned& header);
   static bool FindServerHelloExtensions(TlsParser* parser);
 
  private:
   PacketFilter::Action FilterExtensions(TlsParser* parser,
                                         const DataBuffer& input,
                                         DataBuffer* output);
 };
 
@@ -279,17 +336,17 @@ class TlsAgent;
 typedef std::function<void(void)> VoidFunction;
 
 class AfterRecordN : public TlsRecordFilter {
  public:
   AfterRecordN(TlsAgent* src, TlsAgent* dest, unsigned int record,
                VoidFunction func)
       : src_(src), dest_(dest), record_(record), func_(func), counter_(0) {}
 
-  virtual PacketFilter::Action FilterRecord(const RecordHeader& header,
+  virtual PacketFilter::Action FilterRecord(const TlsRecordHeader& header,
                                             const DataBuffer& body,
                                             DataBuffer* out) override;
 
  private:
   TlsAgent* src_;
   TlsAgent* dest_;
   unsigned int record_;
   VoidFunction func_;
new file mode 100644
--- /dev/null
+++ b/security/nss/gtests/ssl_gtest/tls_protect.cc
@@ -0,0 +1,145 @@
+/* -*- 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 "tls_protect.h"
+#include "tls_filter.h"
+
+namespace nss_test {
+
+AeadCipher::~AeadCipher() {
+  if (key_) {
+    PK11_FreeSymKey(key_);
+  }
+}
+
+bool AeadCipher::Init(PK11SymKey *key, const uint8_t *iv) {
+  key_ = PK11_ReferenceSymKey(key);
+  if (!key_) return false;
+
+  memcpy(iv_, iv, sizeof(iv_));
+  return true;
+}
+
+void AeadCipher::FormatNonce(uint64_t seq, uint8_t *nonce) {
+  memcpy(nonce, iv_, 12);
+
+  for (size_t i = 0; i < 8; ++i) {
+    nonce[12 - (i + 1)] ^= seq & 0xff;
+    seq >>= 8;
+  }
+
+  DataBuffer d(nonce, 12);
+  std::cerr << "Nonce " << d << std::endl;
+}
+
+bool AeadCipher::AeadInner(bool decrypt, void *params, size_t param_length,
+                           const uint8_t *in, size_t inlen, uint8_t *out,
+                           size_t *outlen, size_t maxlen) {
+  SECStatus rv;
+  unsigned int uoutlen = 0;
+  SECItem param = {
+      siBuffer, static_cast<unsigned char *>(params),
+      static_cast<unsigned int>(param_length),
+  };
+
+  if (decrypt) {
+    rv = PK11_Decrypt(key_, mech_, &param, out, &uoutlen, maxlen, in, inlen);
+  } else {
+    rv = PK11_Encrypt(key_, mech_, &param, out, &uoutlen, maxlen, in, inlen);
+  }
+  *outlen = (int)uoutlen;
+
+  return rv == SECSuccess;
+}
+
+bool AeadCipherAesGcm::Aead(bool decrypt, uint64_t seq, const uint8_t *in,
+                            size_t inlen, uint8_t *out, size_t *outlen,
+                            size_t maxlen) {
+  CK_GCM_PARAMS aeadParams;
+  unsigned char nonce[12];
+
+  memset(&aeadParams, 0, sizeof(aeadParams));
+  aeadParams.pIv = nonce;
+  aeadParams.ulIvLen = sizeof(nonce);
+  aeadParams.pAAD = NULL;
+  aeadParams.ulAADLen = 0;
+  aeadParams.ulTagBits = 128;
+
+  FormatNonce(seq, nonce);
+  return AeadInner(decrypt, (unsigned char *)&aeadParams, sizeof(aeadParams),
+                   in, inlen, out, outlen, maxlen);
+}
+
+bool AeadCipherChacha20Poly1305::Aead(bool decrypt, uint64_t seq,
+                                      const uint8_t *in, size_t inlen,
+                                      uint8_t *out, size_t *outlen,
+                                      size_t maxlen) {
+  CK_NSS_AEAD_PARAMS aeadParams;
+  unsigned char nonce[12];
+
+  memset(&aeadParams, 0, sizeof(aeadParams));
+  aeadParams.pNonce = nonce;
+  aeadParams.ulNonceLen = sizeof(nonce);
+  aeadParams.pAAD = NULL;
+  aeadParams.ulAADLen = 0;
+  aeadParams.ulTagLen = 16;
+
+  FormatNonce(seq, nonce);
+  return AeadInner(decrypt, (unsigned char *)&aeadParams, sizeof(aeadParams),
+                   in, inlen, out, outlen, maxlen);
+}
+
+bool TlsCipherSpec::Init(SSLCipherAlgorithm cipher, PK11SymKey *key,
+                         const uint8_t *iv) {
+  switch (cipher) {
+    case ssl_calg_aes_gcm:
+      aead_.reset(new AeadCipherAesGcm());
+      break;
+    case ssl_calg_chacha20:
+      aead_.reset(new AeadCipherChacha20Poly1305());
+      break;
+    default:
+      return false;
+  }
+
+  return aead_->Init(key, iv);
+}
+
+bool TlsCipherSpec::Unprotect(const TlsRecordHeader &header,
+                              const DataBuffer &ciphertext,
+                              DataBuffer *plaintext) {
+  // Make space.
+  plaintext->Allocate(ciphertext.len());
+
+  size_t len;
+  bool ret =
+      aead_->Aead(true, header.sequence_number(), ciphertext.data(),
+                  ciphertext.len(), plaintext->data(), &len, plaintext->len());
+  if (!ret) return false;
+
+  plaintext->Truncate(len);
+
+  return true;
+}
+
+bool TlsCipherSpec::Protect(const TlsRecordHeader &header,
+                            const DataBuffer &plaintext,
+                            DataBuffer *ciphertext) {
+  // Make a padded buffer.
+
+  ciphertext->Allocate(plaintext.len() +
+                       32);  // Room for any plausible auth tag
+  size_t len;
+  bool ret =
+      aead_->Aead(false, header.sequence_number(), plaintext.data(),
+                  plaintext.len(), ciphertext->data(), &len, ciphertext->len());
+  if (!ret) return false;
+  ciphertext->Truncate(len);
+
+  return true;
+}
+
+}  // namespace nss_test
new file mode 100644
--- /dev/null
+++ b/security/nss/gtests/ssl_gtest/tls_protect.h
@@ -0,0 +1,76 @@
+/* -*- 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/. */
+
+#ifndef tls_protection_h_
+#define tls_protection_h_
+
+#include <cstdint>
+#include <memory>
+
+#include "databuffer.h"
+#include "pk11pub.h"
+#include "sslt.h"
+
+namespace nss_test {
+class TlsRecordHeader;
+
+class AeadCipher {
+ public:
+  AeadCipher(CK_MECHANISM_TYPE mech) : mech_(mech), key_(nullptr) {}
+  ~AeadCipher();
+
+  bool Init(PK11SymKey *key, const uint8_t *iv);
+  virtual bool Aead(bool decrypt, uint64_t seq, const uint8_t *in, size_t inlen,
+                    uint8_t *out, size_t *outlen, size_t maxlen) = 0;
+
+ protected:
+  void FormatNonce(uint64_t seq, uint8_t *nonce);
+  bool AeadInner(bool decrypt, void *params, size_t param_length,
+                 const uint8_t *in, size_t inlen, uint8_t *out, size_t *outlen,
+                 size_t maxlen);
+
+  CK_MECHANISM_TYPE mech_;
+  PK11SymKey *key_;
+  uint8_t iv_[12];
+};
+
+class AeadCipherChacha20Poly1305 : public AeadCipher {
+ public:
+  AeadCipherChacha20Poly1305() : AeadCipher(CKM_NSS_CHACHA20_POLY1305) {}
+
+ protected:
+  bool Aead(bool decrypt, uint64_t seq, const uint8_t *in, size_t inlen,
+            uint8_t *out, size_t *outlen, size_t maxlen);
+};
+
+class AeadCipherAesGcm : public AeadCipher {
+ public:
+  AeadCipherAesGcm() : AeadCipher(CKM_AES_GCM) {}
+
+ protected:
+  bool Aead(bool decrypt, uint64_t seq, const uint8_t *in, size_t inlen,
+            uint8_t *out, size_t *outlen, size_t maxlen);
+};
+
+// Our analog of ssl3CipherSpec
+class TlsCipherSpec {
+ public:
+  TlsCipherSpec() : aead_() {}
+
+  bool Init(SSLCipherAlgorithm cipher, PK11SymKey *key, const uint8_t *iv);
+
+  bool Protect(const TlsRecordHeader &header, const DataBuffer &plaintext,
+               DataBuffer *ciphertext);
+  bool Unprotect(const TlsRecordHeader &header, const DataBuffer &ciphertext,
+                 DataBuffer *plaintext);
+
+ private:
+  std::unique_ptr<AeadCipher> aead_;
+};
+
+}  // namespace nss_test
+
+#endif
--- a/security/nss/lib/certhigh/ocsp.c
+++ b/security/nss/lib/certhigh/ocsp.c
@@ -2190,17 +2190,17 @@ CERT_CreateOCSPRequest(CERTCertList *cer
 void
 SetRequestExts(void *object, CERTCertExtension **exts)
 {
     CERTOCSPRequest *request = (CERTOCSPRequest *)object;
 
     request->tbsRequest->requestExtensions = exts;
 }
 
-#if defined(__GNUC__)
+#if defined(__GNUC__) && !defined(NSS_NO_GCC48)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wvarargs"
 #endif
 SECStatus
 CERT_AddOCSPAcceptableResponses(CERTOCSPRequest *request,
                                 SECOidTag responseType0, ...)
 {
     void *extHandle;
@@ -2260,17 +2260,17 @@ CERT_AddOCSPAcceptableResponses(CERTOCSP
 
 loser:
     if (acceptableResponses != NULL)
         PORT_Free(acceptableResponses);
     if (extHandle != NULL)
         (void)CERT_FinishExtensions(extHandle);
     return rv;
 }
-#if defined(__GNUC__)
+#if defined(__GNUC__) && !defined(NSS_NO_GCC48)
 #pragma GCC diagnostic pop
 #endif
 
 /*
  * FUNCTION: CERT_DestroyOCSPRequest
  *   Frees an OCSP Request structure.
  * INPUTS:
  *   CERTOCSPRequest *request
--- a/security/nss/lib/freebl/rsa.c
+++ b/security/nss/lib/freebl/rsa.c
@@ -1231,17 +1231,20 @@ get_blinding_params(RSAPrivateKey *key, 
             */
             PR_INSERT_BEFORE(&rsabp->link, el);
         }
 
         /* We've found (or created) the RSAblindingParams struct for this key.
          * Now, search its list of ready blinding params for a usable one.
          */
         while (0 != (bp = rsabp->bp)) {
-            if (--(bp->counter) > 0) {
+#ifndef UNSAFE_FUZZER_MODE
+            if (--(bp->counter) > 0)
+#endif
+            {
                 /* Found a match and there are still remaining uses left */
                 /* Return the parameters */
                 CHECK_MPI_OK(mp_copy(&bp->f, f));
                 CHECK_MPI_OK(mp_copy(&bp->g, g));
 
                 PZ_Unlock(blindingParamsList.lock);
                 return SECSuccess;
             }
--- a/security/nss/lib/ssl/SSLerrs.h
+++ b/security/nss/lib/ssl/SSLerrs.h
@@ -499,9 +499,9 @@ ER3(SSL_ERROR_BAD_2ND_CLIENT_HELLO, (SSL
 
 ER3(SSL_ERROR_MISSING_SIGNATURE_ALGORITHMS_EXTENSION, (SSL_ERROR_BASE + 157),
     "SSL expected a signature algorithms extension.")
 
 ER3(SSL_ERROR_MALFORMED_PSK_KEY_EXCHANGE_MODES, (SSL_ERROR_BASE + 158),
     "SSL received a malformed PSK key exchange modes extension.")
 
 ER3(SSL_ERROR_MISSING_PSK_KEY_EXCHANGE_MODES, (SSL_ERROR_BASE + 159),
-    "SSL expected a missing PSK key exchange modes extension.")
+    "SSL expected a PSK key exchange modes extension.")
--- a/security/nss/lib/ssl/dtlscon.c
+++ b/security/nss/lib/ssl/dtlscon.c
@@ -230,16 +230,36 @@ dtls_RetransmitDetected(sslSocket *ss)
 
     } else {
         PORT_Assert(ss->ssl3.hs.rtTimerCb == NULL);
         /* ... and ignore it. */
     }
     return rv;
 }
 
+static SECStatus
+dtls_HandleHandshakeMessage(sslSocket *ss, SSL3Opaque *data, PRBool last)
+{
+
+    /* At this point we are advancing our state machine, so we can free our last
+     * flight of messages. */
+    dtls_FreeHandshakeMessages(&ss->ssl3.hs.lastMessageFlight);
+    ss->ssl3.hs.recvdHighWater = -1;
+
+    /* Reset the timer to the initial value if the retry counter
+     * is 0, per Sec. 4.2.4.1 */
+    dtls_CancelTimer(ss);
+    if (ss->ssl3.hs.rtRetries == 0) {
+        ss->ssl3.hs.rtTimeoutMs = DTLS_RETRANSMIT_INITIAL_MS;
+    }
+
+    return ssl3_HandleHandshakeMessage(ss, data, ss->ssl3.hs.msg_len,
+                                       last);
+}
+
 /* Called only from ssl3_HandleRecord, for each (deciphered) DTLS record.
  * origBuf is the decrypted ssl record content and is expected to contain
  * complete handshake records
  * Caller must hold the handshake and RecvBuf locks.
  *
  * Note that this code uses msg_len for two purposes:
  *
  * (1) To pass the length to ssl3_HandleHandshakeMessage()
@@ -324,33 +344,20 @@ dtls_HandleHandshake(sslSocket *ss, sslB
          */
         if ((message_seq == ss->ssl3.hs.recvMessageSeq) &&
             (fragment_offset == 0) &&
             (fragment_length == message_length)) {
             /* Complete next message. Process immediately */
             ss->ssl3.hs.msg_type = (SSL3HandshakeType)type;
             ss->ssl3.hs.msg_len = message_length;
 
-            /* At this point we are advancing our state machine, so
-             * we can free our last flight of messages */
-            dtls_FreeHandshakeMessages(&ss->ssl3.hs.lastMessageFlight);
-            ss->ssl3.hs.recvdHighWater = -1;
-            dtls_CancelTimer(ss);
-
-            /* Reset the timer to the initial value if the retry counter
-             * is 0, per Sec. 4.2.4.1 */
-            if (ss->ssl3.hs.rtRetries == 0) {
-                ss->ssl3.hs.rtTimeoutMs = DTLS_RETRANSMIT_INITIAL_MS;
-            }
-
-            rv = ssl3_HandleHandshakeMessage(ss, buf.buf, ss->ssl3.hs.msg_len,
+            rv = dtls_HandleHandshakeMessage(ss, buf.buf,
                                              buf.len == fragment_length);
             if (rv == SECFailure) {
-                /* Do not attempt to process rest of messages in this record */
-                break;
+                break; /* Discard the remainder of the record. */
             }
         } else {
             if (message_seq < ss->ssl3.hs.recvMessageSeq) {
                 /* Case 3: we do an immediate retransmit if we're
                  * in a waiting state. */
                 rv = dtls_RetransmitDetected(ss);
                 break;
             } else if (message_seq > ss->ssl3.hs.recvMessageSeq) {
@@ -441,34 +448,21 @@ dtls_HandleHandshake(sslSocket *ss, sslB
                         ss->ssl3.hs.recvdHighWater++;
                     } else {
                         break;
                     }
                 }
 
                 /* If we have all the bytes, then we are good to go */
                 if (ss->ssl3.hs.recvdHighWater == ss->ssl3.hs.msg_len) {
-                    ss->ssl3.hs.recvdHighWater = -1;
-
-                    rv = ssl3_HandleHandshakeMessage(
-                        ss,
-                        ss->ssl3.hs.msg_body.buf, ss->ssl3.hs.msg_len,
-                        buf.len == fragment_length);
-                    if (rv == SECFailure)
-                        break; /* Skip rest of record */
+                    rv = dtls_HandleHandshakeMessage(ss, ss->ssl3.hs.msg_body.buf,
+                                                     buf.len == fragment_length);
 
-                    /* At this point we are advancing our state machine, so
-                     * we can free our last flight of messages */
-                    dtls_FreeHandshakeMessages(&ss->ssl3.hs.lastMessageFlight);
-                    dtls_CancelTimer(ss);
-
-                    /* If there have been no retries this time, reset the
-                     * timer value to the default per Section 4.2.4.1 */
-                    if (ss->ssl3.hs.rtRetries == 0) {
-                        ss->ssl3.hs.rtTimeoutMs = DTLS_RETRANSMIT_INITIAL_MS;
+                    if (rv == SECFailure) {
+                        break; /* Discard the rest of the record. */
                     }
                 }
             }
         }
 
         buf.buf += fragment_length;
         buf.len -= fragment_length;
     }
--- a/security/nss/lib/ssl/ssl.def
+++ b/security/nss/lib/ssl/ssl.def
@@ -209,20 +209,15 @@ SSL_ConfigServerCert;
 ;+NSS_3.27 {    # NSS 3.27 release
 ;+    global:
 SSL_NamedGroupConfig;
 ;+    local:
 ;+*;
 ;+};
 ;+NSS_3.28 {    # NSS 3.28 release
 ;+    global:
+SSL_ExportEarlyKeyingMaterial;
 SSL_SendAdditionalKeyShares;
 SSL_SignatureSchemePrefSet;
 SSL_SignatureSchemePrefGet;
 ;+    local:
 ;+*;
 ;+};
-;+NSS_3.29 {    # NSS 3.29 release
-;+    global:
-SSL_ExportEarlyKeyingMaterial;
-;+    local:
-;+*;
-;+};
--- a/security/nss/lib/ssl/ssl3con.c
+++ b/security/nss/lib/ssl/ssl3con.c
@@ -1083,20 +1083,21 @@ ssl3_NegotiateVersion(sslSocket *ss, SSL
 
 /* Used by the client when the server produces a version number.
  * This reads, validates, and normalizes the value. */
 SECStatus
 ssl_ClientReadVersion(sslSocket *ss, SSL3Opaque **b, unsigned int *len,
                       SSL3ProtocolVersion *version)
 {
     SSL3ProtocolVersion v;
-    PRInt32 temp;
-
-    temp = ssl3_ConsumeHandshakeNumber(ss, 2, b, len);
-    if (temp < 0) {
+    PRUint32 temp;
+    SECStatus rv;
+
+    rv = ssl3_ConsumeHandshakeNumber(ss, &temp, 2, b, len);
+    if (rv != SECSuccess) {
         return SECFailure; /* alert has been sent */
     }
 
 #ifdef TLS_1_3_DRAFT_VERSION
     if (temp == SSL_LIBRARY_VERSION_TLS_1_3) {
         (void)SSL3_SendAlert(ss, alert_fatal, protocol_version);
         PORT_SetError(SSL_ERROR_UNSUPPORTED_VERSION);
         return SECFailure;
@@ -4306,17 +4307,17 @@ ssl3_AppendHandshakeHeader(sslSocket *ss
  * stream "b" (which is *length bytes long). Copy them into buffer "v".
  * Reduces *length by bytes.  Advances *b by bytes.
  *
  * If this function returns SECFailure, it has already sent an alert,
  * and has set a generic error code.  The caller should probably
  * override the generic error code by setting another.
  */
 SECStatus
-ssl3_ConsumeHandshake(sslSocket *ss, void *v, PRInt32 bytes, SSL3Opaque **b,
+ssl3_ConsumeHandshake(sslSocket *ss, void *v, PRUint32 bytes, SSL3Opaque **b,
                       PRUint32 *length)
 {
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
     if ((PRUint32)bytes > *length) {
         return ssl3_DecodeError(ss);
     }
@@ -4324,47 +4325,43 @@ ssl3_ConsumeHandshake(sslSocket *ss, voi
     PRINT_BUF(60, (ss, "consume bytes:", *b, bytes));
     *b += bytes;
     *length -= bytes;
     return SECSuccess;
 }
 
 /* Read up the next "bytes" number of bytes from the (decrypted) input
  * stream "b" (which is *length bytes long), and interpret them as an
- * integer in network byte order.  Returns the received value.
+ * integer in network byte order.  Sets *num to the received value.
  * Reduces *length by bytes.  Advances *b by bytes.
  *
- * Returns SECFailure (-1) on failure.
- * This value is indistinguishable from the equivalent received value.
- * Only positive numbers are to be received this way.
- * Thus, the largest value that may be sent this way is 0x7fffffff.
  * On error, an alert has been sent, and a generic error code has been set.
  */
-PRInt32
-ssl3_ConsumeHandshakeNumber(sslSocket *ss, PRInt32 bytes, SSL3Opaque **b,
-                            PRUint32 *length)
+SECStatus
+ssl3_ConsumeHandshakeNumber(sslSocket *ss, PRUint32 *num, PRUint32 bytes,
+                            SSL3Opaque **b, PRUint32 *length)
 {
     PRUint8 *buf = *b;
     int i;
-    PRInt32 num = 0;
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
-    PORT_Assert(bytes <= sizeof num);
-
-    if ((PRUint32)bytes > *length) {
+
+    *num = 0;
+    if (bytes > *length || bytes > sizeof(*num)) {
         return ssl3_DecodeError(ss);
     }
     PRINT_BUF(60, (ss, "consume bytes:", *b, bytes));
 
-    for (i = 0; i < bytes; i++)
-        num = (num << 8) + buf[i];
+    for (i = 0; i < bytes; i++) {
+        *num = (*num << 8) + buf[i];
+    }
     *b += bytes;
     *length -= bytes;
-    return num;
+    return SECSuccess;
 }
 
 /* Read in two values from the incoming decrypted byte stream "b", which is
  * *length bytes long.  The first value is a number whose size is "bytes"
  * bytes long.  The second value is a byte-string whose size is the value
  * of the first number received.  The latter byte-string, and its length,
  * is returned in the SECItem i.
  *
@@ -4372,31 +4369,32 @@ ssl3_ConsumeHandshakeNumber(sslSocket *s
  * On error, an alert has been sent, and a generic error code has been set.
  *
  * RADICAL CHANGE for NSS 3.11.  All callers of this function make copies
  * of the data returned in the SECItem *i, so making a copy of it here
  * is simply wasteful.  So, This function now just sets SECItem *i to
  * point to the values in the buffer **b.
  */
 SECStatus
-ssl3_ConsumeHandshakeVariable(sslSocket *ss, SECItem *i, PRInt32 bytes,
+ssl3_ConsumeHandshakeVariable(sslSocket *ss, SECItem *i, PRUint32 bytes,
                               SSL3Opaque **b, PRUint32 *length)
 {
-    PRInt32 count;
+    PRUint32 count;
+    SECStatus rv;
 
     PORT_Assert(bytes <= 3);
     i->len = 0;
     i->data = NULL;
     i->type = siBuffer;
-    count = ssl3_ConsumeHandshakeNumber(ss, bytes, b, length);
-    if (count < 0) { /* Can't test for SECSuccess here. */
+    rv = ssl3_ConsumeHandshakeNumber(ss, &count, bytes, b, length);
+    if (rv != SECSuccess) {
         return SECFailure;
     }
     if (count > 0) {
-        if ((PRUint32)count > *length) {
+        if (count > *length) {
             return ssl3_DecodeError(ss);
         }
         i->data = *b;
         i->len = count;
         *b += count;
         *length -= count;
     }
     return SECSuccess;
@@ -4657,20 +4655,21 @@ ssl_IsRsaPssSignatureScheme(SSLSignature
  * SignatureAndHashAlgorithm) structure from |b| and puts the resulting value
  * into |out|. |b| and |length| are updated accordingly.
  *
  * See https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */
 SECStatus
 ssl_ConsumeSignatureScheme(sslSocket *ss, SSL3Opaque **b,
                            PRUint32 *length, SSLSignatureScheme *out)
 {
-    PRInt32 tmp;
-
-    tmp = ssl3_ConsumeHandshakeNumber(ss, 2, b, length);
-    if (tmp < 0) {
+    PRUint32 tmp;
+    SECStatus rv;
+
+    rv = ssl3_ConsumeHandshakeNumber(ss, &tmp, 2, b, length);
+    if (rv != SECSuccess) {
         return SECFailure; /* Error code set already. */
     }
     if (!ssl_IsSupportedSignatureScheme((SSLSignatureScheme)tmp)) {
         PORT_SetError(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
         return SECFailure;
     }
     *out = (SSLSignatureScheme)tmp;
     return SECSuccess;
@@ -6582,17 +6581,17 @@ ssl3_SetCipherSuite(sslSocket *ss, ssl3C
 
 /* Called from ssl3_HandleHandshakeMessage() when it has deciphered a complete
  * ssl3 ServerHello message.
  * Caller must hold Handshake and RecvBuf locks.
  */
 static SECStatus
 ssl3_HandleServerHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
-    PRInt32 temp; /* allow for consume number failure */
+    PRUint32 temp;
     PRBool suite_found = PR_FALSE;
     int i;
     int errCode = SSL_ERROR_RX_MALFORMED_SERVER_HELLO;
     SECStatus rv;
     SECItem sidBytes = { siBuffer, NULL, 0 };
     PRBool isTLS = PR_FALSE;
     SSL3AlertDescription desc = illegal_parameter;
 #ifndef TLS_1_3_DRAFT_VERSION
@@ -6625,18 +6624,21 @@ ssl3_HandleServerHello(sslSocket *ss, SS
         ss->ssl3.clientPrivateKey = NULL;
     }
 
     rv = ssl_ClientReadVersion(ss, &b, &length, &ss->version);
     if (rv != SECSuccess) {
         goto loser; /* alert has been sent */
     }
 
-    /* We got a HelloRetryRequest, but the server didn't pick 1.3.  Scream. */
-    if (ss->ssl3.hs.helloRetry && ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+    /* The server didn't pick 1.3 although we either received a
+     * HelloRetryRequest, or we prepared to send early app data. */
+    if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3 &&
+        (ss->ssl3.hs.helloRetry || ss->ssl3.hs.zeroRttState == ssl_0rtt_sent)) {
+        /* SSL3_SendAlert() will uncache the SID. */
         desc = illegal_parameter;
         errCode = SSL_ERROR_RX_MALFORMED_SERVER_HELLO;
         goto alert_loser;
     }
 
     /* Check that the server negotiated the same version as it did
      * in the first handshake. This isn't really the best place for
      * us to be getting this version number, but it's what we have.
@@ -6697,18 +6699,18 @@ ssl3_HandleServerHello(sslSocket *ss, SS
         if (sidBytes.len > SSL3_SESSIONID_BYTES) {
             if (isTLS)
                 desc = decode_error;
             goto alert_loser; /* malformed. */
         }
     }
 
     /* find selected cipher suite in our list. */
-    temp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
-    if (temp < 0) {
+    rv = ssl3_ConsumeHandshakeNumber(ss, &temp, 2, &b, &length);
+    if (rv != SECSuccess) {
         goto loser; /* alert has been sent */
     }
     i = ssl3_config_match_init(ss);
     PORT_Assert(i > 0);
     if (i <= 0) {
         errCode = PORT_GetError();
         goto loser;
     }
@@ -6743,18 +6745,18 @@ ssl3_HandleServerHello(sslSocket *ss, SS
     if (rv != SECSuccess) {
         desc = internal_error;
         errCode = PORT_GetError();
         goto alert_loser;
     }
 
     if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
         /* find selected compression method in our list. */
-        temp = ssl3_ConsumeHandshakeNumber(ss, 1, &b, &length);
-        if (temp < 0) {
+        rv = ssl3_ConsumeHandshakeNumber(ss, &temp, 1, &b, &length);
+        if (rv != SECSuccess) {
             goto loser; /* alert has been sent */
         }
         suite_found = PR_FALSE;
         for (i = 0; i < ssl_compression_method_count; i++) {
             if (temp == ssl_compression_methods[i]) {
                 if (!ssl_CompressionEnabled(ss, ssl_compression_methods[i])) {
                     break; /* failure */
                 }
@@ -6986,16 +6988,29 @@ ssl3_HandleServerHelloPart2(sslSocket *s
         } while (0);
     }
 
     if (sid_match)
         SSL_AtomicIncrementLong(&ssl3stats.hsh_sid_cache_not_ok);
     else
         SSL_AtomicIncrementLong(&ssl3stats.hsh_sid_cache_misses);
 
+    /* We tried to resume a 1.3 session but the server negotiated 1.2. */
+    if (ss->statelessResume) {
+        PORT_Assert(sid->version == SSL_LIBRARY_VERSION_TLS_1_3);
+        PORT_Assert(ss->ssl3.hs.currentSecret);
+
+        /* Reset resumption state, only used by 1.3 code. */
+        ss->statelessResume = PR_FALSE;
+
+        /* Clear TLS 1.3 early data traffic key. */
+        PK11_FreeSymKey(ss->ssl3.hs.currentSecret);
+        ss->ssl3.hs.currentSecret = NULL;
+    }
+
     /* throw the old one away */
     sid->u.ssl3.keys.resumable = PR_FALSE;
     ss->sec.uncache(sid);
     ssl_FreeSID(sid);
 
     /* get a new sid */
     ss->sec.ci.sid = sid = ssl3_NewSessionID(ss, PR_FALSE);
     if (sid == NULL) {
@@ -7252,46 +7267,47 @@ typedef struct dnameNode {
  * Called from:
  * ssl3_HandleCertificateRequest
  * tls13_HandleCertificateRequest
  */
 SECStatus
 ssl3_ParseCertificateRequestCAs(sslSocket *ss, SSL3Opaque **b, PRUint32 *length,
                                 PLArenaPool *arena, CERTDistNames *ca_list)
 {
-    PRInt32 remaining;
+    PRUint32 remaining;
     int nnames = 0;
     dnameNode *node;
+    SECStatus rv;
     int i;
 
-    remaining = ssl3_ConsumeHandshakeNumber(ss, 2, b, length);
-    if (remaining < 0)
+    rv = ssl3_ConsumeHandshakeNumber(ss, &remaining, 2, b, length);
+    if (rv != SECSuccess)
         return SECFailure; /* malformed, alert has been sent */
 
-    if ((PRUint32)remaining > *length)
+    if (remaining > *length)
         goto alert_loser;
 
     ca_list->head = node = PORT_ArenaZNew(arena, dnameNode);
     if (node == NULL)
         goto no_mem;
 
     while (remaining > 0) {
-        PRInt32 len;
+        PRUint32 len;
 
         if (remaining < 2)
             goto alert_loser; /* malformed */
 
-        node->name.len = len = ssl3_ConsumeHandshakeNumber(ss, 2, b, length);
-        if (len <= 0)
+        rv = ssl3_ConsumeHandshakeNumber(ss, &len, 2, b, length);
+        if (rv != SECSuccess)
             return SECFailure; /* malformed, alert has been sent */
+        if (len == 0 || remaining < len + 2)
+            goto alert_loser; /* malformed */
 
         remaining -= 2;
-        if (remaining < len)
-            goto alert_loser; /* malformed */
-
+        node->name.len = len;
         node->name.data = *b;
         *b += len;
         *length -= len;
         remaining -= len;
         nnames++;
         if (remaining <= 0)
             break; /* success */
 
@@ -7357,19 +7373,19 @@ ssl_ParseSignatureSchemes(const sslSocke
         schemes = PORT_ZNewArray(SSLSignatureScheme, max);
     }
     if (!schemes) {
         ssl3_ExtSendAlert(ss, alert_fatal, internal_error);
         return SECFailure;
     }
 
     for (; max; --max) {
-        PRInt32 tmp;
-        tmp = ssl3_ExtConsumeHandshakeNumber(ss, 2, &buf.data, &buf.len);
-        if (tmp < 0) {
+        PRUint32 tmp;
+        rv = ssl3_ExtConsumeHandshakeNumber(ss, &tmp, 2, &buf.data, &buf.len);
+        if (rv != SECSuccess) {
             PORT_Assert(0);
             PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
             return SECFailure;
         }
         if (ssl_IsSupportedSignatureScheme((SSLSignatureScheme)tmp)) {
             schemes[numSchemes++] = (SSLSignatureScheme)tmp;
         }
     }
@@ -8223,17 +8239,17 @@ ssl3_SelectServerCert(sslSocket *ss)
 /* Called from ssl3_HandleHandshakeMessage() when it has deciphered a complete
  * ssl3 Client Hello message.
  * Caller must hold Handshake and RecvBuf locks.
  */
 static SECStatus
 ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
     sslSessionID *sid = NULL;
-    PRInt32 tmp;
+    PRUint32 tmp;
     unsigned int i;
     SECStatus rv;
     int errCode = SSL_ERROR_RX_MALFORMED_CLIENT_HELLO;
     SSL3AlertDescription desc = illegal_parameter;
     SSL3AlertLevel level = alert_fatal;
     SSL3ProtocolVersion version;
     TLSExtension *versionExtension;
     SECItem sidBytes = { siBuffer, NULL, 0 };
@@ -8283,18 +8299,18 @@ ssl3_HandleClientHello(sslSocket *ss, SS
      */
     ssl3_ResetExtensionData(&ss->xtnData);
     ss->statelessResume = PR_FALSE;
 
     if (IS_DTLS(ss)) {
         dtls_RehandshakeCleanup(ss);
     }
 
-    tmp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
-    if (tmp < 0)
+    rv = ssl3_ConsumeHandshakeNumber(ss, &tmp, 2, &b, &length);
+    if (rv != SECSuccess)
         goto loser; /* malformed, alert already sent */
 
     /* Translate the version. */
     if (IS_DTLS(ss)) {
         ss->clientHelloVersion = version =
             dtls_DTLSVersionToTLSVersion((SSL3ProtocolVersion)tmp);
     } else {
         ss->clientHelloVersion = version = (SSL3ProtocolVersion)tmp;
@@ -8337,19 +8353,19 @@ ssl3_HandleClientHello(sslSocket *ss, SS
      * we are restarting a previous session until extensions have been
      * parsed, since we might have received a SessionTicket extension.
      * Note: we allow extensions even when negotiating SSL3 for the sake
      * of interoperability (and backwards compatibility).
      */
 
     if (length) {
         /* Get length of hello extensions */
-        PRInt32 extension_length;
-        extension_length = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
-        if (extension_length < 0) {
+        PRUint32 extension_length;
+        rv = ssl3_ConsumeHandshakeNumber(ss, &extension_length, 2, &b, &length);
+        if (rv != SECSuccess) {
             goto loser; /* alert already sent */
         }
         if (extension_length != length) {
             ssl3_DecodeError(ss); /* send alert */
             goto loser;
         }
 
         rv = ssl3_ParseExtensions(ss, &b, &length);
@@ -9891,19 +9907,19 @@ ssl3_HandleRSAClientKeyExchange(sslSocke
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
     PORT_Assert(ss->ssl3.prSpec == ss->ssl3.pwSpec);
 
     enc_pms.data = b;
     enc_pms.len = length;
 
     if (ss->ssl3.prSpec->version > SSL_LIBRARY_VERSION_3_0) { /* isTLS */
-        PRInt32 kLen;
-        kLen = ssl3_ConsumeHandshakeNumber(ss, 2, &enc_pms.data, &enc_pms.len);
-        if (kLen < 0) {
+        PRUint32 kLen;
+        rv = ssl3_ConsumeHandshakeNumber(ss, &kLen, 2, &enc_pms.data, &enc_pms.len);
+        if (rv != SECSuccess) {
             PORT_SetError(SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE);
             return SECFailure;
         }
         if ((unsigned)kLen < enc_pms.len) {
             enc_pms.len = kLen;
         }
     }
 
@@ -10213,16 +10229,17 @@ loser:
     return rv;
 }
 
 static SECStatus
 ssl3_HandleNewSessionTicket(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
     SECStatus rv;
     SECItem ticketData;
+    PRUint32 temp;
 
     SSL_TRC(3, ("%d: SSL3[%d]: handle session_ticket handshake",
                 SSL_GETPID(), ss->fd));
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
     PORT_Assert(!ss->ssl3.hs.newSessionTicket.ticket.data);
@@ -10239,18 +10256,23 @@ ssl3_HandleNewSessionTicket(sslSocket *s
      * ssl3_FinishHandshake for more details.
      */
     ss->ssl3.hs.newSessionTicket.received_timestamp = ssl_Time();
     if (length < 4) {
         (void)SSL3_SendAlert(ss, alert_fatal, decode_error);
         PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET);
         return SECFailure;
     }
-    ss->ssl3.hs.newSessionTicket.ticket_lifetime_hint =
-        (PRUint32)ssl3_ConsumeHandshakeNumber(ss, 4, &b, &length);
+
+    rv = ssl3_ConsumeHandshakeNumber(ss, &temp, 4, &b, &length);
+    if (rv != SECSuccess) {
+        PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET);
+        return SECFailure;
+    }
+    ss->ssl3.hs.newSessionTicket.ticket_lifetime_hint = temp;
 
     rv = ssl3_ConsumeHandshakeVariable(ss, &ticketData, 2, &b, &length);
     if (rv != SECSuccess || length != 0) {
         (void)SSL3_SendAlert(ss, alert_fatal, decode_error);
         PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET);
         return SECFailure; /* malformed */
     }
     /* If the server sent a zero-length ticket, ignore it and keep the
@@ -10535,31 +10557,30 @@ ssl3_HandleCertificateStatus(sslSocket *
     }
 
     return ssl3_AuthCertificate(ss);
 }
 
 SECStatus
 ssl_ReadCertificateStatus(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
-    PRInt32 status, len;
+    PRUint32 status, len;
+    SECStatus rv;
 
     PORT_Assert(!ss->sec.isServer);
 
     /* Consume the CertificateStatusType enum */
-    status = ssl3_ConsumeHandshakeNumber(ss, 1, &b, &length);
-    if (status != 1 /* ocsp */) {
-        ssl3_DecodeError(ss); /* sets error code */
-        return SECFailure;
-    }
-
-    len = ssl3_ConsumeHandshakeNumber(ss, 3, &b, &length);
-    if (len != length) {
-        ssl3_DecodeError(ss); /* sets error code */
-        return SECFailure;
+    rv = ssl3_ConsumeHandshakeNumber(ss, &status, 1, &b, &length);
+    if (rv != SECSuccess || status != 1 /* ocsp */) {
+        return ssl3_DecodeError(ss);
+    }
+
+    rv = ssl3_ConsumeHandshakeNumber(ss, &len, 3, &b, &length);
+    if (rv != SECSuccess || len != length) {
+        return ssl3_DecodeError(ss);
     }
 
 #define MAX_CERTSTATUS_LEN 0x1ffff /* 128k - 1 */
     if (length > MAX_CERTSTATUS_LEN) {
         ssl3_DecodeError(ss); /* sets error code */
         return SECFailure;
     }
 #undef MAX_CERTSTATUS_LEN
@@ -10606,37 +10627,37 @@ ssl3_HandleCertificate(sslSocket *ss, SS
 
 /* Called from ssl3_HandleCertificate
  */
 SECStatus
 ssl3_CompleteHandleCertificate(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
     ssl3CertNode *c;
     ssl3CertNode *lastCert = NULL;
-    PRInt32 remaining = 0;
-    PRInt32 size;
+    PRUint32 remaining = 0;
+    PRUint32 size;
     SECStatus rv;
     PRBool isServer = ss->sec.isServer;
     PRBool isTLS;
     SSL3AlertDescription desc;
     int errCode = SSL_ERROR_RX_MALFORMED_CERTIFICATE;
     SECItem certItem;
 
     ssl3_CleanupPeerCerts(ss);
     isTLS = (PRBool)(ss->ssl3.prSpec->version > SSL_LIBRARY_VERSION_3_0);
 
     /* It is reported that some TLS client sends a Certificate message
     ** with a zero-length message body.  We'll treat that case like a
     ** normal no_certificates message to maximize interoperability.
     */
     if (length) {
-        remaining = ssl3_ConsumeHandshakeNumber(ss, 3, &b, &length);
-        if (remaining < 0)
+        rv = ssl3_ConsumeHandshakeNumber(ss, &remaining, 3, &b, &length);
+        if (rv != SECSuccess)
             goto loser; /* fatal alert already sent by ConsumeHandshake. */
-        if ((PRUint32)remaining > length)
+        if (remaining > length)
             goto decode_loser;
     }
 
     if (!remaining) {
         if (!(isTLS && isServer)) {
             desc = bad_certificate;
             goto alert_loser;
         }
@@ -10657,25 +10678,24 @@ ssl3_CompleteHandleCertificate(sslSocket
     }
 
     ss->ssl3.peerCertArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
     if (ss->ssl3.peerCertArena == NULL) {
         goto loser; /* don't send alerts on memory errors */
     }
 
     /* First get the peer cert. */
-    remaining -= 3;
-    if (remaining < 0)
+    if (remaining < 3)
         goto decode_loser;
 
-    size = ssl3_ConsumeHandshakeNumber(ss, 3, &b, &length);
-    if (size <= 0)
+    remaining -= 3;
+    rv = ssl3_ConsumeHandshakeNumber(ss, &size, 3, &b, &length);
+    if (rv != SECSuccess)
         goto loser; /* fatal alert already sent by ConsumeHandshake. */
-
-    if (remaining < size)
+    if (size == 0 || remaining < size)
         goto decode_loser;
 
     certItem.data = b;
     certItem.len = size;
     b += size;
     length -= size;
     remaining -= size;
 
@@ -10685,25 +10705,24 @@ ssl3_CompleteHandleCertificate(sslSocket
         /* We should report an alert if the cert was bad, but not if the
          * problem was just some local problem, like memory error.
          */
         goto ambiguous_err;
     }
 
     /* Now get all of the CA certs. */
     while (remaining > 0) {
-        remaining -= 3;
-        if (remaining < 0)
+        if (remaining < 3)
             goto decode_loser;
 
-        size = ssl3_ConsumeHandshakeNumber(ss, 3, &b, &length);
-        if (size <= 0)
+        remaining -= 3;
+        rv = ssl3_ConsumeHandshakeNumber(ss, &size, 3, &b, &length);
+        if (rv != SECSuccess)
             goto loser; /* fatal alert already sent by ConsumeHandshake. */
-
-        if (remaining < size)
+        if (size == 0 || remaining < size)
             goto decode_loser;
 
         certItem.data = b;
         certItem.len = size;
         b += size;
         length -= size;
         remaining -= size;
 
@@ -10722,19 +10741,16 @@ ssl3_CompleteHandleCertificate(sslSocket
         if (lastCert) {
             lastCert->next = c;
         } else {
             ss->ssl3.peerCertChain = c;
         }
         lastCert = c;
     }
 
-    if (remaining != 0)
-        goto decode_loser;
-
     SECKEY_UpdateCertPQG(ss->sec.peerCert);
 
     if (!isServer &&
         ss->version < SSL_LIBRARY_VERSION_TLS_1_3 &&
         ssl3_ExtensionNegotiated(ss, ssl_cert_status_xtn)) {
         ss->ssl3.hs.ws = wait_certificate_status;
         rv = SECSuccess;
     } else {
--- a/security/nss/lib/ssl/ssl3ext.c
+++ b/security/nss/lib/ssl/ssl3ext.c
@@ -166,25 +166,25 @@ ssl3_ClientExtensionAdvertised(const ssl
 SECStatus
 ssl3_ParseExtensions(sslSocket *ss, SSL3Opaque **b, PRUint32 *length)
 {
     /* Clean out the extensions list. */
     ssl3_DestroyRemoteExtensions(&ss->ssl3.hs.remoteExtensions);
 
     while (*length) {
         SECStatus rv;
-        PRInt32 extension_type;
+        PRUint32 extension_type;
         SECItem extension_data = { siBuffer, NULL, 0 };
         TLSExtension *extension;
         PRCList *cursor;
 
         /* Get the extension's type field */
-        extension_type = ssl3_ConsumeHandshakeNumber(ss, 2, b, length);
-        if (extension_type < 0) { /* failure to decode extension_type */
-            return SECFailure;    /* alert already sent */
+        rv = ssl3_ConsumeHandshakeNumber(ss, &extension_type, 2, b, length);
+        if (rv != SECSuccess) {
+            return SECFailure; /* alert already sent */
         }
 
         SSL_TRC(10, ("%d: SSL3[%d]: parsing extension %d",
                      SSL_GETPID(), ss->fd, extension_type));
         /* Check whether an extension has been sent multiple times. */
         for (cursor = PR_NEXT_LINK(&ss->ssl3.hs.remoteExtensions);
              cursor != &ss->ssl3.hs.remoteExtensions;
              cursor = PR_NEXT_LINK(cursor)) {
@@ -500,28 +500,28 @@ ssl3_ExtSendAlert(const sslSocket *ss, S
 
 void
 ssl3_ExtDecodeError(const sslSocket *ss)
 {
     (void)ssl3_DecodeError((sslSocket *)ss);
 }
 
 SECStatus
-ssl3_ExtConsumeHandshake(const sslSocket *ss, void *v, PRInt32 bytes,
+ssl3_ExtConsumeHandshake(const sslSocket *ss, void *v, PRUint32 bytes,
                          SSL3Opaque **b, PRUint32 *length)
 {
     return ssl3_ConsumeHandshake((sslSocket *)ss, v, bytes, b, length);
 }
 
-PRInt32
-ssl3_ExtConsumeHandshakeNumber(const sslSocket *ss, PRInt32 bytes,
-                               SSL3Opaque **b, PRUint32 *length)
+SECStatus
+ssl3_ExtConsumeHandshakeNumber(const sslSocket *ss, PRUint32 *num,
+                               PRUint32 bytes, SSL3Opaque **b, PRUint32 *length)
 {
-    return ssl3_ConsumeHandshakeNumber((sslSocket *)ss, bytes, b, length);
+    return ssl3_ConsumeHandshakeNumber((sslSocket *)ss, num, bytes, b, length);
 }
 
 SECStatus
 ssl3_ExtConsumeHandshakeVariable(const sslSocket *ss, SECItem *i,
-                                 PRInt32 bytes, SSL3Opaque **b,
+                                 PRUint32 bytes, SSL3Opaque **b,
                                  PRUint32 *length)
 {
     return ssl3_ConsumeHandshakeVariable((sslSocket *)ss, i, bytes, b, length);
 }
--- a/security/nss/lib/ssl/ssl3ext.h
+++ b/security/nss/lib/ssl/ssl3ext.h
@@ -140,17 +140,18 @@ SECStatus ssl3_ExtAppendHandshake(const 
 SECStatus ssl3_ExtAppendHandshakeNumber(const sslSocket *ss, PRInt32 num,
                                         PRInt32 lenSize);
 SECStatus ssl3_ExtAppendHandshakeVariable(const sslSocket *ss,
                                           const SSL3Opaque *src, PRInt32 bytes,
                                           PRInt32 lenSize);
 void ssl3_ExtSendAlert(const sslSocket *ss, SSL3AlertLevel level,
                        SSL3AlertDescription desc);
 void ssl3_ExtDecodeError(const sslSocket *ss);
-SECStatus ssl3_ExtConsumeHandshake(const sslSocket *ss, void *v, PRInt32 bytes,
+SECStatus ssl3_ExtConsumeHandshake(const sslSocket *ss, void *v, PRUint32 bytes,
                                    SSL3Opaque **b, PRUint32 *length);
-PRInt32 ssl3_ExtConsumeHandshakeNumber(const sslSocket *ss, PRInt32 bytes,
-                                       SSL3Opaque **b, PRUint32 *length);
+SECStatus ssl3_ExtConsumeHandshakeNumber(const sslSocket *ss, PRUint32 *num,
+                                         PRUint32 bytes, SSL3Opaque **b,
+                                         PRUint32 *length);
 SECStatus ssl3_ExtConsumeHandshakeVariable(const sslSocket *ss, SECItem *i,
-                                           PRInt32 bytes, SSL3Opaque **b,
+                                           PRUint32 bytes, SSL3Opaque **b,
                                            PRUint32 *length);
 
 #endif
--- a/security/nss/lib/ssl/ssl3exthandle.c
+++ b/security/nss/lib/ssl/ssl3exthandle.c
@@ -218,46 +218,46 @@ ssl3_SendServerNameXtn(const sslSocket *
     return 4;
 }
 
 /* Handle an incoming SNI extension. */
 SECStatus
 ssl3_HandleServerNameXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
 {
     SECItem *names = NULL;
-    PRInt32 listLenBytes = 0;
+    PRUint32 listLenBytes = 0;
+    SECStatus rv;
 
     if (!ss->sec.isServer) {
         return SECSuccess; /* ignore extension */
     }
 
     /* Server side - consume client data and register server sender. */
     /* do not parse the data if don't have user extension handling function. */
     if (!ss->sniSocketConfig) {
         return SECSuccess;
     }
 
     /* length of server_name_list */
-    listLenBytes = ssl3_ExtConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
-    if (listLenBytes < 0) {
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &listLenBytes, 2, &data->data, &data->len);
+    if (rv != SECSuccess) {
         goto loser; /* alert already sent */
     }
     if (listLenBytes == 0 || listLenBytes != data->len) {
         goto alert_loser;
     }
 
     /* Read ServerNameList. */
     while (data->len > 0) {
         SECItem tmp;
-        SECStatus rv;
-        PRInt32 type;
+        PRUint32 type;
 
         /* Read Name Type. */
-        type = ssl3_ExtConsumeHandshakeNumber(ss, 1, &data->data, &data->len);
-        if (type < 0) { /* i.e., SECFailure cast to PRint32 */
+        rv = ssl3_ExtConsumeHandshakeNumber(ss, &type, 1, &data->data, &data->len);
+        if (rv != SECSuccess) {
             /* alert sent in ConsumeHandshakeNumber */
             goto loser;
         }
 
         /* Read ServerName (length and value). */
         rv = ssl3_ExtConsumeHandshakeVariable(ss, &tmp, 2, &data->data, &data->len);
         if (rv != SECSuccess) {
             goto loser;
@@ -537,32 +537,32 @@ ssl3_SelectAppProtocol(const sslSocket *
     xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
     return SECITEM_CopyItem(NULL, &xtnData->nextProto, &result);
 }
 
 /* handle an incoming ALPN extension at the server */
 SECStatus
 ssl3_ServerHandleAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
 {
-    int count;
+    PRUint32 count;
     SECStatus rv;
 
     /* We expressly don't want to allow ALPN on renegotiation,
      * despite it being permitted by the spec. */
     if (ss->firstHsDone || data->len == 0) {
         /* Clients MUST send a non-empty ALPN extension. */
         ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
         PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
         return SECFailure;
     }
 
     /* Unlike NPN, ALPN has extra redundant length information so that
      * the extension is the same in both ClientHello and ServerHello. */
-    count = ssl3_ExtConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
-    if (count != data->len) {
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &count, 2, &data->data, &data->len);
+    if (rv != SECSuccess || count != data->len) {
         ssl3_ExtDecodeError(ss);
         return SECFailure;
     }
 
     if (!ss->nextProtoCallback) {
         /* we're not configured for it */
         return SECSuccess;
     }
@@ -616,17 +616,17 @@ ssl3_ClientHandleNextProtoNegoXtn(const 
 
     return ssl3_SelectAppProtocol(ss, xtnData, ex_type, data);
 }
 
 SECStatus
 ssl3_ClientHandleAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
 {
     SECStatus rv;
-    PRInt32 list_len;
+    PRUint32 list_len;
     SECItem protocol_name;
 
     if (ssl3_ExtensionNegotiated(ss, ssl_next_proto_nego_xtn)) {
         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
         return SECFailure;
     }
 
     /* The extension data from the server has the following format:
@@ -634,19 +634,20 @@ ssl3_ClientHandleAppProtoXtn(const sslSo
      *   uint8 len;  // where len >= 1
      *   uint8 protocol_name[len]; */
     if (data->len < 4 || data->len > 2 + 1 + 255) {
         ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
         PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
         return SECFailure;
     }
 
-    list_len = ssl3_ExtConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &list_len, 2, &data->data,
+                                        &data->len);
     /* The list has to be the entire extension. */
-    if (list_len != data->len) {
+    if (rv != SECSuccess || list_len != data->len) {
         ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
         PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
         return SECFailure;
     }
 
     rv = ssl3_ExtConsumeHandshakeVariable(ss, &protocol_name, 1,
                                           &data->data, &data->len);
     /* The list must have exactly one value. */
@@ -1358,19 +1359,19 @@ ssl3_ProcessSessionTicketCommon(sslSocke
     PK11Context *hmac_ctx;
     CK_MECHANISM_TYPE macMech = CKM_SHA256_HMAC;
     PK11Context *aes_ctx;
     CK_MECHANISM_TYPE cipherMech = CKM_AES_CBC;
     unsigned char *padding;
     PRUint32 padding_length;
     unsigned char *buffer;
     unsigned int buffer_len;
-    PRInt32 temp;
+    PRUint32 temp;
     SECItem cert_item;
-    PRInt8 nameType = TLS_STE_NO_SERVER_NAME;
+    PRUint32 nameType;
     SECItem macParam = { siBuffer, NULL, 0 };
     SECItem alpn_item;
     SECItem ivItem;
 
     /* Turn off stateless session resumption if the client sends a
      * SessionTicket extension, even if the extension turns out to be
      * malformed (ss->sec.ci.sid is non-NULL when doing session
      * renegotiation.)
@@ -1494,62 +1495,62 @@ ssl3_ProcessSessionTicketCommon(sslSocke
 
     parsed_session_ticket = PORT_ZAlloc(sizeof(SessionTicket));
     if (parsed_session_ticket == NULL) {
         rv = SECFailure;
         goto loser;
     }
 
     /* Read ticket_version and reject if the version is wrong */
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
-    if (temp != TLS_EX_SESS_TICKET_VERSION)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &buffer_len);
+    if (rv != SECSuccess || temp != TLS_EX_SESS_TICKET_VERSION)
         goto no_ticket;
 
     parsed_session_ticket->ticket_version = (SSL3ProtocolVersion)temp;
 
     /* Read SSLVersion. */
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
-    if (temp < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
     parsed_session_ticket->ssl_version = (SSL3ProtocolVersion)temp;
 
     /* Read cipher_suite. */
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
-    if (temp < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
     parsed_session_ticket->cipher_suite = (ssl3CipherSuite)temp;
 
     /* Read compression_method. */
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
-    if (temp < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
     parsed_session_ticket->compression_method = (SSLCompressionMethod)temp;
 
     /* Read cipher spec parameters. */
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
-    if (temp < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
     parsed_session_ticket->authType = (SSLAuthType)temp;
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
-    if (temp < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
-    parsed_session_ticket->authKeyBits = (PRUint32)temp;
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
-    if (temp < 0)
+    parsed_session_ticket->authKeyBits = temp;
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
     parsed_session_ticket->keaType = (SSLKEAType)temp;
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
-    if (temp < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
-    parsed_session_ticket->keaKeyBits = (PRUint32)temp;
+    parsed_session_ticket->keaKeyBits = temp;
 
     /* Read certificate slot */
     parsed_session_ticket->certType.authType = parsed_session_ticket->authType;
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
-    if (temp < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
     switch (parsed_session_ticket->authType) {
         case ssl_auth_ecdsa:
         case ssl_auth_ecdh_rsa:
         case ssl_auth_ecdh_ecdsa: {
             const sslNamedGroupDef *group =
                 ssl_LookupNamedGroup((SSLNamedGroup)temp);
             if (!group || group->keaType != ssl_kea_ecdh) {
@@ -1557,46 +1558,46 @@ ssl3_ProcessSessionTicketCommon(sslSocke
             }
             parsed_session_ticket->certType.namedCurve = group;
         } break;
         default:
             break;
     }
 
     /* Read wrapped master_secret. */
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
-    if (temp < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
     parsed_session_ticket->ms_is_wrapped = (PRBool)temp;
 
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
-    if (temp < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
     parsed_session_ticket->msWrapMech = (CK_MECHANISM_TYPE)temp;
 
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 2, &buffer, &buffer_len);
-    if (temp < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
     parsed_session_ticket->ms_length = (PRUint16)temp;
     if (parsed_session_ticket->ms_length == 0 || /* sanity check MS. */
         parsed_session_ticket->ms_length >
             sizeof(parsed_session_ticket->master_secret))
         goto no_ticket;
 
     /* Allow for the wrapped master secret to be longer. */
     if (buffer_len < parsed_session_ticket->ms_length)
         goto no_ticket;
     PORT_Memcpy(parsed_session_ticket->master_secret, buffer,
                 parsed_session_ticket->ms_length);
     buffer += parsed_session_ticket->ms_length;
     buffer_len -= parsed_session_ticket->ms_length;
 
     /* Read client_identity */
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
-    if (temp < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
     parsed_session_ticket->client_identity.client_auth_type =
         (ClientAuthenticationType)temp;
     switch (parsed_session_ticket->client_identity.client_auth_type) {
         case CLIENT_AUTH_ANONYMOUS:
             break;
         case CLIENT_AUTH_CERTIFICATE:
             rv = ssl3_ExtConsumeHandshakeVariable(ss, &cert_item, 3,
@@ -1607,40 +1608,41 @@ ssl3_ProcessSessionTicketCommon(sslSocke
                                   &cert_item);
             if (rv != SECSuccess)
                 goto no_ticket;
             break;
         default:
             goto no_ticket;
     }
     /* Read timestamp. */
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 4, &buffer, &buffer_len);
-    if (temp < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
-    parsed_session_ticket->timestamp = (PRUint32)temp;
+    parsed_session_ticket->timestamp = temp;
 
     /* Read server name */
-    nameType =
-        ssl3_ExtConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
-    if (nameType != TLS_STE_NO_SERVER_NAME) {
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &nameType, 1, &buffer, &buffer_len);
+    if (rv != SECSuccess)
+        goto no_ticket;
+    if ((PRInt8)nameType != TLS_STE_NO_SERVER_NAME) {
         SECItem name_item;
         rv = ssl3_ExtConsumeHandshakeVariable(ss, &name_item, 2, &buffer,
                                               &buffer_len);
         if (rv != SECSuccess)
             goto no_ticket;
         rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->srvName,
                               &name_item);
         if (rv != SECSuccess)
             goto no_ticket;
-        parsed_session_ticket->srvName.type = nameType;
+        parsed_session_ticket->srvName.type = (PRUint8)nameType;
     }
 
     /* Read extendedMasterSecretUsed */
-    temp = ssl3_ExtConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len);
-    if (temp < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &buffer_len);
+    if (rv != SECSuccess)
         goto no_ticket;
     PORT_Assert(temp == PR_TRUE || temp == PR_FALSE);
     parsed_session_ticket->extendedMasterSecretUsed = (PRBool)temp;
 
     rv = ssl3_ExtConsumeHandshake(ss, &parsed_session_ticket->flags, 4,
                                   &buffer, &buffer_len);
     if (rv != SECSuccess)
         goto no_ticket;
@@ -2479,45 +2481,47 @@ ssl3_HandleSupportedPointFormatsXtn(cons
     /* Poor client doesn't support uncompressed points. */
     PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE);
     return SECFailure;
 }
 
 static SECStatus
 ssl_UpdateSupportedGroups(sslSocket *ss, SECItem *data)
 {
-    PRInt32 list_len;
+    SECStatus rv;
+    PRUint32 list_len;
     unsigned int i;
     const sslNamedGroupDef *enabled[SSL_NAMED_GROUP_COUNT] = { 0 };
     PORT_Assert(SSL_NAMED_GROUP_COUNT == PR_ARRAY_SIZE(enabled));
 
     if (!data->data || data->len < 4) {
         (void)ssl3_DecodeError(ss);
         return SECFailure;
     }
 
     /* get the length of elliptic_curve_list */
-    list_len = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
-    if (list_len < 0 || data->len != list_len || (data->len % 2) != 0) {
+    rv = ssl3_ConsumeHandshakeNumber(ss, &list_len, 2, &data->data, &data->len);
+    if (rv != SECSuccess || data->len != list_len || (data->len % 2) != 0) {
         (void)ssl3_DecodeError(ss);
         return SECFailure;
     }
 
     /* disable all groups and remember the enabled groups */
     for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
         enabled[i] = ss->namedGroupPreferences[i];
         ss->namedGroupPreferences[i] = NULL;
     }
 
     /* Read groups from data and enable if in |enabled| */
     while (data->len) {
         const sslNamedGroupDef *group;
-        PRInt32 curve_name =
-            ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
-        if (curve_name < 0) {
+        PRUint32 curve_name;
+        rv = ssl3_ConsumeHandshakeNumber(ss, &curve_name, 2, &data->data,
+                                         &data->len);
+        if (rv != SECSuccess) {
             return SECFailure; /* fatal alert already sent */
         }
         group = ssl_LookupNamedGroup(curve_name);
         if (group) {
             for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
                 if (enabled[i] && group == enabled[i]) {
                     ss->namedGroupPreferences[i] = enabled[i];
                     break;
--- a/security/nss/lib/ssl/sslimpl.h
+++ b/security/nss/lib/ssl/sslimpl.h
@@ -29,17 +29,17 @@
 #include "nssrwlk.h"
 #include "prthread.h"
 #include "prclist.h"
 #include "private/pprthred.h"
 
 #include "sslt.h" /* for some formerly private types, now public */
 
 typedef struct sslSocketStr sslSocket;
-
+typedef struct ssl3CipherSpecStr ssl3CipherSpec;
 #include "ssl3ext.h"
 
 /* to make some of these old enums public without namespace pollution,
 ** it was necessary to prepend ssl_ to the names.
 ** These #defines preserve compatibility with the old code here in libssl.
 */
 typedef SSLMACAlgorithm SSL3MACAlgorithm;
 
@@ -195,16 +195,19 @@ struct ssl3CertNodeStr {
 typedef SECStatus (*sslHandshakeFunc)(sslSocket *ss);
 
 typedef void (*sslSessionIDCacheFunc)(sslSessionID *sid);
 typedef void (*sslSessionIDUncacheFunc)(sslSessionID *sid);
 typedef sslSessionID *(*sslSessionIDLookupFunc)(const PRIPv6Addr *addr,
                                                 unsigned char *sid,
                                                 unsigned int sidLen,
                                                 CERTCertDBHandle *dbHandle);
+typedef void (*sslCipherSpecChangedFunc)(void *arg,
+                                         PRBool sending,
+                                         ssl3CipherSpec *newSpec);
 
 /* Socket ops */
 struct sslSocketOpsStr {
     int (*connect)(sslSocket *, const PRNetAddr *);
     PRFileDesc *(*accept)(sslSocket *, PRNetAddr *);
     int (*bind)(sslSocket *, const PRNetAddr *);
     int (*listen)(sslSocket *, int);
     int (*shutdown)(sslSocket *, int);
@@ -463,17 +466,17 @@ typedef struct DTLSRecvdRecordsStr {
     sslSequenceNumber right;
 } DTLSRecvdRecords;
 
 /*
 ** These are the "specs" in the "ssl3" struct.
 ** Access to the pointers to these specs, and all the specs' contents
 ** (direct and indirect) is protected by the reader/writer lock ss->specLock.
 */
-typedef struct {
+struct ssl3CipherSpecStr {
     PRCList link;
     const ssl3BulkCipherDef *cipher_def;
     const ssl3MACDef *mac_def;
     SSLCompressionMethod compression_method;
     int mac_size;
     SSLCipher encode;
     SSLCipher decode;
     SSLAEADCipher aead;
@@ -493,17 +496,17 @@ typedef struct {
     ssl3KeyMaterial client;
     ssl3KeyMaterial server;
     SECItem msItem;
     DTLSEpoch epoch;
     DTLSRecvdRecords recvdRecords;
 
     PRUint8 refCt;
     const char *phase;
-} ssl3CipherSpec;
+};
 
 typedef enum { never_cached,
                in_client_cache,
                in_server_cache,
                invalid_cache /* no longer in any cache. */
 } Cached;
 
 #include "sslcert.h"
@@ -887,16 +890,21 @@ struct ssl3StateStr {
     ** The following Specs and Spec pointers must be protected using the
     ** Spec Lock.
     */
     ssl3CipherSpec *crSpec; /* current read spec. */
     ssl3CipherSpec *prSpec; /* pending read spec. */
     ssl3CipherSpec *cwSpec; /* current write spec. */
     ssl3CipherSpec *pwSpec; /* pending write spec. */
 
+    /* Internal callback for when we do a cipher suite change. Used for
+     * debugging in TLS 1.3. This can only be set by non-public functions. */
+    sslCipherSpecChangedFunc changedCipherSpecFunc;
+    void *changedCipherSpecArg;
+
     CERTCertificate *clientCertificate;   /* used by client */
     SECKEYPrivateKey *clientPrivateKey;   /* used by client */
     CERTCertificateList *clientCertChain; /* used by client */
     PRBool sendEmptyCert;                 /* used by client */
 
     int policy;
     /* This says what cipher suites we can do, and should
      * be either SSL_ALLOWED or SSL_RESTRICTED
@@ -1640,22 +1648,23 @@ extern SECStatus ssl3_AppendHandshake(ss
 extern SECStatus ssl3_AppendHandshakeHeader(sslSocket *ss,
                                             SSL3HandshakeType t, PRUint32 length);
 extern SECStatus ssl3_AppendHandshakeNumber(sslSocket *ss, PRInt32 num,
                                             PRInt32 lenSize);
 extern SECStatus ssl3_AppendHandshakeVariable(sslSocket *ss,
                                               const SSL3Opaque *src, PRInt32 bytes, PRInt32 lenSize);
 extern SECStatus ssl3_AppendSignatureAndHashAlgorithm(
     sslSocket *ss, const SSLSignatureAndHashAlg *sigAndHash);
-extern SECStatus ssl3_ConsumeHandshake(sslSocket *ss, void *v, PRInt32 bytes,
+extern SECStatus ssl3_ConsumeHandshake(sslSocket *ss, void *v, PRUint32 bytes,
                                        SSL3Opaque **b, PRUint32 *length);
-extern PRInt32 ssl3_ConsumeHandshakeNumber(sslSocket *ss, PRInt32 bytes,
-                                           SSL3Opaque **b, PRUint32 *length);
+extern SECStatus ssl3_ConsumeHandshakeNumber(sslSocket *ss, PRUint32 *num,
+                                             PRUint32 bytes, SSL3Opaque **b,
+                                             PRUint32 *length);
 extern SECStatus ssl3_ConsumeHandshakeVariable(sslSocket *ss, SECItem *i,
-                                               PRInt32 bytes, SSL3Opaque **b,
+                                               PRUint32 bytes, SSL3Opaque **b,
                                                PRUint32 *length);
 extern PRUint8 *ssl_EncodeUintX(PRUint64 value, unsigned int bytes,
                                 PRUint8 *to);
 extern PRBool ssl_IsSupportedSignatureScheme(SSLSignatureScheme scheme);
 extern SECStatus ssl_CheckSignatureSchemeConsistency(
     sslSocket *ss, SSLSignatureScheme scheme, CERTCertificate *cert);
 extern SECStatus ssl_ParseSignatureSchemes(const sslSocket *ss, PLArenaPool *arena,
                                            SSLSignatureScheme **schemesOut,
--- a/security/nss/lib/ssl/sslproto.h
+++ b/security/nss/lib/ssl/sslproto.h
@@ -204,16 +204,26 @@
 #define TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 0xCCA9
 #define TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256     0xCCAA
 
 /* Special TLS 1.3 cipher suites that really just specify AEAD */
 #define TLS_AES_128_GCM_SHA256                0x1301
 #define TLS_AES_256_GCM_SHA384                0x1302
 #define TLS_CHACHA20_POLY1305_SHA256          0x1303
 
+/* PSK cipher suites. NSS doesn't actually support these, but we
+ * exposed them when TLS 1.3 used them so we need to keep them
+ * in the API. */
+#define TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256   0xCCAC
+#define TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256     0xCCAD
+#define TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256   0xD001
+#define TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384   0xD002
+#define TLS_DHE_PSK_WITH_AES_128_GCM_SHA256     0x00AA /* RFC 5487 */
+#define TLS_DHE_PSK_WITH_AES_256_GCM_SHA384     0x00AB /* RFC 5487 */
+
 /* DTLS-SRTP cipher suites from RFC 5764 */
 /* If you modify this, also modify MAX_DTLS_SRTP_CIPHER_SUITES in sslimpl.h */
 #define SRTP_AES128_CM_HMAC_SHA1_80             0x0001
 #define SRTP_AES128_CM_HMAC_SHA1_32             0x0002
 #define SRTP_NULL_HMAC_SHA1_80                  0x0005
 #define SRTP_NULL_HMAC_SHA1_32                  0x0006
 
 /* DO NOT USE. (deprecated, will be removed) */
--- a/security/nss/lib/ssl/tls13con.c
+++ b/security/nss/lib/ssl/tls13con.c
@@ -1664,17 +1664,17 @@ tls13_SendCertificateRequest(sslSocket *
 
     return SECSuccess;
 }
 
 SECStatus
 tls13_HandleHelloRetryRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
     SECStatus rv;
-    PRInt32 tmp;
+    PRUint32 tmp;
     SSL3ProtocolVersion version;
 
     SSL_TRC(3, ("%d: TLS13[%d]: handle hello retry request",
                 SSL_GETPID(), ss->fd));
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
@@ -1713,18 +1713,18 @@ tls13_HandleHelloRetryRequest(sslSocket 
     }
     if (version > ss->vrange.max || version < SSL_LIBRARY_VERSION_TLS_1_3) {
         FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST,
                     protocol_version);
         return SECFailure;
     }
 
     /* Extensions. */
-    tmp = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
-    if (tmp < 0) {
+    rv = ssl3_ConsumeHandshakeNumber(ss, &tmp, 2, &b, &length);
+    if (rv != SECSuccess) {
         return SECFailure; /* error code already set */
     }
     /* Extensions must be non-empty and use the remainder of the message.
      * This means that a HelloRetryRequest cannot be a no-op: we must have an
      * extension, it must be one that we understand and recognize as being valid
      * for HelloRetryRequest, and all the extensions we permit cause us to
      * modify our ClientHello in some way. */
     if (!tmp || tmp != length) {
@@ -1752,17 +1752,17 @@ tls13_HandleHelloRetryRequest(sslSocket 
 
 static SECStatus
 tls13_HandleCertificateRequest(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
     SECStatus rv;
     TLS13CertificateRequest *certRequest = NULL;
     SECItem context = { siBuffer, NULL, 0 };
     PLArenaPool *arena;
-    PRInt32 extensionsLength;
+    PRUint32 extensionsLength;
 
     SSL_TRC(3, ("%d: TLS13[%d]: handle certificate_request sequence",
                 SSL_GETPID(), ss->fd));
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
     /* Client */
@@ -1811,18 +1811,18 @@ tls13_HandleCertificateRequest(sslSocket
     }
 
     rv = ssl3_ParseCertificateRequestCAs(ss, &b, &length, arena,
                                          &certRequest->ca_list);
     if (rv != SECSuccess)
         goto loser; /* alert already sent */
 
     /* Verify that the extensions length is correct. */
-    extensionsLength = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
-    if (extensionsLength < 0) {
+    rv = ssl3_ConsumeHandshakeNumber(ss, &extensionsLength, 2, &b, &length);
+    if (rv != SECSuccess) {
         goto loser; /* alert already sent */
     }
     if (extensionsLength != length) {
         FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERT_REQUEST,
                     illegal_parameter);
         goto loser;
     }
 
@@ -2769,16 +2769,20 @@ tls13_SetCipherSpec(sslSocket *ss, Traff
     *specp = spec;                   /* Overwrite. */
     ssl_ReleaseSpecWriteLock(ss);
 
     SSL_TRC(3, ("%d: TLS13[%d]: %s installed key for phase='%s'.%d dir=%s",
                 SSL_GETPID(), ss->fd, SSL_ROLE(ss),
                 spec->phase, spec->epoch,
                 direction == CipherSpecRead ? "read" : "write"));
 
+    if (ss->ssl3.changedCipherSpecFunc) {
+        ss->ssl3.changedCipherSpecFunc(ss->ssl3.changedCipherSpecArg,
+                                       direction == CipherSpecWrite, spec);
+    }
     return SECSuccess;
 }
 
 static SECStatus
 tls13_ComputeHandshakeHashes(sslSocket *ss,
                              SSL3Hashes *hashes)
 {
     SECStatus rv;
@@ -2914,16 +2918,17 @@ tls13_WriteNonce(ssl3KeyMaterial *keys,
     PORT_Assert(nonceLen == 12);
     memcpy(nonce, keys->write_iv, 12);
 
     /* XOR the last 8 bytes of the IV with the sequence number. */
     PORT_Assert(seqNumLen == 8);
     for (i = 0; i < 8; ++i) {
         nonce[4 + i] ^= seqNumBuf[i];
     }
+    PRINT_BUF(50, (NULL, "Nonce", nonce, nonceLen));
 }
 
 /* Implement the SSLAEADCipher interface defined in sslimpl.h.
  *
  * That interface takes the additional data (see below) and reinterprets that as
  * a sequence number. In TLS 1.3 there is no additional data so this value is
  * just the encoded sequence number.
  */
@@ -3003,33 +3008,33 @@ tls13_ChaCha20Poly1305(ssl3KeyMaterial *
                       CKM_NSS_CHACHA20_POLY1305,
                       (unsigned char *)&aeadParams, sizeof(aeadParams));
 }
 
 static SECStatus
 tls13_HandleEncryptedExtensions(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
     SECStatus rv;
-    PRInt32 innerLength;
+    PRUint32 innerLength;
     SECItem oldNpn = { siBuffer, NULL, 0 };
 
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
     SSL_TRC(3, ("%d: TLS13[%d]: handle encrypted extensions",
                 SSL_GETPID(), ss->fd));
 
     rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_ENCRYPTED_EXTENSIONS,
                               wait_encrypted_extensions);
     if (rv != SECSuccess) {
         return SECFailure;
     }
 
-    innerLength = ssl3_ConsumeHandshakeNumber(ss, 2, &b, &length);
-    if (innerLength < 0) {
+    rv = ssl3_ConsumeHandshakeNumber(ss, &innerLength, 2, &b, &length);
+    if (rv != SECSuccess) {
         return SECFailure; /* Alert already sent. */
     }
     if (innerLength != length) {
         FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_ENCRYPTED_EXTENSIONS,
                     illegal_parameter);
         return SECFailure;
     }
 
@@ -3838,17 +3843,16 @@ loser:
     }
     return SECFailure;
 }
 
 static SECStatus
 tls13_HandleNewSessionTicket(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
     SECStatus rv;
-    PRInt32 tmp;
     PRUint32 utmp;
     NewSessionTicket ticket = { 0 };
     SECItem data;
     SECItem ticket_data;
 
     SSL_TRC(3, ("%d: TLS13[%d]: handle new session ticket message",
                 SSL_GETPID(), ss->fd));
 
@@ -3859,23 +3863,23 @@ tls13_HandleNewSessionTicket(sslSocket *
     }
     if (!ss->firstHsDone || ss->sec.isServer) {
         FATAL_ERROR(ss, SSL_ERROR_RX_UNEXPECTED_NEW_SESSION_TICKET,
                     unexpected_message);
         return SECFailure;
     }
 
     ticket.received_timestamp = ssl_Time();
-    tmp = ssl3_ConsumeHandshakeNumber(ss, 4, &b, &length);
-    if (tmp < 0) {
+    rv = ssl3_ConsumeHandshakeNumber(ss, &ticket.ticket_lifetime_hint, 4, &b,
+                                     &length);
+    if (rv != SECSuccess) {
         FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET,
                     decode_error);
         return SECFailure;
     }
-    ticket.ticket_lifetime_hint = (PRUint32)tmp;
     ticket.ticket.type = siBuffer;
 
     rv = ssl3_ConsumeHandshake(ss, &utmp, sizeof(utmp),
                                &b, &length);
     if (rv != SECSuccess) {
         PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET);
         return SECFailure;
     }
--- a/security/nss/lib/ssl/tls13exthandle.c
+++ b/security/nss/lib/ssl/tls13exthandle.c
@@ -203,23 +203,23 @@ tls13_ClientSendKeyShareXtn(const sslSoc
 loser:
     return -1;
 }
 
 static SECStatus
 tls13_HandleKeyShareEntry(const sslSocket *ss, TLSExtensionData *xtnData, SECItem *data)
 {
     SECStatus rv;
-    PRInt32 group;
+    PRUint32 group;
     const sslNamedGroupDef *groupDef;
     TLS13KeyShareEntry *ks = NULL;
     SECItem share = { siBuffer, NULL, 0 };
 
-    group = ssl3_ExtConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
-    if (group < 0) {
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &group, 2, &data->data, &data->len);
+    if (rv != SECSuccess) {
         PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE);
         goto loser;
     }
     groupDef = ssl_LookupNamedGroup(group);
     rv = ssl3_ExtConsumeHandshakeVariable(ss, &share, 2, &data->data,
                                           &data->len);
     if (rv != SECSuccess) {
         goto loser;
@@ -280,27 +280,27 @@ tls13_ClientHandleKeyShareXtn(const sslS
 
     return SECSuccess;
 }
 
 SECStatus
 tls13_ClientHandleKeyShareXtnHrr(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
 {
     SECStatus rv;
-    PRInt32 tmp;
+    PRUint32 tmp;
     const sslNamedGroupDef *group;
 
     PORT_Assert(!ss->sec.isServer);
     PORT_Assert(ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3);
 
     SSL_TRC(3, ("%d: SSL3[%d]: handle key_share extension in HRR",
                 SSL_GETPID(), ss->fd));
 
-    tmp = ssl3_ExtConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
-    if (tmp < 0) {
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &tmp, 2, &data->data, &data->len);
+    if (rv != SECSuccess) {
         return SECFailure; /* error code already set */
     }
     if (data->len) {
         ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
         PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
         return SECFailure;
     }
 
@@ -330,33 +330,33 @@ tls13_ClientHandleKeyShareXtnHrr(const s
 
 /* Handle an incoming KeyShare extension at the server and copy to
  * |xtnData->remoteKeyShares| for future use. The key
  * share is processed in tls13_HandleClientKeyShare(). */
 SECStatus
 tls13_ServerHandleKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type, SECItem *data)
 {
     SECStatus rv;
-    PRInt32 length;
+    PRUint32 length;
 
     PORT_Assert(ss->sec.isServer);
     PORT_Assert(PR_CLIST_IS_EMPTY(&xtnData->remoteKeyShares));
 
     if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
         return SECSuccess;
     }
 
     SSL_TRC(3, ("%d: SSL3[%d]: handle key_share extension",
                 SSL_GETPID(), ss->fd));
 
     /* Redundant length because of TLS encoding (this vector consumes
      * the entire extension.) */
-    length = ssl3_ExtConsumeHandshakeNumber(ss, 2, &data->data,
-                                            &data->len);
-    if (length < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &length, 2, &data->data,
+                                        &data->len);
+    if (rv != SECSuccess)
         goto loser;
     if (length != data->len) {
         /* Check for consistency */
         PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE);
         goto loser;
     }
 
     while (data->len) {
@@ -679,28 +679,29 @@ tls13_ServerSendPreSharedKeyXtn(const ss
 }
 
 /* Handle a TLS 1.3 PreSharedKey Extension. We only accept PSKs
  * that contain session tickets. */
 SECStatus
 tls13_ClientHandlePreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData, PRUint16 ex_type,
                                   SECItem *data)
 {
-    PRInt32 index;
+    PRUint32 index;
+    SECStatus rv;
 
     SSL_TRC(3, ("%d: SSL3[%d]: handle pre_shared_key extension",
                 SSL_GETPID(), ss->fd));
 
     /* If we are doing < TLS 1.3, then ignore this. */
     if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
         return SECSuccess;
     }
 
-    index = ssl3_ExtConsumeHandshakeNumber(ss, 2, &data->data, &data->len);
-    if (index < 0)
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &index, 2, &data->data, &data->len);
+    if (rv != SECSuccess)
         return SECFailure;
 
     /* This should be the end of the extension. */
     if (data->len) {
         PORT_SetError(SSL_ERROR_MALFORMED_PRE_SHARED_KEY);
         return SECFailure;
     }
 
--- a/security/nss/readme.md
+++ b/security/nss/readme.md
@@ -1,98 +1,169 @@
 # Network Security Services
 
-Network Security Services (NSS) is a set of libraries designed to support cross-platform development of security-enabled client and server applications. NSS supports SSL v3-TLS 1.2 (experimental TLS 1.3), PKCS #5, PKCS #7, PKCS #11, PKCS #12, S/MIME, X.509 v3 certificates, and other security standards.
+Network Security Services (NSS) is a set of libraries designed to support
+cross-platform development of security-enabled client and server
+applications. NSS supports SSL v3-TLS 1.2 (experimental TLS 1.3), PKCS #5, PKCS
+#7, PKCS #11, PKCS #12, S/MIME, X.509 v3 certificates, and other security
+standards.
 
 ## Getting started
-In order to get started create a new directory on that you will be uses as your local work area, and check out NSS and NSPR. (Note that there's no git mirror of NSPR and you require mercurial to get the latest NSPR source.)
+
+In order to get started create a new directory on that you will be uses as your
+local work area, and check out NSS and NSPR. (Note that there's no git mirror of
+NSPR and you require mercurial to get the latest NSPR source.)
 
     git clone https://github.com/nss-dev/nss.git
     hg clone https://hg.mozilla.org/projects/nspr
 
 NSS can also be cloned with mercurial `
     hg clone https://hg.mozilla.org/projects/nspr`
 
 ## Building NSS
-*This build system is under development. It does not yet support all the features or platforms that NSS supports. To build on anything other than Mac or Linux please use the legacy build system as described below.*
+
+**This build system is under development. It does not yet support all the
+features or platforms that NSS supports. To build on anything other than Mac or
+Linux please use the legacy build system as described below.**
 
 Build requirements:
 
 * [gyp](https://gyp.gsrc.io/)
 * [ninja](https://ninja-build.org/)
 
 After changing into the NSS directory a typical build is done as follows
 
     ./build.sh
 
-Once the build is done the build output is found in the directory `../dist/*.OBJ`, where `*` will be a name dynamically derived from your system's architecture. Exported header files can be found in the `include` directory, library files in directory `lib`, and tools in directory `bin`. In order to run the tools, set your system environment to use the libraries of your build from the "lib" directory, e.g., using the `LD_LIBRARY_PATH` or `DYLD_LIBRARY_PATH`.
+Once the build is done the build output is found in the directory
+`../dist/*.OBJ`, where `*` will be a name dynamically derived from your system's
+architecture. Exported header files can be found in the `include` directory,
+library files in directory `lib`, and tools in directory `bin`. In order to run
+the tools, set your system environment to use the libraries of your build from
+the "lib" directory, e.g., using the `LD_LIBRARY_PATH` or `DYLD_LIBRARY_PATH`.
 
----
     Usage: build.sh [-hcgv] [-j <n>] [--test] [--fuzz] [--scan-build[=output]]
-                    [-m32] [--opt|-o]
+                    [-m32] [--opt|-o] [--asan] [--ubsan] [--sancov[=edge|bb|func]]
+                    [--pprof] [--msan]
+
+    This script builds NSS with gyp and ninja.
+
+    This build system is still under development.  It does not yet support all
+    the features or platforms that NSS supports.
+
+    NSS build tool options:
 
-    -h            display this help and exit
-    -c            clean before build
-    -g            force a rebuild of gyp (and NSPR, because why not)
-    -j <n>        run at most <n> concurrent jobs
-    -v            verbose build
-    -m32          do a 32-bit build on a 64-bit system
-    --test        ignore map files and export everything we have
-    --fuzz        enable fuzzing mode. this always enables test builds
-    --scan-build  run the build with scan-build (scan-build has to be in the path)
-                  --scan-build=/out/path sets the output path for scan-build
-    --opt|-o      do an opt build
+        -h            display this help and exit
+        -c            clean before build
+        -g            force a rebuild of gyp (and NSPR, because why not)
+        -j <n>        run at most <n> concurrent jobs
+        -v            verbose build
+        -m32          do a 32-bit build on a 64-bit system
+        --test        ignore map files and export everything we have
+        --fuzz        enable fuzzing mode. this always enables test builds
+        --scan-build  run the build with scan-build (scan-build has to be in the path)
+                      --scan-build=/out/path sets the output path for scan-build
+        --opt|-o      do an opt build
+        --asan        do an asan build
+        --ubsan       do an ubsan build
+        --msan        do an msan build
+        --sancov      do sanitize coverage builds
+                      --sancov=func sets coverage to function level for example
+        --pprof       build with gperftool support
+
 
 ## Building NSS (legacy build system)
-After changing into the NSS directory a typical build of 32-bit NSS is done as follows
+
+After changing into the NSS directory a typical build of 32-bit NSS is done as
+follows:
 
     make nss_build_all
 
 The following environment variables might be useful:
+
 * `BUILD_OPT=1` to get an optimised build
+
 * `USE_64=1` to get a 64-bit build (recommended)
 
-The complete list of environment variables can be found [here](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Reference/NSS_environment_variables).
+The complete list of environment variables can be found
+[here](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Reference/NSS_environment_variables).
 
-To clean the build directory run
+To clean the build directory run:
 
     make nss_clean_all
 
 ## Tests
+
 ### Setup
-Make sure that the address `$HOST.$DOMSUF` on your computer is available. This is necessary because NSS tests generate certificates and establish TLS connections, which requires a fully qualified domain name.
-You can test this by calling `ping $HOST.$DOMSUF`. If this is working, you're all set.
-If it's not, set or export
+
+Make sure that the address `$HOST.$DOMSUF` on your computer is available. This
+is necessary because NSS tests generate certificates and establish TLS
+connections, which requires a fully qualified domain name.
+You can test this by
+calling `ping $HOST.$DOMSUF`. If this is working, you're all set.  If it's not,
+set or export:
 
     HOST=nss
     DOMSUF=local
-Note that you might have to add `nss.local` to `/etc/hosts` if it's not there. The entry should look something like `127.0.0.1       nss.local   nss`.
-If you get name resolution errors, try to disable IPv6 on the loopback device, i.e. comment the lines starting with `::1` in your `/etc/hosts` .
+
+Note that you might have to add `nss.local` to `/etc/hosts` if it's not
+there. The entry should look something like `127.0.0.1 nss.local nss`.
+
+If you get name resolution errors, try to ensure that you are using an IPv4
+address; IPv6 is the default on many systems for the loopback device which
+doesn't work.
 
 ### Running tests
-*Runnning all tests will take a while!*
+
+**Runnning all tests will take a while!**
 
     cd tests
     ./all.sh
-Make sure that all environment variables set for the build are set while running the tests as well.
-Test results are published in the folder `../../test_results/`.
-Individual tests can be run with the `NSS_TESTS` environment variable, e.g. `NSS_TESTS=ssl_gtests ./all.sh` or by changing into the according directory and running the bash script there `cd ssl_gtests && ./ssl_gtests.sh`.  The following tests are available:
+
+Make sure that all environment variables set for the build are set while running
+the tests as well.  Test results are published in the folder
+`../../test_results/`.
+
+Individual tests can be run with the `NSS_TESTS` environment variable,
+e.g. `NSS_TESTS=ssl_gtests ./all.sh` or by changing into the according directory
+and running the bash script there `cd ssl_gtests && ./ssl_gtests.sh`.  The
+following tests are available:
 
     cipher lowhash libpkix cert dbtests tools fips sdr crmf smime ssl ocsp merge pkits chains ec gtests ssl_gtests bogo
 
-To make tests run faster it's recommended to set `NSS_CYCLES=standard` to run only the standard cycle.
+To make tests run faster it's recommended to set `NSS_CYCLES=standard` to run
+only the standard cycle.
 
 ## Releases
-NSS releases can be found at [Mozilla's download server](https://ftp.mozilla.org/pub/security/nss/releases/). Because NSS depends on the base library NSPR you should download the archive that combines both NSS and NSPR.
+
+NSS releases can be found at [Mozilla's download
+server](https://ftp.mozilla.org/pub/security/nss/releases/). Because NSS depends
+on the base library NSPR you should download the archive that combines both NSS
+and NSPR.
 
 ## Contributing
-[Bugzilla](https://bugzilla.mozilla.org/) is used to track NSS development and bugs. File new bugs in the NSS product.
-A list with good first bugs to start with are [listed here](https://bugzilla.mozilla.org/buglist.cgi?keywords=good-first-bug%2C%20&keywords_type=allwords&list_id=13238861&resolution=---&query_format=advanced&product=NSS).
+
+[Bugzilla](https://bugzilla.mozilla.org/) is used to track NSS development and
+bugs. File new bugs in the NSS product.
+
+A list with good first bugs to start with are [listed
+here](https://bugzilla.mozilla.org/buglist.cgi?keywords=good-first-bug%2C%20&keywords_type=allwords&list_id=13238861&resolution=---&query_format=advanced&product=NSS).
 
 ### NSS Folder Structure
+
 The nss directory contains the following important subdirectories:
+
 - `coreconf` contains the build logic.
+
 - `lib` contains all library code that is used to create the runtime libraries.
-- `cmd` contains a set of various tool programs that are built with NSS. Several tools are general purpose and can be used to inspect and manipulate the storage files that software using the NSS library creates and modifies. Other tools are only used for testing purposes.
-- `test` and `gtests` contain the NSS test suite. While `test` contains shell scripts to drive test programs in `cmd`, `gtests` holds a set of [gtests](https://github.com/google/googletest).
+
+- `cmd` contains a set of various tool programs that are built with NSS. Several
+  tools are general purpose and can be used to inspect and manipulate the
+  storage files that software using the NSS library creates and modifies. Other
+  tools are only used for testing purposes.
 
-A more comprehensible overview of the NSS folder structure and API guidelines can be found [here](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/NSS_API_Guidelines).
+- `test` and `gtests` contain the NSS test suite. While `test` contains shell
+  scripts to drive test programs in `cmd`, `gtests` holds a set of
+  [gtests](https://github.com/google/googletest).
 
+A more comprehensible overview of the NSS folder structure and API guidelines
+can be found
+[here](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/NSS_API_Guidelines).
deleted file mode 100644
--- a/testing/web-platform/meta/FileAPI/reading-data-section/filereader_abort.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[filereader_abort.html]
-  type: testharness
-  [Aborting before read]
-    expected: FAIL
-
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/picture-tag/top-level/no-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,1 @@
+prefs: [security.mixed_content.send_hsts_priming:false, security.mixed_content.use_hsts:false]
--- a/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/link-prefetch-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/link-prefetch-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https.html.ini
@@ -1,6 +1,7 @@
 [no-opt-in-allows.https.html]
   type: testharness
+  prefs: [security.mixed_content.send_hsts_priming:false, security.mixed_content.use_hsts:false]
   [opt_in_method: no-opt-in\n                                 origin: cross-origin-http\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: keep-scheme-redirect\n                                 subresource: link-prefetch-tag\n                                 expectation: allowed]
     expected: FAIL
     bug: haven't implement prefetch link as an optionally blockable item
 
--- a/toolkit/components/telemetry/Events.yaml
+++ b/toolkit/components/telemetry/Events.yaml
@@ -1,42 +1,57 @@
+navigation:
+- methods: ["search"]
+  objects: ["about_home", "about_newtab", "contextmenu", "oneoff",
+            "suggestion", "alias", "enter", "searchbar", "urlbar"]
+  release_channel_collection: opt-in
+  description: >
+    This is recorded on each search navigation.
+    The value field records the action used to trigger the search:
+      "enter", "oneoff", "suggestion", "alias", null (for contextmenu)
+  bug_numbers: [1316281]
+  notification_emails: ["past@mozilla.com"]
+  expiry_version: "58.0"
+  extra_keys:
+    engine: The id of the search engine used.
+
 # This category contains event entries used for Telemetry tests.
 # They will not be sent out with any pings.
 telemetry.test:
 - methods: ["test1", "test2"]
   objects: ["object1", "object2"]
   bug_numbers: [1286606]
   notification_emails: ["telemetry-client-dev@mozilla.com"]
   description: This is a test entry for Telemetry.
   expiry_date: never
   extra_keys:
     key1: This is just a test description.
     key2: This is another test description.
-- methods: ["test_optout"]
+- methods: ["optout"]
   objects: ["object1", "object2"]
   bug_numbers: [1286606]
   notification_emails: ["telemetry-client-dev@mozilla.com"]
   description: This is an opt-out test entry.
   expiry_date: never
   release_channel_collection: opt-out
   extra_keys:
     key1: This is just a test description.
-- methods: ["test_expired_version"]
+- methods: ["expired_version"]
   objects: ["object1", "object2"]
   bug_numbers: [1286606]
   notification_emails: ["telemetry-client-dev@mozilla.com"]
   description: This is a test entry with an expired version.
   expiry_version: "3.6"
-- methods: ["test_expired_date"]
+- methods: ["expired_date"]
   objects: ["object1", "object2"]
   bug_numbers: [1286606]
   notification_emails: ["telemetry-client-dev@mozilla.com"]
   description: This is a test entry with an expired date.
   expiry_date: 2014-01-28
-- methods: ["test_not_expired_optout"]
+- methods: ["not_expired_optout"]
   objects: ["object1"]
   bug_numbers: [1286606]
   notification_emails: ["telemetry-client-dev@mozilla.com"]
   description: This is an opt-out test entry with unexpired date and version.
   release_channel_collection: opt-out
   expiry_date: 2099-01-01
   expiry_version: "999.0"
 
--- a/toolkit/components/telemetry/TelemetryEvent.cpp
+++ b/toolkit/components/telemetry/TelemetryEvent.cpp
@@ -82,21 +82,21 @@ const uint32_t kEventCount = mozilla::Te
 // This is a special event id used to mark expired events, to make expiry checks
 // faster at runtime.
 const uint32_t kExpiredEventId = kEventCount + 1;
 static_assert(kEventCount < kExpiredEventId, "Should not overflow.");
 
 // This is the hard upper limit on the number of event records we keep in storage.
 // If we cross this limit, we will drop any further event recording until elements
 // are removed from storage.
-const uint32_t kMaxEventRecords = 10000;
+const uint32_t kMaxEventRecords = 1000;
 // Maximum length of any passed value string, in UTF8 byte sequence length.
-const uint32_t kMaxValueByteLength = 100;
+const uint32_t kMaxValueByteLength = 80;
 // Maximum length of any string value in the extra dictionary, in UTF8 byte sequence length.
-const uint32_t kMaxExtraValueByteLength = 100;
+const uint32_t kMaxExtraValueByteLength = 80;
 
 typedef nsDataHashtable<nsCStringHashKey, uint32_t> EventMapType;
 typedef nsClassHashtable<nsCStringHashKey, nsCString> StringMap;
 
 enum class RecordEventResult {
   Ok,
   UnknownEvent,
   InvalidExtraKey,
@@ -514,19 +514,23 @@ TelemetryEvent::RecordEvent(const nsACSt
       return NS_ERROR_FAILURE;
     }
 
     res = ::RecordEvent(lock, timestamp, aCategory, aMethod, aObject, value, extra);
   }
 
   // Trigger warnings or errors where needed.
   switch (res) {
-    case RecordEventResult::UnknownEvent:
-      JS_ReportErrorASCII(cx, "Unknown event.");
+    case RecordEventResult::UnknownEvent: {
+      JS_ReportErrorASCII(cx, R"(Unknown event: ["%s", "%s", "%s"])",
+                          PromiseFlatCString(aCategory).get(),
+                          PromiseFlatCString(aMethod).get(),
+                          PromiseFlatCString(aObject).get());
       return NS_ERROR_INVALID_ARG;
+    }
     case RecordEventResult::InvalidExtraKey:
       LogToBrowserConsole(nsIScriptError::warningFlag,
                           NS_LITERAL_STRING("Invalid extra key for event."));
       return NS_OK;
     case RecordEventResult::StorageLimitReached:
       LogToBrowserConsole(nsIScriptError::warningFlag,
                           NS_LITERAL_STRING("Event storage limit reached."));
       return NS_OK;
@@ -568,82 +572,82 @@ TelemetryEvent::CreateSnapshots(uint32_t
   if (!eventsArray) {
     return NS_ERROR_FAILURE;
   }
 
   for (uint32_t i = 0; i < events.Length(); ++i) {
     const EventRecord& record = events[i];
     const EventInfo& info = gEventInfo[record.EventId()];
 
-    // Each entry is an array of the form:
+    // Each entry is an array of one of the forms:
+    // [timestamp, category, method, object, value]
+    // [timestamp, category, method, object, null, extra]
     // [timestamp, category, method, object, value, extra]
-    JS::RootedObject itemsArray(cx, JS_NewArrayObject(cx, 6));
-    if (!itemsArray) {
-      return NS_ERROR_FAILURE;
-    }
+    JS::AutoValueVector items(cx);
 
     // Add timestamp.
     JS::Rooted<JS::Value> val(cx);
-    uint32_t itemIndex = 0;
-    val.setDouble(floor(record.Timestamp()));
-    if (!JS_DefineElement(cx, itemsArray, itemIndex++, val, JSPROP_ENUMERATE)) {
+    if (!items.append(JS::NumberValue(floor(record.Timestamp())))) {
       return NS_ERROR_FAILURE;
     }
 
     // Add category, method, object.
     const char* strings[] = {
       info.common_info.category(),
       info.method(),
       info.object(),
     };
-    for (uint32_t s = 0; s < ArrayLength(strings); ++s) {
-      const NS_ConvertUTF8toUTF16 wide(strings[s]);
-      val.setString(JS_NewUCStringCopyN(cx, wide.Data(), wide.Length()));
-      if (!JS_DefineElement(cx, itemsArray, itemIndex++, val, JSPROP_ENUMERATE)) {
+    for (const char* s : strings) {
+      const NS_ConvertUTF8toUTF16 wide(s);
+      if (!items.append(JS::StringValue(JS_NewUCStringCopyN(cx, wide.Data(), wide.Length())))) {
         return NS_ERROR_FAILURE;
       }
     }
 
-    // Add the optional string value.
-    if (!record.Value()) {
-      val.setNull();
-    } else {
+    // Add the optional string value only when needed.
+    // When extra is empty and this has no value, we can save a little space.
+    if (record.Value()) {
       const NS_ConvertUTF8toUTF16 wide(record.Value().value());
-      val.setString(JS_NewUCStringCopyN(cx, wide.Data(), wide.Length()));
-    }
-    if (!JS_DefineElement(cx, itemsArray, itemIndex++, val, JSPROP_ENUMERATE)) {
-      return NS_ERROR_FAILURE;
+      if (!items.append(JS::StringValue(JS_NewUCStringCopyN(cx, wide.Data(), wide.Length())))) {
+        return NS_ERROR_FAILURE;
+      }
+    } else if (!record.Extra().IsEmpty()) {
+      if (!items.append(JS::NullValue())) {
+        return NS_ERROR_FAILURE;
+      }
     }
 
     // Add the optional extra dictionary.
-    if (record.Extra().IsEmpty()) {
-      val.setNull();
-    } else {
+    // To save a little space, only add it when it is not empty.
+    if (!record.Extra().IsEmpty()) {
       JS::RootedObject obj(cx, JS_NewPlainObject(cx));
       if (!obj) {
         return NS_ERROR_FAILURE;
       }
 
+      // Add extra key & value entries.
       const ExtraArray& extra = record.Extra();
       for (uint32_t i = 0; i < extra.Length(); ++i) {
         const NS_ConvertUTF8toUTF16 wide(extra[i].value);
         JS::Rooted<JS::Value> value(cx);
         value.setString(JS_NewUCStringCopyN(cx, wide.Data(), wide.Length()));
 
         if (!JS_DefineProperty(cx, obj, extra[i].key.get(), value, JSPROP_ENUMERATE)) {
           return NS_ERROR_FAILURE;
         }
       }
       val.setObject(*obj);
-    }
-    if (!JS_DefineElement(cx, itemsArray, itemIndex++, val, JSPROP_ENUMERATE)) {
-      return NS_ERROR_FAILURE;
+
+      if (!items.append(val)) {
+        return NS_ERROR_FAILURE;
+      }
     }
 
     // Add the record to the events array.
+    JS::RootedObject itemsArray(cx, JS_NewArrayObject(cx, items));
     if (!JS_DefineElement(cx, eventsArray, i, itemsArray, JSPROP_ENUMERATE)) {
       return NS_ERROR_FAILURE;
     }
   }
 
   aResult.setObject(*eventsArray);
   return NS_OK;
 }
--- a/toolkit/components/telemetry/TelemetrySession.jsm
+++ b/toolkit/components/telemetry/TelemetrySession.jsm
@@ -1015,17 +1015,17 @@ var Impl = {
       return [];
     }
 
     let events = Telemetry.snapshotBuiltinEvents(this.getDatasetType(),
                                                  clearSubsession);
 
     // Don't return the test events outside of test environments.
     if (!this._testing) {
-      events = events.filter(e => e[1].startsWith("telemetry.test"));
+      events = events.filter(e => !e[1].startsWith("telemetry.test"));
     }
 
     return events;
   },
 
   getThreadHangStats: function getThreadHangStats(stats) {
     this._log.trace("getThreadHangStats");
 
--- a/toolkit/components/telemetry/parse_events.py
+++ b/toolkit/components/telemetry/parse_events.py
@@ -4,21 +4,21 @@
 
 import re
 import yaml
 import itertools
 import datetime
 import string
 from shared_telemetry_utils import add_expiration_postfix
 
-MAX_CATEGORY_NAME_LENGTH = 100
-MAX_METHOD_NAME_LENGTH = 40
-MAX_OBJECT_NAME_LENGTH = 40
-MAX_EXTRA_KEYS_COUNT = 20
-MAX_EXTRA_KEY_NAME_LENGTH = 20
+MAX_CATEGORY_NAME_LENGTH = 30
+MAX_METHOD_NAME_LENGTH = 20
+MAX_OBJECT_NAME_LENGTH = 20
+MAX_EXTRA_KEYS_COUNT = 10
+MAX_EXTRA_KEY_NAME_LENGTH = 15
 
 IDENTIFIER_PATTERN = r'^[a-zA-Z][a-zA-Z0-9_.]+[a-zA-Z0-9]$'
 DATE_PATTERN = r'^[0-9]{4}-[0-9]{2}-[0-9]{2}$'
 
 def nice_type_name(t):
     if isinstance(t, basestring):
         return "string"
     return t.__name__
@@ -121,18 +121,18 @@ def type_check_event_fields(category, de
 
     # Type-check fields.
     for k,v in definition.iteritems():
         ALL_FIELDS[k].check(k, v)
 
 def string_check(category, field_name, value, min_length, max_length, regex=None):
     # Length check.
     if len(value) > max_length:
-        raise ValueError("Value for %s in %s exceeds maximum length of %d" %\
-                         (field_name, category, max_length))
+        raise ValueError("Value '%s' for %s in %s exceeds maximum length of %d" %\
+                         (value, field_name, category, max_length))
     # Regex check.
     if regex and not re.match(regex, value):
         raise ValueError, 'String value for %s in %s is not matching pattern "%s": %s' % \
                           (field_name, category, regex, value)
 
 class EventData:
     """A class representing one event."""
 
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryEvents.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryEvents.js
@@ -4,27 +4,32 @@
 
 const OPTIN = Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN;
 const OPTOUT = Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTOUT;
 
 function checkEventFormat(events) {
   Assert.ok(Array.isArray(events), "Events should be serialized to an array.");
   for (let e of events) {
     Assert.ok(Array.isArray(e), "Event should be an array.");
-    Assert.equal(e.length, 6, "Event should have 6 elements.");
+    Assert.greaterOrEqual(e.length, 4, "Event should have at least 4 elements.");
+    Assert.lessOrEqual(e.length, 6, "Event should have at most 6 elements.");
 
     Assert.equal(typeof(e[0]), "number", "Element 0 should be a number.");
     Assert.equal(typeof(e[1]), "string", "Element 1 should be a string.");
     Assert.equal(typeof(e[2]), "string", "Element 2 should be a string.");
     Assert.equal(typeof(e[3]), "string", "Element 3 should be a string.");
 
-    Assert.ok(e[4] === null || typeof(e[4]) == "string",
-              "Event element 4 should be null or a string.");
-    Assert.ok(e[5] === null || typeof(e[5]) == "object",
-              "Event element 4 should be null or an object.");
+    if (e.length > 4) {
+      Assert.ok(e[4] === null || typeof(e[4]) == "string",
+                "Event element 4 should be null or a string.");
+    }
+    if (e.length > 5) {
+      Assert.ok(e[5] === null || typeof(e[5]) == "object",
+                "Event element 5 should be null or an object.");
+    }
 
     let extra = e[5];
     if (extra) {
       Assert.ok(Object.keys(extra).every(k => typeof(k) == "string"),
                 "All extra keys should be strings.");
       Assert.ok(Object.values(extra).every(v => typeof(v) == "string"),
                 "All extra values should be strings.");
     }
@@ -39,58 +44,63 @@ add_task(function* test_recording() {
     {optout: false, event: ["telemetry.test", "test1", "object1"]},
     {optout: false, event: ["telemetry.test", "test2", "object2"]},
 
     {optout: false, event: ["telemetry.test", "test1", "object1", "value"]},
     {optout: false, event: ["telemetry.test", "test1", "object1", "value", null]},
     {optout: false, event: ["telemetry.test", "test1", "object1", null, {"key1": "value1"}]},
     {optout: false, event: ["telemetry.test", "test1", "object1", "value", {"key1": "value1", "key2": "value2"}]},
 
-    {optout: true,  event: ["telemetry.test", "test_optout", "object1"]},
+    {optout: true,  event: ["telemetry.test", "optout", "object1"]},
     {optout: false, event: ["telemetry.test.second", "test", "object1"]},
     {optout: false, event: ["telemetry.test.second", "test", "object1", null, {"key1": "value1"}]},
   ];
 
   for (let entry of expected) {
     entry.tsBefore = Math.floor(Telemetry.msSinceProcessStart());
     try {
       Telemetry.recordEvent(...entry.event);
     } catch (ex) {
       Assert.ok(false, `Failed to record event ${JSON.stringify(entry.event)}: ${ex}`);
     }
     entry.tsAfter = Math.floor(Telemetry.msSinceProcessStart());
   }
 
+  // Strip off trailing null values to match the serialized events.
+  for (let entry of expected) {
+    let e = entry.event;
+    while ((e.length >= 3) && (e[e.length - 1] === null)) {
+      e.pop();
+    }
+  }
+
   // The following should not result in any recorded events.
   Assert.throws(() => Telemetry.recordEvent("unknown.category", "test1", "object1"),
-                /Error: Unknown event\./,
+                /Error: Unknown event: \["unknown.category", "test1", "object1"\]/,
                 "Should throw on unknown category.");
   Assert.throws(() => Telemetry.recordEvent("telemetry.test", "unknown", "object1"),
-                /Error: Unknown event\./,
+                /Error: Unknown event: \["telemetry.test", "unknown", "object1"\]/,
                 "Should throw on unknown method.");
   Assert.throws(() => Telemetry.recordEvent("telemetry.test", "test1", "unknown"),
-                /Error: Unknown event\./,
+                /Error: Unknown event: \["telemetry.test", "test1", "unknown"\]/,
                 "Should throw on unknown object.");
 
   let checkEvents = (events, expectedEvents) => {
     checkEventFormat(events);
     Assert.equal(events.length, expectedEvents.length,
                  "Snapshot should have the right number of events.");
 
     for (let i = 0; i < events.length; ++i) {
       let {tsBefore, tsAfter} = expectedEvents[i];
       let ts = events[i][0];
       Assert.greaterOrEqual(ts, tsBefore, "The recorded timestamp should be greater than the one before recording.");
       Assert.lessOrEqual(ts, tsAfter, "The recorded timestamp should be less than the one after recording.");
 
       let recordedData = events[i].slice(1);
       let expectedData = expectedEvents[i].event.slice();
-      for (let j = expectedData.length; j < 5; ++j) {
-        expectedData.push(null);
-      }
       Assert.deepEqual(recordedData, expectedData, "The recorded event data should match.");
     }
   };
 
   // Check that the expected events were recorded.
   let events = Telemetry.snapshotBuiltinEvents(OPTIN, false);
   checkEvents(events, expected);
 
@@ -118,27 +128,27 @@ add_task(function* test_clear() {
   events = Telemetry.snapshotBuiltinEvents(OPTIN, false);
   Assert.equal(events.length, 0, `Should have cleared the events.`);
 });
 
 add_task(function* test_expiry() {
   Telemetry.clearEvents();
 
   // Recording call with event that is expired by version.
-  Telemetry.recordEvent("telemetry.test", "test_expired_version", "object1");
+  Telemetry.recordEvent("telemetry.test", "expired_version", "object1");
   let events = Telemetry.snapshotBuiltinEvents(OPTIN, true);
   Assert.equal(events.length, 0, "Should not record event with expired version.");
 
   // Recording call with event that is expired by date.
-  Telemetry.recordEvent("telemetry.test", "test_expired_date", "object1");
+  Telemetry.recordEvent("telemetry.test", "expired_date", "object1");
   events = Telemetry.snapshotBuiltinEvents(OPTIN, true);
   Assert.equal(events.length, 0, "Should not record event with expired date.");
 
   // Recording call with event that has expiry_version and expiry_date in the future.
-  Telemetry.recordEvent("telemetry.test", "test_not_expired_optout", "object1");
+  Telemetry.recordEvent("telemetry.test", "not_expired_optout", "object1");
   events = Telemetry.snapshotBuiltinEvents(OPTOUT, true);
   Assert.equal(events.length, 1, "Should record event when date and version are not expired.");
 });
 
 add_task(function* test_invalidParams() {
   Telemetry.clearEvents();
 
   // Recording call with wrong type for value argument.
@@ -161,53 +171,60 @@ add_task(function* test_invalidParams() 
   events = Telemetry.snapshotBuiltinEvents(OPTIN, true);
   Assert.equal(events.length, 0, "Should not record event when extra argument with invalid value type is passed.");
 });
 
 add_task(function* test_storageLimit() {
   Telemetry.clearEvents();
 
   // Record more events than the storage limit allows.
-  let LIMIT = 10000;
+  let LIMIT = 1000;
   let COUNT = LIMIT + 10;
   for (let i = 0; i < COUNT; ++i) {
     Telemetry.recordEvent("telemetry.test", "test1", "object1", String(i));
   }
 
   // Check that the right events were recorded.
   let events = Telemetry.snapshotBuiltinEvents(OPTIN, true);
   Assert.equal(events.length, LIMIT, `Should have only recorded ${LIMIT} events`);
   Assert.ok(events.every((e, idx) => e[4] === String(idx)),
             "Should have recorded all events from before hitting the limit.");
 });
 
 add_task(function* test_valueLimits() {
   Telemetry.clearEvents();
 
   // Record values that are at or over the limits for string lengths.
-  let LIMIT = 100;
+  let LIMIT = 80;
   let expected = [
     ["telemetry.test", "test1", "object1", "a".repeat(LIMIT - 10), null],
     ["telemetry.test", "test1", "object1", "a".repeat(LIMIT     ), null],
     ["telemetry.test", "test1", "object1", "a".repeat(LIMIT +  1), null],
     ["telemetry.test", "test1", "object1", "a".repeat(LIMIT + 10), null],
 
     ["telemetry.test", "test1", "object1", null, {key1: "a".repeat(LIMIT - 10)}],
     ["telemetry.test", "test1", "object1", null, {key1: "a".repeat(LIMIT     )}],
     ["telemetry.test", "test1", "object1", null, {key1: "a".repeat(LIMIT +  1)}],
     ["telemetry.test", "test1", "object1", null, {key1: "a".repeat(LIMIT + 10)}],
   ];
 
   for (let event of expected) {
     Telemetry.recordEvent(...event);
     if (event[3]) {
-      event[3] = event[3].substr(0, 100);
+      event[3] = event[3].substr(0, LIMIT);
     }
     if (event[4]) {
-      event[4].key1 = event[4].key1.substr(0, 100);
+      event[4].key1 = event[4].key1.substr(0, LIMIT);
+    }
+  }
+
+  // Strip off trailing null values to match the serialized events.
+  for (let e of expected) {
+    while ((e.length >= 3) && (e[e.length - 1] === null)) {
+      e.pop();
     }
   }
 
   // Check that the right events were recorded.
   let events = Telemetry.snapshotBuiltinEvents(OPTIN, true);
   Assert.equal(events.length, expected.length,
                "Should have recorded the expected number of events");
   for (let i = 0; i < expected.length; ++i) {
--- a/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetrySession.js
@@ -720,16 +720,23 @@ add_task(function* test_checkSubsessionE
   let expected = [
     ["telemetry.test", "test1", "object1", "a", null],
     ["telemetry.test", "test1", "object1", null, {key1: "value"}],
   ];
   for (let event of expected) {
     Telemetry.recordEvent(...event);
   }
 
+  // Strip off trailing null values to match the serialized events.
+  for (let e of expected) {
+    while ((e.length >= 3) && (e[e.length - 1] === null)) {
+      e.pop();
+    }
+  }
+
   // Check that events are not available in classic pings but are in subsession
   // pings. Also clear the subsession.
   let classic = TelemetrySession.getPayload();
   let subsession = TelemetrySession.getPayload("environment-change", true);
 
   Assert.ok("events" in classic.processes.parent, "Should have an events field in classic payload.");
   Assert.ok("events" in subsession.processes.parent, "Should have an events field in subsession payload.");
 
--- a/widget/gtk/gtk3drawing.cpp
+++ b/widget/gtk/gtk3drawing.cpp
@@ -2025,19 +2025,16 @@ moz_gtk_get_widget_border(WidgetNodeType
                 gtk_style_context_add_class(style, "image-button");
             }
 
             moz_gtk_add_style_padding(style, left, top, right, bottom);
 
             if (widget == MOZ_GTK_TOOLBAR_BUTTON)
                 gtk_style_context_restore(style);
 
-            // XXX: Subtract 1 pixel from the border to account for the added
-            // -moz-focus-inner border (Bug 1228281).
-            *left -= 1; *top -= 1; *right -= 1; *bottom -= 1;
             moz_gtk_add_style_border(style, left, top, right, bottom);
 
             ReleaseStyleContext(style);
             return MOZ_GTK_SUCCESS;
         }
     case MOZ_GTK_ENTRY:
         {
             style = ClaimStyleContext(MOZ_GTK_ENTRY);