merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 20 Mar 2017 13:10:48 +0100
changeset 348421 8d967436d696d1f8e3fb33cf7e3d32a72457ffa6
parent 348398 cb4e80abb488f37ec67c4760a128ae177697667b (current diff)
parent 348420 bcb15c0f5a986cc25da6eb7ac22045952a0be0db (diff)
child 348422 7c3e1b806e3c3d94b60e880b5dbc04edaf7960c7
child 348479 530fd9e214e79bf11582ad9db6c77d2705b406c6
child 348520 ced789a467f2d8a098d020dd75baa50451e0d759
child 349257 25901fdd31e74c71027db5fbae0dd882b56c01f0
push id39156
push usercbook@mozilla.com
push dateMon, 20 Mar 2017 13:29:29 +0000
treeherderautoland@7c3e1b806e3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
security/nss/lib/freebl/ecl/ecl_curve.c
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -852,16 +852,35 @@ Event::GetEventPopupControlState(WidgetE
           abuse = openControlled;
         }
         break;
       default:
         break;
       }
     }
     break;
+  case ePointerEventClass:
+    if (aEvent->IsTrusted() &&
+        aEvent->AsPointerEvent()->button == WidgetMouseEvent::eLeftButton) {
+      switch(aEvent->mMessage) {
+      case ePointerUp:
+        if (PopupAllowedForEvent("pointerup")) {
+          abuse = openControlled;
+        }
+        break;
+      case ePointerDown:
+        if (PopupAllowedForEvent("pointerdown")) {
+          abuse = openControlled;
+        }
+        break;
+      default:
+        break;
+      }
+    }
+    break;
   case eFormEventClass:
     // For these following events only allow popups if they're
     // triggered while handling user input. See
     // nsPresShell::HandleEventInternal() for details.
     if (EventStateManager::IsHandlingUserInput()) {
       switch(aEvent->mMessage) {
       case eFormSubmit:
         if (PopupAllowedForEvent("submit")) {
--- a/dom/events/test/pointerevents/mochitest.ini
+++ b/dom/events/test/pointerevents/mochitest.ini
@@ -9,16 +9,18 @@ support-files =
 [test_pointerevent_attributes_hoverable_pointers-manual.html]
   support-files =
     pointerevent_attributes_hoverable_pointers-manual.html
     ./resources/pointerevent_attributes_hoverable_pointers-iframe.html
 [test_pointerevent_attributes_nohover_pointers-manual.html]
   support-files =
     pointerevent_attributes_nohover_pointers-manual.html
     ./resources/pointerevent_attributes_hoverable_pointers-iframe.html
+[test_pointerevent_boundary_events_in_capturing-manual.html]
+  support-files = pointerevent_boundary_events_in_capturing-manual.html
 [test_pointerevent_capture_mouse-manual.html]
   support-files = pointerevent_capture_mouse-manual.html
 [test_pointerevent_capture_suppressing_mouse-manual.html]
   support-files = pointerevent_capture_suppressing_mouse-manual.html
 [test_pointerevent_change-touch-action-onpointerdown_touch-manual.html]
   support-files = pointerevent_change-touch-action-onpointerdown_touch-manual.html
   disabled = disabled
 [test_pointerevent_constructor.html]
@@ -118,11 +120,12 @@ support-files =
     pointerevent_touch-action-pan-up-css_touch-manual.html
 [test_bug1285128.html]
 [test_bug1293174_implicit_pointer_capture_for_touch_1.html]
   support-files = bug1293174_implicit_pointer_capture_for_touch_1.html
 [test_bug1293174_implicit_pointer_capture_for_touch_2.html]
   support-files = bug1293174_implicit_pointer_capture_for_touch_2.html
 [test_bug1303704.html]
 [test_bug1323158.html]
+[test_trigger_popup_by_pointer_events.html]
 [test_empty_file.html]
   disabled = disabled # Bug 1150091 - Issue with support-files
 [test_bug1315862.html]
new file mode 100644
--- /dev/null
+++ b/dom/events/test/pointerevents/test_pointerevent_boundary_events_in_capturing-manual.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>W3C pointerevent_boundary_events_in_capturing-manual.html in Mochitest form</title>
+    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+    <script type="text/javascript" src="mochitest_support_external.js"></script>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+    <script type="text/javascript">
+      SimpleTest.waitForExplicitFinish();
+      function startTest() {
+        runTestInNewWindow("pointerevent_boundary_events_in_capturing-manual.html", true);
+      }
+      function executeTest(int_win) {
+        sendMouseEvent(int_win, "target0", "mousemove");
+        sendMouseEvent(int_win, "target0", "mousedown");
+        sendMouseEvent(int_win, "target0", "mousemove", {buttons: 1});
+        sendMouseEvent(int_win, "target0", "mousemove", {buttons: 1});
+        sendMouseEvent(int_win, "target0", "mouseup");
+
+        window.addEventListener("message", function(aEvent) {
+          if (aEvent.data == "Test Touch") {
+            // Synthesize touch events to run this test.
+            sendTouchEvent(int_win, "target0", "touchstart");
+            sendTouchEvent(int_win, "target0", "touchmove");
+            sendTouchEvent(int_win, "target0", "touchmove");
+            sendTouchEvent(int_win, "target0", "touchmove");
+            sendTouchEvent(int_win, "target0", "touchend");
+            window.postMessage("Test Pen", "*");
+          } else if (aEvent.data == "Test Pen") {
+            // Synthesize pen events to run this test.
+            sendMouseEvent(int_win, "target0", "mousemove", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
+            sendMouseEvent(int_win, "target0", "mousedown", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
+            sendMouseEvent(int_win, "target0", "mousemove", {inputSource:MouseEvent.MOZ_SOURCE_PEN, buttons: 1});
+            sendMouseEvent(int_win, "target0", "mousemove", {inputSource:MouseEvent.MOZ_SOURCE_PEN, buttons: 1});
+            sendMouseEvent(int_win, "target0", "mouseup", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
+          }
+        });
+        window.postMessage("Test Touch", "*");
+      }
+    </script>
+  </head>
+  <body>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/events/test/pointerevents/test_trigger_popup_by_pointer_events.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for triggering popup by pointer events</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<div id="target" style="width: 50px; height: 50px; background: green"></div>
+<script>
+
+SimpleTest.waitForExplicitFinish();
+
+function sendMouseEvent(element, eventName, listenEventName, handler) {
+  element.addEventListener(listenEventName, handler, {once: true});
+  synthesizeMouseAtCenter(element, {type: eventName});
+}
+
+function checkAllowOpenPopup(e) {
+  let w = window.open("about:blank");
+  ok(w, "Should allow popup in the " + e.type + " listener");
+  if (w) {
+    w.close();
+  }
+}
+
+function checkBlockOpenPopup(e) {
+  let w = window.open("about:blank");
+  ok(!w, "Should block popup in the " + e.type + " listener");
+  if (w) {
+    w.close();
+  }
+}
+
+function startTest() {
+  let target = document.getElementById("target");
+  // By default, only allow opening popup in the pointerup listener.
+  sendMouseEvent(target, "mousemove", "pointermove", checkBlockOpenPopup);
+  sendMouseEvent(target, "mousedown", "pointerdown", checkBlockOpenPopup);
+  sendMouseEvent(target, "mousemove", "pointermove", checkBlockOpenPopup);
+  sendMouseEvent(target, "mouseup", "pointerup", checkAllowOpenPopup);
+  SpecialPowers.pushPrefEnv({"set": [["dom.popup_allowed_events",
+                                      "pointerdown pointerup"]]}, () => {
+    // Adding pointerdown to preference should work
+    sendMouseEvent(target, "mousemove", "pointermove", checkBlockOpenPopup);
+    sendMouseEvent(target, "mousedown", "pointerdown", checkAllowOpenPopup);
+    sendMouseEvent(target, "mousemove", "pointermove", checkBlockOpenPopup);
+    sendMouseEvent(target, "mouseup", "pointerup", checkAllowOpenPopup);
+    SpecialPowers.pushPrefEnv({"set": [["dom.popup_allowed_events",
+                                        "pointerdown pointerup pointermove"]]}, () => {
+      // Adding pointermove to preference should be no effect.
+      sendMouseEvent(target, "mousemove", "pointermove", checkBlockOpenPopup);
+      sendMouseEvent(target, "mousedown", "pointerdown", checkAllowOpenPopup);
+      sendMouseEvent(target, "mousemove", "pointermove", checkBlockOpenPopup);
+      sendMouseEvent(target, "mouseup", "pointerup", checkAllowOpenPopup);
+      SimpleTest.finish();
+    });
+  });
+}
+
+const DENY_ACTION = SpecialPowers.Ci.nsIPermissionManager.DENY_ACTION;
+
+SimpleTest.waitForFocus(() => {
+  SpecialPowers.pushPermissions([{'type': 'popup', 'allow': DENY_ACTION,
+                                  'context': document}], () => {
+    SpecialPowers.pushPrefEnv({
+      "set": [["dom.w3c_pointer_events.enabled", true]]
+    }, startTest);
+  });
+});
+
+</script>
+</body>
+</html>
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -336,17 +336,32 @@ APZCCallbackHelper::InitializeRootDispla
   nsIContent* content = aPresShell->GetDocument()->GetDocumentElement();
   if (!content) {
     return;
   }
 
   uint32_t presShellId;
   FrameMetrics::ViewID viewId;
   if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(content, &presShellId, &viewId)) {
-    // Note that the base rect that goes with these margins is set in
+    nsPresContext* pc = aPresShell->GetPresContext();
+    // This code is only correct for root content or toplevel documents.
+    MOZ_ASSERT(!pc || pc->IsRootContentDocument() || !pc->GetParentPresContext());
+    nsIFrame* frame = aPresShell->GetRootScrollFrame();
+    if (!frame) {
+      frame = aPresShell->GetRootFrame();
+    }
+    nsRect baseRect;
+    if (frame) {
+      baseRect =
+        nsRect(nsPoint(0, 0), nsLayoutUtils::CalculateCompositionSizeForFrame(frame));
+    } else if (pc) {
+      baseRect = nsRect(nsPoint(0, 0), pc->GetVisibleArea().Size());
+    }
+    nsLayoutUtils::SetDisplayPortBaseIfNotSet(content, baseRect);
+    // Note that we also set the base rect that goes with these margins in
     // nsRootBoxFrame::BuildDisplayList.
     nsLayoutUtils::SetDisplayPortMargins(content, aPresShell, ScreenMargin(), 0,
         nsLayoutUtils::RepaintMode::DoNotRepaint);
     nsLayoutUtils::SetZeroMarginDisplayPortOnAsyncScrollableAncestors(
         content->GetPrimaryFrame(), nsLayoutUtils::RepaintMode::DoNotRepaint);
   }
 }
 
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1163,17 +1163,17 @@ pref("dom.disable_window_open_feature.mi
 pref("dom.disable_window_open_feature.status",      true);
 
 pref("dom.allow_scripts_to_close_windows",          false);
 
 pref("dom.require_user_interaction_for_beforeunload", true);
 
 pref("dom.disable_open_during_load",                false);
 pref("dom.popup_maximum",                           20);
-pref("dom.popup_allowed_events", "change click dblclick mouseup notificationclick reset submit touchend");
+pref("dom.popup_allowed_events", "change click dblclick mouseup pointerup notificationclick reset submit touchend");
 pref("dom.disable_open_click_delay", 1000);
 
 pref("dom.storage.enabled", true);
 pref("dom.storage.default_quota",      5120);
 pref("dom.storage.testing", false);
 
 pref("dom.send_after_paint_to_content", false);
 
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-37ccb22f8e51
+d621b1e53054
--- a/security/nss/cmd/ecperf/ecperf.c
+++ b/security/nss/cmd/ecperf/ecperf.c
@@ -21,80 +21,16 @@
 
 #define CK_EXTERN extern
 #define CK_PKCS11_FUNCTION_INFO(func) \
     CK_RV __PASTE(NS, func)
 #define CK_NEED_ARG_LIST 1
 
 #include "pkcs11f.h"
 
-/* mapping between ECCurveName enum and pointers to ECCurveParams */
-static SECOidTag ecCurve_oid_map[] = {
-    SEC_OID_UNKNOWN,                /* ECCurve_noName */
-    SEC_OID_ANSIX962_EC_PRIME192V1, /* ECCurve_NIST_P192 */
-    SEC_OID_SECG_EC_SECP224R1,      /* ECCurve_NIST_P224 */
-    SEC_OID_ANSIX962_EC_PRIME256V1, /* ECCurve_NIST_P256 */
-    SEC_OID_SECG_EC_SECP384R1,      /* ECCurve_NIST_P384 */
-    SEC_OID_SECG_EC_SECP521R1,      /* ECCurve_NIST_P521 */
-    SEC_OID_SECG_EC_SECT163K1,      /* ECCurve_NIST_K163 */
-    SEC_OID_SECG_EC_SECT163R1,      /* ECCurve_NIST_B163 */
-    SEC_OID_SECG_EC_SECT233K1,      /* ECCurve_NIST_K233 */
-    SEC_OID_SECG_EC_SECT233R1,      /* ECCurve_NIST_B233 */
-    SEC_OID_SECG_EC_SECT283K1,      /* ECCurve_NIST_K283 */
-    SEC_OID_SECG_EC_SECT283R1,      /* ECCurve_NIST_B283 */
-    SEC_OID_SECG_EC_SECT409K1,      /* ECCurve_NIST_K409 */
-    SEC_OID_SECG_EC_SECT409R1,      /* ECCurve_NIST_B409 */
-    SEC_OID_SECG_EC_SECT571K1,      /* ECCurve_NIST_K571 */
-    SEC_OID_SECG_EC_SECT571R1,      /* ECCurve_NIST_B571 */
-    SEC_OID_ANSIX962_EC_PRIME192V2,
-    SEC_OID_ANSIX962_EC_PRIME192V3,
-    SEC_OID_ANSIX962_EC_PRIME239V1,
-    SEC_OID_ANSIX962_EC_PRIME239V2,
-    SEC_OID_ANSIX962_EC_PRIME239V3,
-    SEC_OID_ANSIX962_EC_C2PNB163V1,
-    SEC_OID_ANSIX962_EC_C2PNB163V2,
-    SEC_OID_ANSIX962_EC_C2PNB163V3,
-    SEC_OID_ANSIX962_EC_C2PNB176V1,
-    SEC_OID_ANSIX962_EC_C2TNB191V1,
-    SEC_OID_ANSIX962_EC_C2TNB191V2,
-    SEC_OID_ANSIX962_EC_C2TNB191V3,
-    SEC_OID_ANSIX962_EC_C2PNB208W1,
-    SEC_OID_ANSIX962_EC_C2TNB239V1,
-    SEC_OID_ANSIX962_EC_C2TNB239V2,
-    SEC_OID_ANSIX962_EC_C2TNB239V3,
-    SEC_OID_ANSIX962_EC_C2PNB272W1,
-    SEC_OID_ANSIX962_EC_C2PNB304W1,
-    SEC_OID_ANSIX962_EC_C2TNB359V1,
-    SEC_OID_ANSIX962_EC_C2PNB368W1,
-    SEC_OID_ANSIX962_EC_C2TNB431R1,
-    SEC_OID_SECG_EC_SECP112R1,
-    SEC_OID_SECG_EC_SECP112R2,
-    SEC_OID_SECG_EC_SECP128R1,
-    SEC_OID_SECG_EC_SECP128R2,
-    SEC_OID_SECG_EC_SECP160K1,
-    SEC_OID_SECG_EC_SECP160R1,
-    SEC_OID_SECG_EC_SECP160R2,
-    SEC_OID_SECG_EC_SECP192K1,
-    SEC_OID_SECG_EC_SECP224K1,
-    SEC_OID_SECG_EC_SECP256K1,
-    SEC_OID_SECG_EC_SECT113R1,
-    SEC_OID_SECG_EC_SECT113R2,
-    SEC_OID_SECG_EC_SECT131R1,
-    SEC_OID_SECG_EC_SECT131R2,
-    SEC_OID_SECG_EC_SECT163R1,
-    SEC_OID_SECG_EC_SECT193R1,
-    SEC_OID_SECG_EC_SECT193R2,
-    SEC_OID_SECG_EC_SECT239K1,
-    SEC_OID_UNKNOWN, /* ECCurve_WTLS_1 */
-    SEC_OID_UNKNOWN, /* ECCurve_WTLS_8 */
-    SEC_OID_UNKNOWN, /* ECCurve_WTLS_9 */
-    SEC_OID_CURVE25519,
-    SEC_OID_UNKNOWN /* ECCurve_pastLastCurve */
-};
-
 typedef SECStatus (*op_func)(void *, void *, void *);
 typedef SECStatus (*pk11_op_func)(CK_SESSION_HANDLE, void *, void *, void *);
 
 typedef struct ThreadDataStr {
     op_func op;
     void *p1;
     void *p2;
     void *p3;
@@ -369,40 +305,16 @@ PKCS11_Verify(CK_SESSION_HANDLE session,
     crv = NSC_Verify(session, digest->data, digest->len, sig->data, sig->len);
     if (crv != CKR_OK) {
         printf("Verify Failed CK_RV=0x%x\n", (int)crv);
         return SECFailure;
     }
     return SECSuccess;
 }
 
-static SECStatus
-ecName2params(ECCurveName curve, SECKEYECParams *params)
-{
-    SECOidData *oidData = NULL;
-
-    if ((curve < ECCurve_noName) || (curve > ECCurve_pastLastCurve) ||
-        ((oidData = SECOID_FindOIDByTag(ecCurve_oid_map[curve])) == NULL)) {
-        PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
-        return SECFailure;
-    }
-
-    SECITEM_AllocItem(NULL, params, (2 + oidData->oid.len));
-    /*
-     * params->data needs to contain the ASN encoding of an object ID (OID)
-     * representing the named curve. The actual OID is in
-     * oidData->oid.data so we simply prepend 0x06 and OID length
-     */
-    params->data[0] = SEC_ASN1_OBJECT_ID;
-    params->data[1] = oidData->oid.len;
-    memcpy(params->data + 2, oidData->oid.data, oidData->oid.len);
-
-    return SECSuccess;
-}
-
 /* Performs basic tests of elliptic curve cryptography over prime fields.
  * If tests fail, then it prints an error message, aborts, and returns an
  * error code. Otherwise, returns 0. */
 SECStatus
 ectest_curve_pkcs11(ECCurveName curve, int iterations, int numThreads)
 {
     CK_OBJECT_HANDLE ecPriv;
     CK_OBJECT_HANDLE ecPub;
@@ -418,17 +330,17 @@ ectest_curve_pkcs11(ECCurveName curve, i
     PRLock *lock = NULL;
     double signRate, deriveRate = 0;
     CK_ATTRIBUTE template;
     SECStatus rv;
     CK_RV crv;
 
     ecParams.data = NULL;
     ecParams.len = 0;
-    rv = ecName2params(curve, &ecParams);
+    rv = SECU_ecName2params(curve, &ecParams);
     if (rv != SECSuccess) {
         goto cleanup;
     }
 
     crv = NSC_OpenSession(1, CKF_SERIAL_SESSION, NULL, 0, &session);
     if (crv != CKR_OK) {
         printf("OpenSession Failed CK_RV=0x%x\n", (int)crv);
         return SECFailure;
@@ -537,52 +449,35 @@ ectest_curve_freebl(ECCurveName curve, i
     ECParams ecParams = { 0 };
     ECPrivateKey *ecPriv = NULL;
     ECPublicKey ecPub;
     SECItem sig;
     SECItem digest;
     unsigned char sigData[256];
     unsigned char digestData[20];
     double signRate, deriveRate = 0;
-    char genenc[3 + 2 * 2 * MAX_ECKEY_LEN];
     SECStatus rv = SECFailure;
     PLArenaPool *arena;
+    SECItem ecEncodedParams = { siBuffer, NULL, 0 };
 
     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
     if (!arena) {
         return SECFailure;
     }
 
     if ((curve < ECCurve_noName) || (curve > ECCurve_pastLastCurve)) {
         PORT_FreeArena(arena, PR_FALSE);
         return SECFailure;
     }
 
-    ecParams.name = curve;
-    ecParams.type = ec_params_named;
-    ecParams.curveOID.data = NULL;
-    ecParams.curveOID.len = 0;
-    ecParams.curve.seed.data = NULL;
-    ecParams.curve.seed.len = 0;
-    ecParams.DEREncoding.data = NULL;
-    ecParams.DEREncoding.len = 0;
-
-    ecParams.fieldID.size = ecCurve_map[curve]->size;
-    ecParams.fieldID.type = fieldType;
-    SECU_HexString2SECItem(arena, &ecParams.fieldID.u.prime, ecCurve_map[curve]->irr);
-    SECU_HexString2SECItem(arena, &ecParams.curve.a, ecCurve_map[curve]->curvea);
-    SECU_HexString2SECItem(arena, &ecParams.curve.b, ecCurve_map[curve]->curveb);
-    genenc[0] = '0';
-    genenc[1] = '4';
-    genenc[2] = '\0';
-    strcat(genenc, ecCurve_map[curve]->genx);
-    strcat(genenc, ecCurve_map[curve]->geny);
-    SECU_HexString2SECItem(arena, &ecParams.base, genenc);
-    SECU_HexString2SECItem(arena, &ecParams.order, ecCurve_map[curve]->order);
-    ecParams.cofactor = ecCurve_map[curve]->cofactor;
+    rv = SECU_ecName2params(curve, &ecEncodedParams);
+    if (rv != SECSuccess) {
+        goto cleanup;
+    }
+    EC_FillParams(arena, &ecEncodedParams, &ecParams);
 
     PORT_Memset(digestData, 0xa5, sizeof(digestData));
     digest.data = digestData;
     digest.len = sizeof(digestData);
     sig.data = sigData;
     sig.len = sizeof(sigData);
 
     rv = EC_NewKey(&ecParams, &ecPriv);
@@ -613,16 +508,17 @@ ectest_curve_freebl(ECCurveName curve, i
         rv = M_TimeOperation(genericThread, (op_func)ECDSA_VerifyDigest, "ECDSA_Verify",
                              &ecPub, &sig, &digest, iterations, numThreads, 0, 0, 0, NULL);
         if (rv != SECSuccess) {
             goto cleanup;
         }
     }
 
 cleanup:
+    SECITEM_FreeItem(&ecEncodedParams, PR_FALSE);
     PORT_FreeArena(arena, PR_FALSE);
     PORT_FreeArena(ecPriv->ecParams.arena, PR_FALSE);
     return rv;
 }
 
 /* Prints help information. */
 void
 printUsage(char *prog)
--- a/security/nss/cmd/fbectest/fbectest.c
+++ b/security/nss/cmd/fbectest/fbectest.c
@@ -83,36 +83,29 @@ ectest_ecdh_kat(ECDH_KAT *kat)
     ECPrivateKey *ecPriv = NULL;
     SECItem theirKey = { siBuffer, NULL, 0 };
     SECStatus rv = SECFailure;
     PLArenaPool *arena = NULL;
     SECItem seed = { siBuffer, NULL, 0 };
     SECItem answer = { siBuffer, NULL, 0 };
     SECItem answer2 = { siBuffer, NULL, 0 };
     SECItem derived = { siBuffer, NULL, 0 };
-    char genenc[3 + 2 * 2 * MAX_ECKEY_LEN];
+    SECItem ecEncodedParams = { siBuffer, NULL, 0 };
     int i;
 
-    rv = init_params(&ecParams, curve, &arena, kat->fieldType);
-    if (rv != SECSuccess) {
-        return rv;
+    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+    if (!arena) {
+        return SECFailure;
     }
 
-    SECU_HexString2SECItem(arena, &ecParams.fieldID.u.prime, ecCurve_map[curve]->irr);
-    SECU_HexString2SECItem(arena, &ecParams.curve.a, ecCurve_map[curve]->curvea);
-    SECU_HexString2SECItem(arena, &ecParams.curve.b, ecCurve_map[curve]->curveb);
-    genenc[0] = '0';
-    genenc[1] = '4';
-    genenc[2] = '\0';
-    PORT_Assert(PR_ARRAY_SIZE(genenc) >= PORT_Strlen(ecCurve_map[curve]->genx));
-    PORT_Assert(PR_ARRAY_SIZE(genenc) >= PORT_Strlen(ecCurve_map[curve]->geny));
-    strcat(genenc, ecCurve_map[curve]->genx);
-    strcat(genenc, ecCurve_map[curve]->geny);
-    SECU_HexString2SECItem(arena, &ecParams.base, genenc);
-    SECU_HexString2SECItem(arena, &ecParams.order, ecCurve_map[curve]->order);
+    rv = SECU_ecName2params(curve, &ecEncodedParams);
+    if (rv != SECSuccess) {
+        goto cleanup;
+    }
+    EC_FillParams(arena, &ecEncodedParams, &ecParams);
 
     if (kat->our_pubhex) {
         SECU_HexString2SECItem(arena, &answer, kat->our_pubhex);
     }
     SECU_HexString2SECItem(arena, &seed, kat->privhex);
     rv = EC_NewKeyFromSeed(&ecParams, &ecPriv, seed.data, seed.len);
     if (rv != SECSuccess) {
         rv = SECFailure;
@@ -156,16 +149,17 @@ ectest_ecdh_kat(ECDH_KAT *kat)
         printBuf(&answer2);
         printf("derived:  ");
         printBuf(&ecPriv->privateValue);
         rv = SECFailure;
         goto cleanup;
     }
 
 cleanup:
+    SECITEM_FreeItem(&ecEncodedParams, PR_FALSE);
     PORT_FreeArena(arena, PR_FALSE);
     if (ecPriv) {
         PORT_FreeArena(ecPriv->ecParams.arena, PR_FALSE);
     }
     if (derived.data) {
         SECITEM_FreeItem(&derived, PR_FALSE);
     }
     return rv;
--- a/security/nss/cmd/lib/basicutil.c
+++ b/security/nss/cmd/lib/basicutil.c
@@ -769,8 +769,98 @@ SECU_HexString2SECItem(PLArenaPool *aren
             item->data[i / 2] = byteval;
             byteval = 0;
         }
         i++;
     }
 
     return item;
 }
+
+/* mapping between ECCurveName enum and SECOidTags */
+static SECOidTag ecCurve_oid_map[] = {
+    SEC_OID_UNKNOWN,                /* ECCurve_noName */
+    SEC_OID_ANSIX962_EC_PRIME192V1, /* ECCurve_NIST_P192 */
+    SEC_OID_SECG_EC_SECP224R1,      /* ECCurve_NIST_P224 */
+    SEC_OID_ANSIX962_EC_PRIME256V1, /* ECCurve_NIST_P256 */
+    SEC_OID_SECG_EC_SECP384R1,      /* ECCurve_NIST_P384 */
+    SEC_OID_SECG_EC_SECP521R1,      /* ECCurve_NIST_P521 */
+    SEC_OID_SECG_EC_SECT163K1,      /* ECCurve_NIST_K163 */
+    SEC_OID_SECG_EC_SECT163R1,      /* ECCurve_NIST_B163 */
+    SEC_OID_SECG_EC_SECT233K1,      /* ECCurve_NIST_K233 */
+    SEC_OID_SECG_EC_SECT233R1,      /* ECCurve_NIST_B233 */
+    SEC_OID_SECG_EC_SECT283K1,      /* ECCurve_NIST_K283 */
+    SEC_OID_SECG_EC_SECT283R1,      /* ECCurve_NIST_B283 */
+    SEC_OID_SECG_EC_SECT409K1,      /* ECCurve_NIST_K409 */
+    SEC_OID_SECG_EC_SECT409R1,      /* ECCurve_NIST_B409 */
+    SEC_OID_SECG_EC_SECT571K1,      /* ECCurve_NIST_K571 */
+    SEC_OID_SECG_EC_SECT571R1,      /* ECCurve_NIST_B571 */
+    SEC_OID_ANSIX962_EC_PRIME192V2,
+    SEC_OID_ANSIX962_EC_PRIME192V3,
+    SEC_OID_ANSIX962_EC_PRIME239V1,
+    SEC_OID_ANSIX962_EC_PRIME239V2,
+    SEC_OID_ANSIX962_EC_PRIME239V3,
+    SEC_OID_ANSIX962_EC_C2PNB163V1,
+    SEC_OID_ANSIX962_EC_C2PNB163V2,
+    SEC_OID_ANSIX962_EC_C2PNB163V3,
+    SEC_OID_ANSIX962_EC_C2PNB176V1,
+    SEC_OID_ANSIX962_EC_C2TNB191V1,
+    SEC_OID_ANSIX962_EC_C2TNB191V2,
+    SEC_OID_ANSIX962_EC_C2TNB191V3,
+    SEC_OID_ANSIX962_EC_C2PNB208W1,
+    SEC_OID_ANSIX962_EC_C2TNB239V1,
+    SEC_OID_ANSIX962_EC_C2TNB239V2,
+    SEC_OID_ANSIX962_EC_C2TNB239V3,
+    SEC_OID_ANSIX962_EC_C2PNB272W1,
+    SEC_OID_ANSIX962_EC_C2PNB304W1,
+    SEC_OID_ANSIX962_EC_C2TNB359V1,
+    SEC_OID_ANSIX962_EC_C2PNB368W1,
+    SEC_OID_ANSIX962_EC_C2TNB431R1,
+    SEC_OID_SECG_EC_SECP112R1,
+    SEC_OID_SECG_EC_SECP112R2,
+    SEC_OID_SECG_EC_SECP128R1,
+    SEC_OID_SECG_EC_SECP128R2,
+    SEC_OID_SECG_EC_SECP160K1,
+    SEC_OID_SECG_EC_SECP160R1,
+    SEC_OID_SECG_EC_SECP160R2,
+    SEC_OID_SECG_EC_SECP192K1,
+    SEC_OID_SECG_EC_SECP224K1,
+    SEC_OID_SECG_EC_SECP256K1,
+    SEC_OID_SECG_EC_SECT113R1,
+    SEC_OID_SECG_EC_SECT113R2,
+    SEC_OID_SECG_EC_SECT131R1,
+    SEC_OID_SECG_EC_SECT131R2,
+    SEC_OID_SECG_EC_SECT163R1,
+    SEC_OID_SECG_EC_SECT193R1,
+    SEC_OID_SECG_EC_SECT193R2,
+    SEC_OID_SECG_EC_SECT239K1,
+    SEC_OID_UNKNOWN, /* ECCurve_WTLS_1 */
+    SEC_OID_UNKNOWN, /* ECCurve_WTLS_8 */
+    SEC_OID_UNKNOWN, /* ECCurve_WTLS_9 */
+    SEC_OID_CURVE25519,
+    SEC_OID_UNKNOWN /* ECCurve_pastLastCurve */
+};
+
+SECStatus
+SECU_ecName2params(ECCurveName curve, SECItem *params)
+{
+    SECOidData *oidData = NULL;
+
+    if ((curve < ECCurve_noName) || (curve > ECCurve_pastLastCurve) ||
+        ((oidData = SECOID_FindOIDByTag(ecCurve_oid_map[curve])) == NULL)) {
+        PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
+        return SECFailure;
+    }
+
+    if (SECITEM_AllocItem(NULL, params, (2 + oidData->oid.len)) == NULL) {
+        return SECFailure;
+    }
+    /*
+     * params->data needs to contain the ASN encoding of an object ID (OID)
+     * representing the named curve. The actual OID is in
+     * oidData->oid.data so we simply prepend 0x06 and OID length
+     */
+    params->data[0] = SEC_ASN1_OBJECT_ID;
+    params->data[1] = oidData->oid.len;
+    memcpy(params->data + 2, oidData->oid.data, oidData->oid.len);
+
+    return SECSuccess;
+}
--- a/security/nss/cmd/lib/basicutil.h
+++ b/security/nss/cmd/lib/basicutil.h
@@ -8,16 +8,17 @@
 #include "secitem.h"
 #include "secoid.h"
 #include "secoidt.h"
 #include "secport.h"
 #include "prerror.h"
 #include "base64.h"
 #include "secasn1.h"
 #include "secder.h"
+#include "ecl-exp.h"
 #include <stdio.h>
 
 #ifdef SECUTIL_NEW
 typedef int (*SECU_PPFunc)(PRFileDesc *out, SECItem *item,
                            char *msg, int level);
 #else
 typedef int (*SECU_PPFunc)(FILE *out, SECItem *item, char *msg, int level);
 #endif
@@ -81,16 +82,18 @@ SECStatus
 SECU_SECItemHexStringToBinary(SECItem *srcdest);
 
 /*
 ** Read a hex string into a SecItem.
 */
 extern SECItem *SECU_HexString2SECItem(PLArenaPool *arena, SECItem *item,
                                        const char *str);
 
+extern SECStatus SECU_ecName2params(ECCurveName curve, SECItem *params);
+
 /*
  *
  *  Utilities for parsing security tools command lines
  *
  */
 
 /*  A single command flag  */
 typedef struct {
--- a/security/nss/cmd/lib/secutil.h
+++ b/security/nss/cmd/lib/secutil.h
@@ -13,16 +13,17 @@
 #include "secpkcs7.h"
 #include "secasn1.h"
 #include "secder.h"
 #include <stdio.h>
 
 #include "basicutil.h"
 #include "sslerr.h"
 #include "sslt.h"
+#include "blapi.h"
 
 #define SEC_CT_PRIVATE_KEY "private-key"
 #define SEC_CT_PUBLIC_KEY "public-key"
 #define SEC_CT_CERTIFICATE "certificate"
 #define SEC_CT_CERTIFICATE_REQUEST "certificate-request"
 #define SEC_CT_CERTIFICATE_ID "certificate-identity"
 #define SEC_CT_PKCS7 "pkcs7"
 #define SEC_CT_CRL "crl"
@@ -397,21 +398,16 @@ SECU_SECItemHexStringToBinary(SECItem *s
  * the caller must provide the desired default values for the min/max values,
  * by providing defaultVersionRange (which can be obtained from libssl by
  * calling SSL_VersionRangeGetSupported).
  */
 SECStatus
 SECU_ParseSSLVersionRangeString(const char *input,
                                 const SSLVersionRange defaultVersionRange,
                                 SSLVersionRange *vrange);
-/*
-** Read a hex string into a SecItem.
-*/
-extern SECItem *SECU_HexString2SECItem(PLArenaPool *arena, SECItem *item,
-                                       const char *str);
 
 SECStatus parseGroupList(const char *arg, SSLNamedGroup **enabledGroups,
                          unsigned int *enabledGroupsCount);
 SSLNamedGroup groupNameToNamedGroup(char *name);
 
 /*
  *
  *  Error messaging
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,8 +5,9 @@
 
 /*
  * 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/gtests/ssl_gtest/libssl_internals.c
+++ b/security/nss/gtests/ssl_gtest/libssl_internals.c
@@ -28,25 +28,18 @@ SECStatus SSLInt_IncrementClientHandshak
 SECStatus SSLInt_UpdateSSLv2ClientRandom(PRFileDesc *fd, uint8_t *rnd,
                                          size_t rnd_len, uint8_t *msg,
                                          size_t msg_len) {
   sslSocket *ss = ssl_FindSocket(fd);
   if (!ss) {
     return SECFailure;
   }
 
-  SECStatus rv = ssl3_InitState(ss);
-  if (rv != SECSuccess) {
-    return rv;
-  }
-
-  rv = ssl3_RestartHandshakeHashes(ss);
-  if (rv != SECSuccess) {
-    return rv;
-  }
+  ssl3_InitState(ss);
+  ssl3_RestartHandshakeHashes(ss);
 
   // Ensure we don't overrun hs.client_random.
   rnd_len = PR_MIN(SSL3_RANDOM_LENGTH, rnd_len);
 
   // Zero the client_random struct.
   PORT_Memset(&ss->ssl3.hs.client_random, 0, SSL3_RANDOM_LENGTH);
 
   // Copy over the challenge bytes.
@@ -61,21 +54,21 @@ PRBool SSLInt_ExtensionNegotiated(PRFile
   sslSocket *ss = ssl_FindSocket(fd);
   return (PRBool)(ss && ssl3_ExtensionNegotiated(ss, ext));
 }
 
 void SSLInt_ClearSessionTicketKey() { ssl_ResetSessionTicketKeys(); }
 
 SECStatus SSLInt_SetMTU(PRFileDesc *fd, PRUint16 mtu) {
   sslSocket *ss = ssl_FindSocket(fd);
-  if (ss) {
-    ss->ssl3.mtu = mtu;
-    return SECSuccess;
+  if (!ss) {
+    return SECFailure;
   }
-  return SECFailure;
+  ss->ssl3.mtu = mtu;
+  return SECSuccess;
 }
 
 PRInt32 SSLInt_CountTls13CipherSpecs(PRFileDesc *fd) {
   PRCList *cur_p;
   PRInt32 ct = 0;
 
   sslSocket *ss = ssl_FindSocket(fd);
   if (!ss) {
@@ -189,17 +182,19 @@ SECStatus SSLInt_Set0RttAlpn(PRFileDesc 
   if (!ss) {
     return SECFailure;
   }
 
   ss->xtnData.nextProtoState = SSL_NEXT_PROTO_EARLY_VALUE;
   if (ss->xtnData.nextProto.data) {
     SECITEM_FreeItem(&ss->xtnData.nextProto, PR_FALSE);
   }
-  if (!SECITEM_AllocItem(NULL, &ss->xtnData.nextProto, len)) return SECFailure;
+  if (!SECITEM_AllocItem(NULL, &ss->xtnData.nextProto, len)) {
+    return SECFailure;
+  }
   PORT_Memcpy(ss->xtnData.nextProto.data, data, len);
 
   return SECSuccess;
 }
 
 PRBool SSLInt_HasCertWithAuthType(PRFileDesc *fd, SSLAuthType authType) {
   sslSocket *ss = ssl_FindSocket(fd);
   if (!ss) {
@@ -246,27 +241,29 @@ SECStatus SSLInt_AdvanceReadSeqNum(PRFil
   sslSocket *ss;
   ssl3CipherSpec *spec;
 
   ss = ssl_FindSocket(fd);
   if (!ss) {
     return SECFailure;
   }
   if (to >= (1ULL << 48)) {
+    PORT_SetError(SEC_ERROR_INVALID_ARGS);
     return SECFailure;
   }
   ssl_GetSpecWriteLock(ss);
   spec = ss->ssl3.crSpec;
   epoch = spec->read_seq_num >> 48;
   spec->read_seq_num = (epoch << 48) | to;
 
   /* For DTLS, we need to fix the record sequence number.  For this, we can just
    * scrub the entire structure on the assumption that the new sequence number
    * is far enough past the last received sequence number. */
   if (to <= spec->recvdRecords.right + DTLS_RECVD_RECORDS_WINDOW) {
+    PORT_SetError(SEC_ERROR_INVALID_ARGS);
     return SECFailure;
   }
   dtls_RecordSetRecvd(&spec->recvdRecords, to);
 
   ssl_ReleaseSpecWriteLock(ss);
   return SECSuccess;
 }
 
@@ -274,16 +271,17 @@ SECStatus SSLInt_AdvanceWriteSeqNum(PRFi
   PRUint64 epoch;
   sslSocket *ss;
 
   ss = ssl_FindSocket(fd);
   if (!ss) {
     return SECFailure;
   }
   if (to >= (1ULL << 48)) {
+    PORT_SetError(SEC_ERROR_INVALID_ARGS);
     return SECFailure;
   }
   ssl_GetSpecWriteLock(ss);
   epoch = ss->ssl3.cwSpec->write_seq_num >> 48;
   ss->ssl3.cwSpec->write_seq_num = (epoch << 48) | to;
   ssl_ReleaseSpecWriteLock(ss);
   return SECSuccess;
 }
@@ -359,15 +357,41 @@ SECStatus SSLInt_UsingShortHeaders(PRFil
   sslSocket *ss;
 
   ss = ssl_FindSocket(fd);
   if (!ss) {
     return SECFailure;
   }
 
   *result = ss->ssl3.hs.shortHeaders;
-
   return SECSuccess;
 }
 
 void SSLInt_SetTicketLifetime(uint32_t lifetime) {
   ssl_ticket_lifetime = lifetime;
 }
+
+void SSLInt_SetMaxEarlyDataSize(uint32_t size) {
+  ssl_max_early_data_size = size;
+}
+
+SECStatus SSLInt_SetSocketMaxEarlyDataSize(PRFileDesc *fd, uint32_t size) {
+  sslSocket *ss;
+
+  ss = ssl_FindSocket(fd);
+  if (!ss) {
+    return SECFailure;
+  }
+
+  /* This only works when resuming. */
+  if (!ss->statelessResume) {
+    PORT_SetError(SEC_INTERNAL_ONLY);
+    return SECFailure;
+  }
+
+  /* Modifying both specs allows this to be used on either peer. */
+  ssl_GetSpecWriteLock(ss);
+  ss->ssl3.crSpec->earlyDataRemaining = size;
+  ss->ssl3.cwSpec->earlyDataRemaining = size;
+  ssl_ReleaseSpecWriteLock(ss);
+
+  return SECSuccess;
+}
--- a/security/nss/gtests/ssl_gtest/libssl_internals.h
+++ b/security/nss/gtests/ssl_gtest/libssl_internals.h
@@ -45,10 +45,12 @@ SECStatus SSLInt_SetCipherSpecChangeFunc
                                          void *arg);
 PK11SymKey *SSLInt_CipherSpecToKey(PRBool isServer, ssl3CipherSpec *spec);
 SSLCipherAlgorithm SSLInt_CipherSpecToAlgorithm(PRBool isServer,
                                                 ssl3CipherSpec *spec);
 unsigned char *SSLInt_CipherSpecToIv(PRBool isServer, ssl3CipherSpec *spec);
 SECStatus SSLInt_EnableShortHeaders(PRFileDesc *fd);
 SECStatus SSLInt_UsingShortHeaders(PRFileDesc *fd, PRBool *result);
 void SSLInt_SetTicketLifetime(uint32_t lifetime);
+void SSLInt_SetMaxEarlyDataSize(uint32_t size);
+SECStatus SSLInt_SetSocketMaxEarlyDataSize(PRFileDesc *fd, uint32_t size);
 
 #endif  // ndef libssl_internals_h_
--- a/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_0rtt_unittest.cc
@@ -277,9 +277,115 @@ TEST_P(TlsConnectTls13, TestTls13ZeroRtt
   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);
   }
 }
 
+static void CheckEarlyDataLimit(const std::shared_ptr<TlsAgent>& agent,
+                                size_t expected_size) {
+  SSLPreliminaryChannelInfo preinfo;
+  SECStatus rv =
+      SSL_GetPreliminaryChannelInfo(agent->ssl_fd(), &preinfo, sizeof(preinfo));
+  EXPECT_EQ(SECSuccess, rv);
+  EXPECT_EQ(expected_size, static_cast<size_t>(preinfo.maxEarlyDataSize));
+}
+
+TEST_P(TlsConnectTls13, SendTooMuchEarlyData) {
+  const char* big_message = "0123456789abcdef";
+  const size_t short_size = strlen(big_message) - 1;
+  const PRInt32 short_length = static_cast<PRInt32>(short_size);
+  SSLInt_SetMaxEarlyDataSize(static_cast<PRUint32>(short_size));
+  SetupForZeroRtt();
+
+  client_->Set0RttEnabled(true);
+  server_->Set0RttEnabled(true);
+  ExpectResumption(RESUME_TICKET);
+  client_->SetExpectedAlertSentCount(1);
+  server_->SetExpectedAlertReceivedCount(1);
+
+  client_->Handshake();
+  CheckEarlyDataLimit(client_, short_size);
+
+  PRInt32 sent;
+  // Writing more than the limit will succeed in TLS, but fail in DTLS.
+  if (mode_ == STREAM) {
+    sent = PR_Write(client_->ssl_fd(), big_message,
+                    static_cast<PRInt32>(strlen(big_message)));
+  } else {
+    sent = PR_Write(client_->ssl_fd(), big_message,
+                    static_cast<PRInt32>(strlen(big_message)));
+    EXPECT_GE(0, sent);
+    EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
+
+    // Try an exact-sized write now.
+    sent = PR_Write(client_->ssl_fd(), big_message, short_length);
+  }
+  EXPECT_EQ(short_length, sent);
+
+  // Even a single octet write should now fail.
+  sent = PR_Write(client_->ssl_fd(), big_message, 1);
+  EXPECT_GE(0, sent);
+  EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
+
+  // Process the ClientHello and read 0-RTT.
+  server_->Handshake();
+  CheckEarlyDataLimit(server_, short_size);
+
+  std::vector<uint8_t> buf(short_size + 1);
+  PRInt32 read = PR_Read(server_->ssl_fd(), buf.data(), buf.capacity());
+  EXPECT_EQ(short_length, read);
+  EXPECT_EQ(0, memcmp(big_message, buf.data(), short_size));
+
+  // Second read fails.
+  read = PR_Read(server_->ssl_fd(), buf.data(), buf.capacity());
+  EXPECT_EQ(SECFailure, read);
+  EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
+
+  Handshake();
+  ExpectEarlyDataAccepted(true);
+  CheckConnected();
+  SendReceive();
+}
+
+TEST_P(TlsConnectTls13, ReceiveTooMuchEarlyData) {
+  const size_t limit = 5;
+  SSLInt_SetMaxEarlyDataSize(limit);
+  SetupForZeroRtt();
+
+  client_->Set0RttEnabled(true);
+  server_->Set0RttEnabled(true);
+  ExpectResumption(RESUME_TICKET);
+
+  client_->Handshake();  // Send ClientHello
+  CheckEarlyDataLimit(client_, limit);
+
+  // Lift the limit on the client.
+  EXPECT_EQ(SECSuccess,
+            SSLInt_SetSocketMaxEarlyDataSize(client_->ssl_fd(), 1000));
+
+  // Send message
+  const char* message = "0123456789abcdef";
+  const PRInt32 message_len = static_cast<PRInt32>(strlen(message));
+  EXPECT_EQ(message_len, PR_Write(client_->ssl_fd(), message, message_len));
+
+  server_->Handshake();  // Process ClientHello, send server flight.
+  server_->Handshake();  // Just to make sure that we don't read ahead.
+  CheckEarlyDataLimit(server_, limit);
+
+  // Attempt to read early data.
+  std::vector<uint8_t> buf(strlen(message) + 1);
+  EXPECT_GT(0, PR_Read(server_->ssl_fd(), buf.data(), buf.capacity()));
+  if (mode_ == STREAM) {
+    // This error isn't fatal for DTLS.
+    server_->CheckErrorCode(SSL_ERROR_TOO_MUCH_EARLY_DATA);
+  }
+
+  client_->Handshake();  // Process the handshake.
+  client_->Handshake();  // Process the alert.
+  if (mode_ == STREAM) {
+    client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
+  }
+}
+
 }  // namespace nss_test
--- a/security/nss/gtests/ssl_gtest/tls_connect.cc
+++ b/security/nss/gtests/ssl_gtest/tls_connect.cc
@@ -170,16 +170,17 @@ void TlsConnectTestBase::ClearServerCach
   SSLInt_ClearSessionTicketKey();
   SSL_ConfigServerSessionIDCache(1024, 0, 0, g_working_dir_path.c_str());
 }
 
 void TlsConnectTestBase::SetUp() {
   SSL_ConfigServerSessionIDCache(1024, 0, 0, g_working_dir_path.c_str());
   SSLInt_ClearSessionTicketKey();
   SSLInt_SetTicketLifetime(30);
+  SSLInt_SetMaxEarlyDataSize(1024);
   ClearStats();
   Init();
 }
 
 void TlsConnectTestBase::TearDown() {
   client_ = nullptr;
   server_ = nullptr;
 
--- a/security/nss/lib/freebl/ecdecode.c
+++ b/security/nss/lib/freebl/ecdecode.c
@@ -17,67 +17,16 @@
 
 #define CHECK_OK(func) \
     if (func == NULL)  \
     goto cleanup
 #define CHECK_SEC_OK(func)         \
     if (SECSuccess != (rv = func)) \
     goto cleanup
 
-/*
- * Initializes a SECItem from a hexadecimal string
- *
- * Warning: This function ignores leading 00's, so any leading 00's
- * in the hexadecimal string must be optional.
- */
-static SECItem *
-hexString2SECItem(PLArenaPool *arena, SECItem *item, const char *str)
-{
-    int i = 0;
-    int byteval = 0;
-    int tmp = PORT_Strlen(str);
-
-    PORT_Assert(arena);
-    PORT_Assert(item);
-
-    if ((tmp % 2) != 0)
-        return NULL;
-
-    /* skip leading 00's unless the hex string is "00" */
-    while ((tmp > 2) && (str[0] == '0') && (str[1] == '0')) {
-        str += 2;
-        tmp -= 2;
-    }
-
-    item->data = (unsigned char *)PORT_ArenaAlloc(arena, tmp / 2);
-    if (item->data == NULL)
-        return NULL;
-    item->len = tmp / 2;
-
-    while (str[i]) {
-        if ((str[i] >= '0') && (str[i] <= '9'))
-            tmp = str[i] - '0';
-        else if ((str[i] >= 'a') && (str[i] <= 'f'))
-            tmp = str[i] - 'a' + 10;
-        else if ((str[i] >= 'A') && (str[i] <= 'F'))
-            tmp = str[i] - 'A' + 10;
-        else
-            return NULL;
-
-        byteval = byteval * 16 + tmp;
-        if ((i % 2) != 0) {
-            item->data[i / 2] = byteval;
-            byteval = 0;
-        }
-        i++;
-    }
-
-    return item;
-}
-
 /* Copy all of the fields from srcParams into dstParams
  */
 SECStatus
 EC_CopyParams(PLArenaPool *arena, ECParams *dstParams,
               const ECParams *srcParams)
 {
     SECStatus rv = SECFailure;
 
@@ -115,50 +64,41 @@ EC_CopyParams(PLArenaPool *arena, ECPara
 
     return SECSuccess;
 
 cleanup:
     return SECFailure;
 }
 
 static SECStatus
-gf_populate_params(ECCurveName name, ECFieldType field_type, ECParams *params)
+gf_populate_params_bytes(ECCurveName name, ECFieldType field_type, ECParams *params)
 {
     SECStatus rv = SECFailure;
-    const ECCurveParams *curveParams;
-    /* 2 ['0'+'4'] + MAX_ECKEY_LEN * 2 [x,y] * 2 [hex string] + 1 ['\0'] */
-    char genenc[3 + 2 * 2 * MAX_ECKEY_LEN];
+    const ECCurveBytes *curveParams;
 
     if ((name < ECCurve_noName) || (name > ECCurve_pastLastCurve))
         goto cleanup;
     params->name = name;
     curveParams = ecCurve_map[params->name];
     CHECK_OK(curveParams);
     params->fieldID.size = curveParams->size;
     params->fieldID.type = field_type;
-    if (field_type == ec_field_GFp ||
-        field_type == ec_field_plain) {
-        CHECK_OK(hexString2SECItem(params->arena, &params->fieldID.u.prime,
-                                   curveParams->irr));
-    } else {
-        CHECK_OK(hexString2SECItem(params->arena, &params->fieldID.u.poly,
-                                   curveParams->irr));
+    if (field_type != ec_field_GFp && field_type != ec_field_plain) {
+        return SECFailure;
     }
-    CHECK_OK(hexString2SECItem(params->arena, &params->curve.a,
-                               curveParams->curvea));
-    CHECK_OK(hexString2SECItem(params->arena, &params->curve.b,
-                               curveParams->curveb));
-    genenc[0] = '0';
-    genenc[1] = '4';
-    genenc[2] = '\0';
-    strcat(genenc, curveParams->genx);
-    strcat(genenc, curveParams->geny);
-    CHECK_OK(hexString2SECItem(params->arena, &params->base, genenc));
-    CHECK_OK(hexString2SECItem(params->arena, &params->order,
-                               curveParams->order));
+    params->fieldID.u.prime.len = curveParams->scalarSize;
+    params->fieldID.u.prime.data = (unsigned char *)curveParams->irr;
+    params->curve.a.len = curveParams->scalarSize;
+    params->curve.a.data = (unsigned char *)curveParams->curvea;
+    params->curve.b.len = curveParams->scalarSize;
+    params->curve.b.data = (unsigned char *)curveParams->curveb;
+    params->base.len = curveParams->pointSize;
+    params->base.data = (unsigned char *)curveParams->base;
+    params->order.len = curveParams->scalarSize;
+    params->order.data = (unsigned char *)curveParams->order;
     params->cofactor = curveParams->cofactor;
 
     rv = SECSuccess;
 
 cleanup:
     return rv;
 }
 
@@ -211,39 +151,40 @@ EC_FillParams(PLArenaPool *arena, const 
     printf("Curve: %s\n", SECOID_FindOIDTagDescription(tag));
 #endif
 
     switch (tag) {
         case SEC_OID_ANSIX962_EC_PRIME256V1:
             /* Populate params for prime256v1 aka secp256r1
              * (the NIST P-256 curve)
              */
-            CHECK_SEC_OK(gf_populate_params(ECCurve_X9_62_PRIME_256V1, ec_field_GFp,
-                                            params));
+            CHECK_SEC_OK(gf_populate_params_bytes(ECCurve_X9_62_PRIME_256V1,
+                                                  ec_field_GFp, params));
             break;
 
         case SEC_OID_SECG_EC_SECP384R1:
             /* Populate params for secp384r1
              * (the NIST P-384 curve)
              */
-            CHECK_SEC_OK(gf_populate_params(ECCurve_SECG_PRIME_384R1, ec_field_GFp,
-                                            params));
+            CHECK_SEC_OK(gf_populate_params_bytes(ECCurve_SECG_PRIME_384R1,
+                                                  ec_field_GFp, params));
             break;
 
         case SEC_OID_SECG_EC_SECP521R1:
             /* Populate params for secp521r1
              * (the NIST P-521 curve)
              */
-            CHECK_SEC_OK(gf_populate_params(ECCurve_SECG_PRIME_521R1, ec_field_GFp,
-                                            params));
+            CHECK_SEC_OK(gf_populate_params_bytes(ECCurve_SECG_PRIME_521R1,
+                                                  ec_field_GFp, params));
             break;
 
         case SEC_OID_CURVE25519:
             /* Populate params for Curve25519 */
-            CHECK_SEC_OK(gf_populate_params(ECCurve25519, ec_field_plain, params));
+            CHECK_SEC_OK(gf_populate_params_bytes(ECCurve25519, ec_field_plain,
+                                                  params));
             break;
 
         default:
             break;
     };
 
 cleanup:
     if (!params->cofactor) {
@@ -291,21 +232,25 @@ EC_DecodeParams(const SECItem *encodedPa
         return SECSuccess;
     }
 }
 
 int
 EC_GetPointSize(const ECParams *params)
 {
     ECCurveName name = params->name;
-    const ECCurveParams *curveParams;
+    const ECCurveBytes *curveParams;
 
     if ((name < ECCurve_noName) || (name > ECCurve_pastLastCurve) ||
         ((curveParams = ecCurve_map[name]) == NULL)) {
-        /* unknown curve, calculate point size from params. assume standard curves with 2 points 
+        /* unknown curve, calculate point size from params. assume standard curves with 2 points
          * and a point compression indicator byte */
         int sizeInBytes = (params->fieldID.size + 7) / 8;
         return sizeInBytes * 2 + 1;
     }
-    return curveParams->pointSize;
+    if (name == ECCurve25519) {
+        /* Only X here */
+        return curveParams->scalarSize;
+    }
+    return curveParams->pointSize - 1;
 }
 
 #endif /* NSS_DISABLE_ECC */
--- a/security/nss/lib/freebl/ecl/ecl-curve.h
+++ b/security/nss/lib/freebl/ecl/ecl-curve.h
@@ -1,68 +1,218 @@
 /* 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 "ecl-exp.h"
+#include "eclt.h"
 #include <stdlib.h>
 
 #ifndef __ecl_curve_h_
 #define __ecl_curve_h_
 
 /* copied from certt.h */
 #define KU_DIGITAL_SIGNATURE (0x80) /* bit 0 */
 #define KU_KEY_AGREEMENT (0x08)     /* bit 4 */
 
-static const ECCurveParams ecCurve_NIST_P256 = {
+static const PRUint8 irr256[32] =
+    { 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+static const PRUint8 a256[32] =
+    { 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC };
+static const PRUint8 b256[32] =
+    { 0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 0xB3, 0xEB, 0xBD, 0x55,
+      0x76, 0x98, 0x86, 0xBC, 0x65, 0x1D, 0x06, 0xB0, 0xCC, 0x53, 0xB0, 0xF6,
+      0x3B, 0xCE, 0x3C, 0x3E, 0x27, 0xD2, 0x60, 0x4B };
+static const PRUint8 x256[32] =
+    { 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6, 0xE5,
+      0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0,
+      0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2, 0x96 };
+static const PRUint8 y256[32] =
+    { 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB, 0x4A,
+      0x7C, 0x0F, 0x9E, 0x16, 0x2B, 0xCE, 0x33, 0x57, 0x6B, 0x31, 0x5E, 0xCE,
+      0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 0xF5 };
+static const PRUint8 order256[32] =
+    { 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
+      0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51 };
+static const PRUint8 base256[66] =
+    { 0x04, 0x00,
+      0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6, 0xE5,
+      0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0,
+      0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2, 0x96,
+      0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB, 0x4A,
+      0x7C, 0x0F, 0x9E, 0x16, 0x2B, 0xCE, 0x33, 0x57, 0x6B, 0x31, 0x5E, 0xCE,
+      0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 0xF5 };
+
+static const ECCurveBytes ecCurve_NIST_P256 = {
     "NIST-P256", ECField_GFp, 256,
-    "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF",
-    "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC",
-    "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B",
-    "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296",
-    "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5",
-    "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551",
-    1, 128, 65, KU_DIGITAL_SIGNATURE | KU_KEY_AGREEMENT
+    irr256, a256, b256, x256, y256, order256, base256,
+    1, 128, 66, 32,
+    KU_DIGITAL_SIGNATURE | KU_KEY_AGREEMENT
+};
+
+static const PRUint8 irr384[48] =
+    { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF };
+static const PRUint8 a384[48] =
+    { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFC };
+static const PRUint8 b384[48] =
+    { 0xB3, 0x31, 0x2F, 0xA7, 0xE2, 0x3E, 0xE7, 0xE4, 0x98, 0x8E, 0x05, 0x6B,
+      0xE3, 0xF8, 0x2D, 0x19, 0x18, 0x1D, 0x9C, 0x6E, 0xFE, 0x81, 0x41, 0x12,
+      0x03, 0x14, 0x08, 0x8F, 0x50, 0x13, 0x87, 0x5A, 0xC6, 0x56, 0x39, 0x8D,
+      0x8A, 0x2E, 0xD1, 0x9D, 0x2A, 0x85, 0xC8, 0xED, 0xD3, 0xEC, 0x2A, 0xEF };
+static const PRUint8 x384[48] =
+    { 0xAA, 0x87, 0xCA, 0x22, 0xBE, 0x8B, 0x05, 0x37, 0x8E, 0xB1, 0xC7, 0x1E,
+      0xF3, 0x20, 0xAD, 0x74, 0x6E, 0x1D, 0x3B, 0x62, 0x8B, 0xA7, 0x9B, 0x98,
+      0x59, 0xF7, 0x41, 0xE0, 0x82, 0x54, 0x2A, 0x38, 0x55, 0x02, 0xF2, 0x5D,
+      0xBF, 0x55, 0x29, 0x6C, 0x3A, 0x54, 0x5E, 0x38, 0x72, 0x76, 0x0A, 0xB7 };
+static const PRUint8 y384[48] =
+    { 0x36, 0x17, 0xDE, 0x4A, 0x96, 0x26, 0x2C, 0x6F, 0x5D, 0x9E, 0x98, 0xBF,
+      0x92, 0x92, 0xDC, 0x29, 0xF8, 0xF4, 0x1D, 0xBD, 0x28, 0x9A, 0x14, 0x7C,
+      0xE9, 0xDA, 0x31, 0x13, 0xB5, 0xF0, 0xB8, 0xC0, 0x0A, 0x60, 0xB1, 0xCE,
+      0x1D, 0x7E, 0x81, 0x9D, 0x7A, 0x43, 0x1D, 0x7C, 0x90, 0xEA, 0x0E, 0x5F };
+static const PRUint8 order384[48] =
+    { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xC7, 0x63, 0x4D, 0x81, 0xF4, 0x37, 0x2D, 0xDF, 0x58, 0x1A, 0x0D, 0xB2,
+      0x48, 0xB0, 0xA7, 0x7A, 0xEC, 0xEC, 0x19, 0x6A, 0xCC, 0xC5, 0x29, 0x73 };
+static const PRUint8 base384[98] =
+    { 0x04, 0x00,
+      0xAA, 0x87, 0xCA, 0x22, 0xBE, 0x8B, 0x05, 0x37, 0x8E, 0xB1, 0xC7, 0x1E,
+      0xF3, 0x20, 0xAD, 0x74, 0x6E, 0x1D, 0x3B, 0x62, 0x8B, 0xA7, 0x9B, 0x98,
+      0x59, 0xF7, 0x41, 0xE0, 0x82, 0x54, 0x2A, 0x38, 0x55, 0x02, 0xF2, 0x5D,
+      0xBF, 0x55, 0x29, 0x6C, 0x3A, 0x54, 0x5E, 0x38, 0x72, 0x76, 0x0A, 0xB7,
+      0x36, 0x17, 0xDE, 0x4A, 0x96, 0x26, 0x2C, 0x6F, 0x5D, 0x9E, 0x98, 0xBF,
+      0x92, 0x92, 0xDC, 0x29, 0xF8, 0xF4, 0x1D, 0xBD, 0x28, 0x9A, 0x14, 0x7C,
+      0xE9, 0xDA, 0x31, 0x13, 0xB5, 0xF0, 0xB8, 0xC0, 0x0A, 0x60, 0xB1, 0xCE,
+      0x1D, 0x7E, 0x81, 0x9D, 0x7A, 0x43, 0x1D, 0x7C, 0x90, 0xEA, 0x0E, 0x5F };
+
+static const ECCurveBytes ecCurve_NIST_P384 = {
+    "NIST-P384", ECField_GFp, 384,
+    irr384, a384, b384, x384, y384, order384, base384,
+    1, 192, 98, 48,
+    KU_DIGITAL_SIGNATURE | KU_KEY_AGREEMENT
 };
 
-static const ECCurveParams ecCurve_NIST_P384 = {
-    "NIST-P384", ECField_GFp, 384,
-    "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF",
-    "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC",
-    "B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF",
-    "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7",
-    "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F",
-    "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973",
-    1, 192, 97, KU_DIGITAL_SIGNATURE | KU_KEY_AGREEMENT
+static const PRUint8 irr521[66] =
+    { 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+static const PRUint8 a521[66] =
+    { 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC };
+static const PRUint8 b521[66] =
+    { 0x00, 0x51, 0x95, 0x3E, 0xB9, 0x61, 0x8E, 0x1C, 0x9A, 0x1F, 0x92, 0x9A,
+      0x21, 0xA0, 0xB6, 0x85, 0x40, 0xEE, 0xA2, 0xDA, 0x72, 0x5B, 0x99, 0xB3,
+      0x15, 0xF3, 0xB8, 0xB4, 0x89, 0x91, 0x8E, 0xF1, 0x09, 0xE1, 0x56, 0x19,
+      0x39, 0x51, 0xEC, 0x7E, 0x93, 0x7B, 0x16, 0x52, 0xC0, 0xBD, 0x3B, 0xB1,
+      0xBF, 0x07, 0x35, 0x73, 0xDF, 0x88, 0x3D, 0x2C, 0x34, 0xF1, 0xEF, 0x45,
+      0x1F, 0xD4, 0x6B, 0x50, 0x3F, 0x00 };
+static const PRUint8 x521[66] =
+    { 0x00, 0xC6, 0x85, 0x8E, 0x06, 0xB7, 0x04, 0x04, 0xE9, 0xCD, 0x9E, 0x3E,
+      0xCB, 0x66, 0x23, 0x95, 0xB4, 0x42, 0x9C, 0x64, 0x81, 0x39, 0x05, 0x3F,
+      0xB5, 0x21, 0xF8, 0x28, 0xAF, 0x60, 0x6B, 0x4D, 0x3D, 0xBA, 0xA1, 0x4B,
+      0x5E, 0x77, 0xEF, 0xE7, 0x59, 0x28, 0xFE, 0x1D, 0xC1, 0x27, 0xA2, 0xFF,
+      0xA8, 0xDE, 0x33, 0x48, 0xB3, 0xC1, 0x85, 0x6A, 0x42, 0x9B, 0xF9, 0x7E,
+      0x7E, 0x31, 0xC2, 0xE5, 0xBD, 0x66 };
+static const PRUint8 y521[66] =
+    { 0x01, 0x18, 0x39, 0x29, 0x6A, 0x78, 0x9A, 0x3B, 0xC0, 0x04, 0x5C, 0x8A,
+      0x5F, 0xB4, 0x2C, 0x7D, 0x1B, 0xD9, 0x98, 0xF5, 0x44, 0x49, 0x57, 0x9B,
+      0x44, 0x68, 0x17, 0xAF, 0xBD, 0x17, 0x27, 0x3E, 0x66, 0x2C, 0x97, 0xEE,
+      0x72, 0x99, 0x5E, 0xF4, 0x26, 0x40, 0xC5, 0x50, 0xB9, 0x01, 0x3F, 0xAD,
+      0x07, 0x61, 0x35, 0x3C, 0x70, 0x86, 0xA2, 0x72, 0xC2, 0x40, 0x88, 0xBE,
+      0x94, 0x76, 0x9F, 0xD1, 0x66, 0x50 };
+static const PRUint8 order521[66] =
+    { 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFA, 0x51, 0x86,
+      0x87, 0x83, 0xBF, 0x2F, 0x96, 0x6B, 0x7F, 0xCC, 0x01, 0x48, 0xF7, 0x09,
+      0xA5, 0xD0, 0x3B, 0xB5, 0xC9, 0xB8, 0x89, 0x9C, 0x47, 0xAE, 0xBB, 0x6F,
+      0xB7, 0x1E, 0x91, 0x38, 0x64, 0x09 };
+static const PRUint8 base521[134] =
+    {
+      0x04, 0x00,
+      0x00, 0xC6, 0x85, 0x8E, 0x06, 0xB7, 0x04, 0x04, 0xE9, 0xCD, 0x9E, 0x3E,
+      0xCB, 0x66, 0x23, 0x95, 0xB4, 0x42, 0x9C, 0x64, 0x81, 0x39, 0x05, 0x3F,
+      0xB5, 0x21, 0xF8, 0x28, 0xAF, 0x60, 0x6B, 0x4D, 0x3D, 0xBA, 0xA1, 0x4B,
+      0x5E, 0x77, 0xEF, 0xE7, 0x59, 0x28, 0xFE, 0x1D, 0xC1, 0x27, 0xA2, 0xFF,
+      0xA8, 0xDE, 0x33, 0x48, 0xB3, 0xC1, 0x85, 0x6A, 0x42, 0x9B, 0xF9, 0x7E,
+      0x7E, 0x31, 0xC2, 0xE5, 0xBD, 0x66,
+      0x01, 0x18, 0x39, 0x29, 0x6A, 0x78, 0x9A, 0x3B, 0xC0, 0x04, 0x5C, 0x8A,
+      0x5F, 0xB4, 0x2C, 0x7D, 0x1B, 0xD9, 0x98, 0xF5, 0x44, 0x49, 0x57, 0x9B,
+      0x44, 0x68, 0x17, 0xAF, 0xBD, 0x17, 0x27, 0x3E, 0x66, 0x2C, 0x97, 0xEE,
+      0x72, 0x99, 0x5E, 0xF4, 0x26, 0x40, 0xC5, 0x50, 0xB9, 0x01, 0x3F, 0xAD,
+      0x07, 0x61, 0x35, 0x3C, 0x70, 0x86, 0xA2, 0x72, 0xC2, 0x40, 0x88, 0xBE,
+      0x94, 0x76, 0x9F, 0xD1, 0x66, 0x50
+    };
+
+static const ECCurveBytes ecCurve_NIST_P521 = {
+    "NIST-P521", ECField_GFp, 521,
+    irr521, a521, b521, x521, y521, order521, base521,
+    1, 256, 134, 66,
+    KU_DIGITAL_SIGNATURE | KU_KEY_AGREEMENT
 };
 
-static const ECCurveParams ecCurve_NIST_P521 = {
-    "NIST-P521", ECField_GFp, 521,
-    "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
-    "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC",
-    "0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00",
-    "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66",
-    "011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650",
-    "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409",
-    1, 256, 133, KU_DIGITAL_SIGNATURE | KU_KEY_AGREEMENT
-};
+static const PRUint8 irr25519[32] =
+    { 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f };
+static const PRUint8 a25519[32] =
+    { 0x06, 0x6d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+static const PRUint8 b25519[32] =
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+static const PRUint8 x25519[32] =
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09 };
+static const PRUint8 y25519[32] =
+    { 0xd9, 0xd3, 0xce, 0x7e, 0xa2, 0xc5, 0xe9, 0x29, 0xb2, 0x61, 0x7c, 0x6d,
+      0x7e, 0x4d, 0x3d, 0x92, 0x4c, 0xd1, 0x48, 0x77, 0x2c, 0xdd, 0x1e, 0xe0,
+      0xb4, 0x86, 0xa0, 0xb8, 0xa1, 0x19, 0xae, 0x20 };
+static const PRUint8 order25519[32] =
+    { 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2,
+      0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 };
+static const PRUint8 base25519[66] =
+    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
+      0xd9, 0xd3, 0xce, 0x7e, 0xa2, 0xc5, 0xe9, 0x29, 0xb2, 0x61, 0x7c, 0x6d,
+      0x7e, 0x4d, 0x3d, 0x92, 0x4c, 0xd1, 0x48, 0x77, 0x2c, 0xdd, 0x1e, 0xe0,
+      0xb4, 0x86, 0xa0, 0xb8, 0xa1, 0x19, 0xae, 0x20, 0x00, 0x04 };
 
-static const ECCurveParams ecCurve25519 = {
+static const ECCurveBytes ecCurve_25519 = {
     "Curve25519", ECField_GFp, 255,
-    "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed",
-    "076D06",
-    "00",
-    "0900000000000000000000000000000000000000000000000000000000000000",
-    "20AE19A1B8A086B4E01EDD2C7748D14C923D4D7E6D7C61B229E9C5A27ECED3D9",
-    "1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed",
-    8, 128, 32, KU_KEY_AGREEMENT
+    irr25519, a25519, b25519, x25519, y25519, order25519, base25519,
+    8, 128, 66, 32,
+    KU_KEY_AGREEMENT
 };
 
 /* mapping between ECCurveName enum and pointers to ECCurveParams */
-static const ECCurveParams *ecCurve_map[] = {
+static const ECCurveBytes *ecCurve_map[] = {
     NULL,               /* ECCurve_noName */
     NULL,               /* ECCurve_NIST_P192 */
     NULL,               /* ECCurve_NIST_P224 */
     &ecCurve_NIST_P256, /* ECCurve_NIST_P256 */
     &ecCurve_NIST_P384, /* ECCurve_NIST_P384 */
     &ecCurve_NIST_P521, /* ECCurve_NIST_P521 */
     NULL,               /* ECCurve_NIST_K163 */
     NULL,               /* ECCurve_NIST_B163 */
@@ -111,13 +261,13 @@ static const ECCurveParams *ecCurve_map[
     NULL,               /* ECCurve_SECG_CHAR2_131R2 */
     NULL,               /* ECCurve_SECG_CHAR2_163R1 */
     NULL,               /* ECCurve_SECG_CHAR2_193R1 */
     NULL,               /* ECCurve_SECG_CHAR2_193R2 */
     NULL,               /* ECCurve_SECG_CHAR2_239K1 */
     NULL,               /* ECCurve_WTLS_1 */
     NULL,               /* ECCurve_WTLS_8 */
     NULL,               /* ECCurve_WTLS_9 */
-    &ecCurve25519,      /* ECCurve25519 */
+    &ecCurve_25519,     /* ECCurve25519 */
     NULL                /* ECCurve_pastLastCurve */
 };
 
 #endif
--- a/security/nss/lib/freebl/ecl/ecl-priv.h
+++ b/security/nss/lib/freebl/ecl/ecl-priv.h
@@ -241,17 +241,10 @@ mp_err ec_group_set_gfp384(ECGroup *grou
 mp_err ec_group_set_gfp521(ECGroup *group, ECCurveName);
 mp_err ec_group_set_gf2m163(ECGroup *group, ECCurveName name);
 mp_err ec_group_set_gf2m193(ECGroup *group, ECCurveName name);
 mp_err ec_group_set_gf2m233(ECGroup *group, ECCurveName name);
 
 /* Optimized point multiplication */
 mp_err ec_group_set_gfp256_32(ECGroup *group, ECCurveName name);
 
-/* Optimized floating-point arithmetic */
-#ifdef ECL_USE_FP
-mp_err ec_group_set_secp160r1_fp(ECGroup *group);
-mp_err ec_group_set_nistp192_fp(ECGroup *group);
-mp_err ec_group_set_nistp224_fp(ECGroup *group);
-#endif
-
 SECStatus ec_Curve25519_mul(PRUint8 *q, const PRUint8 *s, const PRUint8 *p);
 #endif /* __ecl_priv_h_ */
--- a/security/nss/lib/freebl/ecl/ecl.c
+++ b/security/nss/lib/freebl/ecl/ecl.c
@@ -1,17 +1,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#ifdef FREEBL_NO_DEPEND
+#include "../stubs.h"
+#endif
+
 #include "mpi.h"
 #include "mplogic.h"
 #include "ecl.h"
 #include "ecl-priv.h"
 #include "ecp.h"
+#include "ecl-curve.h"
 #include <stdlib.h>
 #include <string.h>
 
 /* Allocate memory for a new ECGroup object. */
 ECGroup *
 ECGroup_new()
 {
     mp_err res = MP_OKAY;
@@ -123,146 +128,171 @@ ECGroup_consGFp_mont(const mp_int *irr, 
 CLEANUP:
     if (res != MP_OKAY) {
         ECGroup_free(group);
         return NULL;
     }
     return group;
 }
 
-/* Construct ECGroup from hex parameters and name, if any. Called by
- * ECGroup_fromHex and ECGroup_fromName. */
+/* Construct an ECGroup. */
 ECGroup *
-ecgroup_fromNameAndHex(const ECCurveName name,
-                       const ECCurveParams *params)
+construct_ecgroup(const ECCurveName name, mp_int irr, mp_int curvea,
+                  mp_int curveb, mp_int genx, mp_int geny, mp_int order,
+                  int cofactor, ECField field, const char *text)
+{
+    int bits;
+    ECGroup *group = NULL;
+    mp_err res = MP_OKAY;
+
+    /* determine number of bits */
+    bits = mpl_significant_bits(&irr) - 1;
+    if (bits < MP_OKAY) {
+        res = bits;
+        goto CLEANUP;
+    }
+
+    /* determine which optimizations (if any) to use */
+    if (field == ECField_GFp) {
+        switch (name) {
+            case ECCurve_SECG_PRIME_256R1:
+                group =
+                    ECGroup_consGFp(&irr, &curvea, &curveb, &genx, &geny,
+                                    &order, cofactor);
+                if (group == NULL) {
+                    res = MP_UNDEF;
+                    goto CLEANUP;
+                }
+                MP_CHECKOK(ec_group_set_gfp256(group, name));
+                MP_CHECKOK(ec_group_set_gfp256_32(group, name));
+                break;
+            case ECCurve_SECG_PRIME_521R1:
+                group =
+                    ECGroup_consGFp(&irr, &curvea, &curveb, &genx, &geny,
+                                    &order, cofactor);
+                if (group == NULL) {
+                    res = MP_UNDEF;
+                    goto CLEANUP;
+                }
+                MP_CHECKOK(ec_group_set_gfp521(group, name));
+                break;
+            default:
+                /* use generic arithmetic */
+                group =
+                    ECGroup_consGFp_mont(&irr, &curvea, &curveb, &genx, &geny,
+                                         &order, cofactor);
+                if (group == NULL) {
+                    res = MP_UNDEF;
+                    goto CLEANUP;
+                }
+        }
+    } else {
+        res = MP_UNDEF;
+        goto CLEANUP;
+    }
+
+    /* set name, if any */
+    if ((group != NULL) && (text != NULL)) {
+        group->text = strdup(text);
+        if (group->text == NULL) {
+            res = MP_MEM;
+        }
+    }
+
+CLEANUP:
+    if (group && res != MP_OKAY) {
+        ECGroup_free(group);
+        return NULL;
+    }
+    return group;
+}
+
+/* Construct ECGroup from parameters and name, if any. */
+ECGroup *
+ecgroup_fromName(const ECCurveName name,
+                 const ECCurveBytes *params)
 {
     mp_int irr, curvea, curveb, genx, geny, order;
-    int bits;
     ECGroup *group = NULL;
     mp_err res = MP_OKAY;
 
     /* initialize values */
     MP_DIGITS(&irr) = 0;
     MP_DIGITS(&curvea) = 0;
     MP_DIGITS(&curveb) = 0;
     MP_DIGITS(&genx) = 0;
     MP_DIGITS(&geny) = 0;
     MP_DIGITS(&order) = 0;
     MP_CHECKOK(mp_init(&irr));
     MP_CHECKOK(mp_init(&curvea));
     MP_CHECKOK(mp_init(&curveb));
     MP_CHECKOK(mp_init(&genx));
     MP_CHECKOK(mp_init(&geny));
     MP_CHECKOK(mp_init(&order));
-    MP_CHECKOK(mp_read_radix(&irr, params->irr, 16));
-    MP_CHECKOK(mp_read_radix(&curvea, params->curvea, 16));
-    MP_CHECKOK(mp_read_radix(&curveb, params->curveb, 16));
-    MP_CHECKOK(mp_read_radix(&genx, params->genx, 16));
-    MP_CHECKOK(mp_read_radix(&geny, params->geny, 16));
-    MP_CHECKOK(mp_read_radix(&order, params->order, 16));
-
-    /* determine number of bits */
-    bits = mpl_significant_bits(&irr) - 1;
-    if (bits < MP_OKAY) {
-        res = bits;
-        goto CLEANUP;
-    }
+    MP_CHECKOK(mp_read_unsigned_octets(&irr, params->irr, params->scalarSize));
+    MP_CHECKOK(mp_read_unsigned_octets(&curvea, params->curvea, params->scalarSize));
+    MP_CHECKOK(mp_read_unsigned_octets(&curveb, params->curveb, params->scalarSize));
+    MP_CHECKOK(mp_read_unsigned_octets(&genx, params->genx, params->scalarSize));
+    MP_CHECKOK(mp_read_unsigned_octets(&geny, params->geny, params->scalarSize));
+    MP_CHECKOK(mp_read_unsigned_octets(&order, params->order, params->scalarSize));
 
-    /* determine which optimizations (if any) to use */
-    if (params->field == ECField_GFp) {
-        switch (name) {
-            case ECCurve_SECG_PRIME_256R1:
-                group =
-                    ECGroup_consGFp(&irr, &curvea, &curveb, &genx, &geny,
-                                    &order, params->cofactor);
-                if (group == NULL) {
-                    res = MP_UNDEF;
-                    goto CLEANUP;
-                }
-                MP_CHECKOK(ec_group_set_gfp256(group, name));
-                MP_CHECKOK(ec_group_set_gfp256_32(group, name));
-                break;
-            case ECCurve_SECG_PRIME_521R1:
-                group =
-                    ECGroup_consGFp(&irr, &curvea, &curveb, &genx, &geny,
-                                    &order, params->cofactor);
-                if (group == NULL) {
-                    res = MP_UNDEF;
-                    goto CLEANUP;
-                }
-                MP_CHECKOK(ec_group_set_gfp521(group, name));
-                break;
-            default:
-                /* use generic arithmetic */
-                group =
-                    ECGroup_consGFp_mont(&irr, &curvea, &curveb, &genx, &geny,
-                                         &order, params->cofactor);
-                if (group == NULL) {
-                    res = MP_UNDEF;
-                    goto CLEANUP;
-                }
-        }
-    } else {
-        res = MP_UNDEF;
-        goto CLEANUP;
-    }
-
-    /* set name, if any */
-    if ((group != NULL) && (params->text != NULL)) {
-        group->text = strdup(params->text);
-        if (group->text == NULL) {
-            res = MP_MEM;
-        }
-    }
+    group = construct_ecgroup(name, irr, curvea, curveb, genx, geny, order,
+                              params->cofactor, params->field, params->text);
 
 CLEANUP:
     mp_clear(&irr);
     mp_clear(&curvea);
     mp_clear(&curveb);
     mp_clear(&genx);
     mp_clear(&geny);
     mp_clear(&order);
-    if (res != MP_OKAY) {
+    if (group && res != MP_OKAY) {
         ECGroup_free(group);
         return NULL;
     }
     return group;
 }
 
-/* Construct ECGroup from hexadecimal representations of parameters. */
-ECGroup *
-ECGroup_fromHex(const ECCurveParams *params)
+/* Construct ECCurveBytes from an ECCurveName */
+const ECCurveBytes *
+ec_GetNamedCurveParams(const ECCurveName name)
 {
-    return ecgroup_fromNameAndHex(ECCurve_noName, params);
+    if ((name <= ECCurve_noName) || (ECCurve_pastLastCurve <= name) ||
+        (ecCurve_map[name] == NULL)) {
+        return NULL;
+    } else {
+        return ecCurve_map[name];
+    }
 }
 
 /* Construct ECGroup from named parameters. */
 ECGroup *
 ECGroup_fromName(const ECCurveName name)
 {
     ECGroup *group = NULL;
-    ECCurveParams *params = NULL;
+    const ECCurveBytes *params = NULL;
     mp_err res = MP_OKAY;
 
-    params = EC_GetNamedCurveParams(name);
+    /* This doesn't work with Curve25519 but it's not necessary to. */
+    PORT_Assert(name != ECCurve25519);
+
+    params = ec_GetNamedCurveParams(name);
     if (params == NULL) {
         res = MP_UNDEF;
         goto CLEANUP;
     }
 
     /* construct actual group */
-    group = ecgroup_fromNameAndHex(name, params);
+    group = ecgroup_fromName(name, params);
     if (group == NULL) {
         res = MP_UNDEF;
-        goto CLEANUP;
     }
 
 CLEANUP:
-    EC_FreeCurveParams(params);
-    if (res != MP_OKAY) {
+    if (group && res != MP_OKAY) {
         ECGroup_free(group);
         return NULL;
     }
     return group;
 }
 
 /* Validates an EC public key as described in Section 5.2.2 of X9.62. */
 mp_err
--- a/security/nss/lib/freebl/ecl/ecl.h
+++ b/security/nss/lib/freebl/ecl/ecl.h
@@ -6,38 +6,27 @@
  * curve point operations will need to include it. */
 
 #ifndef __ecl_h_
 #define __ecl_h_
 
 #include "blapi.h"
 #include "ecl-exp.h"
 #include "mpi.h"
+#include "eclt.h"
 
 struct ECGroupStr;
 typedef struct ECGroupStr ECGroup;
 
-/* Construct ECGroup from hexadecimal representations of parameters. */
-ECGroup *ECGroup_fromHex(const ECCurveParams *params);
-
 /* Construct ECGroup from named parameters. */
 ECGroup *ECGroup_fromName(const ECCurveName name);
 
 /* Free an allocated ECGroup. */
 void ECGroup_free(ECGroup *group);
 
-/* Construct ECCurveParams from an ECCurveName */
-ECCurveParams *EC_GetNamedCurveParams(const ECCurveName name);
-
-/* Duplicates an ECCurveParams */
-ECCurveParams *ECCurveParams_dup(const ECCurveParams *params);
-
-/* Free an allocated ECCurveParams */
-void EC_FreeCurveParams(ECCurveParams *params);
-
 /* Elliptic curve scalar-point multiplication. Computes Q(x, y) = k * P(x,
  * y).  If x, y = NULL, then P is assumed to be the generator (base point)
  * of the group of points on the elliptic curve. Input and output values
  * are assumed to be NOT field-encoded. */
 mp_err ECPoint_mul(const ECGroup *group, const mp_int *k, const mp_int *px,
                    const mp_int *py, mp_int *qx, mp_int *qy);
 
 /* Elliptic curve scalar-point multiplication. Computes Q(x, y) = k1 * G +
deleted file mode 100644
--- a/security/nss/lib/freebl/ecl/ecl_curve.c
+++ /dev/null
@@ -1,93 +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/. */
-
-#include "ecl.h"
-#include "ecl-curve.h"
-#include "ecl-priv.h"
-#include <stdlib.h>
-#include <string.h>
-
-#define CHECK(func)       \
-    if ((func) == NULL) { \
-        res = 0;          \
-        goto CLEANUP;     \
-    }
-
-/* Duplicates an ECCurveParams */
-ECCurveParams *
-ECCurveParams_dup(const ECCurveParams *params)
-{
-    int res = 1;
-    ECCurveParams *ret = NULL;
-
-    CHECK(ret = (ECCurveParams *)calloc(1, sizeof(ECCurveParams)));
-    if (params->text != NULL) {
-        CHECK(ret->text = strdup(params->text));
-    }
-    ret->field = params->field;
-    ret->size = params->size;
-    if (params->irr != NULL) {
-        CHECK(ret->irr = strdup(params->irr));
-    }
-    if (params->curvea != NULL) {
-        CHECK(ret->curvea = strdup(params->curvea));
-    }
-    if (params->curveb != NULL) {
-        CHECK(ret->curveb = strdup(params->curveb));
-    }
-    if (params->genx != NULL) {
-        CHECK(ret->genx = strdup(params->genx));
-    }
-    if (params->geny != NULL) {
-        CHECK(ret->geny = strdup(params->geny));
-    }
-    if (params->order != NULL) {
-        CHECK(ret->order = strdup(params->order));
-    }
-    ret->cofactor = params->cofactor;
-
-CLEANUP:
-    if (res != 1) {
-        EC_FreeCurveParams(ret);
-        return NULL;
-    }
-    return ret;
-}
-
-#undef CHECK
-
-/* Construct ECCurveParams from an ECCurveName */
-ECCurveParams *
-EC_GetNamedCurveParams(const ECCurveName name)
-{
-    if ((name <= ECCurve_noName) || (ECCurve_pastLastCurve <= name) ||
-        (ecCurve_map[name] == NULL)) {
-        return NULL;
-    } else {
-        return ECCurveParams_dup(ecCurve_map[name]);
-    }
-}
-
-/* Free the memory allocated (if any) to an ECCurveParams object. */
-void
-EC_FreeCurveParams(ECCurveParams *params)
-{
-    if (params == NULL)
-        return;
-    if (params->text != NULL)
-        free(params->text);
-    if (params->irr != NULL)
-        free(params->irr);
-    if (params->curvea != NULL)
-        free(params->curvea);
-    if (params->curveb != NULL)
-        free(params->curveb);
-    if (params->genx != NULL)
-        free(params->genx);
-    if (params->geny != NULL)
-        free(params->geny);
-    if (params->order != NULL)
-        free(params->order);
-    free(params);
-}
new file mode 100644
--- /dev/null
+++ b/security/nss/lib/freebl/ecl/eclt.h
@@ -0,0 +1,30 @@
+/* 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/. */
+
+/* This header holds ECC types and must not be exported publicly. */
+
+#ifndef __eclt_h_
+#define __eclt_h_
+
+/* byte encoding of curve parameters */
+struct ECCurveBytesStr {
+    char *text;
+    ECField field;
+    size_t size;
+    const PRUint8 *irr;
+    const PRUint8 *curvea;
+    const PRUint8 *curveb;
+    const PRUint8 *genx;
+    const PRUint8 *geny;
+    const PRUint8 *order;
+    const PRUint8 *base;
+    int cofactor;
+    int security;
+    size_t pointSize;
+    size_t scalarSize;
+    unsigned int usage;
+};
+typedef struct ECCurveBytesStr ECCurveBytes;
+
+#endif /* __ecl_h_ */
--- a/security/nss/lib/freebl/exports.gyp
+++ b/security/nss/lib/freebl/exports.gyp
@@ -28,16 +28,17 @@
         {
           'files': [
             'alghmac.h',
             'blapi.h',
             'chacha20poly1305.h',
             'ec.h',
             'ecl/ecl-curve.h',
             'ecl/ecl.h',
+            'ecl/eclt.h',
             'hmacct.h',
             'secmpi.h',
             'secrng.h'
           ],
           'destination': '<(nss_private_dist_dir)/<(module)'
         }
       ]
     }
--- a/security/nss/lib/freebl/freebl_base.gypi
+++ b/security/nss/lib/freebl/freebl_base.gypi
@@ -16,17 +16,16 @@
     'desblapi.c',
     'dh.c',
     'drbg.c',
     'dsa.c',
     'ec.c',
     'ecdecode.c',
     'ecl/ec_naf.c',
     'ecl/ecl.c',
-    'ecl/ecl_curve.c',
     'ecl/ecl_gf.c',
     'ecl/ecl_mult.c',
     'ecl/ecp_25519.c',
     'ecl/ecp_256.c',
     'ecl/ecp_256_32.c',
     'ecl/ecp_384.c',
     'ecl/ecp_521.c',
     'ecl/ecp_aff.c',
--- a/security/nss/lib/freebl/manifest.mn
+++ b/security/nss/lib/freebl/manifest.mn
@@ -89,25 +89,26 @@ PRIVATE_EXPORTS = \
 	blapi.h \
 	chacha20poly1305.h \
 	hmacct.h \
 	secmpi.h \
 	secrng.h \
 	ec.h \
 	ecl.h \
 	ecl-curve.h \
+	eclt.h \
 	$(NULL)
 
 MPI_HDRS = mpi-config.h mpi.h mpi-priv.h mplogic.h mpprime.h logtab.h mp_gf2m.h
 MPI_SRCS = mpprime.c mpmontg.c mplogic.c mpi.c mp_gf2m.c
 
 
 ECL_HDRS = ecl-exp.h ecl.h ecp.h ecl-priv.h
 ifndef NSS_DISABLE_ECC
-ECL_SRCS = ecl.c ecl_curve.c ecl_mult.c ecl_gf.c \
+ECL_SRCS = ecl.c ecl_mult.c ecl_gf.c \
 	ecp_aff.c ecp_jac.c ecp_mont.c \
 	ec_naf.c ecp_jm.c ecp_256.c ecp_384.c ecp_521.c \
 	ecp_256_32.c ecp_25519.c
 else
 ECL_SRCS = $(NULL)
 endif
 SHA_SRCS = sha_fast.c
 MPCPU_SRCS = mpcpucache.c
--- a/security/nss/lib/ssl/SSLerrs.h
+++ b/security/nss/lib/ssl/SSLerrs.h
@@ -503,8 +503,11 @@ ER3(SSL_ERROR_MISSING_SIGNATURE_ALGORITH
 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 PSK key exchange modes extension.")
 
 ER3(SSL_ERROR_DOWNGRADE_WITH_EARLY_DATA, (SSL_ERROR_BASE + 160),
     "SSL got a pre-TLS 1.3 version even though we sent early data.")
+
+ER3(SSL_ERROR_TOO_MUCH_EARLY_DATA, (SSL_ERROR_BASE + 161),
+    "SSL received more early data than permitted.")
--- a/security/nss/lib/ssl/ssl3con.c
+++ b/security/nss/lib/ssl/ssl3con.c
@@ -2721,20 +2721,17 @@ ssl3_SendRecord(sslSocket *ss,
     }
 
     if (ss->ssl3.initialized == PR_FALSE) {
         /* This can happen on a server if the very first incoming record
         ** looks like a defective ssl3 record (e.g. too long), and we're
         ** trying to send an alert.
         */
         PR_ASSERT(type == content_alert);
-        rv = ssl3_InitState(ss);
-        if (rv != SECSuccess) {
-            return SECFailure; /* ssl3_InitState has set the error code. */
-        }
+        ssl3_InitState(ss);
     }
 
     /* check for Token Presence */
     if (!ssl3_ClientAuthTokenPresent(ss->sec.ci.sid)) {
         PORT_SetError(SSL_ERROR_TOKEN_INSERTION_REMOVAL);
         return SECFailure;
     }
 
@@ -2930,16 +2927,17 @@ ssl3_SendApplicationData(sslSocket *ss, 
              * the middle of a large application data write.  (See
              * Bugzilla bug 127740, comment #1.)
              */
             ssl_ReleaseXmitBufLock(ss);
             PR_Sleep(PR_INTERVAL_NO_WAIT); /* PR_Yield(); */
             ssl_GetXmitBufLock(ss);
         }
         toSend = PR_MIN(len - totalSent, MAX_FRAGMENT_LENGTH);
+
         /*
          * Note that the 0 epoch is OK because flags will never require
          * its use, as guaranteed by the PORT_Assert above.
          */
         sent = ssl3_SendRecord(ss, NULL, content_application_data,
                                in + totalSent, toSend, flags);
         if (sent < 0) {
             if (totalSent > 0 && PR_GetError() == PR_WOULD_BLOCK_ERROR) {
@@ -4096,34 +4094,31 @@ ssl3_InitHandshakeHashes(sslSocket *ss)
             return SECFailure;
         }
         sslBuffer_Clear(&ss->ssl3.hs.messages);
     }
 
     return SECSuccess;
 }
 
-SECStatus
+void
 ssl3_RestartHandshakeHashes(sslSocket *ss)
 {
-    SECStatus rv = SECSuccess;
-
     SSL_TRC(30, ("%d: SSL3[%d]: reset handshake hashes",
                  SSL_GETPID(), ss->fd));
     ss->ssl3.hs.hashType = handshake_hash_unknown;
     ss->ssl3.hs.messages.len = 0;
     if (ss->ssl3.hs.md5) {
         PK11_DestroyContext(ss->ssl3.hs.md5, PR_TRUE);
         ss->ssl3.hs.md5 = NULL;
     }
     if (ss->ssl3.hs.sha) {
         PK11_DestroyContext(ss->ssl3.hs.sha, PR_TRUE);
         ss->ssl3.hs.sha = NULL;
     }
-    return rv;
 }
 
 /*
  * Handshake messages
  */
 /* Called from  ssl3_InitHandshakeHashes()
 **      ssl3_AppendHandshake()
 **      ssl3_HandleV2ClientHello()
@@ -5019,25 +5014,18 @@ ssl3_SendClientHello(sslSocket *ss, sslC
         return SECFailure;
     }
 
     /* If we are responding to a HelloRetryRequest, don't reinitialize. We need
      * to maintain the handshake hashes. */
     if (ss->ssl3.hs.helloRetry) {
         PORT_Assert(type == client_hello_retry);
     } else {
-        rv = ssl3_InitState(ss);
-        if (rv != SECSuccess) {
-            return rv; /* ssl3_InitState has set the error code. */
-        }
-
-        rv = ssl3_RestartHandshakeHashes(ss);
-        if (rv != SECSuccess) {
-            return rv;
-        }
+        ssl3_InitState(ss);
+        ssl3_RestartHandshakeHashes(ss);
     }
 
     /* These must be reset every handshake. */
     ss->ssl3.hs.sendingSCSV = PR_FALSE;
     ss->ssl3.hs.preliminaryInfo = 0;
     PORT_Assert(IS_DTLS(ss) || type != client_hello_retransmit);
     SECITEM_FreeItem(&ss->ssl3.hs.newSessionTicket.ticket, PR_FALSE);
     ss->ssl3.hs.receivedNewSessionTicket = PR_FALSE;
@@ -9159,26 +9147,18 @@ ssl3_HandleV2ClientHello(sslSocket *ss, 
 
     ssl3_ResetExtensionData(&ss->xtnData);
 
     version = (buffer[1] << 8) | buffer[2];
     if (version < SSL_LIBRARY_VERSION_3_0) {
         goto loser;
     }
 
-    rv = ssl3_InitState(ss);
-    if (rv != SECSuccess) {
-        ssl_ReleaseSSL3HandshakeLock(ss);
-        return rv; /* ssl3_InitState has set the error code. */
-    }
-    rv = ssl3_RestartHandshakeHashes(ss);
-    if (rv != SECSuccess) {
-        ssl_ReleaseSSL3HandshakeLock(ss);
-        return rv;
-    }
+    ssl3_InitState(ss);
+    ssl3_RestartHandshakeHashes(ss);
 
     if (ss->ssl3.hs.ws != wait_client_hello) {
         desc = unexpected_message;
         errCode = SSL_ERROR_RX_UNEXPECTED_CLIENT_HELLO;
         goto alert_loser;
     }
 
     total += suite_length = (buffer[3] << 8) | buffer[4];
@@ -11790,20 +11770,17 @@ ssl3_HandleHandshakeMessage(sslSocket *s
     hdr[0] = (PRUint8)ss->ssl3.hs.msg_type;
     hdr[1] = (PRUint8)(length >> 16);
     hdr[2] = (PRUint8)(length >> 8);
     hdr[3] = (PRUint8)(length);
 
     /* Start new handshake hashes when we start a new handshake.  Unless this is
      * TLS 1.3 and we sent a HelloRetryRequest. */
     if (ss->ssl3.hs.msg_type == client_hello && !ss->ssl3.hs.helloRetry) {
-        rv = ssl3_RestartHandshakeHashes(ss);
-        if (rv != SECSuccess) {
-            return rv;
-        }
+        ssl3_RestartHandshakeHashes(ss);
     }
     /* We should not include hello_request and hello_verify_request messages
      * in the handshake hashes */
     if ((ss->ssl3.hs.msg_type != hello_request) &&
         (ss->ssl3.hs.msg_type != hello_verify_request)) {
         rv = ssl3_UpdateHandshakeHashes(ss, (unsigned char *)hdr, 4);
         if (rv != SECSuccess)
             return rv; /* err code already set. */
@@ -12585,21 +12562,18 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Cip
     SSL3ContentType rType;
     sslBuffer *plaintext;
     sslBuffer temp_buf = { NULL, 0, 0 };
     SSL3AlertDescription alert = internal_error;
     PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
 
     if (!ss->ssl3.initialized) {
         ssl_GetSSL3HandshakeLock(ss);
-        rv = ssl3_InitState(ss);
+        ssl3_InitState(ss);
         ssl_ReleaseSSL3HandshakeLock(ss);
-        if (rv != SECSuccess) {
-            return rv; /* ssl3_InitState has set the error code. */
-        }
     }
 
     /* check for Token Presence */
     if (!ssl3_ClientAuthTokenPresent(ss->sec.ci.sid)) {
         PORT_SetError(SSL_ERROR_TOKEN_INSERTION_REMOVAL);
         return SECFailure;
     }
 
@@ -12908,26 +12882,24 @@ ssl3_InitCipherSpec(ssl3CipherSpec *spec
 }
 
 /* Called from: ssl3_SendRecord
 **      ssl3_SendClientHello()
 **      ssl3_HandleV2ClientHello()
 **      ssl3_HandleRecord()
 **
 ** This function should perhaps acquire and release the SpecWriteLock.
-**
-**
 */
-SECStatus
+void
 ssl3_InitState(sslSocket *ss)
 {
     PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
 
     if (ss->ssl3.initialized)
-        return SECSuccess; /* Function should be idempotent */
+        return; /* Function should be idempotent */
 
     ss->ssl3.policy = SSL_ALLOWED;
 
     ssl_InitSecState(&ss->sec);
 
     ssl_GetSpecWriteLock(ss);
     ss->ssl3.crSpec = ss->ssl3.cwSpec = &ss->ssl3.specs[0];
     ss->ssl3.prSpec = ss->ssl3.pwSpec = &ss->ssl3.specs[1];
@@ -12972,17 +12944,16 @@ ssl3_InitState(sslSocket *ss)
     PORT_Memset(&ss->ssl3.hs.newSessionTicket, 0,
                 sizeof(ss->ssl3.hs.newSessionTicket));
 
     ss->ssl3.hs.zeroRttState = ssl_0rtt_none;
 
     ssl_FilterSupportedGroups(ss);
 
     ss->ssl3.initialized = PR_TRUE;
-    return SECSuccess;
 }
 
 /* record the export policy for this cipher suite */
 SECStatus
 ssl3_SetPolicy(ssl3CipherSuite which, int policy)
 {
     ssl3CipherSuiteCfg *suite;
 
--- a/security/nss/lib/ssl/ssl3exthandle.c
+++ b/security/nss/lib/ssl/ssl3exthandle.c
@@ -873,17 +873,17 @@ ssl3_ClientHandleStatusRequestXtn(const 
     }
 
     /* Keep track of negotiated extensions. */
     xtnData->negotiated[xtnData->numNegotiated++] = ex_type;
     return SECSuccess;
 }
 
 PRUint32 ssl_ticket_lifetime = 2 * 24 * 60 * 60; /* 2 days in seconds */
-#define TLS_EX_SESS_TICKET_VERSION (0x0103)
+#define TLS_EX_SESS_TICKET_VERSION (0x0104)
 
 /*
  * Called from ssl3_SendNewSessionTicket, tls13_SendNewSessionTicket
  */
 SECStatus
 ssl3_EncodeSessionTicket(sslSocket *ss,
                          const NewSessionTicket *ticket,
                          SECItem *ticket_data)
@@ -998,17 +998,18 @@ ssl3_EncodeSessionTicket(sslSocket *ss,
         + ms_item.len                          /* master_secret */
         + 1                                    /* client_auth_type */
         + cert_length                          /* cert */
         + 1                                    /* server name type */
         + srvNameLen                           /* name len + length field */
         + 1                                    /* extendedMasterSecretUsed */
         + sizeof(ticket->ticket_lifetime_hint) /* ticket lifetime hint */
         + sizeof(ticket->flags)                /* ticket flags */
-        + 1 + alpnSelection.len;               /* npn value + length field. */
+        + 1 + alpnSelection.len                /* npn value + length field. */
+        + 4;                                   /* maxEarlyData */
 #ifdef UNSAFE_FUZZER_MODE
     padding_length = 0;
 #else
     padding_length = AES_BLOCK_SIZE -
                      (ciphertext_length %
                       AES_BLOCK_SIZE);
 #endif
     ciphertext_length += padding_length;
@@ -1147,16 +1148,20 @@ ssl3_EncodeSessionTicket(sslSocket *ss,
     if (rv != SECSuccess)
         goto loser;
     if (alpnSelection.len) {
         rv = ssl3_AppendToItem(&plaintext, alpnSelection.data, alpnSelection.len);
         if (rv != SECSuccess)
             goto loser;
     }
 
+    rv = ssl3_AppendNumberToItem(&plaintext, ssl_max_early_data_size, 4);
+    if (rv != SECSuccess)
+        goto loser;
+
     PORT_Assert(plaintext.len == padding_length);
     for (i = 0; i < padding_length; i++)
         plaintext.data[i] = (unsigned char)padding_length;
 
     if (SECITEM_AllocItem(NULL, &ciphertext, ciphertext_length) == NULL) {
         rv = SECFailure;
         goto loser;
     }
@@ -1603,16 +1608,22 @@ ssl3_ProcessSessionTicketCommon(sslSocke
         rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->alpnSelection,
                               &alpn_item);
         if (rv != SECSuccess)
             goto no_ticket;
         if (alpn_item.len >= 256)
             goto no_ticket;
     }
 
+    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &buffer_len);
+    if (rv != SECSuccess) {
+        goto no_ticket;
+    }
+    parsed_session_ticket->maxEarlyData = temp;
+
 #ifndef UNSAFE_FUZZER_MODE
     /* Done parsing.  Check that all bytes have been consumed. */
     if (buffer_len != padding_length) {
         goto no_ticket;
     }
 #endif
 
     /* Use the ticket if it has not expired, otherwise free the allocated
@@ -1637,16 +1648,18 @@ ssl3_ProcessSessionTicketCommon(sslSocke
         sid->keaType = parsed_session_ticket->keaType;
         sid->keaKeyBits = parsed_session_ticket->keaKeyBits;
         sid->namedCurve = parsed_session_ticket->namedCurve;
 
         if (SECITEM_CopyItem(NULL, &sid->u.ssl3.locked.sessionTicket.ticket,
                              &extension_data) != SECSuccess)
             goto no_ticket;
         sid->u.ssl3.locked.sessionTicket.flags = parsed_session_ticket->flags;
+        sid->u.ssl3.locked.sessionTicket.max_early_data_size =
+            parsed_session_ticket->maxEarlyData;
 
         if (parsed_session_ticket->ms_length >
             sizeof(sid->u.ssl3.keys.wrapped_master_secret))
             goto no_ticket;
         PORT_Memcpy(sid->u.ssl3.keys.wrapped_master_secret,
                     parsed_session_ticket->master_secret,
                     parsed_session_ticket->ms_length);
         sid->u.ssl3.keys.wrapped_master_secret_len =
--- a/security/nss/lib/ssl/sslerr.h
+++ b/security/nss/lib/ssl/sslerr.h
@@ -240,15 +240,16 @@ typedef enum {
     SSL_ERROR_TOO_MANY_RECORDS = (SSL_ERROR_BASE + 153),
     SSL_ERROR_RX_UNEXPECTED_HELLO_RETRY_REQUEST = (SSL_ERROR_BASE + 154),
     SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST = (SSL_ERROR_BASE + 155),
     SSL_ERROR_BAD_2ND_CLIENT_HELLO = (SSL_ERROR_BASE + 156),
     SSL_ERROR_MISSING_SIGNATURE_ALGORITHMS_EXTENSION = (SSL_ERROR_BASE + 157),
     SSL_ERROR_MALFORMED_PSK_KEY_EXCHANGE_MODES = (SSL_ERROR_BASE + 158),
     SSL_ERROR_MISSING_PSK_KEY_EXCHANGE_MODES = (SSL_ERROR_BASE + 159),
     SSL_ERROR_DOWNGRADE_WITH_EARLY_DATA = (SSL_ERROR_BASE + 160),
+    SSL_ERROR_TOO_MUCH_EARLY_DATA = (SSL_ERROR_BASE + 161),
     SSL_ERROR_END_OF_LIST   /* let the c compiler determine the value of this. */
 } SSLErrorCodes;
 #endif /* NO_SECURITY_ERROR_ENUM */
 
 /* clang-format on */
 
 #endif /* __SSL_ERR_H_ */
--- a/security/nss/lib/ssl/sslimpl.h
+++ b/security/nss/lib/ssl/sslimpl.h
@@ -499,16 +499,19 @@ struct ssl3CipherSpecStr {
     sslSequenceNumber write_seq_num;
     sslSequenceNumber read_seq_num;
     SSL3ProtocolVersion version;
     ssl3KeyMaterial client;
     ssl3KeyMaterial server;
     SECItem msItem;
     DTLSEpoch epoch;
     DTLSRecvdRecords recvdRecords;
+    /* The number of 0-RTT bytes that can be sent or received in TLS 1.3. This
+     * will be zero for everything but 0-RTT. */
+    PRUint32 earlyDataRemaining;
 
     PRUint8 refCt;
     const char *phase;
 };
 
 typedef enum { never_cached,
                in_client_cache,
                in_server_cache,
@@ -1008,16 +1011,17 @@ typedef struct SessionTicketStr {
     SSL3Opaque master_secret[48];
     PRBool extendedMasterSecretUsed;
     ClientIdentity client_identity;
     SECItem peer_cert;
     PRUint32 timestamp;
     PRUint32 flags;
     SECItem srvName; /* negotiated server name */
     SECItem alpnSelection;
+    PRUint32 maxEarlyData;
 } SessionTicket;
 
 /*
  * SSL2 buffers used in SSL3.
  *     writeBuf in the SecurityInfo maintained by sslsecur.c is used
  *              to hold the data just about to be passed to the kernel
  *     sendBuf in the ConnectInfo maintained by sslcon.c is used
  *              to hold handshake messages as they are accumulated
@@ -1226,16 +1230,17 @@ struct sslSocketStr {
 };
 
 extern char ssl_debug;
 extern char ssl_trace;
 extern FILE *ssl_trace_iob;
 extern FILE *ssl_keylog_iob;
 extern PRUint32 ssl3_sid_timeout;
 extern PRUint32 ssl_ticket_lifetime;
+extern PRUint32 ssl_max_early_data_size;
 
 extern const char *const ssl3_cipherName[];
 
 extern sslSessionIDLookupFunc ssl_sid_lookup;
 extern sslSessionIDCacheFunc ssl_sid_cache;
 extern sslSessionIDUncacheFunc ssl_sid_uncache;
 
 extern const sslNamedGroupDef ssl_named_groups[];
@@ -1345,18 +1350,18 @@ extern SECStatus ssl_EnableNagleDelay(ss
 extern void ssl_FinishHandshake(sslSocket *ss);
 
 extern SECStatus ssl_CipherPolicySet(PRInt32 which, PRInt32 policy);
 
 extern SECStatus ssl_CipherPrefSetDefault(PRInt32 which, PRBool enabled);
 
 extern SECStatus ssl3_ConstrainRangeByPolicy(void);
 
-extern SECStatus ssl3_InitState(sslSocket *ss);
-extern SECStatus ssl3_RestartHandshakeHashes(sslSocket *ss);
+extern void ssl3_InitState(sslSocket *ss);
+extern void ssl3_RestartHandshakeHashes(sslSocket *ss);
 extern SECStatus ssl3_UpdateHandshakeHashes(sslSocket *ss,
                                             const unsigned char *b,
                                             unsigned int l);
 
 /* Returns PR_TRUE if we are still waiting for the server to complete its
  * response to our client second round. Once we've received the Finished from
  * the server then there is no need to check false start.
  */
--- a/security/nss/lib/ssl/sslinfo.c
+++ b/security/nss/lib/ssl/sslinfo.c
@@ -136,18 +136,29 @@ SSL_GetPreliminaryChannelInfo(PRFileDesc
 
     memset(&inf, 0, sizeof(inf));
     inf.length = PR_MIN(sizeof(inf), len);
 
     inf.valuesSet = ss->ssl3.hs.preliminaryInfo;
     inf.protocolVersion = ss->version;
     inf.cipherSuite = ss->ssl3.hs.cipher_suite;
     inf.canSendEarlyData = !ss->sec.isServer &&
-                           (ss->ssl3.hs.zeroRttState == ssl_0rtt_sent) &&
-                           !ss->firstHsDone;
+                           (ss->ssl3.hs.zeroRttState == ssl_0rtt_sent ||
+                            ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted);
+    /* We shouldn't be able to send early data if the handshake is done. */
+    PORT_Assert(!ss->firstHsDone || !inf.canSendEarlyData);
+
+    if (ss->sec.ci.sid &&
+        (ss->ssl3.hs.zeroRttState == ssl_0rtt_sent ||
+         ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted)) {
+        inf.maxEarlyDataSize =
+            ss->sec.ci.sid->u.ssl3.locked.sessionTicket.max_early_data_size;
+    } else {
+        inf.maxEarlyDataSize = 0;
+    }
 
     memcpy(info, &inf, inf.length);
     return SECSuccess;
 }
 
 /* name */
 #define CS_(x) x, #x
 #define CS(x) CS_(TLS_##x)
--- a/security/nss/lib/ssl/sslsecur.c
+++ b/security/nss/lib/ssl/sslsecur.c
@@ -879,16 +879,17 @@ ssl_SecureRead(sslSocket *ss, unsigned c
     return ssl_SecureRecv(ss, buf, len, 0);
 }
 
 /* Caller holds the SSL Socket's write lock. SSL_LOCK_WRITER(ss) */
 int
 ssl_SecureSend(sslSocket *ss, const unsigned char *buf, int len, int flags)
 {
     int rv = 0;
+    PRBool zeroRtt = PR_FALSE;
 
     SSL_TRC(2, ("%d: SSL[%d]: SecureSend: sending %d bytes",
                 SSL_GETPID(), ss->fd, len));
 
     if (ss->shutdownHow & ssl_SHUTDOWN_SEND) {
         PORT_SetError(PR_SOCKET_SHUTDOWN_ERROR);
         rv = PR_FAILURE;
         goto done;
@@ -918,65 +919,67 @@ ssl_SecureSend(sslSocket *ss, const unsi
         ss->writerThread = PR_GetCurrentThread();
 
     /* Check to see if we can write even though we're not finished.
      *
      * Case 1: False start
      * Case 2: TLS 1.3 0-RTT
      */
     if (!ss->firstHsDone) {
-        PRBool falseStart = PR_FALSE;
+        PRBool allowEarlySend = PR_FALSE;
+
         ssl_Get1stHandshakeLock(ss);
         if (ss->opt.enableFalseStart ||
             (ss->opt.enable0RttData && !ss->sec.isServer)) {
             ssl_GetSSL3HandshakeLock(ss);
             /* The client can sometimes send before the handshake is fully
              * complete. In TLS 1.2: false start; in TLS 1.3: 0-RTT. */
-            falseStart = ss->ssl3.hs.canFalseStart ||
-                         ss->ssl3.hs.zeroRttState == ssl_0rtt_sent ||
-                         ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted;
+            zeroRtt = ss->ssl3.hs.zeroRttState == ssl_0rtt_sent ||
+                      ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted;
+            allowEarlySend = ss->ssl3.hs.canFalseStart || zeroRtt;
             ssl_ReleaseSSL3HandshakeLock(ss);
         }
-        if (!falseStart && ss->handshake) {
+        if (!allowEarlySend && ss->handshake) {
             rv = ssl_Do1stHandshake(ss);
         }
         ssl_Release1stHandshakeLock(ss);
     }
     if (rv < 0) {
         ss->writerThread = NULL;
         goto done;
     }
 
+    if (zeroRtt) {
+        /* There's a limit to the number of early data octets we can send.
+         *
+         * Note that taking this lock doesn't prevent the cipher specs from
+         * being changed out between here and when records are ultimately
+         * encrypted.  The only effect of that is to occasionally do an
+         * unnecessary short write when data is identified as 0-RTT here but
+         * 1-RTT later.
+         */
+        ssl_GetSpecReadLock(ss);
+        len = tls13_LimitEarlyData(ss, content_application_data, len);
+        ssl_ReleaseSpecReadLock(ss);
+    }
+
     /* Check for zero length writes after we do housekeeping so we make forward
      * progress.
      */
     if (len == 0) {
         rv = 0;
         goto done;
     }
     PORT_Assert(buf != NULL);
     if (!buf) {
         PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
         rv = PR_FAILURE;
         goto done;
     }
 
-    if (!ss->firstHsDone) {
-#ifdef DEBUG
-        ssl_GetSSL3HandshakeLock(ss);
-        PORT_Assert(!ss->sec.isServer &&
-                    (ss->ssl3.hs.canFalseStart ||
-                     ss->ssl3.hs.zeroRttState == ssl_0rtt_sent ||
-                     ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted));
-        ssl_ReleaseSSL3HandshakeLock(ss);
-#endif
-        SSL_TRC(3, ("%d: SSL[%d]: SecureSend: sending data due to false start",
-                    SSL_GETPID(), ss->fd));
-    }
-
     ssl_GetXmitBufLock(ss);
     rv = ssl3_SendApplicationData(ss, buf, len, flags);
     ssl_ReleaseXmitBufLock(ss);
     ss->writerThread = NULL;
 done:
     if (rv < 0) {
         SSL_TRC(2, ("%d: SSL[%d]: SecureSend: returning %d count, error %d",
                     SSL_GETPID(), ss->fd, rv, PORT_GetError()));
--- a/security/nss/lib/ssl/sslt.h
+++ b/security/nss/lib/ssl/sslt.h
@@ -299,16 +299,25 @@ typedef struct SSLPreliminaryChannelInfo
     PRUint16 cipherSuite;
 
     /* The following fields were added in NSS 3.29. */
     /* |canSendEarlyData| is true when a 0-RTT is enabled. This can only be
      * true after sending the ClientHello and before the handshake completes.
      */
     PRBool canSendEarlyData;
 
+    /* The following fields were added in NSS 3.31. */
+    /* The number of early data octets that a client is permitted to send on
+     * this connection.  The value will be zero if the connection was not
+     * resumed or early data is not permitted.  For a client, this value only
+     * has meaning if |canSendEarlyData| is true.  For a server, this indicates
+     * the value that was advertised in the session ticket that was used to
+     * resume this session. */
+    PRUint32 maxEarlyDataSize;
+
     /* When adding new fields to this structure, please document the
      * NSS version in which they were added. */
 } SSLPreliminaryChannelInfo;
 
 typedef struct SSLCipherSuiteInfoStr {
     /* On return, SSL_GetCipherSuitelInfo sets |length| to the smaller of
      * the |len| argument and the length of the struct used by NSS.
      * Callers must ensure the application uses a version of NSS that
--- a/security/nss/lib/ssl/tls13con.c
+++ b/security/nss/lib/ssl/tls13con.c
@@ -2764,16 +2764,21 @@ tls13_SetCipherSpec(sslSocket *ss, Traff
     } else {
         /* The sequence number has the high 16 bits as the epoch. */
         spec->read_seq_num = spec->write_seq_num =
             (sslSequenceNumber)spec->epoch << 48;
 
         dtls_InitRecvdRecords(&spec->recvdRecords);
     }
 
+    if (type == TrafficKeyEarlyApplicationData) {
+        spec->earlyDataRemaining =
+            ss->sec.ci.sid->u.ssl3.locked.sessionTicket.max_early_data_size;
+    }
+
     /* Now that we've set almost everything up, finally cut over. */
     ssl_GetSpecWriteLock(ss);
     tls13_CipherSpecRelease(*specp); /* May delete old cipher. */
     *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),
@@ -3770,17 +3775,17 @@ tls13_SendClientSecondRound(sslSocket *s
  *   struct {
  *       uint32 ticket_lifetime;
  *       uint32 ticket_age_add;
  *       opaque ticket<1..2^16-1>;
  *       TicketExtension extensions<0..2^16-2>;
  *   } NewSessionTicket;
  */
 
-#define MAX_EARLY_DATA_SIZE (2 << 16) /* Arbitrary limit. */
+PRUint32 ssl_max_early_data_size = (2 << 16); /* Arbitrary limit. */
 
 SECStatus
 tls13_SendNewSessionTicket(sslSocket *ss)
 {
     PRUint16 message_length;
     SECItem ticket_data = { 0, NULL, 0 };
     SECStatus rv;
     NewSessionTicket ticket = { 0 };
@@ -3840,17 +3845,17 @@ tls13_SendNewSessionTicket(sslSocket *ss
         if (rv != SECSuccess)
             goto loser;
 
         /* Length */
         rv = ssl3_AppendHandshakeNumber(ss, 4, 2);
         if (rv != SECSuccess)
             goto loser;
 
-        rv = ssl3_AppendHandshakeNumber(ss, MAX_EARLY_DATA_SIZE, 4);
+        rv = ssl3_AppendHandshakeNumber(ss, ssl_max_early_data_size, 4);
         if (rv != SECSuccess)
             goto loser;
     }
 
     SECITEM_FreeItem(&ticket_data, PR_FALSE);
     return SECSuccess;
 
 loser:
@@ -4084,16 +4089,38 @@ tls13_FormatAdditionalData(PRUint8 *aad,
 {
     PRUint8 *ptr = aad;
 
     PORT_Assert(length == 8);
     ptr = ssl_EncodeUintX(seqNum, 8, ptr);
     PORT_Assert((ptr - aad) == length);
 }
 
+PRInt32
+tls13_LimitEarlyData(sslSocket *ss, SSL3ContentType type, PRInt32 toSend)
+{
+    PRInt32 reduced;
+
+    PORT_Assert(type == content_application_data);
+    PORT_Assert(ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3);
+    PORT_Assert(!ss->firstHsDone);
+    if (ss->ssl3.cwSpec->epoch != TrafficKeyEarlyApplicationData) {
+        return toSend;
+    }
+
+    if (IS_DTLS(ss) && toSend > ss->ssl3.cwSpec->earlyDataRemaining) {
+        /* Don't split application data records in DTLS. */
+        return 0;
+    }
+
+    reduced = PR_MIN(toSend, ss->ssl3.cwSpec->earlyDataRemaining);
+    ss->ssl3.cwSpec->earlyDataRemaining -= reduced;
+    return reduced;
+}
+
 SECStatus
 tls13_ProtectRecord(sslSocket *ss,
                     ssl3CipherSpec *cwSpec,
                     SSL3ContentType type,
                     const SSL3Opaque *pIn,
                     PRUint32 contentLen,
                     sslBuffer *wrBuf)
 {
@@ -4235,16 +4262,27 @@ tls13_UnprotectRecord(sslSocket *ss, SSL
         PORT_SetError(SSL_ERROR_BAD_BLOCK_PADDING);
         return SECFailure;
     }
 
     /* Record the type. */
     cText->type = plaintext->buf[plaintext->len - 1];
     --plaintext->len;
 
+    /* Check that we haven't received too much 0-RTT data. */
+    if (crSpec->epoch == TrafficKeyEarlyApplicationData &&
+        cText->type == content_application_data) {
+        if (plaintext->len > crSpec->earlyDataRemaining) {
+            *alert = unexpected_message;
+            PORT_SetError(SSL_ERROR_TOO_MUCH_EARLY_DATA);
+            return SECFailure;
+        }
+        crSpec->earlyDataRemaining -= plaintext->len;
+    }
+
     SSL_TRC(10,
             ("%d: TLS13[%d]: %s received record of length=%d type=%d",
              SSL_GETPID(), ss->fd, SSL_ROLE(ss),
              plaintext->len, cText->type));
 
     return SECSuccess;
 }
 
--- a/security/nss/lib/ssl/tls13con.h
+++ b/security/nss/lib/ssl/tls13con.h
@@ -40,16 +40,17 @@ SSLHashType tls13_GetHashForCipherSuite(
 SSLHashType tls13_GetHash(const sslSocket *ss);
 unsigned int tls13_GetHashSizeForHash(SSLHashType hash);
 unsigned int tls13_GetHashSize(const sslSocket *ss);
 CK_MECHANISM_TYPE tls13_GetHkdfMechanism(sslSocket *ss);
 void tls13_FatalError(sslSocket *ss, PRErrorCode prError,
                       SSL3AlertDescription desc);
 SECStatus tls13_SetupClientHello(sslSocket *ss);
 SECStatus tls13_MaybeDo0RTTHandshake(sslSocket *ss);
+PRInt32 tls13_LimitEarlyData(sslSocket *ss, SSL3ContentType type, PRInt32 toSend);
 PRBool tls13_AllowPskCipher(const sslSocket *ss,
                             const ssl3CipherSuiteDef *cipher_def);
 PRBool tls13_PskSuiteEnabled(sslSocket *ss);
 SECStatus tls13_ComputePskBinder(sslSocket *ss, PRBool sending,
                                  unsigned int prefixLength,
                                  PRUint8 *output, unsigned int *outputLen,
                                  unsigned int maxOutputLen);
 SECStatus tls13_HandleClientHelloPart2(sslSocket *ss,
--- a/toolkit/components/extensions/test/mochitest/mochitest-common.ini
+++ b/toolkit/components/extensions/test/mochitest/mochitest-common.ini
@@ -49,16 +49,17 @@ support-files =
 [test_ext_clipboard.html]
 # skip-if = # disabled test case with_permission_allow_copy, see inline comment.
 [test_ext_inIncognitoContext_window.html]
 skip-if = os == 'android' # Android does not support multiple windows.
 [test_ext_geturl.html]
 [test_ext_background_canvas.html]
 [test_ext_content_security_policy.html]
 [test_ext_contentscript_api_injection.html]
+[test_ext_contentscript_async_loading.html]
 [test_ext_contentscript_cache.html]
 [test_ext_contentscript_context.html]
 [test_ext_contentscript_create_iframe.html]
 [test_ext_contentscript_devtools_metadata.html]
 [test_ext_contentscript_drawWindow.html]
 [test_ext_contentscript_exporthelpers.html]
 [test_ext_contentscript_incognito.html]
 skip-if = os == 'android' # Android does not support multiple windows.
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/test_ext_contentscript_async_loading.html
@@ -0,0 +1,54 @@
+<!doctype html>
+<html>
+<head>
+  <title>Test content script async loading</title>
+  <script src="/tests/SimpleTest/SpawnTask.js"></script>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
+  <script src="head.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script>
+"use strict";
+
+add_task(function* test_async_loading() {
+  const adder = `(function add(a = 1) { this.count += a; })();\n`;
+  const extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      content_scripts: [{
+        matches: ["https://example.org/"],
+        js: ["first.js", "second.js"],
+      }],
+    },
+    files: {
+      "first.js": `
+        this.count = 0;
+        ${adder.repeat(50000)};  // 2Mb
+        browser.test.assertEq(this.count, 50000, "A 50k line script");
+
+        this.order = (this.order || 0) + 1;
+        browser.test.sendMessage("first", this.order);
+      `,
+      "second.js": `
+        this.order = (this.order || 0) + 1;
+        browser.test.sendMessage("second", this.order);
+      `,
+    },
+  });
+
+  yield extension.startup();
+  const win = window.open("https://example.org/");
+
+  const [first, second] = yield Promise.all([
+    extension.awaitMessage("first"),
+    extension.awaitMessage("second"),
+  ]);
+
+  is(first, 1, "first.js finished execution first.");
+  is(second, 2, "second.js finished execution second.");
+
+  yield extension.unload();
+  win.close();
+});
+
+</script>
--- a/toolkit/components/telemetry/TelemetryHistogram.cpp
+++ b/toolkit/components/telemetry/TelemetryHistogram.cpp
@@ -2274,16 +2274,22 @@ TelemetryHistogram::CreateHistogramSnaps
   // OK, now we can actually reflect things.
   JS::Rooted<JSObject*> hobj(cx);
   for (auto h : hs) {
     if (!internal_ShouldReflectHistogram(h) || internal_IsEmpty(h) ||
         internal_IsExpired(h)) {
       continue;
     }
 
+    mozilla::Telemetry::HistogramID id;
+    nsresult rv = internal_GetHistogramEnumId(h->histogram_name().c_str(), &id);
+    if (NS_WARN_IF(NS_FAILED(rv)) || gHistograms[id].keyed) {
+      continue;
+    }
+
     Histogram* original = h;
 #if !defined(MOZ_WIDGET_ANDROID)
     if (subsession) {
       h = internal_GetSubsessionHistogram(*h);
       if (!h) {
         continue;
       }
     }
--- a/toolkit/components/telemetry/TelemetrySession.jsm
+++ b/toolkit/components/telemetry/TelemetrySession.jsm
@@ -52,17 +52,17 @@ const INTERNAL_PROCESSES_NAMES = {
   PARENT: "default",
   CONTENT: "tab",
   GPU: "gpu",
 }
 
 const ENVIRONMENT_CHANGE_LISTENER = "TelemetrySession::onEnvironmentChange";
 
 const MS_IN_ONE_HOUR  = 60 * 60 * 1000;
-const MIN_SUBSESSION_LENGTH_MS = Preferences.get("toolkit.telemetry.minSubsessionLength", 10 * 60) * 1000;
+const MIN_SUBSESSION_LENGTH_MS = Preferences.get("toolkit.telemetry.minSubsessionLength", 5 * 60) * 1000;
 
 const LOGGER_NAME = "Toolkit.Telemetry";
 const LOGGER_PREFIX = "TelemetrySession" + (Utils.isContentProcess ? "#content::" : "::");
 
 const PREF_BRANCH = "toolkit.telemetry.";
 const PREF_PREVIOUS_BUILDID = PREF_BRANCH + "previousBuildID";
 const PREF_FHR_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled";
 const PREF_ASYNC_PLUGIN_INIT = "dom.ipc.plugins.asyncInit.enabled";
@@ -99,20 +99,16 @@ const SCHEDULER_TICK_IDLE_INTERVAL_MS = 
 // The tolerance we have when checking if it's midnight (15 minutes).
 const SCHEDULER_MIDNIGHT_TOLERANCE_MS = 15 * 60 * 1000;
 
 // Seconds of idle time before pinging.
 // On idle-daily a gather-telemetry notification is fired, during it probes can
 // start asynchronous tasks to gather data.
 const IDLE_TIMEOUT_SECONDS = Preferences.get("toolkit.telemetry.idleTimeout", 5 * 60);
 
-// To avoid generating too many main pings, we ignore environment changes that
-// happen within this interval since the last main ping.
-const CHANGE_THROTTLE_INTERVAL_MS = 5 * 60 * 1000;
-
 // The frequency at which we persist session data to the disk to prevent data loss
 // in case of aborted sessions (currently 5 minutes).
 const ABORTED_SESSION_UPDATE_INTERVAL_MS = 5 * 60 * 1000;
 
 const TOPIC_CYCLE_COLLECTOR_BEGIN = "cycle-collector-begin";
 
 // How long to wait in millis for all the child memory reports to come in
 const TOTAL_MEMORY_COLLECTOR_TIMEOUT = 200;
@@ -2089,17 +2085,17 @@ var Impl = {
     };
   },
 
   _onEnvironmentChange(reason, oldEnvironment) {
     this._log.trace("_onEnvironmentChange", reason);
 
     let now = Policy.monotonicNow();
     let timeDelta = now - this._lastEnvironmentChangeDate;
-    if (timeDelta <= CHANGE_THROTTLE_INTERVAL_MS) {
+    if (timeDelta <= MIN_SUBSESSION_LENGTH_MS) {
       this._log.trace(`_onEnvironmentChange - throttling; last change was ${Math.round(timeDelta / 1000)}s ago.`);
       return;
     }
 
     this._lastEnvironmentChangeDate = now;
     let payload = this.getSessionPayload(REASON_ENVIRONMENT_CHANGE, true);
     TelemetryScheduler.reschedulePings(REASON_ENVIRONMENT_CHANGE, payload);
 
--- a/toolkit/components/telemetry/docs/internals/preferences.rst
+++ b/toolkit/components/telemetry/docs/internals/preferences.rst
@@ -95,17 +95,17 @@ Testing
 The following prefs are for testing purpose only.
 
 ``toolkit.telemetry.initDelay``
 
   Delay before initializing telemetry (seconds).
 
 ``toolkit.telemetry.minSubsessionLength``
 
-  Minimum length of a telemetry subsession (seconds).
+  Minimum length of a telemetry subsession and throttling time for common environment changes (seconds).
 
 ``toolkit.telemetry.collectInterval``
 
   Minimum interval between data collection (seconds).
 
 ``toolkit.telemetry.scheduler.tickInterval``
 
   Interval between scheduler ticks (seconds).
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryHistograms.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryHistograms.js
@@ -752,16 +752,24 @@ add_task(function* test_keyed_histogram_
 
   // Restore to disabled
   Telemetry.setHistogramRecordingEnabled("TELEMETRY_TEST_KEYED_COUNT_INIT_NO_RECORD", false);
   h.add(TEST_KEY, 1);
   Assert.equal(h.snapshot(TEST_KEY).sum, 1,
     "Keyed histogram add should not record when recording is disabled");
 });
 
+add_task(function* test_histogramSnapshots() {
+  let keyed = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_COUNT");
+  keyed.add("a", 1);
+
+  // Check that keyed histograms are not returned
+  Assert.ok(!("TELEMETRY_TEST_KEYED_COUNT#a" in Telemetry.histogramSnapshots));
+});
+
 add_task(function* test_datasets() {
   // Check that datasets work as expected.
 
   const RELEASE_CHANNEL_OPTOUT = Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTOUT;
   const RELEASE_CHANNEL_OPTIN  = Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN;
 
   // Check that registeredHistogram works properly
   let registered = Telemetry.registeredHistograms(RELEASE_CHANNEL_OPTIN, []);
--- a/toolkit/themes/linux/global/global.css
+++ b/toolkit/themes/linux/global/global.css
@@ -90,41 +90,35 @@ iframe {
   min-width: 10px;
   min-height: 10px;
 }
 
 /* ::::: statusbar ::::: */
 
 statusbar {
   -moz-appearance: statusbar;
-  border-top: 1px solid ThreeDLightShadow;
-  border-left: 1px solid ThreeDShadow;
-  border-right: 1px solid ThreeDHighlight;
-  border-bottom: 1px solid ThreeDHighlight;
-  background-color: -moz-Dialog;
   min-height: 22px;
 }
 
+statusbar:-moz-lwtheme {
+  -moz-appearance: none;
+}
+
 statusbarpanel {
   -moz-appearance: statusbarpanel;
   -moz-box-align: center;
   -moz-box-pack: center;
-  border-left: 1px solid ThreeDHighlight;
-  border-top: 1px solid ThreeDHighlight;
-  border-right: 1px solid ThreeDShadow;
-  border-bottom: 1px solid ThreeDShadow;
   padding: 0 4px;
 }
 
 .statusbar-resizerpanel {
   -moz-box-align: end;
   -moz-box-pack: end;
   -moz-appearance: resizerpanel;
   padding: 0;
-  border: none;
 }
 
 .statusbarpanel-iconic,
 .statusbarpanel-iconic-text,
 .statusbarpanel-menu-iconic {
   padding: 0 1px;
 }